commit 1664d5cfe66e2f531e795592a1aea3d9ef2e3b13 Author: Mauricio Dinarte Date: Mon Jul 22 21:38:34 2024 -0600 Initial commit diff --git a/D7_SITE_AUDIT.md b/D7_SITE_AUDIT.md new file mode 100644 index 0000000..bb59ac3 --- /dev/null +++ b/D7_SITE_AUDIT.md @@ -0,0 +1,207 @@ +# Drupal 7 site audit + +## Modules and themes + +``` +ddev drush pm:list --fields=package,project,name,type,status,version --format=csv | sort > d7_projects.csv +``` + +## Content types + +```sql +SELECT + nt.module AS provider_module, + nt.type AS machine_name, + nt.name, + nt.description, + COUNT(n.nid) AS node_count, + COUNT(CASE WHEN n.status = 0 THEN 1 ELSE NULL END) AS unpublished_count +FROM node_type AS nt +LEFT JOIN node AS n ON n.type = nt.type +GROUP BY nt.type +ORDER BY node_count DESC; +``` + +## Taxonomy terms + +```sql +SELECT + tv.module, + tv.vid, + tv.machine_name, + tv.name, + tv.description, + COUNT(td.tid) AS term_count +FROM taxonomy_vocabulary AS tv +LEFT JOIN taxonomy_term_data AS td ON td.vid = tv.vid +GROUP BY tv.vid +ORDER BY term_count DESC; +``` + +## Field instances + +```sql +SELECT + fci.entity_type, + fci.bundle, + fci.field_name, + fc.type, + fc.module +FROM field_config_instance AS fci +INNER JOIN field_config AS fc ON fc.id = fci.field_id +ORDER BY + entity_type ASC, + bundle ASC, + field_name ASC; +``` + +## Views + +One record per view + +```sql +SELECT + vv.base_table, + vv.name AS machine_name, + vv.human_name AS label, + vv.description +FROM views_view AS vv +ORDER BY + base_table ASC, + machine_name ASC; +``` + +One record per view display + +```sql +SELECT + vv.base_table, + vv.name AS machine_name, + vv.human_name AS label, + vv.description, + vd.display_plugin, + vd.display_title, + vd.position AS display_position +FROM views_view AS vv +INNER JOIN views_display AS vd ON vd.vid = vv.vid +ORDER BY + vv.base_table ASC, + vv.name ASC, + vd.position ASC; +``` + +## Field collections + +```sql +SELECT DISTINCT + field_name AS field_collection +FROM field_collection_item +ORDER BY field_name ASC; +``` + +## Webform submissions + +```sql +SELECT + n.title AS node_title, + n.nid AS node_id, + COUNT(ws.nid) AS submission_count, + n.status AS node_status, + w.status AS webform_status +FROM webform AS w +INNER JOIN node AS n ON n.nid = w.nid +LEFT JOIN webform_submissions AS ws ON ws.nid = w.nid +GROUP BY n.nid, n.title, n.status, w.status +ORDER BY submission_count DESC; +``` + +## Menus + +```sql +SELECT + COUNT(mc.menu_name) AS link_count, + mc.menu_name AS machine_name, + mc.title AS name, + mc.description +FROM menu_custom AS mc +LEFT JOIN menu_links AS ml ON ml.menu_name = mc.menu_name +GROUP BY mc.menu_name +ORDER BY link_count DESC; +``` + +## Roles + +```sql +SELECT + rid AS role_id, + name +FROM role +ORDER BY rid ASC; +``` + +## Files + +Filemimes + +```sql +SELECT + COUNT(*) AS mime_count, + filemime +FROM file_managed +GROUP BY filemime +ORDER BY mime_count DESC; +``` + +File schemas + +```sql +SELECT DISTINCT + SUBSTRING_INDEX(uri,'://', 1) AS _schema +FROM file_managed; +``` + +File types when using the [File Entity](https://www.drupal.org/project/file_entity) module + +```sql +SELECT DISTINCT + type +FROM file_managed; +``` + +## Text formats + +Filter formats + +```sql +SELECT + format, + name +FROM filter_format; +``` + +Filters + +```sql +SELECT DISTINCT + module, + name AS filter_name +FROM filter +ORDER BY + module ASC, + name ASC; +``` + +## Image styles + +```sql +SELECT + ims.label AS style_label, + ims.name AS style_name, + ime.name AS effect_name, + ime.weight AS effect_weight +FROM image_styles AS ims +INNER JOIN image_effects AS ime ON ime.isid = ims.isid +ORDER BY + style_label ASC, + effect_weight ASC; +``` diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ad0dc6 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# Requirements + +[DDEV](https://ddev.com) is required to run this example project. See the [installation guide](https://ddev.readthedocs.io/en/stable/users/install/ddev-installation/) for instructions for your operating system. You can get support via: + +* [Discord](https://discord.gg/kDvSFBSZfs) +* The #ddev channel in [Drupal Slack](https://drupal.org/slack) + +# Drupal 7 setup + +Execute these commands to get a fully populated Drupal 7 site. It will be available at https://migration-drupal7.ddev.site/ + +``` +cd drupal7 +ddev start +ddev import-db --file ../assets/drupal7_db.sql.gz +ddev import-files --source ../assets/drupal7_files.tar.gz +ddev restart +ddev launch +ddev drush uli +``` + +# Drupal 10 setup + +Execute these commands to get a Drupal 10 site. It will be available at https://migration-drupal10.ddev.site/ + +``` +cd drupal10 +ddev start +ddev composer install +ddev composer si +ddev launch +ddev drush uli +``` diff --git a/assets/drupal7_db.sql.gz b/assets/drupal7_db.sql.gz new file mode 100644 index 0000000..f623a97 Binary files /dev/null and b/assets/drupal7_db.sql.gz differ diff --git a/assets/drupal7_files.tar.gz b/assets/drupal7_files.tar.gz new file mode 100644 index 0000000..7a5f10f Binary files /dev/null and b/assets/drupal7_files.tar.gz differ diff --git a/drupal10/.ddev/config.yaml b/drupal10/.ddev/config.yaml new file mode 100644 index 0000000..cfdb52c --- /dev/null +++ b/drupal10/.ddev/config.yaml @@ -0,0 +1,285 @@ +name: migration-drupal10 +type: drupal +docroot: web +php_version: "8.3" +webserver_type: nginx-fpm +xdebug_enabled: false +additional_hostnames: [] +additional_fqdns: [] +database: + type: mariadb + version: "10.6" +use_dns_when_possible: true +composer_version: "2" +web_environment: [] +corepack_enable: false + +# Key features of DDEV's config.yaml: + +# name: # Name of the project, automatically provides +# http://projectname.ddev.site and https://projectname.ddev.site + +# type: # backdrop, craftcms, django4, drupal, drupal6, drupal7, laravel, magento, magento2, php, python, shopware6, silverstripe, typo3, wordpress +# See https://ddev.readthedocs.io/en/stable/users/quickstart/ for more +# information on the different project types +# "drupal" covers recent Drupal 8+ + +# docroot: # Relative path to the directory containing index.php. + +# php_version: "8.2" # PHP version to use, "5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2", "8.3" + +# You can explicitly specify the webimage but this +# is not recommended, as the images are often closely tied to DDEV's' behavior, +# so this can break upgrades. + +# webimage: # nginx/php docker image. + +# database: +# type: # mysql, mariadb, postgres +# version: # database version, like "10.11" or "8.0" +# MariaDB versions can be 5.5-10.8, 10.11, and 11.4. +# MySQL versions can be 5.5-8.0. +# PostgreSQL versions can be 9-16. + +# router_http_port: # Port to be used for http (defaults to global configuration, usually 80) +# router_https_port: # Port for https (defaults to global configuration, usually 443) + +# xdebug_enabled: false # Set to true to enable Xdebug and "ddev start" or "ddev restart" +# Note that for most people the commands +# "ddev xdebug" to enable Xdebug and "ddev xdebug off" to disable it work better, +# as leaving Xdebug enabled all the time is a big performance hit. + +# xhprof_enabled: false # Set to true to enable Xhprof and "ddev start" or "ddev restart" +# Note that for most people the commands +# "ddev xhprof" to enable Xhprof and "ddev xhprof off" to disable it work better, +# as leaving Xhprof enabled all the time is a big performance hit. + +# webserver_type: nginx-fpm, apache-fpm, or nginx-gunicorn + +# timezone: Europe/Berlin +# This is the timezone used in the containers and by PHP; +# it can be set to any valid timezone, +# see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones +# For example Europe/Dublin or MST7MDT + +# composer_root: +# Relative path to the Composer root directory from the project root. This is +# the directory which contains the composer.json and where all Composer related +# commands are executed. + +# composer_version: "2" +# You can set it to "" or "2" (default) for Composer v2 or "1" for Composer v1 +# to use the latest major version available at the time your container is built. +# It is also possible to use each other Composer version channel. This includes: +# - 2.2 (latest Composer LTS version) +# - stable +# - preview +# - snapshot +# Alternatively, an explicit Composer version may be specified, for example "2.2.18". +# To reinstall Composer after the image was built, run "ddev debug refresh". + +# nodejs_version: "20" +# change from the default system Node.js version to any other version. +# Numeric version numbers can be complete (i.e. 18.15.0) or +# incomplete (18, 17.2, 16). 'lts' and 'latest' can be used as well along with +# other named releases. +# see https://www.npmjs.com/package/n#specifying-nodejs-versions +# Note that you can continue using 'ddev nvm' or nvm inside the web container +# to change the project's installed node version if you need to. + +# corepack_enable: false +# Change to 'true' to 'corepack enable' and gain access to latest versions of yarn/pnpm + +# additional_hostnames: +# - somename +# - someothername +# would provide http and https URLs for "somename.ddev.site" +# and "someothername.ddev.site". + +# additional_fqdns: +# - example.com +# - sub1.example.com +# would provide http and https URLs for "example.com" and "sub1.example.com" +# Please take care with this because it can cause great confusion. + +# upload_dirs: "custom/upload/dir" +# +# upload_dirs: +# - custom/upload/dir +# - ../private +# +# would set the destination paths for ddev import-files to /custom/upload/dir +# When Mutagen is enabled this path is bind-mounted so that all the files +# in the upload_dirs don't have to be synced into Mutagen. + +# disable_upload_dirs_warning: false +# If true, turns off the normal warning that says +# "You have Mutagen enabled and your 'php' project type doesn't have upload_dirs set" + +# ddev_version_constraint: "" +# Example: +# ddev_version_constraint: ">= 1.22.4" +# This will enforce that the running ddev version is within this constraint. +# See https://github.com/Masterminds/semver#checking-version-constraints for +# supported constraint formats + +# working_dir: +# web: /var/www/html +# db: /home +# would set the default working directory for the web and db services. +# These values specify the destination directory for ddev ssh and the +# directory in which commands passed into ddev exec are run. + +# omit_containers: [db, ddev-ssh-agent] +# Currently only these containers are supported. Some containers can also be +# omitted globally in the ~/.ddev/global_config.yaml. Note that if you omit +# the "db" container, several standard features of DDEV that access the +# database container will be unusable. In the global configuration it is also +# possible to omit ddev-router, but not here. + +# performance_mode: "global" +# DDEV offers performance optimization strategies to improve the filesystem +# performance depending on your host system. Should be configured globally. +# +# If set, will override the global config. Possible values are: +# - "global": uses the value from the global config. +# - "none": disables performance optimization for this project. +# - "mutagen": enables Mutagen for this project. +# - "nfs": enables NFS for this project. +# +# See https://ddev.readthedocs.io/en/stable/users/install/performance/#nfs +# See https://ddev.readthedocs.io/en/stable/users/install/performance/#mutagen + +# fail_on_hook_fail: False +# Decide whether 'ddev start' should be interrupted by a failing hook + +# host_https_port: "59002" +# The host port binding for https can be explicitly specified. It is +# dynamic unless otherwise specified. +# This is not used by most people, most people use the *router* instead +# of the localhost port. + +# host_webserver_port: "59001" +# The host port binding for the ddev-webserver can be explicitly specified. It is +# dynamic unless otherwise specified. +# This is not used by most people, most people use the *router* instead +# of the localhost port. + +# host_db_port: "59002" +# The host port binding for the ddev-dbserver can be explicitly specified. It is dynamic +# unless explicitly specified. + +# mailpit_http_port: "8025" +# mailpit_https_port: "8026" +# The Mailpit ports can be changed from the default 8025 and 8026 + +# host_mailpit_port: "8025" +# The mailpit port is not normally bound on the host at all, instead being routed +# through ddev-router, but it can be bound directly to localhost if specified here. + +# webimage_extra_packages: [php7.4-tidy, php-bcmath] +# Extra Debian packages that are needed in the webimage can be added here + +# dbimage_extra_packages: [telnet,netcat] +# Extra Debian packages that are needed in the dbimage can be added here + +# use_dns_when_possible: true +# If the host has internet access and the domain configured can +# successfully be looked up, DNS will be used for hostname resolution +# instead of editing /etc/hosts +# Defaults to true + +# project_tld: ddev.site +# The top-level domain used for project URLs +# The default "ddev.site" allows DNS lookup via a wildcard +# If you prefer you can change this to "ddev.local" to preserve +# pre-v1.9 behavior. + +# ngrok_args: --basic-auth username:pass1234 +# Provide extra flags to the "ngrok http" command, see +# https://ngrok.com/docs/ngrok-agent/config or run "ngrok http -h" + +# disable_settings_management: false +# If true, DDEV will not create CMS-specific settings files like +# Drupal's settings.php/settings.ddev.php or TYPO3's additional.php +# In this case the user must provide all such settings. + +# You can inject environment variables into the web container with: +# web_environment: +# - SOMEENV=somevalue +# - SOMEOTHERENV=someothervalue + +# no_project_mount: false +# (Experimental) If true, DDEV will not mount the project into the web container; +# the user is responsible for mounting it manually or via a script. +# This is to enable experimentation with alternate file mounting strategies. +# For advanced users only! + +# bind_all_interfaces: false +# If true, host ports will be bound on all network interfaces, +# not the localhost interface only. This means that ports +# will be available on the local network if the host firewall +# allows it. + +# default_container_timeout: 120 +# The default time that DDEV waits for all containers to become ready can be increased from +# the default 120. This helps in importing huge databases, for example. + +#web_extra_exposed_ports: +#- name: nodejs +# container_port: 3000 +# http_port: 2999 +# https_port: 3000 +#- name: something +# container_port: 4000 +# https_port: 4000 +# http_port: 3999 +# Allows a set of extra ports to be exposed via ddev-router +# Fill in all three fields even if you don’t intend to use the https_port! +# If you don’t add https_port, then it defaults to 0 and ddev-router will fail to start. +# +# The port behavior on the ddev-webserver must be arranged separately, for example +# using web_extra_daemons. +# For example, with a web app on port 3000 inside the container, this config would +# expose that web app on https://.ddev.site:9999 and http://.ddev.site:9998 +# web_extra_exposed_ports: +# - name: myapp +# container_port: 3000 +# http_port: 9998 +# https_port: 9999 + +#web_extra_daemons: +#- name: "http-1" +# command: "/var/www/html/node_modules/.bin/http-server -p 3000" +# directory: /var/www/html +#- name: "http-2" +# command: "/var/www/html/node_modules/.bin/http-server /var/www/html/sub -p 3000" +# directory: /var/www/html + +# override_config: false +# By default, config.*.yaml files are *merged* into the configuration +# But this means that some things can't be overridden +# For example, if you have 'use_dns_when_possible: true'' you can't override it with a merge +# and you can't erase existing hooks or all environment variables. +# However, with "override_config: true" in a particular config.*.yaml file, +# 'use_dns_when_possible: false' can override the existing values, and +# hooks: +# post-start: [] +# or +# web_environment: [] +# or +# additional_hostnames: [] +# can have their intended affect. 'override_config' affects only behavior of the +# config.*.yaml file it exists in. + +# Many DDEV commands can be extended to run tasks before or after the +# DDEV command is executed, for example "post-start", "post-import-db", +# "pre-composer", "post-composer" +# See https://ddev.readthedocs.io/en/stable/users/extend/custom-commands/ for more +# information on the commands that can be extended and the tasks you can define +# for them. Example: +#hooks: +# post-import-db: +# - exec: drush sql:sanitize +# - exec: drush updatedb +# - exec: drush cache:rebuild diff --git a/drupal10/.editorconfig b/drupal10/.editorconfig new file mode 100644 index 0000000..686c443 --- /dev/null +++ b/drupal10/.editorconfig @@ -0,0 +1,17 @@ +# Drupal editor configuration normalization +# @see http://editorconfig.org/ + +# This is the top-most .editorconfig file; do not search in parent directories. +root = true + +# All files. +[*] +end_of_line = LF +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[composer.{json,lock}] +indent_size = 4 diff --git a/drupal10/.gitattributes b/drupal10/.gitattributes new file mode 100644 index 0000000..e7b792f --- /dev/null +++ b/drupal10/.gitattributes @@ -0,0 +1,64 @@ +# Drupal git normalization +# @see https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html +# @see https://www.drupal.org/node/1542048 + +# Normally these settings would be done with macro attributes for improved +# readability and easier maintenance. However macros can only be defined at the +# repository root directory. Drupal avoids making any assumptions about where it +# is installed. + +# Define text file attributes. +# - Treat them as text. +# - Ensure no CRLF line-endings, neither on checkout nor on checkin. +# - Detect whitespace errors. +# - Exposed by default in `git diff --color` on the CLI. +# - Validate with `git diff --check`. +# - Deny applying with `git apply --whitespace=error-all`. +# - Fix automatically with `git apply --whitespace=fix`. + +*.config text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.css text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.dist text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.engine text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.html text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=html +*.inc text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.install text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.js text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.json text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.lock text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.map text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.md text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.module text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.php text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.po text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.profile text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.script text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.sh text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.sql text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.svg text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.theme text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.twig text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.txt text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.xml text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.yml text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 + +# PHPStan's baseline uses tabs instead of spaces. +core/.phpstan-baseline.php text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tabwidth=2 diff=php linguist-language=php + +# Define binary file attributes. +# - Do not treat them as text. +# - Include binary diff in patches instead of "binary files differ." +*.eot -text diff +*.exe -text diff +*.gif -text diff +*.gz -text diff +*.ico -text diff +*.jpeg -text diff +*.jpg -text diff +*.otf -text diff +*.phar -text diff +*.png -text diff +*.svgz -text diff +*.ttf -text diff +*.woff -text diff +*.woff2 -text diff diff --git a/drupal10/.gitignore b/drupal10/.gitignore new file mode 100644 index 0000000..8fae307 --- /dev/null +++ b/drupal10/.gitignore @@ -0,0 +1,25 @@ +# Ignore directories generated by Composer +/drush/contrib/ +/vendor/ +/web/core/ +/web/modules/contrib/ +/web/themes/contrib/ +/web/profiles/contrib/ +/web/libraries/ + +# Ignore sensitive information +/web/sites/*/settings.local.php + +# Ignore Drupal's file directory +/web/sites/*/files/ + +# Ignore SimpleTest multi-site environment +/web/sites/simpletest + +# Ignore files generated by common IDEs +/.idea/ +/.vscode/ + +# Ignore .env files as they are personal +/.env + diff --git a/drupal10/composer.json b/drupal10/composer.json new file mode 100644 index 0000000..06ea182 --- /dev/null +++ b/drupal10/composer.json @@ -0,0 +1,87 @@ +{ + "name": "drupal/recommended-project", + "description": "Project template for Drupal projects with a relocated document root", + "type": "project", + "license": "GPL-2.0-or-later", + "homepage": "https://www.drupal.org/project/drupal", + "support": { + "docs": "https://www.drupal.org/docs/user_guide/en/index.html", + "chat": "https://www.drupal.org/node/314178" + }, + "repositories": [ + { + "type": "composer", + "url": "https://packages.drupal.org/8" + } + ], + "require": { + "composer/installers": "^2.0", + "drupal/address": "^2.0", + "drupal/core-composer-scaffold": "^10.3.0", + "drupal/core-recommended": "^10.3.0", + "drupal/field_group": "^3.4", + "drupal/migrate_plus": "^6.0", + "drupal/migrate_skip_fields": "^1.0@alpha", + "drupal/migrate_upgrade": "^4.0", + "drupal/migrate_url2link": "^1.0", + "drupal/paragraphs": "^1.17", + "drupal/pathauto": "^1.12", + "drupal/social_link_field": "^2.0", + "drupal/views_migration": "^1.2", + "drush/drush": "^13.0@beta" + }, + "conflict": { + "drupal/drupal": "*" + }, + "scripts": { + "si": "drush --yes sql:drop && drush --yes -v site:install tag1_profile --existing-config" + }, + "minimum-stability": "stable", + "prefer-stable": true, + "config": { + "allow-plugins": { + "composer/installers": true, + "drupal/core-composer-scaffold": true, + "phpstan/extension-installer": true, + "dealerdirect/phpcodesniffer-composer-installer": true, + "php-http/discovery": true + }, + "sort-packages": true + }, + "extra": { + "drupal-scaffold": { + "locations": { + "web-root": "web/" + } + }, + "installer-paths": { + "web/core": [ + "type:drupal-core" + ], + "web/libraries/{$name}": [ + "type:drupal-library" + ], + "web/modules/contrib/{$name}": [ + "type:drupal-module" + ], + "web/profiles/contrib/{$name}": [ + "type:drupal-profile" + ], + "web/themes/contrib/{$name}": [ + "type:drupal-theme" + ], + "drush/Commands/contrib/{$name}": [ + "type:drupal-drush" + ], + "web/modules/custom/{$name}": [ + "type:drupal-custom-module" + ], + "web/profiles/custom/{$name}": [ + "type:drupal-custom-profile" + ], + "web/themes/custom/{$name}": [ + "type:drupal-custom-theme" + ] + } + } +} diff --git a/drupal10/composer.lock b/drupal10/composer.lock new file mode 100644 index 0000000..d4f9f68 --- /dev/null +++ b/drupal10/composer.lock @@ -0,0 +1,7382 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "79a239149fab227ba17bb542a5de5a7d", + "packages": [ + { + "name": "asm89/stack-cors", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/asm89/stack-cors.git", + "reference": "50f57105bad3d97a43ec4a485eb57daf347eafea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/asm89/stack-cors/zipball/50f57105bad3d97a43ec4a485eb57daf347eafea", + "reference": "50f57105bad3d97a43ec4a485eb57daf347eafea", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0", + "symfony/http-foundation": "^5.3|^6|^7", + "symfony/http-kernel": "^5.3|^6|^7" + }, + "require-dev": { + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-4": { + "Asm89\\Stack\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alexander", + "email": "iam.asm89@gmail.com" + } + ], + "description": "Cross-origin resource sharing library and stack middleware", + "homepage": "https://github.com/asm89/stack-cors", + "keywords": [ + "cors", + "stack" + ], + "support": { + "issues": "https://github.com/asm89/stack-cors/issues", + "source": "https://github.com/asm89/stack-cors/tree/v2.2.0" + }, + "time": "2023-11-14T13:51:46+00:00" + }, + { + "name": "chi-teck/drupal-code-generator", + "version": "3.6.1", + "source": { + "type": "git", + "url": "https://github.com/Chi-teck/drupal-code-generator.git", + "reference": "2dbd8d231945681a398862a3282ade3cf0ea23ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Chi-teck/drupal-code-generator/zipball/2dbd8d231945681a398862a3282ade3cf0ea23ab", + "reference": "2dbd8d231945681a398862a3282ade3cf0ea23ab", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=8.1.0", + "psr/event-dispatcher": "^1.0", + "psr/log": "^3.0", + "symfony/console": "^6.3", + "symfony/dependency-injection": "^6.3.2", + "symfony/filesystem": "^6.3", + "symfony/string": "^6.3", + "twig/twig": "^3.4" + }, + "conflict": { + "squizlabs/php_codesniffer": "<3.6" + }, + "require-dev": { + "chi-teck/drupal-coder-extension": "^2.0.0-beta3", + "drupal/coder": "8.3.23", + "drupal/core": "10.3.x-dev", + "ext-simplexml": "*", + "phpspec/prophecy-phpunit": "^2.2", + "phpunit/phpunit": "^9.6", + "squizlabs/php_codesniffer": "^3.9", + "symfony/var-dumper": "^6.4", + "symfony/yaml": "^6.3", + "vimeo/psalm": "^5.22.2" + }, + "bin": [ + "bin/dcg" + ], + "type": "library", + "autoload": { + "psr-4": { + "DrupalCodeGenerator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "description": "Drupal code generator", + "support": { + "issues": "https://github.com/Chi-teck/drupal-code-generator/issues", + "source": "https://github.com/Chi-teck/drupal-code-generator/tree/3.6.1" + }, + "time": "2024-06-06T17:36:37+00:00" + }, + { + "name": "commerceguys/addressing", + "version": "v2.2.1", + "source": { + "type": "git", + "url": "https://github.com/commerceguys/addressing.git", + "reference": "3e679cd280169109ab6f9d7bff0d069e14d309ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/commerceguys/addressing/zipball/3e679cd280169109ab6f9d7bff0d069e14d309ac", + "reference": "3e679cd280169109ab6f9d7bff0d069e14d309ac", + "shasum": "" + }, + "require": { + "doctrine/collections": "^1.6 || ^2.0", + "php": ">=8.0" + }, + "require-dev": { + "ext-json": "*", + "mikey179/vfsstream": "^1.6.11", + "phpunit/phpunit": "^9.6", + "squizlabs/php_codesniffer": "^3.7", + "symfony/validator": "^5.4 || ^6.3 || ^7.0" + }, + "suggest": { + "symfony/validator": "to validate addresses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "CommerceGuys\\Addressing\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bojan Zivanovic" + }, + { + "name": "Damien Tournoud" + } + ], + "description": "Addressing library powered by CLDR and Google's address data.", + "keywords": [ + "address", + "internationalization", + "localization", + "postal" + ], + "support": { + "issues": "https://github.com/commerceguys/addressing/issues", + "source": "https://github.com/commerceguys/addressing/tree/v2.2.1" + }, + "time": "2024-07-04T15:28:57+00:00" + }, + { + "name": "composer/installers", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/composer/installers.git", + "reference": "12fb2dfe5e16183de69e784a7b84046c43d97e8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/installers/zipball/12fb2dfe5e16183de69e784a7b84046c43d97e8e", + "reference": "12fb2dfe5e16183de69e784a7b84046c43d97e8e", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "composer/composer": "^1.10.27 || ^2.7", + "composer/semver": "^1.7.2 || ^3.4.0", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-phpunit": "^1", + "symfony/phpunit-bridge": "^7.1.1", + "symfony/process": "^5 || ^6 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "Composer\\Installers\\Plugin", + "branch-alias": { + "dev-main": "2.x-dev" + }, + "plugin-modifies-install-path": true + }, + "autoload": { + "psr-4": { + "Composer\\Installers\\": "src/Composer/Installers" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kyle Robinson Young", + "email": "kyle@dontkry.com", + "homepage": "https://github.com/shama" + } + ], + "description": "A multi-framework Composer library installer", + "homepage": "https://composer.github.io/installers/", + "keywords": [ + "Dolibarr", + "Eliasis", + "Hurad", + "ImageCMS", + "Kanboard", + "Lan Management System", + "MODX Evo", + "MantisBT", + "Mautic", + "Maya", + "OXID", + "Plentymarkets", + "Porto", + "RadPHP", + "SMF", + "Starbug", + "Thelia", + "Whmcs", + "WolfCMS", + "agl", + "annotatecms", + "attogram", + "bitrix", + "cakephp", + "chef", + "cockpit", + "codeigniter", + "concrete5", + "concreteCMS", + "croogo", + "dokuwiki", + "drupal", + "eZ Platform", + "elgg", + "expressionengine", + "fuelphp", + "grav", + "installer", + "itop", + "known", + "kohana", + "laravel", + "lavalite", + "lithium", + "magento", + "majima", + "mako", + "matomo", + "mediawiki", + "miaoxing", + "modulework", + "modx", + "moodle", + "osclass", + "pantheon", + "phpbb", + "piwik", + "ppi", + "processwire", + "puppet", + "pxcms", + "reindex", + "roundcube", + "shopware", + "silverstripe", + "sydes", + "sylius", + "tastyigniter", + "wordpress", + "yawik", + "zend", + "zikula" + ], + "support": { + "issues": "https://github.com/composer/installers/issues", + "source": "https://github.com/composer/installers/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-06-24T20:46:46+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/c51258e759afdb17f1fd1fe83bc12baaef6309d6", + "reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-07-12T11:35:52+00:00" + }, + { + "name": "consolidation/annotated-command", + "version": "4.10.0", + "source": { + "type": "git", + "url": "https://github.com/consolidation/annotated-command.git", + "reference": "1e830ba908c9ffb1ba7ca056203531b27188812c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/1e830ba908c9ffb1ba7ca056203531b27188812c", + "reference": "1e830ba908c9ffb1ba7ca056203531b27188812c", + "shasum": "" + }, + "require": { + "consolidation/output-formatters": "^4.3.1", + "php": ">=7.1.3", + "psr/log": "^1 || ^2 || ^3", + "symfony/console": "^4.4.8 || ^5 || ^6 || ^7", + "symfony/event-dispatcher": "^4.4.8 || ^5 || ^6 || ^7", + "symfony/finder": "^4.4.8 || ^5 || ^6 || ^7" + }, + "require-dev": { + "composer-runtime-api": "^2.0", + "phpunit/phpunit": "^7.5.20 || ^8 || ^9", + "squizlabs/php_codesniffer": "^3", + "yoast/phpunit-polyfills": "^0.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\AnnotatedCommand\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Initialize Symfony Console commands from annotated command class methods.", + "support": { + "issues": "https://github.com/consolidation/annotated-command/issues", + "source": "https://github.com/consolidation/annotated-command/tree/4.10.0" + }, + "time": "2024-04-05T21:05:39+00:00" + }, + { + "name": "consolidation/config", + "version": "2.1.2", + "source": { + "type": "git", + "url": "https://github.com/consolidation/config.git", + "reference": "597f8d7fbeef801736250ec10c3e190569b1b0ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/config/zipball/597f8d7fbeef801736250ec10c3e190569b1b0ae", + "reference": "597f8d7fbeef801736250ec10c3e190569b1b0ae", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^1.1.0 || ^2 || ^3", + "grasmash/expander": "^2.0.1 || ^3", + "php": ">=7.1.3", + "symfony/event-dispatcher": "^4 || ^5 || ^6" + }, + "require-dev": { + "ext-json": "*", + "phpunit/phpunit": ">=7.5.20", + "squizlabs/php_codesniffer": "^3", + "symfony/console": "^4 || ^5 || ^6", + "symfony/yaml": "^4 || ^5 || ^6", + "yoast/phpunit-polyfills": "^1" + }, + "suggest": { + "symfony/event-dispatcher": "Required to inject configuration into Command options", + "symfony/yaml": "Required to use Consolidation\\Config\\Loader\\YamlConfigLoader" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Provide configuration services for a commandline tool.", + "support": { + "issues": "https://github.com/consolidation/config/issues", + "source": "https://github.com/consolidation/config/tree/2.1.2" + }, + "time": "2022-10-06T17:48:03+00:00" + }, + { + "name": "consolidation/filter-via-dot-access-data", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/consolidation/filter-via-dot-access-data.git", + "reference": "cb2eeba41f8e2e3c61698a5cf70ef048ff6c9d5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/filter-via-dot-access-data/zipball/cb2eeba41f8e2e3c61698a5cf70ef048ff6c9d5b", + "reference": "cb2eeba41f8e2e3c61698a5cf70ef048ff6c9d5b", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^1.1.0 || ^2.0.0 || ^3.0.0", + "php": ">=7.1.3" + }, + "require-dev": { + "phpunit/phpunit": "^7.5.20 || ^8 || ^9", + "squizlabs/php_codesniffer": "^3", + "yoast/phpunit-polyfills": "^0.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\Filter\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "This project uses dflydev/dot-access-data to provide simple output filtering for applications built with annotated-command / Robo.", + "support": { + "source": "https://github.com/consolidation/filter-via-dot-access-data/tree/2.0.2" + }, + "time": "2021-12-30T03:56:08+00:00" + }, + { + "name": "consolidation/log", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/consolidation/log.git", + "reference": "c27a3beb36137c141ccbce0d89f64befb243c015" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/log/zipball/c27a3beb36137c141ccbce0d89f64befb243c015", + "reference": "c27a3beb36137c141ccbce0d89f64befb243c015", + "shasum": "" + }, + "require": { + "php": ">=8.0.0", + "psr/log": "^3", + "symfony/console": "^5 || ^6 || ^7" + }, + "require-dev": { + "phpunit/phpunit": "^7.5.20 || ^8 || ^9", + "squizlabs/php_codesniffer": "^3", + "yoast/phpunit-polyfills": "^0.2.0" + }, + "type": "library", + "extra": { + "platform": { + "php": "8.2.17" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", + "support": { + "issues": "https://github.com/consolidation/log/issues", + "source": "https://github.com/consolidation/log/tree/3.1.0" + }, + "time": "2024-04-04T23:50:25+00:00" + }, + { + "name": "consolidation/output-formatters", + "version": "4.5.0", + "source": { + "type": "git", + "url": "https://github.com/consolidation/output-formatters.git", + "reference": "7a611b01eb48eb19cd54672339fc08c0985bf540" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/7a611b01eb48eb19cd54672339fc08c0985bf540", + "reference": "7a611b01eb48eb19cd54672339fc08c0985bf540", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^1.1.0 || ^2 || ^3", + "php": ">=7.1.3", + "symfony/console": "^4 || ^5 || ^6 || ^7", + "symfony/finder": "^4 || ^5 || ^6 || ^7" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.4.2", + "phpunit/phpunit": "^7 || ^8 || ^9", + "squizlabs/php_codesniffer": "^3", + "symfony/var-dumper": "^4 || ^5 || ^6 || ^7", + "symfony/yaml": "^4 || ^5 || ^6 || ^7", + "yoast/phpunit-polyfills": "^1" + }, + "suggest": { + "symfony/var-dumper": "For using the var_dump formatter" + }, + "type": "library", + "autoload": { + "psr-4": { + "Consolidation\\OutputFormatters\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Format text by applying transformations provided by plug-in formatters.", + "support": { + "issues": "https://github.com/consolidation/output-formatters/issues", + "source": "https://github.com/consolidation/output-formatters/tree/4.5.0" + }, + "time": "2024-04-02T15:18:52+00:00" + }, + { + "name": "consolidation/robo", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/consolidation/robo.git", + "reference": "55a272370940607649e5c46eb173c5c54f7c166d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/robo/zipball/55a272370940607649e5c46eb173c5c54f7c166d", + "reference": "55a272370940607649e5c46eb173c5c54f7c166d", + "shasum": "" + }, + "require": { + "consolidation/annotated-command": "^4.8.1", + "consolidation/config": "^2.0.1", + "consolidation/log": "^2.0.2 || ^3", + "consolidation/output-formatters": "^4.1.2", + "consolidation/self-update": "^2.0", + "league/container": "^3.3.1 || ^4.0", + "php": ">=8.0", + "phpowermove/docblock": "^4.0", + "symfony/console": "^6", + "symfony/event-dispatcher": "^6", + "symfony/filesystem": "^6", + "symfony/finder": "^6", + "symfony/process": "^6", + "symfony/yaml": "^6" + }, + "conflict": { + "codegyre/robo": "*" + }, + "require-dev": { + "natxet/cssmin": "3.0.4", + "patchwork/jsqueeze": "^2", + "pear/archive_tar": "^1.4.4", + "phpunit/phpunit": "^7.5.20 || ^8", + "squizlabs/php_codesniffer": "^3.6", + "yoast/phpunit-polyfills": "^0.2.0" + }, + "suggest": { + "natxet/cssmin": "For minifying CSS files in taskMinify", + "patchwork/jsqueeze": "For minifying JS files in taskMinify", + "pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively.", + "totten/lurkerlite": "For monitoring filesystem changes in taskWatch" + }, + "bin": [ + "robo" + ], + "type": "library", + "autoload": { + "psr-4": { + "Robo\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Davert", + "email": "davert.php@resend.cc" + } + ], + "description": "Modern task runner", + "support": { + "issues": "https://github.com/consolidation/robo/issues", + "source": "https://github.com/consolidation/robo/tree/4.0.6" + }, + "time": "2023-04-30T21:49:04+00:00" + }, + { + "name": "consolidation/self-update", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/consolidation/self-update.git", + "reference": "972a1016761c9b63314e040836a12795dff6953a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/self-update/zipball/972a1016761c9b63314e040836a12795dff6953a", + "reference": "972a1016761c9b63314e040836a12795dff6953a", + "shasum": "" + }, + "require": { + "composer/semver": "^3.2", + "php": ">=5.5.0", + "symfony/console": "^2.8 || ^3 || ^4 || ^5 || ^6", + "symfony/filesystem": "^2.5 || ^3 || ^4 || ^5 || ^6" + }, + "bin": [ + "scripts/release" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "SelfUpdate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alexander Menk", + "email": "menk@mestrona.net" + }, + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Provides a self:update command for Symfony Console applications.", + "support": { + "issues": "https://github.com/consolidation/self-update/issues", + "source": "https://github.com/consolidation/self-update/tree/2.2.0" + }, + "time": "2023-03-18T01:37:41+00:00" + }, + { + "name": "consolidation/site-alias", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/consolidation/site-alias.git", + "reference": "1056ceb93f6aafe6f7600d7bbe1b62b8488abccf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/site-alias/zipball/1056ceb93f6aafe6f7600d7bbe1b62b8488abccf", + "reference": "1056ceb93f6aafe6f7600d7bbe1b62b8488abccf", + "shasum": "" + }, + "require": { + "consolidation/config": "^1.2.1 || ^2 || ^3", + "php": ">=7.4", + "symfony/filesystem": "^5.4 || ^6 || ^7", + "symfony/finder": "^5 || ^6 || ^7" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.4.2", + "phpunit/phpunit": ">=7", + "squizlabs/php_codesniffer": "^3", + "symfony/var-dumper": "^4", + "yoast/phpunit-polyfills": "^0.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\SiteAlias\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + }, + { + "name": "Moshe Weitzman", + "email": "weitzman@tejasa.com" + } + ], + "description": "Manage alias records for local and remote sites.", + "support": { + "issues": "https://github.com/consolidation/site-alias/issues", + "source": "https://github.com/consolidation/site-alias/tree/4.1.0" + }, + "time": "2024-04-05T15:58:04+00:00" + }, + { + "name": "consolidation/site-process", + "version": "5.4.0", + "source": { + "type": "git", + "url": "https://github.com/consolidation/site-process.git", + "reference": "7ab3ffe4195a89b8dc334ea22e7881abe79ffd9a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/site-process/zipball/7ab3ffe4195a89b8dc334ea22e7881abe79ffd9a", + "reference": "7ab3ffe4195a89b8dc334ea22e7881abe79ffd9a", + "shasum": "" + }, + "require": { + "consolidation/config": "^2 || ^3", + "consolidation/site-alias": "^3 || ^4", + "php": ">=8.0.14", + "symfony/console": "^5.4 || ^6 || ^7", + "symfony/process": "^6 || ^7" + }, + "require-dev": { + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\SiteProcess\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + }, + { + "name": "Moshe Weitzman", + "email": "weitzman@tejasa.com" + } + ], + "description": "A thin wrapper around the Symfony Process Component that allows applications to use the Site Alias library to specify the target for a remote call.", + "support": { + "issues": "https://github.com/consolidation/site-process/issues", + "source": "https://github.com/consolidation/site-process/tree/5.4.0" + }, + "time": "2024-04-06T00:00:28+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" + }, + "time": "2024-07-08T12:26:09+00:00" + }, + { + "name": "doctrine/annotations", + "version": "1.14.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af", + "reference": "fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^1 || ^2", + "ext-tokenizer": "*", + "php": "^7.1 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" + }, + "require-dev": { + "doctrine/cache": "^1.11 || ^2.0", + "doctrine/coding-standard": "^9 || ^10", + "phpstan/phpstan": "~1.4.10 || ^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^4.4 || ^5.4 || ^6", + "vimeo/psalm": "^4.10" + }, + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/1.14.3" + }, + "time": "2023-02-01T09:20:38+00:00" + }, + { + "name": "doctrine/collections", + "version": "2.2.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "d8af7f248c74f195f7347424600fd9e17b57af59" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/d8af7f248c74f195f7347424600fd9e17b57af59", + "reference": "d8af7f248c74f195f7347424600fd9e17b57af59", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1", + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "ext-json": "*", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^10.5", + "vimeo/psalm": "^5.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Collections\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.", + "homepage": "https://www.doctrine-project.org/projects/collections.html", + "keywords": [ + "array", + "collections", + "iterators", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/collections/issues", + "source": "https://github.com/doctrine/collections/tree/2.2.2" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcollections", + "type": "tidelift" + } + ], + "time": "2024-04-18T06:56:21+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + }, + "time": "2024-01-30T19:34:25+00:00" + }, + { + "name": "doctrine/lexer", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6", + "reference": "861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^4.11 || ^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/2.1.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:35:39+00:00" + }, + { + "name": "drupal/address", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/address.git", + "reference": "2.0.2" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/address-2.0.2.zip", + "reference": "2.0.2", + "shasum": "62875933847d9dcab5584de9c88712fa372cab9d" + }, + "require": { + "commerceguys/addressing": "^2.1.1", + "drupal/core": "^9.5 || ^10 || ^11", + "php": "^8.0" + }, + "require-dev": { + "drupal/diff": "^1", + "drupal/feeds": "^3", + "drupal/token": "^1" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "2.0.2", + "datestamp": "1720108676", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "bojanz", + "homepage": "https://www.drupal.org/user/86106" + }, + { + "name": "Centarro", + "homepage": "https://www.drupal.org/user/3661446" + }, + { + "name": "dww", + "homepage": "https://www.drupal.org/user/46549" + }, + { + "name": "jsacksick", + "homepage": "https://www.drupal.org/user/972218" + }, + { + "name": "rszrama", + "homepage": "https://www.drupal.org/user/49344" + } + ], + "description": "Provides functionality for storing, validating and displaying international postal addresses.", + "homepage": "http://drupal.org/project/address", + "support": { + "source": "https://git.drupalcode.org/project/address" + } + }, + { + "name": "drupal/core", + "version": "10.3.1", + "source": { + "type": "git", + "url": "https://github.com/drupal/core.git", + "reference": "d137403a30d4154404e473785f48dfc889d77e23" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/drupal/core/zipball/d137403a30d4154404e473785f48dfc889d77e23", + "reference": "d137403a30d4154404e473785f48dfc889d77e23", + "shasum": "" + }, + "require": { + "asm89/stack-cors": "^2.1", + "composer-runtime-api": "^2.1", + "composer/semver": "^3.3", + "doctrine/annotations": "^1.14", + "egulias/email-validator": "^3.2.1|^4.0", + "ext-date": "*", + "ext-dom": "*", + "ext-filter": "*", + "ext-gd": "*", + "ext-hash": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-pdo": "*", + "ext-session": "*", + "ext-simplexml": "*", + "ext-spl": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "guzzlehttp/guzzle": "^7.5", + "guzzlehttp/psr7": "^2.4.5", + "masterminds/html5": "^2.7", + "mck89/peast": "^1.14", + "pear/archive_tar": "^1.4.14", + "php": ">=8.1.0", + "psr/log": "^3.0", + "sebastian/diff": "^4", + "symfony/console": "^6.4", + "symfony/dependency-injection": "^6.4", + "symfony/event-dispatcher": "^6.4", + "symfony/filesystem": "^6.4", + "symfony/finder": "^6.4", + "symfony/http-foundation": "^6.4", + "symfony/http-kernel": "^6.4", + "symfony/mailer": "^6.4", + "symfony/mime": "^6.4", + "symfony/polyfill-iconv": "^1.26", + "symfony/process": "^6.4", + "symfony/psr-http-message-bridge": "^2.1|^6.4", + "symfony/routing": "^6.4", + "symfony/serializer": "^6.4", + "symfony/validator": "^6.4", + "symfony/yaml": "^6.4", + "twig/twig": "^3.9.3" + }, + "conflict": { + "drush/drush": "<12.4.3" + }, + "replace": { + "drupal/core-annotation": "self.version", + "drupal/core-assertion": "self.version", + "drupal/core-class-finder": "self.version", + "drupal/core-datetime": "self.version", + "drupal/core-dependency-injection": "self.version", + "drupal/core-diff": "self.version", + "drupal/core-discovery": "self.version", + "drupal/core-event-dispatcher": "self.version", + "drupal/core-file-cache": "self.version", + "drupal/core-file-security": "self.version", + "drupal/core-filesystem": "self.version", + "drupal/core-front-matter": "self.version", + "drupal/core-gettext": "self.version", + "drupal/core-graph": "self.version", + "drupal/core-http-foundation": "self.version", + "drupal/core-php-storage": "self.version", + "drupal/core-plugin": "self.version", + "drupal/core-proxy-builder": "self.version", + "drupal/core-render": "self.version", + "drupal/core-serialization": "self.version", + "drupal/core-transliteration": "self.version", + "drupal/core-utility": "self.version", + "drupal/core-uuid": "self.version", + "drupal/core-version": "self.version" + }, + "suggest": { + "ext-zip": "Needed to extend the plugin.manager.archiver service capability with the handling of files in the ZIP format." + }, + "type": "drupal-core", + "extra": { + "drupal-scaffold": { + "file-mapping": { + "[project-root]/.editorconfig": "assets/scaffold/files/editorconfig", + "[project-root]/.gitattributes": "assets/scaffold/files/gitattributes", + "[web-root]/.csslintrc": "assets/scaffold/files/csslintrc", + "[web-root]/.eslintignore": "assets/scaffold/files/eslintignore", + "[web-root]/.eslintrc.json": "assets/scaffold/files/eslintrc.json", + "[web-root]/.ht.router.php": "assets/scaffold/files/ht.router.php", + "[web-root]/.htaccess": "assets/scaffold/files/htaccess", + "[web-root]/example.gitignore": "assets/scaffold/files/example.gitignore", + "[web-root]/index.php": "assets/scaffold/files/index.php", + "[web-root]/INSTALL.txt": "assets/scaffold/files/drupal.INSTALL.txt", + "[web-root]/README.md": "assets/scaffold/files/drupal.README.md", + "[web-root]/robots.txt": "assets/scaffold/files/robots.txt", + "[web-root]/update.php": "assets/scaffold/files/update.php", + "[web-root]/web.config": "assets/scaffold/files/web.config", + "[web-root]/sites/README.txt": "assets/scaffold/files/sites.README.txt", + "[web-root]/sites/development.services.yml": "assets/scaffold/files/development.services.yml", + "[web-root]/sites/example.settings.local.php": "assets/scaffold/files/example.settings.local.php", + "[web-root]/sites/example.sites.php": "assets/scaffold/files/example.sites.php", + "[web-root]/sites/default/default.services.yml": "assets/scaffold/files/default.services.yml", + "[web-root]/sites/default/default.settings.php": "assets/scaffold/files/default.settings.php", + "[web-root]/modules/README.txt": "assets/scaffold/files/modules.README.txt", + "[web-root]/profiles/README.txt": "assets/scaffold/files/profiles.README.txt", + "[web-root]/themes/README.txt": "assets/scaffold/files/themes.README.txt" + } + } + }, + "autoload": { + "files": [ + "includes/bootstrap.inc" + ], + "psr-4": { + "Drupal\\Core\\": "lib/Drupal/Core", + "Drupal\\Component\\": "lib/Drupal/Component" + }, + "classmap": [ + "lib/Drupal.php", + "lib/Drupal/Component/DependencyInjection/Container.php", + "lib/Drupal/Component/DependencyInjection/PhpArrayContainer.php", + "lib/Drupal/Component/FileCache/FileCacheFactory.php", + "lib/Drupal/Component/Utility/Timer.php", + "lib/Drupal/Component/Utility/Unicode.php", + "lib/Drupal/Core/Cache/Cache.php", + "lib/Drupal/Core/Cache/CacheBackendInterface.php", + "lib/Drupal/Core/Cache/CacheTagsChecksumInterface.php", + "lib/Drupal/Core/Cache/CacheTagsChecksumTrait.php", + "lib/Drupal/Core/Cache/CacheTagsInvalidatorInterface.php", + "lib/Drupal/Core/Cache/DatabaseBackend.php", + "lib/Drupal/Core/Cache/DatabaseCacheTagsChecksum.php", + "lib/Drupal/Core/Database/Connection.php", + "lib/Drupal/Core/Database/Database.php", + "lib/Drupal/Core/Database/StatementInterface.php", + "lib/Drupal/Core/DependencyInjection/Container.php", + "lib/Drupal/Core/DrupalKernel.php", + "lib/Drupal/Core/DrupalKernelInterface.php", + "lib/Drupal/Core/Installer/InstallerRedirectTrait.php", + "lib/Drupal/Core/Site/Settings.php", + "lib/Drupal/Component/Datetime/Time.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "description": "Drupal is an open source content management platform powering millions of websites and applications.", + "support": { + "source": "https://github.com/drupal/core/tree/10.3.1" + }, + "time": "2024-07-04T11:33:45+00:00" + }, + { + "name": "drupal/core-composer-scaffold", + "version": "10.3.1", + "source": { + "type": "git", + "url": "https://github.com/drupal/core-composer-scaffold.git", + "reference": "a1a186caeb89899143e0c6912ccee9d3d7181dbe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/drupal/core-composer-scaffold/zipball/a1a186caeb89899143e0c6912ccee9d3d7181dbe", + "reference": "a1a186caeb89899143e0c6912ccee9d3d7181dbe", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2", + "php": ">=7.3.0" + }, + "conflict": { + "drupal-composer/drupal-scaffold": "*" + }, + "require-dev": { + "composer/composer": "^1.8@stable" + }, + "type": "composer-plugin", + "extra": { + "class": "Drupal\\Composer\\Plugin\\Scaffold\\Plugin", + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Drupal\\Composer\\Plugin\\Scaffold\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "description": "A flexible Composer project scaffold builder.", + "homepage": "https://www.drupal.org/project/drupal", + "keywords": [ + "drupal" + ], + "support": { + "source": "https://github.com/drupal/core-composer-scaffold/tree/10.3.1" + }, + "time": "2024-05-11T08:21:39+00:00" + }, + { + "name": "drupal/core-recommended", + "version": "10.3.1", + "source": { + "type": "git", + "url": "https://github.com/drupal/core-recommended.git", + "reference": "a5183f2be315b7e5deec89fdeafe9fc9a2e54f57" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/drupal/core-recommended/zipball/a5183f2be315b7e5deec89fdeafe9fc9a2e54f57", + "reference": "a5183f2be315b7e5deec89fdeafe9fc9a2e54f57", + "shasum": "" + }, + "require": { + "asm89/stack-cors": "~v2.2.0", + "composer/semver": "~3.4.0", + "doctrine/annotations": "~1.14.3", + "doctrine/deprecations": "~1.1.3", + "doctrine/lexer": "~2.1.1", + "drupal/core": "10.3.1", + "egulias/email-validator": "~4.0.2", + "guzzlehttp/guzzle": "~7.8.1", + "guzzlehttp/promises": "~2.0.2", + "guzzlehttp/psr7": "~2.6.2", + "masterminds/html5": "~2.9.0", + "mck89/peast": "~v1.16.2", + "pear/archive_tar": "~1.5.0", + "pear/console_getopt": "~v1.4.3", + "pear/pear-core-minimal": "~v1.10.15", + "pear/pear_exception": "~v1.0.2", + "psr/cache": "~3.0.0", + "psr/container": "~2.0.2", + "psr/event-dispatcher": "~1.0.0", + "psr/http-client": "~1.0.3", + "psr/http-factory": "~1.1.0", + "psr/log": "~3.0.0", + "ralouphie/getallheaders": "~3.0.3", + "sebastian/diff": "~4.0.6", + "symfony/console": "~v6.4.8", + "symfony/dependency-injection": "~v6.4.7", + "symfony/deprecation-contracts": "~v3.5.0", + "symfony/error-handler": "~v6.4.7", + "symfony/event-dispatcher": "~v6.4.7", + "symfony/event-dispatcher-contracts": "~v3.5.0", + "symfony/filesystem": "~v6.4.8", + "symfony/finder": "~v6.4.8", + "symfony/http-foundation": "~v6.4.7", + "symfony/http-kernel": "~v6.4.7", + "symfony/mailer": "~v6.4.7", + "symfony/mime": "~v6.4.7", + "symfony/polyfill-ctype": "~v1.29.0", + "symfony/polyfill-iconv": "~v1.29.0", + "symfony/polyfill-intl-grapheme": "~v1.29.0", + "symfony/polyfill-intl-idn": "~v1.29.0", + "symfony/polyfill-intl-normalizer": "~v1.29.0", + "symfony/polyfill-mbstring": "~v1.29.0", + "symfony/polyfill-php83": "~v1.29.0", + "symfony/process": "~v6.4.8", + "symfony/psr-http-message-bridge": "~v6.4.7", + "symfony/routing": "~v6.4.7", + "symfony/serializer": "~v6.4.7", + "symfony/service-contracts": "~v3.5.0", + "symfony/string": "~v6.4.8", + "symfony/translation-contracts": "~v3.5.0", + "symfony/validator": "~v6.4.7", + "symfony/var-dumper": "~v6.4.7", + "symfony/var-exporter": "~v6.4.7", + "symfony/yaml": "~v6.4.7", + "twig/twig": "~v3.10.2" + }, + "conflict": { + "webflo/drupal-core-strict": "*" + }, + "type": "metapackage", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "description": "Core and its dependencies with known-compatible minor versions. Require this project INSTEAD OF drupal/core.", + "support": { + "source": "https://github.com/drupal/core-recommended/tree/10.3.1" + }, + "time": "2024-07-04T11:33:45+00:00" + }, + { + "name": "drupal/ctools", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/ctools.git", + "reference": "4.1.0" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/ctools-4.1.0.zip", + "reference": "4.1.0", + "shasum": "69f5889cf557df9e55519390e6a95cfa31b67874" + }, + "require": { + "drupal/core": "^9.5 || ^10 || ^11" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "4.1.0", + "datestamp": "1718144949", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + }, + "branch-alias": { + "dev-8.x-3.x": "3.x-dev" + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Kris Vanderwater (EclipseGc)", + "homepage": "https://www.drupal.org/u/eclipsegc", + "role": "Maintainer" + }, + { + "name": "Jakob Perry (japerry)", + "homepage": "https://www.drupal.org/u/japerry", + "role": "Maintainer" + }, + { + "name": "Tim Plunkett (tim.plunkett)", + "homepage": "https://www.drupal.org/u/timplunkett", + "role": "Maintainer" + }, + { + "name": "James Gilliland (neclimdul)", + "homepage": "https://www.drupal.org/u/neclimdul", + "role": "Maintainer" + }, + { + "name": "Daniel Wehner (dawehner)", + "homepage": "https://www.drupal.org/u/dawehner", + "role": "Maintainer" + }, + { + "name": "joelpittet", + "homepage": "https://www.drupal.org/user/160302" + }, + { + "name": "merlinofchaos", + "homepage": "https://www.drupal.org/user/26979" + }, + { + "name": "neclimdul", + "homepage": "https://www.drupal.org/user/48673" + }, + { + "name": "sdboyer", + "homepage": "https://www.drupal.org/user/146719" + }, + { + "name": "sun", + "homepage": "https://www.drupal.org/user/54136" + }, + { + "name": "tim.plunkett", + "homepage": "https://www.drupal.org/user/241634" + } + ], + "description": "Provides a number of utility and helper APIs for Drupal developers and site builders.", + "homepage": "https://www.drupal.org/project/ctools", + "support": { + "source": "https://git.drupalcode.org/project/ctools", + "issues": "https://www.drupal.org/project/issues/ctools" + } + }, + { + "name": "drupal/entity_reference_revisions", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/entity_reference_revisions.git", + "reference": "8.x-1.11" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/entity_reference_revisions-8.x-1.11.zip", + "reference": "8.x-1.11", + "shasum": "de21cbb0d8a0344dc3496addcad4ed536747cec5" + }, + "require": { + "drupal/core": "^9 || ^10" + }, + "require-dev": { + "drupal/diff": "1.x-dev" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.11", + "datestamp": "1705140721", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + }, + "drush": { + "services": { + "drush.services.yml": "^9 || ^10 || ^11" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Berdir", + "homepage": "https://www.drupal.org/user/214652" + }, + { + "name": "Frans", + "homepage": "https://www.drupal.org/user/514222" + }, + { + "name": "jeroen.b", + "homepage": "https://www.drupal.org/user/1853532" + }, + { + "name": "miro_dietiker", + "homepage": "https://www.drupal.org/user/227761" + } + ], + "description": "Entity Reference Revisions", + "homepage": "https://www.drupal.org/project/entity_reference_revisions", + "support": { + "source": "https://git.drupalcode.org/project/entity_reference_revisions" + } + }, + { + "name": "drupal/field_group", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/field_group.git", + "reference": "8.x-3.4" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/field_group-8.x-3.4.zip", + "reference": "8.x-3.4", + "shasum": "80b937e1a11f8b29c69d853fc4bf798c057c6f94" + }, + "require": { + "drupal/core": "^9.2 || ^10" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-3.4", + "datestamp": "1667241979", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Anybody", + "homepage": "https://www.drupal.org/user/291091" + }, + { + "name": "Hydra", + "homepage": "https://www.drupal.org/user/647364" + }, + { + "name": "jyve", + "homepage": "https://www.drupal.org/user/591438" + }, + { + "name": "nils.destoop", + "homepage": "https://www.drupal.org/user/361625" + }, + { + "name": "Stalski", + "homepage": "https://www.drupal.org/user/322618" + }, + { + "name": "swentel", + "homepage": "https://www.drupal.org/user/107403" + } + ], + "description": "Provides the field_group module.", + "homepage": "https://www.drupal.org/project/field_group", + "support": { + "source": "https://git.drupalcode.org/project/field_group", + "issues": "https://www.drupal.org/project/issues/field_group" + } + }, + { + "name": "drupal/migrate_plus", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/migrate_plus.git", + "reference": "6.0.3" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/migrate_plus-6.0.3.zip", + "reference": "6.0.3", + "shasum": "08feb88178a45d8354862b03fd7fcf9b47ae14e1" + }, + "require": { + "drupal/core": ">=9.1", + "php": ">=7.4" + }, + "require-dev": { + "drupal/migrate_example_advanced_setup": "*", + "drupal/migrate_example_setup": "*" + }, + "suggest": { + "ext-soap": "*", + "sainsburys/guzzle-oauth2-plugin": "3.0 required for the OAuth2 authentication plugin" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "6.0.3", + "datestamp": "1721333111", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Mike Ryan", + "homepage": "https://www.drupal.org/u/mikeryan", + "role": "Maintainer" + }, + { + "name": "Lucas Hedding", + "homepage": "https://www.drupal.org/u/heddn", + "role": "Maintainer" + }, + { + "name": "mikeryan", + "homepage": "https://www.drupal.org/user/4420" + } + ], + "description": "Enhancements to core migration support.", + "homepage": "https://www.drupal.org/project/migrate_plus", + "support": { + "source": "https://git.drupalcode.org/project/migrate_plus", + "issues": "https://www.drupal.org/project/issues/migrate_plus", + "slack": "#migrate" + } + }, + { + "name": "drupal/migrate_skip_fields", + "version": "1.0.0-alpha4", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/migrate_skip_fields.git", + "reference": "1.0.0-alpha4" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/migrate_skip_fields-1.0.0-alpha4.zip", + "reference": "1.0.0-alpha4", + "shasum": "5ea6402b55a3cde6f5ae2f06666adbfc257d1528" + }, + "require": { + "drupal/core": "^9 || ^10" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "1.0.0-alpha4", + "datestamp": "1717493591", + "security-coverage": { + "status": "not-covered", + "message": "Project has not opted into security advisory coverage!" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "dinarcon", + "homepage": "https://www.drupal.org/user/909522" + } + ], + "description": "Skip fields from being migrated as part of an upgrade.", + "homepage": "https://www.drupal.org/project/migrate_skip_fields", + "support": { + "source": "https://git.drupalcode.org/project/migrate_skip_fields" + } + }, + { + "name": "drupal/migrate_tools", + "version": "6.0.4", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/migrate_tools.git", + "reference": "6.0.4" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/migrate_tools-6.0.4.zip", + "reference": "6.0.4", + "shasum": "63c571aefece96b199ce8b8f90da648186502be4" + }, + "require": { + "drupal/core": ">=9.1", + "php": ">=7.4" + }, + "require-dev": { + "drupal/migrate_plus": ">=5", + "drupal/migrate_source_csv": ">=3", + "drush/drush": ">=11" + }, + "suggest": { + "drupal/migrate_plus": ">=5", + "drush/drush": ">=11" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "6.0.4", + "datestamp": "1707330330", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + }, + "drush": { + "services": { + "drush.services.yml": ">=9" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Mike Ryan", + "homepage": "https://www.drupal.org/u/mikeryan", + "role": "Maintainer" + }, + { + "name": "Lucas Hedding", + "homepage": "https://www.drupal.org/u/heddn", + "role": "Maintainer" + }, + { + "name": "mikeryan", + "homepage": "https://www.drupal.org/user/4420" + } + ], + "description": "Tools to assist in developing and running migrations.", + "homepage": "http://drupal.org/project/migrate_tools", + "support": { + "source": "https://git.drupalcode.org/project/migrate_tools", + "issues": "https://www.drupal.org/project/issues/migrate_tools", + "slack": "#migrate" + } + }, + { + "name": "drupal/migrate_upgrade", + "version": "4.0.2", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/migrate_upgrade.git", + "reference": "4.0.2" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/migrate_upgrade-4.0.2.zip", + "reference": "4.0.2", + "shasum": "358f3cea91411b179a1c78841f1cc3166a947088" + }, + "require": { + "drupal/core": ">=9", + "drupal/migrate_plus": ">=5", + "drush/drush": ">=9", + "php": ">7.3" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "4.0.2", + "datestamp": "1719350600", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Mike Ryan", + "homepage": "https://www.drupal.org/u/mikeryan", + "role": "Maintainer" + }, + { + "name": "Lucas Hedding", + "homepage": "https://www.drupal.org/u/heddn", + "role": "Maintainer" + } + ], + "description": "Drush support for direct upgrades from older Drupal versions.", + "homepage": "https://www.drupal.org/project/migrate_upgrade", + "support": { + "source": "https://git.drupalcode.org/project/migrate_upgrade", + "issues": "https://www.drupal.org/project/issues/migrate_upgrade", + "slack": "#migrate" + } + }, + { + "name": "drupal/migrate_url2link", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/migrate_url2link.git", + "reference": "1.0.0" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/migrate_url2link-1.0.0.zip", + "reference": "1.0.0", + "shasum": "8e9f3011bfbd97ef3028d11a9cb8b8284782cccd" + }, + "require": { + "drupal/core": "^8 || ^9 || ^10", + "drupal/migrate_plus": "*" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "1.0.0", + "datestamp": "1679338355", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "DamienMcKenna", + "homepage": "https://www.drupal.org/user/108450" + } + ], + "description": "Migrates the URL field on D7 to the Link module to D8/9.", + "homepage": "https://www.drupal.org/project/migrate_url2link", + "support": { + "source": "https://git.drupalcode.org/project/migrate_url2link" + } + }, + { + "name": "drupal/paragraphs", + "version": "1.17.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/paragraphs.git", + "reference": "8.x-1.17" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/paragraphs-8.x-1.17.zip", + "reference": "8.x-1.17", + "shasum": "81c05f6a1eb59ab957c9ac97b2e79d6c9837bd72" + }, + "require": { + "drupal/core": "^9.3 || ^10", + "drupal/entity_reference_revisions": "~1.3" + }, + "require-dev": { + "drupal/block_field": "1.x-dev", + "drupal/diff": "1.x-dev", + "drupal/entity_browser": "2.x-dev", + "drupal/entity_usage": "2.x-dev", + "drupal/field_group": "3.x-dev", + "drupal/inline_entity_form": "1.x-dev", + "drupal/paragraphs-paragraphs_library": "*", + "drupal/replicate": "1.x-dev", + "drupal/search_api": "^1", + "drupal/search_api_db": "*" + }, + "suggest": { + "drupal/entity_browser": "Recommended for an improved user experience when using the Paragraphs library module" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.17", + "datestamp": "1709804220", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Berdir", + "homepage": "https://www.drupal.org/user/214652" + }, + { + "name": "Frans", + "homepage": "https://www.drupal.org/user/514222" + }, + { + "name": "jeroen.b", + "homepage": "https://www.drupal.org/user/1853532" + }, + { + "name": "jstoller", + "homepage": "https://www.drupal.org/user/99012" + }, + { + "name": "miro_dietiker", + "homepage": "https://www.drupal.org/user/227761" + }, + { + "name": "Primsi", + "homepage": "https://www.drupal.org/user/282629" + } + ], + "description": "Enables the creation of Paragraphs entities.", + "homepage": "https://www.drupal.org/project/paragraphs", + "support": { + "source": "https://git.drupalcode.org/project/paragraphs" + } + }, + { + "name": "drupal/pathauto", + "version": "1.12.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/pathauto.git", + "reference": "8.x-1.12" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/pathauto-8.x-1.12.zip", + "reference": "8.x-1.12", + "shasum": "b7b6432e315e38e59a7c6cc117134326c580de4c" + }, + "require": { + "drupal/core": "^9.3 || ^10", + "drupal/ctools": "*", + "drupal/token": "*" + }, + "suggest": { + "drupal/redirect": "When installed Pathauto will provide a new \"Update Action\" in case your URLs change. This is the recommended update action and is considered the best practice for SEO and usability." + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.12", + "datestamp": "1712319355", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + }, + "drush": { + "services": { + "drush.services.yml": "^9 || ^10" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Berdir", + "homepage": "https://www.drupal.org/user/214652" + }, + { + "name": "Dave Reid", + "homepage": "https://www.drupal.org/user/53892" + }, + { + "name": "Freso", + "homepage": "https://www.drupal.org/user/27504" + }, + { + "name": "greggles", + "homepage": "https://www.drupal.org/user/36762" + } + ], + "description": "Provides a mechanism for modules to automatically generate aliases for the content they manage.", + "homepage": "https://www.drupal.org/project/pathauto", + "support": { + "source": "https://cgit.drupalcode.org/pathauto", + "issues": "https://www.drupal.org/project/issues/pathauto", + "documentation": "https://www.drupal.org/docs/8/modules/pathauto" + } + }, + { + "name": "drupal/social_link_field", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/social_link_field.git", + "reference": "2.0.1" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/social_link_field-2.0.1.zip", + "reference": "2.0.1", + "shasum": "bb76fafdb6766d3125ce306171f981cac352a29d" + }, + "require": { + "drupal/core": "^9 || ^10" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "2.0.1", + "datestamp": "1710435719", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0+" + ], + "authors": [ + { + "name": "Roman Hryshkanych", + "homepage": "https://www.drupal.org/u/romixua" + }, + { + "name": "NickDickinsonWilde", + "homepage": "https://www.drupal.org/user/3094661" + }, + { + "name": "Romixua", + "homepage": "https://www.drupal.org/user/3516201" + }, + { + "name": "voleger", + "homepage": "https://www.drupal.org/user/3260314" + } + ], + "description": "Provide field type with social links.", + "homepage": "https://www.drupal.org/project/social_link_field", + "support": { + "source": "http://cgit.drupalcode.org/social_link_field", + "issues": "https://www.drupal.org/project/issues/social_link_field" + } + }, + { + "name": "drupal/token", + "version": "1.14.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/token.git", + "reference": "8.x-1.14" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/token-8.x-1.14.zip", + "reference": "8.x-1.14", + "shasum": "df3cae709fcc1a99ac1111ce67a0d6af56d287d7" + }, + "require": { + "drupal/core": "^9.2 || ^10" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.14", + "datestamp": "1713009399", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + }, + "drush": { + "services": { + "drush.services.yml": ">=9" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Berdir", + "homepage": "https://www.drupal.org/user/214652" + }, + { + "name": "Dave Reid", + "homepage": "https://www.drupal.org/user/53892" + }, + { + "name": "eaton", + "homepage": "https://www.drupal.org/user/16496" + }, + { + "name": "fago", + "homepage": "https://www.drupal.org/user/16747" + }, + { + "name": "greggles", + "homepage": "https://www.drupal.org/user/36762" + }, + { + "name": "mikeryan", + "homepage": "https://www.drupal.org/user/4420" + } + ], + "description": "Provides a user interface for the Token API, some missing core tokens.", + "homepage": "https://www.drupal.org/project/token", + "support": { + "source": "https://git.drupalcode.org/project/token" + } + }, + { + "name": "drupal/views_migration", + "version": "1.2.7", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/views_migration.git", + "reference": "1.2.7" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/views_migration-1.2.7.zip", + "reference": "1.2.7", + "shasum": "9ba961aaf20209c2ab868024da134f18d8011f28" + }, + "require": { + "drupal/core": "^10 || ^11", + "drupal/migrate_plus": "^6.0", + "drupal/migrate_tools": "^6.0" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "1.2.7", + "datestamp": "1714384505", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Jeya Sundharam (Jeya sundhar)", + "homepage": "https://www.drupal.org/u/jeya-sundhar", + "role": "Maintainer" + }, + { + "name": "Contributors", + "homepage": "https://git.drupalcode.org/project/views_migration/-/graphs/HEAD", + "role": "Contributor" + }, + { + "name": "tame4tex", + "homepage": "https://www.drupal.org/user/1824356" + }, + { + "name": "webchick", + "homepage": "https://www.drupal.org/user/24967" + } + ], + "description": "Migrate Drupal 7 Views to Drupal 9", + "homepage": "https://drupal.org/project/views_migration", + "support": { + "source": "https://git.drupalcode.org/project/views_migration", + "issues": "https://www.drupal.org/project/issues/views_migration" + } + }, + { + "name": "drush/drush", + "version": "13.0.0-beta5", + "source": { + "type": "git", + "url": "https://github.com/drush-ops/drush.git", + "reference": "8389f62c17dee1e9212c97e69410e126989f211a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/drush-ops/drush/zipball/8389f62c17dee1e9212c97e69410e126989f211a", + "reference": "8389f62c17dee1e9212c97e69410e126989f211a", + "shasum": "" + }, + "require": { + "chi-teck/drupal-code-generator": "^3.6 || ^4@alpha", + "composer-runtime-api": "^2.2", + "composer/semver": "^1.4 || ^3", + "consolidation/annotated-command": "^4.9.2", + "consolidation/config": "^2.1.2 || ^3", + "consolidation/filter-via-dot-access-data": "^2.0.2", + "consolidation/output-formatters": "^4.3.2", + "consolidation/robo": "^4.0.6 || ^5", + "consolidation/site-alias": "^4", + "consolidation/site-process": "^5.2.0", + "dflydev/dot-access-data": "^3.0.2", + "ext-dom": "*", + "grasmash/yaml-cli": "^3.1", + "guzzlehttp/guzzle": "^7.0", + "laravel/prompts": "^0.1.21", + "league/container": "^4.2", + "php": ">=8.2", + "psy/psysh": "~0.12", + "symfony/event-dispatcher": "^6 || ^7", + "symfony/filesystem": "^6.1 || ^7", + "symfony/finder": "^6 || ^7", + "symfony/var-dumper": "^6.0 || ^7", + "symfony/yaml": "^6.0 || ^7" + }, + "conflict": { + "drupal/core": "< 10.2", + "drupal/migrate_run": "*", + "drupal/migrate_tools": "<= 5" + }, + "require-dev": { + "composer/installers": "^2", + "cweagans/composer-patches": "~1.7.3", + "drupal/core-recommended": "^10.2.5 || 11.0.x-dev", + "drupal/semver_example": "2.3.0", + "jetbrains/phpstorm-attributes": "^1.0", + "mglaman/phpstan-drupal": "^1.2", + "phpunit/phpunit": "^9 || ^10", + "rector/rector": "^1", + "squizlabs/php_codesniffer": "^3.7" + }, + "bin": [ + "drush" + ], + "type": "library", + "extra": { + "installer-paths": { + "sut/core": [ + "type:drupal-core" + ], + "sut/libraries/{$name}": [ + "type:drupal-library" + ], + "sut/modules/unish/{$name}": [ + "drupal/devel" + ], + "sut/themes/unish/{$name}": [ + "drupal/empty_theme" + ], + "sut/modules/contrib/{$name}": [ + "type:drupal-module" + ], + "sut/profiles/contrib/{$name}": [ + "type:drupal-profile" + ], + "sut/themes/contrib/{$name}": [ + "type:drupal-theme" + ], + "sut/drush/contrib/{$name}": [ + "type:drupal-drush" + ] + } + }, + "autoload": { + "psr-4": { + "Drush\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Moshe Weitzman", + "email": "weitzman@tejasa.com" + }, + { + "name": "Owen Barton", + "email": "drupal@owenbarton.com" + }, + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + }, + { + "name": "Jonathan Araña Cruz", + "email": "jonhattan@faita.net" + }, + { + "name": "Jonathan Hedstrom", + "email": "jhedstrom@gmail.com" + }, + { + "name": "Christopher Gervais", + "email": "chris@ergonlogic.com" + }, + { + "name": "Dave Reid", + "email": "dave@davereid.net" + }, + { + "name": "Damian Lee", + "email": "damiankloip@googlemail.com" + } + ], + "description": "Drush is a command line shell and scripting interface for Drupal, a veritable Swiss Army knife designed to make life easier for those of us who spend some of our working hours hacking away at the command prompt.", + "homepage": "http://www.drush.org", + "support": { + "forum": "http://drupal.stackexchange.com/questions/tagged/drush", + "issues": "https://github.com/drush-ops/drush/issues", + "security": "https://github.com/drush-ops/drush/security/advisories", + "slack": "https://drupal.slack.com/messages/C62H9CWQM", + "source": "https://github.com/drush-ops/drush/tree/13.0.0-beta5" + }, + "funding": [ + { + "url": "https://github.com/weitzman", + "type": "github" + } + ], + "time": "2024-06-22T17:22:18+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.2", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.2" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2023-10-06T06:47:41+00:00" + }, + { + "name": "grasmash/expander", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/grasmash/expander.git", + "reference": "bb1c1a2430957945cf08c5a62f5d72a6aa6a2c82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/grasmash/expander/zipball/bb1c1a2430957945cf08c5a62f5d72a6aa6a2c82", + "reference": "bb1c1a2430957945cf08c5a62f5d72a6aa6a2c82", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.0", + "php": ">=8.0", + "psr/log": "^2 | ^3" + }, + "require-dev": { + "greg-1-anderson/composer-test-scenarios": "^1", + "php-coveralls/php-coveralls": "^2.5", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Grasmash\\Expander\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Grasmick" + } + ], + "description": "Expands internal property references in PHP arrays file.", + "support": { + "issues": "https://github.com/grasmash/expander/issues", + "source": "https://github.com/grasmash/expander/tree/3.0.0" + }, + "time": "2022-05-10T13:14:49+00:00" + }, + { + "name": "grasmash/yaml-cli", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/grasmash/yaml-cli.git", + "reference": "09a8860566958a1576cc54bbe910a03477e54971" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/grasmash/yaml-cli/zipball/09a8860566958a1576cc54bbe910a03477e54971", + "reference": "09a8860566958a1576cc54bbe910a03477e54971", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3", + "php": ">=8.0", + "symfony/console": "^6 || ^7", + "symfony/filesystem": "^6 || ^7", + "symfony/yaml": "^6 || ^7" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.0" + }, + "bin": [ + "bin/yaml-cli" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Grasmash\\YamlCli\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Grasmick" + } + ], + "description": "A command line tool for reading and manipulating yaml files.", + "support": { + "issues": "https://github.com/grasmash/yaml-cli/issues", + "source": "https://github.com/grasmash/yaml-cli/tree/3.2.1" + }, + "time": "2024-04-23T02:10:57+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.8.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "f4152d9eb85c445fe1f992001d1748e8bec070d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4152d9eb85c445fe1f992001d1748e8bec070d2", + "reference": "f4152d9eb85c445fe1f992001d1748e8bec070d2", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^1.9.1 || ^2.6.3", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.8.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2024-07-18T11:12:18+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", + "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2024-07-18T10:29:17+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.6.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "6de29867b18790c0d2c846af4c13a24cc3ad56f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/6de29867b18790c0d2c846af4c13a24cc3ad56f3", + "reference": "6de29867b18790c0d2c846af4c13a24cc3ad56f3", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.6.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2024-07-18T09:59:12+00:00" + }, + { + "name": "illuminate/collections", + "version": "v11.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/collections.git", + "reference": "ba2cf689f7d75315f483334b4efc8c6af1d5159c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/collections/zipball/ba2cf689f7d75315f483334b4efc8c6af1d5159c", + "reference": "ba2cf689f7d75315f483334b4efc8c6af1d5159c", + "shasum": "" + }, + "require": { + "illuminate/conditionable": "^11.0", + "illuminate/contracts": "^11.0", + "illuminate/macroable": "^11.0", + "php": "^8.2" + }, + "suggest": { + "symfony/var-dumper": "Required to use the dump method (^7.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Collections package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2024-07-15T21:44:45+00:00" + }, + { + "name": "illuminate/conditionable", + "version": "v11.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/conditionable.git", + "reference": "362dd761b9920367bca1427a902158225e9e3a23" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/362dd761b9920367bca1427a902158225e9e3a23", + "reference": "362dd761b9920367bca1427a902158225e9e3a23", + "shasum": "" + }, + "require": { + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Conditionable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2024-06-28T20:10:30+00:00" + }, + { + "name": "illuminate/contracts", + "version": "v11.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/contracts.git", + "reference": "be935e9d9115a57be74d20176f43fa8a207029f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/be935e9d9115a57be74d20176f43fa8a207029f3", + "reference": "be935e9d9115a57be74d20176f43fa8a207029f3", + "shasum": "" + }, + "require": { + "php": "^8.2", + "psr/container": "^1.1.1|^2.0.1", + "psr/simple-cache": "^1.0|^2.0|^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Contracts\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Contracts package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2024-07-09T13:57:38+00:00" + }, + { + "name": "illuminate/macroable", + "version": "v11.16.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/macroable.git", + "reference": "e1cb9e51b9ed5d3c9bc1ab431d0a52fe42a990ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/e1cb9e51b9ed5d3c9bc1ab431d0a52fe42a990ed", + "reference": "e1cb9e51b9ed5d3c9bc1ab431d0a52fe42a990ed", + "shasum": "" + }, + "require": { + "php": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Macroable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2024-06-28T20:10:30+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.1.24", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "409b0b4305273472f3754826e68f4edbd0150149" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/409b0b4305273472f3754826e68f4edbd0150149", + "reference": "409b0b4305273472f3754826e68f4edbd0150149", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/collections": "^10.0|^11.0", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.1.24" + }, + "time": "2024-06-17T13:58:22+00:00" + }, + { + "name": "league/container", + "version": "4.2.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/container.git", + "reference": "ff346319ca1ff0e78277dc2311a42107cc1aab88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/container/zipball/ff346319ca1ff0e78277dc2311a42107cc1aab88", + "reference": "ff346319ca1ff0e78277dc2311a42107cc1aab88", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "psr/container": "^1.1 || ^2.0" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "replace": { + "orno/di": "~2.0" + }, + "require-dev": { + "nette/php-generator": "^3.4", + "nikic/php-parser": "^4.10", + "phpstan/phpstan": "^0.12.47", + "phpunit/phpunit": "^8.5.17", + "roave/security-advisories": "dev-latest", + "scrutinizer/ocular": "^1.8", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev", + "dev-4.x": "4.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Container\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Phil Bennett", + "email": "mail@philbennett.co.uk", + "role": "Developer" + } + ], + "description": "A fast and intuitive dependency injection container.", + "homepage": "https://github.com/thephpleague/container", + "keywords": [ + "container", + "dependency", + "di", + "injection", + "league", + "provider", + "service" + ], + "support": { + "issues": "https://github.com/thephpleague/container/issues", + "source": "https://github.com/thephpleague/container/tree/4.2.2" + }, + "funding": [ + { + "url": "https://github.com/philipobenito", + "type": "github" + } + ], + "time": "2024-03-13T13:12:53+00:00" + }, + { + "name": "masterminds/html5", + "version": "2.9.0", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.9.0" + }, + "time": "2024-03-31T07:05:07+00:00" + }, + { + "name": "mck89/peast", + "version": "v1.16.2", + "source": { + "type": "git", + "url": "https://github.com/mck89/peast.git", + "reference": "2791b08ffcc1862fe18eef85675da3aa58c406fe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mck89/peast/zipball/2791b08ffcc1862fe18eef85675da3aa58c406fe", + "reference": "2791b08ffcc1862fe18eef85675da3aa58c406fe", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.16.2-dev" + } + }, + "autoload": { + "psr-4": { + "Peast\\": "lib/Peast/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Marco Marchiò", + "email": "marco.mm89@gmail.com" + } + ], + "description": "Peast is PHP library that generates AST for JavaScript code", + "support": { + "issues": "https://github.com/mck89/peast/issues", + "source": "https://github.com/mck89/peast/tree/v1.16.2" + }, + "time": "2024-03-05T09:16:03+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.1.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" + }, + "time": "2024-07-01T20:03:41+00:00" + }, + { + "name": "pear/archive_tar", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/pear/Archive_Tar.git", + "reference": "b439c859564f5cbb0f64ad6002d0afe84a889602" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/b439c859564f5cbb0f64ad6002d0afe84a889602", + "reference": "b439c859564f5cbb0f64ad6002d0afe84a889602", + "shasum": "" + }, + "require": { + "pear/pear-core-minimal": "^1.10.0alpha2", + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-bz2": "Bz2 compression support.", + "ext-xz": "Lzma2 compression support.", + "ext-zlib": "Gzip compression support." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Archive_Tar": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "./" + ], + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Vincent Blavet", + "email": "vincent@phpconcept.net" + }, + { + "name": "Greg Beaver", + "email": "greg@chiaraquartet.net" + }, + { + "name": "Michiel Rook", + "email": "mrook@php.net" + } + ], + "description": "Tar file management class with compression support (gzip, bzip2, lzma2)", + "homepage": "https://github.com/pear/Archive_Tar", + "keywords": [ + "archive", + "tar" + ], + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Archive_Tar", + "source": "https://github.com/pear/Archive_Tar" + }, + "time": "2024-03-16T16:21:40+00:00" + }, + { + "name": "pear/console_getopt", + "version": "v1.4.3", + "source": { + "type": "git", + "url": "https://github.com/pear/Console_Getopt.git", + "reference": "a41f8d3e668987609178c7c4a9fe48fecac53fa0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/Console_Getopt/zipball/a41f8d3e668987609178c7c4a9fe48fecac53fa0", + "reference": "a41f8d3e668987609178c7c4a9fe48fecac53fa0", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Console": "./" + } + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "./" + ], + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Andrei Zmievski", + "email": "andrei@php.net", + "role": "Lead" + }, + { + "name": "Stig Bakken", + "email": "stig@php.net", + "role": "Developer" + }, + { + "name": "Greg Beaver", + "email": "cellog@php.net", + "role": "Helper" + } + ], + "description": "More info available on: http://pear.php.net/package/Console_Getopt", + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Console_Getopt", + "source": "https://github.com/pear/Console_Getopt" + }, + "time": "2019-11-20T18:27:48+00:00" + }, + { + "name": "pear/pear-core-minimal", + "version": "v1.10.15", + "source": { + "type": "git", + "url": "https://github.com/pear/pear-core-minimal.git", + "reference": "ce0adade8b97561656ace07cdaac4751c271ea8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/pear-core-minimal/zipball/ce0adade8b97561656ace07cdaac4751c271ea8c", + "reference": "ce0adade8b97561656ace07cdaac4751c271ea8c", + "shasum": "" + }, + "require": { + "pear/console_getopt": "~1.4", + "pear/pear_exception": "~1.0", + "php": ">=5.4" + }, + "replace": { + "rsky/pear-core-min": "self.version" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "src/" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@php.net", + "role": "Lead" + } + ], + "description": "Minimal set of PEAR core files to be used as composer dependency", + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=PEAR", + "source": "https://github.com/pear/pear-core-minimal" + }, + "time": "2024-03-16T18:41:45+00:00" + }, + { + "name": "pear/pear_exception", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/pear/PEAR_Exception.git", + "reference": "b14fbe2ddb0b9f94f5b24cf08783d599f776fff0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/b14fbe2ddb0b9f94f5b24cf08783d599f776fff0", + "reference": "b14fbe2ddb0b9f94f5b24cf08783d599f776fff0", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "<9" + }, + "type": "class", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "PEAR/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "." + ], + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Helgi Thormar", + "email": "dufuz@php.net" + }, + { + "name": "Greg Beaver", + "email": "cellog@php.net" + } + ], + "description": "The PEAR Exception base class.", + "homepage": "https://github.com/pear/PEAR_Exception", + "keywords": [ + "exception" + ], + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=PEAR_Exception", + "source": "https://github.com/pear/PEAR_Exception" + }, + "time": "2021-03-21T15:43:46+00:00" + }, + { + "name": "phootwork/collection", + "version": "v3.2.2", + "source": { + "type": "git", + "url": "https://github.com/phootwork/collection.git", + "reference": "46dde20420fba17766c89200bc3ff91d3e58eafa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phootwork/collection/zipball/46dde20420fba17766c89200bc3ff91d3e58eafa", + "reference": "46dde20420fba17766c89200bc3ff91d3e58eafa", + "shasum": "" + }, + "require": { + "phootwork/lang": "^3.0", + "php": ">=8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "phootwork\\collection\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Thomas Gossmann", + "homepage": "http://gos.si" + } + ], + "description": "The phootwork library fills gaps in the php language and provides better solutions than the existing ones php offers.", + "homepage": "https://phootwork.github.io/collection/", + "keywords": [ + "Array object", + "Text object", + "collection", + "collections", + "json", + "list", + "map", + "queue", + "set", + "stack", + "xml" + ], + "support": { + "issues": "https://github.com/phootwork/phootwork/issues", + "source": "https://github.com/phootwork/collection/tree/v3.2.2" + }, + "time": "2022-08-27T12:51:24+00:00" + }, + { + "name": "phootwork/lang", + "version": "v3.2.2", + "source": { + "type": "git", + "url": "https://github.com/phootwork/lang.git", + "reference": "baaf154ae7d521ebeee5e89105f5b12b0f234597" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phootwork/lang/zipball/baaf154ae7d521ebeee5e89105f5b12b0f234597", + "reference": "baaf154ae7d521ebeee5e89105f5b12b0f234597", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "symfony/polyfill-mbstring": "^1.12", + "symfony/polyfill-php81": "^1.22" + }, + "type": "library", + "autoload": { + "psr-4": { + "phootwork\\lang\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Thomas Gossmann", + "homepage": "http://gos.si" + } + ], + "description": "Missing PHP language constructs", + "homepage": "https://phootwork.github.io/lang/", + "keywords": [ + "array", + "comparator", + "comparison", + "string" + ], + "support": { + "issues": "https://github.com/phootwork/phootwork/issues", + "source": "https://github.com/phootwork/lang/tree/v3.2.2" + }, + "time": "2023-05-26T05:37:59+00:00" + }, + { + "name": "phpowermove/docblock", + "version": "v4.0", + "source": { + "type": "git", + "url": "https://github.com/phpowermove/docblock.git", + "reference": "a73f6e17b7d4e1b92ca5378c248c952c9fae7826" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpowermove/docblock/zipball/a73f6e17b7d4e1b92ca5378c248c952c9fae7826", + "reference": "a73f6e17b7d4e1b92ca5378c248c952c9fae7826", + "shasum": "" + }, + "require": { + "phootwork/collection": "^3.0", + "phootwork/lang": "^3.0", + "php": ">=8.0" + }, + "require-dev": { + "phootwork/php-cs-fixer-config": "^0.4", + "phpunit/phpunit": "^9.0", + "psalm/phar": "^4.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "phpowermove\\docblock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Thomas Gossmann", + "homepage": "http://gos.si" + } + ], + "description": "PHP Docblock parser and generator. An API to read and write Docblocks.", + "keywords": [ + "docblock", + "generator", + "parser" + ], + "support": { + "issues": "https://github.com/phpowermove/docblock/issues", + "source": "https://github.com/phpowermove/docblock/tree/v4.0" + }, + "time": "2021-09-22T16:57:06+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "psy/psysh", + "version": "v0.12.4", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "2fd717afa05341b4f8152547f142cd2f130f6818" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/2fd717afa05341b4f8152547f142cd2f130f6818", + "reference": "2fd717afa05341b4f8152547f142cd2f130f6818", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^5.0 || ^4.0", + "php": "^8.0 || ^7.4", + "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2" + }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.12.x-dev" + }, + "bamarni-bin": { + "bin-links": false, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "http://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.12.4" + }, + "time": "2024-06-10T01:18:23+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:30:58+00:00" + }, + { + "name": "symfony/console", + "version": "v6.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "6edb5363ec0c78ad4d48c5128ebf4d083d89d3a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/6edb5363ec0c78ad4d48c5128ebf4d083d89d3a9", + "reference": "6edb5363ec0c78ad4d48c5128ebf4d083d89d3a9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v6.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-28T09:49:33+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v6.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "a4df9dfe5da2d177af6643610c7bee2cb76a9f5e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/a4df9dfe5da2d177af6643610c7bee2cb76a9f5e", + "reference": "a4df9dfe5da2d177af6643610c7bee2cb76a9f5e", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.2.10|^7.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2", + "symfony/config": "<6.1", + "symfony/finder": "<5.4", + "symfony/proxy-manager-bridge": "<6.3", + "symfony/yaml": "<5.4" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "symfony/service-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "symfony/config": "^6.1|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows you to standardize and centralize the way objects are constructed in your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v6.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-19T10:45:28+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v6.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "c9b7cc075b3ab484239855622ca05cb0b99c13ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/c9b7cc075b3ab484239855622ca05cb0b99c13ec", + "reference": "c9b7cc075b3ab484239855622ca05cb0b99c13ec", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^5.4|^6.0|^7.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v6.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-21T16:04:15+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v6.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "8d7507f02b06e06815e56bb39aa0128e3806208b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/8d7507f02b06e06815e56bb39aa0128e3806208b", + "reference": "8d7507f02b06e06815e56bb39aa0128e3806208b", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:49:08+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "b51ef8059159330b74a4d52f68e671033c0fe463" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b51ef8059159330b74a4d52f68e671033c0fe463", + "reference": "b51ef8059159330b74a4d52f68e671033c0fe463", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^5.4|^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-28T09:49:33+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "3ef977a43883215d560a2cecb82ec8e62131471c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/3ef977a43883215d560a2cecb82ec8e62131471c", + "reference": "3ef977a43883215d560a2cecb82ec8e62131471c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v6.4.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:49:08+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v6.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "27de8cc95e11db7a50b027e71caaab9024545947" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/27de8cc95e11db7a50b027e71caaab9024545947", + "reference": "27de8cc95e11db7a50b027e71caaab9024545947", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "symfony/cache": "<6.3" + }, + "require-dev": { + "doctrine/dbal": "^2.13.1|^3|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.3|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/rate-limiter": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v6.4.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:49:08+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v6.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "cc4a9bec6e1bdd2405f40277a68a6ed1bb393005" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/cc4a9bec6e1bdd2405f40277a68a6ed1bb393005", + "reference": "cc4a9bec6e1bdd2405f40277a68a6ed1bb393005", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<5.4", + "symfony/cache": "<5.4", + "symfony/config": "<6.1", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<5.4", + "symfony/form": "<5.4", + "symfony/http-client": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<5.4", + "symfony/messenger": "<5.4", + "symfony/translation": "<5.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<5.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.3", + "twig/twig": "<2.13" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/clock": "^6.2|^7.0", + "symfony/config": "^6.1|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4.5|^6.0.5|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.4|^7.0.4", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^5.4|^6.4|^7.0", + "symfony/var-exporter": "^6.2|^7.0", + "twig/twig": "^2.13|^3.0.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v6.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-28T11:48:06+00:00" + }, + { + "name": "symfony/mailer", + "version": "v6.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "e2d56f180f5b8c5e7c0fbea872bb1f529b6d6d45" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/e2d56f180f5b8c5e7c0fbea872bb1f529b6d6d45", + "reference": "e2d56f180f5b8c5e7c0fbea872bb1f529b6d6d45", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.1", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/mime": "^6.2|^7.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/messenger": "<6.2", + "symfony/mime": "<6.2", + "symfony/twig-bridge": "<6.2.1" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/messenger": "^6.2|^7.0", + "symfony/twig-bridge": "^6.2|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v6.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-28T07:59:05+00:00" + }, + { + "name": "symfony/mime", + "version": "v6.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "7d048964877324debdcb4e0549becfa064a20d43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/7d048964877324debdcb4e0549becfa064a20d43", + "reference": "7d048964877324debdcb4e0549becfa064a20d43", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<5.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.4|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v6.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-28T09:49:33+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-iconv", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-iconv.git", + "reference": "cd4226d140ecd3d0f13d32ed0a4a095ffe871d2f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/cd4226d140ecd3d0f13d32ed0a4a095ffe871d2f", + "reference": "cd4226d140ecd3d0f13d32ed0a4a095ffe871d2f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-iconv": "*" + }, + "suggest": { + "ext-iconv": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Iconv\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Iconv extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "iconv", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "a287ed7475f85bf6f61890146edbc932c0fff919" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a287ed7475f85bf6f61890146edbc932c0fff919", + "reference": "a287ed7475f85bf6f61890146edbc932c0fff919", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "10112722600777e02d2745716b70c5db4ca70442" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/10112722600777e02d2745716b70c5db4ca70442", + "reference": "10112722600777e02d2745716b70c5db4ca70442", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php72/tree/v1.30.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-19T12:30:46+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T15:07:36+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/3fb075789fb91f9ad9af537c4012d523085bd5af", + "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.30.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-19T12:30:46+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "86fcae159633351e5fd145d1c47de6c528f8caff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/86fcae159633351e5fd145d1c47de6c528f8caff", + "reference": "86fcae159633351e5fd145d1c47de6c528f8caff", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-php80": "^1.14" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/process", + "version": "v6.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "8d92dd79149f29e89ee0f480254db595f6a6a2c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/8d92dd79149f29e89ee0f480254db595f6a6a2c5", + "reference": "8d92dd79149f29e89ee0f480254db595f6a6a2c5", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v6.4.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:49:08+00:00" + }, + { + "name": "symfony/psr-http-message-bridge", + "version": "v6.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/psr-http-message-bridge.git", + "reference": "23a162bd446b93948a2c2f6909d80ad06195be10" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/23a162bd446b93948a2c2f6909d80ad06195be10", + "reference": "23a162bd446b93948a2c2f6909d80ad06195be10", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/http-message": "^1.0|^2.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-kernel": "<6.2" + }, + "require-dev": { + "nyholm/psr7": "^1.1", + "php-http/discovery": "^1.15", + "psr/log": "^1.1.4|^2|^3", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/framework-bundle": "^6.2|^7.0", + "symfony/http-kernel": "^6.2|^7.0" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\PsrHttpMessage\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "PSR HTTP message bridge", + "homepage": "https://symfony.com", + "keywords": [ + "http", + "http-message", + "psr-17", + "psr-7" + ], + "support": { + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v6.4.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:51:39+00:00" + }, + { + "name": "symfony/routing", + "version": "v6.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "8a40d0f9b01f0fbb80885d3ce0ad6714fb603a58" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/8a40d0f9b01f0fbb80885d3ce0ad6714fb603a58", + "reference": "8a40d0f9b01f0fbb80885d3ce0ad6714fb603a58", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "doctrine/annotations": "<1.12", + "symfony/config": "<6.2", + "symfony/dependency-injection": "<5.4", + "symfony/yaml": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.12|^2", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.2|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v6.4.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:49:08+00:00" + }, + { + "name": "symfony/serializer", + "version": "v6.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/serializer.git", + "reference": "56ce31d19127e79647ac53387c7555bdcd5730ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/serializer/zipball/56ce31d19127e79647ac53387c7555bdcd5730ce", + "reference": "56ce31d19127e79647ac53387c7555bdcd5730ce", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "doctrine/annotations": "<1.12", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/dependency-injection": "<5.4", + "symfony/property-access": "<5.4", + "symfony/property-info": "<5.4.24|>=6,<6.2.11", + "symfony/uid": "<5.4", + "symfony/validator": "<6.4", + "symfony/yaml": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.12|^2", + "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0", + "seld/jsonlint": "^1.10", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/filesystem": "^5.4|^6.0|^7.0", + "symfony/form": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4.26|^6.3|^7.0", + "symfony/property-info": "^5.4.24|^6.2.11|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Serializer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/serializer/tree/v6.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-28T07:59:05+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/string", + "version": "v6.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "76792dbd99690a5ebef8050d9206c60c59e681d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/76792dbd99690a5ebef8050d9206c60c59e681d7", + "reference": "76792dbd99690a5ebef8050d9206c60c59e681d7", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-28T09:25:38+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", + "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/validator", + "version": "v6.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/validator.git", + "reference": "ee0a4d6a327a963aee094f730da238f7ea18cb01" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/validator/zipball/ee0a4d6a327a963aee094f730da238f7ea18cb01", + "reference": "ee0a4d6a327a963aee094f730da238f7ea18cb01", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php83": "^1.27", + "symfony/translation-contracts": "^2.5|^3" + }, + "conflict": { + "doctrine/annotations": "<1.13", + "doctrine/lexer": "<1.1", + "symfony/dependency-injection": "<5.4", + "symfony/expression-language": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/intl": "<5.4", + "symfony/property-info": "<5.4", + "symfony/translation": "<5.4.35|>=6.0,<6.3.12|>=6.4,<6.4.3|>=7.0,<7.0.3", + "symfony/yaml": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.13|^2", + "egulias/email-validator": "^2.1.10|^3|^4", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4.35|~6.3.12|^6.4.3|^7.0.3", + "symfony/yaml": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Validator\\": "" + }, + "exclude-from-classmap": [ + "/Tests/", + "/Resources/bin/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to validate values", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/validator/tree/v6.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-22T07:42:41+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v6.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "c31566e4ca944271cc8d8ac6887cbf31b8c6a172" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c31566e4ca944271cc8d8ac6887cbf31b8c6a172", + "reference": "c31566e4ca944271cc8d8ac6887cbf31b8c6a172", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^6.3|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", + "twig/twig": "^2.13|^3.0.4" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v6.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-27T13:23:14+00:00" + }, + { + "name": "symfony/var-exporter", + "version": "v6.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "f9a060622e0d93777b7f8687ec4860191e16802e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/f9a060622e0d93777b7f8687ec4860191e16802e", + "reference": "f9a060622e0d93777b7f8687ec4860191e16802e", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "require-dev": { + "symfony/property-access": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v6.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-24T15:53:56+00:00" + }, + { + "name": "symfony/yaml", + "version": "v6.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "52903de178d542850f6f341ba92995d3d63e60c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/52903de178d542850f6f341ba92995d3d63e60c9", + "reference": "52903de178d542850f6f341ba92995d3d63e60c9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v6.4.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:49:08+00:00" + }, + { + "name": "twig/twig", + "version": "v3.10.3", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "67f29781ffafa520b0bbfbd8384674b42db04572" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/67f29781ffafa520b0bbfbd8384674b42db04572", + "reference": "67f29781ffafa520b0bbfbd8384674b42db04572", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php80": "^1.22" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.10.3" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2024-05-16T10:04:27+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "drupal/migrate_skip_fields": 15, + "drush/drush": 10 + }, + "prefer-stable": true, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/drupal10/config/.gitkeep b/drupal10/config/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/drupal10/config/.htaccess b/drupal10/config/.htaccess new file mode 100644 index 0000000..b0dc540 --- /dev/null +++ b/drupal10/config/.htaccess @@ -0,0 +1,24 @@ +# Deny all requests from Apache 2.4+. + + Require all denied + + +# Deny all requests from Apache 2.0-2.2. + + Deny from all + + +# Turn off all options we don't need. +Options -Indexes -ExecCGI -Includes -MultiViews + +# Set the catch-all handler to prevent scripts from being executed. +SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006 + + # Override the handler again if we're run later in the evaluation list. + SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003 + + +# If we know how to do it safely, disable the PHP engine entirely. + + php_flag engine off + \ No newline at end of file diff --git a/drupal10/config/announcements_feed.settings.yml b/drupal10/config/announcements_feed.settings.yml new file mode 100644 index 0000000..2149bcc --- /dev/null +++ b/drupal10/config/announcements_feed.settings.yml @@ -0,0 +1,5 @@ +_core: + default_config_hash: 0G5pZBcxbg8ONYzNLd1RJIsvuFFewm9htnS4I-ABKJ8 +max_age: 86400 +cron_interval: 21600 +limit: 10 diff --git a/drupal10/config/automated_cron.settings.yml b/drupal10/config/automated_cron.settings.yml new file mode 100644 index 0000000..3fc5821 --- /dev/null +++ b/drupal10/config/automated_cron.settings.yml @@ -0,0 +1,3 @@ +_core: + default_config_hash: fUksROt4FfkAU9BV4hV2XvhTBSS2nTNrZS4U7S-tKrs +interval: 10800 diff --git a/drupal10/config/block.block.claro_breadcrumbs.yml b/drupal10/config/block.block.claro_breadcrumbs.yml new file mode 100644 index 0000000..96d50fd --- /dev/null +++ b/drupal10/config/block.block.claro_breadcrumbs.yml @@ -0,0 +1,22 @@ +uuid: 7df65449-6701-494e-ab1e-8978aa393636 +langcode: en +status: true +dependencies: + module: + - system + theme: + - claro +_core: + default_config_hash: NjcxOBrPOiK5-38t56DwFBDVY4yer7YSlbRWXFuHe7A +id: claro_breadcrumbs +theme: claro +region: breadcrumb +weight: 0 +provider: null +plugin: system_breadcrumb_block +settings: + id: system_breadcrumb_block + label: Breadcrumbs + label_display: '0' + provider: system +visibility: { } diff --git a/drupal10/config/block.block.claro_content.yml b/drupal10/config/block.block.claro_content.yml new file mode 100644 index 0000000..9408832 --- /dev/null +++ b/drupal10/config/block.block.claro_content.yml @@ -0,0 +1,22 @@ +uuid: e0b5023b-0806-45c1-83cb-22e4e241182a +langcode: en +status: true +dependencies: + module: + - system + theme: + - claro +_core: + default_config_hash: a0Yyx1GeyKarZ4T_yXQBR_ZFKnXiFLtxAb6gWLd8nr0 +id: claro_content +theme: claro +region: content +weight: 0 +provider: null +plugin: system_main_block +settings: + id: system_main_block + label: 'Main page content' + label_display: '0' + provider: system +visibility: { } diff --git a/drupal10/config/block.block.claro_help.yml b/drupal10/config/block.block.claro_help.yml new file mode 100644 index 0000000..1e6718b --- /dev/null +++ b/drupal10/config/block.block.claro_help.yml @@ -0,0 +1,22 @@ +uuid: 691d1c73-2a96-4e7d-ab50-a51a03f93663 +langcode: en +status: true +dependencies: + module: + - help + theme: + - claro +_core: + default_config_hash: jccFSSVqV0WCDb6NtML1VWAWTtDbZ-zn5YgTRMgMrIM +id: claro_help +theme: claro +region: help +weight: 0 +provider: null +plugin: help_block +settings: + id: help_block + label: Help + label_display: '0' + provider: help +visibility: { } diff --git a/drupal10/config/block.block.claro_help_search.yml b/drupal10/config/block.block.claro_help_search.yml new file mode 100644 index 0000000..50ad276 --- /dev/null +++ b/drupal10/config/block.block.claro_help_search.yml @@ -0,0 +1,32 @@ +uuid: 0de0d4cf-e6ce-41bc-8812-8181dca95338 +langcode: en +status: true +dependencies: + module: + - search + - system + theme: + - claro + enforced: + config: + - search.page.help_search +_core: + default_config_hash: 2ToeZLvlHKTeFY74gpgu1PejLoFyCECLO_gw6rAZwqw +id: claro_help_search +theme: claro +region: help +weight: -4 +provider: null +plugin: search_form_block +settings: + id: search_form_block + label: 'Search help' + label_display: visible + provider: search + page_id: help_search +visibility: + request_path: + id: request_path + negate: false + context_mapping: { } + pages: /admin/help diff --git a/drupal10/config/block.block.claro_local_actions.yml b/drupal10/config/block.block.claro_local_actions.yml new file mode 100644 index 0000000..4ac73df --- /dev/null +++ b/drupal10/config/block.block.claro_local_actions.yml @@ -0,0 +1,20 @@ +uuid: 25d71f89-ee95-410d-bc8c-a00a2e57ab06 +langcode: en +status: true +dependencies: + theme: + - claro +_core: + default_config_hash: CdXfDmRgAvms7EQovxxWPdYi0GitxeRbVtScYK16ZH0 +id: claro_local_actions +theme: claro +region: content +weight: -10 +provider: null +plugin: local_actions_block +settings: + id: local_actions_block + label: 'Primary admin actions' + label_display: '0' + provider: core +visibility: { } diff --git a/drupal10/config/block.block.claro_messages.yml b/drupal10/config/block.block.claro_messages.yml new file mode 100644 index 0000000..efe646c --- /dev/null +++ b/drupal10/config/block.block.claro_messages.yml @@ -0,0 +1,22 @@ +uuid: 2677e3c6-64a5-49fa-b32e-aa8145929e43 +langcode: en +status: true +dependencies: + module: + - system + theme: + - claro +_core: + default_config_hash: '-Ac3ISpIT0PQ-whzD7_dw0SdKi6dAbRFNWdSjOiVDqg' +id: claro_messages +theme: claro +region: highlighted +weight: 0 +provider: null +plugin: system_messages_block +settings: + id: system_messages_block + label: 'Status messages' + label_display: '0' + provider: system +visibility: { } diff --git a/drupal10/config/block.block.claro_page_title.yml b/drupal10/config/block.block.claro_page_title.yml new file mode 100644 index 0000000..c0d8bb2 --- /dev/null +++ b/drupal10/config/block.block.claro_page_title.yml @@ -0,0 +1,20 @@ +uuid: df4dcae2-9233-4494-a649-d2c43a16d0d3 +langcode: en +status: true +dependencies: + theme: + - claro +_core: + default_config_hash: fNwDdW063tk_ktzSWzZVeQS9wzvLooVO280BQ9WrsIs +id: claro_page_title +theme: claro +region: header +weight: -30 +provider: null +plugin: page_title_block +settings: + id: page_title_block + label: 'Page title' + label_display: '0' + provider: core +visibility: { } diff --git a/drupal10/config/block.block.claro_primary_local_tasks.yml b/drupal10/config/block.block.claro_primary_local_tasks.yml new file mode 100644 index 0000000..845f1ed --- /dev/null +++ b/drupal10/config/block.block.claro_primary_local_tasks.yml @@ -0,0 +1,22 @@ +uuid: 5d0269ac-94be-4c95-9132-f2d080a09943 +langcode: en +status: true +dependencies: + theme: + - claro +_core: + default_config_hash: ACjBZI5shAMiiUpsz-inLYVXDqNNXRnSzAWV3kV_8Hw +id: claro_primary_local_tasks +theme: claro +region: header +weight: 0 +provider: null +plugin: local_tasks_block +settings: + id: local_tasks_block + label: 'Primary tabs' + label_display: '0' + provider: core + primary: true + secondary: false +visibility: { } diff --git a/drupal10/config/block.block.claro_secondary_local_tasks.yml b/drupal10/config/block.block.claro_secondary_local_tasks.yml new file mode 100644 index 0000000..87665aa --- /dev/null +++ b/drupal10/config/block.block.claro_secondary_local_tasks.yml @@ -0,0 +1,22 @@ +uuid: 723ef8f9-3881-4ed0-bead-9df6472226d9 +langcode: en +status: true +dependencies: + theme: + - claro +_core: + default_config_hash: 2L0geP-ixCbCkEpW6BVF6H7vDUZN4ea07_Y9CociQm4 +id: claro_secondary_local_tasks +theme: claro +region: pre_content +weight: 0 +provider: null +plugin: local_tasks_block +settings: + id: local_tasks_block + label: 'Secondary tabs' + label_display: '0' + provider: core + primary: false + secondary: true +visibility: { } diff --git a/drupal10/config/block.block.olivero_account_menu.yml b/drupal10/config/block.block.olivero_account_menu.yml new file mode 100644 index 0000000..6f0468e --- /dev/null +++ b/drupal10/config/block.block.olivero_account_menu.yml @@ -0,0 +1,27 @@ +uuid: f406a961-81b8-4e2f-a5fc-3a0a75a2fef5 +langcode: en +status: true +dependencies: + config: + - system.menu.account + module: + - system + theme: + - olivero +_core: + default_config_hash: gmxYWWHmgbe0Pnv8y48ZLSLH5mEHejOjAP6RLxUfdzU +id: olivero_account_menu +theme: olivero +region: secondary_menu +weight: -4 +provider: null +plugin: 'system_menu_block:account' +settings: + id: 'system_menu_block:account' + label: 'User account menu' + label_display: '0' + provider: system + level: 1 + depth: 1 + expand_all_items: false +visibility: { } diff --git a/drupal10/config/block.block.olivero_breadcrumbs.yml b/drupal10/config/block.block.olivero_breadcrumbs.yml new file mode 100644 index 0000000..0ce08f7 --- /dev/null +++ b/drupal10/config/block.block.olivero_breadcrumbs.yml @@ -0,0 +1,22 @@ +uuid: 76f27f7e-ab15-48cf-a586-8f3f130534d9 +langcode: en +status: true +dependencies: + module: + - system + theme: + - olivero +_core: + default_config_hash: VhBzWb7lMRtIOg9G7VSw_0uopi-7zXeHq4vXqqV1HFE +id: olivero_breadcrumbs +theme: olivero +region: breadcrumb +weight: 0 +provider: null +plugin: system_breadcrumb_block +settings: + id: system_breadcrumb_block + label: Breadcrumbs + label_display: '0' + provider: system +visibility: { } diff --git a/drupal10/config/block.block.olivero_content.yml b/drupal10/config/block.block.olivero_content.yml new file mode 100644 index 0000000..89e0130 --- /dev/null +++ b/drupal10/config/block.block.olivero_content.yml @@ -0,0 +1,22 @@ +uuid: cd3916be-d592-4b40-b276-03d95e9aded1 +langcode: en +status: true +dependencies: + module: + - system + theme: + - olivero +_core: + default_config_hash: erQSEZF2XUjNmgTl0uNRBzmg18ZGXwUcw2FhApoeuHk +id: olivero_content +theme: olivero +region: content +weight: 0 +provider: null +plugin: system_main_block +settings: + id: system_main_block + label: 'Main page content' + label_display: '0' + provider: system +visibility: { } diff --git a/drupal10/config/block.block.olivero_help.yml b/drupal10/config/block.block.olivero_help.yml new file mode 100644 index 0000000..d559bf2 --- /dev/null +++ b/drupal10/config/block.block.olivero_help.yml @@ -0,0 +1,22 @@ +uuid: af7e8e41-3c35-47ec-83ea-27d570a7add1 +langcode: en +status: true +dependencies: + module: + - help + theme: + - olivero +_core: + default_config_hash: VfPFqqxfkomud5CO8DUijw85QIl9GIxh_nIxLOYESxg +id: olivero_help +theme: olivero +region: content_above +weight: 0 +provider: null +plugin: help_block +settings: + id: help_block + label: Help + label_display: '0' + provider: help +visibility: { } diff --git a/drupal10/config/block.block.olivero_main_menu.yml b/drupal10/config/block.block.olivero_main_menu.yml new file mode 100644 index 0000000..2644785 --- /dev/null +++ b/drupal10/config/block.block.olivero_main_menu.yml @@ -0,0 +1,27 @@ +uuid: b69aee3b-9597-4659-9594-ee43f8a8836f +langcode: en +status: true +dependencies: + config: + - system.menu.main + module: + - system + theme: + - olivero +_core: + default_config_hash: KWAiziL39uEzmOJEql_wbUP2RtqGceL3WM2CfxhMelE +id: olivero_main_menu +theme: olivero +region: primary_menu +weight: 0 +provider: null +plugin: 'system_menu_block:main' +settings: + id: 'system_menu_block:main' + label: 'Main navigation' + label_display: '0' + provider: system + level: 1 + depth: 2 + expand_all_items: true +visibility: { } diff --git a/drupal10/config/block.block.olivero_messages.yml b/drupal10/config/block.block.olivero_messages.yml new file mode 100644 index 0000000..b1bb2ca --- /dev/null +++ b/drupal10/config/block.block.olivero_messages.yml @@ -0,0 +1,22 @@ +uuid: b566f769-ef8e-4f37-8ddf-ddcb3e66d913 +langcode: en +status: true +dependencies: + module: + - system + theme: + - olivero +_core: + default_config_hash: BZ5tpW7H8X4PVGRm3MImTIHd2tN0eF7zOtp4SpRYUA0 +id: olivero_messages +theme: olivero +region: highlighted +weight: -5 +provider: null +plugin: system_messages_block +settings: + id: system_messages_block + label: 'Status messages' + label_display: '0' + provider: system +visibility: { } diff --git a/drupal10/config/block.block.olivero_page_title.yml b/drupal10/config/block.block.olivero_page_title.yml new file mode 100644 index 0000000..2684a92 --- /dev/null +++ b/drupal10/config/block.block.olivero_page_title.yml @@ -0,0 +1,20 @@ +uuid: 2b0799d9-446e-4f4a-ab77-46b368627d31 +langcode: en +status: true +dependencies: + theme: + - olivero +_core: + default_config_hash: 6aOgWsNTXjqrDm98TXSAjP6qd2nCijD1xw45MrnbK-Y +id: olivero_page_title +theme: olivero +region: content_above +weight: -5 +provider: null +plugin: page_title_block +settings: + id: page_title_block + label: 'Page title' + label_display: '0' + provider: core +visibility: { } diff --git a/drupal10/config/block.block.olivero_powered.yml b/drupal10/config/block.block.olivero_powered.yml new file mode 100644 index 0000000..4b0153d --- /dev/null +++ b/drupal10/config/block.block.olivero_powered.yml @@ -0,0 +1,22 @@ +uuid: d40fa2f7-3073-4d52-8c9f-56b86dbb72f5 +langcode: en +status: true +dependencies: + module: + - system + theme: + - olivero +_core: + default_config_hash: eYL19CLDyinGTWYQfBD1DswWzglEotE_kHnHx3AxTXM +id: olivero_powered +theme: olivero +region: footer_bottom +weight: 0 +provider: null +plugin: system_powered_by_block +settings: + id: system_powered_by_block + label: 'Powered by Drupal' + label_display: '0' + provider: system +visibility: { } diff --git a/drupal10/config/block.block.olivero_primary_admin_actions.yml b/drupal10/config/block.block.olivero_primary_admin_actions.yml new file mode 100644 index 0000000..b2b975e --- /dev/null +++ b/drupal10/config/block.block.olivero_primary_admin_actions.yml @@ -0,0 +1,20 @@ +uuid: 02121c29-75ea-40b1-8a90-cbf6c2c4462f +langcode: en +status: true +dependencies: + theme: + - olivero +_core: + default_config_hash: Q9_2whdOj1YIomfvsIfopROW4FT_X5pY0DjdOiOaQ5U +id: olivero_primary_admin_actions +theme: olivero +region: highlighted +weight: -5 +provider: null +plugin: local_actions_block +settings: + id: local_actions_block + label: 'Primary admin actions' + label_display: '0' + provider: core +visibility: { } diff --git a/drupal10/config/block.block.olivero_primary_local_tasks.yml b/drupal10/config/block.block.olivero_primary_local_tasks.yml new file mode 100644 index 0000000..f451976 --- /dev/null +++ b/drupal10/config/block.block.olivero_primary_local_tasks.yml @@ -0,0 +1,22 @@ +uuid: 7c547979-802d-4514-9468-3031c04df942 +langcode: en +status: true +dependencies: + theme: + - olivero +_core: + default_config_hash: nGE3EoPQQaQCuqTUtZgw0-KIzmrqdKDzdNQf2JyPUt4 +id: olivero_primary_local_tasks +theme: olivero +region: highlighted +weight: -4 +provider: null +plugin: local_tasks_block +settings: + id: local_tasks_block + label: 'Primary tabs' + label_display: '0' + provider: core + primary: true + secondary: false +visibility: { } diff --git a/drupal10/config/block.block.olivero_search_form_narrow.yml b/drupal10/config/block.block.olivero_search_form_narrow.yml new file mode 100644 index 0000000..605e4a8 --- /dev/null +++ b/drupal10/config/block.block.olivero_search_form_narrow.yml @@ -0,0 +1,23 @@ +uuid: 8d680e1b-71c1-42a9-9074-8f4236a45067 +langcode: en +status: true +dependencies: + module: + - search + theme: + - olivero +_core: + default_config_hash: yEBET0cqDbk8dkWzaJw-8CKft0961VBflsISoSR6Lj8 +id: olivero_search_form_narrow +theme: olivero +region: primary_menu +weight: -4 +provider: null +plugin: search_form_block +settings: + id: search_form_block + label: 'Search form (narrow)' + label_display: '0' + provider: search + page_id: '' +visibility: { } diff --git a/drupal10/config/block.block.olivero_search_form_wide.yml b/drupal10/config/block.block.olivero_search_form_wide.yml new file mode 100644 index 0000000..b057413 --- /dev/null +++ b/drupal10/config/block.block.olivero_search_form_wide.yml @@ -0,0 +1,23 @@ +uuid: 323b8ea4-d808-4d06-93c3-b070e53df21a +langcode: en +status: true +dependencies: + module: + - search + theme: + - olivero +_core: + default_config_hash: imMyHD6LYci0gtXq56qr9ZKGHzbEG9uFydrN5EhKtSU +id: olivero_search_form_wide +theme: olivero +region: secondary_menu +weight: -5 +provider: null +plugin: search_form_block +settings: + id: search_form_block + label: 'Search form (wide)' + label_display: '0' + provider: search + page_id: '' +visibility: { } diff --git a/drupal10/config/block.block.olivero_secondary_local_tasks.yml b/drupal10/config/block.block.olivero_secondary_local_tasks.yml new file mode 100644 index 0000000..9aad36c --- /dev/null +++ b/drupal10/config/block.block.olivero_secondary_local_tasks.yml @@ -0,0 +1,22 @@ +uuid: 4d60b6d1-79ff-41c4-9f36-1fe7f46407ba +langcode: en +status: true +dependencies: + theme: + - olivero +_core: + default_config_hash: ydSxdq7R66I8UMC460rOzlfzvlUL4VRbdwc6z9DWaUI +id: olivero_secondary_local_tasks +theme: olivero +region: highlighted +weight: -2 +provider: null +plugin: local_tasks_block +settings: + id: local_tasks_block + label: 'Secondary tabs' + label_display: '0' + provider: core + primary: false + secondary: true +visibility: { } diff --git a/drupal10/config/block.block.olivero_site_branding.yml b/drupal10/config/block.block.olivero_site_branding.yml new file mode 100644 index 0000000..8f6f651 --- /dev/null +++ b/drupal10/config/block.block.olivero_site_branding.yml @@ -0,0 +1,25 @@ +uuid: f2053ba7-b769-431c-b5b4-eb489658eb75 +langcode: en +status: true +dependencies: + module: + - system + theme: + - olivero +_core: + default_config_hash: n_nlgjggHVfQt2H__zvLOKB2YtjPDbQ5tHijF9LE1aM +id: olivero_site_branding +theme: olivero +region: header +weight: 0 +provider: null +plugin: system_branding_block +settings: + id: system_branding_block + label: 'Site branding' + label_display: '0' + provider: system + use_site_logo: true + use_site_name: true + use_site_slogan: false +visibility: { } diff --git a/drupal10/config/block.block.olivero_syndicate.yml b/drupal10/config/block.block.olivero_syndicate.yml new file mode 100644 index 0000000..e3d48f2 --- /dev/null +++ b/drupal10/config/block.block.olivero_syndicate.yml @@ -0,0 +1,23 @@ +uuid: 1caf1d0c-7c6b-430e-bb9c-8787021a9a50 +langcode: en +status: true +dependencies: + module: + - node + theme: + - olivero +_core: + default_config_hash: 0gq3VPg-_UM69FCCWurLFIrrnIjC2HLKhwo9iQNtcUo +id: olivero_syndicate +theme: olivero +region: social +weight: 0 +provider: null +plugin: node_syndicate_block +settings: + id: node_syndicate_block + label: 'RSS feed' + label_display: '0' + provider: node + block_count: 10 +visibility: { } diff --git a/drupal10/config/block_content.type.basic.yml b/drupal10/config/block_content.type.basic.yml new file mode 100644 index 0000000..074b28d --- /dev/null +++ b/drupal10/config/block_content.type.basic.yml @@ -0,0 +1,8 @@ +uuid: 47c172da-e2bb-4e1f-baf5-9db7876702af +langcode: en +status: true +dependencies: { } +id: basic +label: 'Basic block' +revision: false +description: 'A basic block contains a title and a body.' diff --git a/drupal10/config/claro.settings.yml b/drupal10/config/claro.settings.yml new file mode 100644 index 0000000..6bba58b --- /dev/null +++ b/drupal10/config/claro.settings.yml @@ -0,0 +1,3 @@ +third_party_settings: + shortcut: + module_link: true diff --git a/drupal10/config/contact.form.personal.yml b/drupal10/config/contact.form.personal.yml new file mode 100644 index 0000000..1170aa9 --- /dev/null +++ b/drupal10/config/contact.form.personal.yml @@ -0,0 +1,13 @@ +uuid: d5d66dc9-31f4-4d87-99df-0af63ec0e5d5 +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: jonvgt3CkUM2eMLTFwWfHileWWDC4YtXCuIlCahTk_I +id: personal +label: 'Personal contact form' +recipients: { } +reply: '' +weight: 0 +message: 'Your message has been sent.' +redirect: '' diff --git a/drupal10/config/contact.settings.yml b/drupal10/config/contact.settings.yml new file mode 100644 index 0000000..1c949a6 --- /dev/null +++ b/drupal10/config/contact.settings.yml @@ -0,0 +1,7 @@ +_core: + default_config_hash: U69DBeuvXuNVOC15rVNaBjDPK2fWFbo9v4takdYSSO8 +default_form: feedback +flood: + limit: 5 + interval: 3600 +user_default_enabled: true diff --git a/drupal10/config/core.date_format.fallback.yml b/drupal10/config/core.date_format.fallback.yml new file mode 100644 index 0000000..f0903e0 --- /dev/null +++ b/drupal10/config/core.date_format.fallback.yml @@ -0,0 +1,10 @@ +uuid: 1cdd2f68-493b-4d2d-8e7a-aa1e7aa5961b +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: 7klS5IWXrwzVaPpYZFAs6wcx8U2FF1X73OfrtTsvuvE +id: fallback +label: 'Fallback date format' +locked: true +pattern: 'D, m/d/Y - H:i' diff --git a/drupal10/config/core.date_format.html_date.yml b/drupal10/config/core.date_format.html_date.yml new file mode 100644 index 0000000..e4c29bb --- /dev/null +++ b/drupal10/config/core.date_format.html_date.yml @@ -0,0 +1,10 @@ +uuid: 92308926-bf17-4314-9433-20099dddd800 +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: EOQltUQPmgc6UQ2rcJ4Xi_leCEJj5ui0TR-12duS-Tk +id: html_date +label: 'HTML Date' +locked: true +pattern: Y-m-d diff --git a/drupal10/config/core.date_format.html_datetime.yml b/drupal10/config/core.date_format.html_datetime.yml new file mode 100644 index 0000000..5b34a5a --- /dev/null +++ b/drupal10/config/core.date_format.html_datetime.yml @@ -0,0 +1,10 @@ +uuid: f1ab4e0f-8838-40db-b263-e0df6c9c5998 +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: jxfClwZIRXIdcvMrE--WkcZxDGUVoOIE3Sm2NRZlFuE +id: html_datetime +label: 'HTML Datetime' +locked: true +pattern: 'Y-m-d\TH:i:sO' diff --git a/drupal10/config/core.date_format.html_month.yml b/drupal10/config/core.date_format.html_month.yml new file mode 100644 index 0000000..703e064 --- /dev/null +++ b/drupal10/config/core.date_format.html_month.yml @@ -0,0 +1,10 @@ +uuid: 61781b84-2d1e-4c03-a9cf-47f714a26861 +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: Z7KuCUwM_WdTNvLcoltuX3_8d-s-8FZkTN6KgNwF0eM +id: html_month +label: 'HTML Month' +locked: true +pattern: Y-m diff --git a/drupal10/config/core.date_format.html_time.yml b/drupal10/config/core.date_format.html_time.yml new file mode 100644 index 0000000..d790a3e --- /dev/null +++ b/drupal10/config/core.date_format.html_time.yml @@ -0,0 +1,10 @@ +uuid: efc43262-b612-4a5e-8557-13d2f583a0d3 +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: M7yqicYkU36hRy5p9drAaGBBihhUD1OyujFrAaQ93ZE +id: html_time +label: 'HTML Time' +locked: true +pattern: 'H:i:s' diff --git a/drupal10/config/core.date_format.html_week.yml b/drupal10/config/core.date_format.html_week.yml new file mode 100644 index 0000000..760445e --- /dev/null +++ b/drupal10/config/core.date_format.html_week.yml @@ -0,0 +1,10 @@ +uuid: 9043d627-d38c-4d89-8a47-f23450657aef +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: wKD4WsoV_wFgv2vgI4mcAAFSIzrye17ykzdwrnApkfY +id: html_week +label: 'HTML Week' +locked: true +pattern: Y-\WW diff --git a/drupal10/config/core.date_format.html_year.yml b/drupal10/config/core.date_format.html_year.yml new file mode 100644 index 0000000..cf3c8c1 --- /dev/null +++ b/drupal10/config/core.date_format.html_year.yml @@ -0,0 +1,10 @@ +uuid: 9af249db-dff3-4b76-a49a-8c4b24e7782d +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: OjekiQuX9RbVQ2_8jOHBL94RgYLePqX7wpfNGgcQzrk +id: html_year +label: 'HTML Year' +locked: true +pattern: 'Y' diff --git a/drupal10/config/core.date_format.html_yearless_date.yml b/drupal10/config/core.date_format.html_yearless_date.yml new file mode 100644 index 0000000..dba271c --- /dev/null +++ b/drupal10/config/core.date_format.html_yearless_date.yml @@ -0,0 +1,10 @@ +uuid: b0e559ed-55a1-4fd3-b8bd-58154d397a6c +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: 5VpawMrKPEPCkoO4YpPa0TDFO2dgiIHfTziJtwlmUxc +id: html_yearless_date +label: 'HTML Yearless date' +locked: true +pattern: m-d diff --git a/drupal10/config/core.date_format.long.yml b/drupal10/config/core.date_format.long.yml new file mode 100644 index 0000000..fb887c8 --- /dev/null +++ b/drupal10/config/core.date_format.long.yml @@ -0,0 +1,10 @@ +uuid: af5f318c-a819-458c-b14d-2a31e85176ee +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: og8sWXhBuHbLMw3CoiBEZjgqSyhFBFmcbUW_wLcfNbo +id: long +label: 'Default long date' +locked: false +pattern: 'l, F j, Y - H:i' diff --git a/drupal10/config/core.date_format.medium.yml b/drupal10/config/core.date_format.medium.yml new file mode 100644 index 0000000..a0f1459 --- /dev/null +++ b/drupal10/config/core.date_format.medium.yml @@ -0,0 +1,10 @@ +uuid: c2136cff-869c-4e79-89b0-97fb571dc6a0 +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: nzL5d024NjXIX_8TlT6uFAu973lmfkmHklJC-2i9rAE +id: medium +label: 'Default medium date' +locked: false +pattern: 'D, m/d/Y - H:i' diff --git a/drupal10/config/core.date_format.olivero_medium.yml b/drupal10/config/core.date_format.olivero_medium.yml new file mode 100644 index 0000000..45d0104 --- /dev/null +++ b/drupal10/config/core.date_format.olivero_medium.yml @@ -0,0 +1,13 @@ +uuid: 3861a8bd-5f00-4f25-92d1-f4310e56b6b7 +langcode: en +status: true +dependencies: + enforced: + theme: + - olivero +_core: + default_config_hash: Mt6cmxUbDZ9XxD6p25WQ8tj3_JcX8ylfcddwZc8gcAE +id: olivero_medium +label: 'Olivero Medium' +locked: false +pattern: 'j F, Y' diff --git a/drupal10/config/core.date_format.short.yml b/drupal10/config/core.date_format.short.yml new file mode 100644 index 0000000..3d5a8eb --- /dev/null +++ b/drupal10/config/core.date_format.short.yml @@ -0,0 +1,10 @@ +uuid: 8ec39912-40f1-4a53-ab9a-3b7de063c75d +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: AlzeyytA8InBgxIG9H2UDJYs3CG98Zj6yRsDKmlbZwA +id: short +label: 'Default short date' +locked: false +pattern: 'm/d/Y - H:i' diff --git a/drupal10/config/core.entity_form_display.block_content.basic.default.yml b/drupal10/config/core.entity_form_display.block_content.basic.default.yml new file mode 100644 index 0000000..5e79b66 --- /dev/null +++ b/drupal10/config/core.entity_form_display.block_content.basic.default.yml @@ -0,0 +1,33 @@ +uuid: 65ae2182-fc82-4349-aa0e-1e871b0ff7b9 +langcode: en +status: true +dependencies: + config: + - block_content.type.basic + - field.field.block_content.basic.body + module: + - text +id: block_content.basic.default +targetEntityType: block_content +bundle: basic +mode: default +content: + body: + type: text_textarea_with_summary + weight: 26 + region: content + settings: + rows: 9 + summary_rows: 3 + placeholder: '' + show_summary: false + third_party_settings: { } + info: + type: string_textfield + weight: -5 + region: content + settings: + size: 60 + placeholder: '' + third_party_settings: { } +hidden: { } diff --git a/drupal10/config/core.entity_form_mode.media.media_library.yml b/drupal10/config/core.entity_form_mode.media.media_library.yml new file mode 100644 index 0000000..bc7b4c5 --- /dev/null +++ b/drupal10/config/core.entity_form_mode.media.media_library.yml @@ -0,0 +1,16 @@ +uuid: 17fe9931-7321-4f86-8ee9-0bbfb48c1300 +langcode: en +status: true +dependencies: + module: + - media + enforced: + module: + - media_library +_core: + default_config_hash: 04_dAqpWYP1WmsXZ7IXJ7-yarCvNddD10EUkBDtIFy4 +id: media.media_library +label: 'Media library' +description: '' +targetEntityType: media +cache: true diff --git a/drupal10/config/core.entity_form_mode.user.register.yml b/drupal10/config/core.entity_form_mode.user.register.yml new file mode 100644 index 0000000..24f439d --- /dev/null +++ b/drupal10/config/core.entity_form_mode.user.register.yml @@ -0,0 +1,13 @@ +uuid: 0ddc0536-7d2e-4993-89e0-fd13b93764a7 +langcode: en +status: true +dependencies: + module: + - user +_core: + default_config_hash: 5pE_4hurqtIlZN3XDi7eTo5RG13BMG0Rh9HYlRI3h8U +id: user.register +label: Register +description: '' +targetEntityType: user +cache: true diff --git a/drupal10/config/core.entity_view_display.block_content.basic.default.yml b/drupal10/config/core.entity_view_display.block_content.basic.default.yml new file mode 100644 index 0000000..f0db096 --- /dev/null +++ b/drupal10/config/core.entity_view_display.block_content.basic.default.yml @@ -0,0 +1,22 @@ +uuid: ff8da10a-781a-48d4-a2c5-ab528d4dbfad +langcode: en +status: true +dependencies: + config: + - block_content.type.basic + - field.field.block_content.basic.body + module: + - text +id: block_content.basic.default +targetEntityType: block_content +bundle: basic +mode: default +content: + body: + type: text_default + label: hidden + settings: { } + third_party_settings: { } + weight: 0 + region: content +hidden: { } diff --git a/drupal10/config/core.entity_view_mode.block.token.yml b/drupal10/config/core.entity_view_mode.block.token.yml new file mode 100644 index 0000000..7e85e4f --- /dev/null +++ b/drupal10/config/core.entity_view_mode.block.token.yml @@ -0,0 +1,11 @@ +uuid: 7c9548d8-e913-4c90-92f4-3b0876411cac +langcode: en +status: true +dependencies: + module: + - block +id: block.token +label: Token +description: '' +targetEntityType: block +cache: true diff --git a/drupal10/config/core.entity_view_mode.block_content.full.yml b/drupal10/config/core.entity_view_mode.block_content.full.yml new file mode 100644 index 0000000..963e769 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.block_content.full.yml @@ -0,0 +1,13 @@ +uuid: a4deb950-03c3-47c0-a230-204a840784c0 +langcode: en +status: false +dependencies: + module: + - block_content +_core: + default_config_hash: Q7yUUYeRLByl-MCGveKKF_KhAtNICLCMJuKWfugCvso +id: block_content.full +label: Full +description: '' +targetEntityType: block_content +cache: true diff --git a/drupal10/config/core.entity_view_mode.block_content.token.yml b/drupal10/config/core.entity_view_mode.block_content.token.yml new file mode 100644 index 0000000..06ae325 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.block_content.token.yml @@ -0,0 +1,11 @@ +uuid: c83e4e0b-8c0b-4a71-a43f-c8318dcefb12 +langcode: en +status: true +dependencies: + module: + - block_content +id: block_content.token +label: Token +description: '' +targetEntityType: block_content +cache: true diff --git a/drupal10/config/core.entity_view_mode.contact_message.token.yml b/drupal10/config/core.entity_view_mode.contact_message.token.yml new file mode 100644 index 0000000..414bb07 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.contact_message.token.yml @@ -0,0 +1,11 @@ +uuid: 210118cd-ea82-4d0c-b67b-8419a10ed232 +langcode: en +status: true +dependencies: + module: + - contact +id: contact_message.token +label: Token +description: '' +targetEntityType: contact_message +cache: true diff --git a/drupal10/config/core.entity_view_mode.file.token.yml b/drupal10/config/core.entity_view_mode.file.token.yml new file mode 100644 index 0000000..0857ebb --- /dev/null +++ b/drupal10/config/core.entity_view_mode.file.token.yml @@ -0,0 +1,11 @@ +uuid: 1f4bba13-448b-4eaa-a9d5-f8d7b425903f +langcode: en +status: true +dependencies: + module: + - file +id: file.token +label: Token +description: '' +targetEntityType: file +cache: true diff --git a/drupal10/config/core.entity_view_mode.media.full.yml b/drupal10/config/core.entity_view_mode.media.full.yml new file mode 100644 index 0000000..df4c002 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.media.full.yml @@ -0,0 +1,13 @@ +uuid: 4063ea91-cb85-44b1-a755-b025906ce392 +langcode: en +status: false +dependencies: + module: + - media +_core: + default_config_hash: dTfAUHooYV0uOVPO3saGpgv-c5PppJXDwxvwRTJOycM +id: media.full +label: 'Full content' +description: '' +targetEntityType: media +cache: true diff --git a/drupal10/config/core.entity_view_mode.media.media_library.yml b/drupal10/config/core.entity_view_mode.media.media_library.yml new file mode 100644 index 0000000..45e6b61 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.media.media_library.yml @@ -0,0 +1,16 @@ +uuid: 9b3f1a84-e5d8-43df-b4ad-de80b4488545 +langcode: en +status: true +dependencies: + module: + - media + enforced: + module: + - media_library +_core: + default_config_hash: 04_dAqpWYP1WmsXZ7IXJ7-yarCvNddD10EUkBDtIFy4 +id: media.media_library +label: 'Media library' +description: '' +targetEntityType: media +cache: true diff --git a/drupal10/config/core.entity_view_mode.media.token.yml b/drupal10/config/core.entity_view_mode.media.token.yml new file mode 100644 index 0000000..a8e92e1 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.media.token.yml @@ -0,0 +1,11 @@ +uuid: 07bded36-2aca-47cf-a7a3-32e04f4dfb8d +langcode: en +status: true +dependencies: + module: + - media +id: media.token +label: Token +description: '' +targetEntityType: media +cache: true diff --git a/drupal10/config/core.entity_view_mode.menu_link_content.token.yml b/drupal10/config/core.entity_view_mode.menu_link_content.token.yml new file mode 100644 index 0000000..26df47a --- /dev/null +++ b/drupal10/config/core.entity_view_mode.menu_link_content.token.yml @@ -0,0 +1,11 @@ +uuid: 8c3b661b-4668-4e92-bcff-a422e9c94bf7 +langcode: en +status: true +dependencies: + module: + - menu_link_content +id: menu_link_content.token +label: Token +description: '' +targetEntityType: menu_link_content +cache: true diff --git a/drupal10/config/core.entity_view_mode.node.full.yml b/drupal10/config/core.entity_view_mode.node.full.yml new file mode 100644 index 0000000..050c92a --- /dev/null +++ b/drupal10/config/core.entity_view_mode.node.full.yml @@ -0,0 +1,13 @@ +uuid: e3cfad0a-a499-43dd-84e2-ddfde46cbe73 +langcode: en +status: false +dependencies: + module: + - node +_core: + default_config_hash: QJ2aZ1xfVf59aq6Pz5X7fyUOa2HxuCoTwQ_RQjoulAU +id: node.full +label: 'Full content' +description: '' +targetEntityType: node +cache: true diff --git a/drupal10/config/core.entity_view_mode.node.rss.yml b/drupal10/config/core.entity_view_mode.node.rss.yml new file mode 100644 index 0000000..a3166e7 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.node.rss.yml @@ -0,0 +1,13 @@ +uuid: 0b2c8678-f292-4693-b566-f4c129bed813 +langcode: en +status: false +dependencies: + module: + - node +_core: + default_config_hash: l8fiAFE3Kng_6bhLlUDnVTkTDzXWxzYFrCWTrngVXEA +id: node.rss +label: RSS +description: '' +targetEntityType: node +cache: true diff --git a/drupal10/config/core.entity_view_mode.node.search_index.yml b/drupal10/config/core.entity_view_mode.node.search_index.yml new file mode 100644 index 0000000..fdca55c --- /dev/null +++ b/drupal10/config/core.entity_view_mode.node.search_index.yml @@ -0,0 +1,13 @@ +uuid: 3bf9834a-3a73-4097-9e6c-03af019ca637 +langcode: en +status: false +dependencies: + module: + - node +_core: + default_config_hash: r_A0T3aTqGDwLyvoH7wLps-0PM--RHlS8UsiJe_Ac64 +id: node.search_index +label: 'Search index' +description: '' +targetEntityType: node +cache: true diff --git a/drupal10/config/core.entity_view_mode.node.search_result.yml b/drupal10/config/core.entity_view_mode.node.search_result.yml new file mode 100644 index 0000000..cc45a49 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.node.search_result.yml @@ -0,0 +1,13 @@ +uuid: b50ecec7-0126-4f6e-9f29-5098b5e84955 +langcode: en +status: false +dependencies: + module: + - node +_core: + default_config_hash: d8wBEm7XvJ6H3S0IneDD9PfTBklPIH7GMpxElVemPf8 +id: node.search_result +label: 'Search result highlighting input' +description: '' +targetEntityType: node +cache: true diff --git a/drupal10/config/core.entity_view_mode.node.teaser.yml b/drupal10/config/core.entity_view_mode.node.teaser.yml new file mode 100644 index 0000000..b0d7a71 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.node.teaser.yml @@ -0,0 +1,13 @@ +uuid: e28743f2-c2c8-4de7-91ee-4d63108b48a7 +langcode: en +status: true +dependencies: + module: + - node +_core: + default_config_hash: KgGJDZFpMaz_8bTv6fN1bXS3Qi5LWmRJI9R53kEGsNQ +id: node.teaser +label: Teaser +description: '' +targetEntityType: node +cache: true diff --git a/drupal10/config/core.entity_view_mode.node.token.yml b/drupal10/config/core.entity_view_mode.node.token.yml new file mode 100644 index 0000000..62116fe --- /dev/null +++ b/drupal10/config/core.entity_view_mode.node.token.yml @@ -0,0 +1,11 @@ +uuid: f2dae8df-fb24-4d3b-8077-74773d6387b9 +langcode: en +status: true +dependencies: + module: + - node +id: node.token +label: Token +description: '' +targetEntityType: node +cache: true diff --git a/drupal10/config/core.entity_view_mode.paragraph.preview.yml b/drupal10/config/core.entity_view_mode.paragraph.preview.yml new file mode 100644 index 0000000..5cc1216 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.paragraph.preview.yml @@ -0,0 +1,13 @@ +uuid: 7ef7911b-55a0-4ce8-927c-2301067cb2a7 +langcode: en +status: true +dependencies: + module: + - paragraphs +_core: + default_config_hash: h3BeHVei4Lnyqbkao3YiF4KqoY-DhRvUNfEgKG8Rgjg +id: paragraph.preview +label: Preview +description: '' +targetEntityType: paragraph +cache: true diff --git a/drupal10/config/core.entity_view_mode.paragraph.token.yml b/drupal10/config/core.entity_view_mode.paragraph.token.yml new file mode 100644 index 0000000..6cca1f7 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.paragraph.token.yml @@ -0,0 +1,11 @@ +uuid: 1ad0a586-ca9d-4fda-9a0b-913cc767566b +langcode: en +status: true +dependencies: + module: + - paragraphs +id: paragraph.token +label: Token +description: '' +targetEntityType: paragraph +cache: true diff --git a/drupal10/config/core.entity_view_mode.path_alias.token.yml b/drupal10/config/core.entity_view_mode.path_alias.token.yml new file mode 100644 index 0000000..a3c6890 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.path_alias.token.yml @@ -0,0 +1,11 @@ +uuid: aba5e100-db40-4940-be6a-4603d5b6c5ed +langcode: en +status: true +dependencies: + module: + - path_alias +id: path_alias.token +label: Token +description: '' +targetEntityType: path_alias +cache: true diff --git a/drupal10/config/core.entity_view_mode.shortcut.token.yml b/drupal10/config/core.entity_view_mode.shortcut.token.yml new file mode 100644 index 0000000..baaaac6 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.shortcut.token.yml @@ -0,0 +1,11 @@ +uuid: 2461d81b-21ef-4164-a353-79d4660621d5 +langcode: en +status: true +dependencies: + module: + - shortcut +id: shortcut.token +label: Token +description: '' +targetEntityType: shortcut +cache: true diff --git a/drupal10/config/core.entity_view_mode.taxonomy_term.full.yml b/drupal10/config/core.entity_view_mode.taxonomy_term.full.yml new file mode 100644 index 0000000..96767a2 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.taxonomy_term.full.yml @@ -0,0 +1,13 @@ +uuid: eeba63b1-81ed-4b3c-8def-1125b7cefe51 +langcode: en +status: true +dependencies: + module: + - taxonomy +_core: + default_config_hash: iukUENpf8CFvjZbGGacKX_Ges0-lU9z6zvsd32P6kbo +id: taxonomy_term.full +label: 'Taxonomy term page' +description: '' +targetEntityType: taxonomy_term +cache: true diff --git a/drupal10/config/core.entity_view_mode.taxonomy_term.token.yml b/drupal10/config/core.entity_view_mode.taxonomy_term.token.yml new file mode 100644 index 0000000..6a66040 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.taxonomy_term.token.yml @@ -0,0 +1,11 @@ +uuid: a3e6a04d-653a-43ea-949e-23c430bc0377 +langcode: en +status: true +dependencies: + module: + - taxonomy +id: taxonomy_term.token +label: Token +description: '' +targetEntityType: taxonomy_term +cache: true diff --git a/drupal10/config/core.entity_view_mode.user.compact.yml b/drupal10/config/core.entity_view_mode.user.compact.yml new file mode 100644 index 0000000..103f8e8 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.user.compact.yml @@ -0,0 +1,13 @@ +uuid: cfffd2eb-01b5-4fea-891d-6fef985769b4 +langcode: en +status: true +dependencies: + module: + - user +_core: + default_config_hash: TtD7OuGskOsQfoGyxXkrdtllBpR37J19d5BMQDZWJgA +id: user.compact +label: Compact +description: '' +targetEntityType: user +cache: true diff --git a/drupal10/config/core.entity_view_mode.user.full.yml b/drupal10/config/core.entity_view_mode.user.full.yml new file mode 100644 index 0000000..27f3860 --- /dev/null +++ b/drupal10/config/core.entity_view_mode.user.full.yml @@ -0,0 +1,13 @@ +uuid: af8c3b21-1a95-4c2f-beea-18298c990842 +langcode: en +status: false +dependencies: + module: + - user +_core: + default_config_hash: ZbXunWS_xAvMZXFfinyvClDAb_RCVLt7gAzE3v16E-Q +id: user.full +label: 'User account' +description: '' +targetEntityType: user +cache: true diff --git a/drupal10/config/core.entity_view_mode.user.token.yml b/drupal10/config/core.entity_view_mode.user.token.yml new file mode 100644 index 0000000..ac4ca3f --- /dev/null +++ b/drupal10/config/core.entity_view_mode.user.token.yml @@ -0,0 +1,11 @@ +uuid: 4815393e-fe4d-4c0b-8a05-3f0446db15b6 +langcode: en +status: true +dependencies: + module: + - user +id: user.token +label: Token +description: '' +targetEntityType: user +cache: true diff --git a/drupal10/config/core.extension.yml b/drupal10/config/core.extension.yml new file mode 100644 index 0000000..957bcc7 --- /dev/null +++ b/drupal10/config/core.extension.yml @@ -0,0 +1,68 @@ +_core: + default_config_hash: 4GIX5Esnc_umpXUBj4IIocRX7Mt5fPhm4AgXfE3E56E +module: + address: 0 + announcements_feed: 0 + automated_cron: 0 + big_pipe: 0 + block: 0 + block_content: 0 + breakpoint: 0 + ckeditor5: 0 + config: 0 + contact: 0 + contextual: 0 + datetime: 0 + dblog: 0 + dynamic_page_cache: 0 + editor: 0 + entity_reference_revisions: 0 + field: 0 + field_group: 0 + field_group_migrate: 0 + field_ui: 0 + file: 0 + filter: 0 + help: 0 + history: 0 + image: 0 + link: 0 + media: 0 + media_library: 0 + menu_link_content: 0 + menu_ui: 0 + migrate: 0 + migrate_drupal: 0 + migrate_plus: 0 + migrate_skip_fields: 0 + migrate_upgrade: 0 + migrate_url2link: 0 + mysql: 0 + node: 0 + options: 0 + page_cache: 0 + path: 0 + path_alias: 0 + phpass: 0 + search: 0 + shortcut: 0 + social_link_field: 0 + system: 0 + tag1_migration_config: 0 + taxonomy: 0 + telephone: 0 + text: 0 + token: 0 + toolbar: 0 + update: 0 + user: 0 + views_ui: 0 + pathauto: 1 + views: 10 + paragraphs: 11 + tag1_profile: 1000 +theme: + stark: 0 + olivero: 0 + claro: 0 +profile: tag1_profile diff --git a/drupal10/config/core.menu.static_menu_link_overrides.yml b/drupal10/config/core.menu.static_menu_link_overrides.yml new file mode 100644 index 0000000..2261a22 --- /dev/null +++ b/drupal10/config/core.menu.static_menu_link_overrides.yml @@ -0,0 +1,3 @@ +_core: + default_config_hash: jdY7AU0tU-QsjmiOw3W8vwpYMb-By--_MSFgbqKUTYM +definitions: { } diff --git a/drupal10/config/dblog.settings.yml b/drupal10/config/dblog.settings.yml new file mode 100644 index 0000000..fbd17ea --- /dev/null +++ b/drupal10/config/dblog.settings.yml @@ -0,0 +1,3 @@ +_core: + default_config_hash: e883aGsrt1wFrsydlYU584PZONCSfRy0DtkZ9KzHb58 +row_limit: 1000 diff --git a/drupal10/config/field.field.block_content.basic.body.yml b/drupal10/config/field.field.block_content.basic.body.yml new file mode 100644 index 0000000..c9a0fb8 --- /dev/null +++ b/drupal10/config/field.field.block_content.basic.body.yml @@ -0,0 +1,24 @@ +uuid: 086ca743-af62-48ca-838f-7a45a47c087e +langcode: en +status: true +dependencies: + config: + - block_content.type.basic + - field.storage.block_content.body + module: + - text +id: block_content.basic.body +field_name: body +entity_type: block_content +bundle: basic +label: Body +description: '' +required: false +translatable: true +default_value: { } +default_value_callback: '' +settings: + display_summary: false + required_summary: false + allowed_formats: { } +field_type: text_with_summary diff --git a/drupal10/config/field.settings.yml b/drupal10/config/field.settings.yml new file mode 100644 index 0000000..2225b8f --- /dev/null +++ b/drupal10/config/field.settings.yml @@ -0,0 +1,3 @@ +_core: + default_config_hash: nJk0TAQBzlNo52ehiHI7bIEPLGi0BYqZvPdEn7Chfu0 +purge_batch_size: 50 diff --git a/drupal10/config/field.storage.block_content.body.yml b/drupal10/config/field.storage.block_content.body.yml new file mode 100644 index 0000000..46dcb95 --- /dev/null +++ b/drupal10/config/field.storage.block_content.body.yml @@ -0,0 +1,21 @@ +uuid: 39f3e784-92ac-4e8b-8293-e8682206a08d +langcode: en +status: true +dependencies: + module: + - block_content + - text +_core: + default_config_hash: eS0snV_L3dx9shtWRTzm5eblwOJ7qKWC9IE-4GMTDFc +id: block_content.body +field_name: body +entity_type: block_content +type: text_with_summary +settings: { } +module: text +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: true +custom_storage: false diff --git a/drupal10/config/field.storage.node.body.yml b/drupal10/config/field.storage.node.body.yml new file mode 100644 index 0000000..a6516f3 --- /dev/null +++ b/drupal10/config/field.storage.node.body.yml @@ -0,0 +1,21 @@ +uuid: b08455f0-c601-4849-b14c-a936a90f2bd7 +langcode: en +status: true +dependencies: + module: + - node + - text +_core: + default_config_hash: EBUo7qOWqaiZaQ_RC9sLY5IoDKphS34v77VIHSACmVY +id: node.body +field_name: body +entity_type: node +type: text_with_summary +settings: { } +module: text +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: true +custom_storage: false diff --git a/drupal10/config/field_ui.settings.yml b/drupal10/config/field_ui.settings.yml new file mode 100644 index 0000000..365450f --- /dev/null +++ b/drupal10/config/field_ui.settings.yml @@ -0,0 +1,3 @@ +_core: + default_config_hash: Q1nMi90W6YQxKzZAgJQw7Ag9U4JrsEUwkomF0lhvbIM +field_prefix: field_ diff --git a/drupal10/config/file.settings.yml b/drupal10/config/file.settings.yml new file mode 100644 index 0000000..fdb62ff --- /dev/null +++ b/drupal10/config/file.settings.yml @@ -0,0 +1,15 @@ +_core: + default_config_hash: 2bwjZB1IjEYbppgZT3g7YW_5h_LDZYNa3DaDEfWX82U +description: + type: textfield + length: 128 +icon: + directory: core/modules/file/icons +make_unused_managed_files_temporary: false +filename_sanitization: + transliterate: false + replace_whitespace: false + replace_non_alphanumeric: false + deduplicate_separators: false + lowercase: false + replacement_character: '-' diff --git a/drupal10/config/filter.format.plain_text.yml b/drupal10/config/filter.format.plain_text.yml new file mode 100644 index 0000000..c586f07 --- /dev/null +++ b/drupal10/config/filter.format.plain_text.yml @@ -0,0 +1,29 @@ +uuid: 09dcc227-982d-4d89-965e-c617ade6697a +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: JLMwODO0epnA_H-jFlH9ezVfa5YBJniRv3MmLqYNl_Q +name: 'Plain text' +format: plain_text +weight: 10 +filters: + filter_autop: + id: filter_autop + provider: filter + status: true + weight: 0 + settings: { } + filter_html_escape: + id: filter_html_escape + provider: filter + status: true + weight: -10 + settings: { } + filter_url: + id: filter_url + provider: filter + status: true + weight: 0 + settings: + filter_url_length: 72 diff --git a/drupal10/config/filter.settings.yml b/drupal10/config/filter.settings.yml new file mode 100644 index 0000000..9ce2939 --- /dev/null +++ b/drupal10/config/filter.settings.yml @@ -0,0 +1,4 @@ +_core: + default_config_hash: FiPjM3WdB__ruFA7B6TLwni_UcZbmek5G4b2dxQItxA +fallback_format: plain_text +always_show_fallback_choice: false diff --git a/drupal10/config/image.settings.yml b/drupal10/config/image.settings.yml new file mode 100644 index 0000000..b757490 --- /dev/null +++ b/drupal10/config/image.settings.yml @@ -0,0 +1,5 @@ +_core: + default_config_hash: k-yDFHbqNfpe-Srg4sdCSqaosCl2D8uwyEY5esF8gEw +preview_image: core/modules/image/sample.png +allow_insecure_derivatives: false +suppress_itok_output: false diff --git a/drupal10/config/image.style.large.yml b/drupal10/config/image.style.large.yml new file mode 100644 index 0000000..3468c35 --- /dev/null +++ b/drupal10/config/image.style.large.yml @@ -0,0 +1,23 @@ +uuid: 0f5346c0-e314-415d-b34b-b3fb976d552b +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: rDR2BOewa2UFH9yG4tVvrGxEVv8U7LQo-RLkJhFpERs +name: large +label: 'Large (480×480)' +effects: + ddd73aa7-4bd6-4c85-b600-bdf2b1628d1d: + uuid: ddd73aa7-4bd6-4c85-b600-bdf2b1628d1d + id: image_scale + weight: 0 + data: + width: 480 + height: 480 + upscale: false + 6e8fe467-84c1-4ef0-a73b-7eccf1cc20e8: + uuid: 6e8fe467-84c1-4ef0-a73b-7eccf1cc20e8 + id: image_convert + weight: 2 + data: + extension: webp diff --git a/drupal10/config/image.style.media_library.yml b/drupal10/config/image.style.media_library.yml new file mode 100644 index 0000000..54cc720 --- /dev/null +++ b/drupal10/config/image.style.media_library.yml @@ -0,0 +1,26 @@ +uuid: 382f2379-187f-4426-9162-5961b11a61a3 +langcode: en +status: true +dependencies: + enforced: + module: + - media_library +_core: + default_config_hash: FxMdscEA4aDH0KPM73HIZtVn3zIAgC1kQ3CkBw26HYs +name: media_library +label: 'Media Library thumbnail (220×220)' +effects: + 75b076a8-1234-4b42-85db-bf377c4d8d5f: + uuid: 75b076a8-1234-4b42-85db-bf377c4d8d5f + id: image_scale + weight: 0 + data: + width: 220 + height: 220 + upscale: false + 1021da71-fc2a-43d0-be5d-efaf1c79e2ea: + uuid: 1021da71-fc2a-43d0-be5d-efaf1c79e2ea + id: image_convert + weight: 2 + data: + extension: webp diff --git a/drupal10/config/image.style.medium.yml b/drupal10/config/image.style.medium.yml new file mode 100644 index 0000000..ada06ff --- /dev/null +++ b/drupal10/config/image.style.medium.yml @@ -0,0 +1,23 @@ +uuid: 40cad417-30d7-4a7b-be59-aa459f9a7c03 +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: dlar76VBuGj5iMGTruB_uMZX8VbivXt9_QLemaG2q4E +name: medium +label: 'Medium (220×220)' +effects: + bddf0d06-42f9-4c75-a700-a33cafa25ea0: + uuid: bddf0d06-42f9-4c75-a700-a33cafa25ea0 + id: image_scale + weight: 0 + data: + width: 220 + height: 220 + upscale: false + c410ed2f-aa30-4d9c-a224-d2865d9188cd: + uuid: c410ed2f-aa30-4d9c-a224-d2865d9188cd + id: image_convert + weight: 2 + data: + extension: webp diff --git a/drupal10/config/image.style.thumbnail.yml b/drupal10/config/image.style.thumbnail.yml new file mode 100644 index 0000000..f470614 --- /dev/null +++ b/drupal10/config/image.style.thumbnail.yml @@ -0,0 +1,23 @@ +uuid: ce8165d8-c7c0-4542-bb52-421ed13b039f +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: BgeBV-xOFINMsz-y2thm45EGesj6bRM6DTLZ6ce3V9I +name: thumbnail +label: 'Thumbnail (100×100)' +effects: + 1cfec298-8620-4749-b100-ccb6c4500779: + uuid: 1cfec298-8620-4749-b100-ccb6c4500779 + id: image_scale + weight: 0 + data: + width: 100 + height: 100 + upscale: false + c4eb9942-2c9e-4a81-949f-6161a44b6559: + uuid: c4eb9942-2c9e-4a81-949f-6161a44b6559 + id: image_convert + weight: 2 + data: + extension: webp diff --git a/drupal10/config/image.style.wide.yml b/drupal10/config/image.style.wide.yml new file mode 100644 index 0000000..d4f7803 --- /dev/null +++ b/drupal10/config/image.style.wide.yml @@ -0,0 +1,23 @@ +uuid: 5447caf6-bd53-4867-8636-5920139f9115 +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: XHh3ATMH7z4ljwmzdndM47qNMkgLnoYsP98rGxVgCOw +name: wide +label: 'Wide (1090)' +effects: + 09959c15-59ce-4f6d-90df-e2d7cf32bce5: + uuid: 09959c15-59ce-4f6d-90df-e2d7cf32bce5 + id: image_scale + weight: 1 + data: + width: 1090 + height: null + upscale: false + 294c5f76-42a4-43ce-82c2-81c2f4723da0: + uuid: 294c5f76-42a4-43ce-82c2-81c2f4723da0 + id: image_convert + weight: 2 + data: + extension: webp diff --git a/drupal10/config/media.settings.yml b/drupal10/config/media.settings.yml new file mode 100644 index 0000000..d54f078 --- /dev/null +++ b/drupal10/config/media.settings.yml @@ -0,0 +1,6 @@ +_core: + default_config_hash: WCFqLQAxMw1weToDJEhfnW1Z-iOF7cqMdL8X7YTFxBA +icon_base_uri: 'public://media-icons/generic' +iframe_domain: null +oembed_providers_url: 'https://oembed.com/providers.json' +standalone_url: false diff --git a/drupal10/config/media_library.settings.yml b/drupal10/config/media_library.settings.yml new file mode 100644 index 0000000..3a1479a --- /dev/null +++ b/drupal10/config/media_library.settings.yml @@ -0,0 +1,3 @@ +_core: + default_config_hash: _3gQsCnZELUjUUqHk8SSh8bXnx7TZwN95vctAeVJG60 +advanced_ui: false diff --git a/drupal10/config/menu_ui.settings.yml b/drupal10/config/menu_ui.settings.yml new file mode 100644 index 0000000..0b987fa --- /dev/null +++ b/drupal10/config/menu_ui.settings.yml @@ -0,0 +1,3 @@ +_core: + default_config_hash: SqMarzIjxC3F8dZo9FEOxfqDKD_sdW1tbcFTV1BA2zU +override_parent_selector: false diff --git a/drupal10/config/migrate_drupal.settings.yml b/drupal10/config/migrate_drupal.settings.yml new file mode 100644 index 0000000..13b1994 --- /dev/null +++ b/drupal10/config/migrate_drupal.settings.yml @@ -0,0 +1,7 @@ +_core: + default_config_hash: 1daEO2inZc1i3d0Sn-ADIq9mUIU7tSLCxn579NT6f2g +enforce_source_module_tags: + - 'Drupal 6' + - 'Drupal 7' +follow_up_migration_tags: + - 'Follow-up migration' diff --git a/drupal10/config/migrate_plus.migration_group.default.yml b/drupal10/config/migrate_plus.migration_group.default.yml new file mode 100644 index 0000000..69a66f4 --- /dev/null +++ b/drupal10/config/migrate_plus.migration_group.default.yml @@ -0,0 +1,10 @@ +uuid: c0560e78-99d8-41b1-b551-599c9a78fc9e +langcode: en +status: true +dependencies: { } +id: default +label: Default +description: 'A container for any migrations not explicitly assigned to a group.' +source_type: null +module: null +shared_configuration: null diff --git a/drupal10/config/node.settings.yml b/drupal10/config/node.settings.yml new file mode 100644 index 0000000..3a9d97e --- /dev/null +++ b/drupal10/config/node.settings.yml @@ -0,0 +1,3 @@ +_core: + default_config_hash: 2OMXCScXUOLSYID9-phjO4q36nnnaMWNUlDxEqZzG1U +use_admin_theme: false diff --git a/drupal10/config/olivero.settings.yml b/drupal10/config/olivero.settings.yml new file mode 100644 index 0000000..3949455 --- /dev/null +++ b/drupal10/config/olivero.settings.yml @@ -0,0 +1,17 @@ +_core: + default_config_hash: 1TswGK46jyu77aIM7Z-0JVQs5bxHmo-gtgrvrQGMXxc +favicon: + use_default: true +features: + comment_user_picture: true + comment_user_verification: true + favicon: true + node_user_picture: false +logo: + use_default: false +third_party_settings: + shortcut: + module_link: true +mobile_menu_all_widths: 0 +site_branding_bg_color: default +base_primary_color: '#1b9ae4' diff --git a/drupal10/config/paragraphs.settings.yml b/drupal10/config/paragraphs.settings.yml new file mode 100644 index 0000000..c43733b --- /dev/null +++ b/drupal10/config/paragraphs.settings.yml @@ -0,0 +1,3 @@ +_core: + default_config_hash: 7eR0sk71Eol86r_A7BMqn5_46wzenh5J1O5vZRCGKv8 +show_unpublished: true diff --git a/drupal10/config/pathauto.settings.yml b/drupal10/config/pathauto.settings.yml new file mode 100644 index 0000000..acfd943 --- /dev/null +++ b/drupal10/config/pathauto.settings.yml @@ -0,0 +1,22 @@ +_core: + default_config_hash: SwvLp8snyPEExF41CaJJYdPUVomofLqtXvwciHc4cPg +enabled_entity_types: + - user +punctuation: + hyphen: 1 +verbose: false +separator: '-' +max_length: 100 +max_component_length: 100 +transliterate: true +reduce_ascii: false +case: true +ignore_words: 'a, an, as, at, before, but, by, for, from, is, in, into, like, of, off, on, onto, per, since, than, the, this, that, to, up, via, with' +update_action: 2 +safe_tokens: + - alias + - path + - join-path + - login-url + - url + - url-brief diff --git a/drupal10/config/search.page.help_search.yml b/drupal10/config/search.page.help_search.yml new file mode 100644 index 0000000..9ae8380 --- /dev/null +++ b/drupal10/config/search.page.help_search.yml @@ -0,0 +1,14 @@ +uuid: e250bbc7-5aac-4de0-ac6e-731ffb4d06b8 +langcode: en +status: true +dependencies: + module: + - help +_core: + default_config_hash: RZ-bcSekNSsAbIPLW7Gmyd3uUjIOSrPvnb8RCCZYJm8 +id: help_search +label: Help +path: help +weight: 0 +plugin: help_search +configuration: { } diff --git a/drupal10/config/search.page.node_search.yml b/drupal10/config/search.page.node_search.yml new file mode 100644 index 0000000..9086b32 --- /dev/null +++ b/drupal10/config/search.page.node_search.yml @@ -0,0 +1,15 @@ +uuid: 9ef9473d-9ecd-4616-a7f9-52df15f819e5 +langcode: en +status: true +dependencies: + module: + - node +_core: + default_config_hash: 97tvtzGOa8_flb22CzSjgtm_YkiGMHvEBO-6q2K9V_U +id: node_search +label: Content +path: node +weight: -10 +plugin: node_search +configuration: + rankings: { } diff --git a/drupal10/config/search.page.user_search.yml b/drupal10/config/search.page.user_search.yml new file mode 100644 index 0000000..f54e21d --- /dev/null +++ b/drupal10/config/search.page.user_search.yml @@ -0,0 +1,14 @@ +uuid: 67a72ee1-8619-40cf-8056-728cc79f95e8 +langcode: en +status: true +dependencies: + module: + - user +_core: + default_config_hash: k3aUaZXGDuhkek2TZIee0PApOPTvYZLadziekdyHA5A +id: user_search +label: Users +path: user +weight: 0 +plugin: user_search +configuration: { } diff --git a/drupal10/config/search.settings.yml b/drupal10/config/search.settings.yml new file mode 100644 index 0000000..f864b47 --- /dev/null +++ b/drupal10/config/search.settings.yml @@ -0,0 +1,22 @@ +_core: + default_config_hash: hvVxL1G-ZCxaq32IZws0YsfuhvaDiQE_np-0g35KjUk +and_or_limit: 7 +default_page: node_search +index: + cron_limit: 100 + overlap_cjk: true + minimum_word_size: 3 + tag_weights: + h1: 25 + h2: 18 + h3: 15 + h4: 14 + h5: 9 + h6: 6 + u: 3 + b: 3 + i: 3 + strong: 3 + em: 3 + a: 10 +logging: false diff --git a/drupal10/config/shortcut.set.default.yml b/drupal10/config/shortcut.set.default.yml new file mode 100644 index 0000000..c050ee3 --- /dev/null +++ b/drupal10/config/shortcut.set.default.yml @@ -0,0 +1,8 @@ +uuid: 7f0210ec-1149-42d9-be50-fdb7d4dec8ab +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: U5VlGjd_SfV0Qm_EfnaynOfc549cNscFAx48JfYoMRI +id: default +label: Default diff --git a/drupal10/config/social_link_field.settings.yml b/drupal10/config/social_link_field.settings.yml new file mode 100644 index 0000000..30a426f --- /dev/null +++ b/drupal10/config/social_link_field.settings.yml @@ -0,0 +1,3 @@ +_core: + default_config_hash: _eR1prkFQkOwAwVLC3c_p7tDh6eJt_PkeKKAdpe-f-w +attached_fa: true diff --git a/drupal10/config/system.action.media_delete_action.yml b/drupal10/config/system.action.media_delete_action.yml new file mode 100644 index 0000000..a94147e --- /dev/null +++ b/drupal10/config/system.action.media_delete_action.yml @@ -0,0 +1,13 @@ +uuid: a4746c6f-be70-475c-aca3-c22426c7fec1 +langcode: en +status: true +dependencies: + module: + - media +_core: + default_config_hash: FrZy1tmuXJcOxhXlBoI1Hsnen5TT-9OCC1iolWH84go +id: media_delete_action +label: 'Delete media' +type: media +plugin: 'entity:delete_action:media' +configuration: { } diff --git a/drupal10/config/system.action.media_publish_action.yml b/drupal10/config/system.action.media_publish_action.yml new file mode 100644 index 0000000..0122651 --- /dev/null +++ b/drupal10/config/system.action.media_publish_action.yml @@ -0,0 +1,13 @@ +uuid: a32c2c24-0348-4568-aca3-a3d3d752fc25 +langcode: en +status: true +dependencies: + module: + - media +_core: + default_config_hash: nh83qNNrmWE-CDdHz2MdFOAk60T9mzv3R-MaKfZR2jw +id: media_publish_action +label: 'Publish media' +type: media +plugin: 'entity:publish_action:media' +configuration: { } diff --git a/drupal10/config/system.action.media_save_action.yml b/drupal10/config/system.action.media_save_action.yml new file mode 100644 index 0000000..5bd949b --- /dev/null +++ b/drupal10/config/system.action.media_save_action.yml @@ -0,0 +1,13 @@ +uuid: f23f7a8f-f777-43b1-aed2-1138296302c7 +langcode: en +status: true +dependencies: + module: + - media +_core: + default_config_hash: VVyUA6PIaVeGtcIbgEWqJ6SYDiJdReBeojFswURFpKs +id: media_save_action +label: 'Save media' +type: media +plugin: 'entity:save_action:media' +configuration: { } diff --git a/drupal10/config/system.action.media_unpublish_action.yml b/drupal10/config/system.action.media_unpublish_action.yml new file mode 100644 index 0000000..8d151ae --- /dev/null +++ b/drupal10/config/system.action.media_unpublish_action.yml @@ -0,0 +1,13 @@ +uuid: 15c25ae5-1a15-439c-9064-52a05bf06e03 +langcode: en +status: true +dependencies: + module: + - media +_core: + default_config_hash: CsK6TseQ2DatEbZgbd30swOlZ28_HHwAESU2LvEnWq0 +id: media_unpublish_action +label: 'Unpublish media' +type: media +plugin: 'entity:unpublish_action:media' +configuration: { } diff --git a/drupal10/config/system.action.node_delete_action.yml b/drupal10/config/system.action.node_delete_action.yml new file mode 100644 index 0000000..f047930 --- /dev/null +++ b/drupal10/config/system.action.node_delete_action.yml @@ -0,0 +1,13 @@ +uuid: 87c8b853-4f4e-41ee-a93f-b4ed17bfae03 +langcode: en +status: true +dependencies: + module: + - node +_core: + default_config_hash: t43OqwzP3CTbcAagSsWKNy6KwMm_zShXo3c4-So6rQg +id: node_delete_action +label: 'Delete content' +type: node +plugin: 'entity:delete_action:node' +configuration: { } diff --git a/drupal10/config/system.action.node_make_sticky_action.yml b/drupal10/config/system.action.node_make_sticky_action.yml new file mode 100644 index 0000000..12e995e --- /dev/null +++ b/drupal10/config/system.action.node_make_sticky_action.yml @@ -0,0 +1,13 @@ +uuid: b9b013cb-fb96-4877-9244-fe095ae0df05 +langcode: en +status: true +dependencies: + module: + - node +_core: + default_config_hash: sOb26JSy3fGpWkvR0WYN6_hMqj_6d1rvbvrkzp1yya0 +id: node_make_sticky_action +label: 'Make content sticky' +type: node +plugin: node_make_sticky_action +configuration: { } diff --git a/drupal10/config/system.action.node_make_unsticky_action.yml b/drupal10/config/system.action.node_make_unsticky_action.yml new file mode 100644 index 0000000..d15fcee --- /dev/null +++ b/drupal10/config/system.action.node_make_unsticky_action.yml @@ -0,0 +1,13 @@ +uuid: 82348ee8-c6b4-4c1f-9ed8-6844a29da57c +langcode: en +status: true +dependencies: + module: + - node +_core: + default_config_hash: lDM9mvIGAu8Sw8rt-uCO4Sr7yX5VPrDPxYcawkbKd6k +id: node_make_unsticky_action +label: 'Make content unsticky' +type: node +plugin: node_make_unsticky_action +configuration: { } diff --git a/drupal10/config/system.action.node_promote_action.yml b/drupal10/config/system.action.node_promote_action.yml new file mode 100644 index 0000000..e7b0a93 --- /dev/null +++ b/drupal10/config/system.action.node_promote_action.yml @@ -0,0 +1,13 @@ +uuid: 6e6532c2-959d-4db5-beaa-07df8d9015a2 +langcode: en +status: true +dependencies: + module: + - node +_core: + default_config_hash: N0RDBTqiK4dKoN4p4oW2j0SGWycdHyALUe9M-Ofp89U +id: node_promote_action +label: 'Promote content to front page' +type: node +plugin: node_promote_action +configuration: { } diff --git a/drupal10/config/system.action.node_publish_action.yml b/drupal10/config/system.action.node_publish_action.yml new file mode 100644 index 0000000..7606bef --- /dev/null +++ b/drupal10/config/system.action.node_publish_action.yml @@ -0,0 +1,13 @@ +uuid: 14641b6c-4a58-4e62-b087-6fc40af1d561 +langcode: en +status: true +dependencies: + module: + - node +_core: + default_config_hash: 2B9uF8NL5gutNKSdPRAhhAsDWFZZG1PJOBmx0aBGd_0 +id: node_publish_action +label: 'Publish content' +type: node +plugin: 'entity:publish_action:node' +configuration: { } diff --git a/drupal10/config/system.action.node_save_action.yml b/drupal10/config/system.action.node_save_action.yml new file mode 100644 index 0000000..495bfe3 --- /dev/null +++ b/drupal10/config/system.action.node_save_action.yml @@ -0,0 +1,13 @@ +uuid: 7b602d9f-a301-4e61-a66c-a205ca7f1dae +langcode: en +status: true +dependencies: + module: + - node +_core: + default_config_hash: LhdsoZPL_pFas2fjaAWue4zvrQ_tEVofLYtcNec-JGM +id: node_save_action +label: 'Save content' +type: node +plugin: 'entity:save_action:node' +configuration: { } diff --git a/drupal10/config/system.action.node_unpromote_action.yml b/drupal10/config/system.action.node_unpromote_action.yml new file mode 100644 index 0000000..675cb4c --- /dev/null +++ b/drupal10/config/system.action.node_unpromote_action.yml @@ -0,0 +1,13 @@ +uuid: f85b4a8f-9029-4a34-82d4-23499798f22e +langcode: en +status: true +dependencies: + module: + - node +_core: + default_config_hash: JBptjnfuOMtsdKygklXxoOgeOCTMtQxlkymjnnj-cC0 +id: node_unpromote_action +label: 'Remove content from front page' +type: node +plugin: node_unpromote_action +configuration: { } diff --git a/drupal10/config/system.action.node_unpublish_action.yml b/drupal10/config/system.action.node_unpublish_action.yml new file mode 100644 index 0000000..11a5b75 --- /dev/null +++ b/drupal10/config/system.action.node_unpublish_action.yml @@ -0,0 +1,13 @@ +uuid: 9ed8318c-e47d-4c94-943e-8662c160f750 +langcode: en +status: true +dependencies: + module: + - node +_core: + default_config_hash: C7X8h9FWlwkQ9y5mnU2JzgaZICAdc6HFbPVbhvjlAYE +id: node_unpublish_action +label: 'Unpublish content' +type: node +plugin: 'entity:unpublish_action:node' +configuration: { } diff --git a/drupal10/config/system.action.pathauto_update_alias_node.yml b/drupal10/config/system.action.pathauto_update_alias_node.yml new file mode 100644 index 0000000..5242516 --- /dev/null +++ b/drupal10/config/system.action.pathauto_update_alias_node.yml @@ -0,0 +1,16 @@ +uuid: 774e8e79-f959-4624-b829-1dd52a74f310 +langcode: en +status: true +dependencies: + module: + - pathauto + enforced: + module: + - node +_core: + default_config_hash: lno8QThS348UX-kaUsagJtCnuPHKLXYnTQiF_9HSDWA +id: pathauto_update_alias_node +label: 'Update URL alias' +type: node +plugin: pathauto_update_alias +configuration: { } diff --git a/drupal10/config/system.action.pathauto_update_alias_user.yml b/drupal10/config/system.action.pathauto_update_alias_user.yml new file mode 100644 index 0000000..1762654 --- /dev/null +++ b/drupal10/config/system.action.pathauto_update_alias_user.yml @@ -0,0 +1,16 @@ +uuid: ff0f03c9-4db3-4823-ada5-5b899f29ed2d +langcode: en +status: true +dependencies: + module: + - pathauto + enforced: + module: + - user +_core: + default_config_hash: x_ok_ZsfA4Xk4B_hVW3O4-3PcNoK57nXLz_Dlletidg +id: pathauto_update_alias_user +label: 'Update URL alias' +type: user +plugin: pathauto_update_alias +configuration: { } diff --git a/drupal10/config/system.action.taxonomy_term_publish_action.yml b/drupal10/config/system.action.taxonomy_term_publish_action.yml new file mode 100644 index 0000000..c7efd92 --- /dev/null +++ b/drupal10/config/system.action.taxonomy_term_publish_action.yml @@ -0,0 +1,13 @@ +uuid: 6a5356e2-57ac-46da-bf31-86a36a9318f3 +langcode: en +status: true +dependencies: + module: + - taxonomy +_core: + default_config_hash: DoVt_VGgVLcDD4XmVbSFzr0K17SJy9imFiYusKkJBgY +id: taxonomy_term_publish_action +label: 'Publish taxonomy term' +type: taxonomy_term +plugin: 'entity:publish_action:taxonomy_term' +configuration: { } diff --git a/drupal10/config/system.action.taxonomy_term_unpublish_action.yml b/drupal10/config/system.action.taxonomy_term_unpublish_action.yml new file mode 100644 index 0000000..b2e0122 --- /dev/null +++ b/drupal10/config/system.action.taxonomy_term_unpublish_action.yml @@ -0,0 +1,13 @@ +uuid: e188ee40-6b90-4647-b0e1-ad0e23402bd3 +langcode: en +status: true +dependencies: + module: + - taxonomy +_core: + default_config_hash: z2sNRM3ECa7FPCGnSNje_9SmZJQgwhD_6fG_L4Mr8zI +id: taxonomy_term_unpublish_action +label: 'Unpublish taxonomy term' +type: taxonomy_term +plugin: 'entity:unpublish_action:taxonomy_term' +configuration: { } diff --git a/drupal10/config/system.action.user_block_user_action.yml b/drupal10/config/system.action.user_block_user_action.yml new file mode 100644 index 0000000..be734d9 --- /dev/null +++ b/drupal10/config/system.action.user_block_user_action.yml @@ -0,0 +1,13 @@ +uuid: 6ac44d32-3588-432a-98ba-4ded35792814 +langcode: en +status: true +dependencies: + module: + - user +_core: + default_config_hash: DyypzTfThX10FFQw-399qPfEbLLyrhXgQrKPVsmAoJ4 +id: user_block_user_action +label: 'Block the selected user(s)' +type: user +plugin: user_block_user_action +configuration: { } diff --git a/drupal10/config/system.action.user_cancel_user_action.yml b/drupal10/config/system.action.user_cancel_user_action.yml new file mode 100644 index 0000000..29a1c64 --- /dev/null +++ b/drupal10/config/system.action.user_cancel_user_action.yml @@ -0,0 +1,13 @@ +uuid: 38f7c545-677d-4139-a651-96c6598af80b +langcode: en +status: true +dependencies: + module: + - user +_core: + default_config_hash: nvrL9bFilzBvm2bjO9rQnFDpBA7dBBUjShSSt6NS-DU +id: user_cancel_user_action +label: 'Cancel the selected user account(s)' +type: user +plugin: user_cancel_user_action +configuration: { } diff --git a/drupal10/config/system.action.user_unblock_user_action.yml b/drupal10/config/system.action.user_unblock_user_action.yml new file mode 100644 index 0000000..a971c9c --- /dev/null +++ b/drupal10/config/system.action.user_unblock_user_action.yml @@ -0,0 +1,13 @@ +uuid: 94a10bef-a6f0-4531-b3cc-5c3ceb0831e2 +langcode: en +status: true +dependencies: + module: + - user +_core: + default_config_hash: SPsUXsR3Rc8d1y3gewzaAKWa1ncea_ywXX3f7LTn7k0 +id: user_unblock_user_action +label: 'Unblock the selected user(s)' +type: user +plugin: user_unblock_user_action +configuration: { } diff --git a/drupal10/config/system.advisories.yml b/drupal10/config/system.advisories.yml new file mode 100644 index 0000000..2ad53d6 --- /dev/null +++ b/drupal10/config/system.advisories.yml @@ -0,0 +1,4 @@ +_core: + default_config_hash: x0FuQ_7Cg81mSDQwG028_Z0CjH3R9ib5IDlHeV2BbAo +enabled: true +interval_hours: 6 diff --git a/drupal10/config/system.cron.yml b/drupal10/config/system.cron.yml new file mode 100644 index 0000000..e185420 --- /dev/null +++ b/drupal10/config/system.cron.yml @@ -0,0 +1,6 @@ +_core: + default_config_hash: f3yToH8v8EaBZU0NFFw2qR8ogSCuWVmQaN0hpYR1BD8 +threshold: + requirements_warning: 172800 + requirements_error: 1209600 +logging: true diff --git a/drupal10/config/system.date.yml b/drupal10/config/system.date.yml new file mode 100644 index 0000000..1fa80bc --- /dev/null +++ b/drupal10/config/system.date.yml @@ -0,0 +1,11 @@ +_core: + default_config_hash: IVsFTD1mvR2NGBP_1myt9kFIFmGepH4PyaN5aQBYpno +first_day: 0 +country: + default: null +timezone: + default: UTC + user: + configurable: true + default: 0 + warn: false diff --git a/drupal10/config/system.diff.yml b/drupal10/config/system.diff.yml new file mode 100644 index 0000000..f2fff7b --- /dev/null +++ b/drupal10/config/system.diff.yml @@ -0,0 +1,5 @@ +_core: + default_config_hash: 1WanmaEhxW_vM8_5Ktsdntj8MaO9UBHXg0lN603PsWM +context: + lines_leading: 2 + lines_trailing: 2 diff --git a/drupal10/config/system.feature_flags.yml b/drupal10/config/system.feature_flags.yml new file mode 100644 index 0000000..8cc8071 --- /dev/null +++ b/drupal10/config/system.feature_flags.yml @@ -0,0 +1,3 @@ +_core: + default_config_hash: ZYyVj1FtPGV40Cf65YDVTUIc7YgLH6trXlotuevfs2I +linkset_endpoint: false diff --git a/drupal10/config/system.file.yml b/drupal10/config/system.file.yml new file mode 100644 index 0000000..de47424 --- /dev/null +++ b/drupal10/config/system.file.yml @@ -0,0 +1,5 @@ +_core: + default_config_hash: mguGHCYb9Dw5EcpfjwoShGV1Vjkbz3QuPRCLfxiye-g +allow_insecure_uploads: false +default_scheme: public +temporary_maximum_age: 21600 diff --git a/drupal10/config/system.image.gd.yml b/drupal10/config/system.image.gd.yml new file mode 100644 index 0000000..2d81266 --- /dev/null +++ b/drupal10/config/system.image.gd.yml @@ -0,0 +1,3 @@ +_core: + default_config_hash: eNXaHfkJJUThHeF0nvkoXyPLRrKYGxgHRjORvT4F5rQ +jpeg_quality: 75 diff --git a/drupal10/config/system.image.yml b/drupal10/config/system.image.yml new file mode 100644 index 0000000..2e18f7f --- /dev/null +++ b/drupal10/config/system.image.yml @@ -0,0 +1,3 @@ +_core: + default_config_hash: durWHaKeBaq4d9Wpi4RqwADj1OufDepcnJuhVLmKN24 +toolkit: gd diff --git a/drupal10/config/system.logging.yml b/drupal10/config/system.logging.yml new file mode 100644 index 0000000..d6164de --- /dev/null +++ b/drupal10/config/system.logging.yml @@ -0,0 +1,3 @@ +_core: + default_config_hash: u3-njszl92FaxjrCMiq0yDcjAfcdx72w1zT1O9dx6aA +error_level: hide diff --git a/drupal10/config/system.mail.yml b/drupal10/config/system.mail.yml new file mode 100644 index 0000000..8ab75c1 --- /dev/null +++ b/drupal10/config/system.mail.yml @@ -0,0 +1,11 @@ +_core: + default_config_hash: 5PvD9swkqWUeHkabdvbJ2SQqdhrzjkCT21wtD4BLfk4 +interface: + default: php_mail +mailer_dsn: + scheme: sendmail + host: default + user: null + password: null + port: null + options: { } diff --git a/drupal10/config/system.maintenance.yml b/drupal10/config/system.maintenance.yml new file mode 100644 index 0000000..65a36d2 --- /dev/null +++ b/drupal10/config/system.maintenance.yml @@ -0,0 +1,4 @@ +_core: + default_config_hash: 1SNdA25INsV5YjlgAJtfC-6AM8VcWe_00xneMLb2yFg +langcode: en +message: '@site is currently under maintenance. We should be back shortly. Thank you for your patience.' diff --git a/drupal10/config/system.menu.account.yml b/drupal10/config/system.menu.account.yml new file mode 100644 index 0000000..adbd0c6 --- /dev/null +++ b/drupal10/config/system.menu.account.yml @@ -0,0 +1,10 @@ +uuid: 73451a4c-8796-4e85-9114-b61d01859272 +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: M_Bh81osDyUQ4wV0GgU_NdBNqkzM87sLxjaCdFj9mnw +id: account +label: 'User account menu' +description: 'Links related to the active user account' +locked: true diff --git a/drupal10/config/system.menu.admin.yml b/drupal10/config/system.menu.admin.yml new file mode 100644 index 0000000..1c26806 --- /dev/null +++ b/drupal10/config/system.menu.admin.yml @@ -0,0 +1,10 @@ +uuid: 984e745b-9ad7-4eba-b374-43ee612b9c28 +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: sapEi2YDGoI9yQIT_WgIV2vUdQ6DScH0V3fAyTadAL0 +id: admin +label: Administration +description: 'Administrative task links' +locked: true diff --git a/drupal10/config/system.menu.footer.yml b/drupal10/config/system.menu.footer.yml new file mode 100644 index 0000000..3d96805 --- /dev/null +++ b/drupal10/config/system.menu.footer.yml @@ -0,0 +1,10 @@ +uuid: 1b0342f7-e598-47e1-bb9b-8d4c84f81069 +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: 7yrlW5z9zdg2eBucB2GPqXKSMQfH9lSRSO4DbWF7AFc +id: footer +label: Footer +description: 'Site information links' +locked: true diff --git a/drupal10/config/system.menu.main.yml b/drupal10/config/system.menu.main.yml new file mode 100644 index 0000000..e14d1e2 --- /dev/null +++ b/drupal10/config/system.menu.main.yml @@ -0,0 +1,10 @@ +uuid: 35ce2c9a-78ee-48c0-96de-12abec46b9b3 +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: Q2Ra3jfoIVk0f3SjxJX61byRQFVBAbpzYDQOiY-kno8 +id: main +label: 'Main navigation' +description: 'Site section links' +locked: true diff --git a/drupal10/config/system.menu.tools.yml b/drupal10/config/system.menu.tools.yml new file mode 100644 index 0000000..23e7ea8 --- /dev/null +++ b/drupal10/config/system.menu.tools.yml @@ -0,0 +1,10 @@ +uuid: 03ee263b-f3e9-49fa-9092-2a1df0fc1974 +langcode: en +status: true +dependencies: { } +_core: + default_config_hash: BCM-vV1zzRaLHN18dqAR_CuGOj8AFJvTx7BKl_8Gcxc +id: tools +label: Tools +description: 'User tool links, often added by modules' +locked: true diff --git a/drupal10/config/system.performance.yml b/drupal10/config/system.performance.yml new file mode 100644 index 0000000..2219cf1 --- /dev/null +++ b/drupal10/config/system.performance.yml @@ -0,0 +1,16 @@ +_core: + default_config_hash: jtno5biznHZbrIgKwzq-ze-7XaQxLCGe6PeUOR7bRiQ +cache: + page: + max_age: 0 +css: + preprocess: true + gzip: true +fast_404: + enabled: true + paths: '/\.(?:txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i' + exclude_paths: '/\/(?:styles|imagecache)\//' + html: '404 Not Found

Not Found

The requested URL "@path" was not found on this server.

' +js: + preprocess: true + gzip: true diff --git a/drupal10/config/system.rss.yml b/drupal10/config/system.rss.yml new file mode 100644 index 0000000..d806208 --- /dev/null +++ b/drupal10/config/system.rss.yml @@ -0,0 +1,4 @@ +_core: + default_config_hash: MIpNzlG4gPunfS7vTCwUPum6QH3GUsEBMj-qS631Jw0 +items: + view_mode: rss diff --git a/drupal10/config/system.site.yml b/drupal10/config/system.site.yml new file mode 100644 index 0000000..8fb9e1c --- /dev/null +++ b/drupal10/config/system.site.yml @@ -0,0 +1,15 @@ +_core: + default_config_hash: HlN7eAN2N4JIHsYv56V4E7sqC9bS609KwvGFjyD_mgk +langcode: en +uuid: 2e1ba8d2-6119-48b2-9827-f25f0139550f +name: 'Drupal meetups' +mail: dinarcon@example.com +slogan: '' +page: + 403: '' + 404: '' + front: /node +admin_compact_mode: false +weight_select_max: 100 +default_langcode: en +mail_notification: null diff --git a/drupal10/config/system.theme.global.yml b/drupal10/config/system.theme.global.yml new file mode 100644 index 0000000..cd50893 --- /dev/null +++ b/drupal10/config/system.theme.global.yml @@ -0,0 +1,16 @@ +_core: + default_config_hash: KZiV2LB312xgJBphfq5BTjmp16xiR5KM8InBJ-pff7Y +favicon: + mimetype: image/vnd.microsoft.icon + path: '' + url: '' + use_default: true +features: + comment_user_picture: true + comment_user_verification: true + favicon: true + node_user_picture: true +logo: + path: '' + url: null + use_default: true diff --git a/drupal10/config/system.theme.yml b/drupal10/config/system.theme.yml new file mode 100644 index 0000000..7ac1814 --- /dev/null +++ b/drupal10/config/system.theme.yml @@ -0,0 +1,4 @@ +_core: + default_config_hash: 6lQ55NXM9ysybMQ6NzJj4dtiQ1dAkOYxdDompa-r_kk +admin: claro +default: olivero diff --git a/drupal10/config/taxonomy.settings.yml b/drupal10/config/taxonomy.settings.yml new file mode 100644 index 0000000..0e3837d --- /dev/null +++ b/drupal10/config/taxonomy.settings.yml @@ -0,0 +1,5 @@ +_core: + default_config_hash: zKpaWT6cJc1tVQQaTqatGELaCqU_oyRym6zTl27Yias +maintain_index_table: true +override_selector: false +terms_per_page_admin: 100 diff --git a/drupal10/config/text.settings.yml b/drupal10/config/text.settings.yml new file mode 100644 index 0000000..a295c7e --- /dev/null +++ b/drupal10/config/text.settings.yml @@ -0,0 +1,3 @@ +_core: + default_config_hash: Bkewb77RBOK3_aXMPsp8p87gbc03NvmC5gBLzPl7hVA +default_summary_length: 600 diff --git a/drupal10/config/update.settings.yml b/drupal10/config/update.settings.yml new file mode 100644 index 0000000..3be29cf --- /dev/null +++ b/drupal10/config/update.settings.yml @@ -0,0 +1,13 @@ +_core: + default_config_hash: xbYr66-g0FjNgVBkGypCuN46vBI2XHntXN1URawq1s4 +check: + disabled_extensions: false + interval_days: 1 +fetch: + url: null + max_attempts: 2 + timeout: 30 +notification: + emails: + - admin@example.com + threshold: all diff --git a/drupal10/config/user.flood.yml b/drupal10/config/user.flood.yml new file mode 100644 index 0000000..4eb3644 --- /dev/null +++ b/drupal10/config/user.flood.yml @@ -0,0 +1,7 @@ +_core: + default_config_hash: UYfMzeP1S8jKm9PSvxf7nQNe8DsNS-3bc2WSNNXBQWs +uid_only: false +ip_limit: 50 +ip_window: 3600 +user_limit: 5 +user_window: 21600 diff --git a/drupal10/config/user.mail.yml b/drupal10/config/user.mail.yml new file mode 100644 index 0000000..bf425c0 --- /dev/null +++ b/drupal10/config/user.mail.yml @@ -0,0 +1,116 @@ +_core: + default_config_hash: 6CZIzFifRq3qbdq3n3nDpEOO4hWIQtKOAQNPvGNGKeM +langcode: en +cancel_confirm: + subject: 'Account cancellation request for [user:display-name] at [site:name]' + body: |- + [user:display-name] + + A request to cancel your account has been made at [site:name]. + + You may now cancel your account on [site:url-brief] by clicking this link or copying and pasting it into your browser: + + [user:cancel-url] + + NOTE: The cancellation of your account is not reversible. + + This link expires in one day and nothing will happen if it is not used. + + -- [site:name] team +password_reset: + subject: 'Replacement login information for [user:display-name] at [site:name]' + body: |- + [user:display-name], + + A request to reset the password for your account has been made at [site:name]. + + You may now log in by clicking this link or copying and pasting it into your browser: + + [user:one-time-login-url] + + This link can only be used once to log in and will lead you to a page where you can set your password. It expires after one day and nothing will happen if it's not used. + + -- [site:name] team +register_admin_created: + subject: 'An administrator created an account for you at [site:name]' + body: |- + [user:display-name], + + A site administrator at [site:name] has created an account for you. You may now log in by clicking this link or copying and pasting it into your browser: + + [user:one-time-login-url] + + This link can only be used once to log in and will lead you to a page where you can set your password. + + After setting your password, you will be able to log in at [site:login-url] in the future using: + + username: [user:name] + password: Your password + + -- [site:name] team +register_no_approval_required: + subject: 'Account details for [user:display-name] at [site:name]' + body: |- + [user:display-name], + + Thank you for registering at [site:name]. You may now log in by clicking this link or copying and pasting it into your browser: + + [user:one-time-login-url] + + This link can only be used once to log in and will lead you to a page where you can set your password. + + After setting your password, you will be able to log in at [site:login-url] in the future using: + + username: [user:name] + password: Your password + + -- [site:name] team +register_pending_approval: + subject: 'Account details for [user:display-name] at [site:name] (pending admin approval)' + body: |- + [user:display-name], + + Thank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details. + + -- [site:name] team +register_pending_approval_admin: + subject: 'Account details for [user:display-name] at [site:name] (pending admin approval)' + body: |- + [user:display-name] has applied for an account. + + [user:edit-url] +status_activated: + subject: 'Account details for [user:display-name] at [site:name] (approved)' + body: |- + [user:display-name], + + Your account at [site:name] has been activated. + + You may now log in by clicking this link or copying and pasting it into your browser: + + [user:one-time-login-url] + + This link can only be used once to log in and will lead you to a page where you can set your password. + + After setting your password, you will be able to log in at [site:login-url] in the future using: + + username: [user:account-name] + password: Your password + + -- [site:name] team +status_blocked: + subject: 'Account details for [user:display-name] at [site:name] (blocked)' + body: |- + [user:display-name], + + Your account on [site:name] has been blocked. + + -- [site:name] team +status_canceled: + subject: 'Account details for [user:display-name] at [site:name] (canceled)' + body: |- + [user:display-name], + + Your account on [site:name] has been canceled. + + -- [site:name] team \ No newline at end of file diff --git a/drupal10/config/user.role.anonymous.yml b/drupal10/config/user.role.anonymous.yml new file mode 100644 index 0000000..de946cd --- /dev/null +++ b/drupal10/config/user.role.anonymous.yml @@ -0,0 +1,16 @@ +uuid: 6e598ba7-7a21-4cf3-ab22-346768c48847 +langcode: en +status: true +dependencies: + module: + - media + - system +_core: + default_config_hash: j5zLMOdJBqC0bMvSdth5UebkprJB8g_2FXHqhfpJzow +id: anonymous +label: 'Anonymous user' +weight: 0 +is_admin: false +permissions: + - 'access content' + - 'view media' diff --git a/drupal10/config/user.role.authenticated.yml b/drupal10/config/user.role.authenticated.yml new file mode 100644 index 0000000..d6493e2 --- /dev/null +++ b/drupal10/config/user.role.authenticated.yml @@ -0,0 +1,16 @@ +uuid: d683432d-6eb1-4a04-8923-91a7308211a4 +langcode: en +status: true +dependencies: + module: + - media + - system +_core: + default_config_hash: dJ0L2DNSj5q6XVZAGsuVDpJTh5UeYkIPwKrUOOpr8YI +id: authenticated +label: 'Authenticated user' +weight: 1 +is_admin: false +permissions: + - 'access content' + - 'view media' diff --git a/drupal10/config/user.settings.yml b/drupal10/config/user.settings.yml new file mode 100644 index 0000000..2ac0466 --- /dev/null +++ b/drupal10/config/user.settings.yml @@ -0,0 +1,18 @@ +_core: + default_config_hash: M4F5x5CHrctvvJZY1qyP3D4ht3xaPjp2_CEo2TE-uJw +langcode: en +anonymous: Anonymous +verify_mail: true +notify: + cancel_confirm: true + password_reset: true + status_activated: true + status_blocked: false + status_canceled: false + register_admin_created: true + register_no_approval_required: true + register_pending_approval: true +register: visitors +cancel_method: user_cancel_block +password_reset_timeout: 86400 +password_strength: true diff --git a/drupal10/config/views.settings.yml b/drupal10/config/views.settings.yml new file mode 100644 index 0000000..fab7304 --- /dev/null +++ b/drupal10/config/views.settings.yml @@ -0,0 +1,47 @@ +_core: + default_config_hash: 8oDr9oPVb_ostrNnVV6V7VdphwoH_u-NqTS0u7SE7qc +display_extenders: { } +sql_signature: false +ui: + show: + additional_queries: false + advanced_column: false + default_display: false + performance_statistics: false + preview_information: true + sql_query: + enabled: false + where: above + display_embed: false + always_live_preview: true + exposed_filter_any_label: old_any +field_rewrite_elements: + div: DIV + span: SPAN + h1: H1 + h2: H2 + h3: H3 + h4: H4 + h5: H5 + h6: H6 + p: P + header: HEADER + footer: FOOTER + article: ARTICLE + section: SECTION + aside: ASIDE + details: DETAILS + blockquote: BLOCKQUOTE + figure: FIGURE + address: ADDRESS + code: CODE + pre: PRE + var: VAR + samp: SAMP + kbd: KBD + strong: STRONG + em: EM + del: DEL + ins: INS + q: Q + s: S diff --git a/drupal10/config/views.view.archive.yml b/drupal10/config/views.view.archive.yml new file mode 100644 index 0000000..71ca732 --- /dev/null +++ b/drupal10/config/views.view.archive.yml @@ -0,0 +1,248 @@ +uuid: 70e29187-17c5-4527-87a1-30bc282aedfa +langcode: en +status: false +dependencies: + config: + - core.entity_view_mode.node.teaser + module: + - node + - user +_core: + default_config_hash: exIF08PTvYSK6tJCsAzBqMEKUBKIptPorMn74SVhevc +id: archive +label: Archive +module: node +description: 'All content, by month.' +tag: default +base_table: node_field_data +base_field: nid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: 'Monthly archive' + fields: { } + pager: + type: mini + options: + offset: 0 + pagination_heading_level: h4 + items_per_page: 10 + total_pages: 0 + 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: created + exposed: false + granularity: second + arguments: + created_year_month: + id: created_year_month + table: node_field_data + field: created_year_month + entity_type: node + plugin_id: date_year_month + default_action: summary + exception: + title_enable: true + title_enable: true + title: '{{ arguments.created_year_month }}' + default_argument_type: fixed + summary_options: + override: true + items_per_page: 30 + summary: + sort_order: desc + format: default_summary + specify_validation: true + filters: + status: + id: status + table: node_field_data + field: status + entity_type: node + entity_field: status + plugin_id: boolean + value: '1' + group: 0 + expose: + operator: '0' + operator_limit_selection: false + operator_list: { } + langcode: + id: langcode + table: node_field_data + field: langcode + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: langcode + plugin_id: language + operator: in + value: + '***LANGUAGE_language_content***': '***LANGUAGE_language_content***' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + style: + type: default + options: + grouping: { } + row_class: '' + default_row_class: true + uses_fields: false + 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_interface' + - url + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } + block_1: + id: block_1 + display_title: Block + display_plugin: block + position: 1 + display_options: + arguments: + created_year_month: + id: created_year_month + table: node_field_data + field: created_year_month + entity_type: node + plugin_id: date_year_month + default_action: summary + exception: + title_enable: true + title_enable: true + title: '{{ arguments.created_year_month }}' + default_argument_type: fixed + summary_options: + items_per_page: 30 + summary: + format: default_summary + specify_validation: true + query: + type: views_query + options: { } + defaults: + arguments: false + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_interface' + - url + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } + page_1: + id: page_1 + display_title: Page + display_plugin: page + position: 2 + display_options: + query: + type: views_query + options: { } + display_extenders: { } + path: archive + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_interface' + - url + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } diff --git a/drupal10/config/views.view.block_content.yml b/drupal10/config/views.view.block_content.yml new file mode 100644 index 0000000..18bedbe --- /dev/null +++ b/drupal10/config/views.view.block_content.yml @@ -0,0 +1,553 @@ +uuid: a8e2dca6-c37f-4c73-bdcf-a88192f8cc0c +langcode: en +status: true +dependencies: + module: + - block_content + - user +_core: + default_config_hash: HfvTcWhiVuvxchoh4DjIkXhKkj9ow2TGgHkLHSvRLq8 +id: block_content +label: 'Content blocks' +module: views +description: 'Find and manage content blocks.' +tag: default +base_table: block_content_field_data +base_field: id +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: 'Content blocks' + fields: + info: + id: info + table: block_content_field_data + field: info + relationship: none + group_type: group + admin_label: '' + entity_type: null + entity_field: info + plugin_id: field + label: 'Block description' + 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: 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 + type: + id: type + table: block_content_field_data + field: type + relationship: none + group_type: group + admin_label: '' + entity_type: block_content + entity_field: type + plugin_id: field + label: 'Block type' + 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: 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: target_id + type: entity_reference_label + settings: + link: false + group_column: target_id + 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 + changed: + id: changed + table: block_content_field_data + field: changed + relationship: none + group_type: group + admin_label: '' + entity_type: block_content + entity_field: changed + plugin_id: field + label: Updated + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: timestamp + settings: + date_format: short + custom_date_format: '' + timezone: '' + tooltip: + date_format: long + custom_date_format: '' + time_diff: + enabled: false + future_format: '@interval hence' + past_format: '@interval ago' + granularity: 2 + refresh: 60 + operations: + id: operations + table: block_content + field: operations + relationship: none + group_type: group + admin_label: '' + entity_type: block_content + plugin_id: entity_operations + label: Operations + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + destination: true + pager: + type: mini + options: + offset: 0 + pagination_heading_level: h4 + items_per_page: 50 + total_pages: null + id: 0 + tags: + next: 'Next ›' + previous: '‹ 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: true + 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 block library' + cache: + type: tag + options: { } + empty: + area_text_custom: + id: area_text_custom + table: views + field: area_text_custom + relationship: none + group_type: group + admin_label: '' + plugin_id: text_custom + empty: true + content: 'There are no content blocks available.' + tokenize: false + block_content_listing_empty: + id: block_content_listing_empty + table: block_content + field: block_content_listing_empty + relationship: none + group_type: group + admin_label: '' + entity_type: block_content + plugin_id: block_content_listing_empty + label: '' + empty: true + sorts: { } + arguments: { } + filters: + info: + id: info + table: block_content_field_data + field: info + relationship: none + group_type: group + admin_label: '' + entity_type: block_content + entity_field: info + plugin_id: string + operator: contains + value: '' + group: 1 + exposed: true + expose: + operator_id: info_op + label: 'Block description' + description: '' + use_operator: false + operator: info_op + operator_limit_selection: false + operator_list: { } + identifier: info + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + type: + id: type + table: block_content_field_data + field: type + relationship: none + group_type: group + admin_label: '' + entity_type: block_content + entity_field: type + plugin_id: bundle + operator: in + value: { } + group: 1 + exposed: true + expose: + operator_id: type_op + label: 'Block type' + description: '' + use_operator: false + operator: type_op + operator_limit_selection: false + operator_list: { } + identifier: type + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + reusable: + id: reusable + table: block_content_field_data + field: reusable + relationship: none + group_type: group + admin_label: '' + entity_type: block_content + entity_field: reusable + plugin_id: boolean + operator: '=' + value: '1' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: true + columns: + info: info + type: type + changed: changed + operations: operations + default: changed + info: + info: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + type: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + changed: + sortable: true + default_sort_order: desc + align: '' + separator: '' + empty_column: false + responsive: '' + operations: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + override: true + sticky: false + summary: '' + empty_table: true + caption: '' + description: '' + row: + type: fields + 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 + - url.query_args + - user.permissions + tags: { } + page_1: + id: page_1 + display_title: Page + display_plugin: page + position: 1 + display_options: + display_extenders: { } + path: admin/content/block + menu: + type: tab + title: Blocks + description: 'Create and edit content blocks.' + weight: 0 + menu_name: admin + parent: system.admin_content + context: '0' + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.permissions + tags: { } diff --git a/drupal10/config/views.view.content.yml b/drupal10/config/views.view.content.yml new file mode 100644 index 0000000..7ab5847 --- /dev/null +++ b/drupal10/config/views.view.content.yml @@ -0,0 +1,693 @@ +uuid: a2d93e16-9c5d-4388-873c-de028b7c9ea0 +langcode: en +status: true +dependencies: + module: + - node + - user +_core: + default_config_hash: whBYBAcXQuQblGEdvQ9QfGuuW3zC49F2-5vXSm1slFM +id: content +label: Content +module: node +description: 'Find and manage content.' +tag: default +base_table: node_field_data +base_field: nid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: Content + fields: + node_bulk_form: + id: node_bulk_form + table: node + field: node_bulk_form + entity_type: node + plugin_id: node_bulk_form + label: '' + exclude: false + alter: + alter_text: false + element_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + title: + id: title + table: node_field_data + field: title + entity_type: node + entity_field: title + plugin_id: field + label: Title + exclude: false + alter: + alter_text: false + element_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: string + settings: + link_to_entity: true + type: + id: type + table: node_field_data + field: type + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: type + plugin_id: field + label: 'Content type' + 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: 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: target_id + type: entity_reference_label + settings: + link: false + group_column: target_id + 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 + name: + id: name + table: users_field_data + field: name + relationship: uid + entity_type: user + entity_field: name + plugin_id: field + label: Author + exclude: false + alter: + alter_text: false + element_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: user_name + status: + id: status + table: node_field_data + field: status + entity_type: node + entity_field: status + plugin_id: field + label: Status + exclude: false + alter: + alter_text: false + element_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: boolean + settings: + format: custom + format_custom_false: Unpublished + format_custom_true: Published + changed: + id: changed + table: node_field_data + field: changed + entity_type: node + entity_field: changed + plugin_id: field + label: Updated + exclude: false + alter: + alter_text: false + element_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: timestamp + settings: + date_format: short + custom_date_format: '' + timezone: '' + tooltip: + date_format: long + custom_date_format: '' + time_diff: + enabled: false + future_format: '@interval hence' + past_format: '@interval ago' + granularity: 2 + refresh: 60 + langcode: + id: langcode + table: node_field_data + field: langcode + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: langcode + plugin_id: field + label: Language + 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: 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: language + settings: + link_to_entity: false + native_language: false + 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 + operations: + id: operations + table: node + field: operations + relationship: none + group_type: group + admin_label: '' + plugin_id: entity_operations + label: Operations + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + destination: true + pager: + type: full + options: + pagination_heading_level: h4 + items_per_page: 50 + tags: + next: 'Next ›' + previous: '‹ Previous' + first: '« First' + last: 'Last »' + exposed_form: + type: basic + options: + submit_button: Filter + reset_button: true + 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 overview' + cache: + type: tag + empty: + area_text_custom: + id: area_text_custom + table: views + field: area_text_custom + plugin_id: text_custom + empty: true + content: 'No content available.' + sorts: { } + arguments: { } + filters: + title: + id: title + table: node_field_data + field: title + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: title + plugin_id: string + operator: contains + value: '' + group: 1 + exposed: true + expose: + operator_id: title_op + label: Title + description: '' + use_operator: false + operator: title_op + operator_limit_selection: false + operator_list: { } + identifier: title + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + type: + id: type + table: node_field_data + field: type + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: type + plugin_id: bundle + operator: in + value: { } + group: 1 + exposed: true + expose: + operator_id: type_op + label: 'Content type' + description: '' + use_operator: false + operator: type_op + operator_limit_selection: false + operator_list: { } + identifier: type + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + status: + id: status + table: node_field_data + field: status + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: status + plugin_id: boolean + operator: '=' + value: '1' + group: 1 + exposed: true + expose: + operator_id: '' + label: Status + description: '' + use_operator: false + operator: status_op + operator_limit_selection: false + operator_list: { } + identifier: status + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: true + group_info: + label: 'Published status' + description: '' + identifier: status + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: + 1: + title: Published + operator: '=' + value: '1' + 2: + title: Unpublished + operator: '=' + value: '0' + langcode: + id: langcode + table: node_field_data + field: langcode + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: langcode + plugin_id: language + operator: in + value: { } + group: 1 + exposed: true + expose: + operator_id: langcode_op + label: Language + description: '' + use_operator: false + operator: langcode_op + operator_limit_selection: false + operator_list: { } + identifier: langcode + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + status_extra: + id: status_extra + table: node_field_data + field: status_extra + entity_type: node + plugin_id: node_status + operator: '=' + value: false + group: 1 + expose: + operator_limit_selection: false + operator_list: { } + filter_groups: + operator: AND + groups: + 1: AND + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: true + columns: + node_bulk_form: node_bulk_form + title: title + type: type + name: name + status: status + changed: changed + edit_node: edit_node + delete_node: delete_node + dropbutton: dropbutton + timestamp: title + default: changed + info: + node_bulk_form: + align: '' + separator: '' + empty_column: false + responsive: '' + title: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + type: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + name: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: priority-low + status: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + changed: + sortable: true + default_sort_order: desc + align: '' + separator: '' + empty_column: false + responsive: priority-low + edit_node: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + delete_node: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + dropbutton: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + timestamp: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + override: true + sticky: true + summary: '' + empty_table: true + caption: '' + description: '' + row: + type: fields + query: + type: views_query + relationships: + uid: + id: uid + table: node_field_data + field: uid + admin_label: author + plugin_id: standard + required: true + show_admin_links: false + display_extenders: { } + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user + - '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: admin/content/node + menu: + type: 'default tab' + title: Content + description: '' + weight: -10 + menu_name: admin + context: '' + tab_options: + type: normal + title: Content + description: 'Find and manage content' + weight: -10 + menu_name: admin + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user + - 'user.node_grants:view' + - user.permissions + tags: { } diff --git a/drupal10/config/views.view.content_recent.yml b/drupal10/config/views.view.content_recent.yml new file mode 100644 index 0000000..b708865 --- /dev/null +++ b/drupal10/config/views.view.content_recent.yml @@ -0,0 +1,323 @@ +uuid: 7677aa67-0571-4475-b577-6c385f02bf1c +langcode: en +status: true +dependencies: + module: + - node + - user +_core: + default_config_hash: YqZN5rc7XDQcFcInc8wkzuaHJmC5YvirhTmDcrarT6M +id: content_recent +label: 'Recent content' +module: node +description: 'Recent content.' +tag: default +base_table: node_field_data +base_field: nid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: 'Recent content' + 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: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: string + settings: + link_to_entity: true + changed: + id: changed + table: node_field_data + field: changed + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: changed + plugin_id: 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 + click_sort_column: value + type: timestamp_ago + settings: { } + 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: some + options: + offset: 0 + items_per_page: 10 + 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: + area_text_custom: + id: area_text_custom + table: views + field: area_text_custom + relationship: none + group_type: group + admin_label: '' + plugin_id: text_custom + empty: true + content: 'No content available.' + tokenize: false + sorts: + changed: + id: changed + table: node_field_data + field: changed + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: changed + plugin_id: date + order: DESC + expose: + label: '' + field_identifier: changed + exposed: false + granularity: second + arguments: { } + filters: + status_extra: + id: status_extra + table: node_field_data + field: status_extra + relationship: none + group_type: group + admin_label: '' + entity_type: node + plugin_id: node_status + operator: '=' + value: false + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + langcode: + id: langcode + table: node_field_data + field: langcode + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: langcode + plugin_id: language + operator: in + value: + '***LANGUAGE_language_content***': '***LANGUAGE_language_content***' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + style: + type: html_list + options: + grouping: { } + row_class: '' + default_row_class: true + type: ul + wrapper_class: item-list + class: '' + row: + type: fields + query: + type: views_query + options: + query_comment: '' + disable_sql_rewrite: false + distinct: false + replica: false + query_tags: { } + relationships: + uid: + id: uid + table: node_field_data + field: uid + relationship: none + group_type: group + admin_label: author + entity_type: node + entity_field: uid + plugin_id: standard + required: true + use_more: false + use_more_always: false + use_more_text: More + link_display: '0' + link_url: '' + header: { } + footer: { } + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - user + - 'user.node_grants:view' + - user.permissions + tags: { } + block_1: + id: block_1 + display_title: Block + display_plugin: block + position: 1 + display_options: + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - user + - 'user.node_grants:view' + - user.permissions + tags: { } diff --git a/drupal10/config/views.view.files.yml b/drupal10/config/views.view.files.yml new file mode 100644 index 0000000..e074b4e --- /dev/null +++ b/drupal10/config/views.view.files.yml @@ -0,0 +1,1199 @@ +uuid: 61a5d355-932e-43d0-8859-4619984bd382 +langcode: en +status: true +dependencies: + module: + - file + - user +_core: + default_config_hash: blP1XdpU4RqVoA_8po7qnIokIbHB6DJt0lpilgY5ovM +id: files +label: Files +module: file +description: 'Find and manage files.' +tag: default +base_table: file_managed +base_field: fid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: Files + fields: + fid: + id: fid + table: file_managed + field: fid + relationship: none + group_type: group + admin_label: '' + entity_type: file + entity_field: fid + plugin_id: field + label: Fid + exclude: true + 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 + filename: + id: filename + table: file_managed + field: filename + relationship: none + group_type: group + admin_label: '' + entity_type: file + entity_field: filename + plugin_id: field + label: Name + 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: false + ellipsis: false + 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: 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: file_link + 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 + filemime: + id: filemime + table: file_managed + field: filemime + relationship: none + group_type: group + admin_label: '' + entity_type: file + entity_field: filemime + plugin_id: field + label: 'MIME type' + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: file_filemime + filesize: + id: filesize + table: file_managed + field: filesize + relationship: none + group_type: group + admin_label: '' + entity_type: file + entity_field: filesize + plugin_id: field + label: Size + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: file_size + status: + id: status + table: file_managed + field: status + relationship: none + group_type: group + admin_label: '' + entity_type: file + entity_field: status + plugin_id: field + label: Status + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: boolean + settings: + format: custom + format_custom_false: Temporary + format_custom_true: Permanent + created: + id: created + table: file_managed + field: created + relationship: none + group_type: group + admin_label: '' + entity_type: file + entity_field: created + plugin_id: field + label: 'Upload date' + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: timestamp + settings: + date_format: medium + custom_date_format: '' + timezone: '' + tooltip: + date_format: long + custom_date_format: '' + time_diff: + enabled: false + future_format: '@interval hence' + past_format: '@interval ago' + granularity: 2 + refresh: 60 + changed: + id: changed + table: file_managed + field: changed + relationship: none + group_type: group + admin_label: '' + entity_type: file + entity_field: changed + plugin_id: field + label: 'Changed date' + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: timestamp + settings: + date_format: medium + custom_date_format: '' + timezone: '' + tooltip: + date_format: long + custom_date_format: '' + time_diff: + enabled: false + future_format: '@interval hence' + past_format: '@interval ago' + granularity: 2 + refresh: 60 + count: + id: count + table: file_usage + field: count + relationship: fid + group_type: sum + admin_label: '' + plugin_id: numeric + label: 'Used in' + exclude: false + alter: + alter_text: false + text: '' + make_link: true + path: 'admin/content/files/usage/{{ fid }}' + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + set_precision: false + precision: 0 + decimal: . + separator: ',' + format_plural: true + format_plural_string: !!binary MSBwbGFjZQNAY291bnQgcGxhY2Vz + prefix: '' + suffix: '' + operations: + id: operations + table: file_managed + field: operations + relationship: none + group_type: group + admin_label: '' + entity_type: file + plugin_id: entity_operations + label: Operations + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + destination: false + pager: + type: mini + options: + offset: 0 + pagination_heading_level: h4 + items_per_page: 50 + total_pages: 0 + id: 0 + tags: + next: 'Next ›' + previous: '‹ 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: Filter + 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 files overview' + cache: + type: tag + options: { } + empty: + area_text_custom: + id: area_text_custom + table: views + field: area_text_custom + plugin_id: text_custom + empty: true + content: 'No files available.' + sorts: { } + arguments: { } + filters: + filename: + id: filename + table: file_managed + field: filename + relationship: none + group_type: group + admin_label: '' + entity_type: file + entity_field: filename + plugin_id: string + operator: word + value: '' + group: 1 + exposed: true + expose: + operator_id: filemime_op + label: Filename + description: '' + use_operator: false + operator: filename_op + operator_limit_selection: false + operator_list: { } + identifier: filename + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + filemime: + id: filemime + table: file_managed + field: filemime + relationship: none + group_type: group + admin_label: '' + entity_type: file + entity_field: filemime + plugin_id: string + operator: word + value: '' + group: 1 + exposed: true + expose: + operator_id: filemime_op + label: 'MIME type' + description: '' + use_operator: false + operator: filemime_op + operator_limit_selection: false + operator_list: { } + identifier: filemime + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + status: + id: status + table: file_managed + field: status + relationship: none + group_type: group + admin_label: '' + entity_type: file + entity_field: status + plugin_id: file_status + operator: in + value: { } + group: 1 + exposed: true + expose: + operator_id: status_op + label: Status + description: '' + use_operator: false + operator: status_op + operator_limit_selection: false + operator_list: { } + identifier: status + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: true + columns: + fid: fid + filename: filename + filemime: filemime + filesize: filesize + status: status + created: created + changed: changed + count: count + default: changed + info: + fid: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + filename: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + filemime: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: priority-medium + filesize: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: priority-low + status: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: priority-low + created: + sortable: true + default_sort_order: desc + align: '' + separator: '' + empty_column: false + responsive: '' + changed: + sortable: true + default_sort_order: desc + align: '' + separator: '' + empty_column: false + responsive: '' + count: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: priority-medium + override: true + sticky: false + summary: '' + empty_table: true + caption: '' + description: '' + row: + type: fields + query: + type: views_query + options: + query_comment: '' + disable_sql_rewrite: false + distinct: false + replica: false + query_tags: { } + relationships: + fid: + id: fid + table: file_managed + field: fid + relationship: none + group_type: group + admin_label: 'File usage' + required: true + group_by: true + show_admin_links: true + header: { } + footer: { } + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.permissions + tags: { } + page_1: + id: page_1 + display_title: 'Files overview' + display_plugin: page + position: 1 + display_options: + defaults: + pager: true + relationships: false + relationships: + fid: + id: fid + table: file_managed + field: fid + relationship: none + group_type: group + admin_label: 'File usage' + required: false + display_description: '' + display_extenders: { } + path: admin/content/files + menu: + type: tab + title: Files + description: '' + weight: 0 + menu_name: admin + context: '' + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.permissions + tags: { } + page_2: + id: page_2 + display_title: 'File usage' + display_plugin: page + position: 2 + display_options: + title: 'File usage' + fields: + entity_label: + id: entity_label + table: file_usage + field: entity_label + relationship: none + group_type: group + admin_label: '' + plugin_id: entity_label + label: Entity + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + link_to_entity: true + type: + id: type + table: file_usage + field: type + relationship: none + group_type: group + admin_label: '' + plugin_id: standard + label: 'Entity type' + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + module: + id: module + table: file_usage + field: module + relationship: none + group_type: group + admin_label: '' + plugin_id: standard + label: 'Registering module' + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + count: + id: count + table: file_usage + field: count + relationship: none + group_type: group + admin_label: '' + plugin_id: numeric + label: 'Use count' + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + set_precision: false + precision: 0 + decimal: . + separator: ',' + format_plural: false + format_plural_string: !!binary MQNAY291bnQ= + prefix: '' + suffix: '' + pager: + type: mini + options: + offset: 0 + pagination_heading_level: h4 + items_per_page: 10 + total_pages: 0 + id: 0 + tags: + next: 'Next ›' + previous: '‹ 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 + empty: { } + arguments: + fid: + id: fid + table: file_managed + field: fid + relationship: none + group_type: group + admin_label: '' + entity_type: file + entity_field: fid + plugin_id: file_fid + default_action: 'not found' + exception: + value: all + title_enable: false + title: All + title_enable: true + title: 'File usage information for {{ arguments.fid }}' + default_argument_type: fixed + default_argument_options: + argument: '' + summary_options: + base_path: '' + count: true + override: false + items_per_page: 25 + summary: + sort_order: asc + number_of_records: 0 + format: default_summary + specify_validation: false + validate: + type: none + fail: 'not found' + validate_options: { } + break_phrase: false + not: false + filters: { } + filter_groups: + operator: AND + groups: { } + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: true + columns: + entity_label: entity_label + type: type + module: module + count: count + default: entity_label + info: + entity_label: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + type: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: priority-medium + module: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: priority-low + count: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + override: true + sticky: false + summary: '' + empty_table: true + caption: '' + description: '' + row: + type: fields + options: { } + defaults: + empty: false + title: false + pager: false + group_by: false + style: false + row: false + relationships: false + fields: false + arguments: false + filters: false + filter_groups: false + relationships: + fid: + id: fid + table: file_managed + field: fid + relationship: none + group_type: group + admin_label: 'File usage' + required: true + group_by: false + display_description: '' + display_extenders: { } + path: admin/content/files/usage/% + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_interface' + - url + - url.query_args + - user.permissions + tags: { } diff --git a/drupal10/config/views.view.frontpage.yml b/drupal10/config/views.view.frontpage.yml new file mode 100644 index 0000000..2845940 --- /dev/null +++ b/drupal10/config/views.view.frontpage.yml @@ -0,0 +1,313 @@ +uuid: 0c8f33b1-6187-4ca3-a5db-100daa2265a0 +langcode: en +status: true +dependencies: + config: + - core.entity_view_mode.node.rss + - core.entity_view_mode.node.teaser + module: + - node + - user +_core: + default_config_hash: OEEnRQIjfIaCFRsz8U3VqDrE82ltQKlo8wNbQJRZXu0 +id: frontpage +label: Frontpage +module: node +description: 'All content promoted to the front page.' +tag: default +base_table: node_field_data +base_field: nid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: '' + fields: { } + pager: + type: full + options: + offset: 0 + pagination_heading_level: h4 + items_per_page: 10 + total_pages: 0 + id: 0 + tags: + next: 'Next ›' + previous: '‹ Previous' + first: '« First' + last: 'Last »' + 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 + quantity: 9 + 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: + area_text_custom: + id: area_text_custom + table: views + field: area_text_custom + relationship: none + group_type: group + admin_label: '' + plugin_id: text_custom + label: '' + empty: true + content: 'No front page content has been created yet.
Follow the User Guide to start building your site.' + tokenize: false + node_listing_empty: + id: node_listing_empty + table: node + field: node_listing_empty + relationship: none + group_type: group + admin_label: '' + entity_type: node + plugin_id: node_listing_empty + label: '' + empty: true + title: + id: title + table: views + field: title + relationship: none + group_type: group + admin_label: '' + plugin_id: title + label: '' + empty: true + title: Welcome! + sorts: + sticky: + id: sticky + table: node_field_data + field: sticky + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: sticky + plugin_id: standard + order: DESC + expose: + label: '' + field_identifier: sticky + exposed: false + 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: created + exposed: false + granularity: second + arguments: { } + filters: + promote: + id: promote + table: node_field_data + field: promote + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: promote + plugin_id: boolean + operator: '=' + value: '1' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + status: + id: status + table: node_field_data + field: status + entity_type: node + entity_field: status + plugin_id: boolean + value: '1' + group: 1 + expose: + operator: '' + operator_limit_selection: false + operator_list: { } + langcode: + id: langcode + table: node_field_data + field: langcode + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: langcode + plugin_id: language + operator: in + value: + '***LANGUAGE_language_content***': '***LANGUAGE_language_content***' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + style: + type: default + options: + grouping: { } + row_class: '' + default_row_class: true + uses_fields: false + 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_interface' + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } + feed_1: + id: feed_1 + display_title: Feed + display_plugin: feed + position: 2 + display_options: + pager: + type: some + options: + offset: 0 + items_per_page: 10 + style: + type: rss + options: + grouping: { } + uses_fields: false + description: '' + row: + type: node_rss + options: + relationship: none + view_mode: rss + display_extenders: { } + path: rss.xml + sitename_title: true + displays: + page_1: page_1 + default: '' + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_interface' + - '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: node + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_interface' + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } diff --git a/drupal10/config/views.view.glossary.yml b/drupal10/config/views.view.glossary.yml new file mode 100644 index 0000000..5db8dd2 --- /dev/null +++ b/drupal10/config/views.view.glossary.yml @@ -0,0 +1,481 @@ +uuid: 1f7090e5-92cc-484c-a551-48896ca97d94 +langcode: en +status: false +dependencies: + config: + - system.menu.main + module: + - node + - user +_core: + default_config_hash: r-klgGeO4haVHqAwxW1HXZLzdzLSMUVC08OuTBrRh58 +id: glossary +label: Glossary +module: node +description: 'All content, by letter.' +tag: default +base_table: node_field_data +base_field: nid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + 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: Title + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + name: + id: name + table: users_field_data + field: name + relationship: uid + group_type: group + admin_label: '' + entity_type: user + entity_field: name + plugin_id: field + label: Author + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: user_name + changed: + id: changed + table: node_field_data + field: changed + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: changed + plugin_id: field + label: 'Last update' + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: timestamp + settings: + date_format: long + custom_date_format: '' + timezone: '' + tooltip: + date_format: long + custom_date_format: '' + time_diff: + enabled: false + future_format: '@interval hence' + past_format: '@interval ago' + granularity: 2 + refresh: 60 + pager: + type: mini + options: + offset: 0 + pagination_heading_level: h4 + items_per_page: 36 + total_pages: 0 + 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: { } + arguments: + title: + id: title + table: node_field_data + field: title + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: title + plugin_id: string + default_action: default + exception: + title_enable: true + title_enable: false + title: '' + default_argument_type: fixed + default_argument_options: + argument: a + summary_options: { } + summary: + format: default_summary + specify_validation: true + validate: + type: none + fail: 'not found' + validate_options: { } + glossary: true + limit: 1 + case: upper + path_case: lower + transform_dash: false + break_phrase: false + 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: '' + operator_limit_selection: false + operator_list: { } + langcode: + id: langcode + table: node_field_data + field: langcode + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: langcode + plugin_id: language + operator: in + value: + '***LANGUAGE_language_content***': '***LANGUAGE_language_content***' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: true + uses_fields: false + columns: + title: title + name: name + changed: changed + default: title + info: + title: + sortable: true + separator: '' + name: + sortable: true + separator: '' + changed: + sortable: true + separator: '' + override: true + sticky: false + summary: '' + order: asc + empty_table: false + row: + type: fields + options: + default_field_elements: true + inline: { } + separator: '' + hide_empty: false + query: + type: views_query + options: + query_comment: '' + disable_sql_rewrite: false + distinct: false + replica: false + query_tags: { } + relationships: + uid: + id: uid + table: node_field_data + field: uid + relationship: none + group_type: group + admin_label: author + plugin_id: standard + required: false + use_ajax: true + header: { } + footer: { } + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } + attachment_1: + id: attachment_1 + display_title: Attachment + display_plugin: attachment + position: 2 + display_options: + pager: + type: none + options: + offset: 0 + items_per_page: 0 + arguments: + title: + id: title + table: node_field_data + field: title + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: title + plugin_id: string + default_action: summary + exception: + title_enable: true + title_enable: false + title: '' + default_argument_type: fixed + default_argument_options: + argument: a + summary_options: + items_per_page: 25 + inline: true + separator: ' | ' + summary: + format: unformatted_summary + specify_validation: true + validate: + type: none + fail: 'not found' + validate_options: { } + glossary: true + limit: 1 + case: upper + path_case: lower + transform_dash: false + break_phrase: false + query: + type: views_query + options: { } + defaults: + arguments: false + display_extenders: { } + displays: + default: default + page_1: page_1 + inherit_arguments: false + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - 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: + query: + type: views_query + options: { } + display_extenders: { } + path: glossary + menu: + type: normal + title: Glossary + weight: 0 + menu_name: main + parent: '' + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } diff --git a/drupal10/config/views.view.media.yml b/drupal10/config/views.view.media.yml new file mode 100644 index 0000000..a142281 --- /dev/null +++ b/drupal10/config/views.view.media.yml @@ -0,0 +1,920 @@ +uuid: 9607cfe1-2c7b-4003-8287-f3e330ef9e60 +langcode: en +status: true +dependencies: + config: + - image.style.thumbnail + module: + - image + - media + - user +_core: + default_config_hash: kOTUk5XWeupgBOJuRRarCiDcFKVZcvaW-FT5MTzrML8 +id: media +label: Media +module: views +description: 'Find and manage media.' +tag: '' +base_table: media_field_data +base_field: mid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: Media + fields: + media_bulk_form: + id: media_bulk_form + table: media + field: media_bulk_form + relationship: none + group_type: group + admin_label: '' + entity_type: media + plugin_id: bulk_form + 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 + action_title: Action + include_exclude: exclude + selected_actions: { } + thumbnail__target_id: + id: thumbnail__target_id + table: media_field_data + field: thumbnail__target_id + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: thumbnail + plugin_id: field + label: Thumbnail + 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: 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: target_id + type: image + settings: + image_link: '' + image_style: thumbnail + image_loading: + attribute: lazy + group_column: '' + 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 + name: + id: name + table: media_field_data + field: name + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: media + plugin_id: field + label: 'Media name' + 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 + bundle: + id: bundle + table: media_field_data + field: bundle + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: bundle + plugin_id: field + label: Type + 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: 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: target_id + type: entity_reference_label + settings: + link: false + group_column: target_id + 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 + uid: + id: uid + table: media_field_data + field: uid + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: uid + plugin_id: field + label: Author + 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: 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: target_id + type: entity_reference_label + settings: + link: true + group_column: target_id + 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 + status: + id: status + table: media_field_data + field: status + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: status + plugin_id: field + label: Status + 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: 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: boolean + settings: + format: custom + format_custom_false: Unpublished + format_custom_true: Published + 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 + changed: + id: changed + table: media_field_data + field: changed + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: changed + plugin_id: field + label: Updated + 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: 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: timestamp + settings: + date_format: short + custom_date_format: '' + timezone: '' + tooltip: + date_format: long + custom_date_format: '' + time_diff: + enabled: false + future_format: '@interval hence' + past_format: '@interval ago' + granularity: 2 + refresh: 60 + 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 + operations: + id: operations + table: media + field: operations + relationship: none + group_type: group + admin_label: '' + entity_type: media + plugin_id: entity_operations + label: Operations + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + destination: true + pager: + type: full + options: + offset: 0 + pagination_heading_level: h4 + items_per_page: 50 + total_pages: null + id: 0 + tags: + next: 'Next ›' + previous: '‹ Previous' + first: '« First' + last: 'Last »' + 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 + quantity: 9 + exposed_form: + type: basic + options: + submit_button: Filter + 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 media overview' + cache: + type: tag + options: { } + empty: + area_text_custom: + id: area_text_custom + table: views + field: area_text_custom + relationship: none + group_type: group + admin_label: '' + plugin_id: text_custom + empty: true + content: 'No media available.' + tokenize: false + sorts: + created: + id: created + table: media_field_data + field: created + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: created + plugin_id: date + order: DESC + expose: + label: '' + field_identifier: created + exposed: false + granularity: second + arguments: { } + filters: + name: + id: name + table: media_field_data + field: name + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: name + plugin_id: string + operator: contains + value: '' + group: 1 + exposed: true + expose: + operator_id: name_op + label: 'Media name' + description: '' + use_operator: false + operator: name_op + operator_limit_selection: false + operator_list: { } + identifier: name + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + bundle: + id: bundle + table: media_field_data + field: bundle + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: bundle + plugin_id: bundle + operator: in + value: { } + group: 1 + exposed: true + expose: + operator_id: bundle_op + label: Type + description: '' + use_operator: false + operator: bundle_op + operator_limit_selection: false + operator_list: { } + identifier: type + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + status: + id: status + table: media_field_data + field: status + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: status + plugin_id: boolean + operator: '=' + value: '1' + group: 1 + exposed: true + expose: + operator_id: '' + label: 'True' + description: null + use_operator: false + operator: status_op + operator_limit_selection: false + operator_list: { } + identifier: status + required: true + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: true + group_info: + label: 'Published status' + description: '' + identifier: status + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: + 1: + title: Published + operator: '=' + value: '1' + 2: + title: Unpublished + operator: '=' + value: '0' + status_extra: + id: status_extra + table: media_field_data + field: status_extra + relationship: none + group_type: group + admin_label: '' + entity_type: media + plugin_id: media_status + operator: '=' + value: '' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + langcode: + id: langcode + table: media_field_data + field: langcode + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: langcode + plugin_id: language + operator: in + value: { } + group: 1 + exposed: true + expose: + operator_id: langcode_op + label: Language + description: '' + use_operator: false + operator: langcode_op + operator_limit_selection: false + operator_list: { } + identifier: langcode + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: true + columns: + name: name + bundle: bundle + changed: changed + uid: uid + status: status + thumbnail__target_id: thumbnail__target_id + default: changed + info: + name: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + bundle: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + changed: + sortable: true + default_sort_order: desc + align: '' + separator: '' + empty_column: false + responsive: '' + uid: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + status: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + thumbnail__target_id: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + override: true + sticky: false + summary: '' + empty_table: true + caption: '' + description: '' + row: + type: fields + 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: 0 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user + - user.permissions + tags: { } + media_page_list: + id: media_page_list + display_title: Media + display_plugin: page + position: 1 + display_options: + display_description: '' + display_extenders: { } + path: admin/content/media + menu: + type: tab + title: Media + description: '' + weight: 0 + expanded: false + menu_name: main + parent: '' + context: '0' + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user + - user.permissions + tags: { } diff --git a/drupal10/config/views.view.media_library.yml b/drupal10/config/views.view.media_library.yml new file mode 100644 index 0000000..b7fcc3e --- /dev/null +++ b/drupal10/config/views.view.media_library.yml @@ -0,0 +1,1402 @@ +uuid: 5e356d00-2681-443f-a060-0b69dfe0e662 +langcode: en +status: true +dependencies: + config: + - core.entity_view_mode.media.media_library + - image.style.media_library + module: + - image + - media + - media_library + - user + enforced: + module: + - media_library +_core: + default_config_hash: 9u9hwW-Q0-uQJz1vgenJTQhZGYN0FBZoOogwPD6kgkQ +id: media_library +label: 'Media library' +module: views +description: 'Find and manage media.' +tag: '' +base_table: media_field_data +base_field: mid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: Media + fields: + media_bulk_form: + id: media_bulk_form + table: media + field: media_bulk_form + relationship: none + group_type: group + admin_label: '' + entity_type: media + plugin_id: bulk_form + 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 + action_title: Action + include_exclude: exclude + selected_actions: { } + rendered_entity: + id: rendered_entity + table: media + field: rendered_entity + relationship: none + group_type: group + admin_label: '' + entity_type: media + plugin_id: rendered_entity + 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 + view_mode: media_library + pager: + type: mini + options: + offset: 0 + pagination_heading_level: h4 + items_per_page: 24 + total_pages: null + id: 0 + tags: + next: ›› + previous: ‹‹ + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '6, 12, 24, 48' + 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 filters' + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: false + sort_asc_label: Asc + sort_desc_label: Desc + access: + type: perm + options: + perm: 'access media overview' + cache: + type: tag + options: { } + empty: + area_text_custom: + id: area_text_custom + table: views + field: area_text_custom + relationship: none + group_type: group + admin_label: '' + plugin_id: text_custom + empty: true + content: 'No media available.' + tokenize: false + sorts: + created: + id: created + table: media_field_data + field: created + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: created + plugin_id: date + order: DESC + expose: + label: 'Newest first' + field_identifier: created + exposed: true + granularity: second + name: + id: name + table: media_field_data + field: name + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: name + plugin_id: standard + order: ASC + expose: + label: 'Name (A-Z)' + field_identifier: name + exposed: true + name_1: + id: name_1 + table: media_field_data + field: name + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: name + plugin_id: standard + order: DESC + expose: + label: 'Name (Z-A)' + field_identifier: name_1 + exposed: true + filters: + status: + id: status + table: media_field_data + field: status + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: status + plugin_id: boolean + operator: '=' + value: '1' + group: 1 + exposed: true + expose: + operator_id: '' + label: 'Publishing status' + description: null + use_operator: false + operator: status_op + operator_limit_selection: false + operator_list: { } + identifier: status + required: true + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: true + group_info: + label: Published + description: '' + identifier: status + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: + 1: + title: Published + operator: '=' + value: '1' + 2: + title: Unpublished + operator: '=' + value: '0' + name: + id: name + table: media_field_data + field: name + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: name + plugin_id: string + operator: contains + value: '' + group: 1 + exposed: true + expose: + operator_id: name_op + label: Name + description: '' + use_operator: false + operator: name_op + operator_limit_selection: false + operator_list: { } + identifier: name + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + bundle: + id: bundle + table: media_field_data + field: bundle + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: bundle + plugin_id: bundle + operator: in + value: { } + group: 1 + exposed: true + expose: + operator_id: bundle_op + label: 'Media type' + description: '' + use_operator: false + operator: bundle_op + operator_limit_selection: false + operator_list: { } + identifier: type + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + reduce: false + is_grouped: false + group_info: + label: 'Media type' + description: null + identifier: bundle + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: + 1: { } + 2: { } + 3: { } + status_extra: + id: status_extra + table: media_field_data + field: status_extra + relationship: none + group_type: group + admin_label: '' + entity_type: media + plugin_id: media_status + operator: '=' + value: '' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + langcode: + id: langcode + table: media_field_data + field: langcode + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: langcode + plugin_id: language + operator: in + value: { } + group: 1 + exposed: true + expose: + operator_id: langcode_op + label: Language + description: '' + use_operator: false + operator: langcode_op + operator_limit_selection: false + operator_list: { } + identifier: langcode + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + style: + type: default + options: + grouping: { } + row_class: '' + default_row_class: true + row: + type: fields + options: + default_field_elements: true + inline: { } + separator: '' + hide_empty: false + query: + type: views_query + options: + query_comment: '' + disable_sql_rewrite: false + distinct: false + replica: false + query_tags: { } + relationships: { } + css_class: '' + use_ajax: true + header: { } + footer: { } + display_extenders: { } + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_interface' + - url + - url.query_args + - 'url.query_args:sort_by' + - user + - user.permissions + tags: { } + page: + id: page + display_title: Page + display_plugin: page + position: 1 + display_options: + fields: + media_bulk_form: + id: media_bulk_form + table: media + field: media_bulk_form + relationship: none + group_type: group + admin_label: '' + entity_type: media + plugin_id: bulk_form + 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 + action_title: Action + include_exclude: exclude + selected_actions: { } + name: + id: name + table: media_field_data + field: name + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: name + plugin_id: field + label: '' + exclude: true + 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 + click_sort_column: value + type: string + settings: + link_to_entity: false + 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 + edit_media: + id: edit_media + table: media + field: edit_media + relationship: none + group_type: group + admin_label: '' + entity_type: media + plugin_id: entity_link_edit + label: '' + exclude: false + alter: + alter_text: true + text: 'Edit {{ name }}' + make_link: true + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: 'Edit {{ name }}' + 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: '0' + element_wrapper_class: '' + element_default_classes: false + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + text: Edit + output_url_as_text: false + absolute: false + delete_media: + id: delete_media + table: media + field: delete_media + relationship: none + group_type: group + admin_label: '' + entity_type: media + plugin_id: entity_link_delete + label: '' + exclude: false + alter: + alter_text: true + text: 'Delete {{ name }}' + make_link: true + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: 'Delete {{ name }}' + 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: '0' + element_wrapper_class: '' + element_default_classes: false + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + text: Delete + output_url_as_text: false + absolute: false + rendered_entity: + id: rendered_entity + table: media + field: rendered_entity + relationship: none + group_type: group + admin_label: '' + entity_type: media + plugin_id: rendered_entity + 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 + view_mode: media_library + defaults: + fields: false + display_extenders: { } + path: admin/content/media-grid + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - 'url.query_args:sort_by' + - user + - user.permissions + tags: { } + widget: + id: widget + display_title: Widget + display_plugin: page + position: 2 + display_options: + fields: + media_library_select_form: + id: media_library_select_form + table: media + field: media_library_select_form + relationship: none + group_type: group + admin_label: '' + entity_type: media + plugin_id: media_library_select_form + 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 + rendered_entity: + id: rendered_entity + table: media + field: rendered_entity + relationship: none + group_type: group + admin_label: '' + entity_type: media + plugin_id: rendered_entity + 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 + view_mode: media_library + access: + type: perm + options: + perm: 'view media' + arguments: + bundle: + id: bundle + table: media_field_data + field: bundle + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: bundle + plugin_id: string + default_action: ignore + exception: + value: all + title_enable: false + title: All + title_enable: false + title: '' + default_argument_type: fixed + default_argument_options: + argument: '' + summary_options: + base_path: '' + count: true + override: false + items_per_page: 24 + summary: + sort_order: asc + number_of_records: 0 + format: default_summary + specify_validation: false + validate: + type: none + fail: 'not found' + validate_options: { } + glossary: false + limit: 0 + case: none + path_case: none + transform_dash: false + break_phrase: false + filters: + status: + id: status + table: media_field_data + field: status + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: status + plugin_id: boolean + operator: '=' + value: '1' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + name: + id: name + table: media_field_data + field: name + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: name + plugin_id: string + operator: contains + value: '' + group: 1 + exposed: true + expose: + operator_id: name_op + label: Name + description: '' + use_operator: false + operator: name_op + operator_limit_selection: false + operator_list: { } + identifier: name + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + default_langcode: + id: default_langcode + table: media_field_data + field: default_langcode + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: default_langcode + plugin_id: boolean + operator: '=' + value: '1' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + filter_groups: + operator: AND + groups: + 1: AND + defaults: + access: false + css_class: false + fields: false + arguments: false + filters: false + filter_groups: false + header: false + css_class: '' + display_description: '' + header: + display_link_grid: + id: display_link_grid + table: views + field: display_link + plugin_id: display_link + label: Grid + empty: true + display_id: widget + display_link_table: + id: display_link_table + table: views + field: display_link + plugin_id: display_link + label: Table + empty: true + display_id: widget_table + rendering_language: '***LANGUAGE_language_interface***' + display_extenders: { } + path: admin/content/media-widget + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_interface' + - url + - url.query_args + - 'url.query_args:sort_by' + - user.permissions + tags: { } + widget_table: + id: widget_table + display_title: 'Widget (table)' + display_plugin: page + position: 3 + display_options: + fields: + media_library_select_form: + id: media_library_select_form + table: media + field: media_library_select_form + relationship: none + entity_type: media + plugin_id: media_library_select_form + label: '' + element_class: '' + element_wrapper_class: '' + thumbnail__target_id: + id: thumbnail__target_id + table: media_field_data + field: thumbnail__target_id + relationship: none + entity_type: media + entity_field: thumbnail + plugin_id: field + label: Thumbnail + type: image + settings: + image_link: '' + image_style: media_library + image_loading: + attribute: eager + name: + id: name + table: media_field_data + field: name + relationship: none + entity_type: media + entity_field: name + plugin_id: field + label: Name + type: string + settings: + link_to_entity: false + uid: + id: uid + table: media_field_revision + field: uid + relationship: none + entity_type: media + entity_field: uid + plugin_id: field + label: Author + type: entity_reference_label + settings: + link: true + changed: + id: changed + table: media_field_data + field: changed + relationship: none + entity_type: media + entity_field: changed + plugin_id: field + label: Updated + type: timestamp + settings: + date_format: short + custom_date_format: '' + timezone: '' + tooltip: + date_format: long + custom_date_format: '' + time_diff: + enabled: false + future_format: '@interval hence' + past_format: '@interval ago' + granularity: 2 + refresh: 60 + access: + type: perm + options: + perm: 'view media' + arguments: + bundle: + id: bundle + table: media_field_data + field: bundle + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: bundle + plugin_id: string + default_action: ignore + exception: + value: all + title_enable: false + title: All + title_enable: false + title: '' + default_argument_type: fixed + default_argument_options: + argument: '' + summary_options: + base_path: '' + count: true + override: false + items_per_page: 24 + summary: + sort_order: asc + number_of_records: 0 + format: default_summary + specify_validation: false + validate: + type: none + fail: 'not found' + validate_options: { } + glossary: false + limit: 0 + case: none + path_case: none + transform_dash: false + break_phrase: false + filters: + status: + id: status + table: media_field_data + field: status + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: status + plugin_id: boolean + operator: '=' + value: '1' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + name: + id: name + table: media_field_data + field: name + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: name + plugin_id: string + operator: contains + value: '' + group: 1 + exposed: true + expose: + operator_id: name_op + label: Name + description: '' + use_operator: false + operator: name_op + operator_limit_selection: false + operator_list: { } + identifier: name + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + default_langcode: + id: default_langcode + table: media_field_data + field: default_langcode + relationship: none + group_type: group + admin_label: '' + entity_type: media + entity_field: default_langcode + plugin_id: boolean + operator: '=' + value: '1' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + filter_groups: + operator: AND + groups: + 1: AND + style: + type: table + options: + row_class: 'media-library-item media-library-item--table js-media-library-item js-click-to-select' + default_row_class: true + row: + type: fields + defaults: + access: false + css_class: false + style: false + row: false + fields: false + arguments: false + filters: false + filter_groups: false + header: false + css_class: '' + header: + display_link_grid: + id: display_link_grid + table: views + field: display_link + plugin_id: display_link + label: Grid + empty: true + display_id: widget + display_link_table: + id: display_link_table + table: views + field: display_link + plugin_id: display_link + label: Table + empty: true + display_id: widget_table + rendering_language: '***LANGUAGE_language_interface***' + display_extenders: { } + path: admin/content/media-widget-table + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_interface' + - url + - url.query_args + - 'url.query_args:sort_by' + - user.permissions + tags: { } diff --git a/drupal10/config/views.view.taxonomy_term.yml b/drupal10/config/views.view.taxonomy_term.yml new file mode 100644 index 0000000..db54d6e --- /dev/null +++ b/drupal10/config/views.view.taxonomy_term.yml @@ -0,0 +1,319 @@ +uuid: c66113f8-310b-4be3-abd6-aa1e4c40762e +langcode: en +status: true +dependencies: + config: + - core.entity_view_mode.node.teaser + module: + - node + - taxonomy + - user +_core: + default_config_hash: KejdxDJQmx8SaO-t83OVekLw6ar4ldjmsJMGcotGxnQ +id: taxonomy_term +label: 'Taxonomy term' +module: taxonomy +description: 'Content belonging to a certain taxonomy term.' +tag: default +base_table: node_field_data +base_field: nid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + fields: { } + pager: + type: mini + options: + offset: 0 + pagination_heading_level: h4 + items_per_page: 10 + total_pages: 0 + 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: + sticky: + id: sticky + table: taxonomy_index + field: sticky + relationship: none + group_type: group + admin_label: '' + plugin_id: standard + order: DESC + expose: + label: '' + field_identifier: sticky + exposed: false + created: + id: created + table: taxonomy_index + field: created + relationship: none + group_type: group + admin_label: '' + plugin_id: date + order: DESC + expose: + label: '' + field_identifier: created + exposed: false + granularity: second + arguments: + tid: + id: tid + table: taxonomy_index + field: tid + relationship: none + group_type: group + admin_label: '' + plugin_id: taxonomy_index_tid + default_action: 'not found' + exception: + value: '' + title_enable: false + title: All + title_enable: true + title: '{{ arguments.tid }}' + default_argument_type: fixed + default_argument_options: + argument: '' + summary_options: + base_path: '' + count: true + override: false + items_per_page: 25 + summary: + sort_order: asc + number_of_records: 0 + format: default_summary + specify_validation: true + validate: + type: 'entity:taxonomy_term' + fail: 'not found' + validate_options: + bundles: { } + access: true + operation: view + multiple: 0 + break_phrase: false + add_table: false + require_value: false + reduce_duplicates: false + filters: + langcode: + id: langcode + table: node_field_data + field: langcode + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: langcode + plugin_id: language + operator: in + value: + '***LANGUAGE_language_content***': '***LANGUAGE_language_content***' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + status: + id: status + table: taxonomy_index + field: status + relationship: none + group_type: group + admin_label: '' + plugin_id: boolean + operator: '=' + value: '1' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + style: + type: default + options: + grouping: { } + row_class: '' + default_row_class: true + uses_fields: false + 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: { } + link_display: page_1 + link_url: '' + header: + entity_taxonomy_term: + id: entity_taxonomy_term + table: views + field: entity_taxonomy_term + relationship: none + group_type: group + admin_label: '' + plugin_id: entity + empty: true + target: '{{ raw_arguments.tid }}' + view_mode: full + tokenize: true + bypass_access: false + footer: { } + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_interface' + - url + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } + feed_1: + id: feed_1 + display_title: Feed + display_plugin: feed + position: 2 + display_options: + pager: + type: some + options: + offset: 0 + items_per_page: 10 + style: + type: rss + options: + grouping: { } + uses_fields: false + description: '' + row: + type: node_rss + options: + relationship: none + view_mode: default + query: + type: views_query + options: { } + display_extenders: { } + path: taxonomy/term/%/feed + displays: + page_1: page_1 + default: '0' + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_interface' + - url + - 'user.node_grants:view' + - user.permissions + tags: { } + page_1: + id: page_1 + display_title: Page + display_plugin: page + position: 1 + display_options: + query: + type: views_query + options: { } + display_extenders: { } + path: taxonomy/term/% + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_interface' + - url + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } diff --git a/drupal10/config/views.view.user_admin_people.yml b/drupal10/config/views.view.user_admin_people.yml new file mode 100644 index 0000000..3ef6bfd --- /dev/null +++ b/drupal10/config/views.view.user_admin_people.yml @@ -0,0 +1,928 @@ +uuid: 52abd13c-0ce8-411a-b811-d5f9c131bba3 +langcode: en +status: true +dependencies: + module: + - user +_core: + default_config_hash: FFLg74mnzRE10lgg5fspPCZWLnIEcvCnJMlNzj8OEtw +id: user_admin_people +label: People +module: user +description: 'Find and manage people interacting with your site.' +tag: default +base_table: users_field_data +base_field: uid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: People + fields: + user_bulk_form: + id: user_bulk_form + table: users + field: user_bulk_form + relationship: none + group_type: group + admin_label: '' + entity_type: user + plugin_id: user_bulk_form + label: 'Bulk update' + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + name: + id: name + table: users_field_data + field: name + relationship: none + group_type: group + admin_label: '' + entity_type: user + entity_field: name + plugin_id: field + label: Username + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: user_name + status: + id: status + table: users_field_data + field: status + relationship: none + group_type: group + admin_label: '' + entity_type: user + entity_field: status + plugin_id: field + label: Status + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: boolean + settings: + format: custom + format_custom_false: Blocked + format_custom_true: Active + roles_target_id: + id: roles_target_id + table: user__roles + field: roles_target_id + relationship: none + group_type: group + admin_label: '' + plugin_id: user_roles + label: Roles + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: ul + separator: ', ' + created: + id: created + table: users_field_data + field: created + relationship: none + group_type: group + admin_label: '' + entity_type: user + entity_field: created + plugin_id: field + label: 'Member for' + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: timestamp_ago + settings: + future_format: '@interval' + past_format: '@interval' + granularity: 2 + access: + id: access + table: users_field_data + field: access + relationship: none + group_type: group + admin_label: '' + entity_type: user + entity_field: access + plugin_id: field + label: 'Last access' + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + type: timestamp_ago + settings: + future_format: '@interval hence' + past_format: '@interval ago' + granularity: 2 + operations: + id: operations + table: users + field: operations + relationship: none + group_type: group + admin_label: '' + entity_type: user + plugin_id: entity_operations + label: Operations + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + destination: true + mail: + id: mail + table: users_field_data + field: mail + relationship: none + group_type: group + admin_label: '' + entity_type: user + entity_field: mail + plugin_id: field + label: '' + exclude: true + 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 + click_sort_column: value + type: basic_string + settings: { } + 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: full + options: + offset: 0 + pagination_heading_level: h4 + items_per_page: 50 + total_pages: 0 + id: 0 + tags: + next: 'Next ›' + previous: '‹ Previous' + first: '« First' + last: 'Last »' + 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 + quantity: 9 + exposed_form: + type: basic + options: + submit_button: Filter + reset_button: true + 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: 'administer users' + cache: + type: tag + empty: + area_text_custom: + id: area_text_custom + table: views + field: area_text_custom + relationship: none + group_type: group + admin_label: '' + plugin_id: text_custom + empty: true + content: 'No people available.' + tokenize: false + sorts: + created: + id: created + table: users_field_data + field: created + relationship: none + group_type: group + admin_label: '' + entity_type: user + entity_field: created + plugin_id: date + order: DESC + expose: + label: '' + field_identifier: created + exposed: false + granularity: second + filters: + combine: + id: combine + table: views + field: combine + relationship: none + group_type: group + admin_label: '' + plugin_id: combine + operator: contains + value: '' + group: 1 + exposed: true + expose: + operator_id: combine_op + label: 'Name or email contains' + description: '' + use_operator: false + operator: combine_op + operator_limit_selection: false + operator_list: { } + identifier: user + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + fields: + name: name + mail: mail + status: + id: status + table: users_field_data + field: status + relationship: none + group_type: group + admin_label: '' + entity_type: user + entity_field: status + plugin_id: boolean + operator: '=' + value: '1' + group: 1 + exposed: true + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: status_op + operator_limit_selection: false + operator_list: { } + identifier: status + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + is_grouped: true + group_info: + label: Status + description: '' + identifier: status + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: + 1: + title: Active + operator: '=' + value: '1' + 2: + title: Blocked + operator: '=' + value: '0' + roles_target_id: + id: roles_target_id + table: user__roles + field: roles_target_id + relationship: none + group_type: group + admin_label: '' + plugin_id: user_roles + operator: or + value: { } + group: 1 + exposed: true + expose: + operator_id: roles_target_id_op + label: Role + description: '' + use_operator: false + operator: roles_target_id_op + operator_limit_selection: false + operator_list: { } + identifier: role + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + reduce_duplicates: false + permission: + id: permission + table: user__roles + field: permission + relationship: none + group_type: group + admin_label: '' + plugin_id: user_permissions + operator: or + value: { } + group: 1 + exposed: true + expose: + operator_id: permission_op + label: Permission + description: '' + use_operator: false + operator: permission_op + operator_limit_selection: false + operator_list: { } + identifier: permission + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + reduce_duplicates: false + default_langcode: + id: default_langcode + table: users_field_data + field: default_langcode + relationship: none + group_type: group + admin_label: '' + entity_type: user + entity_field: default_langcode + plugin_id: boolean + operator: '=' + value: '1' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + uid_raw: + id: uid_raw + table: users_field_data + field: uid_raw + relationship: none + group_type: group + admin_label: '' + entity_type: user + plugin_id: numeric + operator: '!=' + value: + min: '' + max: '' + value: '0' + group: 1 + exposed: false + expose: + operator_id: '0' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + filter_groups: + operator: AND + groups: + 1: AND + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: true + columns: + user_bulk_form: user_bulk_form + name: name + status: status + rid: rid + created: created + access: access + edit_node: edit_node + dropbutton: dropbutton + default: created + info: + user_bulk_form: + align: '' + separator: '' + empty_column: false + responsive: '' + name: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + status: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: priority-low + rid: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: priority-low + created: + sortable: true + default_sort_order: desc + align: '' + separator: '' + empty_column: false + responsive: priority-low + access: + sortable: true + default_sort_order: desc + align: '' + separator: '' + empty_column: false + responsive: priority-low + edit_node: + align: '' + separator: '' + empty_column: false + responsive: priority-low + dropbutton: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + override: true + sticky: false + summary: '' + empty_table: true + row: + type: fields + query: + type: views_query + options: + query_comment: '' + disable_sql_rewrite: false + distinct: false + replica: false + query_tags: { } + css_class: '' + use_ajax: false + group_by: false + show_admin_links: true + use_more: false + use_more_always: false + use_more_text: more + link_display: page_1 + link_url: '' + display_comment: '' + hide_attachment_summary: false + display_extenders: { } + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.permissions + tags: { } + page_1: + id: page_1 + display_title: Page + display_plugin: page + position: 1 + display_options: + defaults: + show_admin_links: false + show_admin_links: false + display_extenders: { } + path: admin/people/list + menu: + type: 'default tab' + title: List + description: 'Find and manage people interacting with your site.' + weight: -10 + menu_name: admin + context: '' + tab_options: + type: normal + title: People + description: 'Manage user accounts, roles, and permissions.' + weight: 0 + menu_name: admin + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.permissions + tags: { } diff --git a/drupal10/config/views.view.watchdog.yml b/drupal10/config/views.view.watchdog.yml new file mode 100644 index 0000000..1dfa0da --- /dev/null +++ b/drupal10/config/views.view.watchdog.yml @@ -0,0 +1,713 @@ +uuid: b9568b08-0d29-4c36-9bfd-5e0e3749ce0e +langcode: en +status: true +dependencies: + module: + - dblog + - user +_core: + default_config_hash: SR5P89f0vUTPC7WY2YpLAffkDx8-Ar8LKFfowVXeqNU +id: watchdog +label: Watchdog +module: views +description: 'Recent log messages' +tag: '' +base_table: watchdog +base_field: wid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: 'Recent log messages' + fields: + nothing: + id: nothing + table: views + field: nothing + relationship: none + group_type: group + admin_label: Icon + plugin_id: custom + label: '' + exclude: false + alter: + alter_text: true + 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: icon + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: false + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: false + wid: + id: wid + table: watchdog + field: wid + relationship: none + group_type: group + admin_label: '' + plugin_id: standard + label: WID + exclude: true + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + severity: + id: severity + table: watchdog + field: severity + relationship: none + group_type: group + admin_label: '' + plugin_id: machine_name + label: Severity + exclude: true + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + machine_name: false + type: + id: type + table: watchdog + field: type + relationship: none + group_type: group + admin_label: '' + plugin_id: standard + label: Type + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + timestamp: + id: timestamp + table: watchdog + field: timestamp + relationship: none + group_type: group + admin_label: '' + plugin_id: date + label: Date + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + date_format: short + custom_date_format: '' + timezone: '' + message: + id: message + table: watchdog + field: message + relationship: none + group_type: group + admin_label: '' + plugin_id: dblog_message + label: Message + exclude: false + alter: + alter_text: false + text: '' + make_link: true + path: 'admin/reports/dblog/event/{{ wid }}' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '{{ message }}' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 56 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: true + trim: true + preserve_tags: '' + html: true + 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 + replace_variables: true + name: + id: name + table: users_field_data + field: name + relationship: uid + group_type: group + admin_label: '' + entity_type: user + entity_field: name + plugin_id: field + label: User + 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: 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: user_name + 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 + link: + id: link + table: watchdog + field: link + relationship: none + group_type: group + admin_label: '' + plugin_id: dblog_operations + label: Operations + 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: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + pager: + type: mini + options: + offset: 0 + pagination_heading_level: h4 + items_per_page: 50 + 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: Filter + reset_button: true + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: false + sort_asc_label: Asc + sort_desc_label: Desc + access: + type: perm + options: + perm: 'access site reports' + cache: + type: none + options: { } + empty: + area: + id: area_text_custom + table: views + field: area_text_custom + relationship: none + group_type: group + admin_label: 'No log messages available.' + plugin_id: text_custom + empty: true + content: 'No log messages available.' + tokenize: false + sorts: + wid: + id: wid + table: watchdog + field: wid + relationship: none + group_type: group + admin_label: '' + plugin_id: standard + order: DESC + expose: + label: '' + field_identifier: wid + exposed: false + arguments: { } + filters: + type: + id: type + table: watchdog + field: type + relationship: none + group_type: group + admin_label: '' + plugin_id: dblog_types + operator: in + value: { } + group: 1 + exposed: true + expose: + operator_id: type_op + label: Type + description: '' + use_operator: false + operator: type_op + operator_limit_selection: false + operator_list: { } + identifier: type + required: false + remember: false + multiple: true + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + severity: + id: severity + table: watchdog + field: severity + relationship: none + group_type: group + admin_label: '' + plugin_id: in_operator + operator: in + value: { } + group: 1 + exposed: true + expose: + operator_id: severity_op + label: Severity + description: '' + use_operator: false + operator: severity_op + operator_limit_selection: false + operator_list: { } + identifier: severity + required: false + remember: false + multiple: true + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + filter_groups: + operator: AND + groups: + 1: AND + style: + type: table + options: + grouping: { } + row_class: '{{ type }} {{ severity }}' + default_row_class: true + columns: + nothing: nothing + wid: wid + severity: severity + type: type + timestamp: timestamp + message: message + name: name + link: link + default: wid + info: + nothing: + align: '' + separator: '' + empty_column: false + responsive: priority-medium + wid: + sortable: false + default_sort_order: desc + align: '' + separator: '' + empty_column: false + responsive: priority-low + severity: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: priority-low + type: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: priority-medium + timestamp: + sortable: true + default_sort_order: desc + align: '' + separator: '' + empty_column: false + responsive: priority-low + message: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + name: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: priority-medium + link: + align: '' + separator: '' + empty_column: false + responsive: priority-low + override: true + sticky: false + summary: '' + empty_table: false + caption: '' + description: '' + row: + type: fields + query: + type: views_query + options: + query_comment: '' + disable_sql_rewrite: false + distinct: false + replica: false + query_tags: { } + relationships: + uid: + id: uid + table: watchdog + field: uid + relationship: none + group_type: group + admin_label: User + plugin_id: standard + required: false + css_class: admin-dblog + header: { } + footer: { } + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.permissions + tags: { } + page: + id: page + display_title: Page + display_plugin: page + position: 1 + display_options: + display_extenders: { } + path: admin/reports/dblog + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.permissions + tags: { } diff --git a/drupal10/config/views.view.who_s_new.yml b/drupal10/config/views.view.who_s_new.yml new file mode 100644 index 0000000..0544cfc --- /dev/null +++ b/drupal10/config/views.view.who_s_new.yml @@ -0,0 +1,197 @@ +uuid: f6a857cc-742d-40a1-887d-c3cd5a7d57d2 +langcode: en +status: true +dependencies: + module: + - user +_core: + default_config_hash: zji0_13MyVGK7Bn1lUMDeZyyOIZedWvqpYUeM_SioPI +id: who_s_new +label: "Who's new" +module: user +description: 'Shows a list of the newest user accounts on the site.' +tag: default +base_table: users_field_data +base_field: uid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: "Who's new" + fields: + name: + id: name + table: users_field_data + field: name + relationship: none + group_type: group + admin_label: '' + entity_type: user + entity_field: name + 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 + type: user_name + pager: + type: some + options: + offset: 0 + items_per_page: 5 + 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: users_field_data + field: created + relationship: none + group_type: group + admin_label: '' + entity_type: user + entity_field: created + plugin_id: date + order: DESC + expose: + label: '' + field_identifier: created + exposed: false + granularity: second + arguments: { } + filters: + status: + id: status + table: users_field_data + field: status + entity_type: user + entity_field: status + plugin_id: boolean + value: '1' + group: 1 + expose: + operator: '0' + operator_limit_selection: false + operator_list: { } + access: + id: access + table: users_field_data + field: access + relationship: none + group_type: group + admin_label: '' + entity_type: user + entity_field: access + plugin_id: date + operator: '>' + value: + min: '' + max: '' + value: '1970-01-01' + type: date + group: 1 + exposed: false + expose: + operator_id: '0' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + style: + type: html_list + row: + type: fields + 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' + - user.permissions + tags: { } + block_1: + id: block_1 + display_title: "Who's new" + display_plugin: block + position: 1 + display_options: + display_description: 'A list of new users' + display_extenders: { } + block_description: "Who's new" + block_category: User + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - user.permissions + tags: { } diff --git a/drupal10/config/views.view.who_s_online.yml b/drupal10/config/views.view.who_s_online.yml new file mode 100644 index 0000000..015f375 --- /dev/null +++ b/drupal10/config/views.view.who_s_online.yml @@ -0,0 +1,226 @@ +uuid: b67ebb51-da78-4abf-8e5f-3cbe10835283 +langcode: en +status: true +dependencies: + module: + - user +_core: + default_config_hash: suDsVMgawXoQt4rfmdfpr05EVX3z3KyfDDTYgeSM898 +id: who_s_online +label: "Who's online block" +module: user +description: 'Shows the user names of the most recently active users, and the total number of active users.' +tag: default +base_table: users_field_data +base_field: uid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: "Who's online" + fields: + name: + id: name + table: users_field_data + field: name + relationship: none + group_type: group + admin_label: '' + entity_type: user + entity_field: name + 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 + type: user_name + pager: + type: some + options: + offset: 0 + items_per_page: 10 + 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 user profiles' + cache: + type: tag + options: { } + empty: + area_text_custom: + id: area_text_custom + table: views + field: area_text_custom + relationship: none + group_type: group + admin_label: '' + plugin_id: text_custom + empty: true + content: 'There are currently 0 users online.' + tokenize: false + sorts: + access: + id: access + table: users_field_data + field: access + relationship: none + group_type: group + admin_label: '' + entity_type: user + entity_field: access + plugin_id: date + order: DESC + expose: + label: '' + field_identifier: access + exposed: false + granularity: second + arguments: { } + filters: + status: + id: status + table: users_field_data + field: status + entity_type: user + entity_field: status + plugin_id: boolean + value: '1' + group: 1 + expose: + operator: '0' + operator_limit_selection: false + operator_list: { } + access: + id: access + table: users_field_data + field: access + relationship: none + group_type: group + admin_label: '' + entity_type: user + entity_field: access + plugin_id: date + operator: '>=' + value: + min: '' + max: '' + value: '-15 minutes' + type: offset + group: 1 + exposed: false + expose: + operator_id: access_op + label: 'Last access' + description: 'A user is considered online for this long after they have last viewed a page.' + use_operator: false + operator: access_op + operator_limit_selection: false + operator_list: { } + identifier: access + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + style: + type: html_list + options: + grouping: { } + row_class: '' + default_row_class: true + type: ul + wrapper_class: item-list + class: '' + row: + type: fields + query: + type: views_query + options: + query_comment: '' + disable_sql_rewrite: false + distinct: false + replica: false + query_tags: { } + relationships: { } + header: + result: + id: result + table: views + field: result + relationship: none + group_type: group + admin_label: '' + plugin_id: result + empty: false + content: 'There are currently @total users online.' + footer: { } + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - user.permissions + tags: { } + who_s_online_block: + id: who_s_online_block + display_title: "Who's online" + display_plugin: block + position: 1 + display_options: + display_description: 'A list of users that are currently logged in.' + display_extenders: { } + block_description: "Who's online" + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - user.permissions + tags: { } diff --git a/drupal10/web/.csslintrc b/drupal10/web/.csslintrc new file mode 100644 index 0000000..177e4fc --- /dev/null +++ b/drupal10/web/.csslintrc @@ -0,0 +1,40 @@ +--errors=box-model, + display-property-grouping, + duplicate-background-images, + duplicate-properties, + empty-rules, + ids, + import, + important, + known-properties, + outline-none, + overqualified-elements, + qualified-headings, + shorthand, + star-property-hack, + text-indent, + underscore-property-hack, + unique-headings, + unqualified-attributes, + vendor-prefix, + zero-units +--ignore=adjoining-classes, + box-sizing, + bulletproof-font-face, + compatible-vendor-prefixes, + errors, + fallback-colors, + floats, + font-faces, + font-sizes, + gradients, + import-ie-limit, + order-alphabetical, + regex-selectors, + rules-count, + selector-max, + selector-max-approaching, + selector-newline, + universal-selector +--exclude-list=core/assets, + vendor diff --git a/drupal10/web/.eslintignore b/drupal10/web/.eslintignore new file mode 100644 index 0000000..9c13487 --- /dev/null +++ b/drupal10/web/.eslintignore @@ -0,0 +1,8 @@ +core/**/* +vendor/**/* +sites/**/files/**/* +libraries/**/* +sites/**/libraries/**/* +profiles/**/libraries/**/* +**/js_test_files/**/* +**/node_modules/**/* diff --git a/drupal10/web/.eslintrc.json b/drupal10/web/.eslintrc.json new file mode 100644 index 0000000..d4bbc92 --- /dev/null +++ b/drupal10/web/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "./core/.eslintrc.json" +} diff --git a/drupal10/web/.ht.router.php b/drupal10/web/.ht.router.php new file mode 100644 index 0000000..b5884ef --- /dev/null +++ b/drupal10/web/.ht.router.php @@ -0,0 +1,71 @@ + + + Require all denied + + + Order allow,deny + + + +# Don't show directory listings for URLs which map to a directory. +Options -Indexes + +# Set the default handler. +DirectoryIndex index.php index.html index.htm + +# Add correct encoding for SVGZ. +AddType image/svg+xml svg svgz +AddEncoding gzip svgz + +# Most of the following PHP settings cannot be changed at runtime. See +# sites/default/default.settings.php and +# Drupal\Core\DrupalKernel::bootEnvironment() for settings that can be +# changed at runtime. + + php_value assert.active 0 + + +# Requires mod_expires to be enabled. + + # Enable expirations. + ExpiresActive On + + # Cache all files for 1 year after access. + ExpiresDefault "access plus 1 year" + + + # Do not allow PHP scripts to be cached unless they explicitly send cache + # headers themselves. Otherwise all scripts would have to overwrite the + # headers set by mod_expires if they want another caching behavior. This may + # fail if an error occurs early in the bootstrap process, and it may cause + # problems if a non-Drupal PHP file is installed in a subdirectory. + ExpiresActive Off + + + +# Set a fallback resource if mod_rewrite is not enabled. This allows Drupal to +# work without clean URLs. This requires Apache version >= 2.2.16. If Drupal is +# not accessed by the top level URL (i.e.: http://example.com/drupal/ instead of +# http://example.com/), the path to index.php will need to be adjusted. + + FallbackResource /index.php + + +# Various rewrite rules. + + RewriteEngine on + + # Set "protossl" to "s" if we were accessed via https://. This is used later + # if you enable "www." stripping or enforcement, in order to ensure that + # you don't bounce between http and https. + RewriteRule ^ - [E=protossl] + RewriteCond %{HTTPS} on + RewriteRule ^ - [E=protossl:s] + + # Make sure Authorization HTTP header is available to PHP + # even when running as CGI or FastCGI. + RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # Block access to "hidden" directories whose names begin with a period. This + # includes directories used by version control systems such as Subversion or + # Git to store control files. Files whose names begin with a period, as well + # as the control files used by CVS, are protected by the FilesMatch directive + # above. + # + # NOTE: This only works when mod_rewrite is loaded. Without mod_rewrite, it is + # not possible to block access to entire directories from .htaccess because + # is not allowed here. + # + # If you do not have mod_rewrite installed, you should remove these + # directories from your webroot or otherwise protect them from being + # downloaded. + RewriteRule "/\.|^\.(?!well-known/)" - [F] + + # If your site can be accessed both with and without the 'www.' prefix, you + # can use one of the following settings to redirect users to your preferred + # URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option: + # + # To redirect all users to access the site WITH the 'www.' prefix, + # (http://example.com/foo will be redirected to http://www.example.com/foo) + # uncomment the following: + # RewriteCond %{HTTP_HOST} . + # RewriteCond %{HTTP_HOST} !^www\. [NC] + # RewriteRule ^ http%{ENV:protossl}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301] + # + # To redirect all users to access the site WITHOUT the 'www.' prefix, + # (http://www.example.com/foo will be redirected to http://example.com/foo) + # uncomment the following: + # RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] + # RewriteRule ^ http%{ENV:protossl}://%1%{REQUEST_URI} [L,R=301] + + # Modify the RewriteBase if you are using Drupal in a subdirectory or in a + # VirtualDocumentRoot and the rewrite rules are not working properly. + # For example if your site is at http://example.com/drupal uncomment and + # modify the following line: + # RewriteBase /drupal + # + # If your site is running in a VirtualDocumentRoot at http://example.com/, + # uncomment the following line: + # RewriteBase / + + # Redirect common PHP files to their new locations. + RewriteCond %{REQUEST_URI} ^(.*)?/(install\.php) [OR] + RewriteCond %{REQUEST_URI} ^(.*)?/(rebuild\.php) + RewriteCond %{REQUEST_URI} !core + RewriteRule ^ %1/core/%2 [L,QSA,R=301] + + # Rewrite install.php during installation to see if mod_rewrite is working + RewriteRule ^core/install\.php core/install.php?rewrite=ok [QSA,L] + + # Pass all requests not referring directly to files in the filesystem to + # index.php. + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} !=/favicon.ico + RewriteRule ^ index.php [L] + + # For security reasons, deny access to other PHP files on public sites. + # Note: The following URI conditions are not anchored at the start (^), + # because Drupal may be located in a subdirectory. To further improve + # security, you can replace '!/' with '!^/'. + # Allow access to PHP files in /core (like authorize.php or install.php): + RewriteCond %{REQUEST_URI} !/core/[^/]*\.php$ + # Allow access to test-specific PHP files: + RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?\.php + # Allow access to Statistics module's custom front controller. + # Copy and adapt this rule to directly execute PHP files in contributed or + # custom modules or to run another PHP application in the same directory. + RewriteCond %{REQUEST_URI} !/core/modules/statistics/statistics\.php$ + # Deny access to any other PHP files that do not match the rules above. + # Specifically, disallow autoload.php from being served directly. + RewriteRule "^(.+/.*|autoload)\.php($|/)" - [F] + + # Rules to correctly serve gzip compressed CSS and JS files. + # Requires both mod_rewrite and mod_headers to be enabled. + + # Serve gzip compressed CSS files if they exist and the client accepts gzip. + RewriteCond %{HTTP:Accept-encoding} gzip + RewriteCond %{REQUEST_FILENAME}\.gz -s + RewriteRule ^(.*css_[a-zA-Z0-9-_]+)\.css$ $1\.css\.gz [QSA] + + # Serve gzip compressed JS files if they exist and the client accepts gzip. + RewriteCond %{HTTP:Accept-encoding} gzip + RewriteCond %{REQUEST_FILENAME}\.gz -s + RewriteRule ^(.*js_[a-zA-Z0-9-_]+)\.js$ $1\.js\.gz [QSA] + + # Serve correct content types, and prevent double compression. + RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1,E=no-brotli:1] + RewriteRule \.js\.gz$ - [T=text/javascript,E=no-gzip:1,E=no-brotli:1] + + + # Serve correct encoding type. + Header set Content-Encoding gzip + # Force proxies to cache gzipped & non-gzipped css/js files separately. + Header append Vary Accept-Encoding + + + + +# Various header fixes. + + # Disable content sniffing for all responses, since it's an attack vector. + # This header is also set in FinishResponseSubscriber, which depending on + # Apache configuration might get placed in the 'onsuccess' table. To prevent + # header duplication, unset that one prior to setting in the 'always' table. + # See "To circumvent this limitation..." in + # https://httpd.apache.org/docs/current/mod/mod_headers.html. + Header onsuccess unset X-Content-Type-Options + Header always set X-Content-Type-Options nosniff + # Disable Proxy header, since it's an attack vector. + RequestHeader unset Proxy + diff --git a/drupal10/web/INSTALL.txt b/drupal10/web/INSTALL.txt new file mode 100644 index 0000000..be9e29d --- /dev/null +++ b/drupal10/web/INSTALL.txt @@ -0,0 +1,3 @@ + +Read core/INSTALL.txt for detailed installation instructions for your Drupal +website. diff --git a/drupal10/web/README.md b/drupal10/web/README.md new file mode 100644 index 0000000..c3e5371 --- /dev/null +++ b/drupal10/web/README.md @@ -0,0 +1,75 @@ +Drupal Logo + +Drupal is an open source content management platform supporting a variety of +websites ranging from personal weblogs to large community-driven websites. For +more information, visit the Drupal website, [Drupal.org][Drupal.org], and join +the [Drupal community][Drupal community]. + +## Contributing + +Drupal is developed on [Drupal.org][Drupal.org], the home of the international +Drupal community since 2001! + +[Drupal.org][Drupal.org] hosts Drupal's [GitLab repository][GitLab repository], +its [issue queue][issue queue], and its [documentation][documentation]. Before +you start working on code, be sure to search the [issue queue][issue queue] and +create an issue if your aren't able to find an existing issue. + +Every issue on Drupal.org automatically creates a new community-accessible fork +that you can contribute to. Learn more about the code contribution process on +the [Issue forks & merge requests page][issue forks]. + +## Usage + +For a brief introduction, see [USAGE.txt](/core/USAGE.txt). You can also find +guides, API references, and more by visiting Drupal's [documentation +page][documentation]. + +You can quickly extend Drupal's core feature set by installing any of its +[thousands of free and open source modules][modules]. With Drupal and its +module ecosystem, you can often build most or all of what your project needs +before writing a single line of code. + +## Changelog + +Drupal keeps detailed [change records][changelog]. You can search Drupal's +changes for a record of every notable breaking change and new feature since +2011. + +## Security + +For a list of security announcements, see the [Security advisories +page][Security advisories] (available as [an RSS feed][security RSS]). This +page also describes how to subscribe to these announcements via email. + +For information about the Drupal security process, or to find out how to report +a potential security issue to the Drupal security team, see the [Security team +page][security team]. + +## Need a helping hand? + +Visit the [Support page][support] or browse [over a thousand Drupal +providers][service providers] offering design, strategy, development, and +hosting services. + +## Legal matters + +Know your rights when using Drupal by reading Drupal core's +[license](/core/LICENSE.txt). + +Learn about the [Drupal trademark and logo policy here][trademark]. + +[Drupal.org]: https://www.drupal.org +[Drupal community]: https://www.drupal.org/community +[GitLab repository]: https://git.drupalcode.org/project/drupal +[issue queue]: https://www.drupal.org/project/issues/drupal +[issue forks]: https://www.drupal.org/drupalorg/docs/gitlab-integration/issue-forks-merge-requests +[documentation]: https://www.drupal.org/documentation +[changelog]: https://www.drupal.org/list-changes/drupal +[modules]: https://www.drupal.org/project/project_module +[security advisories]: https://www.drupal.org/security +[security RSS]: https://www.drupal.org/security/rss.xml +[security team]: https://www.drupal.org/drupal-security-team +[service providers]: https://www.drupal.org/drupal-services +[support]: https://www.drupal.org/support +[trademark]: https://www.drupal.com/trademark diff --git a/drupal10/web/autoload.php b/drupal10/web/autoload.php new file mode 100644 index 0000000..7379151 --- /dev/null +++ b/drupal10/web/autoload.php @@ -0,0 +1,15 @@ +handle($request); +$response->send(); + +$kernel->terminate($request, $response); diff --git a/drupal10/web/modules/README.txt b/drupal10/web/modules/README.txt new file mode 100644 index 0000000..249e817 --- /dev/null +++ b/drupal10/web/modules/README.txt @@ -0,0 +1,42 @@ +Modules extend your site functionality beyond Drupal core. + +WHAT TO PLACE IN THIS DIRECTORY? +-------------------------------- + +Placing downloaded and custom modules in this directory separates downloaded and +custom modules from Drupal core's modules. This allows Drupal core to be updated +without overwriting these files. + +DOWNLOAD ADDITIONAL MODULES +--------------------------- + +Contributed modules from the Drupal community may be downloaded at +https://www.drupal.org/project/project_module. + +ORGANIZING MODULES IN THIS DIRECTORY +------------------------------------ + +You may create subdirectories in this directory, to organize your added modules, +without breaking the site. Some common subdirectories include "contrib" for +contributed modules, and "custom" for custom modules. Note that if you move a +module to a subdirectory after it has been enabled, you may need to clear the +Drupal cache so it can be found. + +There are number of directories that are ignored when looking for modules. These +are 'src', 'lib', 'vendor', 'assets', 'css', 'files', 'images', 'js', 'misc', +'templates', 'includes', 'fixtures' and 'Drupal'. + +MULTISITE CONFIGURATION +----------------------- + +In multisite configurations, modules found in this directory are available to +all sites. You may also put modules in the sites/all/modules directory, and the +versions in sites/all/modules will take precedence over versions of the same +module that are here. Alternatively, the sites/your_site_name/modules directory +pattern may be used to restrict modules to a specific site instance. + +MORE INFORMATION +---------------- + +Refer to the “Developing for Drupal” section of the README.md in the Drupal +root directory for further information on extending Drupal with custom modules. diff --git a/drupal10/web/modules/custom/.gitkeep b/drupal10/web/modules/custom/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/drupal10/web/modules/custom/tag1_migration/migrations/.gitkeep b/drupal10/web/modules/custom/tag1_migration/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/drupal10/web/modules/custom/tag1_migration/tag1_migration.info.yml b/drupal10/web/modules/custom/tag1_migration/tag1_migration.info.yml new file mode 100644 index 0000000..d25fa2f --- /dev/null +++ b/drupal10/web/modules/custom/tag1_migration/tag1_migration.info.yml @@ -0,0 +1,9 @@ +name: Tag1 Content Migration Example +type: module +description: Drupal 7 to Drupal 10 content migration example +package: Tag1 +core_version_requirement: ^10 || ^11 +dependencies: + - drupal:migrate + - drupal:migrate_drupal + - migrate_plus:migrate_plus diff --git a/drupal10/web/modules/custom/tag1_migration/tag1_migration_config/migrations/.gitkeep b/drupal10/web/modules/custom/tag1_migration/tag1_migration_config/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/drupal10/web/modules/custom/tag1_migration/tag1_migration_config/tag1_migration_config.info.yml b/drupal10/web/modules/custom/tag1_migration/tag1_migration_config/tag1_migration_config.info.yml new file mode 100644 index 0000000..7d1102e --- /dev/null +++ b/drupal10/web/modules/custom/tag1_migration/tag1_migration_config/tag1_migration_config.info.yml @@ -0,0 +1,9 @@ +name: Tag1 Configuration Migration Example +type: module +description: Drupal 7 to Drupal 10 configuration migration example +package: Tag1 +core_version_requirement: ^10 || ^11 +dependencies: + - drupal:migrate + - drupal:migrate_drupal + - migrate_plus:migrate_plus diff --git a/drupal10/web/profiles/README.txt b/drupal10/web/profiles/README.txt new file mode 100644 index 0000000..6d11a51 --- /dev/null +++ b/drupal10/web/profiles/README.txt @@ -0,0 +1,28 @@ +Installation profiles define additional steps that run after the base +installation of Drupal is completed. They may also offer additional +functionality and change the behavior of the site. + +WHAT TO PLACE IN THIS DIRECTORY? +-------------------------------- + +Place downloaded and custom installation profiles in this directory. +Note that installation profiles are generally provided as part of a Drupal +distribution. + +DOWNLOAD ADDITIONAL DISTRIBUTIONS +--------------------------------- + +Contributed distributions from the Drupal community may be downloaded at +https://www.drupal.org/project/project_distribution. + +MULTISITE CONFIGURATION +----------------------- + +In multisite configurations, installation profiles found in this directory are +available to all sites during their initial site installation. + +MORE INFORMATION +---------------- + +Refer to the "Installation profiles" section of the README.md in the Drupal +root directory for further information on extending Drupal with custom profiles. diff --git a/drupal10/web/profiles/tag1_profile/tag1_profile.info.yml b/drupal10/web/profiles/tag1_profile/tag1_profile.info.yml new file mode 100644 index 0000000..186f69b --- /dev/null +++ b/drupal10/web/profiles/tag1_profile/tag1_profile.info.yml @@ -0,0 +1,12 @@ +name: 'Tag1 profile' +type: profile +description: 'Custom installation profile for Drupal 7 to Drupal 10 migration' +core_version_requirement: ^10 +install: + - node + - block + - dblog + - page_cache + - dynamic_page_cache +themes: + - stark diff --git a/drupal10/web/robots.txt b/drupal10/web/robots.txt new file mode 100644 index 0000000..3ad8e2e --- /dev/null +++ b/drupal10/web/robots.txt @@ -0,0 +1,73 @@ +# +# robots.txt +# +# This file is to prevent the crawling and indexing of certain parts +# of your site by web crawlers and spiders run by sites like Yahoo! +# and Google. By telling these "robots" where not to go on your site, +# you save bandwidth and server resources. +# +# This file will be ignored unless it is at the root of your host: +# Used: http://example.com/robots.txt +# Ignored: http://example.com/site/robots.txt +# +# For more information about the robots.txt standard, see: +# http://www.robotstxt.org/robotstxt.html + +User-agent: * +# CSS, JS, Images +Allow: /core/*.css$ +Allow: /core/*.css? +Allow: /core/*.js$ +Allow: /core/*.js? +Allow: /core/*.gif +Allow: /core/*.jpg +Allow: /core/*.jpeg +Allow: /core/*.png +Allow: /core/*.svg +Allow: /profiles/*.css$ +Allow: /profiles/*.css? +Allow: /profiles/*.js$ +Allow: /profiles/*.js? +Allow: /profiles/*.gif +Allow: /profiles/*.jpg +Allow: /profiles/*.jpeg +Allow: /profiles/*.png +Allow: /profiles/*.svg +# Directories +Disallow: /core/ +Disallow: /profiles/ +# Files +Disallow: /README.md +Disallow: /composer/Metapackage/README.txt +Disallow: /composer/Plugin/ProjectMessage/README.md +Disallow: /composer/Plugin/Scaffold/README.md +Disallow: /composer/Plugin/VendorHardening/README.txt +Disallow: /composer/Template/README.txt +Disallow: /modules/README.txt +Disallow: /sites/README.txt +Disallow: /themes/README.txt +Disallow: /web.config +# Paths (clean URLs) +Disallow: /admin/ +Disallow: /comment/reply/ +Disallow: /filter/tips +Disallow: /node/add/ +Disallow: /search/ +Disallow: /user/register +Disallow: /user/password +Disallow: /user/login +Disallow: /user/logout +Disallow: /media/oembed +Disallow: /*/media/oembed +# Paths (no clean URLs) +Disallow: /index.php/admin/ +Disallow: /index.php/comment/reply/ +Disallow: /index.php/filter/tips +Disallow: /index.php/node/add/ +Disallow: /index.php/search/ +Disallow: /index.php/user/password +Disallow: /index.php/user/register +Disallow: /index.php/user/login +Disallow: /index.php/user/logout +Disallow: /index.php/media/oembed +Disallow: /index.php/*/media/oembed diff --git a/drupal10/web/sites/README.txt b/drupal10/web/sites/README.txt new file mode 100644 index 0000000..0372902 --- /dev/null +++ b/drupal10/web/sites/README.txt @@ -0,0 +1,10 @@ +This directory structure contains the settings and configuration files specific +to your site or sites and is an integral part of multisite configurations. + +It is now recommended to place your custom and downloaded extensions in the +/modules, /themes, and /profiles directories located in the Drupal root. The +sites/all/ subdirectory structure, which was recommended in previous versions +of Drupal, is still supported. + +See core/INSTALL.txt for information about single-site installation or +multisite configuration. diff --git a/drupal10/web/sites/default/.gitignore b/drupal10/web/sites/default/.gitignore new file mode 100644 index 0000000..475b99a --- /dev/null +++ b/drupal10/web/sites/default/.gitignore @@ -0,0 +1 @@ +/drushrc.php diff --git a/drupal10/web/sites/default/default.services.yml b/drupal10/web/sites/default/default.services.yml new file mode 100644 index 0000000..dacb3f7 --- /dev/null +++ b/drupal10/web/sites/default/default.services.yml @@ -0,0 +1,239 @@ +parameters: + # Toggles the super user access policy. If your website has at least one user + # with the Administrator role, it is advised to set this to false. This allows + # you to make user 1 a regular user, strengthening the security of your site. + security.enable_super_user: true + session.storage.options: + # Default ini options for sessions. + # + # Some distributions of Linux (most notably Debian) ship their PHP + # installations with garbage collection (gc) disabled. Since Drupal depends + # on PHP's garbage collection for clearing sessions, ensure that garbage + # collection occurs by using the most common settings. + # @default 1 + gc_probability: 1 + # @default 100 + gc_divisor: 100 + # + # Set session lifetime (in seconds), i.e. the grace period for session + # data. Sessions are deleted by the session garbage collector after one + # session lifetime has elapsed since the user's last visit. When a session + # is deleted, authenticated users are logged out, and the contents of the + # user's session is discarded. + # @default 200000 + gc_maxlifetime: 200000 + # + # Set session cookie lifetime (in seconds), i.e. the time from the session + # is created to the cookie expires, i.e. when the browser is expected to + # discard the cookie. The value 0 means "until the browser is closed". + # @default 2000000 + cookie_lifetime: 2000000 + # + # Drupal automatically generates a unique session cookie name based on the + # full domain name used to access the site. This mechanism is sufficient + # for most use-cases, including multi-site deployments. However, if it is + # desired that a session can be reused across different subdomains, the + # cookie domain needs to be set to the shared base domain. Doing so assures + # that users remain logged in as they cross between various subdomains. + # To maximize compatibility and normalize the behavior across user agents, + # the cookie domain should start with a dot. + # + # @default none + # cookie_domain: '.example.com' + # + # Set the SameSite cookie attribute: 'None', 'Lax', or 'Strict'. If set, + # this value will override the server value. See + # https://www.php.net/manual/en/session.security.ini.php for more + # information. + # @default no value + cookie_samesite: Lax + # + # Set the session ID string length. The length can be between 22 to 256. The + # PHP recommended value is 48. See + # https://www.php.net/manual/session.security.ini.php for more information. + # This value should be kept in sync with + # \Drupal\Core\Session\SessionConfiguration::__construct() + # @default 48 + sid_length: 48 + # + # Set the number of bits in encoded session ID character. The possible + # values are '4' (0-9, a-f), '5' (0-9, a-v), and '6' (0-9, a-z, A-Z, "-", + # ","). The PHP recommended value is 6. See + # https://www.php.net/manual/session.security.ini.php for more information. + # This value should be kept in sync with + # \Drupal\Core\Session\SessionConfiguration::__construct() + # @default 6 + sid_bits_per_character: 6 + # By default, Drupal generates a session cookie name based on the full + # domain name. Set the name_suffix to a short random string to ensure this + # session cookie name is unique on different installations on the same + # domain and path (for example, when migrating from Drupal 7). + name_suffix: '' + twig.config: + # Twig debugging: + # + # When debugging is enabled: + # - The markup of each Twig template is surrounded by HTML comments that + # contain theming information, such as template file name suggestions. + # - Note that this debugging markup will cause automated tests that directly + # check rendered HTML to fail. When running automated tests, 'debug' + # should be set to FALSE. + # - The dump() function can be used in Twig templates to output information + # about template variables. + # - Twig templates are automatically recompiled whenever the source code + # changes (see auto_reload below). + # + # For more information about debugging Twig templates, see + # https://www.drupal.org/node/1906392. + # + # Enabling Twig debugging is not recommended in production environments. + # @default false + debug: false + # Twig auto-reload: + # + # Automatically recompile Twig templates whenever the source code changes. + # If you don't provide a value for auto_reload, it will be determined + # based on the value of debug. + # + # Enabling auto-reload is not recommended in production environments. + # @default null + auto_reload: null + # Twig cache: + # + # By default, Twig templates will be compiled and stored in the filesystem + # to increase performance. Disabling the Twig cache will recompile the + # templates from source each time they are used. In most cases the + # auto_reload setting above should be enabled rather than disabling the + # Twig cache. + # + # Disabling the Twig cache is not recommended in production environments. + # @default true + cache: true + # File extensions: + # + # List of file extensions the Twig system is allowed to load via the + # twig.loader.filesystem service. Files with other extensions will not be + # loaded unless they are added here. For example, to allow a file named + # 'example.partial' to be loaded, add 'partial' to this list. To load files + # with no extension, add an empty string '' to the list. + # + # @default ['css', 'html', 'js', 'svg', 'twig'] + allowed_file_extensions: + - css + - html + - js + - svg + - twig + renderer.config: + # Renderer required cache contexts: + # + # The Renderer will automatically associate these cache contexts with every + # render array, hence varying every render array by these cache contexts. + # + # @default ['languages:language_interface', 'theme', 'user.permissions'] + required_cache_contexts: ['languages:language_interface', 'theme', 'user.permissions'] + # Renderer automatic placeholdering conditions: + # + # Drupal allows portions of the page to be automatically deferred when + # rendering to improve cache performance. That is especially helpful for + # cache contexts that vary widely, such as the active user. On some sites + # those may be different, however, such as sites with only a handful of + # users. If you know what the high-cardinality cache contexts are for your + # site, specify those here. If you're not sure, the defaults are fairly safe + # in general. + # + # For more information about rendering optimizations see + # https://www.drupal.org/developing/api/8/render/arrays/cacheability#optimizing + auto_placeholder_conditions: + # Max-age at or below which caching is not considered worthwhile. + # + # Disable by setting to -1. + # + # @default 0 + max-age: 0 + # Cache contexts with a high cardinality. + # + # Disable by setting to []. + # + # @default ['session', 'user'] + contexts: ['session', 'user'] + # Tags with a high invalidation frequency. + # + # Disable by setting to []. + # + # @default [] + tags: [] + # Renderer cache debug: + # + # Allows cache debugging output for each rendered element. + # + # Enabling render cache debugging is not recommended in production + # environments. + # @default false + debug: false + # Cacheability debugging: + # + # Responses with cacheability metadata (CacheableResponseInterface instances) + # get X-Drupal-Cache-Tags, X-Drupal-Cache-Contexts and X-Drupal-Cache-Max-Age + # headers. + # + # For more information about debugging cacheable responses, see + # https://www.drupal.org/developing/api/8/response/cacheable-response-interface + # + # Enabling cacheability debugging is not recommended in production + # environments. + # @default false + http.response.debug_cacheability_headers: false + factory.keyvalue: {} + # Default key/value storage service to use. + # @default keyvalue.database + # default: keyvalue.database + # Collection-specific overrides. + # state: keyvalue.database + factory.keyvalue.expirable: {} + # Default key/value expirable storage service to use. + # @default keyvalue.database.expirable + # default: keyvalue.database.expirable + # Allowed protocols for URL generation. + filter_protocols: + - http + - https + - ftp + - news + - nntp + - tel + - telnet + - mailto + - irc + - ssh + - sftp + - webcal + - rtsp + + # Configure Cross-Site HTTP requests (CORS). + # Read https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS + # for more information about the topic in general. + # Note: By default the configuration is disabled. + cors.config: + enabled: false + # Specify allowed headers, like 'x-allowed-header'. + allowedHeaders: [] + # Specify allowed request methods, specify ['*'] to allow all possible ones. + allowedMethods: [] + # Configure requests allowed from specific origins. Do not include trailing + # slashes with URLs. + allowedOrigins: ['*'] + # Configure requests allowed from origins, matching against regex patterns. + allowedOriginsPatterns: [] + # Sets the Access-Control-Expose-Headers header. + exposedHeaders: false + # Sets the Access-Control-Max-Age header. + maxAge: false + # Sets the Access-Control-Allow-Credentials header. + supportsCredentials: false + + queue.config: + # The maximum number of seconds to wait if a queue is temporarily suspended. + # This is not applicable when a queue is suspended but does not specify + # how long to wait before attempting to resume. + suspendMaximumWait: 30 diff --git a/drupal10/web/sites/default/default.settings.php b/drupal10/web/sites/default/default.settings.php new file mode 100644 index 0000000..264597b --- /dev/null +++ b/drupal10/web/sites/default/default.settings.php @@ -0,0 +1,892 @@ + 'database_name', + * 'username' => 'sql_username', + * 'password' => 'sql_password', + * 'host' => 'localhost', + * 'port' => '3306', + * 'driver' => 'mysql', + * 'prefix' => '', + * 'collation' => 'utf8mb4_general_ci', + * ]; + * @endcode + */ +$databases = []; + +/** + * Customizing database settings. + * + * Many of the values of the $databases array can be customized for your + * particular database system. Refer to the sample in the section above as a + * starting point. + * + * The "driver" property indicates what Drupal database driver the + * connection should use. This is usually the same as the name of the + * database type, such as mysql or sqlite, but not always. The other + * properties will vary depending on the driver. For SQLite, you must + * specify a database file name in a directory that is writable by the + * webserver. For most other drivers, you must specify a + * username, password, host, and database name. + * + * Drupal core implements drivers for mysql, pgsql, and sqlite. Other drivers + * can be provided by contributed or custom modules. To use a contributed or + * custom driver, the "namespace" property must be set to the namespace of the + * driver. The code in this namespace must be autoloadable prior to connecting + * to the database, and therefore, prior to when module root namespaces are + * added to the autoloader. To add the driver's namespace to the autoloader, + * set the "autoload" property to the PSR-4 base directory of the driver's + * namespace. This is optional for projects managed with Composer if the + * driver's namespace is in Composer's autoloader. + * + * For each database, you may optionally specify multiple "target" databases. + * A target database allows Drupal to try to send certain queries to a + * different database if it can but fall back to the default connection if not. + * That is useful for primary/replica replication, as Drupal may try to connect + * to a replica server when appropriate and if one is not available will simply + * fall back to the single primary server (The terms primary/replica are + * traditionally referred to as master/slave in database server documentation). + * + * The general format for the $databases array is as follows: + * @code + * $databases['default']['default'] = $info_array; + * $databases['default']['replica'][] = $info_array; + * $databases['default']['replica'][] = $info_array; + * $databases['extra']['default'] = $info_array; + * @endcode + * + * In the above example, $info_array is an array of settings described above. + * The first line sets a "default" database that has one primary database + * (the second level default). The second and third lines create an array + * of potential replica databases. Drupal will select one at random for a given + * request as needed. The fourth line creates a new database with a name of + * "extra". + * + * For MySQL, MariaDB or equivalent databases the 'isolation_level' option can + * be set. The recommended transaction isolation level for Drupal sites is + * 'READ COMMITTED'. The 'REPEATABLE READ' option is supported but can result + * in deadlocks, the other two options are 'READ UNCOMMITTED' and 'SERIALIZABLE'. + * They are available but not supported; use them at your own risk. For more + * info: + * https://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-isolation-levels.html + * + * On your settings.php, change the isolation level: + * @code + * $databases['default']['default']['init_commands'] = [ + * 'isolation_level' => 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', + * ]; + * @endcode + * + * You can optionally set a prefix for all database table names by using the + * 'prefix' setting. If a prefix is specified, the table name will be prepended + * with its value. Be sure to use valid database characters only, usually + * alphanumeric and underscore. If no prefix is desired, do not set the 'prefix' + * key or set its value to an empty string ''. + * + * For example, to have all database table prefixed with 'main_', set: + * @code + * 'prefix' => 'main_', + * @endcode + * + * Advanced users can add or override initial commands to execute when + * connecting to the database server, as well as PDO connection settings. For + * example, to enable MySQL SELECT queries to exceed the max_join_size system + * variable, and to reduce the database connection timeout to 5 seconds: + * @code + * $databases['default']['default'] = [ + * 'init_commands' => [ + * 'big_selects' => 'SET SQL_BIG_SELECTS=1', + * ], + * 'pdo' => [ + * PDO::ATTR_TIMEOUT => 5, + * ], + * ]; + * @endcode + * + * WARNING: The above defaults are designed for database portability. Changing + * them may cause unexpected behavior, including potential data loss. See + * https://www.drupal.org/docs/8/api/database-api/database-configuration for + * more information on these defaults and the potential issues. + * + * More details can be found in the constructor methods for each driver: + * - \Drupal\mysql\Driver\Database\mysql\Connection::__construct() + * - \Drupal\pgsql\Driver\Database\pgsql\Connection::__construct() + * - \Drupal\sqlite\Driver\Database\sqlite\Connection::__construct() + * + * Sample Database configuration format for PostgreSQL (pgsql): + * @code + * $databases['default']['default'] = [ + * 'driver' => 'pgsql', + * 'database' => 'database_name', + * 'username' => 'sql_username', + * 'password' => 'sql_password', + * 'host' => 'localhost', + * 'prefix' => '', + * ]; + * @endcode + * + * Sample Database configuration format for SQLite (sqlite): + * @code + * $databases['default']['default'] = [ + * 'driver' => 'sqlite', + * 'database' => '/path/to/database_filename', + * ]; + * @endcode + * + * Sample Database configuration format for a driver in a contributed module: + * @code + * $databases['default']['default'] = [ + * 'driver' => 'my_driver', + * 'namespace' => 'Drupal\my_module\Driver\Database\my_driver', + * 'autoload' => 'modules/my_module/src/Driver/Database/my_driver/', + * 'database' => 'database_name', + * 'username' => 'sql_username', + * 'password' => 'sql_password', + * 'host' => 'localhost', + * 'prefix' => '', + * ]; + * @endcode + * + * Sample Database configuration format for a driver that is extending another + * database driver. + * @code + * $databases['default']['default'] = [ + * 'driver' => 'my_driver', + * 'namespace' => 'Drupal\my_module\Driver\Database\my_driver', + * 'autoload' => 'modules/my_module/src/Driver/Database/my_driver/', + * 'database' => 'database_name', + * 'username' => 'sql_username', + * 'password' => 'sql_password', + * 'host' => 'localhost', + * 'prefix' => '', + * 'dependencies' => [ + * 'parent_module' => [ + * 'namespace' => 'Drupal\parent_module', + * 'autoload' => 'core/modules/parent_module/src/', + * ], + * ], + * ]; + * @endcode + */ + +/** + * Location of the site configuration files. + * + * The $settings['config_sync_directory'] specifies the location of file system + * directory used for syncing configuration data. On install, the directory is + * created. This is used for configuration imports. + * + * The default location for this directory is inside a randomly-named + * directory in the public files path. The setting below allows you to set + * its location. + */ +# $settings['config_sync_directory'] = '/directory/outside/webroot'; + +/** + * Settings: + * + * $settings contains environment-specific configuration, such as the files + * directory and reverse proxy address, and temporary configuration, such as + * security overrides. + * + * @see \Drupal\Core\Site\Settings::get() + */ + +/** + * Salt for one-time login links, cancel links, form tokens, etc. + * + * This variable will be set to a random value by the installer. All one-time + * login links will be invalidated if the value is changed. Note that if your + * site is deployed on a cluster of web servers, you must ensure that this + * variable has the same value on each server. + * + * For enhanced security, you may set this variable to the contents of a file + * outside your document root, and vary the value across environments (like + * production and development); you should also ensure that this file is not + * stored with backups of your database. + * + * Example: + * @code + * $settings['hash_salt'] = file_get_contents('/home/example/salt.txt'); + * @endcode + */ +$settings['hash_salt'] = ''; + +/** + * Deployment identifier. + * + * Drupal's dependency injection container will be automatically invalidated and + * rebuilt when the Drupal core version changes. When updating contributed or + * custom code that changes the container, changing this identifier will also + * allow the container to be invalidated as soon as code is deployed. + */ +# $settings['deployment_identifier'] = \Drupal::VERSION; + +/** + * Access control for update.php script. + * + * If you are updating your Drupal installation using the update.php script but + * are not logged in using either an account with the "Administer software + * updates" permission or the site maintenance account (the account that was + * created during installation), you will need to modify the access check + * statement below. Change the FALSE to a TRUE to disable the access check. + * After finishing the upgrade, be sure to open this file again and change the + * TRUE back to a FALSE! + */ +$settings['update_free_access'] = FALSE; + +/** + * Fallback to HTTP for Update Manager and for fetching security advisories. + * + * If your site fails to connect to updates.drupal.org over HTTPS (either when + * fetching data on available updates, or when fetching the feed of critical + * security announcements), you may uncomment this setting and set it to TRUE to + * allow an insecure fallback to HTTP. Note that doing so will open your site up + * to a potential man-in-the-middle attack. You should instead attempt to + * resolve the issues before enabling this option. + * @see https://www.drupal.org/docs/system-requirements/php-requirements#openssl + * @see https://en.wikipedia.org/wiki/Man-in-the-middle_attack + * @see \Drupal\update\UpdateFetcher + * @see \Drupal\system\SecurityAdvisories\SecurityAdvisoriesFetcher + */ +# $settings['update_fetch_with_http_fallback'] = TRUE; + +/** + * External access proxy settings: + * + * If your site must access the Internet via a web proxy then you can enter the + * proxy settings here. Set the full URL of the proxy, including the port, in + * variables: + * - $settings['http_client_config']['proxy']['http']: The proxy URL for HTTP + * requests. + * - $settings['http_client_config']['proxy']['https']: The proxy URL for HTTPS + * requests. + * You can pass in the user name and password for basic authentication in the + * URLs in these settings. + * + * You can also define an array of host names that can be accessed directly, + * bypassing the proxy, in $settings['http_client_config']['proxy']['no']. + */ +# $settings['http_client_config']['proxy']['http'] = 'http://proxy_user:proxy_pass@example.com:8080'; +# $settings['http_client_config']['proxy']['https'] = 'http://proxy_user:proxy_pass@example.com:8080'; +# $settings['http_client_config']['proxy']['no'] = ['127.0.0.1', 'localhost']; + +/** + * Reverse Proxy Configuration: + * + * Reverse proxy servers are often used to enhance the performance + * of heavily visited sites and may also provide other site caching, + * security, or encryption benefits. In an environment where Drupal + * is behind a reverse proxy, the real IP address of the client should + * be determined such that the correct client IP address is available + * to Drupal's logging and access management systems. In the most simple + * scenario, the proxy server will add an X-Forwarded-For header to the request + * that contains the client IP address. However, HTTP headers are vulnerable to + * spoofing, where a malicious client could bypass restrictions by setting the + * X-Forwarded-For header directly. Therefore, Drupal's proxy configuration + * requires the IP addresses of all remote proxies to be specified in + * $settings['reverse_proxy_addresses'] to work correctly. + * + * Enable this setting to get Drupal to determine the client IP from the + * X-Forwarded-For header. If you are unsure about this setting, do not have a + * reverse proxy, or Drupal operates in a shared hosting environment, this + * setting should remain commented out. + * + * In order for this setting to be used you must specify every possible + * reverse proxy IP address in $settings['reverse_proxy_addresses']. + * If a complete list of reverse proxies is not available in your + * environment (for example, if you use a CDN) you may set the + * $_SERVER['REMOTE_ADDR'] variable directly in settings.php. + * Be aware, however, that it is likely that this would allow IP + * address spoofing unless more advanced precautions are taken. + */ +# $settings['reverse_proxy'] = TRUE; + +/** + * Reverse proxy addresses. + * + * Specify every reverse proxy IP address in your environment, as an array of + * IPv4/IPv6 addresses or subnets in CIDR notation. This setting is required if + * $settings['reverse_proxy'] is TRUE. + */ +# $settings['reverse_proxy_addresses'] = ['a.b.c.d', 'e.f.g.h/24', ...]; + +/** + * Reverse proxy trusted headers. + * + * Sets which headers to trust from your reverse proxy. + * + * Common values are: + * - \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_FOR + * - \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_HOST + * - \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PORT + * - \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PROTO + * - \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED + * + * Note the default value of + * @code + * \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_FOR | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_HOST | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PORT | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PROTO | \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED + * @endcode + * is not secure by default. The value should be set to only the specific + * headers the reverse proxy uses. For example: + * @code + * \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_FOR | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_HOST | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PORT | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PROTO + * @endcode + * This would trust the following headers: + * - X_FORWARDED_FOR + * - X_FORWARDED_HOST + * - X_FORWARDED_PROTO + * - X_FORWARDED_PORT + * + * @see \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_FOR + * @see \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_HOST + * @see \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PORT + * @see \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PROTO + * @see \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED + * @see \Symfony\Component\HttpFoundation\Request::setTrustedProxies + */ +# $settings['reverse_proxy_trusted_headers'] = \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_FOR | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_HOST | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PORT | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PROTO | \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED; + + +/** + * Page caching: + * + * By default, Drupal sends a "Vary: Cookie" HTTP header for anonymous page + * views. This tells a HTTP proxy that it may return a page from its local + * cache without contacting the web server, if the user sends the same Cookie + * header as the user who originally requested the cached page. Without "Vary: + * Cookie", authenticated users would also be served the anonymous page from + * the cache. If the site has mostly anonymous users except a few known + * editors/administrators, the Vary header can be omitted. This allows for + * better caching in HTTP proxies (including reverse proxies), i.e. even if + * clients send different cookies, they still get content served from the cache. + * However, authenticated users should access the site directly (i.e. not use an + * HTTP proxy, and bypass the reverse proxy if one is used) in order to avoid + * getting cached pages from the proxy. + */ +# $settings['omit_vary_cookie'] = TRUE; + + +/** + * Cache TTL for client error (4xx) responses. + * + * Items cached per-URL tend to result in a large number of cache items, and + * this can be problematic on 404 pages which by their nature are unbounded. A + * fixed TTL can be set for these items, defaulting to one hour, so that cache + * backends which do not support LRU can purge older entries. To disable caching + * of client error responses set the value to 0. Currently applies only to + * page_cache module. + */ +# $settings['cache_ttl_4xx'] = 3600; + +/** + * Expiration of cached forms. + * + * Drupal's Form API stores details of forms in a cache and these entries are + * kept for at least 6 hours by default. Expired entries are cleared by cron. + * + * @see \Drupal\Core\Form\FormCache::setCache() + */ +# $settings['form_cache_expiration'] = 21600; + +/** + * Class Loader. + * + * If the APCu extension is detected, the classloader will be optimized to use + * it. Set to FALSE to disable this. + * + * @see https://getcomposer.org/doc/articles/autoloader-optimization.md + */ +# $settings['class_loader_auto_detect'] = FALSE; + +/** + * Authorized file system operations: + * + * The Update Manager module included with Drupal provides a mechanism for + * site administrators to securely install missing updates for the site + * directly through the web user interface. On securely-configured servers, + * the Update manager will require the administrator to provide SSH or FTP + * credentials before allowing the installation to proceed; this allows the + * site to update the new files as the user who owns all the Drupal files, + * instead of as the user the webserver is running as. On servers where the + * webserver user is itself the owner of the Drupal files, the administrator + * will not be prompted for SSH or FTP credentials (note that these server + * setups are common on shared hosting, but are inherently insecure). + * + * Some sites might wish to disable the above functionality, and only update + * the code directly via SSH or FTP themselves. This setting completely + * disables all functionality related to these authorized file operations. + * + * @see https://www.drupal.org/node/244924 + * + * Remove the leading hash signs to disable. + */ +# $settings['allow_authorize_operations'] = FALSE; + +/** + * Default mode for directories and files written by Drupal. + * + * Value should be in PHP Octal Notation, with leading zero. + */ +# $settings['file_chmod_directory'] = 0775; +# $settings['file_chmod_file'] = 0664; + +/** + * Optimized assets path: + * + * A local file system path where optimized assets will be stored. This directory + * must exist and be writable by Drupal. This directory must be relative to + * the Drupal installation directory and be accessible over the web. + */ +# $settings['file_assets_path'] = 'sites/default/files'; + +/** + * Public file base URL: + * + * An alternative base URL to be used for serving public files. This must + * include any leading directory path. + * + * A different value from the domain used by Drupal to be used for accessing + * public files. This can be used for a simple CDN integration, or to improve + * security by serving user-uploaded files from a different domain or subdomain + * pointing to the same server. Do not include a trailing slash. + */ +# $settings['file_public_base_url'] = 'http://downloads.example.com/files'; + +/** + * Public file path: + * + * A local file system path where public files will be stored. This directory + * must exist and be writable by Drupal. This directory must be relative to + * the Drupal installation directory and be accessible over the web. + */ +# $settings['file_public_path'] = 'sites/default/files'; + +/** + * Additional public file schemes: + * + * Public schemes are URI schemes that allow download access to all users for + * all files within that scheme. + * + * The "public" scheme is always public, and the "private" scheme is always + * private, but other schemes, such as "https", "s3", "example", or others, + * can be either public or private depending on the site. By default, they're + * private, and access to individual files is controlled via + * hook_file_download(). + * + * Typically, if a scheme should be public, a module makes it public by + * implementing hook_file_download(), and granting access to all users for all + * files. This could be either the same module that provides the stream wrapper + * for the scheme, or a different module that decides to make the scheme + * public. However, in cases where a site needs to make a scheme public, but + * is unable to add code in a module to do so, the scheme may be added to this + * variable, the result of which is that system_file_download() grants public + * access to all files within that scheme. + */ +# $settings['file_additional_public_schemes'] = ['example']; + +/** + * File schemes whose paths should not be normalized: + * + * Normally, Drupal normalizes '/./' and '/../' segments in file URIs in order + * to prevent unintended file access. For example, 'private://css/../image.png' + * is normalized to 'private://image.png' before checking access to the file. + * + * On Windows, Drupal also replaces '\' with '/' in URIs for the local + * filesystem. + * + * If file URIs with one or more scheme should not be normalized like this, then + * list the schemes here. For example, if 'porcelain://china/./plate.png' should + * not be normalized to 'porcelain://china/plate.png', then add 'porcelain' to + * this array. In this case, make sure that the module providing the 'porcelain' + * scheme does not allow unintended file access when using '/../' to move up the + * directory tree. + */ +# $settings['file_sa_core_2023_005_schemes'] = ['porcelain']; + +/** + * Configuration for phpinfo() admin status report. + * + * Drupal's admin UI includes a report at admin/reports/status/php which shows + * the output of phpinfo(). The full output can contain sensitive information + * so by default Drupal removes some sections. + * + * This behavior can be configured by setting this variable to a different + * value corresponding to the flags parameter of phpinfo(). + * + * If you need to expose more information in the report - for example to debug a + * problem - consider doing so temporarily. + * + * @see https://www.php.net/manual/function.phpinfo.php + */ +# $settings['sa_core_2023_004_phpinfo_flags'] = ~ (INFO_VARIABLES | INFO_ENVIRONMENT); + +/** + * Private file path: + * + * A local file system path where private files will be stored. This directory + * must be absolute, outside of the Drupal installation directory and not + * accessible over the web. + * + * Note: Caches need to be cleared when this value is changed to make the + * private:// stream wrapper available to the system. + * + * See https://www.drupal.org/documentation/modules/file for more information + * about securing private files. + */ +# $settings['file_private_path'] = ''; + +/** + * Temporary file path: + * + * A local file system path where temporary files will be stored. This directory + * must be absolute, outside of the Drupal installation directory and not + * accessible over the web. + * + * If this is not set, the default for the operating system will be used. + * + * @see \Drupal\Component\FileSystem\FileSystem::getOsTemporaryDirectory() + */ +# $settings['file_temp_path'] = '/tmp'; + +/** + * Session write interval: + * + * Set the minimum interval between each session write to database. + * For performance reasons it defaults to 180. + */ +# $settings['session_write_interval'] = 180; + +/** + * String overrides: + * + * To override specific strings on your site with or without enabling the Locale + * module, add an entry to this list. This functionality allows you to change + * a small number of your site's default English language interface strings. + * + * Remove the leading hash signs to enable. + * + * The "en" part of the variable name, is dynamic and can be any langcode of + * any added language. (eg locale_custom_strings_de for german). + */ +# $settings['locale_custom_strings_en'][''] = [ +# 'Home' => 'Front page', +# '@count min' => '@count minutes', +# ]; + +/** + * A custom theme for the offline page: + * + * This applies when the site is explicitly set to maintenance mode through the + * administration page or when the database is inactive due to an error. + * The template file should also be copied into the theme. It is located inside + * 'core/modules/system/templates/maintenance-page.html.twig'. + * + * Note: This setting does not apply to installation and update pages. + */ +# $settings['maintenance_theme'] = 'claro'; + +/** + * PHP settings: + * + * To see what PHP settings are possible, including whether they can be set at + * runtime (by using ini_set()), read the PHP documentation: + * http://php.net/manual/ini.list.php + * See \Drupal\Core\DrupalKernel::bootEnvironment() for required runtime + * settings and the .htaccess file for non-runtime settings. + * Settings defined there should not be duplicated here so as to avoid conflict + * issues. + */ + +/** + * If you encounter a situation where users post a large amount of text, and + * the result is stripped out upon viewing but can still be edited, Drupal's + * output filter may not have sufficient memory to process it. If you + * experience this issue, you may wish to uncomment the following two lines + * and increase the limits of these variables. For more information, see + * http://php.net/manual/pcre.configuration.php. + */ +# ini_set('pcre.backtrack_limit', 200000); +# ini_set('pcre.recursion_limit', 200000); + +/** + * Configuration overrides. + * + * To globally override specific configuration values for this site, + * set them here. You usually don't need to use this feature. This is + * useful in a configuration file for a vhost or directory, rather than + * the default settings.php. + * + * Note that any values you provide in these variable overrides will not be + * viewable from the Drupal administration interface. The administration + * interface displays the values stored in configuration so that you can stage + * changes to other environments that don't have the overrides. + * + * There are particular configuration values that are risky to override. For + * example, overriding the list of installed modules in 'core.extension' is not + * supported as module install or uninstall has not occurred. Other examples + * include field storage configuration, because it has effects on database + * structure, and 'core.menu.static_menu_link_overrides' since this is cached in + * a way that is not config override aware. Also, note that changing + * configuration values in settings.php will not fire any of the configuration + * change events. + */ +# $config['system.site']['name'] = 'My Drupal site'; +# $config['user.settings']['anonymous'] = 'Visitor'; + +/** + * Load services definition file. + */ +$settings['container_yamls'][] = $app_root . '/' . $site_path . '/services.yml'; + +/** + * Override the default service container class. + * + * This is useful for example to trace the service container for performance + * tracking purposes, for testing a service container with an error condition or + * to test a service container that throws an exception. + */ +# $settings['container_base_class'] = '\Drupal\Core\DependencyInjection\Container'; + +/** + * Override the default yaml parser class. + * + * Provide a fully qualified class name here if you would like to provide an + * alternate implementation YAML parser. The class must implement the + * \Drupal\Component\Serialization\SerializationInterface interface. + */ +# $settings['yaml_parser_class'] = NULL; + +/** + * Trusted host configuration. + * + * Drupal core can use the Symfony trusted host mechanism to prevent HTTP Host + * header spoofing. + * + * To enable the trusted host mechanism, you enable your allowable hosts + * in $settings['trusted_host_patterns']. This should be an array of regular + * expression patterns, without delimiters, representing the hosts you would + * like to allow. + * + * For example: + * @code + * $settings['trusted_host_patterns'] = [ + * '^www\.example\.com$', + * ]; + * @endcode + * will allow the site to only run from www.example.com. + * + * If you are running multisite, or if you are running your site from + * different domain names (eg, you don't redirect http://www.example.com to + * http://example.com), you should specify all of the host patterns that are + * allowed by your site. + * + * For example: + * @code + * $settings['trusted_host_patterns'] = [ + * '^example\.com$', + * '^.+\.example\.com$', + * '^example\.org$', + * '^.+\.example\.org$', + * ]; + * @endcode + * will allow the site to run off of all variants of example.com and + * example.org, with all subdomains included. + * + * @see https://www.drupal.org/docs/installing-drupal/trusted-host-settings + */ +# $settings['trusted_host_patterns'] = []; + +/** + * The default list of directories that will be ignored by Drupal's file API. + * + * By default ignore node_modules and bower_components folders to avoid issues + * with common frontend tools and recursive scanning of directories looking for + * extensions. + * + * @see \Drupal\Core\File\FileSystemInterface::scanDirectory() + * @see \Drupal\Core\Extension\ExtensionDiscovery::scanDirectory() + */ +$settings['file_scan_ignore_directories'] = [ + 'node_modules', + 'bower_components', +]; + +/** + * The default number of entities to update in a batch process. + * + * This is used by update and post-update functions that need to go through and + * change all the entities on a site, so it is useful to increase this number + * if your hosting configuration (i.e. RAM allocation, CPU speed) allows for a + * larger number of entities to be processed in a single batch run. + */ +$settings['entity_update_batch_size'] = 50; + +/** + * Entity update backup. + * + * This is used to inform the entity storage handler that the backup tables as + * well as the original entity type and field storage definitions should be + * retained after a successful entity update process. + */ +$settings['entity_update_backup'] = TRUE; + +/** + * State caching. + * + * State caching uses the cache collector pattern to cache all requested keys + * from the state API in a single cache entry, which can greatly reduce the + * amount of database queries. However, some sites may use state with a + * lot of dynamic keys which could result in a very large cache. + */ +$settings['state_cache'] = TRUE; + +/** + * Node migration type. + * + * This is used to force the migration system to use the classic node migrations + * instead of the default complete node migrations. The migration system will + * use the classic node migration only if there are existing migrate_map tables + * for the classic node migrations and they contain data. These tables may not + * exist if you are developing custom migrations and do not want to use the + * complete node migrations. Set this to TRUE to force the use of the classic + * node migrations. + */ +$settings['migrate_node_migrate_type_classic'] = FALSE; + +/** + * The default settings for migration sources. + * + * These settings are used as the default settings on the Credential form at + * /upgrade/credentials. + * + * - migrate_source_version - The version of the source database. This can be + * '6' or '7'. Defaults to '7'. + * - migrate_source_connection - The key in the $databases array for the source + * site. + * - migrate_file_public_path - The location of the source Drupal 6 or Drupal 7 + * public files. This can be a local file directory containing the source + * Drupal 6 or Drupal 7 site (e.g /var/www/docroot), or the site address + * (e.g http://example.com). + * - migrate_file_private_path - The location of the source Drupal 7 private + * files. This can be a local file directory containing the source Drupal 7 + * site (e.g /var/www/docroot), or empty to use the same value as Public + * files directory. + * + * Sample configuration for a drupal 6 source site with the source files in a + * local directory. + * + * @code + * $settings['migrate_source_version'] = '6'; + * $settings['migrate_source_connection'] = 'migrate'; + * $settings['migrate_file_public_path'] = '/var/www/drupal6'; + * @endcode + * + * Sample configuration for a drupal 7 source site with public source files on + * the source site and the private files in a local directory. + * + * @code + * $settings['migrate_source_version'] = '7'; + * $settings['migrate_source_connection'] = 'migrate'; + * $settings['migrate_file_public_path'] = 'https://drupal7.com'; + * $settings['migrate_file_private_path'] = '/var/www/drupal7'; + * @endcode + */ +# $settings['migrate_source_connection'] = ''; +# $settings['migrate_source_version'] = ''; +# $settings['migrate_file_public_path'] = ''; +# $settings['migrate_file_private_path'] = ''; + +/** + * Load local development override configuration, if available. + * + * Create a settings.local.php file to override variables on secondary (staging, + * development, etc.) installations of this site. + * + * Typical uses of settings.local.php include: + * - Disabling caching. + * - Disabling JavaScript/CSS compression. + * - Rerouting outgoing emails. + * + * Keep this code block at the end of this file to take full effect. + */ +# +# if (file_exists($app_root . '/' . $site_path . '/settings.local.php')) { +# include $app_root . '/' . $site_path . '/settings.local.php'; +# } diff --git a/drupal10/web/sites/default/settings.ddev.php b/drupal10/web/sites/default/settings.ddev.php new file mode 100644 index 0000000..1db8668 --- /dev/null +++ b/drupal10/web/sites/default/settings.ddev.php @@ -0,0 +1,53 @@ + 'databasename', + * 'username' => 'sqlusername', + * 'password' => 'sqlpassword', + * 'host' => 'localhost', + * 'port' => '3306', + * 'driver' => 'mysql', + * 'prefix' => '', + * 'collation' => 'utf8mb4_general_ci', + * ]; + * @endcode + */ +$databases = []; + +/** + * Customizing database settings. + * + * Many of the values of the $databases array can be customized for your + * particular database system. Refer to the sample in the section above as a + * starting point. + * + * The "driver" property indicates what Drupal database driver the + * connection should use. This is usually the same as the name of the + * database type, such as mysql or sqlite, but not always. The other + * properties will vary depending on the driver. For SQLite, you must + * specify a database file name in a directory that is writable by the + * webserver. For most other drivers, you must specify a + * username, password, host, and database name. + * + * Drupal core implements drivers for mysql, pgsql, and sqlite. Other drivers + * can be provided by contributed or custom modules. To use a contributed or + * custom driver, the "namespace" property must be set to the namespace of the + * driver. The code in this namespace must be autoloadable prior to connecting + * to the database, and therefore, prior to when module root namespaces are + * added to the autoloader. To add the driver's namespace to the autoloader, + * set the "autoload" property to the PSR-4 base directory of the driver's + * namespace. This is optional for projects managed with Composer if the + * driver's namespace is in Composer's autoloader. + * + * For each database, you may optionally specify multiple "target" databases. + * A target database allows Drupal to try to send certain queries to a + * different database if it can but fall back to the default connection if not. + * That is useful for primary/replica replication, as Drupal may try to connect + * to a replica server when appropriate and if one is not available will simply + * fall back to the single primary server (The terms primary/replica are + * traditionally referred to as master/slave in database server documentation). + * + * The general format for the $databases array is as follows: + * @code + * $databases['default']['default'] = $info_array; + * $databases['default']['replica'][] = $info_array; + * $databases['default']['replica'][] = $info_array; + * $databases['extra']['default'] = $info_array; + * @endcode + * + * In the above example, $info_array is an array of settings described above. + * The first line sets a "default" database that has one primary database + * (the second level default). The second and third lines create an array + * of potential replica databases. Drupal will select one at random for a given + * request as needed. The fourth line creates a new database with a name of + * "extra". + * + * For MySQL, MariaDB or equivalent databases the 'isolation_level' option can + * be set. The recommended transaction isolation level for Drupal sites is + * 'READ COMMITTED'. The 'REPEATABLE READ' option is supported but can result + * in deadlocks, the other two options are 'READ UNCOMMITTED' and 'SERIALIZABLE'. + * They are available but not supported; use them at your own risk. For more + * info: + * https://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-isolation-levels.html + * + * On your settings.php, change the isolation level: + * @code + * $databases['default']['default']['init_commands'] = [ + * 'isolation_level' => 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', + * ]; + * @endcode + * + * You can optionally set a prefix for all database table names by using the + * 'prefix' setting. If a prefix is specified, the table name will be prepended + * with its value. Be sure to use valid database characters only, usually + * alphanumeric and underscore. If no prefix is desired, do not set the 'prefix' + * key or set its value to an empty string ''. + * + * For example, to have all database table prefixed with 'main_', set: + * @code + * 'prefix' => 'main_', + * @endcode + * + * Advanced users can add or override initial commands to execute when + * connecting to the database server, as well as PDO connection settings. For + * example, to enable MySQL SELECT queries to exceed the max_join_size system + * variable, and to reduce the database connection timeout to 5 seconds: + * @code + * $databases['default']['default'] = [ + * 'init_commands' => [ + * 'big_selects' => 'SET SQL_BIG_SELECTS=1', + * ], + * 'pdo' => [ + * PDO::ATTR_TIMEOUT => 5, + * ], + * ]; + * @endcode + * + * WARNING: The above defaults are designed for database portability. Changing + * them may cause unexpected behavior, including potential data loss. See + * https://www.drupal.org/developing/api/database/configuration for more + * information on these defaults and the potential issues. + * + * More details can be found in the constructor methods for each driver: + * - \Drupal\mysql\Driver\Database\mysql\Connection::__construct() + * - \Drupal\pgsql\Driver\Database\pgsql\Connection::__construct() + * - \Drupal\sqlite\Driver\Database\sqlite\Connection::__construct() + * + * Sample Database configuration format for PostgreSQL (pgsql): + * @code + * $databases['default']['default'] = [ + * 'driver' => 'pgsql', + * 'database' => 'databasename', + * 'username' => 'sqlusername', + * 'password' => 'sqlpassword', + * 'host' => 'localhost', + * 'prefix' => '', + * ]; + * @endcode + * + * Sample Database configuration format for SQLite (sqlite): + * @code + * $databases['default']['default'] = [ + * 'driver' => 'sqlite', + * 'database' => '/path/to/databasefilename', + * ]; + * @endcode + * + * Sample Database configuration format for a driver in a contributed module: + * @code + * $databases['default']['default'] = [ + * 'driver' => 'my_driver', + * 'namespace' => 'Drupal\my_module\Driver\Database\my_driver', + * 'autoload' => 'modules/my_module/src/Driver/Database/my_driver/', + * 'database' => 'databasename', + * 'username' => 'sqlusername', + * 'password' => 'sqlpassword', + * 'host' => 'localhost', + * 'prefix' => '', + * ]; + * @endcode + */ + +/** + * Location of the site configuration files. + * + * The $settings['config_sync_directory'] specifies the location of file system + * directory used for syncing configuration data. On install, the directory is + * created. This is used for configuration imports. + * + * The default location for this directory is inside a randomly-named + * directory in the public files path. The setting below allows you to set + * its location. + */ +$settings['config_sync_directory'] = '../config'; + +/** + * Settings: + * + * $settings contains environment-specific configuration, such as the files + * directory and reverse proxy address, and temporary configuration, such as + * security overrides. + * + * @see \Drupal\Core\Site\Settings::get() + */ + +/** + * Salt for one-time login links, cancel links, form tokens, etc. + * + * This variable will be set to a random value by the installer. All one-time + * login links will be invalidated if the value is changed. Note that if your + * site is deployed on a cluster of web servers, you must ensure that this + * variable has the same value on each server. + * + * For enhanced security, you may set this variable to the contents of a file + * outside your document root, and vary the value across environments (like + * production and development); you should also ensure that this file is not + * stored with backups of your database. + * + * Example: + * @code + * $settings['hash_salt'] = file_get_contents('/home/example/salt.txt'); + * @endcode + */ +$settings['hash_salt'] = ''; + +/** + * Deployment identifier. + * + * Drupal's dependency injection container will be automatically invalidated and + * rebuilt when the Drupal core version changes. When updating contributed or + * custom code that changes the container, changing this identifier will also + * allow the container to be invalidated as soon as code is deployed. + */ +# $settings['deployment_identifier'] = \Drupal::VERSION; + +/** + * Access control for update.php script. + * + * If you are updating your Drupal installation using the update.php script but + * are not logged in using either an account with the "Administer software + * updates" permission or the site maintenance account (the account that was + * created during installation), you will need to modify the access check + * statement below. Change the FALSE to a TRUE to disable the access check. + * After finishing the upgrade, be sure to open this file again and change the + * TRUE back to a FALSE! + */ +$settings['update_free_access'] = FALSE; + +/** + * Fallback to HTTP for Update Manager and for fetching security advisories. + * + * If your site fails to connect to updates.drupal.org over HTTPS (either when + * fetching data on available updates, or when fetching the feed of critical + * security announcements), you may uncomment this setting and set it to TRUE to + * allow an insecure fallback to HTTP. Note that doing so will open your site up + * to a potential man-in-the-middle attack. You should instead attempt to + * resolve the issues before enabling this option. + * @see https://www.drupal.org/docs/system-requirements/php-requirements#openssl + * @see https://en.wikipedia.org/wiki/Man-in-the-middle_attack + * @see \Drupal\update\UpdateFetcher + * @see \Drupal\system\SecurityAdvisories\SecurityAdvisoriesFetcher + */ +# $settings['update_fetch_with_http_fallback'] = TRUE; + +/** + * External access proxy settings: + * + * If your site must access the Internet via a web proxy then you can enter the + * proxy settings here. Set the full URL of the proxy, including the port, in + * variables: + * - $settings['http_client_config']['proxy']['http']: The proxy URL for HTTP + * requests. + * - $settings['http_client_config']['proxy']['https']: The proxy URL for HTTPS + * requests. + * You can pass in the user name and password for basic authentication in the + * URLs in these settings. + * + * You can also define an array of host names that can be accessed directly, + * bypassing the proxy, in $settings['http_client_config']['proxy']['no']. + */ +# $settings['http_client_config']['proxy']['http'] = 'http://proxy_user:proxy_pass@example.com:8080'; +# $settings['http_client_config']['proxy']['https'] = 'http://proxy_user:proxy_pass@example.com:8080'; +# $settings['http_client_config']['proxy']['no'] = ['127.0.0.1', 'localhost']; + +/** + * Reverse Proxy Configuration: + * + * Reverse proxy servers are often used to enhance the performance + * of heavily visited sites and may also provide other site caching, + * security, or encryption benefits. In an environment where Drupal + * is behind a reverse proxy, the real IP address of the client should + * be determined such that the correct client IP address is available + * to Drupal's logging, statistics, and access management systems. In + * the most simple scenario, the proxy server will add an + * X-Forwarded-For header to the request that contains the client IP + * address. However, HTTP headers are vulnerable to spoofing, where a + * malicious client could bypass restrictions by setting the + * X-Forwarded-For header directly. Therefore, Drupal's proxy + * configuration requires the IP addresses of all remote proxies to be + * specified in $settings['reverse_proxy_addresses'] to work correctly. + * + * Enable this setting to get Drupal to determine the client IP from the + * X-Forwarded-For header. If you are unsure about this setting, do not have a + * reverse proxy, or Drupal operates in a shared hosting environment, this + * setting should remain commented out. + * + * In order for this setting to be used you must specify every possible + * reverse proxy IP address in $settings['reverse_proxy_addresses']. + * If a complete list of reverse proxies is not available in your + * environment (for example, if you use a CDN) you may set the + * $_SERVER['REMOTE_ADDR'] variable directly in settings.php. + * Be aware, however, that it is likely that this would allow IP + * address spoofing unless more advanced precautions are taken. + */ +# $settings['reverse_proxy'] = TRUE; + +/** + * Reverse proxy addresses. + * + * Specify every reverse proxy IP address in your environment, as an array of + * IPv4/IPv6 addresses or subnets in CIDR notation. This setting is required if + * $settings['reverse_proxy'] is TRUE. + */ +# $settings['reverse_proxy_addresses'] = ['a.b.c.d', 'e.f.g.h/24', ...]; + +/** + * Reverse proxy trusted headers. + * + * Sets which headers to trust from your reverse proxy. + * + * Common values are: + * - \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_FOR + * - \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_HOST + * - \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PORT + * - \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PROTO + * - \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED + * + * Note the default value of + * @code + * \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_FOR | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_HOST | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PORT | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PROTO | \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED + * @endcode + * is not secure by default. The value should be set to only the specific + * headers the reverse proxy uses. For example: + * @code + * \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_FOR | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_HOST | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PORT | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PROTO + * @endcode + * This would trust the following headers: + * - X_FORWARDED_FOR + * - X_FORWARDED_HOST + * - X_FORWARDED_PROTO + * - X_FORWARDED_PORT + * + * @see \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_FOR + * @see \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_HOST + * @see \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PORT + * @see \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PROTO + * @see \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED + * @see \Symfony\Component\HttpFoundation\Request::setTrustedProxies + */ +# $settings['reverse_proxy_trusted_headers'] = \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_FOR | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_HOST | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PORT | \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_PROTO | \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED; + + +/** + * Page caching: + * + * By default, Drupal sends a "Vary: Cookie" HTTP header for anonymous page + * views. This tells a HTTP proxy that it may return a page from its local + * cache without contacting the web server, if the user sends the same Cookie + * header as the user who originally requested the cached page. Without "Vary: + * Cookie", authenticated users would also be served the anonymous page from + * the cache. If the site has mostly anonymous users except a few known + * editors/administrators, the Vary header can be omitted. This allows for + * better caching in HTTP proxies (including reverse proxies), i.e. even if + * clients send different cookies, they still get content served from the cache. + * However, authenticated users should access the site directly (i.e. not use an + * HTTP proxy, and bypass the reverse proxy if one is used) in order to avoid + * getting cached pages from the proxy. + */ +# $settings['omit_vary_cookie'] = TRUE; + + +/** + * Cache TTL for client error (4xx) responses. + * + * Items cached per-URL tend to result in a large number of cache items, and + * this can be problematic on 404 pages which by their nature are unbounded. A + * fixed TTL can be set for these items, defaulting to one hour, so that cache + * backends which do not support LRU can purge older entries. To disable caching + * of client error responses set the value to 0. Currently applies only to + * page_cache module. + */ +# $settings['cache_ttl_4xx'] = 3600; + +/** + * Expiration of cached forms. + * + * Drupal's Form API stores details of forms in a cache and these entries are + * kept for at least 6 hours by default. Expired entries are cleared by cron. + * + * @see \Drupal\Core\Form\FormCache::setCache() + */ +# $settings['form_cache_expiration'] = 21600; + +/** + * Class Loader. + * + * If the APCu extension is detected, the classloader will be optimized to use + * it. Set to FALSE to disable this. + * + * @see https://getcomposer.org/doc/articles/autoloader-optimization.md + */ +# $settings['class_loader_auto_detect'] = FALSE; + +/** + * Authorized file system operations: + * + * The Update Manager module included with Drupal provides a mechanism for + * site administrators to securely install missing updates for the site + * directly through the web user interface. On securely-configured servers, + * the Update manager will require the administrator to provide SSH or FTP + * credentials before allowing the installation to proceed; this allows the + * site to update the new files as the user who owns all the Drupal files, + * instead of as the user the webserver is running as. On servers where the + * webserver user is itself the owner of the Drupal files, the administrator + * will not be prompted for SSH or FTP credentials (note that these server + * setups are common on shared hosting, but are inherently insecure). + * + * Some sites might wish to disable the above functionality, and only update + * the code directly via SSH or FTP themselves. This setting completely + * disables all functionality related to these authorized file operations. + * + * @see https://www.drupal.org/node/244924 + * + * Remove the leading hash signs to disable. + */ +# $settings['allow_authorize_operations'] = FALSE; + +/** + * Default mode for directories and files written by Drupal. + * + * Value should be in PHP Octal Notation, with leading zero. + */ +# $settings['file_chmod_directory'] = 0775; +# $settings['file_chmod_file'] = 0664; + +/** + * Optimized assets path: + * + * A local file system path where optimized assets will be stored. This directory + * must exist and be writable by Drupal. This directory must be relative to + * the Drupal installation directory and be accessible over the web. + */ +# $settings['file_assets_path'] = 'sites/default/files'; + +/** + * Public file base URL: + * + * An alternative base URL to be used for serving public files. This must + * include any leading directory path. + * + * A different value from the domain used by Drupal to be used for accessing + * public files. This can be used for a simple CDN integration, or to improve + * security by serving user-uploaded files from a different domain or subdomain + * pointing to the same server. Do not include a trailing slash. + */ +# $settings['file_public_base_url'] = 'http://downloads.example.com/files'; + +/** + * Public file path: + * + * A local file system path where public files will be stored. This directory + * must exist and be writable by Drupal. This directory must be relative to + * the Drupal installation directory and be accessible over the web. + */ +# $settings['file_public_path'] = 'sites/default/files'; + +/** + * Additional public file schemes: + * + * Public schemes are URI schemes that allow download access to all users for + * all files within that scheme. + * + * The "public" scheme is always public, and the "private" scheme is always + * private, but other schemes, such as "https", "s3", "example", or others, + * can be either public or private depending on the site. By default, they're + * private, and access to individual files is controlled via + * hook_file_download(). + * + * Typically, if a scheme should be public, a module makes it public by + * implementing hook_file_download(), and granting access to all users for all + * files. This could be either the same module that provides the stream wrapper + * for the scheme, or a different module that decides to make the scheme + * public. However, in cases where a site needs to make a scheme public, but + * is unable to add code in a module to do so, the scheme may be added to this + * variable, the result of which is that system_file_download() grants public + * access to all files within that scheme. + */ +# $settings['file_additional_public_schemes'] = ['example']; + +/** + * File schemes whose paths should not be normalized: + * + * Normally, Drupal normalizes '/./' and '/../' segments in file URIs in order + * to prevent unintended file access. For example, 'private://css/../image.png' + * is normalized to 'private://image.png' before checking access to the file. + * + * On Windows, Drupal also replaces '\' with '/' in URIs for the local + * filesystem. + * + * If file URIs with one or more scheme should not be normalized like this, then + * list the schemes here. For example, if 'porcelain://china/./plate.png' should + * not be normalized to 'porcelain://china/plate.png', then add 'porcelain' to + * this array. In this case, make sure that the module providing the 'porcelain' + * scheme does not allow unintended file access when using '/../' to move up the + * directory tree. + */ +# $settings['file_sa_core_2023_005_schemes'] = ['porcelain']; + +/** + * Configuration for phpinfo() admin status report. + * + * Drupal's admin UI includes a report at admin/reports/status/php which shows + * the output of phpinfo(). The full output can contain sensitive information + * so by default Drupal removes some sections. + * + * This behaviour can be configured by setting this variable to a different + * value corresponding to the flags parameter of phpinfo(). + * + * If you need to expose more information in the report - for example to debug a + * problem - consider doing so temporarily. + * + * @see https://www.php.net/manual/function.phpinfo.php + */ +# $settings['sa_core_2023_004_phpinfo_flags'] = ~ (INFO_VARIABLES | INFO_ENVIRONMENT); + +/** + * Private file path: + * + * A local file system path where private files will be stored. This directory + * must be absolute, outside of the Drupal installation directory and not + * accessible over the web. + * + * Note: Caches need to be cleared when this value is changed to make the + * private:// stream wrapper available to the system. + * + * See https://www.drupal.org/documentation/modules/file for more information + * about securing private files. + */ +# $settings['file_private_path'] = ''; + +/** + * Temporary file path: + * + * A local file system path where temporary files will be stored. This directory + * must be absolute, outside of the Drupal installation directory and not + * accessible over the web. + * + * If this is not set, the default for the operating system will be used. + * + * @see \Drupal\Component\FileSystem\FileSystem::getOsTemporaryDirectory() + */ +# $settings['file_temp_path'] = '/tmp'; + +/** + * Session write interval: + * + * Set the minimum interval between each session write to database. + * For performance reasons it defaults to 180. + */ +# $settings['session_write_interval'] = 180; + +/** + * String overrides: + * + * To override specific strings on your site with or without enabling the Locale + * module, add an entry to this list. This functionality allows you to change + * a small number of your site's default English language interface strings. + * + * Remove the leading hash signs to enable. + * + * The "en" part of the variable name, is dynamic and can be any langcode of + * any added language. (eg locale_custom_strings_de for german). + */ +# $settings['locale_custom_strings_en'][''] = [ +# 'Home' => 'Front page', +# '@count min' => '@count minutes', +# ]; + +/** + * A custom theme for the offline page: + * + * This applies when the site is explicitly set to maintenance mode through the + * administration page or when the database is inactive due to an error. + * The template file should also be copied into the theme. It is located inside + * 'core/modules/system/templates/maintenance-page.html.twig'. + * + * Note: This setting does not apply to installation and update pages. + */ +# $settings['maintenance_theme'] = 'claro'; + +/** + * PHP settings: + * + * To see what PHP settings are possible, including whether they can be set at + * runtime (by using ini_set()), read the PHP documentation: + * http://php.net/manual/ini.list.php + * See \Drupal\Core\DrupalKernel::bootEnvironment() for required runtime + * settings and the .htaccess file for non-runtime settings. + * Settings defined there should not be duplicated here so as to avoid conflict + * issues. + */ + +/** + * If you encounter a situation where users post a large amount of text, and + * the result is stripped out upon viewing but can still be edited, Drupal's + * output filter may not have sufficient memory to process it. If you + * experience this issue, you may wish to uncomment the following two lines + * and increase the limits of these variables. For more information, see + * http://php.net/manual/pcre.configuration.php. + */ +# ini_set('pcre.backtrack_limit', 200000); +# ini_set('pcre.recursion_limit', 200000); + +/** + * Configuration overrides. + * + * To globally override specific configuration values for this site, + * set them here. You usually don't need to use this feature. This is + * useful in a configuration file for a vhost or directory, rather than + * the default settings.php. + * + * Note that any values you provide in these variable overrides will not be + * viewable from the Drupal administration interface. The administration + * interface displays the values stored in configuration so that you can stage + * changes to other environments that don't have the overrides. + * + * There are particular configuration values that are risky to override. For + * example, overriding the list of installed modules in 'core.extension' is not + * supported as module install or uninstall has not occurred. Other examples + * include field storage configuration, because it has effects on database + * structure, and 'core.menu.static_menu_link_overrides' since this is cached in + * a way that is not config override aware. Also, note that changing + * configuration values in settings.php will not fire any of the configuration + * change events. + */ +# $config['system.site']['name'] = 'My Drupal site'; +# $config['user.settings']['anonymous'] = 'Visitor'; + +/** + * Load services definition file. + */ +$settings['container_yamls'][] = $app_root . '/' . $site_path . '/services.yml'; + +/** + * Override the default service container class. + * + * This is useful for example to trace the service container for performance + * tracking purposes, for testing a service container with an error condition or + * to test a service container that throws an exception. + */ +# $settings['container_base_class'] = '\Drupal\Core\DependencyInjection\Container'; + +/** + * Override the default yaml parser class. + * + * Provide a fully qualified class name here if you would like to provide an + * alternate implementation YAML parser. The class must implement the + * \Drupal\Component\Serialization\SerializationInterface interface. + */ +# $settings['yaml_parser_class'] = NULL; + +/** + * Trusted host configuration. + * + * Drupal core can use the Symfony trusted host mechanism to prevent HTTP Host + * header spoofing. + * + * To enable the trusted host mechanism, you enable your allowable hosts + * in $settings['trusted_host_patterns']. This should be an array of regular + * expression patterns, without delimiters, representing the hosts you would + * like to allow. + * + * For example: + * @code + * $settings['trusted_host_patterns'] = [ + * '^www\.example\.com$', + * ]; + * @endcode + * will allow the site to only run from www.example.com. + * + * If you are running multisite, or if you are running your site from + * different domain names (eg, you don't redirect http://www.example.com to + * http://example.com), you should specify all of the host patterns that are + * allowed by your site. + * + * For example: + * @code + * $settings['trusted_host_patterns'] = [ + * '^example\.com$', + * '^.+\.example\.com$', + * '^example\.org$', + * '^.+\.example\.org$', + * ]; + * @endcode + * will allow the site to run off of all variants of example.com and + * example.org, with all subdomains included. + * + * @see https://www.drupal.org/docs/installing-drupal/trusted-host-settings + */ +# $settings['trusted_host_patterns'] = []; + +/** + * The default list of directories that will be ignored by Drupal's file API. + * + * By default ignore node_modules and bower_components folders to avoid issues + * with common frontend tools and recursive scanning of directories looking for + * extensions. + * + * @see \Drupal\Core\File\FileSystemInterface::scanDirectory() + * @see \Drupal\Core\Extension\ExtensionDiscovery::scanDirectory() + */ +$settings['file_scan_ignore_directories'] = [ + 'node_modules', + 'bower_components', +]; + +/** + * The default number of entities to update in a batch process. + * + * This is used by update and post-update functions that need to go through and + * change all the entities on a site, so it is useful to increase this number + * if your hosting configuration (i.e. RAM allocation, CPU speed) allows for a + * larger number of entities to be processed in a single batch run. + */ +$settings['entity_update_batch_size'] = 50; + +/** + * Entity update backup. + * + * This is used to inform the entity storage handler that the backup tables as + * well as the original entity type and field storage definitions should be + * retained after a successful entity update process. + */ +$settings['entity_update_backup'] = TRUE; + +/** + * Node migration type. + * + * This is used to force the migration system to use the classic node migrations + * instead of the default complete node migrations. The migration system will + * use the classic node migration only if there are existing migrate_map tables + * for the classic node migrations and they contain data. These tables may not + * exist if you are developing custom migrations and do not want to use the + * complete node migrations. Set this to TRUE to force the use of the classic + * node migrations. + */ +$settings['migrate_node_migrate_type_classic'] = FALSE; + +/** + * The default settings for migration sources. + * + * These settings are used as the default settings on the Credential form at + * /upgrade/credentials. + * + * - migrate_source_version - The version of the source database. This can be + * '6' or '7'. Defaults to '7'. + * - migrate_source_connection - The key in the $databases array for the source + * site. + * - migrate_file_public_path - The location of the source Drupal 6 or Drupal 7 + * public files. This can be a local file directory containing the source + * Drupal 6 or Drupal 7 site (e.g /var/www/docroot), or the site address + * (e.g http://example.com). + * - migrate_file_private_path - The location of the source Drupal 7 private + * files. This can be a local file directory containing the source Drupal 7 + * site (e.g /var/www/docroot), or empty to use the same value as Public + * files directory. + * + * Sample configuration for a drupal 6 source site with the source files in a + * local directory. + * + * @code + * $settings['migrate_source_version'] = '6'; + * $settings['migrate_source_connection'] = 'migrate'; + * $settings['migrate_file_public_path'] = '/var/www/drupal6'; + * @endcode + * + * Sample configuration for a drupal 7 source site with public source files on + * the source site and the private files in a local directory. + * + * @code + * $settings['migrate_source_version'] = '7'; + * $settings['migrate_source_connection'] = 'migrate'; + * $settings['migrate_file_public_path'] = 'https://drupal7.com'; + * $settings['migrate_file_private_path'] = '/var/www/drupal7'; + * @endcode + */ +# $settings['migrate_source_connection'] = ''; +# $settings['migrate_source_version'] = ''; +# $settings['migrate_file_public_path'] = ''; +# $settings['migrate_file_private_path'] = ''; + +if (getenv('IS_DDEV_PROJECT') == 'true' && file_exists(__DIR__ . '/settings.ddev.php')) { + include __DIR__ . '/settings.ddev.php'; +} + +if (file_exists($app_root . '/' . $site_path . '/settings.migrate.php')) { + include $app_root . '/' . $site_path . '/settings.migrate.php'; +} + +/** + * Load local development override configuration, if available. + * + * Create a settings.local.php file to override variables on secondary (staging, + * development, etc.) installations of this site. + * + * Typical uses of settings.local.php include: + * - Disabling caching. + * - Disabling JavaScript/CSS compression. + * - Rerouting outgoing emails. + * + * Keep this code block at the end of this file to take full effect. + */ +if (file_exists($app_root . '/' . $site_path . '/settings.local.php')) { + include $app_root . '/' . $site_path . '/settings.local.php'; +} diff --git a/drupal10/web/sites/development.services.yml b/drupal10/web/sites/development.services.yml new file mode 100644 index 0000000..c02d3ff --- /dev/null +++ b/drupal10/web/sites/development.services.yml @@ -0,0 +1,19 @@ +# Local development services. +# +# The development.services.yml file allows the developer to override +# container parameters for debugging. +# +# To activate this feature, follow the instructions at the top of the +# 'example.settings.local.php' file, which sits next to this file. +# +# Be aware that in Drupal's configuration system, all the files that +# provide container definitions are merged using a shallow merge approach +# within \Drupal\Core\DependencyInjection\YamlFileLoader. +# This means that if you want to override any value of a parameter, the +# whole parameter array needs to be copied from +# sites/default/default.services.yml or from core/core.services.yml file. +parameters: + http.response.debug_cacheability_headers: true +services: + cache.backend.null: + class: Drupal\Core\Cache\NullBackendFactory diff --git a/drupal10/web/sites/example.settings.local.php b/drupal10/web/sites/example.settings.local.php new file mode 100644 index 0000000..bfe061d --- /dev/null +++ b/drupal10/web/sites/example.settings.local.php @@ -0,0 +1,151 @@ +..' => 'directory'. As an + * example, to map https://www.drupal.org:8080/my-site/test to the configuration + * directory sites/example.com, the array should be defined as: + * @code + * $sites = [ + * '8080.www.drupal.org.my-site.test' => 'example.com', + * ]; + * @endcode + * The URL, https://www.drupal.org:8080/my-site/test/, could be a symbolic link + * or an Apache Alias directive that points to the Drupal root containing + * index.php. An alias could also be created for a subdomain. See the + * @link https://www.drupal.org/documentation/install online Drupal installation guide @endlink + * for more information on setting up domains, subdomains, and subdirectories. + * + * The following examples look for a site configuration in sites/example.com: + * @code + * URL: http://dev.drupal.org + * $sites['dev.drupal.org'] = 'example.com'; + * + * URL: http://localhost/example + * $sites['localhost.example'] = 'example.com'; + * + * URL: http://localhost:8080/example + * $sites['8080.localhost.example'] = 'example.com'; + * + * URL: https://www.drupal.org:8080/my-site/test/ + * $sites['8080.www.drupal.org.my-site.test'] = 'example.com'; + * @endcode + * + * @see default.settings.php + * @see \Drupal\Core\DrupalKernel::getSitePath() + * @see https://www.drupal.org/docs/getting-started/multisite-drupal + */ diff --git a/drupal10/web/themes/README.txt b/drupal10/web/themes/README.txt new file mode 100644 index 0000000..1e00ead --- /dev/null +++ b/drupal10/web/themes/README.txt @@ -0,0 +1,31 @@ +Themes allow you to change the look and feel of your Drupal site. You can use +themes contributed by others or create your own. + +WHAT TO PLACE IN THIS DIRECTORY? +-------------------------------- + +Placing downloaded and custom themes in this directory separates downloaded and +custom themes from Drupal core's themes. This allows Drupal core to be updated +without overwriting these files. + +DOWNLOAD ADDITIONAL THEMES +-------------------------- + +Contributed themes from the Drupal community may be downloaded at +https://www.drupal.org/project/project_theme. + +MULTISITE CONFIGURATION +----------------------- + +In multisite configurations, themes found in this directory are available to +all sites. You may also put themes in the sites/all/themes directory, and the +versions in sites/all/themes will take precedence over versions of the same +themes that are here. Alternatively, the sites/your_site_name/themes directory +pattern may be used to restrict themes to a specific site instance. + +MORE INFORMATION +----------------- + +Refer to the "Appearance" section of the README.md in the Drupal root directory +for further information on customizing the appearance of Drupal with custom +themes. diff --git a/drupal10/web/update.php b/drupal10/web/update.php new file mode 100644 index 0000000..b65649c --- /dev/null +++ b/drupal10/web/update.php @@ -0,0 +1,30 @@ +handle($request); +$response->send(); + +$kernel->terminate($request, $response); diff --git a/drupal10/web/web.config b/drupal10/web/web.config new file mode 100644 index 0000000..b769e45 --- /dev/null +++ b/drupal10/web/web.config @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drupal7/.ddev/config.yaml b/drupal7/.ddev/config.yaml new file mode 100644 index 0000000..59025bf --- /dev/null +++ b/drupal7/.ddev/config.yaml @@ -0,0 +1,277 @@ +name: migration-drupal7 +type: drupal7 +docroot: web +php_version: "8.1" +webserver_type: nginx-fpm +xdebug_enabled: false +additional_hostnames: [] +additional_fqdns: [] +database: + type: mariadb + version: "10.4" +use_dns_when_possible: true +composer_version: "2" +web_environment: [] + +# Key features of DDEV's config.yaml: + +# name: # Name of the project, automatically provides +# http://projectname.ddev.site and https://projectname.ddev.site + +# type: # backdrop, craftcms, django4, drupal6/7/8/9/10, laravel, magento, magento2, php, python, shopware6, silverstripe, typo3, wordpress +# See https://ddev.readthedocs.io/en/latest/users/quickstart/ for more +# information on the different project types + +# docroot: # Relative path to the directory containing index.php. + +# php_version: "8.1" # PHP version to use, "5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2", "8.3" + +# You can explicitly specify the webimage but this +# is not recommended, as the images are often closely tied to DDEV's' behavior, +# so this can break upgrades. + +# webimage: # nginx/php docker image. + +# database: +# type: # mysql, mariadb, postgres +# version: # database version, like "10.4" or "8.0" +# MariaDB versions can be 5.5-10.8 and 10.11, MySQL versions can be 5.5-8.0 +# PostgreSQL versions can be 9-16. + +# router_http_port: # Port to be used for http (defaults to global configuration, usually 80) +# router_https_port: # Port for https (defaults to global configuration, usually 443) + +# xdebug_enabled: false # Set to true to enable Xdebug and "ddev start" or "ddev restart" +# Note that for most people the commands +# "ddev xdebug" to enable Xdebug and "ddev xdebug off" to disable it work better, +# as leaving Xdebug enabled all the time is a big performance hit. + +# xhprof_enabled: false # Set to true to enable Xhprof and "ddev start" or "ddev restart" +# Note that for most people the commands +# "ddev xhprof" to enable Xhprof and "ddev xhprof off" to disable it work better, +# as leaving Xhprof enabled all the time is a big performance hit. + +# webserver_type: nginx-fpm, apache-fpm, or nginx-gunicorn + +# timezone: Europe/Berlin +# This is the timezone used in the containers and by PHP; +# it can be set to any valid timezone, +# see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones +# For example Europe/Dublin or MST7MDT + +# composer_root: +# Relative path to the Composer root directory from the project root. This is +# the directory which contains the composer.json and where all Composer related +# commands are executed. + +# composer_version: "2" +# You can set it to "" or "2" (default) for Composer v2 or "1" for Composer v1 +# to use the latest major version available at the time your container is built. +# It is also possible to use each other Composer version channel. This includes: +# - 2.2 (latest Composer LTS version) +# - stable +# - preview +# - snapshot +# Alternatively, an explicit Composer version may be specified, for example "2.2.18". +# To reinstall Composer after the image was built, run "ddev debug refresh". + +# nodejs_version: "18" +# change from the default system Node.js version to any other version. +# Numeric version numbers can be complete (i.e. 18.15.0) or +# incomplete (18, 17.2, 16). 'lts' and 'latest' can be used as well along with +# other named releases. +# see https://www.npmjs.com/package/n#specifying-nodejs-versions +# Note that you can continue using 'ddev nvm' or nvm inside the web container +# to change the project's installed node version if you need to. + +# additional_hostnames: +# - somename +# - someothername +# would provide http and https URLs for "somename.ddev.site" +# and "someothername.ddev.site". + +# additional_fqdns: +# - example.com +# - sub1.example.com +# would provide http and https URLs for "example.com" and "sub1.example.com" +# Please take care with this because it can cause great confusion. + +# upload_dirs: "custom/upload/dir" +# +# upload_dirs: +# - custom/upload/dir +# - ../private +# +# would set the destination paths for ddev import-files to /custom/upload/dir +# When Mutagen is enabled this path is bind-mounted so that all the files +# in the upload_dirs don't have to be synced into Mutagen. + +# disable_upload_dirs_warning: false +# If true, turns off the normal warning that says +# "You have Mutagen enabled and your 'php' project type doesn't have upload_dirs set" + +# ddev_version_constraint: "" +# Example: +# ddev_version_constraint: ">= 1.22.4" +# This will enforce that the running ddev version is within this constraint. +# See https://github.com/Masterminds/semver#checking-version-constraints for +# supported constraint formats + +# working_dir: +# web: /var/www/html +# db: /home +# would set the default working directory for the web and db services. +# These values specify the destination directory for ddev ssh and the +# directory in which commands passed into ddev exec are run. + +# omit_containers: [db, ddev-ssh-agent] +# Currently only these containers are supported. Some containers can also be +# omitted globally in the ~/.ddev/global_config.yaml. Note that if you omit +# the "db" container, several standard features of DDEV that access the +# database container will be unusable. In the global configuration it is also +# possible to omit ddev-router, but not here. + +# performance_mode: "global" +# DDEV offers performance optimization strategies to improve the filesystem +# performance depending on your host system. Should be configured globally. +# +# If set, will override the global config. Possible values are: +# - "global": uses the value from the global config. +# - "none": disables performance optimization for this project. +# - "mutagen": enables Mutagen for this project. +# - "nfs": enables NFS for this project. +# +# See https://ddev.readthedocs.io/en/latest/users/install/performance/#nfs +# See https://ddev.readthedocs.io/en/latest/users/install/performance/#mutagen + +# fail_on_hook_fail: False +# Decide whether 'ddev start' should be interrupted by a failing hook + +# host_https_port: "59002" +# The host port binding for https can be explicitly specified. It is +# dynamic unless otherwise specified. +# This is not used by most people, most people use the *router* instead +# of the localhost port. + +# host_webserver_port: "59001" +# The host port binding for the ddev-webserver can be explicitly specified. It is +# dynamic unless otherwise specified. +# This is not used by most people, most people use the *router* instead +# of the localhost port. + +# host_db_port: "59002" +# The host port binding for the ddev-dbserver can be explicitly specified. It is dynamic +# unless explicitly specified. + +# mailpit_http_port: "8025" +# mailpit_https_port: "8026" +# The Mailpit ports can be changed from the default 8025 and 8026 + +# host_mailpit_port: "8025" +# The mailpit port is not normally bound on the host at all, instead being routed +# through ddev-router, but it can be bound directly to localhost if specified here. + +# webimage_extra_packages: [php7.4-tidy, php-bcmath] +# Extra Debian packages that are needed in the webimage can be added here + +# dbimage_extra_packages: [telnet,netcat] +# Extra Debian packages that are needed in the dbimage can be added here + +# use_dns_when_possible: true +# If the host has internet access and the domain configured can +# successfully be looked up, DNS will be used for hostname resolution +# instead of editing /etc/hosts +# Defaults to true + +# project_tld: ddev.site +# The top-level domain used for project URLs +# The default "ddev.site" allows DNS lookup via a wildcard +# If you prefer you can change this to "ddev.local" to preserve +# pre-v1.9 behavior. + +# ngrok_args: --basic-auth username:pass1234 +# Provide extra flags to the "ngrok http" command, see +# https://ngrok.com/docs/ngrok-agent/config or run "ngrok http -h" + +# disable_settings_management: false +# If true, DDEV will not create CMS-specific settings files like +# Drupal's settings.php/settings.ddev.php or TYPO3's AdditionalConfiguration.php +# In this case the user must provide all such settings. + +# You can inject environment variables into the web container with: +# web_environment: +# - SOMEENV=somevalue +# - SOMEOTHERENV=someothervalue + +# no_project_mount: false +# (Experimental) If true, DDEV will not mount the project into the web container; +# the user is responsible for mounting it manually or via a script. +# This is to enable experimentation with alternate file mounting strategies. +# For advanced users only! + +# bind_all_interfaces: false +# If true, host ports will be bound on all network interfaces, +# not the localhost interface only. This means that ports +# will be available on the local network if the host firewall +# allows it. + +# default_container_timeout: 120 +# The default time that DDEV waits for all containers to become ready can be increased from +# the default 120. This helps in importing huge databases, for example. + +#web_extra_exposed_ports: +#- name: nodejs +# container_port: 3000 +# http_port: 2999 +# https_port: 3000 +#- name: something +# container_port: 4000 +# https_port: 4000 +# http_port: 3999 +# Allows a set of extra ports to be exposed via ddev-router +# Fill in all three fields even if you don’t intend to use the https_port! +# If you don’t add https_port, then it defaults to 0 and ddev-router will fail to start. +# +# The port behavior on the ddev-webserver must be arranged separately, for example +# using web_extra_daemons. +# For example, with a web app on port 3000 inside the container, this config would +# expose that web app on https://.ddev.site:9999 and http://.ddev.site:9998 +# web_extra_exposed_ports: +# - name: myapp +# container_port: 3000 +# http_port: 9998 +# https_port: 9999 + +#web_extra_daemons: +#- name: "http-1" +# command: "/var/www/html/node_modules/.bin/http-server -p 3000" +# directory: /var/www/html +#- name: "http-2" +# command: "/var/www/html/node_modules/.bin/http-server /var/www/html/sub -p 3000" +# directory: /var/www/html + +# override_config: false +# By default, config.*.yaml files are *merged* into the configuration +# But this means that some things can't be overridden +# For example, if you have 'use_dns_when_possible: true'' you can't override it with a merge +# and you can't erase existing hooks or all environment variables. +# However, with "override_config: true" in a particular config.*.yaml file, +# 'use_dns_when_possible: false' can override the existing values, and +# hooks: +# post-start: [] +# or +# web_environment: [] +# or +# additional_hostnames: [] +# can have their intended affect. 'override_config' affects only behavior of the +# config.*.yaml file it exists in. + +# Many DDEV commands can be extended to run tasks before or after the +# DDEV command is executed, for example "post-start", "post-import-db", +# "pre-composer", "post-composer" +# See https://ddev.readthedocs.io/en/stable/users/extend/custom-commands/ for more +# information on the commands that can be extended and the tasks you can define +# for them. Example: +#hooks: +# post-import-db: +# - exec: drush cc all diff --git a/drupal7/web/.editorconfig b/drupal7/web/.editorconfig new file mode 100644 index 0000000..ccc6a28 --- /dev/null +++ b/drupal7/web/.editorconfig @@ -0,0 +1,14 @@ +# Drupal editor configuration normalization +# @see http://editorconfig.org/ + +# This is the top-most .editorconfig file; do not search in parent directories. +root = true + +# All files. +[*] +end_of_line = LF +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/drupal7/web/.gitignore b/drupal7/web/.gitignore new file mode 100644 index 0000000..baff153 --- /dev/null +++ b/drupal7/web/.gitignore @@ -0,0 +1,6 @@ +# Ignore configuration files that may contain sensitive information. +sites/*/settings*.php + +# Ignore paths that contain user-generated content. +sites/*/files +sites/*/private diff --git a/drupal7/web/.gitlab-ci.yml b/drupal7/web/.gitlab-ci.yml new file mode 100644 index 0000000..e1503e4 --- /dev/null +++ b/drupal7/web/.gitlab-ci.yml @@ -0,0 +1,210 @@ +################ +# Drupal GitLabCI template +# +# Based off GitlabCI templates project: https://git.drupalcode.org/project/gitlab_templates +# Guide: https://www.drupal.org/docs/develop/git/using-gitlab-to-contribute-to-drupal/gitlab-ci +# +# With thanks to: +# - The GitLab Acceleration Initiative participants +# - DrupalSpoons +################ + +################ +# Includes +# +# Additional configuration can be provided through includes. +# One advantage of include files is that if they are updated upstream, the +# changes affect all pipelines using that include. +# +# Includes can be overriden by re-declaring anything provided in an include, +# here in gitlab-ci.yml +# https://docs.gitlab.com/ee/ci/yaml/includes.html#override-included-configuration-values +################ + +include: + - project: $_GITLAB_TEMPLATES_REPO + ref: $_GITLAB_TEMPLATES_REF + file: + - '/includes/include.drupalci.variables.yml' + - '/includes/include.drupalci.workflows.yml' + +################ +# Variables +# +# Overriding variables +# - To override one or more of these variables, simply declare your own +# variables keyword. +# - Keywords declared directly in .gitlab-ci.yml take precedence over include +# files. +# - Documentation: https://docs.gitlab.com/ee/ci/variables/ +# - Predefined variables: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html +# +################ + +variables: + _TARGET_PHP: "8.1" + CONCURRENCY: 15 + GIT_DEPTH: "3" + +################ +# Stages +# +# Each job is assigned to a stage, defining the order in which the jobs are executed. +# Jobs in the same stage run in parallel. +# +# If all jobs in a stage succeed, the pipeline will proceed to the next stage. +# If any job in the stage fails, the pipeline will exit early. +################ + +stages: + ################ + # Code quality checks + # + # This stage includes any codebase validation that we want to perform + # before running functional tests. + ################ + - 🪄 Lint + + ################ + # Test + # + # The test phase actually executes the tests, as well as gathering results + # and artifacts. + ################ + - 🗜️ Test + +############# +# Templates # +############# + +.run-on-mr: &run-on-mr + if: $CI_PIPELINE_SOURCE == "merge_request_event" + +.run-on-mr-manual: &run-on-mr-manual + if: $CI_PIPELINE_SOURCE == "merge_request_event" + when: manual + allow_failure: true + +.run-on-commit: &run-on-commit + if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ROOT_NAMESPACE == "project" + +.run-daily: &run-daily + if: $CI_PIPELINE_SOURCE == "schedule" && $CI_PROJECT_ROOT_NAMESPACE == "project" + +.default-stage: &default-stage + stage: 🗜️ Test + trigger: + # Rely on the status of the child pipeline. + strategy: depend + include: + - local: .gitlab-ci/pipeline.yml + rules: + - <<: *run-on-commit + - <<: *run-on-mr-manual + +################ +# Jobs +# +# Jobs define what scripts are actually executed in each stage. +################ + +'🧹 PHP Compatibility checks (PHPCS)': + stage: 🪄 Lint + variables: + PHPCS_PHP_VERSION: "5.6" + KUBERNETES_CPU_REQUEST: "16" + interruptible: true + allow_failure: true + retry: + max: 2 + when: + - unknown_failure + - api_failure + - stuck_or_timeout_failure + - runner_system_failure + - scheduler_failure + image: + name: $_CONFIG_DOCKERHUB_ROOT/php-$_TARGET_PHP-apache:production + artifacts: + expire_in: 6 mos + paths: + - phpcs-quality-report.json + reports: + codequality: phpcs-quality-report.json + rules: + - <<: *run-on-mr + before_script: + - echo "{}" > composer.json + - composer config allow-plugins true -n + - composer require --dev drupal/coder:^8.2@stable micheh/phpcs-gitlab phpcompatibility/php-compatibility dealerdirect/phpcodesniffer-composer-installer + - export TARGET_BRANCH=${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}${CI_COMMIT_BRANCH} + script: + - git fetch -vn --depth=$GIT_DEPTH origin "+refs/heads/$TARGET_BRANCH:refs/heads/$TARGET_BRANCH" + - export MODIFIED=`git diff --name-only refs/heads/$TARGET_BRANCH|while read r;do echo "$CI_PROJECT_DIR/$r";done|tr "\n" " "` + - echo -e "$MODIFIED" | tr " " "\n" + - echo "If this list contains more files than what you changed, then you need to rebase your branch." + - vendor/bin/phpcs --basepath=$CI_PROJECT_DIR --report-\\Micheh\\PhpCodeSniffer\\Report\\Gitlab=phpcs-quality-report.json --report-full --report-summary --standard=PHPCompatibility --runtime-set testVersion $PHPCS_PHP_VERSION --extensions=php,module,inc,install,test,profile,theme $MODIFIED + +# Default job. +'PHP 8.1 MySQL 5.7': + <<: *default-stage + variables: + _TARGET_PHP: "8.1" + _TARGET_DB: "mysql-5.7" + rules: + - <<: *run-on-commit + - <<: *run-on-mr + +'PHP 5.6 MySQL 5.5': + <<: *default-stage + variables: + _TARGET_PHP: "5.6" + _TARGET_DB: "mysql-5.5" + +'PHP 7.2 MySQL 5.7': + <<: *default-stage + variables: + _TARGET_PHP: "7.2" + _TARGET_DB: "mysql-5.7" + +'PHP 7.4 MySQL 5.7': + <<: *default-stage + variables: + _TARGET_PHP: "7.4" + _TARGET_DB: "mysql-5.7" + +'PHP 8.0 MySQL 5.7': + <<: *default-stage + variables: + _TARGET_PHP: "8.0" + _TARGET_DB: "mysql-5.7" + +'PHP 8.2 MySQL 8': + <<: *default-stage + variables: + _TARGET_PHP: "8.2" + _TARGET_DB: "mysql-8" + +'PHP 7.4 PostgreSQL 9.5': + <<: *default-stage + variables: + _TARGET_PHP: "7.4" + _TARGET_DB: "pgsql-9.5" + +'PHP 8.1 PostgreSQL 14.1': + <<: *default-stage + variables: + _TARGET_PHP: "8.1" + _TARGET_DB: "pgsql-14.1" + +'PHP 7.4 SQLite 3.27.0': + <<: *default-stage + variables: + _TARGET_PHP: "7.4" + _TARGET_DB: "sqlite-3" + +'PHP 8.1 MariaDB 10.3.22': + <<: *default-stage + variables: + _TARGET_PHP: "8.1" + _TARGET_DB: "mariadb-10.3.22" diff --git a/drupal7/web/.gitlab-ci/.htaccess-parent b/drupal7/web/.gitlab-ci/.htaccess-parent new file mode 100644 index 0000000..e12b14e --- /dev/null +++ b/drupal7/web/.gitlab-ci/.htaccess-parent @@ -0,0 +1,4 @@ +# Redirect everything via the subdirectory. + +RewriteEngine on +RewriteRule (.*) subdirectory/$1 [L] diff --git a/drupal7/web/.gitlab-ci/pipeline.yml b/drupal7/web/.gitlab-ci/pipeline.yml new file mode 100644 index 0000000..3272b26 --- /dev/null +++ b/drupal7/web/.gitlab-ci/pipeline.yml @@ -0,0 +1,160 @@ +stages: + ################ + # Test + # + # The test phase actually executes the tests, as well as gathering results + # and artifacts. + ################ + - 🗜️ Test + +############# +# Templates # +############# + +.default-job-settings: &default-job-settings + interruptible: true + allow_failure: false + retry: + max: 2 + when: + - unknown_failure + - api_failure + - stuck_or_timeout_failure + - runner_system_failure + - scheduler_failure + image: + name: $_CONFIG_DOCKERHUB_ROOT/php-$_TARGET_PHP-apache:production + rules: + - if: $CI_PIPELINE_SOURCE == "parent_pipeline" + +.test-variables: &test-variables + FF_NETWORK_PER_BUILD: 1 + SIMPLETEST_BASE_URL: http://localhost/subdirectory + DB_DRIVER: mysql + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: drupal + MYSQL_USER: drupaltestbot + MYSQL_PASSWORD: drupaltestbotpw + POSTGRES_DB: drupaltestbot + POSTGRES_USER: drupaltestbot + POSTGRES_PASSWORD: drupaltestbotpw + CI_PARALLEL_NODE_INDEX: $CI_NODE_INDEX + CI_PARALLEL_NODE_TOTAL: $CI_NODE_TOTAL + +.with-database: &with-database + name: $_CONFIG_DOCKERHUB_ROOT/$_TARGET_DB:production + alias: database + +.with-chrome: &with-chrome + name: $_CONFIG_DOCKERHUB_ROOT/chromedriver:production + alias: chrome + entrypoint: + - chromedriver + - "--no-sandbox" + - "--log-path=/tmp/chromedriver.log" + - "--verbose" + - "--whitelisted-ips=" + +.phpunit-artifacts: &phpunit-artifacts + artifacts: + when: always + expire_in: 6 mos + reports: + junit: ./sites/default/files/simpletest/*.xml + paths: + - ./sites/default/files/simpletest + +.setup-webroot: &setup-webserver + before_script: + - ln -s $CI_PROJECT_DIR /var/www/html/subdirectory + - cp $CI_PROJECT_DIR/.gitlab-ci/.htaccess-parent /var/www/html/.htaccess + - sudo service apache2 start + +.get-simpletest-db: &get-simpletest-db + - | + # Assume SQLite unless we have another known target. + export SIMPLETEST_DB=sqlite://localhost/$CI_PROJECT_DIR/sites/default/files/db.sqlite + [[ $_TARGET_DB == mysql* ]] && export SIMPLETEST_DB=mysql://$MYSQL_USER:$MYSQL_PASSWORD@database/$MYSQL_DATABASE + [[ $_TARGET_DB == mariadb* ]] && export SIMPLETEST_DB=mysql://$MYSQL_USER:$MYSQL_PASSWORD@database/$MYSQL_DATABASE + [[ $_TARGET_DB == pgsql* ]] && export SIMPLETEST_DB=pgsql://$POSTGRES_USER:$POSTGRES_PASSWORD@database/$POSTGRES_DB + - echo "SIMPLETEST_DB = $SIMPLETEST_DB" + +.prepare-dirs: &prepare-dirs + - mkdir -p ./sites/default/files ./sites/default/files/simpletest ./build/logs/junit + - chown -R www-data:www-data ./sites ./build/logs/junit /var/www/ + - sudo -u www-data git config --global --add safe.directory $CI_PROJECT_DIR + +.install-drupal: &install-drupal + - sudo -u www-data /usr/local/bin/drush si -y --db-url=$SIMPLETEST_DB --clean-url=0 --account-name=admin --account-pass=drupal --account-mail=admin@example.com + - sudo -u www-data /usr/local/bin/drush vset simpletest_clear_results '0' + - sudo -u www-data /usr/local/bin/drush vset simpletest_verbose '1' + - sudo -u www-data /usr/local/bin/drush en -y simpletest + +.run-tests: &run-tests + script: + - *get-simpletest-db + - *prepare-dirs + - *install-drupal + # We need to pass this along directly even though it's set in the environment parameters. + - sudo -u www-data php ./scripts/run-tests.sh --color --concurrency "$CONCURRENCY" --url "$SIMPLETEST_BASE_URL" --verbose --fail-only --all --xml "$CI_PROJECT_DIR/sites/default/files/simpletest" --ci-parallel-node-index $CI_PARALLEL_NODE_INDEX --ci-parallel-node-total $CI_PARALLEL_NODE_TOTAL + +.run-test-only-tests: &run-test-only-tests + script: + - *get-simpletest-db + - *prepare-dirs + - *install-drupal + - export TARGET_BRANCH=${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}${CI_COMMIT_BRANCH} + - git fetch -vn --depth=50 origin "+refs/heads/$TARGET_BRANCH:refs/heads/$TARGET_BRANCH" + - | + echo "ℹ️ Changes from ${TARGET_BRANCH}" + git diff ${CI_MERGE_REQUEST_DIFF_BASE_SHA} --name-only + echo "1️⃣ Reverting non test changes" + if [[ $(git diff ${CI_MERGE_REQUEST_DIFF_BASE_SHA} --diff-filter=DM --name-only|grep -Ev '.test$'|grep -v .gitlab-ci|grep -v scripts/run-tests.sh) ]]; then + git diff ${CI_MERGE_REQUEST_DIFF_BASE_SHA} --diff-filter=DM --name-only|grep -Ev '.test$'|grep -v .gitlab-ci|grep -v scripts/run-tests.sh|while read file;do + echo "↩️ Reverting $file" + git checkout refs/heads/${TARGET_BRANCH} -- $file; + done + fi + echo "2️⃣ Deleting new files" + if [[ $(git diff ${CI_MERGE_REQUEST_DIFF_BASE_SHA} --diff-filter=A --name-only|grep -Ev '.test$'|grep -v .gitlab-ci|grep -v scripts/run-tests.sh) ]]; then + git diff ${CI_MERGE_REQUEST_DIFF_BASE_SHA} --diff-filter=A --name-only|grep -Ev '.test$'|grep -v .gitlab-ci|grep -v scripts/run-tests.sh|while read file;do + echo "🗑️️ Deleting $file" + git rm $file + done + fi + echo "3️⃣ Running test changes for this branch" + if [[ $(git diff ${CI_MERGE_REQUEST_DIFF_BASE_SHA} --name-only|grep -E '.test$') ]]; then + git diff ${CI_MERGE_REQUEST_DIFF_BASE_SHA} --name-only|grep -E ".test$"|while read file;do + sudo -u www-data php ./scripts/run-tests.sh --color --concurrency "$CONCURRENCY" --url "$SIMPLETEST_BASE_URL" --verbose --fail-only --xml "$CI_PROJECT_DIR/sites/default/files/simpletest/test-only" --file "$file" + done + fi + +################ +# Jobs +# +# Jobs define what scripts are actually executed in each stage. +################ + +'⚡️ PHPUnit Unit': + <<: [ *phpunit-artifacts, *setup-webserver, *run-tests, *default-job-settings ] + stage: 🗜️ Test + parallel: 3 + services: + - <<: *with-database + - <<: *with-chrome + variables: + <<: *test-variables + CONCURRENCY: "$CONCURRENCY" + KUBERNETES_CPU_REQUEST: "16" + +'🩹 Test-only changes': + <<: [ *phpunit-artifacts, *setup-webserver, *run-test-only-tests, *default-job-settings ] + stage: 🗜️ Test + when: manual + interruptible: true + allow_failure: true + variables: + <<: *test-variables + services: + - <<: *with-database + - <<: *with-chrome diff --git a/drupal7/web/.htaccess b/drupal7/web/.htaccess new file mode 100644 index 0000000..7dd1d14 --- /dev/null +++ b/drupal7/web/.htaccess @@ -0,0 +1,156 @@ +# +# Apache/PHP/Drupal settings: +# + +# Protect files and directories from prying eyes. + + + Require all denied + + + Order allow,deny + + + +# Don't show directory listings for URLs which map to a directory. +Options -Indexes + +# Follow symbolic links in this directory. +Options +FollowSymLinks + +# Make Drupal handle any 404 errors. +ErrorDocument 404 /index.php + +# Set the default handler. +DirectoryIndex index.php index.html index.htm + +# Override PHP settings that cannot be changed at runtime. See +# sites/default/default.settings.php and drupal_environment_initialize() in +# includes/bootstrap.inc for settings that can be changed at runtime. + +# PHP 5, Apache 1 and 2. + + php_flag magic_quotes_gpc off + php_flag magic_quotes_sybase off + php_flag register_globals off + php_flag session.auto_start off + php_value mbstring.http_input pass + php_value mbstring.http_output pass + php_flag mbstring.encoding_translation off + + +# Requires mod_expires to be enabled. + + # Enable expirations. + ExpiresActive On + + # Cache all files for 2 weeks after access (A). + ExpiresDefault A1209600 + + + # Do not allow PHP scripts to be cached unless they explicitly send cache + # headers themselves. Otherwise all scripts would have to overwrite the + # headers set by mod_expires if they want another caching behavior. This may + # fail if an error occurs early in the bootstrap process, and it may cause + # problems if a non-Drupal PHP file is installed in a subdirectory. + ExpiresActive Off + + + +# Various rewrite rules. + + RewriteEngine on + + # Set "protossl" to "s" if we were accessed via https://. This is used later + # if you enable "www." stripping or enforcement, in order to ensure that + # you don't bounce between http and https. + RewriteRule ^ - [E=protossl] + RewriteCond %{HTTPS} on + RewriteRule ^ - [E=protossl:s] + + # Make sure Authorization HTTP header is available to PHP + # even when running as CGI or FastCGI. + RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # Block access to "hidden" directories whose names begin with a period. This + # includes directories used by version control systems such as Subversion or + # Git to store control files. Files whose names begin with a period, as well + # as the control files used by CVS, are protected by the FilesMatch directive + # above. + # + # NOTE: This only works when mod_rewrite is loaded. Without mod_rewrite, it is + # not possible to block access to entire directories from .htaccess, because + # is not allowed here. + # + # If you do not have mod_rewrite installed, you should remove these + # directories from your webroot or otherwise protect them from being + # downloaded. + RewriteRule "/\.|^\.(?!well-known/)" - [F] + + # If your site can be accessed both with and without the 'www.' prefix, you + # can use one of the following settings to redirect users to your preferred + # URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option: + # + # To redirect all users to access the site WITH the 'www.' prefix, + # (http://example.com/... will be redirected to http://www.example.com/...) + # uncomment the following: + # RewriteCond %{HTTP_HOST} . + # RewriteCond %{HTTP_HOST} !^www\. [NC] + # RewriteRule ^ http%{ENV:protossl}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301] + # + # To redirect all users to access the site WITHOUT the 'www.' prefix, + # (http://www.example.com/... will be redirected to http://example.com/...) + # uncomment the following: + # RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] + # RewriteRule ^ http%{ENV:protossl}://%1%{REQUEST_URI} [L,R=301] + + # Modify the RewriteBase if you are using Drupal in a subdirectory or in a + # VirtualDocumentRoot and the rewrite rules are not working properly. + # For example if your site is at http://example.com/drupal uncomment and + # modify the following line: + # RewriteBase /drupal + # + # If your site is running in a VirtualDocumentRoot at http://example.com/, + # uncomment the following line: + # RewriteBase / + + # Pass all requests not referring directly to files in the filesystem to + # index.php. Clean URLs are handled in drupal_environment_initialize(). + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} !=/favicon.ico + RewriteRule ^ index.php [L] + + # Rules to correctly serve gzip compressed CSS and JS files. + # Requires both mod_rewrite and mod_headers to be enabled. + + # Serve gzip compressed CSS files if they exist and the client accepts gzip. + RewriteCond %{HTTP:Accept-encoding} gzip + RewriteCond %{REQUEST_FILENAME}\.gz -s + RewriteRule ^(.*)\.css $1\.css\.gz [QSA] + + # Serve gzip compressed JS files if they exist and the client accepts gzip. + RewriteCond %{HTTP:Accept-encoding} gzip + RewriteCond %{REQUEST_FILENAME}\.gz -s + RewriteRule ^(.*)\.js $1\.js\.gz [QSA] + + # Serve correct content types, and prevent double compression. + RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1,E=no-brotli:1] + RewriteRule \.js\.gz$ - [T=text/javascript,E=no-gzip:1,E=no-brotli:1] + + + # Serve correct encoding type. + Header set Content-Encoding gzip + # Force proxies to cache gzipped & non-gzipped css/js files separately. + Header append Vary Accept-Encoding + + + + +# Various header fixes. + + # Disable content sniffing, since it's an attack vector. + Header always set X-Content-Type-Options nosniff + # Disable Proxy header, since it's an attack vector. + RequestHeader unset Proxy + diff --git a/drupal7/web/CHANGELOG.txt b/drupal7/web/CHANGELOG.txt new file mode 100644 index 0000000..73c7f8f --- /dev/null +++ b/drupal7/web/CHANGELOG.txt @@ -0,0 +1,2556 @@ +Drupal 7.100, 2024-03-06 +------------------------ +- Security improvements +- Announcements module added + +Drupal 7.99, 2023-12-06 +----------------------- +- Various security improvements +- Various bug fixes, optimizations and improvements + +Drupal 7.98, 2023-06-07 +----------------------- +- Various security improvements +- Various bug fixes, optimizations and improvements + +Drupal 7.97, 2023-04-21 +----------------------- +- Fix PHP 5.x regression caused by SA-CORE-2023-005 + +Drupal 7.96, 2023-04-19 +----------------------- +- Fixed security issues: + - SA-CORE-2023-005 + +Drupal 7.95, 2023-03-15 +----------------------- +- Fixed security issues: + - SA-CORE-2023-004 + +Drupal 7.94, 2022-12-14 +----------------------- +- Hotfix for book.module and Select query properties + +Drupal 7.93, 2022-12-07 +----------------------- +- Improved support for PHP 8.2 +- Minimum PHP version changed to PHP 5.3 +- Various security hardenings +- Various bug fixes, optimizations and improvements + +Drupal 7.92, 2022-09-07 +----------------------- +- Improved support for PHP 8.1 +- Various security hardenings +- Various bug fixes, optimizations and improvements + +Drupal 7.91, 2022-07-20 +----------------------- +- Fixed security issues: + - SA-CORE-2022-012 + +Drupal 7.90, 2022-06-01 +----------------------- +- Improved support for PHP 8.1 +- Improved support for PostgreSQL +- Various bug fixes, optimizations and improvements + +Drupal 7.89, 2022-03-02 +----------------------- +- Bug fixes for PHP 8.1 +- Fix tests for PostgreSQL + +Drupal 7.88, 2022-02-15 +----------------------- +- Fixed security issues: + - SA-CORE-2022-003 + +Drupal 7.87, 2022-01-19 +----------------------- +- Fix regression caused by jQuery UI position() backport + +Drupal 7.86, 2022-01-18 +----------------------- +- Fixed security issues: + - SA-CORE-2022-001 + - SA-CORE-2022-002 + +Drupal 7.85, 2022-01-12 +----------------------- +- Fix session cookies for sites with different base_urls but a shared domain + +Drupal 7.84, 2021-12-13 +----------------------- +- Hotfix for session cookie domain on www subdomains + +Drupal 7.83, 2021-12-01 +----------------------- +- Initial support for PHP 8.1 +- The has_js cookie has been removed (but can be re-enabled) +- The leading www. is no longer stripped from cookie domain by default +- The user entity now has a "changed" property +- Introduced a skip_permissions_hardening setting +- Changes to the password reset process to avoid email and username enumeration +- Various bug fixes, optimizations and improvements + +Drupal 7.82, 2021-07-21 +----------------------- +- Fixed security issues: + - SA-CORE-2021-004 + +Drupal 7.81, 2021-06-02 +----------------------- +- Block Google FLoC by default +- Testing and accessibility enhancements +- Various bug fixes, optimizations and improvements + +Drupal 7.80, 2021-04-20 +----------------------- +- Fixed security issues: + - SA-CORE-2021-002 + +Drupal 7.79, 2021-04-07 +----------------------- +- Initial support for PHP 8 +- Support for SameSite cookie attribute +- Avoid write for unchanged fields (opt-in) + +Drupal 7.78, 2021-01-19 +----------------------- +- Fixed security issues: + - SA-CORE-2021-001 + +Drupal 7.77, 2020-12-03 +----------------------- +- Hotfix for schema.prefixed tables + +Drupal 7.76, 2020-12-02 +----------------------- +- Support for MySQL 8 +- Core tests pass in SQLite +- Better user flood control logging + +Drupal 7.75, 2020-11-26 +----------------------- +- Fixed security issues: + - SA-CORE-2020-013 + +Drupal 7.74, 2020-11-17 +----------------------- +- Fixed security issues: + - SA-CORE-2020-012 + +Drupal 7.73, 2020-09-16 +----------------------- +- Fixed security issues: + - SA-CORE-2020-007 + +Drupal 7.72, 2020-06-17 +----------------------- +- Fixed security issues: + - SA-CORE-2020-004 + +Drupal 7.71, 2020-06-03 +----------------------- +- Fix for jQuery Form bug in Chromium-based browsers +- Full support for PHP 7.4 + +Drupal 7.70, 2020-05-19 +----------------------- +- Fixed security issues: + - SA-CORE-2020-002 + - SA-CORE-2020-003 + +Drupal 7.69, 2019-12-18 +----------------------- +- Fixed security issues: + - SA-CORE-2019-012 + +Drupal 7.68, 2019-12-04 +----------------------- +- Fixed: Hide toolbar when printing +- Fixed: Settings returned via ajax are not run through hook_js_alter() +- Fixed: Use drupal_http_build_query() in drupal_http_request() +- Fixed: DrupalRequestSanitizer not found fatal error when bootstrap phase order is changed +- Fixed: Block web.config in .htaccess (and vice-versa) +- Fixed: Create "scripts" element to align rendering workflow to how "styles" are handled +- PHP 7.3: Fixed 'Cannot change session id when session is active' +- PHP 7.1: Fixed 'A non-numeric value encountered in theme_pager()' +- PHP 7.x: Fixed file.inc generated .htaccess does not cover PHP 7 +- PHP 5.3: Fixed check_plain() 'Invalid multibyte sequence in argument' test failures +- Fixed: Allow passing data as array to drupal_http_request() +- Fixed: Skip module_invoke/module_hook in calling hook_watchdog (excessive function_exist) +- Fixed: HTTP status 200 returned for 'Additional uncaught exception thrown while handling exception' +- Fixed: theme_table() should take an optional footer variable and produce +- Fixed: 'uasort() expects parameter 1 to be array, null given in node_view_multiple()' +- [regression] Fix default.settings.php permission + +Drupal 7.67, 2019-05-08 +----------------------- +- Fixed security issues: + - SA-CORE-2019-007 + +Drupal 7.66, 2019-04-17 +----------------------- +- Fixed security issues: + - SA-CORE-2019-006 + +Drupal 7.65, 2019-03-20 +----------------------- +- Fixed security issues: + - SA-CORE-2019-004 + +Drupal 7.64, 2019-02-06 +----------------------- +- [regression] Unset the 'host' header in drupal_http_request() during redirect +- Fixed: 7.x does not have Phar protection and Phar tests are failing on Drupal 7 +- Fixed: Notice: Undefined index: display_field in file_field_widget_value() (line 582 of /module/file/file.field.inc) +- Performance improvement: Registry rebuild should not parse the same file twice in the same request +- Fixed _registry_update() to clear caches after transaction is committed + +Drupal 7.63, 2019-01-16 +----------------------- +- Fixed a fatal error for some Drush users introduced by SA-CORE-2019-002. + +Drupal 7.62, 2019-01-15 +----------------------- +- Fixed security issues: + - SA-CORE-2019-001 + - SA-CORE-2019-002 + +Drupal 7.61, 2018-11-07 +----------------------- +- File upload validation functions and hook_file_validate() implementations are + now always passed the correct file URI. +- The default form cache expiration of 6 hours is now configurable (API + addition: https://www.drupal.org/node/2857751). +- Allowed callers of drupal_http_request() to optionally specify an explicit + Host header. +- Allowed the + character to appear in usernames. +- PHP 7.2: Fixed Archive_Tar incompatibility. +- PHP 7.2: Removed deprecated function each(). +- PHP 7.2: Avoid count() calls on uncountable variables. +- PHP 7.2: Removed deprecated create_function() call. +- PHP 7.2: Make sure variables are arrays in theme_links(). +- Fixed theme-settings.php not being loaded on cached forms +- Fixed problem with IE11 & Chrome(PointerEvents enabled) & some Firefox scroll to the top of the page after dragging the bottom item with jquery 1.5 <-> 1.11 + +Drupal 7.60, 2018-10-18 +------------------------ +- Fixed security issues. See SA-CORE-2018-006. + +Drupal 7.59, 2018-04-25 +----------------------- +- Fixed security issues (remote code execution). See SA-CORE-2018-004. + +Drupal 7.58, 2018-03-28 +----------------------- +- Fixed security issues (remote code execution). See SA-CORE-2018-002. + +Drupal 7.57, 2018-02-21 +----------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2018-001. + +Drupal 7.56, 2017-06-21 +----------------------- +- Fixed security issues (access bypass). See SA-CORE-2017-003. + +Drupal 7.55, 2017-06-07 +----------------------- +- Fixed incompatibility with PHP versions 7.0.19 and 7.1.5 due to duplicate + DATE_RFC7231 definition. +- Made Drupal core pass all automated tests on PHP 7.1. +- Allowed services such as Let's Encrypt to work with Drupal on Apache, by + making Drupal's .htaccess file allow access to the .well-known directory + defined by RFC 5785. +- Made new Drupal sites work correctly on Apache 2.4 when the mod_access_compat + Apache module is disabled. +- Fixed Drupal's URL-generating functions to always encode '[' and ']' so that + the URLs will pass HTML5 validation. +- Various additional bug fixes. +- Various API documentation improvements. +- Additional automated test coverage. + +Drupal 7.54, 2017-02-01 +----------------------- +- Modules are now able to define theme engines (API addition: + https://www.drupal.org/node/2826480). +- Logging of searches can now be disabled (new option in the administrative + interface). +- Added menu tree render structure to (pre-)process hooks for theme_menu_tree() + (API addition: https://www.drupal.org/node/2827134). +- Added new function for determining whether an HTTPS request is being served + (API addition: https://www.drupal.org/node/2824590). +- Fixed incorrect default value for short and medium date formats on the date + type configuration page. +- File validation error message is now removed after subsequent upload of valid + file. +- Numerous bug fixes. +- Numerous API documentation improvements. +- Additional performance improvements. +- Additional automated test coverage. + +Drupal 7.53, 2016-12-07 +----------------------- +- Fixed drag and drop support on newer Chrome/IE 11+ versions after 7.51 update + when jQuery is updated to 1.7-1.11.0. + +Drupal 7.52, 2016-11-16 +----------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2016-005. + +Drupal 7.51, 2016-10-05 +----------------------- +- The Update module now also checks for updates to a disabled theme that is + used as an admin theme. +- Exceptions thrown in dblog_watchdog() are now caught and ignored. +- Clarified the warning that appears when modules are missing or have moved. +- Log messages are now XSS filtered on display. +- Draggable tables now work on touch screen devices. +- Added a setting for allowing double underscores in CSS identifiers + (https://www.drupal.org/node/2810369). +- If a user navigates away from a page while an Ajax request is running they + will no longer get an error message saying "An Ajax HTTP request terminated + abnormally". +- The system_region_list() API function now takes an optional third parameter + which allows region name translations to be skipped when they are not needed + (API addition: https://www.drupal.org/node/2810365). +- Numerous performance improvements. +- Numerous bug fixes. +- Numerous API documentation improvements. +- Additional automated test coverage. + +Drupal 7.50, 2016-07-07 +----------------------- +- Added a new "administer fields" permission for trusted users, which is + required in addition to other permissions to use the field UI + (https://www.drupal.org/node/2483307). +- Added clickjacking protection to Drupal core by setting the X-Frame-Options + header to SAMEORIGIN by default (https://www.drupal.org/node/2735873). +- Added support for full UTF-8 (emojis, Asian symbols, mathematical symbols) on + MySQL and other database drivers when the site and database are configured to + allow it (https://www.drupal.org/node/2761183). +- Improved performance by avoiding a re-scan of directories when a file is + missing; instead, trigger a PHP warning (minor API change: + https://www.drupal.org/node/2581445). +- Made it possible to use any PHP callable in Ajax form callbacks, form API + form-building functions, and form API wrapper callbacks (API addition: + https://www.drupal.org/node/2761169). +- Fixed that following a password reset link while logged in leaves users unable + to change their password (minor user interface change: + https://www.drupal.org/node/2759023). +- Implemented various fixes for automated test failures on PHP 5.4+ and PHP 7. + Drupal core automated tests now pass in these environments. +- Improved support for PHP 7 by fixing various problems. +- Fixed various bugs with PHP 5.5+ imagerotate(), including when incorrect + color indices are passed in. +- Fixed a regression introduced in Drupal 7.43 that allowed files uploaded by + anonymous users to be lost after form validation errors, and that also caused + regressions with certain contributed modules. +- Fixed a regression introduced in Drupal 7.36 which caused the default value + of hidden textarea fields to be ignored. +- Fixed robots.txt to allow search engines to access CSS, JavaScript and image + files. +- Changed wording on the Update Manager settings page to clarify that the + option to check for disabled module updates also applies to uninstalled + modules (administrative-facing translatable string change). +- Changed the help text when editing menu links and configuring URL redirect + actions so that it does not reference "Drupal" or the drupal.org website + (administrative-facing translatable string change). +- Fixed the locale safety check that is used to ensure that translations are + safe to allow for tokens in the href/src attributes of translated strings. +- Fixed that URL generation only works on port 80 when using domain based + language negotation. +- Made method="get" forms work inside the administrative overlay. The fix adds + a new hidden field to these forms when they appear inside the overlay (minor + data structure change). +- Increased maxlength of menu link title input fields in the node form and + menu link form from 128 to 255 characters. +- Removed meaningless post-check=0 and pre-check=0 cache control headers from + Drupal HTTP responses. +- Added a .editorconfig file to auto-configure editors that support it. +- Added --directory option to run-tests.sh for easier test discovery of all + tests within a project. +- Made run-tests.sh exit with a failure code when there are test fails or + problems running the script. +- Fixed that cookies from previous tests are still present when a new test + starts in DrupalWebTestCase. +- Improved performance of queries on the {authmap} database table. +- Fixed handling of missing files and functions inside the registry. +- Fixed Ajax handling for tableselect form elements that use checkboxes. +- Fixed a bug which caused ip_address() to return nothing when the client IP + address and proxy IP address are the same. +- Added a new option to format_xml_elements() to allow for already encoded + values. +- Changed the {history} table's node ID field to be an unsigned integer, to + match the same field in the {node} table and to prevent errors with very + large node IDs. +- Added an explicit page callback to the "admin/people/create" menu item in the + User module (minor data structure change). Previously this automatically + inherited the page callback from the parent "admin/people" menu item, which + broke contributed modules that override the "admin/people" page. +- Numerous small bug fixes. +- Numerous API documentation improvements. +- Additional automated test coverage. + +Drupal 7.44, 2016-06-15 +----------------------- +- Fixed security issues (privilege escalation). See SA-CORE-2016-002. + +Drupal 7.43, 2016-02-24 +----------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2016-001. + +Drupal 7.42, 2016-02-03 +----------------------- +- Stopped invoking hook_flush_caches() on every cron run, since some modules + use that hook for expensive operations that are only needed on cache clears. +- Changed the default .htaccess and web.config to block Composer-related files. +- Added static caching to module_load_include() to improve performance. +- Fixed double-encoding bugs in select field widgets provided by the Options + module. The fix deprecates the 'strip_tags' property on option widgets and + replaces it with a new 'strip_tags_and_unescape' property (minor data + structure change). +- Improved MySQL 5.7 support by changing the MySQL database driver to stop + using the ANSI SQL mode alias, which has different meanings for different + MySQL versions. +- Fixed a regression introduced in Drupal 7.39 which prevented autocomplete + functionality from working on servers that are not configured to + automatically recognize index.php. +- Updated the Archive_Tar PEAR package to the latest 1.4.0 release, to fix bugs + with tar file handling on various operating systems. +- Fixed fatal errors on node preview when a field is displayed in the node + teaser but hidden in the full node view. The fix removes a + field_attach_prepare_view() call from the node_preview() function since it is + redundant with one in the node preview theme layer. +- Improved the description of the "Trimmed" format option on text fields + (translatable string change, and minor UI and data structure change). +- Numerous small bug fixes. +- Numerous API documentation improvements. +- Additional automated test coverage. + +Drupal 7.41, 2015-10-21 +----------------------- +- Fixed security issues (open redirect). See SA-CORE-2015-004. + +Drupal 7.40, 2015-10-14 +----------------------- +- Made Drupal's code for parsing .info files run much faster and use much less + memory. +- Prevented drupal_http_request() from returning an error when it receives a + 201 through 206 HTTP status code. +- Added support for autoloading traits via the registry on sites running PHP + 5.4 or higher. +- Allowed the user-picture.tpl.php theme template to have HTML classes besides + the default "user-picture" class printed in it (markup change). +- Fixed the URL text filter to convert e-mail addresses with plus signs into + mailto: links. +- Added alternate text to file icons displayed by the File module, to improve + accessibility (string change, and minor API addition to theme_file_icon()). +- Changed one-time login link failure messages to be displayed as errors or + warnings as appropriate, rather than as regular status messages (minor UI + change and data structure change). +- Changed the default settings.php configuration to exclude private files from + the "404_fast_paths" behavior. +- Changed the page that displays filter tips for a particular text format, for + example filter/tips/full_html, to return "page not found" or "access denied" + if the format does not exist or the user does not have access to it. This + change adds a new menu item to the Filter module's hook_menu() entry (minor + data structure change). +- Added a new hook, hook_block_cid_parts_alter(), to allow modules to alter the + cache keys used for caching a particular block. +- Made drupal_set_message() display and return messages when "0" is passed in + as the message to set. +- Fixed non-functional "Files displayed by default" setting on file fields. +- The "worker callback" provided in hook_cron_queue_info() and the "finished" + callback specified during batch processing can now be any PHP callable + instead of just functions. +- Prevented drupal_set_time_limit() from decreasing the time limit in the case + where the PHP maximum execution time is already unlimited. +- Changed the default thousand marker for numeric fields from a space ("1 000") + to nothing ("1000") (minor UI change: https://www.drupal.org/node/1388376). +- Prevented malformed theme .info files (without a "name" key) from causing + exceptions during menu rebuilds. If an .info file without a "name" key is + found in a module or theme directory, Drupal will now use the module or + theme's machine name as the display name instead. +- Made the format column in the {date_format_locale} database table + case-sensitive, to match the equivalent column in the {date_formats} table. +- Fixed a bug in the Statistics module that caused JavaScript files attached to + a node while it is being viewed to be omitted from the page. +- Added an optional 'project:' prefix that can be added to dependencies in a + module's .info file to indicate which project the dependency resides in (API + addition: https://www.drupal.org/node/2299747). +- Fixed various bugs that occurred after hooks were invoked early in the Drupal + bootstrap and that caused module_implements() and drupal_alter() to cache an + incomplete set of hook implementations for later use. +- Set the X-Content-Type-Options header to "nosniff" when possible, to prevent + certain web browsers from picking an unsafe MIME type. +- Prevented the database API from executing multiple queries at once on MySQL, + if the site's PHP version is new enough to do so. This is a secondary defense + against SQL injection (API change: https://www.drupal.org/node/2463973). +- Fixed a bug in the Drupal 6 to Drupal 7 upgrade path which caused the upgrade + to fail when there were multiple file records pointing to the same file. +- Numerous small bug fixes. +- Numerous API documentation improvements. +- Additional automated test coverage. + +Drupal 7.39, 2015-08-19 +----------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2015-003. + +Drupal 7.38, 2015-06-17 +----------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2015-002. + +Drupal 7.37, 2015-05-07 +----------------------- +- Fixed a regression in Drupal 7.36 which caused certain kinds of content types + to become disabled if they were defined by a no-longer-enabled module. +- Removed a confusing description regarding automatic time zone detection from + the user account form (minor UI and data structure change). +- Allowed custom HTML tags with a dash in the name to pass through filter_xss() + when specified in the list of allowed tags. +- Allowed hook_field_schema() implementations to specify indexes for fields + based on a fixed-length column prefix (rather than the entire column), as was + already allowed in hook_schema() implementations. +- Fixed PDO exceptions on PostgreSQL when accessing invalid entity URLs. +- Added a sites/all/libraries folder to the codebase, with instructions for + using it. +- Added a description to the "Administer text formats and filters" permission + on the Permissions page (string change). +- Numerous small bug fixes. +- Numerous API documentation improvements. +- Additional automated test coverage. + +Drupal 7.36, 2015-04-01 +----------------------- +- Added a 'file_public_schema' variable which allows modules that define + publicly-accessible streams in hook_stream_wrappers() to bypass file download + access checks when processing managed file upload fields. +- Fixed a bug that caused database query tags not to be added to search-related + database queries under many circumstances, and which prevented the + corresponding hook_query_TAG_alter() implementations from being called. +- Fixed the "for" attribute on managed file upload field labels to improve + accessibility (minor markup change). +- Added a 'javascript_always_use_jquery' variable which can be set to FALSE by + sites that may not need jQuery loaded on all pages, and a 'requires_jquery' + option to drupal_add_js() which modules can set to FALSE when adding + JavaScript files that have no dependency on jQuery (API addition: + https://www.drupal.org/node/2462717). +- Fixed incorrect foreign keys in the User module's role_permission and + users_roles database tables. +- Changed permission descriptions throughout Drupal core to consistently link + to relevant administrative pages, regardless of whether the user viewing the + Permissions page can view the page being linked to (minor UI change). +- Fixed the drupal_add_region_content() function so that it actually adds + content to the page. +- Added an 'image_suppress_itok_output' variable to allow sites already using + the existing 'image_allow_insecure_derivatives' variable to also prevent + security tokens from appearing in image derivative URLs. +- Fixed double-escaping of theme names in the Block module administrative + interface (minor string change). +- Added basic support for Xdebug when running automated tests. +- Fixed a bug which caused previewing a node to remove elements from the node + being edited. With this fix, calling node_preview() will no longer modify the + passed-in node object (minor API change). +- Added a user_has_role() function to check whether a user has a particular + role (API addition: https://www.drupal.org/node/2462411). +- Fixed installation failures when an opcode cache is enabled. +- Fixed a bug in the Drupal 6 to Drupal 7 upgrade path which caused private + files to be inaccessible. +- Fixed a bug in the Drupal 6 to Drupal 7 upgrade path which caused user + pictures to be lost. +- Fixed missing language code in hook_field_attach_view_alter() when it is + invoked from field_view_field(). +- Stopped sending ETag and Last-Modified headers for uncached page requests, + since they break caching for certain Varnish and Nginx configurations. +- Changed the Simpletest module to allow PSR-4 test classes to be used in + Drupal 7. +- Fixed a fatal error that occurred when using the Comment module's "Unpublish + comment containing keyword(s)" action. +- Changed the "lang" attribute on language links to "xml:lang" so it validates + as XHTML (minor markup change). +- Prevented the form API from allowing arrays to be submitted for various form + elements, such as textfields, textareas, and password fields (API change: + https://www.drupal.org/node/2462723). +- Fixed a bug in the Contact module which caused the global user object to have + the incorrect name and e-mail address during the remainder of the page + request after the contact form is submitted. +- Numerous small bug fixes. +- Numerous API documentation improvements. +- Additional automated test coverage. + +Drupal 7.35, 2015-03-18 +----------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2015-001. + +Drupal 7.34, 2014-11-19 +----------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-006. + +Drupal 7.33, 2014-11-07 +----------------------- +- Began storing the file modification time of each module and theme in the + {system} database table so that contributed modules can use it to identify + recently changed modules and themes (minor data structure change to the + return value of system_get_info() and other related functions). +- Added a "Did you mean?" feature to the run-tests.sh script for running + automated tests from the command line, to help developers who are attempting + to run a particular test class or group. +- Changed the date format used in various HTTP headers output by Drupal core + from RFC 1123 format to RFC 7231 format. +- Added a "block_cache_bypass_node_grants" variable to allow sites which have + node access modules enabled to use the block cache if desired (API addition). +- Made image derivative generation HTTP requests return a 404 error (rather + than a 500 error) when the source image does not exist. +- Fixed a bug which caused user pictures to be removed from the user object + after saving, and resulted in data loss if the user account was subsequently + re-saved. +- Fixed a bug in which field_has_data() did not return TRUE for fields that + only had data in older entity revisions, leading to loss of the field's data + when the field configuration was edited. +- Fixed a bug which caused the Ajax progress throbber to appear misaligned in + many situatons (minor styling change). +- Prevented the Bartik theme from lower-casing the "Permalink" link on + comments, for improved multilingual support (minor UI change). +- Added a "preferred_menu_links" tag to the database query that is used by + menu_link_get_preferred() to find the preferred menu link for a given path, + to make it easier to alter. +- Increased the maximum allowed length of block titles to 255 characters + (database schema change to the {block} table). +- Removed the Field module's field_modules_uninstalled() function, since it did + not do anything when it was invoked. +- Added a "theme_hook_original" variable to templates and theme functions and + an optional sitewide theme debug mode, to provide contextual information in + the page's HTML to theme developers. The theme debug mode is based on the one + used with Twig in Drupal 8 and can be accessed by setting the "theme_debug" + variable to TRUE (API addition). +- Added an entity_view_mode_prepare() API function to allow entity-defining + modules to properly invoke hook_entity_view_mode_alter(), and used it + throughout Drupal core to fix bugs with the invocation of that hook (API + change: https://www.drupal.org/node/2369141). +- Security improvement: Made the database API's orderBy() method sanitize the + sort direction ("ASC" or "DESC") for queries built with db_select(), so that + calling code does not have to. +- Changed the RDF module to consistently output RDF metadata for nodes and + comments near where the node is rendered in the HTML (minor markup and data + structure change). +- Added an HTML class to RDFa metatags throughout Drupal to prevent them from + accidentally affecting the site appearance (minor markup change). +- Fixed a bug in the Unicode requirements check which prevented installing + Drupal on PHP 5.6. +- Fixed a bug which caused drupal_get_bootstrap_phase() to abort the bootstrap + when called early in the page request. +- Renamed the "Search result" view mode to "Search result highlighting input" + to better reflect how it is used (UI change). +- Improved database queries generated by EntityFieldQuery in the case where + delta or language condition groups are used, to reduce the number of INNER + JOINs (this is a minor data structure change affecting code which implements + hook_query_alter() on these queries). +- Removed special-case behavior for file uploads which allowed user #1 to + bypass maximum file size and user quota limits. +- Numerous small bug fixes. +- Numerous API documentation improvements. +- Additional automated test coverage. + +Drupal 7.32, 2014-10-15 +----------------------- +- Fixed security issues (SQL injection). See SA-CORE-2014-005. + +Drupal 7.31, 2014-08-06 +----------------------- +- Fixed security issues (denial of service). See SA-CORE-2014-004. + +Drupal 7.30, 2014-07-24 +----------------------- +- Fixed a regression introduced in Drupal 7.29 that caused files or images + attached to taxonomy terms to be deleted when the taxonomy term was edited + and resaved (and other related bugs with contributed and custom modules). +- Added a warning on the permissions page to recommend restricting access to + the "View site reports" permission to trusted administrators. See + DRUPAL-PSA-2014-002. +- Numerous API documentation improvements. +- Additional automated test coverage. + +Drupal 7.29, 2014-07-16 +----------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-003. + +Drupal 7.28, 2014-05-08 +----------------------- +- Fixed a regression introduced in Drupal 7.27 that caused JavaScript to break + on older browsers (such as Internet Explorer 8 and earlier) when Ajax was + used. +- Increased the timeout used by the Update Manager module when it fetches data + from drupal.org (from 5 seconds to 30 seconds), to work around a problem + which causes incomplete information about security updates to be presented to + site administrators. This fix may lead to a performance slowdown on the + Update Manager administration pages, when installing Drupal distributions, + and (for sites that use the automated cron feature) on occasional page loads + by site visitors. +- Fixed the behavior of the token system's "[node:summary]" token when the body + field does not have a manual summary. +- Changed the behavior of db_query_temporary() so that it works on SELECT + queries even when they have leading comments/whitespace. A side effect of + this fix is that db_query_temporary() will now fail with an error if it is + ever used on non-SELECT queries. +- Added a "node_admin_filter" tag to the database query used to build the list + of nodes on the content administration page, to make it easier to alter. +- Made the cron queue system log any exceptions that are thrown while an item + in the queue is being processed, rather than stopping the entire PHP request. +- Improved screen reader support by adding an aria-live HTML attribute to file + upload fields when there is an error uploading the file (minor markup + change). +- Made the pager on the Tracker module listing pages show the same number of + items as other pagers throughout Drupal core (minor UI change). +- Fixed a bug which caused caches not to be properly cleared when a file entity + was saved or deleted. +- Added several missing countries to the default list returned by + country_get_list() (string change). +- Replaced the term "weight" with "influence" in the content ranking settings + for search, and added help text for administrators (string change). +- Fixed untranslatable text strings in the administrative interface for the + "Crop" effect provided by the Image module (minor string change). +- Fixed a bug in the Taxonomy module update function introduced in Drupal 7.26 + that caused memory and CPU problems on sites with very large numbers of + unpublished nodes. +- Numerous small bug fixes. +- Numerous API documentation improvements. +- Additional automated test coverage. + +Drupal 7.27, 2014-04-16 +----------------------- +- Fixed security issues (information disclosure). See SA-CORE-2014-002. + +Drupal 7.26, 2014-01-15 +----------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-001. + +Drupal 7.25, 2014-01-02 +----------------------- +- Fixed a bug in node_save() which prevented the saved node from being updated + in hook_node_insert() and other similar hooks. +- Added a meta tag to install.php to prevent it from being indexed by search + engines even when Drupal is installed in a subfolder (minor markup change). +- Fixed a bug in the database API that caused frequent deadlock errors when + running merge queries on some servers. +- Performance improvement: Prevented block rehashing from writing blocks to the + database on every cache clear and cron run when the blocks have not changed. + This fix results in an extra 'saved' key which is added and set to TRUE for + each block returned by _block_rehash() that actually is saved to the database + (data structure change). +- Added an optional 'skip on cron' parameter to hook_cron_queue_info() to allow + queues to avoid being automatically processed on cron runs (API addition). +- Fixed a bug which caused hook_block_view_MODULE_DELTA_alter() to never be + invoked if the block delta had a hyphen in it. To implement the hook when the + block delta has a hyphen, modules should now replace hyphens with underscores + when constructing the function name for the hook implementation. +- Fixed a bug which caused cached pages to sometimes be sent to the browser + with incorrect compression. The fix adds a new 'page_compressed' key to the + $cache->data array returned by drupal_page_get_cache() (minor data structure + change). +- Fixed broken tests on PHP 5.5. +- Made the File and Image modules more robust when saving entities that have + deleted files attached. The code in file_field_presave() will now remove the + record of the deleted file from the entity before saving (minor data + structure change). +- Standardized menu callback functions throughout Drupal core to return + MENU_NOT_FOUND and MENU_ACCESS_DENIED rather than printing their own "page + not found" or "access denied" pages (minor API change in the return value of + these functions under some circumstances). +- Fixed a bug in which caches were not properly cleared when a node was deleted + via the administrative interface. +- Changed the Bartik theme to render content contained in
,  and
+  similar tags in a larger font size, so it is easier to read.
+- Fixed a bug in the Search module that caused exceptions to be thrown during
+  searches if the server was not configured to represent decimal points as a
+  period.
+- Fixed a regression in the Image module that made image_style_url() not work
+  when a relative path (rather than a complete file URI) was passed to it.
+- Added an optional feature to the Statistics module to allow node views to be
+  tracked by Ajax requests rather than during the server-side generation of the
+  page. This allows the node counter to work on sites that use external page
+  caches (string change and new administrative option:
+  https://drupal.org/node/2164069).
+- Added a link to the drupal.org documentation page for cron to the Cron
+  settings page (string change).
+- Added a 'drupal_anonymous_user_object' variable to allow the anonymous user
+  object returned by drupal_anonymous_user() to be overridden with a classed
+  object (API addition).
+- Changed the database API to allow inserts based on a SELECT * query to work
+  correctly.
+- Changed the database schema of the {file_managed} table to allow Drupal to
+  manage files larger than 4 GB.
+- Changed the File module's hook_field_load() implementation to prevent file
+  entity properties which have the same name as file or image field properties
+  from overwriting the field properties (minor API change).
+- Numerous small bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.24, 2013-11-20
+-----------------------
+- Fixed security issues (multiple vulnerabilities), see SA-CORE-2013-003.
+
+Drupal 7.23, 2013-08-07
+-----------------------
+- Fixed a fatal error on PostgreSQL databases when updating the Taxonomy module
+  from Drupal 6 to Drupal 7.
+- Fixed the default ordering of CSS files for sites using right-to-left
+  languages, to consistently place the right-to-left override file immediately
+  after the CSS it is overriding (API change: https://drupal.org/node/2058463).
+- Added a drupal_check_memory_limit() API function to allow the memory limit to
+  be checked consistently (API addition).
+- Changed the default web.config file for IIS servers to allow favicon.ico
+  files which are present in the filesystem to be accessed.
+- Fixed inconsistent support for the 'tel' protocol in Drupal's URL filtering
+  functions.
+- Performance improvement: Allowed all hooks to be included in the
+  module_implements() cache, even those that are only invoked on HTTP POST
+  requests.
+- Made the database system replace truncate queries with delete queries when
+  inside a transaction, to fix issues with PostgreSQL and other databases.
+- Fixed a bug which caused nested contextual links to display improperly.
+- Fixed a bug which prevented cached image derivatives from being flushed for
+  private files and other non-default file schemes.
+- Fixed drupal_render() to always return an empty string when there is no
+  output, rather than sometimes returning NULL (minor API change).
+- Added protection to cache_clear_all() to ensure that non-cache tables cannot
+  be truncated (API addition: a new isValidBin() method has been added to the
+  default database cache implementation).
+- Changed the default .htaccess file to support HTTP authorization in CGI
+  environments.
+- Changed the password reset form to pre-fill the username when requested via a
+  URL query parameter, and used this in the error message that appears after a
+  failed login attempt (minor data structure and behavior change).
+- Fixed broken support for foreign keys in the field API.
+- Fixed "No active batch" error when a user cancels their own account.
+- Added a description to the "access content overview" permission on the
+  permissions page (string change).
+- Added a drupal_array_diff_assoc_recursive() function to allow associative
+  arrays to be compared recursively (API addition).
+- Added human-readable labels to image styles, in addition to the existing
+  machine-readable name (API change: https://drupal.org/node/2058503).
+- Moved the drupal_get_hash_salt() function to bootstrap.inc and used it in
+  additional places in the code, for added security in the case where there is
+  no hash salt in settings.php.
+- Fixed a regression in Drupal 7.22 that caused internal server errors for
+  sites running on very old Apache 1.x web servers.
+- Numerous small bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.22, 2013-04-03
+-----------------------
+- Allowed the drupal_http_request() function to be overridden so that
+  additional HTTP request capabilities can be added by contributed modules.
+- Changed the Simpletest module to allow PSR-0 test classes to be used in
+  Drupal 7.
+- Removed an unnecessary "Content-Disposition" header from private file
+  downloads; it prevented many private files from being viewed inline in a web
+  browser.
+- Changed various field API functions to allow them to optionally act on a
+  single field within an entity (API addition: http://drupal.org/node/1825844).
+- Fixed a bug which prevented Drupal's file transfer functionality from working
+  on some PHP 5.4 systems.
+- Fixed incorrect log message when theme() is called for a theme hook that does
+  not exist (minor string change).
+- Fixed Drupal's token-replacement system to allow spaces in the token value.
+- Changed the default behavior after a user creates a node they do not have
+  access to view. The user will now be redirected to the front page rather than
+  an access denied page.
+- Fixed a bug which prevented empty HTTP headers (such as "0") from being set.
+  (Minor behavior change: Callers of drupal_add_http_header() must now set
+  FALSE explicitly to prevent a header from being sent at all; this was already
+  indicated in the function's documentation.)
+- Fixed OpenID errors when more than one module implements hook_openid(). The
+  behavior is now changed so that if more than one module tries to set the same
+  parameter, the last module's change takes effect.
+- Fixed a serious documentation bug: The $name variable in the
+  taxonomy-term.tpl.php theme template was incorrectly documented as being
+  sanitized when in fact it is not.
+- Fixed a bug which prevented Drupal 6 to Drupal 7 upgrades on sites which had
+  duplicate permission names in the User module's database tables.
+- Added an empty "datatype" attribute to taxonomy term and username links to
+  make the RDFa markup upward compatible with RDFa 1.1 (minor markup addition).
+- Fixed a bug which caused the denial-of-service protection added in Drupal
+  7.20 to break certain valid image URLs that had an extra slash in them.
+- Fixed a bug with update queries in the SQLite database driver that prevented
+  Drupal from being installed with SQLite on PHP 5.4.
+- Fixed enforced dependencies errors updating to recent versions of Drupal 7 on
+  certain non-MySQL databases.
+- Refactored the Field module's caching behavior to obtain large improvements
+  in memory usage for sites with many fields and instances (API addition:
+  http://drupal.org/node/1915646).
+- Fixed entity argument not being passed to implementations of
+  hook_file_download_access_alter(). The fix adds an additional context
+  parameter that can be passed when calling drupal_alter() for any hook (API
+  change: http://drupal.org/node/1882722).
+- Fixed broken support for translatable comment fields (API change:
+  http://drupal.org/node/1874724).
+- Added an assertThemeOutput() method to Simpletest to allow tests to check
+  that themed output matches an expected HTML string (API addition).
+- Added a link to "Install another module" after a module has been successfully
+  downloaded via the Update Manager (UI change).
+- Added an optional "exclusive" flag to installation profile .info files which
+  allows Drupal distributions to force a profile to be selected during
+  installation (API addition: http://drupal.org/node/1961012).
+- Fixed a bug which caused the database API to not properly close database
+  connections.
+- Added a link to the URL for running cron from outside the site to the Cron
+  settings page (UI change).
+- Fixed a bug which prevented image styles from being reverted on PHP 5.4.
+- Made the default .htaccess rules protocol sensitive to improve security for
+  sites which use HTTPS and redirect between "www" and non-"www" versions of
+  the page.
+- Numerous small bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.21, 2013-03-06
+-----------------------
+- Allowed sites using the 'image_allow_insecure_derivatives' variable to still
+  have partial protection from the security issues fixed in Drupal 7.20.
+
+Drupal 7.20, 2013-02-20
+-----------------------
+- Fixed security issues (denial of service). See SA-CORE-2013-002.
+
+Drupal 7.19, 2013-01-16
+-----------------------
+- Fixed security issues (multiple vulnerabilities). See SA-CORE-2013-001.
+
+Drupal 7.18, 2012-12-19
+-----------------------
+- Fixed security issues (multiple vulnerabilities). See SA-CORE-2012-004.
+
+Drupal 7.17, 2012-11-07
+-----------------------
+- Changed the default value of the '404_fast_html' variable to have a DOCTYPE
+  declaration.
+- Made it possible to use associative arrays for the 'items' variable in
+  theme_item_list().
+- Fixed a bug which prevented required form elements without a title from being
+  given an "error" class when the form fails validation.
+- Prevented duplicate HTML IDs from appearing when two forms are displayed on
+  the same page and one of them is submitted with invalid data (minor markup
+  change).
+- Fixed a bug which prevented Drupal 6 to Drupal 7 upgrades on sites which had
+  stale data in the Upload module's database tables.
+- Fixed a bug in the States API which prevented certain types of form elements
+  from being disabled when requested.
+- Allowed aggregator feed items with author names longer than 255 characters to
+  have a truncated version saved to the database (rather than causing a fatal
+  error).
+- Allowed aggregator feed items to have URLs longer than 255 characters
+  (schema change which results in several columns in the Aggregator module's
+  database tables changing from VARCHAR to TEXT fields).
+- Added hook_taxonomy_term_view() and standardized the process for rendering
+  taxonomy terms to invoke hook_entity_view() and otherwise make it consistent
+  with other entities (API change: http://drupal.org/node/1808870).
+- Added hook_entity_view_mode_alter() to allow modules to change entity view
+  modes on display (API addition: http://drupal.org/node/1833086).
+- Fixed a bug which made database queries running a "LIKE" query on blob fields
+  fail on PostgreSQL databases. This caused errors during the Drupal 6 to
+  Drupal 7 upgrade.
+- Changed the hook_menu() entry for Drupal's rss.xml page to prevent extra path
+  components from being accidentally passed to the page callback function (data
+  structure change).
+- Removed a non-standard "name" attribute from Drupal's default Content-Type
+  header for file downloads.
+- Fixed the theme settings form to properly clean up submitted values in
+  $form_state['values'] when the form is submitted (data structure change).
+- Fixed an inconsistency by removing the colon from the end of the label on
+  multi-valued form fields (minor string change).
+- Added support for 'weight' in hook_field_widget_info() to allow modules to
+  control the order in which widgets are displayed in the Field UI.
+- Updated various tables in the OpenID and Book modules to use the default
+  "empty table" text pattern (string change).
+- Added proxy server support to drupal_http_request().
+- Added "lang" attributes to language links, to better support screen readers.
+- Fixed double occurrence of a "ul" HTML tag on secondary local tasks in the
+  Seven theme (markup change).
+- Fixed bugs which caused taxonomy vocabulary and shortcut set titles to be
+  double-escaped. The fix replaces the taxonomy vocabulary overview page and
+  "Edit shortcuts" menu items' title callback entries in hook_menu() with new
+  functions that do not escape HTML characters (data structure change).
+- Modified the Update manager module to allow drupal.org to collect usage
+  statistics for individual modules and themes, rather than only for entire
+  projects.
+- Modified the node listing database query on Drupal's default front page to
+  add table aliases for better query altering (this is a data structure change
+  affecting code which implements hook_query_alter() on this query).
+- Improved the translatability of the "Field type(s) in use" message on the
+  modules page (admin-facing string change).
+- Fixed a regression which caused a "call to undefined function
+  drupal_find_base_themes()" fatal error under rare circumstances.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.16, 2012-10-17
+-----------------------
+- Fixed security issues (Arbitrary PHP code execution and information
+  disclosure). See SA-CORE-2012-003.
+
+Drupal 7.15, 2012-08-01
+-----------------------
+- Introduced a 'user_password_reset_timeout' variable to allow the 24-hour
+  expiration for user password reset links to be adjusted (API addition).
+- Fixed database errors due to ambiguous column names that occurred when
+  EntityFieldQuery was used in certain situations.
+- Changed the drupal_array_get_nested_value() function to return a reference
+  (API addition).
+- Changed the System module's hook_block_info() implementation to assign the
+  "Main page content" and "System help" blocks to appropriate regions by
+  default and prevent error messages on the block administration page (data
+  structure change).
+- Fixed regression: Non-node entities couldn't be accessed with
+  EntityFieldQuery.
+- Fixed regression: Optional radio buttons with an empty, non-NULL default
+  value led to an illegal choice error when none were selected.
+- Reorganized the testing framework to split setUp() into specific sub-methods
+  and fix several regressions in the process.
+- Fixed bug which made it impossible to search for strings that have not been
+  translated into a particular language.
+- Renamed the "Field" column on the Manage Fields screen to "Field type", since
+  the former was confusing and inaccurate (UI change).
+- Performance improvement: Removed needless call to system_rebuild_module_data()
+  in field_sync_field_status(), greatly speeding up bulk module enable/disable.
+- Fixed bug which prevented notifications from being sent when core, module, and
+  theme updates are available.
+- Fixed bug which prevented sub-themes from inheriting the default values of
+  theme settings defined by the base theme.
+- Fixed bug which prevented the jQuery UI Datepicker from being localized.
+- Made Ajax alert dialogs respect error reporting settings.
+- Fixed bug which prevented image styles from being deleted on PHP 5.4.
+- Fixed bug: Language detection by domain only worked on port 80.
+- Fixed regression: The first plural index on a page was not calculated
+  correctly.
+- Introduced generic entity language support. Entities may now declare their
+  language property in hook_entity_info(), and modules working with entities
+  may access the language using entity_language() (API change:
+  http://drupal.org/node/1626346).
+- Added EntityFieldQuery support for taxonomy bundles.
+- Fixed issue where field form structure was incomplete if field_access()
+  returned FALSE. Instead of being incomplete, the form structure now has
+  #access set to FALSE and field form validation is skipped (data structure
+  change: http://drupal.org/node/1663020).
+- Fixed data loss issue due to field_has_data() returning inconsistent results.
+  The fix adds an optional DANGEROUS_ACCESS_CHECK_OPT_OUT tag to entity field
+  queries which field storage engines can respond to (API addition:
+  http://drupal.org/node/1597378).
+- Fixed notice: Undefined index: default_image in image_field_prepare_view()
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.14, 2012-05-02
+-----------------------
+- Fixed "integrity constraint" fatal errors when rebuilding registry.
+- Fixed custom logo and favicon functionality referencing incorrect paths.
+- Fixed DB Case Sensitivity: Allow BINARY attribute in MySQL.
+- Split field_bundle_settings out per bundle.
+- Improve UX for machine names for fields (UI change).
+- Fixed User pictures are not removed properly.
+- Fixed HTTPS sessions not working in all cases.
+- Fixed Regression: Required radios throw illegal choice error when none
+  selected.
+- Fixed allow autocompletion requests to include slashes.
+- Eliminate $user->cache and {session}.cache in favor of
+  $_SESSION['cache_expiration'][$bin] (Performance).
+- Fixed focus jumps to tab when pressing enter on a form element within tab.
+- Fixed race condition in locale() - duplicates in {locales_source}.
+- Fixed Missing "Default image" per field instance.
+- Quit clobbering people's work when they click the filter tips link
+- Form API #states: Fix conditionals to allow OR and XOR constructions.
+- Fixed Focus jumps to tab when pressing enter on a form element within tab.
+  (Accessibility)
+- Improved performance of node_access queries.
+- Fixed Fieldsets inside vertical tabs have no title and can't be collapsed.
+- Reduce size of cache_menu table (Performance).
+- Fixed unnecessary aggregation of CSS/JS (Performance).
+- Fixed taxonomy_autocomplete() produces SQL error for nonexistent field.
+- Fixed HTML filter is not run first by default, despite default weight.
+- Fixed Overlay does not work with prefixed URL paths.
+- Better debug info for field errors (string change).
+- Fixed Data corruption in comment IDs (results in broken threading on
+  PostgreSQL).
+- Fixed machine name not editable if every character is replaced.
+- Fixed user picture not appearing in comment preview (Markup change).
+- Added optional vid argument for taxonomy_get_term_by_name().
+- Fixed Invalid Unicode code range in PREG_CLASS_UNICODE_WORD_BOUNDARY fails
+  with PCRE 8.30.
+- Fixed {trigger_assignments()}.hook has only 32 characters, is too short.
+- Numerous fixes to run-tests.sh.
+- Fixed Tests in profiles/[name]/modules cannot be run and cannot use a
+  different profile for running tests.
+- Numerous JavaScript performance fixes.
+- Numerous documentation fixes.
+- Fixed All pager links have an 'active' CSS class.
+- Numerous upgrade path fixes; notably:
+  - system_update_7061() fails on inserting files with same name but different
+    case.
+  - system_update_7061() converts filepaths too aggressively.
+  - Trigger upgrade path: Node triggers removed when upgrading to 7-x from 6.25.
+
+Drupal 7.13, 2012-05-02
+-----------------------
+- Fixed security issues (Multiple vulnerabilities), see SA-CORE-2012-002.
+
+Drupal 7.12, 2012-02-01
+-----------------------
+- Fixed bug preventing custom menus from receiving an active trail.
+- Fixed hook_field_delete() no longer invoked during field_purge_data().
+- Fixed bug causing entity info cache to not be cleared with the rest of caches.
+- Fixed file_unmanaged_copy() fails with Drupal 7.7+ and safe_mode() or
+  open_basedir().
+- Fixed Nested transactions throw exceptions when they got out of scope.
+- Fixed bugs with the Return-Path when sending mail on both Windows and
+  non-Windows systems.
+- Fixed bug with DrupalCacheArray property visibility preventing others from
+  extending it (API change: http://drupal.org/node/1422264).
+- Fixed bug with handling of non-ASCII characters in file names (API change:
+  http://drupal.org/node/1424840).
+- Reconciled field maximum length with database column size in image and
+  aggregator modules.
+- Fixes to various core JavaScript files to allow for minification and
+  aggregation.
+- Fixed Prevent tests from deleting main installation's tables when
+  parent::setUp() is not called.
+- Fixed several Poll module bugs.
+- Fixed several Shortcut module bugs.
+- Added new hook_system_theme_info() to provide ability for contributed modules
+  to test theme functionality.
+- Added ability to cancel mail sending from hook_mail_alter().
+- Added support for configurable PDO connection options, enabling master-master
+  database replication.
+- Numerous improvements to tests and test runner to pave the way for faster test
+  runs.
+- Expanded test coverage.
+- Numerous API documentation improvements.
+- Numerous performance improvements, including token replacement and render
+  cache.
+
+Drupal 7.11, 2012-02-01
+-----------------------
+- Fixed security issues (Multiple vulnerabilities), see SA-CORE-2012-001.
+
+Drupal 7.10, 2011-12-05
+-----------------------
+- Fixed Content-Language HTTP header to not cause issues with Drush 5.x.
+- Reduce memory usage of theme registry (performance).
+- Fixed PECL upload progress bar for FileField
+- Fixed running update.php doesn't always clear the cache.
+- Fixed PDO exceptions on long titles.
+- Fixed Overlay redirect does not include query string.
+- Fixed D6 modules satisfy D7 module dependencies.
+- Fixed the ordering of module hooks when using module_implements_alter().
+- Fixed "floating" submit buttons during AJAX requests.
+- Fixed timezone selected on install not propogating to admin account.
+- Added msgctx context to JS translation functions, for feature parity with t().
+- Profiles' .install files now available during hook_install_tasks().
+- Added test coverage of 7.0 -> 7.x upgrade path.
+- Numerous notice fixes.
+- Numerous documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.9, 2011-10-26
+----------------------
+- Critical fixes to OpenID to spec violations that could allow for
+  impersonation in certain scenarios. Existing OpenID users should see
+  http://drupal.org/node/1120290#comment-5092796 for more information on
+  transitioning.
+- Fixed files getting lost when adding multiple files to multiple file fields
+  at the same time.
+- Improved usability of the clean URL test screens.
+- Restored height/width attributes on images run through the theme system.
+- Fixed usability bug with first password field being pre-filled by certain
+  browser plugins.
+- Fixed file_usage_list() so that it can return more than one result.
+- Fixed bug preventing preview of private images on node form.
+- Fixed PDO error when inserting an aggregator title longer than 255 characters.
+- Spelled out what TRADITIONAL means in MySQL sql_mode.
+- Deprecated "!=" operator for DBTNG; should be "<>".
+- Added two new API functions (menu_tree_set_path()/menu_tree_get_path()) were
+  added in order to enable setting the active menu trail for dynamically
+  generated menu paths.
+- Added new "fast 404" capability in settings.php to bypass Drupal bootstrap
+  when serving 404 pages for certain file types.
+- Added format_string() function which can perform string munging ala the t()
+  function without the overhead of the translation system.
+- Numerous #states system fixes.
+- Numerous EntityFieldQuery, DBTNG, and SQLite fixes.
+- Numerous Shortcut module fixes.
+- Numerous language system fixes.
+- Numerous token fixes.
+- Numerous CSS fixes.
+- Numerous upgrade path fixes.
+- Numerous minor string fixes.
+- Numerous notice fixes.
+
+Drupal 7.8, 2011-08-31
+----------------------
+- Fixed critical upgrade path issue with multilingual sites, leading to lost
+  content.
+- Numerous fixes to upgrade path, preventing fatal errors due to incorrect
+  dependencies.
+- Fixed issue with saving files on hosts with open_basedir restrictions.
+- Fixed Update manger error when used with Overlay.
+- Fixed RTL support in Seven administration theme and Overlay.
+- Fixes to nested transaction support.
+- Introduced performance pattern to reduce Drupal core's RAM usage.
+- Added support for HTML 5 tags to filter_xss_admin().
+- Added exception handling to cron.
+- Added new hook hook_field_widget_form_alter() for contribtued modules.
+- element_validate_*() functions now available to contrib.
+- Added new maintainers for several subsystems.
+- Numerous testing system improvements.
+- Numerous markup and CSS fixes.
+- Numerous poll module fixes.
+- Numerous notice/warning fixes.
+- Numerous documentation fixes.
+- Numerous token fixes.
+
+Drupal 7.7, 2011-07-27
+----------------------
+- Fixed VERSION string.
+
+Drupal 7.6, 2011-07-27
+----------------------
+- Fixed support for remote streamwrappers.
+- AJAX now binds to 'click' instead of 'mousedown'.
+- 'Translatable' flag on fields created in UI now defaults to FALSE, to match those created via the API.
+- Performance enhancement to permissions page on large numbers of permissions.
+- More secure password generation.
+- Fix for temporary directory on Windows servers.
+- run-tests.sh now uses proc_open() instead of pcntl_fork() for better Windows support.
+- Numerous upgrade path fixes.
+- Numerous documentation fixes.
+- Numerous notice fixes.
+- Numerous fixes to improve PHP 5.4 support.
+- Numerous RTL improvements.
+
+Drupal 7.5, 2011-07-27
+----------------------
+- Fixed security issue (Access bypass), see SA-CORE-2011-003.
+
+Drupal 7.4, 2011-06-29
+----------------------
+- Rolled back patch that caused fatal errors in CTools, Feeds, and other modules using the class registry.
+- Fixed critical bug with saving default images.
+- Fixed fatal errors when uninstalling some modules.
+- Added workaround for MySQL transaction support breaking on DDL statments.
+- Improved page caching with external caching systems.
+- Fix to Batch API, which was terminating too early.
+- Numerous upgrade path fixes.
+- Performance fixes.
+- Additional test coverage.
+- Numerous documentation fixes.
+
+Drupal 7.3, 2011-06-29
+----------------------
+- Fixed security issue (Access bypass), see SA-CORE-2011-002.
+
+Drupal 7.2, 2011-05-25
+----------------------
+- Added a default .gitignore file.
+- Improved PostgreSQL and SQLite support.
+- Numerous critical performance improvements.
+- Numerous critical fixes to the upgrade path.
+- Numerous fixes to language and translation systems.
+- Numerous fixes to AJAX and #states systems.
+- Improvements to the locking system.
+- Numerous documentation fixes.
+- Numerous styling and theme system fixes.
+- Numerous fixes for schema mis-matches between Drupal 6 and 7.
+- Minor internal API clean-ups.
+
+Drupal 7.1, 2011-05-25
+----------------------
+- Fixed security issues (Cross site scripting, File access bypass), see SA-CORE-2011-001.
+
+Drupal 7.0, 2011-01-05 
+----------------------
+- Database:
+    * Fully rewritten database layer utilizing PHP 5's PDO abstraction layer.
+    * Drupal now requires MySQL >= 5.0.15 or PostgreSQL >= 8.3.
+    * Added query builders for INSERT, UPDATE, DELETE, MERGE, and SELECT queries.
+    * Support for master/slave replication, transactions, multi-insert queries,
+      and other features.
+    * Added support for the SQLite database engine.
+    * Default to InnoDB engine, rather than MyISAM, on MySQL when available.
+      This offers increased scalability and data integrity.
+- Security:
+    * Protected cron.php -- cron will only run if the proper key is provided.
+    * Implemented a pluggable password system and much stronger password hashes
+      that are compatible with the Portable PHP password hashing framework.
+    * Rate limited login attempts to prevent brute-force password guessing, and
+      improved the flood control API to allow variable time windows and
+      identifiers for limiting user access to resources.
+    * Transformed the "Update status" module into the "Update manager" which
+      can securely install or update modules and themes via a web interface.
+- Usability:
+    * Added contextual links (a.k.a. local tasks) to page elements, such as
+      blocks, nodes, or comments, which allows to perform the most common tasks
+      with a single click only.
+    * Improved installer requirements check.
+    * Improved support for integration of WYSIWYG editors.
+    * Implemented drag-and-drop positioning for input format listings.
+    * Implemented drag-and-drop positioning for language listing.
+    * Implemented drag-and-drop positioning for poll options.
+    * Provided descriptions and human-readable names for user permissions.
+    * Removed comment controls for users.
+    * Removed display order settings for comment module. Comment display
+      order can now be customized using the Views module.
+    * Removed the 'related terms' feature from taxonomy module since this can
+      now be achieved with Field API.
+    * Added additional features to the default installation profile, and
+      implemented a "slimmed down" profile designed for developers.
+    * Added a built-in, automated cron run feature, which is triggered by site
+      visitors.
+    * Added an administrator role which is assigned all permissions for
+      installed modules automatically.
+    * Image toolkits are now provided by modules (rather than requiring a
+      manual file copy to the includes directory).
+    * Added an edit tab to taxonomy term pages.
+    * Redesigned password strength validator.
+    * Redesigned the add content type screen.
+    * Highlight duplicate URL aliases.
+    * Renamed "input formats" to "text formats".
+    * Moved text format permissions to the main permissions page.
+    * Added configurable ability for users to cancel their own accounts.
+    * Added "vertical tabs", a reusable interface component that features
+      automatic summaries and increases usability.
+    * Replaced fieldsets on node edit and add pages with vertical tabs.
+- Performance:
+    * Improved performance on uncached page views by loading multiple core
+      objects in a single database query.
+    * Improved performance for logged-in users by reducing queries for path
+      alias lookups.
+    * Improved support for HTTP proxies (including reverse proxies), allowing
+      anonymous page views to be served entirely from the proxy.
+- Documentation:
+    * Hook API documentation now included in Drupal core.
+- News aggregator:
+    * Added OPML import functionality for RSS feeds.
+    * Optionally, RSS feeds may be configured to not automatically generate feed blocks.
+- Search:
+    * Added support for language-aware searches.
+- Aggregator:
+    * Introduced architecture that allows pluggable parsers and processors for
+      syndicating RSS and Atom feeds.
+    * Added options to suspend updating specific feeds and never discard feeds
+      items.
+- Testing:
+    * Added test framework and tests.
+- Improved time zone support:
+    * Drupal now uses PHP's time zone database when rendering dates in local
+      time. Site-wide and user-configured time zone offsets have been converted
+      to time zone names, e.g. Africa/Abidjan.
+    * In some cases the upgrade and install scripts do not choose the preferred
+      site default time zone. The automatically-selected time zone can be
+      corrected at admin/config/regional/settings.
+    * If your site is being upgraded from Drupal 6 and you do not have the
+      contributed date or event modules installed, user time zone settings will
+      fallback to the system time zone and will have to be reconfigured by each user.
+    * User-configured time zones now serve as the default time zone for PHP
+      date/time functions.
+- Filter system:
+    * Revamped the filter API and text format storage.
+    * Added support for default text formats to be assigned on a per-role basis.
+    * Refactored the HTML corrector to take advantage of PHP 5 features.
+- User system:
+    * Added clean API functions for creating, loading, updating, and deleting
+      user roles and permissions.
+    * Refactored the "access rules" component of user module: The user module
+      now provides a simple interface for blocking single IP addresses. The
+      previous functionality in the user module for restricting certain e-mail
+      addresses and usernames is now available as a contributed module. Further,
+      IP address range blocking is no longer supported and should be implemented
+      at the operating system level.
+    * Removed per-user themes: Contributed modules with similar functionality
+      are available.
+- OpenID:
+    * Added support for Gmail and Google Apps for Domain identifiers. Users can
+      now login with their user@example.com identifier when example.com is powered
+      by Google.
+    * Made the OpenID module more pluggable.
+- Added code registry:
+    * Using the registry, modules declare their includable files via their .info file,
+      allowing Drupal to lazy-load classes and interfaces as needed.
+- Theme system:
+    * Removed the Bluemarine, Chameleon and Pushbutton themes. These themes live
+      on as contributed themes (http://drupal.org/project/bluemarine,
+      http://drupal.org/project/chameleon and http://drupal.org/project/pushbutton).
+    * Added Stark theme to make analyzing Drupal's default HTML and CSS easier.
+    * Added Seven as the default administration theme.
+    * Variable preprocessing of theme hooks prior to template rendering now goes
+      through two phases: a 'preprocess' phase and a new 'process' phase. See
+      http://api.drupal.org/api/function/theme/7 for details.
+    * Theme hooks implemented as functions (rather than as templates) can now
+      also have preprocess (and process) functions. See
+      http://api.drupal.org/api/function/theme/7 for details.
+    * Added Bartik as the default theme.
+- File handling:
+    * Files are now first class Drupal objects with file_load(), file_save(),
+      and file_validate() functions and corresponding hooks.
+    * The file_move(), file_copy() and file_delete() functions now operate on
+      file objects and invoke file hooks so that modules are notified and can
+      respond to changes.
+    * For the occasions when only basic file manipulation are needed--such as
+      uploading a site logo--that don't require the overhead of databases and
+      hooks, the current unmanaged copy, move and delete operations have been
+      preserved but renamed to file_unmanaged_*().
+    * Rewrote file handling to use PHP stream wrappers to enable support for
+      both public and private files and to support pluggable storage mechanisms
+      and access to remote resources (e.g. S3 storage or Flickr photos).
+    * The mime_extension_mapping variable has been removed. Modules that need to
+      alter the default MIME type extension mappings should implement
+      hook_file_mimetype_mapping_alter().
+    * Added the hook_file_url_alter() hook, which makes it possible to serve
+      files from a CDN.
+    * Added a field specifically for uploading files, previously provided by
+      the contributed module FileField.
+- Image handling:
+    * Improved image handling, including better support for add-on image
+      libraries.
+    * Added API and interface for creating advanced image thumbnails.
+    * Inclusion of additional effects such as rotate and desaturate.
+    * Added a field specifically for uploading images, previously provided by
+      the contributed module ImageField.
+- Added aliased multi-site support:
+    * Added support for mapping domain names to sites directories.
+- Added RDF support:
+    * Modules can declare RDF namespaces which are serialized in the  tag
+      for RDFa support.
+    * Modules can specify how their data structure maps to RDF.
+    * Added support for RDFa export of nodes, comments, terms, users, etc. and
+      their fields.
+- Search engine optimization and web linking:
+    * Added a rel="canonical" link on node and comment pages to prevent
+      duplicate content indexing by search engines.
+    * Added a default rel="shortlink" link on node and comment pages that
+      advertises a short link as an alternative URL to third-party services.
+    * Meta information is now alterable by all modules before rendering.
+- Field API:
+    * Custom data fields may be attached to nodes, users, comments and taxonomy
+      terms.
+    * Node bodies and teasers are now Field API fields instead of
+      being a hard-coded property of node objects.
+    * In addition, any other object type may register with Field API
+      and allow custom data fields to be attached to itself.
+    * Provides most of the features of the former Content Construction
+      Kit (CCK) module.
+    * Taxonomy terms are now Field API fields that can be added to any fieldable
+      object.
+- Installer:
+    * Refactored the installer into an API that allows Drupal to be installed
+      via a command line script.
+- Page organization
+    * Made the help text area a full featured region with blocks.
+    * Site mission is replaced with the highlighted content block region and
+      separate RSS feed description settings.
+    * The footer message setting was removed in favor of custom blocks.
+    * Made the main page content a block which can be moved and ordered
+      with other blocks in the same region.
+    * Blocks can now return structured arrays for later rendering just
+      like page callbacks.
+- Translation system
+    * The translation system now supports message context (msgctxt).
+    * Added support for translatable fields to Field API.
+- JavaScript changes
+    * Upgraded the core JavaScript library to jQuery version 1.4.4.
+    * Upgraded the jQuery Forms library to 2.52.
+    * Added jQuery UI 1.8.7, which allows improvements to Drupal's user
+      experience.
+- Better module version support
+    * Modules now can specify which version of another module they depend on.
+- Removed modules from core
+    * The following modules have been removed from core, because contributed
+      modules with similar functionality are available:
+      * Blog API module
+      * Ping module
+      * Throttle module
+- Improved node access control system.
+    * All modules may now influence the access to a node at runtime, not just
+      the module that defined a node.
+    * Users may now be allowed to bypass node access restrictions without giving
+      them complete access to the site.
+    * Access control affects both published and unpublished nodes.
+    * Numerous other improvements to the node access system.
+- Actions system
+    * Simplified definitions of actions and triggers.
+    * Removed dependency on the combination of hooks and operations. Triggers
+      now directly map to module hooks.
+- Task handling
+    * Added a queue API to process many or long-running tasks.
+    * Added queue API support to cron API.
+    * Added a locking framework to coordinate long-running operations across
+      requests.
+
+Drupal 6.23-dev, xxxx-xx-xx (development release)
+---------------------------
+
+Drupal 6.22, 2011-05-25
+-----------------------
+- Made Drupal 6 work better with IIS and Internet Explorer.
+- Fixed .po file imports to work better with custom textgroups.
+- Improved code documentation at various places.
+- Fixed a variety of other bugs.
+
+Drupal 6.21, 2011-05-25
+-----------------------
+- Fixed security issues (Cross site scripting), see SA-CORE-2011-001.
+
+Drupal 6.20, 2010-12-15
+-----------------------
+- Fixed a variety of small bugs, improved code documentation.
+
+Drupal 6.19, 2010-08-11
+-----------------------
+- Fixed a variety of small bugs, improved code documentation.
+
+Drupal 6.18, 2010-08-11
+-----------------------
+- Fixed security issues (OpenID authentication bypass, File download access
+  bypass, Comment unpublishing bypass, Actions cross site scripting),
+  see SA-CORE-2010-002.
+
+Drupal 6.17, 2010-06-02
+-----------------------
+- Improved PostgreSQL compatibility
+- Better PHP 5.3 and PHP 4 compatibility
+- Better browser compatibility of CSS and JS aggregation
+- Improved logging for login failures
+- Fixed an incompatibility with some contributed modules and the locking system
+- Fixed a variety of other bugs.
+
+Drupal 6.16, 2010-03-03
+-----------------------
+- Fixed security issues (Installation cross site scripting, Open redirection,
+  Locale module cross site scripting, Blocked user session regeneration),
+  see SA-CORE-2010-001.
+- Better support for updated jQuery versions.
+- Reduced resource usage of update.module.
+- Fixed several issues relating to support of installation profiles and
+  distributions.
+- Added a locking framework to avoid data corruption on long operations.
+- Fixed a variety of other bugs.
+
+Drupal 6.15, 2009-12-16
+-----------------------
+- Fixed security issues (Cross site scripting), see SA-CORE-2009-009.
+- Fixed a variety of other bugs.
+
+Drupal 6.14, 2009-09-16
+-----------------------
+- Fixed security issues (OpenID association cross site request forgeries,
+  OpenID impersonation and File upload), see SA-CORE-2009-008.
+- Changed the system modules page to not run all cache rebuilds; use the
+  button on the performance settings page to achieve the same effect.
+- Added support for PHP 5.3.0 out of the box.
+- Fixed a variety of small bugs.
+
+Drupal 6.13, 2009-07-01
+-----------------------
+- Fixed security issues (Cross site scripting, Input format access bypass and
+  Password leakage in URL), see SA-CORE-2009-007.
+- Fixed a variety of small bugs.
+
+Drupal 6.12, 2009-05-13
+-----------------------
+- Fixed security issues (Cross site scripting), see SA-CORE-2009-006.
+- Fixed a variety of small bugs.
+
+Drupal 6.11, 2009-04-29
+-----------------------
+- Fixed security issues (Cross site scripting and limited information
+  disclosure), see SA-CORE-2009-005
+- Fixed performance issues with the menu router cache, the update
+  status cache and improved cache invalidation
+- Fixed a variety of small bugs.
+
+Drupal 6.10, 2009-02-25
+-----------------------
+- Fixed a security issue, (Local file inclusion on Windows),
+  see SA-CORE-2009-003
+- Fixed node_feed() so custom fields can show up in RSS feeds.
+- Improved PostgreSQL compatibility.
+- Fixed a variety of small bugs.
+
+Drupal 6.9, 2009-01-14
+----------------------
+- Fixed security issues, (Access Bypass, Validation Bypass and Hardening
+  against SQL injection), see SA-CORE-2009-001
+- Made HTTP request checking more robust and informative.
+- Fixed HTTP_HOST checking to work again with HTTP 1.0 clients and
+  basic shell scripts.
+- Removed t() calls from all schema documentation. Suggested best practice
+  changed for contributed modules, see http://drupal.org/node/322731.
+- Fixed a variety of small bugs.
+
+Drupal 6.8, 2008-12-11
+----------------------
+- Removed a previous change incompatible with PHP 5.1.x and lower.
+
+Drupal 6.7, 2008-12-10
+----------------------
+- Fixed security issues, (Cross site request forgery and Cross site scripting), see SA-2008-073
+- Updated robots.txt and .htaccess to match current file use.
+- Fixed a variety of small bugs.
+
+Drupal 6.6, 2008-10-22
+----------------------
+- Fixed security issues, (File inclusion, Cross site scripting), see SA-2008-067
+- Fixed a variety of small bugs.
+
+Drupal 6.5, 2008-10-08
+----------------------
+- Fixed security issues, (File upload access bypass, Access rules bypass,
+  BlogAPI access bypass), see SA-2008-060.
+- Fixed a variety of small bugs.
+
+Drupal 6.4, 2008-08-13
+----------------------
+- Fixed a security issue (Cross site scripting, Arbitrary file uploads via
+  BlogAPI, Cross site request forgeries and Various Upload module
+  vulnerabilities), see SA-2008-047.
+- Improved error messages during installation.
+- Fixed a bug that prevented AHAH handlers to be attached to radios widgets.
+- Fixed a variety of small bugs.
+
+Drupal 6.3, 2008-07-09
+----------------------
+- Fixed security issues, (Cross site scripting, cross site request forgery,
+  session fixation and SQL injection), see SA-2008-044.
+- Slightly modified installation process to prevent file ownership issues on
+  shared hosts.
+- Improved PostgreSQL compatibility (rewritten queries; custom blocks).
+- Upgraded to jQuery 1.2.6.
+- Performance improvements to search, menu handling and form API caches.
+- Fixed Views compatibility issues (Views for Drupal 6 requires Drupal 6.3+).
+- Fixed a variety of small bugs.
+
+Drupal 6.2, 2008-04-09
+----------------------
+- Fixed a variety of small bugs.
+- Fixed a security issue (Access bypasses), see SA-2008-026.
+
+Drupal 6.1, 2008-02-27
+----------------------
+- Fixed a variety of small bugs.
+- Fixed a security issue (Cross site scripting), see SA-2008-018.
+
+Drupal 6.0, 2008-02-13
+----------------------
+- New, faster and better menu system.
+- New watchdog as a hook functionality.
+   * New hook_watchdog that can be implemented by any module to route log
+     messages to various destinations.
+   * Expands the severity levels from 3 (Error, Warning, Notice) to the 8
+     levels defined in RFC 3164.
+   * The watchdog module is now called dblog, and is optional, but enabled by
+     default in the default installation profile.
+   * Extended the database log module so log messages can be filtered.
+   * Added syslog module: useful for monitoring large Drupal installations.
+- Added optional e-mail notifications when users are approved, blocked, or
+  deleted.
+- Drupal works with error reporting set to E_ALL.
+- Added scripts/drupal.sh to execute Drupal code from the command line. Useful
+  to use Drupal as a framework to build command-line tools.
+- Made signature support optional and made it possible to theme signatures.
+- Made it possible to filter the URL aliases on the URL alias administration
+  screen.
+- Language system improvements:
+    * Support for right to left languages.
+    * Language detection based on parts of the URL.
+    * Browser based language detection.
+    * Made it possible to specify a node's language.
+    * Support for translating posts on the site to different languages.
+    * Language dependent path aliases.
+    * Automatically import translations when adding a new language.
+    * JavaScript interface translation.
+    * Automatically import a module's translation upon enabling that module.
+- Moved "PHP input filter" to a standalone module so it can be deleted for
+  security reasons.
+- Usability:
+    * Improved handling of teasers in posts.
+    * Added sticky table headers.
+    * Check for clean URL support automatically with JavaScript.
+    * Removed default/settings.php. Instead the installer will create it from
+      default.settings.php.
+    * Made it possible to configure your own date formats.
+    * Remember anonymous comment posters.
+    * Only allow modules and themes to be enabled that have explicitly been
+      ported to the correct core API version.
+    * Can now specify the minimum PHP version required for a module within the
+      .info file.
+    * Drupal core no longer requires CREATE TEMPORARY TABLES or LOCK TABLES
+      database rights.
+    * Dynamically check password strength and confirmation.
+    * Refactored poll administration.
+    * Implemented drag-and-drop positioning for blocks, menu items, taxonomy
+      vocabularies and terms, forums, profile fields, and input format filters.
+- Theme system:
+    * Added .info files to themes and made it easier to specify regions and
+      features.
+    * Added theme registry: modules can directly provide .tpl.php files for
+      their themes without having to create theme_ functions.
+    * Used the Garland theme for the installation and maintenance pages.
+    * Added theme preprocess functions for themes that are templates.
+    * Added support for themeable functions in JavaScript.
+- Refactored update.php to a generic batch API to be able to run time-consuming
+  operations in multiple subsequent HTTP requests.
+- Installer:
+    * Themed the installer with the Garland theme.
+    * Added form to provide initial site information during installation.
+    * Added ability to provide extra installation steps programmatically.
+    * Made it possible to import interface translations during installation.
+- Added the HTML corrector filter:
+    * Fixes faulty and chopped off HTML in postings.
+    * Tags are now automatically closed at the end of the teaser.
+- Performance:
+    * Made it easier to conditionally load .include files and split up many core
+      modules.
+    * Added a JavaScript aggregator.
+    * Added block-level caching, improving performance for both authenticated
+      and anonymous users.
+    * Made Drupal work correctly when running behind a reverse proxy like
+      Squid or Pound.
+- File handling improvements:
+    * Entries in the files table are now keyed to a user instead of a node.
+    * Added reusable validation functions to check for uploaded file sizes,
+      extensions, and image resolution.
+    * Added ability to create and remove temporary files during a cron job.
+- Forum improvements:
+    * Any node type may now be posted in a forum.
+- Taxonomy improvements:
+    * Descriptions for terms are now shown on taxonomy/term pages as well
+      as RSS feeds.
+    * Added versioning support to categories by associating them with node
+      revisions.
+- Added support for OpenID.
+- Added support for triggering configurable actions.
+- Added the Update status module to automatically check for available updates
+  and warn sites if they are missing security updates or newer versions.
+  Sites deploying from CVS should use http://drupal.org/project/cvs_deploy.
+  Advanced settings provided by http://drupal.org/project/update_advanced.
+- Upgraded the core JavaScript library to jQuery version 1.2.3.
+- Added a new Schema API, which provides built-in support for core and
+  contributed modules to work with databases other than MySQL.
+- Removed drupal.module. The functionality lives on as the Site network
+  contributed module (http://drupal.org/project/site_network).
+- Removed old system updates. Updates from Drupal versions prior to 5.x will
+  require upgrading to 5.x before upgrading to 6.x.
+
+Drupal 5.23, 2010-08-11
+-----------------------
+- Fixed security issues (File download access bypass, Comment unpublishing
+  bypass), see SA-CORE-2010-002.
+
+Drupal 5.22, 2010-03-03
+-----------------------
+- Fixed security issues (Open redirection, Locale module cross site scripting,
+  Blocked user session regeneration), see SA-CORE-2010-001.
+
+Drupal 5.21, 2009-12-16
+-----------------------
+- Fixed a security issue (Cross site scripting), see SA-CORE-2009-009.
+- Fixed a variety of small bugs.
+
+Drupal 5.20, 2009-09-16
+-----------------------
+- Avoid security problems resulting from writing Drupal 6-style menu
+  declarations.
+- Fixed security issues (session fixation), see SA-CORE-2009-008.
+- Fixed a variety of small bugs.
+
+Drupal 5.19, 2009-07-01
+-----------------------
+- Fixed security issues (Cross site scripting and Password leakage in URL), see
+  SA-CORE-2009-007.          
+- Fixed a variety of small bugs.
+
+Drupal 5.18, 2009-05-13
+-----------------------
+- Fixed security issues (Cross site scripting), see SA-CORE-2009-006.
+- Fixed a variety of small bugs.
+
+Drupal 5.17, 2009-04-29
+-----------------------
+- Fixed security issues (Cross site scripting and limited information
+  disclosure) see SA-CORE-2009-005.
+- Fixed a variety of small bugs.
+
+Drupal 5.16, 2009-02-25
+-----------------------
+- Fixed a security issue, (Local file inclusion on Windows), see SA-CORE-2009-004.
+- Fixed a variety of small bugs.
+
+Drupal 5.15, 2009-01-14
+-----------------------
+- Fixed security issues, (Hardening against SQL injection), see
+  SA-CORE-2009-001
+- Fixed HTTP_HOST checking to work again with HTTP 1.0 clients and basic shell
+  scripts.
+- Fixed a variety of small bugs.
+
+Drupal 5.14, 2008-12-11
+-----------------------
+- removed a previous change incompatible with PHP 5.1.x and lower.
+
+Drupal 5.13, 2008-12-10
+-----------------------
+- fixed a variety of small bugs.
+- fixed security issues, (Cross site request forgery and Cross site scripting), see SA-2008-073
+- updated robots.txt and .htaccess to match current file use.
+
+Drupal 5.12, 2008-10-22
+-----------------------
+- fixed security issues, (File inclusion), see SA-2008-067
+
+Drupal 5.11, 2008-10-08
+-----------------------
+- fixed a variety of small bugs.
+- fixed security issues, (File upload access bypass, Access rules bypass,
+  BlogAPI access bypass, Node validation bypass), see SA-2008-060
+
+Drupal 5.10, 2008-08-13
+-----------------------
+- fixed a variety of small bugs.
+- fixed security issues, (Cross site scripting, Arbitrary file uploads via
+  BlogAPI and Cross site request forgery), see SA-2008-047
+
+Drupal 5.9, 2008-07-23
+----------------------
+- fixed a variety of small bugs.
+- fixed security issues, (Session fixation), see SA-2008-046
+
+Drupal 5.8, 2008-07-09
+----------------------
+- fixed a variety of small bugs.
+- fixed security issues, (Cross site scripting, cross site request forgery, and
+  session fixation), see SA-2008-044
+
+Drupal 5.7, 2008-01-28
+----------------------
+- fixed the input format configuration page.
+- fixed a variety of small bugs.
+
+Drupal 5.6, 2008-01-10
+----------------------
+- fixed a variety of small bugs.
+- fixed a security issue (Cross site request forgery), see SA-2008-005
+- fixed a security issue (Cross site scripting, UTF8), see SA-2008-006
+- fixed a security issue (Cross site scripting, register_globals), see SA-2008-007
+
+Drupal 5.5, 2007-12-06
+----------------------
+- fixed missing missing brackets in a query in the user module.
+- fixed taxonomy feed bug introduced by SA-2007-031
+
+Drupal 5.4, 2007-12-05
+----------------------
+- fixed a variety of small bugs.
+- fixed a security issue (SQL injection), see SA-2007-031
+
+Drupal 5.3, 2007-10-17
+----------------------
+- fixed a variety of small bugs.
+- fixed a security issue (HTTP response splitting), see SA-2007-024
+- fixed a security issue (Arbitrary code execution via installer), see SA-2007-025
+- fixed a security issue (Cross site scripting via uploads), see SA-2007-026
+- fixed a security issue (User deletion cross site request forgery), see SA-2007-029
+- fixed a security issue (API handling of unpublished comment), see SA-2007-030
+
+Drupal 5.2, 2007-07-26
+----------------------
+- changed hook_link() $teaser argument to match documentation.
+- fixed a variety of small bugs.
+- fixed a security issue (cross-site request forgery), see SA-2007-017
+- fixed a security issue (cross-site scripting), see SA-2007-018
+
+Drupal 5.1, 2007-01-29
+----------------------
+- fixed security issue (code execution), see SA-2007-005
+- fixed a variety of small bugs.
+
+Drupal 5.0, 2007-01-15
+----------------------
+- Completely retooled the administration page
+    * /Admin now contains an administration page which may be themed
+    * Reorganised administration menu items by task and by module
+    * Added a status report page with detailed PHP/MySQL/Drupal information
+- Added web-based installer which can:
+    * Check installation and run-time requirements
+    * Automatically generate the database configuration file
+    * Install pre-made installation profiles or distributions
+    * Import the database structure with automatic table prefixing
+    * Be localized
+- Added new default Garland theme
+- Added color module to change some themes' color schemes
+- Included the jQuery JavaScript library 1.0.4 and converted all core JavaScript to use it
+- Introduced the ability to alter mail sent from system
+- Module system:
+    * Added .info files for module meta-data
+    * Added support for module dependencies
+    * Improved module installation screen
+    * Moved core modules to their own directories
+    * Added support for module uninstalling
+- Added support for different cache backends
+- Added support for a generic "sites/all" directory.
+- Usability:
+    * Added support for auto-complete forms (AJAX) to user profiles.
+    * Made it possible to instantly assign roles to newly created user accounts.
+    * Improved configurability of the contact forms.
+    * Reorganized the settings pages.
+    * Made it easy to investigate popular search terms.
+    * Added a 'select all' checkbox and a range select feature to administration tables.
+    * Simplified the 'break' tag to split teasers from body.
+    * Use proper capitalization for titles, menu items and operations.
+- Integrated urlfilter.module into filter.module
+- Block system:
+    * Extended the block visibility settings with a role specific setting.
+    * Made it possible to customize all block titles.
+- Poll module:
+    * Optionally allow people to inspect all votes.
+    * Optionally allow people to cancel their vote.
+- Distributed authentication:
+    * Added default server option.
+- Added default robots.txt to control crawlers.
+- Database API:
+    * Added db_table_exists().
+- Blogapi module:
+    * 'Blogapi new' and 'blogapi edit' nodeapi operations.
+- User module:
+    * Added hook_profile_alter().
+    * E-mail verification is made optional.
+    * Added mass editing and filtering on admin/user/user.
+- PHP Template engine:
+    * Add the ability to look for a series of suggested templates.
+    * Look for page templates based upon the path.
+    * Look for block templates based upon the region, module, and delta.
+- Content system:
+    * Made it easier for node access modules to work well with each other.
+    * Added configurable content types.
+    * Changed node rendering to work with structured arrays.
+- Performance:
+    * Improved session handling: reduces database overhead.
+    * Improved access checking: reduces database overhead.
+    * Made it possible to do memcached based session management.
+    * Omit sidebars when serving a '404 - Page not found': saves CPU cycles and bandwidth.
+    * Added an 'aggressive' caching policy.
+    * Added a CSS aggregator and compressor (up to 40% faster page loads).
+- Removed the archive module.
+- Upgrade system:
+    * Created space for update branches.
+- Form API:
+    * Made it possible to programmatically submit forms.
+    * Improved api for multistep forms.
+- Theme system:
+    * Split up and removed drupal.css.
+    * Added nested lists generation.
+    * Added a self-clearing block class.
+
+Drupal 4.7.11, 2008-01-10
+-------------------------
+- fixed a security issue (Cross site request forgery), see SA-2008-005
+- fixed a security issue (Cross site scripting, UTF8), see SA-2008-006
+- fixed a security issue (Cross site scripting, register_globals), see SA-2008-007
+
+Drupal 4.7.10, 2007-12-06
+-------------------------
+- fixed taxonomy feed bug introduced by SA-2007-031
+
+Drupal 4.7.9, 2007-12-05
+------------------------
+- fixed a security issue (SQL injection), see SA-2007-031
+
+Drupal 4.7.8, 2007-10-17
+------------------------
+- fixed a security issue (HTTP response splitting), see SA-2007-024
+- fixed a security issue (Cross site scripting via uploads), see SA-2007-026
+- fixed a security issue (API handling of unpublished comment), see SA-2007-030
+
+Drupal 4.7.7, 2007-07-26
+------------------------
+- fixed security issue (XSS), see SA-2007-018
+
+Drupal 4.7.6, 2007-01-29
+------------------------
+- fixed security issue (code execution), see SA-2007-005
+
+Drupal 4.7.5, 2007-01-05
+------------------------
+- Fixed security issue (XSS), see SA-2007-001
+- Fixed security issue (DoS), see SA-2007-002
+
+Drupal 4.7.4, 2006-10-18
+------------------------
+- Fixed security issue (XSS), see SA-2006-024
+- Fixed security issue (CSRF), see SA-2006-025
+- Fixed security issue (Form action attribute injection), see SA-2006-026
+
+Drupal 4.7.3, 2006-08-02
+------------------------
+- Fixed security issue (XSS), see SA-2006-011
+
+Drupal 4.7.2, 2006-06-01
+------------------------
+- Fixed critical upload issue, see SA-2006-007
+- Fixed taxonomy XSS issue, see SA-2006-008
+- Fixed a variety of small bugs.
+
+Drupal 4.7.1, 2006-05-24
+------------------------
+- Fixed critical SQL issue, see SA-2006-005
+- Fixed a serious upgrade related bug.
+- Fixed a variety of small bugs.
+
+Drupal 4.7.0, 2006-05-01
+------------------------
+- Added free tagging support.
+- Added a site-wide contact form.
+- Theme system:
+    * Added the PHPTemplate theme engine and removed the Xtemplate engine.
+    * Converted the bluemarine theme from XTemplate to PHPTemplate.
+    * Converted the pushbutton theme from XTemplate to PHPTemplate.
+- Usability:
+    * Reworked the 'request new password' functionality.
+    * Reworked the node and comment edit forms.
+    * Made it easy to add nodes to the navigation menu.
+    * Added site 'offline for maintenance' feature.
+    * Added support for auto-complete forms (AJAX).
+    * Added support for collapsible page sections (JS).
+    * Added support for resizable text fields (JS).
+    * Improved file upload functionality (AJAX).
+    * Reorganized some settings pages.
+    * Added friendly database error screens.
+    * Improved styling of update.php.
+- Refactored the forms API.
+    * Made it possible to alter, extend or theme forms.
+- Comment system:
+    * Added support for "mass comment operations" to ease repetitive tasks.
+    * Comment moderation has been removed.
+- Node system:
+    * Reworked the revision functionality.
+    * Removed the bookmarklet code. Third-party modules can now handle
+      This.
+- Upgrade system:
+    * Allows contributed modules to plug into the upgrade system.
+- Profiles:
+    * Added a block to display author information along with posts.
+    * Added support for private profile fields.
+- Statistics module:
+    * Added the ability to track page generation times.
+    * Made it possible to block certain IPs/hostnames.
+- Block system:
+    * Added support for theme-specific block regions.
+- Syndication:
+    * Made the aggregator module parse Atom feeds.
+    * Made the aggregator generate RSS feeds.
+    * Added RSS feed settings.
+- XML-RPC:
+    * Replaced the XML-RPC library by a better one.
+- Performance:
+    * Added 'loose caching' option for high-traffic sites.
+    * Improved performance of path aliasing.
+    * Added the ability to track page generation times.
+- Internationalization:
+    * Improved Unicode string handling API.
+    * Added support for PHP's multibyte string module.
+- Added support for PHP5's 'mysqli' extension.
+- Search module:
+    * Made indexer smarter and more robust.
+    * Added advanced search operators (e.g. phrase, node type, ...).
+    * Added customizable result ranking.
+- PostgreSQL support:
+    * Removed dependency on PL/pgSQL procedural language.
+- Menu system:
+    * Added support for external URLs.
+- Queue module:
+    * Removed from core.
+- HTTP handling:
+    * Added support for a tolerant Base URL.
+    * Output URIs relative to the root, without a base tag.
+
+Drupal 4.6.11, 2007-01-05
+-------------------------
+- Fixed security issue (XSS), see SA-2007-001
+- Fixed security issue (DoS), see SA-2007-002
+
+Drupal 4.6.10, 2006-10-18
+-------------------------
+- Fixed security issue (XSS), see SA-2006-024
+- Fixed security issue (CSRF), see SA-2006-025
+- Fixed security issue (Form action attribute injection), see SA-2006-026
+
+Drupal 4.6.9, 2006-08-02
+------------------------
+- Fixed security issue (XSS), see SA-2006-011
+
+Drupal 4.6.8, 2006-06-01
+------------------------
+- Fixed critical upload issue, see SA-2006-007
+- Fixed taxonomy XSS issue, see SA-2006-008
+
+Drupal 4.6.7, 2006-05-24
+------------------------
+- Fixed critical SQL issue, see SA-2006-005
+
+Drupal 4.6.6, 2006-03-13
+------------------------
+- Fixed bugs, including 4 security vulnerabilities.
+
+Drupal 4.6.5, 2005-12-12
+------------------------
+- Fixed bugs: no critical bugs were identified.
+
+Drupal 4.6.4, 2005-11-30
+------------------------
+- Fixed bugs, including 3 security vulnerabilities.
+
+Drupal 4.6.3, 2005-08-15
+------------------------
+- Fixed bugs, including a critical "arbitrary PHP code execution" bug.
+
+Drupal 4.6.2, 2005-06-29
+------------------------
+- Fixed bugs, including two critical "arbitrary PHP code execution" bugs.
+
+Drupal 4.6.1, 2005-06-01
+------------------------
+- Fixed bugs, including a critical input validation bug.
+
+Drupal 4.6.0, 2005-04-15
+------------------------
+- PHP5 compliance
+- Search:
+    * Added UTF-8 support to make it work with all languages.
+    * Improved search indexing algorithm.
+    * Improved search output.
+    * Impose a throttle on indexing of large sites.
+    * Added search block.
+- Syndication:
+    * Made the ping module ping pingomatic.com which, in turn, will ping all the major ping services.
+    * Made Drupal generate RSS 2.0 feeds.
+    * Made RSS feeds extensible.
+    * Added categories to RSS feeds.
+    * Added enclosures to RSS feeds.
+- Flood control mechanism:
+    * Added a mechanism to throttle certain operations.
+- Usability:
+    * Refactored the block configuration pages.
+    * Refactored the statistics pages.
+    * Refactored the watchdog pages.
+    * Refactored the throttle module configuration.
+    * Refactored the access rules page.
+    * Refactored the content administration page.
+    * Introduced forum configuration pages.
+    * Added a 'add child page' link to book pages.
+- Contact module:
+    * Added a simple contact module that allows users to contact each other using e-mail.
+- Multi-site configuration:
+    * Made it possible to run multiple sites from a single code base.
+- Added an image API: enables better image handling.
+- Block system:
+    * Extended the block visibility settings.
+- Theme system:
+    * Added new theme functions.
+- Database backend:
+    * The PEAR database backend is no longer supported.
+- Performance:
+    * Improved performance of the forum topics block.
+    * Improved performance of the tracker module.
+    * Improved performance of the node pages.
+- Documentation:
+    * Improved and extended PHPDoc/Doxygen comments.
+
+Drupal 4.5.8, 2006-03-13
+------------------------
+- Fixed bugs, including 3 security vulnerabilities.
+
+Drupal 4.5.7, 2005-12-12
+------------------------
+- Fixed bugs: no critical bugs were identified.
+
+Drupal 4.5.6, 2005-11-30
+------------------------
+- Fixed bugs, including 3 security vulnerabilities.
+
+Drupal 4.5.5, 2005-08-15
+------------------------
+- Fixed bugs, including a critical "arbitrary PHP code execution" bug.
+
+Drupal 4.5.4, 2005-06-29
+------------------------
+- Fixed bugs, including two critical "arbitrary PHP code execution" bugs.
+
+Drupal 4.5.3, 2005-06-01
+------------------------
+- Fixed bugs, including a critical input validation bug.
+
+Drupal 4.5.2, 2005-01-15
+------------------------
+- Fixed bugs: a cross-site scripting (XSS) vulnerability has been fixed.
+
+Drupal 4.5.1, 2004-12-01
+------------------------
+- Fixed bugs: no critical bugs were identified.
+
+Drupal 4.5.0, 2004-10-18
+------------------------
+- Navigation:
+    * Made it possible to add, delete, rename and move menu items.
+    * Introduced tabs and subtabs for local tasks.
+    * Reorganized the navigation menus.
+- User management:
+    * Added support for multiple roles per user.
+    * Made it possible to add custom profile fields.
+    * Made it possible to browse user profiles by field.
+- Node system:
+    * Added support for node-level permissions.
+- Comment module:
+    * Made it possible to leave contact information without having to register.
+- Upload module:
+    * Added support for uploading documents (includes images).
+- Forum module:
+    * Added support for sticky forum topics.
+    * Made it possible to track forum topics.
+- Syndication:
+    * Added support for RSS ping-notifications of http://technorati.com/.
+    * Refactored the categorization of syndicated news items.
+    * Added an URL alias for 'rss.xml'.
+    * Improved date parsing.
+- Database backend:
+    * Added support for multiple database connections.
+    * The PostgreSQL backend does no longer require PEAR.
+- Theme system:
+    * Changed all GIFs to PNGs.
+    * Reorganised the handling of themes, template engines, templates and styles.
+    * Unified and extended the available theme settings.
+    * Added theme screenshots.
+- Blocks:
+    * Added 'recent comments' block.
+    * Added 'categories' block.
+- Blogger API:
+    * Added support for auto-discovery of blogger API via RSD.
+- Performance:
+    * Added support for sending gzip compressed pages.
+    * Improved performance of the forum module.
+- Accessibility:
+    * Improved the accessibility of the archive module's calendar.
+    * Improved form handling and error reporting.
+    * Added HTTP redirects to prevent submitting twice when refreshing right after a form submission.
+- Refactored 403 (forbidden) handling and added support for custom 403 pages.
+- Documentation:
+    * Added PHPDoc/Doxygen comments.
+- Filter system:
+    * Added support for using multiple input formats on the site
+    * Expanded the embedded PHP-code feature so it can be used everywhere
+    * Added support for role-dependent filtering, through input formats
+- UI translation:
+    * Managing translations is now completely done through the administration interface
+    * Added support for importing/exporting gettext .po files
+
+Drupal 4.4.3, 2005-06-01
+------------------------
+- Fixed bugs, including a critical input validation bug.
+
+Drupal 4.4.2, 2004-07-04
+------------------------
+- Fixed bugs: no critical bugs were identified.
+
+Drupal 4.4.1, 2004-05-01
+------------------------
+- Fixed bugs: no critical bugs were identified.
+
+Drupal 4.4.0, 2004-04-01
+------------------------
+- Added support for the MetaWeblog API and MovableType extensions.
+- Added a file API: enables better document management.
+- Improved the watchdog and search module to log search keys.
+- News aggregator:
+    * Added support for conditional GET.
+    * Added OPML feed subscription list.
+    * Added support for , , , ,  and .
+- Comment module:
+    * Made it possible to disable the "comment viewing controls".
+- Performance:
+    * Improved module loading when serving cached pages.
+    * Made it possible to automatically disable modules when under heavy load.
+    * Made it possible to automatically disable blocks when under heavy load.
+    * Improved performance and memory footprint of the locale module.
+- Theme system:
+    * Made all theme functions start with 'theme_'.
+    * Made all theme functions return their output.
+    * Migrated away from using the BaseTheme class.
+    * Added many new theme functions and refactored existing theme functions.
+    * Added avatar support to 'Xtemplate'.
+    * Replaced theme 'UnConeD' by 'Chameleon'.
+    * Replaced theme 'Marvin' by 'Pushbutton'.
+- Usability:
+    * Added breadcrumb navigation to all pages.
+    * Made it possible to add context-sensitive help to all pages.
+    * Replaced drop-down menus by radio buttons where appropriate.
+    * Removed the 'magic_quotes_gpc = 0' requirement.
+    * Added a 'book navigation' block.
+- Accessibility:
+    * Made themes degrade gracefully in absence of CSS.
+    * Grouped form elements using '
' and '' tags. + * Added '
'; ?> + * @endcode + * + * @see template_preprocess() + * @see template_preprocess_search_result() + * @see template_process() + * + * @ingroup themeable + */ +?> +
  • > + +

    > + +

    + +
    + +

    >

    + + +

    + +
    +
  • diff --git a/drupal7/web/modules/search/search-results.tpl.php b/drupal7/web/modules/search/search-results.tpl.php new file mode 100644 index 0000000..aa9bf8d --- /dev/null +++ b/drupal7/web/modules/search/search-results.tpl.php @@ -0,0 +1,35 @@ + + +

    +
      + +
    + + +

    + + diff --git a/drupal7/web/modules/search/search-rtl.css b/drupal7/web/modules/search/search-rtl.css new file mode 100644 index 0000000..da9e8d9 --- /dev/null +++ b/drupal7/web/modules/search/search-rtl.css @@ -0,0 +1,13 @@ + +.search-advanced .criterion { + float: right; + margin-right: 0; + margin-left: 2em; +} +.search-advanced .action { + float: right; + clear: right; +} +.search-results .search-snippet-info { + padding-right: 1em; /* LTR */ +} \ No newline at end of file diff --git a/drupal7/web/modules/search/search.admin.inc b/drupal7/web/modules/search/search.admin.inc new file mode 100644 index 0000000..a37d37b --- /dev/null +++ b/drupal7/web/modules/search/search.admin.inc @@ -0,0 +1,196 @@ + $info) { + $names[$module] = $system_info[$module]['name']; + } + asort($names, SORT_STRING); + return $names; +} + +/** + * Menu callback: displays the search module settings page. + * + * @ingroup forms + * + * @see search_admin_settings_validate() + * @see search_admin_settings_submit() + * @see search_admin_reindex_submit() + */ +function search_admin_settings($form) { + // Collect some stats + $remaining = 0; + $total = 0; + foreach (variable_get('search_active_modules', array('node', 'user')) as $module) { + if ($status = module_invoke($module, 'search_status')) { + $remaining += $status['remaining']; + $total += $status['total']; + } + } + + $count = format_plural($remaining, 'There is 1 item left to index.', 'There are @count items left to index.'); + $percentage = ((int)min(100, 100 * ($total - $remaining) / max(1, $total))) . '%'; + $status = '

    ' . t('%percentage of the site has been indexed.', array('%percentage' => $percentage)) . ' ' . $count . '

    '; + $form['status'] = array('#type' => 'fieldset', '#title' => t('Indexing status')); + $form['status']['status'] = array('#markup' => $status); + $form['status']['wipe'] = array('#type' => 'submit', '#value' => t('Re-index site'), '#submit' => array('search_admin_reindex_submit')); + + $items = drupal_map_assoc(array(10, 20, 50, 100, 200, 500)); + + // Indexing throttle: + $form['indexing_throttle'] = array( + '#type' => 'fieldset', + '#title' => t('Indexing throttle') + ); + $form['indexing_throttle']['search_cron_limit'] = array( + '#type' => 'select', + '#title' => t('Number of items to index per cron run'), + '#default_value' => variable_get('search_cron_limit', 100), + '#options' => $items, + '#description' => t('The maximum number of items indexed in each pass of a cron maintenance task. If necessary, reduce the number of items to prevent timeouts and memory errors while indexing.', array('@cron' => url('admin/reports/status'))) + ); + // Indexing settings: + $form['indexing_settings'] = array( + '#type' => 'fieldset', + '#title' => t('Indexing settings') + ); + $form['indexing_settings']['info'] = array( + '#markup' => t('

    Changing the settings below will cause the site index to be rebuilt. The search index is not cleared but systematically updated to reflect the new settings. Searching will continue to work but new content won\'t be indexed until all existing content has been re-indexed.

    The default settings should be appropriate for the majority of sites.

    ') + ); + $form['indexing_settings']['minimum_word_size'] = array( + '#type' => 'textfield', + '#title' => t('Minimum word length to index'), + '#default_value' => variable_get('minimum_word_size', 3), + '#size' => 5, + '#maxlength' => 3, + '#description' => t('The number of characters a word has to be to be indexed. A lower setting means better search result ranking, but also a larger database. Each search query must contain at least one keyword that is this size (or longer).'), + '#element_validate' => array('element_validate_integer_positive'), + ); + $form['indexing_settings']['overlap_cjk'] = array( + '#type' => 'checkbox', + '#title' => t('Simple CJK handling'), + '#default_value' => variable_get('overlap_cjk', TRUE), + '#description' => t('Whether to apply a simple Chinese/Japanese/Korean tokenizer based on overlapping sequences. Turn this off if you want to use an external preprocessor for this instead. Does not affect other languages.') + ); + + $form['active'] = array( + '#type' => 'fieldset', + '#title' => t('Active search modules') + ); + $module_options = _search_get_module_names(); + $form['active']['search_active_modules'] = array( + '#type' => 'checkboxes', + '#title' => t('Active modules'), + '#title_display' => 'invisible', + '#default_value' => variable_get('search_active_modules', array('node', 'user')), + '#options' => $module_options, + '#description' => t('Choose which search modules are active from the available modules.') + ); + $form['active']['search_default_module'] = array( + '#title' => t('Default search module'), + '#type' => 'radios', + '#default_value' => variable_get('search_default_module', 'node'), + '#options' => $module_options, + '#description' => t('Choose which search module is the default.') + ); + $form['logging'] = array( + '#type' => 'fieldset', + '#title' => t('Logging') + ); + $form['logging']['search_logging'] = array( + '#type' => 'checkbox', + '#title' => t('Log searches'), + '#default_value' => variable_get('search_logging', 1), + '#description' => t('If checked, all searches will be logged. Uncheck to skip logging. Logging may affect performance.'), + ); + $form['#validate'][] = 'search_admin_settings_validate'; + $form['#submit'][] = 'search_admin_settings_submit'; + + // Per module settings + foreach (variable_get('search_active_modules', array('node', 'user')) as $module) { + $added_form = module_invoke($module, 'search_admin'); + if (is_array($added_form)) { + $form = array_merge($form, $added_form); + } + } + + return system_settings_form($form); +} + +/** + * Form validation handler for search_admin_settings(). + */ +function search_admin_settings_validate($form, &$form_state) { + // Check whether we selected a valid default. + if ($form_state['triggering_element']['#value'] != t('Reset to defaults')) { + $new_modules = array_filter($form_state['values']['search_active_modules']); + $default = $form_state['values']['search_default_module']; + if (!in_array($default, $new_modules, TRUE)) { + form_set_error('search_default_module', t('Your default search module is not selected as an active module.')); + } + } +} + +/** + * Form submission handler for search_admin_settings(). + */ +function search_admin_settings_submit($form, &$form_state) { + // If these settings change, the index needs to be rebuilt. + if ((variable_get('minimum_word_size', 3) != $form_state['values']['minimum_word_size']) || + (variable_get('overlap_cjk', TRUE) != $form_state['values']['overlap_cjk'])) { + drupal_set_message(t('The index will be rebuilt.')); + search_reindex(); + } + $current_modules = variable_get('search_active_modules', array('node', 'user')); + // Check whether we are resetting the values. + if ($form_state['triggering_element']['#value'] == t('Reset to defaults')) { + $new_modules = array('node', 'user'); + } + else { + $new_modules = array_filter($form_state['values']['search_active_modules']); + } + if (array_diff($current_modules, $new_modules)) { + drupal_set_message(t('The active search modules have been changed.')); + variable_set('menu_rebuild_needed', TRUE); + } +} + +/** + * Form submission handler for reindex button on search_admin_settings_form(). + */ +function search_admin_reindex_submit($form, &$form_state) { + // send the user to the confirmation page + $form_state['redirect'] = 'admin/config/search/settings/reindex'; +} diff --git a/drupal7/web/modules/search/search.api.php b/drupal7/web/modules/search/search.api.php new file mode 100644 index 0000000..8c17bb4 --- /dev/null +++ b/drupal7/web/modules/search/search.api.php @@ -0,0 +1,380 @@ + 'Content', + 'path' => 'node', + 'conditions_callback' => 'callback_search_conditions', + ); +} + +/** + * Define access to a custom search routine. + * + * This hook allows a module to define permissions for a search tab. + * + * @ingroup search + */ +function hook_search_access() { + return user_access('access content'); +} + +/** + * Take action when the search index is going to be rebuilt. + * + * Modules that use hook_update_index() should update their indexing + * bookkeeping so that it starts from scratch the next time + * hook_update_index() is called. + * + * @ingroup search + */ +function hook_search_reset() { + db_update('search_dataset') + ->fields(array('reindex' => REQUEST_TIME)) + ->condition('type', 'node') + ->execute(); +} + +/** + * Report the status of indexing. + * + * The core search module only invokes this hook on active modules. + * Implementing modules do not need to check whether they are active when + * calculating their return values. + * + * @return + * An associative array with the key-value pairs: + * - 'remaining': The number of items left to index. + * - 'total': The total number of items to index. + * + * @ingroup search + */ +function hook_search_status() { + $total = db_query('SELECT COUNT(*) FROM {node} WHERE status = 1')->fetchField(); + $remaining = db_query("SELECT COUNT(*) FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE n.status = 1 AND d.sid IS NULL OR d.reindex <> 0")->fetchField(); + return array('remaining' => $remaining, 'total' => $total); +} + +/** + * Add elements to the search settings form. + * + * @return + * Form array for the Search settings page at admin/config/search/settings. + * + * @ingroup search + */ +function hook_search_admin() { + // Output form for defining rank factor weights. + $form['content_ranking'] = array( + '#type' => 'fieldset', + '#title' => t('Content ranking'), + ); + $form['content_ranking']['#theme'] = 'node_search_admin'; + $form['content_ranking']['info'] = array( + '#value' => '' . t('The following numbers control which properties the content search should favor when ordering the results. Higher numbers mean more influence, zero means the property is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . '' + ); + + // Note: reversed to reflect that higher number = higher ranking. + $options = drupal_map_assoc(range(0, 10)); + foreach (module_invoke_all('ranking') as $var => $values) { + $form['content_ranking']['factors']['node_rank_' . $var] = array( + '#title' => $values['title'], + '#type' => 'select', + '#options' => $options, + '#default_value' => variable_get('node_rank_' . $var, 0), + ); + } + return $form; +} + +/** + * Execute a search for a set of key words. + * + * Use database API with the 'PagerDefault' query extension to perform your + * search. + * + * If your module uses hook_update_index() and search_index() to index its + * items, use table 'search_index' aliased to 'i' as the main table in your + * query, with the 'SearchQuery' extension. You can join to your module's table + * using the 'i.sid' field, which will contain the $sid values you provided to + * search_index(). Add the main keywords to the query by using method + * searchExpression(). The functions search_expression_extract() and + * search_expression_insert() may also be helpful for adding custom search + * parameters to the search expression. + * + * See node_search_execute() for an example of a module that uses the search + * index, and user_search_execute() for an example that doesn't use the search + * index. + * + * @param $keys + * The search keywords as entered by the user. + * @param $conditions + * An optional array of additional conditions, such as filters. + * + * @return + * An array of search results. To use the default search result + * display, each item should have the following keys': + * - 'link': Required. The URL of the found item. + * - 'type': The type of item (such as the content type). + * - 'title': Required. The name of the item. + * - 'user': The author of the item. + * - 'date': A timestamp when the item was last modified. + * - 'extra': An array of optional extra information items. + * - 'snippet': An excerpt or preview to show with the result (can be + * generated with search_excerpt()). + * - 'language': Language code for the item (usually two characters). + * + * @ingroup search + */ +function hook_search_execute($keys = NULL, $conditions = NULL) { + // Build matching conditions + $query = db_select('search_index', 'i', array('target' => 'slave'))->extend('SearchQuery')->extend('PagerDefault'); + $query->join('node', 'n', 'n.nid = i.sid'); + $query + ->condition('n.status', 1) + ->addTag('node_access') + ->searchExpression($keys, 'node'); + + // Insert special keywords. + $query->setOption('type', 'n.type'); + $query->setOption('language', 'n.language'); + if ($query->setOption('term', 'ti.tid')) { + $query->join('taxonomy_index', 'ti', 'n.nid = ti.nid'); + } + // Only continue if the first pass query matches. + if (!$query->executeFirstPass()) { + return array(); + } + + // Add the ranking expressions. + _node_rankings($query); + + // Load results. + $find = $query + ->limit(10) + ->execute(); + $results = array(); + foreach ($find as $item) { + // Build the node body. + $node = node_load($item->sid); + node_build_content($node, 'search_result'); + $node->body = drupal_render($node->content); + + // Fetch comments for snippet. + $node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node); + // Fetch terms for snippet. + $node->rendered .= ' ' . module_invoke('taxonomy', 'node_update_index', $node); + + $extra = module_invoke_all('node_search_result', $node); + + $results[] = array( + 'link' => url('node/' . $item->sid, array('absolute' => TRUE)), + 'type' => check_plain(node_type_get_name($node)), + 'title' => $node->title, + 'user' => theme('username', array('account' => $node)), + 'date' => $node->changed, + 'node' => $node, + 'extra' => $extra, + 'score' => $item->calculated_score, + 'snippet' => search_excerpt($keys, $node->body), + ); + } + return $results; +} + +/** + * Override the rendering of search results. + * + * A module that implements hook_search_info() to define a type of search may + * implement this hook in order to override the default theming of its search + * results, which is otherwise themed using theme('search_results'). + * + * Note that by default, theme('search_results') and theme('search_result') + * work together to create an ordered list (OL). So your hook_search_page() + * implementation should probably do this as well. + * + * @param $results + * An array of search results. + * + * @return + * A renderable array, which will render the formatted search results with a + * pager included. + * + * @see search-result.tpl.php + * @see search-results.tpl.php + */ +function hook_search_page($results) { + $output['prefix']['#markup'] = '
      '; + + foreach ($results as $entry) { + $output[] = array( + '#theme' => 'search_result', + '#result' => $entry, + '#module' => 'my_module_name', + ); + } + $output['suffix']['#markup'] = '
    ' . theme('pager'); + + return $output; +} + +/** + * Preprocess text for search. + * + * This hook is called to preprocess both the text added to the search index and + * the keywords users have submitted for searching. + * + * Possible uses: + * - Adding spaces between words of Chinese or Japanese text. + * - Stemming words down to their root words to allow matches between, for + * instance, walk, walked, walking, and walks in searching. + * - Expanding abbreviations and acronymns that occur in text. + * + * @param $text + * The text to preprocess. This is a single piece of plain text extracted + * from between two HTML tags or from the search query. It will not contain + * any HTML entities or HTML tags. + * + * @return + * The text after preprocessing. Note that if your module decides not to alter + * the text, it should return the original text. Also, after preprocessing, + * words in the text should be separated by a space. + * + * @ingroup search + */ +function hook_search_preprocess($text) { + // Do processing on $text + return $text; +} + +/** + * Update the search index for this module. + * + * This hook is called every cron run if search.module is enabled, your + * module has implemented hook_search_info(), and your module has been set as + * an active search module on the Search settings page + * (admin/config/search/settings). It allows your module to add items to the + * built-in search index using search_index(), or to add them to your module's + * own indexing mechanism. + * + * When implementing this hook, your module should index content items that + * were modified or added since the last run. PHP has a time limit + * for cron, though, so it is advisable to limit how many items you index + * per run using variable_get('search_cron_limit') (see example below). Also, + * since the cron run could time out and abort in the middle of your run, you + * should update your module's internal bookkeeping on when items have last + * been indexed as you go rather than waiting to the end of indexing. + * + * @ingroup search + */ +function hook_update_index() { + $limit = (int)variable_get('search_cron_limit', 100); + + $result = db_query_range("SELECT n.nid FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE d.sid IS NULL OR d.reindex <> 0 ORDER BY d.reindex ASC, n.nid ASC", 0, $limit); + + foreach ($result as $node) { + $node = node_load($node->nid); + + // Save the changed time of the most recent indexed node, for the search + // results half-life calculation. + variable_set('node_cron_last', $node->changed); + + // Render the node. + node_build_content($node, 'search_index'); + $node->rendered = drupal_render($node->content); + + $text = '

    ' . check_plain($node->title) . '

    ' . $node->rendered; + + // Fetch extra data normally not visible + $extra = module_invoke_all('node_update_index', $node); + foreach ($extra as $t) { + $text .= $t; + } + + // Update index + search_index($node->nid, 'node', $text); + } +} +/** + * @} End of "addtogroup hooks". + */ + +/** + * Provide search query conditions. + * + * Callback for hook_search_info(). + * + * This callback is invoked by search_view() to get an array of additional + * search conditions to pass to search_data(). For example, a search module + * may get additional keywords, filters, or modifiers for the search from + * the query string. + * + * This example pulls additional search keywords out of the $_REQUEST variable, + * (i.e. from the query string of the request). The conditions may also be + * generated internally - for example based on a module's settings. + * + * @param $keys + * The search keywords string. + * + * @return + * An array of additional conditions, such as filters. + * + * @ingroup callbacks + * @ingroup search + */ +function callback_search_conditions($keys) { + $conditions = array(); + + if (!empty($_REQUEST['keys'])) { + $conditions['keys'] = $_REQUEST['keys']; + } + if (!empty($_REQUEST['sample_search_keys'])) { + $conditions['sample_search_keys'] = $_REQUEST['sample_search_keys']; + } + if ($force_keys = config('sample_search.settings')->get('force_keywords')) { + $conditions['sample_search_force_keywords'] = $force_keys; + } + return $conditions; +} diff --git a/drupal7/web/modules/search/search.css b/drupal7/web/modules/search/search.css new file mode 100644 index 0000000..ff7230f --- /dev/null +++ b/drupal7/web/modules/search/search.css @@ -0,0 +1,34 @@ + +.search-form { + margin-bottom: 1em; +} +.search-form input { + margin-top: 0; + margin-bottom: 0; +} +.search-results { + list-style: none; +} +.search-results p { + margin-top: 0; +} +.search-results .title { + font-size: 1.2em; +} +.search-results li { + margin-bottom: 1em; +} +.search-results .search-snippet-info { + padding-left: 1em; /* LTR */ +} +.search-results .search-info { + font-size: 0.85em; +} +.search-advanced .criterion { + float: left; /* LTR */ + margin-right: 2em; /* LTR */ +} +.search-advanced .action { + float: left; /* LTR */ + clear: left; /* LTR */ +} diff --git a/drupal7/web/modules/search/search.extender.inc b/drupal7/web/modules/search/search.extender.inc new file mode 100644 index 0000000..a9abf0d --- /dev/null +++ b/drupal7/web/modules/search/search.extender.inc @@ -0,0 +1,546 @@ + array(), 'negative' => array()); + + /** + * Indicates whether the first pass query requires complex conditions (LIKE). + * + * @var boolean. + */ + protected $simple = TRUE; + + /** + * Conditions that are used for exact searches. + * + * This is always used for the second pass query but not for the first pass, + * unless $this->simple is FALSE. + * + * @var DatabaseCondition + */ + protected $conditions; + + /** + * Indicates how many matches for a search query are necessary. + * + * @var int + */ + protected $matches = 0; + + /** + * Array of search words. + * + * These words have to match against {search_index}.word. + * + * @var array + */ + protected $words = array(); + + /** + * Multiplier for the normalized search score. + * + * This value is calculated by the first pass query and multiplied with the + * actual score of a specific word to make sure that the resulting calculated + * score is between 0 and 1. + * + * @var float + */ + protected $normalize; + + /** + * Indicates whether the first pass query has been executed. + * + * @var boolean + */ + protected $executedFirstPass = FALSE; + + /** + * Stores score expressions. + * + * @var array + * + * @see addScore() + */ + protected $scores = array(); + + /** + * Stores arguments for score expressions. + * + * @var array + */ + protected $scoresArguments = array(); + + /** + * Stores multipliers for score expressions. + * + * @var array + */ + protected $multiply = array(); + + /** + * Whether or not search expressions were ignored. + * + * The maximum number of AND/OR combinations exceeded can be configured to + * avoid Denial-of-Service attacks. Expressions beyond the limit are ignored. + * + * @var boolean + */ + protected $expressionsIgnored = FALSE; + + /** + * Sets up the search query expression. + * + * @param $query + * A search query string, which can contain options. + * @param $module + * The search module. This maps to {search_index}.type in the database. + * + * @return + * The SearchQuery object. + */ + public function searchExpression($expression, $module) { + $this->searchExpression = $expression; + $this->type = $module; + + // Add a search_* tag. This needs to be added before any preExecute methods + // for decorated queries are called, as $this->prepared will be set to TRUE + // and tags added in the execute method will never get used. For example, + // if $query is extended by 'SearchQuery' then 'PagerDefault', the + // search-specific tag will be added too late (when preExecute() has + // already been called from the PagerDefault extender), and as a + // consequence will not be available to hook_query_alter() implementations, + // nor will the correct hook_query_TAG_alter() implementations get invoked. + // See node_search_execute(). + $this->addTag('search_' . $module); + + return $this; + } + + /** + * Applies a search option and removes it from the search query string. + * + * These options are in the form option:value,value2,value3. + * + * @param $option + * Name of the option. + * @param $column + * Name of the database column to which the value should be applied. + * + * @return + * TRUE if a value for that option was found, FALSE if not. + */ + public function setOption($option, $column) { + if ($values = search_expression_extract($this->searchExpression, $option)) { + $or = db_or(); + foreach (explode(',', $values) as $value) { + $or->condition($column, $value); + } + $this->condition($or); + $this->searchExpression = search_expression_insert($this->searchExpression, $option); + return TRUE; + } + return FALSE; + } + + /** + * Parses the search query into SQL conditions. + * + * We build two queries that match the dataset bodies. + */ + protected function parseSearchExpression() { + // Matchs words optionally prefixed by a dash. A word in this case is + // something between two spaces, optionally quoted. + preg_match_all('/ (-?)("[^"]+"|[^" ]+)/i', ' ' . $this->searchExpression , $keywords, PREG_SET_ORDER); + + if (count($keywords) == 0) { + return; + } + + // Classify tokens. + $or = FALSE; + $warning = ''; + $limit_combinations = variable_get('search_and_or_limit', 7); + // The first search expression does not count as AND. + $and_count = -1; + $or_count = 0; + foreach ($keywords as $match) { + if ($or_count && $and_count + $or_count >= $limit_combinations) { + // Ignore all further search expressions to prevent Denial-of-Service + // attacks using a high number of AND/OR combinations. + $this->expressionsIgnored = TRUE; + break; + } + $phrase = FALSE; + // Strip off phrase quotes. + if ($match[2][0] == '"') { + $match[2] = substr($match[2], 1, -1); + $phrase = TRUE; + $this->simple = FALSE; + } + // Simplify keyword according to indexing rules and external + // preprocessors. Use same process as during search indexing, so it + // will match search index. + $words = search_simplify($match[2]); + // Re-explode in case simplification added more words, except when + // matching a phrase. + $words = $phrase ? array($words) : preg_split('/ /', $words, -1, PREG_SPLIT_NO_EMPTY); + // Negative matches. + if ($match[1] == '-') { + $this->keys['negative'] = array_merge($this->keys['negative'], $words); + } + // OR operator: instead of a single keyword, we store an array of all + // OR'd keywords. + elseif ($match[2] == 'OR' && count($this->keys['positive'])) { + $last = array_pop($this->keys['positive']); + // Starting a new OR? + if (!is_array($last)) { + $last = array($last); + } + $this->keys['positive'][] = $last; + $or = TRUE; + $or_count++; + continue; + } + // AND operator: implied, so just ignore it. + elseif ($match[2] == 'AND' || $match[2] == 'and') { + $warning = $match[2]; + continue; + } + + // Plain keyword. + else { + if ($match[2] == 'or') { + $warning = $match[2]; + } + if ($or) { + // Add to last element (which is an array). + $this->keys['positive'][count($this->keys['positive']) - 1] = array_merge($this->keys['positive'][count($this->keys['positive']) - 1], $words); + } + else { + $this->keys['positive'] = array_merge($this->keys['positive'], $words); + $and_count++; + } + } + $or = FALSE; + } + + // Convert keywords into SQL statements. + $this->conditions = db_and(); + $simple_and = FALSE; + $simple_or = FALSE; + // Positive matches. + foreach ($this->keys['positive'] as $key) { + // Group of ORed terms. + if (is_array($key) && count($key)) { + $simple_or = TRUE; + $any = FALSE; + $queryor = db_or(); + foreach ($key as $or) { + list($num_new_scores) = $this->parseWord($or); + $any |= $num_new_scores; + $queryor->condition('d.data', "% $or %", 'LIKE'); + } + if (count($queryor)) { + $this->conditions->condition($queryor); + // A group of OR keywords only needs to match once. + $this->matches += ($any > 0); + } + } + // Single ANDed term. + else { + $simple_and = TRUE; + list($num_new_scores, $num_valid_words) = $this->parseWord($key); + $this->conditions->condition('d.data', "% $key %", 'LIKE'); + if (!$num_valid_words) { + $this->simple = FALSE; + } + // Each AND keyword needs to match at least once. + $this->matches += $num_new_scores; + } + } + if ($simple_and && $simple_or) { + $this->simple = FALSE; + } + // Negative matches. + foreach ($this->keys['negative'] as $key) { + $this->conditions->condition('d.data', "% $key %", 'NOT LIKE'); + $this->simple = FALSE; + } + + if ($warning == 'or') { + drupal_set_message(t('Search for either of the two terms with uppercase OR. For example, cats OR dogs.')); + } + } + + /** + * Helper function for parseQuery(). + */ + protected function parseWord($word) { + $num_new_scores = 0; + $num_valid_words = 0; + // Determine the scorewords of this word/phrase. + $split = explode(' ', $word); + foreach ($split as $s) { + $num = is_numeric($s); + if ($num || drupal_strlen($s) >= variable_get('minimum_word_size', 3)) { + if (!isset($this->words[$s])) { + $this->words[$s] = $s; + $num_new_scores++; + } + $num_valid_words++; + } + } + // Return matching snippet and number of added words. + return array($num_new_scores, $num_valid_words); + } + + /** + * Executes the first pass query. + * + * This can either be done explicitly, so that additional scores and + * conditions can be applied to the second pass query, or implicitly by + * addScore() or execute(). + * + * @return + * TRUE if search items exist, FALSE if not. + */ + public function executeFirstPass() { + $this->parseSearchExpression(); + + if (count($this->words) == 0) { + form_set_error('keys', format_plural(variable_get('minimum_word_size', 3), 'You must include at least one positive keyword with 1 character or more.', 'You must include at least one positive keyword with @count characters or more.')); + return FALSE; + } + if ($this->expressionsIgnored) { + drupal_set_message(t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', array('@count' => variable_get('search_and_or_limit', 7))), 'warning'); + } + $this->executedFirstPass = TRUE; + + if (!empty($this->words)) { + $or = db_or(); + foreach ($this->words as $word) { + $or->condition('i.word', $word); + } + $this->condition($or); + } + // Build query for keyword normalization. + $this->join('search_total', 't', 'i.word = t.word'); + $this + ->condition('i.type', $this->type) + ->groupBy('i.type') + ->groupBy('i.sid') + ->having('COUNT(*) >= :matches', array(':matches' => $this->matches)); + + // Clone the query object to do the firstPass query; + $first = clone $this->query; + + // For complex search queries, add the LIKE conditions to the first pass query. + if (!$this->simple) { + $first->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type'); + $first->condition($this->conditions); + } + + // Calculate maximum keyword relevance, to normalize it. + $first->addExpression('SUM(i.score * t.count)', 'calculated_score'); + $this->normalize = $first + ->range(0, 1) + ->orderBy('calculated_score', 'DESC') + ->execute() + ->fetchField(); + + if ($this->normalize) { + return TRUE; + } + return FALSE; + } + + /** + * Adds a custom score expression to the search query. + * + * Score expressions are used to order search results. If no calls to + * addScore() have taken place, a default keyword relevance score will be + * used. However, if at least one call to addScore() has taken place, the + * keyword relevance score is not automatically added. + * + * Note that you must use this method to add ordering to your searches, and + * not call orderBy() directly, when using the SearchQuery extender. This is + * because of the two-pass system the SearchQuery class uses to normalize + * scores. + * + * @param $score + * The score expression, which should evaluate to a number between 0 and 1. + * The string 'i.relevance' in a score expression will be replaced by a + * measure of keyword relevance between 0 and 1. + * @param $arguments + * Query arguments needed to provide values to the score expression. + * @param $multiply + * If set, the score is multiplied with this value. However, all scores + * with multipliers are then divided by the total of all multipliers, so + * that overall, the normalization is maintained. + * + * @return object + * The updated query object. + */ + public function addScore($score, $arguments = array(), $multiply = FALSE) { + if ($multiply) { + $i = count($this->multiply); + // Modify the score expression so it is multiplied by the multiplier, + // with a divisor to renormalize. + $score = "CAST(:multiply_$i AS DECIMAL) * COALESCE(( " . $score . "), 0) / CAST(:total_$i AS DECIMAL)"; + // Add an argument for the multiplier. The :total_$i argument is taken + // care of in the execute() method, which is when the total divisor is + // calculated. + $arguments[':multiply_' . $i] = $multiply; + $this->multiply[] = $multiply; + } + + $this->scores[] = $score; + $this->scoresArguments += $arguments; + + return $this; + } + + /** + * Executes the search. + * + * If not already done, this executes the first pass query. Then the complex + * conditions are applied to the query including score expressions and + * ordering. + * + * @return + * FALSE if the first pass query returned no results, and a database result + * set if there were results. + */ + public function execute() + { + if (!$this->executedFirstPass) { + $this->executeFirstPass(); + } + if (!$this->normalize) { + return new DatabaseStatementEmpty(); + } + + // Add conditions to query. + $this->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type'); + $this->condition($this->conditions); + + if (empty($this->scores)) { + // Add default score. + $this->addScore('i.relevance'); + } + + if (count($this->multiply)) { + // Re-normalize scores with multipliers by dividing by the total of all + // multipliers. The expressions were altered in addScore(), so here just + // add the arguments for the total. + $i = 0; + $sum = array_sum($this->multiply); + foreach ($this->multiply as $total) { + $this->scoresArguments[':total_' . $i] = $sum; + $i++; + } + } + + // Replace the pseudo-expression 'i.relevance' with a measure of keyword + // relevance in all score expressions, using string replacement. Careful + // though! If you just print out a float, some locales use ',' as the + // decimal separator in PHP, while SQL always uses '.'. So, make sure to + // set the number format correctly. + $relevance = number_format((1.0 / $this->normalize), 10, '.', ''); + $this->scores = str_replace('i.relevance', '(' . $relevance . ' * i.score * t.count)', $this->scores); + + // Add all scores together to form a query field. + $this->addExpression('SUM(' . implode(' + ', $this->scores) . ')', 'calculated_score', $this->scoresArguments); + + // If an order has not yet been set for this query, add a default order + // that sorts by the calculated sum of scores. + if (count($this->getOrderBy()) == 0) { + $this->orderBy('calculated_score', 'DESC'); + } + + // Add useful metadata. + $this + ->addMetaData('normalize', $this->normalize) + ->fields('i', array('type', 'sid')); + + return $this->query->execute(); + } + + /** + * Builds the default count query for SearchQuery. + * + * Since SearchQuery always uses GROUP BY, we can default to a subquery. We + * also add the same conditions as execute() because countQuery() is called + * first. + */ + public function countQuery() { + // Clone the inner query. + $inner = clone $this->query; + + // Add conditions to query. + $inner->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type'); + $inner->condition($this->conditions); + + // Remove existing fields and expressions, they are not needed for a count + // query. + $fields =& $inner->getFields(); + $fields = array(); + $expressions =& $inner->getExpressions(); + $expressions = array(); + + // Add the sid as the only field and count them as a subquery. + $count = db_select($inner->fields('i', array('sid')), NULL, array('target' => 'slave')); + + // Add the COUNT() expression. + $count->addExpression('COUNT(*)'); + + return $count; + } +} diff --git a/drupal7/web/modules/search/search.info b/drupal7/web/modules/search/search.info new file mode 100644 index 0000000..62a6d87 --- /dev/null +++ b/drupal7/web/modules/search/search.info @@ -0,0 +1,14 @@ +name = Search +description = Enables site-wide keyword searching. +package = Core +version = VERSION +core = 7.x +files[] = search.extender.inc +files[] = search.test +configure = admin/config/search/settings +stylesheets[all][] = search.css + +; Information added by Drupal.org packaging script on 2024-03-06 +version = "7.100" +project = "drupal" +datestamp = "1709734591" diff --git a/drupal7/web/modules/search/search.install b/drupal7/web/modules/search/search.install new file mode 100644 index 0000000..c91283c --- /dev/null +++ b/drupal7/web/modules/search/search.install @@ -0,0 +1,183 @@ + 'Stores items that will be searched.', + 'fields' => array( + 'sid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'Search item ID, e.g. node ID for nodes.', + ), + 'type' => array( + 'type' => 'varchar', + 'length' => 16, + 'not null' => TRUE, + 'description' => 'Type of item, e.g. node.', + ), + 'data' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + 'description' => 'List of space-separated words from the item.', + ), + 'reindex' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'Set to force node reindexing.', + ), + ), + 'primary key' => array('sid', 'type'), + ); + + $schema['search_index'] = array( + 'description' => 'Stores the search index, associating words, items and scores.', + 'fields' => array( + 'word' => array( + 'type' => 'varchar', + 'length' => 50, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The {search_total}.word that is associated with the search item.', + ), + 'sid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The {search_dataset}.sid of the searchable item to which the word belongs.', + ), + 'type' => array( + 'type' => 'varchar', + 'length' => 16, + 'not null' => TRUE, + 'description' => 'The {search_dataset}.type of the searchable item to which the word belongs.', + ), + 'score' => array( + 'type' => 'float', + 'not null' => FALSE, + 'description' => 'The numeric score of the word, higher being more important.', + ), + ), + 'indexes' => array( + 'sid_type' => array('sid', 'type'), + ), + 'foreign keys' => array( + 'search_dataset' => array( + 'table' => 'search_dataset', + 'columns' => array( + 'sid' => 'sid', + 'type' => 'type', + ), + ), + ), + 'primary key' => array('word', 'sid', 'type'), + ); + + $schema['search_total'] = array( + 'description' => 'Stores search totals for words.', + 'fields' => array( + 'word' => array( + 'description' => 'Primary Key: Unique word in the search index.', + 'type' => 'varchar', + 'length' => 50, + 'not null' => TRUE, + 'default' => '', + ), + 'count' => array( + 'description' => "The count of the word in the index using Zipf's law to equalize the probability distribution.", + 'type' => 'float', + 'not null' => FALSE, + ), + ), + 'primary key' => array('word'), + ); + + $schema['search_node_links'] = array( + 'description' => 'Stores items (like nodes) that link to other nodes, used to improve search scores for nodes that are frequently linked to.', + 'fields' => array( + 'sid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The {search_dataset}.sid of the searchable item containing the link to the node.', + ), + 'type' => array( + 'type' => 'varchar', + 'length' => 16, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The {search_dataset}.type of the searchable item containing the link to the node.', + ), + 'nid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The {node}.nid that this item links to.', + ), + 'caption' => array( + 'type' => 'text', + 'size' => 'big', + 'not null' => FALSE, + 'description' => 'The text used to link to the {node}.nid.', + ), + ), + 'primary key' => array('sid', 'type', 'nid'), + 'indexes' => array( + 'nid' => array('nid'), + ), + ); + + return $schema; +} + +/** + * Replace unique keys in 'search_dataset' and 'search_index' by primary keys. + */ +function search_update_7000() { + db_drop_unique_key('search_dataset', 'sid_type'); + $dataset_type_spec = array( + 'type' => 'varchar', + 'length' => 16, + 'not null' => TRUE, + 'description' => 'Type of item, e.g. node.', + ); + db_change_field('search_dataset', 'type', 'type', $dataset_type_spec); + db_add_primary_key('search_dataset', array('sid', 'type')); + + db_drop_index('search_index', 'word'); + db_drop_unique_key('search_index', 'word_sid_type'); + $index_type_spec = array( + 'type' => 'varchar', + 'length' => 16, + 'not null' => TRUE, + 'description' => 'The {search_dataset}.type of the searchable item to which the word belongs.', + ); + db_change_field('search_index', 'type', 'type', $index_type_spec); + db_add_primary_key('search_index', array('word', 'sid', 'type')); +} + diff --git a/drupal7/web/modules/search/search.module b/drupal7/web/modules/search/search.module new file mode 100644 index 0000000..7d4db0b --- /dev/null +++ b/drupal7/web/modules/search/search.module @@ -0,0 +1,1356 @@ +' . t('About') . ''; + $output .= '

    ' . t('The Search module provides the ability to index and search for content by exact keywords, and for users by username or e-mail. For more information, see the online handbook entry for Search module.', array('@search-module' => 'http://drupal.org/documentation/modules/search/', '@search' => url('search'))) . '

    '; + $output .= '

    ' . t('Uses') . '

    '; + $output .= '
    '; + $output .= '
    ' . t('Searching content and users') . '
    '; + $output .= '
    ' . t('Users with Use search permission can use the search block and Search page. Users with the View published content permission can search for content containing exact keywords. Users with the View user profiles permission can search for users containing the keyword anywhere in the user name, and users with the Administer users permission can search for users by email address. Additionally, users with Use advanced search permission can find content using more complex search methods and filtering by choosing the Advanced search option on the Search page.', array('@search' => url('search'))) . '
    '; + $output .= '
    ' . t('Indexing content with cron') . '
    '; + $output .= '
    ' . t('To provide keyword searching, the search engine maintains an index of words found in the content and its fields, along with text added to your content by other modules (such as comments from the core Comment module, and taxonomy terms from the core Taxonomy module). To build and maintain this index, a correctly configured cron maintenance task is required. Users with Administer search permission can further configure the cron settings on the Search settings page.', array('@cron' => 'http://drupal.org/cron', '@searchsettings' => url('admin/config/search/settings'))) . '
    '; + $output .= '
    ' . t('Content reindexing') . '
    '; + $output .= '
    ' . t('Content-related actions on your site (creating, editing, or deleting content and comments) automatically cause affected content items to be marked for indexing or reindexing at the next cron run. When content is marked for reindexing, the previous content remains in the index until cron runs, at which time it is replaced by the new content. Unlike content-related actions, actions related to the structure of your site do not cause affected content to be marked for reindexing. Examples of structure-related actions that affect content include deleting or editing taxonomy terms, enabling or disabling modules that add text to content (such as Taxonomy, Comment, and field-providing modules), and modifying the fields or display parameters of your content types. If you take one of these actions and you want to ensure that the search index is updated to reflect your changed site structure, you can mark all content for reindexing by clicking the "Re-index site" button on the Search settings page. If you have a lot of content on your site, it may take several cron runs for the content to be reindexed.', array('@searchsettings' => url('admin/config/search/settings'))) . '
    '; + $output .= '
    ' . t('Configuring search settings') . '
    '; + $output .= '
    ' . t('Indexing behavior can be adjusted using the Search settings page. Users with Administer search permission can control settings such as the Number of items to index per cron run, Indexing settings (word length), Active search modules, and Content ranking, which lets you adjust the priority in which indexed content is returned in results.', array('@searchsettings' => url('admin/config/search/settings'))) . '
    '; + $output .= '
    ' . t('Search block') . '
    '; + $output .= '
    ' . t('The Search module includes a default Search form block, which can be enabled and configured on the Blocks administration page. The block is available to users with the Search content permission.', array('@blocks' => url('admin/structure/block'))) . '
    '; + $output .= '
    ' . t('Extending Search module') . '
    '; + $output .= '
    ' . t('By default, the Search module only supports exact keyword matching in content searches. You can modify this behavior by installing a language-specific stemming module for your language (such as Porter Stemmer for American English), which allows words such as walk, walking, and walked to be matched in the Search module. Another approach is to use a third-party search technology with stemming or partial word matching features built in, such as Apache Solr or Sphinx. These and other search-related contributed modules can be downloaded by visiting Drupal.org.', array('@contrib-search' => 'http://drupal.org/project/modules?filters=tid%3A105')) . '
    '; + $output .= '
    '; + return $output; + case 'admin/config/search/settings': + return '

    ' . t('The search engine maintains an index of words found in your site\'s content. To build and maintain this index, a correctly configured cron maintenance task is required. Indexing behavior can be adjusted using the settings below.', array('@cron' => url('admin/reports/status'))) . '

    '; + case 'search#noresults': + return t('
      +
    • Check if your spelling is correct.
    • +
    • Remove quotes around phrases to search for each word individually. bike shed will often show more results than "bike shed".
    • +
    • Consider loosening your query with OR. bike OR shed will often show more results than bike shed.
    • +
    '); + } +} + +/** + * Implements hook_theme(). + */ +function search_theme() { + return array( + 'search_block_form' => array( + 'render element' => 'form', + 'template' => 'search-block-form', + ), + 'search_result' => array( + 'variables' => array('result' => NULL, 'module' => NULL), + 'file' => 'search.pages.inc', + 'template' => 'search-result', + ), + 'search_results' => array( + 'variables' => array('results' => NULL, 'module' => NULL), + 'file' => 'search.pages.inc', + 'template' => 'search-results', + ), + ); +} + +/** + * Implements hook_permission(). + */ +function search_permission() { + return array( + 'administer search' => array( + 'title' => t('Administer search'), + ), + 'search content' => array( + 'title' => t('Use search'), + ), + 'use advanced search' => array( + 'title' => t('Use advanced search'), + ), + ); +} + +/** + * Implements hook_block_info(). + */ +function search_block_info() { + $blocks['form']['info'] = t('Search form'); + // Not worth caching. + $blocks['form']['cache'] = DRUPAL_NO_CACHE; + $blocks['form']['properties']['administrative'] = TRUE; + return $blocks; +} + +/** + * Implements hook_block_view(). + */ +function search_block_view($delta = '') { + if (user_access('search content')) { + $block['content'] = drupal_get_form('search_block_form'); + return $block; + } +} + +/** + * Implements hook_menu(). + */ +function search_menu() { + $items['search'] = array( + 'title' => 'Search', + 'page callback' => 'search_view', + 'access callback' => 'search_is_active', + 'type' => MENU_SUGGESTED_ITEM, + 'file' => 'search.pages.inc', + ); + $items['admin/config/search/settings'] = array( + 'title' => 'Search settings', + 'description' => 'Configure relevance settings for search and other indexing options.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('search_admin_settings'), + 'access arguments' => array('administer search'), + 'weight' => -10, + 'file' => 'search.admin.inc', + ); + $items['admin/config/search/settings/reindex'] = array( + 'title' => 'Clear index', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('search_reindex_confirm'), + 'access arguments' => array('administer search'), + 'type' => MENU_VISIBLE_IN_BREADCRUMB, + 'file' => 'search.admin.inc', + ); + + // Add paths for searching. We add each module search path twice: once without + // and once with %menu_tail appended. The reason for this is that we want to + // preserve keywords when switching tabs, and also to have search tabs + // highlighted properly. The only way to do that within the Drupal menu + // system appears to be having two sets of tabs. See discussion on issue + // http://drupal.org/node/245103 for details. + + drupal_static_reset('search_get_info'); + $default_info = search_get_default_module_info(); + if ($default_info) { + foreach (search_get_info() as $module => $search_info) { + $path = 'search/' . $search_info['path']; + $items[$path] = array( + 'title' => $search_info['title'], + 'page callback' => 'search_view', + 'page arguments' => array($module, ''), + 'access callback' => '_search_menu_access', + 'access arguments' => array($module), + 'type' => MENU_LOCAL_TASK, + 'file' => 'search.pages.inc', + 'weight' => $module == $default_info['module'] ? -10 : 0, + ); + $items["$path/%menu_tail"] = array( + 'title' => $search_info['title'], + 'load arguments' => array('%map', '%index'), + 'page callback' => 'search_view', + 'page arguments' => array($module, 2), + 'access callback' => '_search_menu_access', + 'access arguments' => array($module), + // The default local task points to its parent, but this item points to + // where it should so it should not be changed. + 'type' => MENU_LOCAL_TASK, + 'file' => 'search.pages.inc', + 'weight' => 0, + // These tabs are not subtabs. + 'tab_root' => 'search/' . $default_info['path'] . '/%', + // These tabs need to display at the same level. + 'tab_parent' => 'search/' . $default_info['path'], + ); + } + } + return $items; +} + +/** + * Determines access for the ?q=search path. + */ +function search_is_active() { + // This path cannot be accessed if there are no active modules. + return user_access('search content') && search_get_info(); +} + +/** + * Returns information about available search modules. + * + * @param $all + * If TRUE, information about all enabled modules implementing + * hook_search_info() will be returned. If FALSE (default), only modules that + * have been set to active on the search settings page will be returned. + * + * @return + * Array of hook_search_info() return values, keyed by module name. The + * 'title' and 'path' array elements will be set to defaults for each module + * if not supplied by hook_search_info(), and an additional array element of + * 'module' will be added (set to the module name). + */ +function search_get_info($all = FALSE) { + $search_hooks = &drupal_static(__FUNCTION__); + + if (!isset($search_hooks)) { + foreach (module_implements('search_info') as $module) { + $search_hooks[$module] = call_user_func($module . '_search_info'); + // Use module name as the default value. + $search_hooks[$module] += array('title' => $module, 'path' => $module); + // Include the module name itself in the array. + $search_hooks[$module]['module'] = $module; + } + } + + if ($all) { + return $search_hooks; + } + + $active = variable_get('search_active_modules', array('node', 'user')); + return array_intersect_key($search_hooks, array_flip($active)); +} + +/** + * Returns information about the default search module. + * + * @return + * The search_get_info() array element for the default search module, if any. + */ +function search_get_default_module_info() { + $info = search_get_info(); + $default = variable_get('search_default_module', 'node'); + if (isset($info[$default])) { + return $info[$default]; + } + // The variable setting does not match any active module, so just return + // the info for the first active module (if any). + return reset($info); +} + +/** + * Access callback for search tabs. + */ +function _search_menu_access($name) { + return user_access('search content') && (!function_exists($name . '_search_access') || module_invoke($name, 'search_access')); +} + +/** + * Clears a part of or the entire search index. + * + * @param $sid + * (optional) The ID of the item to remove from the search index. If + * specified, $module must also be given. Omit both $sid and $module to clear + * the entire search index. + * @param $module + * (optional) The machine-readable name of the module for the item to remove + * from the search index. + */ +function search_reindex($sid = NULL, $module = NULL, $reindex = FALSE) { + if ($module == NULL && $sid == NULL) { + module_invoke_all('search_reset'); + } + else { + db_delete('search_dataset') + ->condition('sid', $sid) + ->condition('type', $module) + ->execute(); + db_delete('search_index') + ->condition('sid', $sid) + ->condition('type', $module) + ->execute(); + // Don't remove links if re-indexing. + if (!$reindex) { + db_delete('search_node_links') + ->condition('sid', $sid) + ->condition('type', $module) + ->execute(); + } + } +} + +/** + * Marks a word as "dirty" (changed), or retrieves the list of dirty words. + * + * This is used during indexing (cron). Words that are dirty have outdated + * total counts in the search_total table, and need to be recounted. + */ +function search_dirty($word = NULL) { + $dirty = &drupal_static(__FUNCTION__, array()); + if ($word !== NULL) { + $dirty[$word] = TRUE; + } + else { + return $dirty; + } +} + +/** + * Implements hook_cron(). + * + * Fires hook_update_index() in all modules and cleans up dirty words. + * + * @see search_dirty() + */ +function search_cron() { + // We register a shutdown function to ensure that search_total is always up + // to date. + drupal_register_shutdown_function('search_update_totals'); + + foreach (variable_get('search_active_modules', array('node', 'user')) as $module) { + // Update word index + module_invoke($module, 'update_index'); + } +} + +/** + * Updates the {search_total} database table. + * + * This function is called on shutdown to ensure that {search_total} is always + * up to date (even if cron times out or otherwise fails). + */ +function search_update_totals() { + // Update word IDF (Inverse Document Frequency) counts for new/changed words. + foreach (search_dirty() as $word => $dummy) { + // Get total count + $total = db_query("SELECT SUM(score) FROM {search_index} WHERE word = :word", array(':word' => $word), array('target' => 'slave'))->fetchField(); + // Apply Zipf's law to equalize the probability distribution. + $total = log10(1 + 1/(max(1, $total))); + db_merge('search_total') + ->key(array('word' => $word)) + ->fields(array('count' => $total)) + ->execute(); + } + // Find words that were deleted from search_index, but are still in + // search_total. We use a LEFT JOIN between the two tables and keep only the + // rows which fail to join. + $result = db_query("SELECT t.word AS realword, i.word FROM {search_total} t LEFT JOIN {search_index} i ON t.word = i.word WHERE i.word IS NULL", array(), array('target' => 'slave')); + $or = db_or(); + foreach ($result as $word) { + $or->condition('word', $word->realword); + } + if (count($or) > 0) { + db_delete('search_total') + ->condition($or) + ->execute(); + } +} + +/** + * Simplifies a string according to indexing rules. + * + * @param $text + * Text to simplify. + * + * @return + * Simplified text. + * + * @see hook_search_preprocess() + */ +function search_simplify($text) { + // Decode entities to UTF-8 + $text = decode_entities($text); + + // Lowercase + $text = drupal_strtolower($text); + + // Call an external processor for word handling. + search_invoke_preprocess($text); + + // Simple CJK handling + if (variable_get('overlap_cjk', TRUE)) { + $text = preg_replace_callback('/[' . PREG_CLASS_CJK . ']+/u', 'search_expand_cjk', $text); + } + + // To improve searching for numerical data such as dates, IP addresses + // or version numbers, we consider a group of numerical characters + // separated only by punctuation characters to be one piece. + // This also means that searching for e.g. '20/03/1984' also returns + // results with '20-03-1984' in them. + // Readable regexp: ([number]+)[punctuation]+(?=[number]) + $text = preg_replace('/([' . PREG_CLASS_NUMBERS . ']+)[' . PREG_CLASS_PUNCTUATION . ']+(?=[' . PREG_CLASS_NUMBERS . '])/u', '\1', $text); + + // Multiple dot and dash groups are word boundaries and replaced with space. + // No need to use the unicode modifer here because 0-127 ASCII characters + // can't match higher UTF-8 characters as the leftmost bit of those are 1. + $text = preg_replace('/[.-]{2,}/', ' ', $text); + + // The dot, underscore and dash are simply removed. This allows meaningful + // search behavior with acronyms and URLs. See unicode note directly above. + $text = preg_replace('/[._-]+/', '', $text); + + // With the exception of the rules above, we consider all punctuation, + // marks, spacers, etc, to be a word boundary. + $text = preg_replace('/[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . ']+/u', ' ', $text); + + // Truncate everything to 50 characters. + $words = explode(' ', $text); + array_walk($words, '_search_index_truncate'); + $text = implode(' ', $words); + + return $text; +} + +/** + * Splits CJK (Chinese, Japanese, Korean) text into tokens. + * + * The Search module matches exact words, where a word is defined to be a + * sequence of characters delimited by spaces or punctuation. CJK languages are + * written in long strings of characters, though, not split up into words. So + * in order to allow search matching, we split up CJK text into tokens + * consisting of consecutive, overlapping sequences of characters whose length + * is equal to the 'minimum_word_size' variable. This tokenizing is only done if + * the 'overlap_cjk' variable is TRUE. + * + * @param $matches + * This function is a callback for preg_replace_callback(), which is called + * from search_simplify(). So, $matches is an array of regular expression + * matches, which means that $matches[0] contains the matched text -- a string + * of CJK characters to tokenize. + * + * @return + * Tokenized text, starting and ending with a space character. + */ +function search_expand_cjk($matches) { + $min = variable_get('minimum_word_size', 3); + $str = $matches[0]; + $length = drupal_strlen($str); + // If the text is shorter than the minimum word size, don't tokenize it. + if ($length <= $min) { + return ' ' . $str . ' '; + } + $tokens = ' '; + // Build a FIFO queue of characters. + $chars = array(); + for ($i = 0; $i < $length; $i++) { + // Add the next character off the beginning of the string to the queue. + $current = drupal_substr($str, 0, 1); + $str = substr($str, strlen($current)); + $chars[] = $current; + if ($i >= $min - 1) { + // Make a token of $min characters, and add it to the token string. + $tokens .= implode('', $chars) . ' '; + // Shift out the first character in the queue. + array_shift($chars); + } + } + return $tokens; +} + +/** + * Simplifies and splits a string into tokens for indexing. + */ +function search_index_split($text) { + $last = &drupal_static(__FUNCTION__); + $lastsplit = &drupal_static(__FUNCTION__ . ':lastsplit'); + + if ($last == $text) { + return $lastsplit; + } + // Process words + $text = search_simplify($text); + $words = explode(' ', $text); + + // Save last keyword result + $last = $text; + $lastsplit = $words; + + return $words; +} + +/** + * Helper function for array_walk in search_index_split. + */ +function _search_index_truncate(&$text) { + if (is_numeric($text)) { + $text = ltrim($text, '0'); + } + $text = truncate_utf8($text, 50); +} + +/** + * Invokes hook_search_preprocess() in modules. + */ +function search_invoke_preprocess(&$text) { + foreach (module_implements('search_preprocess') as $module) { + $text = module_invoke($module, 'search_preprocess', $text); + } +} + +/** + * Update the full-text search index for a particular item. + * + * @param $sid + * An ID number identifying this particular item (e.g., node ID). + * @param $module + * The machine-readable name of the module that this item comes from (a module + * that implements hook_search_info()). + * @param $text + * The content of this item. Must be a piece of HTML or plain text. + * + * @ingroup search + */ +function search_index($sid, $module, $text) { + $minimum_word_size = variable_get('minimum_word_size', 3); + + // Link matching + global $base_url; + $node_regexp = '@href=[\'"]?(?:' . preg_quote($base_url, '@') . '/|' . preg_quote(base_path(), '@') . ')(?:\?q=)?/?((?![a-z]+:)[^\'">]+)[\'">]@i'; + + // Multipliers for scores of words inside certain HTML tags. The weights are stored + // in a variable so that modules can overwrite the default weights. + // Note: 'a' must be included for link ranking to work. + $tags = variable_get('search_tag_weights', array( + 'h1' => 25, + 'h2' => 18, + 'h3' => 15, + 'h4' => 12, + 'h5' => 9, + 'h6' => 6, + 'u' => 3, + 'b' => 3, + 'i' => 3, + 'strong' => 3, + 'em' => 3, + 'a' => 10)); + + // Strip off all ignored tags to speed up processing, but insert space before/after + // them to keep word boundaries. + $text = str_replace(array('<', '>'), array(' <', '> '), $text); + $text = strip_tags($text, '<' . implode('><', array_keys($tags)) . '>'); + + // Split HTML tags from plain text. + $split = preg_split('/\s*<([^>]+?)>\s*/', $text, -1, PREG_SPLIT_DELIM_CAPTURE); + // Note: PHP ensures the array consists of alternating delimiters and literals + // and begins and ends with a literal (inserting $null as required). + + $tag = FALSE; // Odd/even counter. Tag or no tag. + $link = FALSE; // State variable for link analyzer + $score = 1; // Starting score per word + $accum = ' '; // Accumulator for cleaned up data + $tagstack = array(); // Stack with open tags + $tagwords = 0; // Counter for consecutive words + $focus = 1; // Focus state + + $results = array(0 => array()); // Accumulator for words for index + + foreach ($split as $value) { + if ($tag) { + // Increase or decrease score per word based on tag + list($tagname) = explode(' ', $value, 2); + $tagname = drupal_strtolower($tagname); + // Closing or opening tag? + if ($tagname[0] == '/') { + $tagname = substr($tagname, 1); + // If we encounter unexpected tags, reset score to avoid incorrect boosting. + if (!count($tagstack) || $tagstack[0] != $tagname) { + $tagstack = array(); + $score = 1; + } + else { + // Remove from tag stack and decrement score + $score = max(1, $score - $tags[array_shift($tagstack)]); + } + if ($tagname == 'a') { + $link = FALSE; + } + } + else { + if (isset($tagstack[0]) && $tagstack[0] == $tagname) { + // None of the tags we look for make sense when nested identically. + // If they are, it's probably broken HTML. + $tagstack = array(); + $score = 1; + } + else { + // Add to open tag stack and increment score + array_unshift($tagstack, $tagname); + $score += $tags[$tagname]; + } + if ($tagname == 'a') { + // Check if link points to a node on this site + if (preg_match($node_regexp, $value, $match)) { + $path = drupal_get_normal_path($match[1]); + if (preg_match('!(?:node|book)/(?:view/)?([0-9]+)!i', $path, $match)) { + $linknid = $match[1]; + if ($linknid > 0) { + $node = db_query('SELECT title, nid, vid FROM {node} WHERE nid = :nid', array(':nid' => $linknid), array('target' => 'slave'))->fetchObject(); + $link = TRUE; + $linktitle = $node->title; + } + } + } + } + } + // A tag change occurred, reset counter. + $tagwords = 0; + } + else { + // Note: use of PREG_SPLIT_DELIM_CAPTURE above will introduce empty values + if ($value != '') { + if ($link) { + // Check to see if the node link text is its URL. If so, we use the target node title instead. + if (preg_match('!^https?://!i', $value)) { + $value = $linktitle; + } + } + $words = search_index_split($value); + foreach ($words as $word) { + // Add word to accumulator + $accum .= $word . ' '; + // Check wordlength + if (is_numeric($word) || drupal_strlen($word) >= $minimum_word_size) { + // Links score mainly for the target. + if ($link) { + if (!isset($results[$linknid])) { + $results[$linknid] = array(); + } + $results[$linknid][] = $word; + // Reduce score of the link caption in the source. + $focus *= 0.2; + } + // Fall-through + if (!isset($results[0][$word])) { + $results[0][$word] = 0; + } + $results[0][$word] += $score * $focus; + + // Focus is a decaying value in terms of the amount of unique words up to this point. + // From 100 words and more, it decays, to e.g. 0.5 at 500 words and 0.3 at 1000 words. + $focus = min(1, .01 + 3.5 / (2 + count($results[0]) * .015)); + } + $tagwords++; + // Too many words inside a single tag probably mean a tag was accidentally left open. + if (count($tagstack) && $tagwords >= 15) { + $tagstack = array(); + $score = 1; + } + } + } + } + $tag = !$tag; + } + + search_reindex($sid, $module, TRUE); + + // Insert cleaned up data into dataset + db_insert('search_dataset') + ->fields(array( + 'sid' => $sid, + 'type' => $module, + 'data' => $accum, + 'reindex' => 0, + )) + ->execute(); + + // Insert results into search index + foreach ($results[0] as $word => $score) { + // If a word already exists in the database, its score gets increased + // appropriately. If not, we create a new record with the appropriate + // starting score. + db_merge('search_index') + ->key(array( + 'word' => $word, + 'sid' => $sid, + 'type' => $module, + )) + ->fields(array('score' => $score)) + ->expression('score', 'score + :score', array(':score' => $score)) + ->execute(); + search_dirty($word); + } + unset($results[0]); + + // Get all previous links from this item. + $result = db_query("SELECT nid, caption FROM {search_node_links} WHERE sid = :sid AND type = :type", array( + ':sid' => $sid, + ':type' => $module + ), array('target' => 'slave')); + $links = array(); + foreach ($result as $link) { + $links[$link->nid] = $link->caption; + } + + // Now store links to nodes. + foreach ($results as $nid => $words) { + $caption = implode(' ', $words); + if (isset($links[$nid])) { + if ($links[$nid] != $caption) { + // Update the existing link and mark the node for reindexing. + db_update('search_node_links') + ->fields(array('caption' => $caption)) + ->condition('sid', $sid) + ->condition('type', $module) + ->condition('nid', $nid) + ->execute(); + search_touch_node($nid); + } + // Unset the link to mark it as processed. + unset($links[$nid]); + } + elseif ($sid != $nid || $module != 'node') { + // Insert the existing link and mark the node for reindexing, but don't + // reindex if this is a link in a node pointing to itself. + db_insert('search_node_links') + ->fields(array( + 'caption' => $caption, + 'sid' => $sid, + 'type' => $module, + 'nid' => $nid, + )) + ->execute(); + search_touch_node($nid); + } + } + // Any left-over links in $links no longer exist. Delete them and mark the nodes for reindexing. + foreach ($links as $nid => $caption) { + db_delete('search_node_links') + ->condition('sid', $sid) + ->condition('type', $module) + ->condition('nid', $nid) + ->execute(); + search_touch_node($nid); + } +} + +/** + * Changes a node's changed timestamp to 'now' to force reindexing. + * + * @param $nid + * The node ID of the node that needs reindexing. + */ +function search_touch_node($nid) { + db_update('search_dataset') + ->fields(array('reindex' => REQUEST_TIME)) + ->condition('type', 'node') + ->condition('sid', $nid) + ->execute(); +} + +/** + * Implements hook_node_update_index(). + */ +function search_node_update_index($node) { + // Transplant links to a node into the target node. + $result = db_query("SELECT caption FROM {search_node_links} WHERE nid = :nid", array(':nid' => $node->nid), array('target' => 'slave')); + $output = array(); + foreach ($result as $link) { + $output[] = $link->caption; + } + if (count($output)) { + return '(' . implode(', ', $output) . ')'; + } +} + +/** + * Implements hook_node_update(). + */ +function search_node_update($node) { + // Reindex the node when it is updated. The node is automatically indexed + // when it is added, simply by being added to the node table. + search_touch_node($node->nid); +} + +/** + * Implements hook_comment_insert(). + */ +function search_comment_insert($comment) { + // Reindex the node when comments are added. + search_touch_node($comment->nid); +} + +/** + * Implements hook_comment_update(). + */ +function search_comment_update($comment) { + // Reindex the node when comments are changed. + search_touch_node($comment->nid); +} + +/** + * Implements hook_comment_delete(). + */ +function search_comment_delete($comment) { + // Reindex the node when comments are deleted. + search_touch_node($comment->nid); +} + +/** + * Implements hook_comment_publish(). + */ +function search_comment_publish($comment) { + // Reindex the node when comments are published. + search_touch_node($comment->nid); +} + +/** + * Implements hook_comment_unpublish(). + */ +function search_comment_unpublish($comment) { + // Reindex the node when comments are unpublished. + search_touch_node($comment->nid); +} + +/** + * Extracts a module-specific search option from a search expression. + * + * Search options are added using search_expression_insert(), and retrieved + * using search_expression_extract(). They take the form option:value, and + * are added to the ordinary keywords in the search expression. + * + * @param $expression + * The search expression to extract from. + * @param $option + * The name of the option to retrieve from the search expression. + * + * @return + * The value previously stored in the search expression for option $option, + * if any. Trailing spaces in values will not be included. + */ +function search_expression_extract($expression, $option) { + if (preg_match('/(^| )' . $option . ':([^ ]*)( |$)/i', $expression, $matches)) { + return $matches[2]; + } +} + +/** + * Adds a module-specific search option to a search expression. + * + * Search options are added using search_expression_insert(), and retrieved + * using search_expression_extract(). They take the form option:value, and + * are added to the ordinary keywords in the search expression. + * + * @param $expression + * The search expression to add to. + * @param $option + * The name of the option to add to the search expression. + * @param $value + * The value to add for the option. If present, it will replace any previous + * value added for the option. Cannot contain any spaces or | characters, as + * these are used as delimiters. If you want to add a blank value $option: to + * the search expression, pass in an empty string or a string that is composed + * of only spaces. To clear a previously-stored option without adding a + * replacement, pass in NULL for $value or omit. + * + * @return + * $expression, with any previous value for this option removed, and a new + * $option:$value pair added if $value was provided. + */ +function search_expression_insert($expression, $option, $value = NULL) { + // Remove any previous values stored with $option. + $expression = trim(preg_replace('/(^| )' . $option . ':[^ ]*/i', '', $expression)); + + // Set new value, if provided. + if (isset($value)) { + $expression .= ' ' . $option . ':' . trim($value); + } + return $expression; +} + +/** + * @defgroup search Search interface + * @{ + * The Drupal search interface manages a global search mechanism. + * + * Modules may plug into this system to provide searches of different types of + * data. Most of the system is handled by search.module, so this must be enabled + * for all of the search features to work. + * + * There are three ways to interact with the search system: + * - Specifically for searching nodes, you can implement + * hook_node_update_index() and hook_node_search_result(). However, note that + * the search system already indexes all visible output of a node; i.e., + * everything displayed normally by hook_view() and hook_node_view(). This is + * usually sufficient. You should only use this mechanism if you want + * additional, non-visible data to be indexed. + * - Implement hook_search_info(). This will create a search tab for your module + * on the /search page with a simple keyword search form. You will also need + * to implement hook_search_execute() to perform the search. + * - Implement hook_update_index(). This allows your module to use Drupal's + * HTML indexing mechanism for searching full text efficiently. + * + * If your module needs to provide a more complicated search form, then you need + * to implement it yourself without hook_search_info(). In that case, you should + * define it as a local task (tab) under the /search page (e.g. /search/mymodule) + * so that users can easily find it. + */ + +/** + * Builds a search form. + * + * @param $action + * Form action. Defaults to "search/$path", where $path is the search path + * associated with the module in its hook_search_info(). This will be + * run through url(). + * @param $keys + * The search string entered by the user, containing keywords for the search. + * @param $module + * The search module to render the form for: a module that implements + * hook_search_info(). If not supplied, the default search module is used. + * @param $prompt + * Label for the keywords field. Defaults to t('Enter your keywords') if NULL. + * Supply '' to omit. + * + * @return + * A Form API array for the search form. + */ +function search_form($form, &$form_state, $action = '', $keys = '', $module = NULL, $prompt = NULL) { + $module_info = FALSE; + if (!$module) { + $module_info = search_get_default_module_info(); + } + else { + $info = search_get_info(); + $module_info = isset($info[$module]) ? $info[$module] : FALSE; + } + + // Sanity check. + if (!$module_info) { + form_set_error(NULL, t('Search is currently disabled.'), 'error'); + return $form; + } + + if (!$action) { + $action = 'search/' . $module_info['path']; + } + if (!isset($prompt)) { + $prompt = t('Enter your keywords'); + } + + $form['#action'] = url($action); + // Record the $action for later use in redirecting. + $form_state['action'] = $action; + $form['#attributes']['class'][] = 'search-form'; + $form['module'] = array('#type' => 'value', '#value' => $module); + $form['basic'] = array('#type' => 'container', '#attributes' => array('class' => array('container-inline'))); + $form['basic']['keys'] = array( + '#type' => 'textfield', + '#title' => $prompt, + '#default_value' => $keys, + '#size' => $prompt ? 40 : 20, + '#maxlength' => 255, + ); + // processed_keys is used to coordinate keyword passing between other forms + // that hook into the basic search form. + $form['basic']['processed_keys'] = array('#type' => 'value', '#value' => ''); + $form['basic']['submit'] = array('#type' => 'submit', '#value' => t('Search')); + + return $form; +} + +/** + * Form builder; Output a search form for the search block's search box. + * + * @ingroup forms + * @see search_box_form_submit() + * @see search-block-form.tpl.php + */ +function search_box($form, &$form_state, $form_id) { + $form[$form_id] = array( + '#type' => 'textfield', + '#title' => t('Search'), + '#title_display' => 'invisible', + '#size' => 15, + '#default_value' => '', + '#attributes' => array('title' => t('Enter the terms you wish to search for.')), + ); + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Search')); + $form['#submit'][] = 'search_box_form_submit'; + + return $form; +} + +/** + * Process a block search form submission. + */ +function search_box_form_submit($form, &$form_state) { + // The search form relies on control of the redirect destination for its + // functionality, so we override any static destination set in the request, + // for example by drupal_access_denied() or drupal_not_found() + // (see http://drupal.org/node/292565). + if (isset($_GET['destination'])) { + unset($_GET['destination']); + } + + // Check to see if the form was submitted empty. + // If it is empty, display an error message. + // (This method is used instead of setting #required to TRUE for this field + // because that results in a confusing error message. It would say a plain + // "field is required" because the search keywords field has no title. + // The error message would also complain about a missing #title field.) + if ($form_state['values']['search_block_form'] == '') { + form_set_error('keys', t('Please enter some keywords.')); + } + + $form_id = $form['form_id']['#value']; + $info = search_get_default_module_info(); + if ($info) { + $form_state['redirect'] = 'search/' . $info['path'] . '/' . trim($form_state['values'][$form_id]); + } + else { + form_set_error(NULL, t('Search is currently disabled.'), 'error'); + } +} + +/** + * Process variables for search-block-form.tpl.php. + * + * The $variables array contains the following arguments: + * - $form + * + * @see search-block-form.tpl.php + */ +function template_preprocess_search_block_form(&$variables) { + $variables['search'] = array(); + $hidden = array(); + // Provide variables named after form keys so themers can print each element independently. + foreach (element_children($variables['form']) as $key) { + $type = isset($variables['form'][$key]['#type']) ? $variables['form'][$key]['#type'] : ''; + if ($type == 'hidden' || $type == 'token') { + $hidden[] = drupal_render($variables['form'][$key]); + } + else { + $variables['search'][$key] = drupal_render($variables['form'][$key]); + } + } + // Hidden form elements have no value to themers. No need for separation. + $variables['search']['hidden'] = implode($hidden); + // Collect all form elements to make it easier to print the whole form. + $variables['search_form'] = implode($variables['search']); +} + +/** + * Performs a search by calling hook_search_execute(). + * + * @param $keys + * Keyword query to search on. + * @param $module + * Search module to search. + * @param $conditions + * Optional array of additional search conditions. + * + * @return + * Renderable array of search results. No return value if $keys are not + * supplied or if the given search module is not active. + */ +function search_data($keys, $module, $conditions = NULL) { + if (module_hook($module, 'search_execute')) { + $results = module_invoke($module, 'search_execute', $keys, $conditions); + if (module_hook($module, 'search_page')) { + return module_invoke($module, 'search_page', $results); + } + else { + return array( + '#theme' => 'search_results', + '#results' => $results, + '#module' => $module, + ); + } + } +} + +/** + * Returns snippets from a piece of text, with certain keywords highlighted. + * Used for formatting search results. + * + * @param $keys + * A string containing a search query. + * + * @param $text + * The text to extract fragments from. + * + * @return + * A string containing HTML for the excerpt. + */ +function search_excerpt($keys, $text) { + // We highlight around non-indexable or CJK characters. + $boundary = '(?:(?<=[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . PREG_CLASS_CJK . '])|(?=[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . PREG_CLASS_CJK . ']))'; + + // Extract positive keywords and phrases + preg_match_all('/ ("([^"]+)"|(?!OR)([^" ]+))/', ' ' . $keys, $matches); + $keys = array_merge($matches[2], $matches[3]); + + // Prepare text by stripping HTML tags and decoding HTML entities. + $text = strip_tags(str_replace(array('<', '>'), array(' <', '> '), $text)); + $text = decode_entities($text); + + // Slash-escape quotes in the search keyword string. + array_walk($keys, '_search_excerpt_replace'); + $workkeys = $keys; + + // Extract fragments around keywords. + // First we collect ranges of text around each keyword, starting/ending + // at spaces, trying to get to 256 characters. + // If the sum of all fragments is too short, we look for second occurrences. + $ranges = array(); + $included = array(); + $foundkeys = array(); + $length = 0; + while ($length < 256 && count($workkeys)) { + foreach ($workkeys as $k => $key) { + if (strlen($key) == 0) { + unset($workkeys[$k]); + unset($keys[$k]); + continue; + } + if ($length >= 256) { + break; + } + // Remember occurrence of key so we can skip over it if more occurrences + // are desired. + if (!isset($included[$key])) { + $included[$key] = 0; + } + // Locate a keyword (position $p, always >0 because $text starts with a + // space). First try bare keyword, but if that doesn't work, try to find a + // derived form from search_simplify(). + $p = 0; + if (preg_match('/' . $boundary . $key . $boundary . '/iu', $text, $match, PREG_OFFSET_CAPTURE, $included[$key])) { + $p = $match[0][1]; + } + else { + $info = search_simplify_excerpt_match($key, $text, $included[$key], $boundary); + if (isset($info['where'])) { + $p = $info['where']; + if ($info['keyword']) { + $foundkeys[] = $info['keyword']; + } + } + } + // Now locate a space in front (position $q) and behind it (position $s), + // leaving about 60 characters extra before and after for context. + // Note that a space was added to the front and end of $text above. + if ($p) { + if (($q = strpos(' ' . $text, ' ', max(0, $p - 61))) !== FALSE) { + $end = substr($text . ' ', $p, 80); + if (($s = strrpos($end, ' ')) !== FALSE) { + // Account for the added spaces. + $q = max($q - 1, 0); + $s = min($s, strlen($end) - 1); + $ranges[$q] = $p + $s; + $length += $p + $s - $q; + $included[$key] = $p + 1; + } + else { + unset($workkeys[$k]); + } + } + else { + unset($workkeys[$k]); + } + } + else { + unset($workkeys[$k]); + } + } + } + + if (count($ranges) == 0) { + // We didn't find any keyword matches, so just return the first part of the + // text. We also need to re-encode any HTML special characters that we + // entity-decoded above. + return check_plain(truncate_utf8($text, 256, TRUE, TRUE)); + } + + // Sort the text ranges by starting position. + ksort($ranges); + + // Now we collapse overlapping text ranges into one. The sorting makes it O(n). + $newranges = array(); + foreach ($ranges as $from2 => $to2) { + if (!isset($from1)) { + $from1 = $from2; + $to1 = $to2; + continue; + } + if ($from2 <= $to1) { + $to1 = max($to1, $to2); + } + else { + $newranges[$from1] = $to1; + $from1 = $from2; + $to1 = $to2; + } + } + $newranges[$from1] = $to1; + + // Fetch text + $out = array(); + foreach ($newranges as $from => $to) { + $out[] = substr($text, $from, $to - $from); + } + + // Let translators have the ... separator text as one chunk. + $dots = explode('!excerpt', t('... !excerpt ... !excerpt ...')); + + $text = (isset($newranges[0]) ? '' : $dots[0]) . implode($dots[1], $out) . $dots[2]; + $text = check_plain($text); + + // Slash-escape quotes in keys found in a derived form and merge with original keys. + array_walk($foundkeys, '_search_excerpt_replace'); + $keys = array_merge($keys, $foundkeys); + + // Highlight keywords. Must be done at once to prevent conflicts ('strong' and ''). + $text = preg_replace('/' . $boundary . '(' . implode('|', $keys) . ')' . $boundary . '/iu', '\0', $text); + return $text; +} + +/** + * @} End of "defgroup search". + */ + +/** + * Helper function for array_walk() in search_excerpt(). + */ +function _search_excerpt_replace(&$text) { + $text = preg_quote($text, '/'); +} + +/** + * Find words in the original text that matched via search_simplify(). + * + * This is called in search_excerpt() if an exact match is not found in the + * text, so that we can find the derived form that matches. + * + * @param $key + * The keyword to find. + * @param $text + * The text to search for the keyword. + * @param $offset + * Offset position in $text to start searching at. + * @param $boundary + * Text to include in a regular expression that will match a word boundary. + * + * @return + * FALSE if no match is found. If a match is found, return an associative + * array with element 'where' giving the position of the match, and element + * 'keyword' giving the actual word found in the text at that position. + */ +function search_simplify_excerpt_match($key, $text, $offset, $boundary) { + $pos = NULL; + $simplified_key = search_simplify($key); + $simplified_text = search_simplify($text); + + // Return immediately if simplified key or text are empty. + if (!$simplified_key || !$simplified_text) { + return FALSE; + } + + // Check if we have a match after simplification in the text. + if (!preg_match('/' . $boundary . $simplified_key . $boundary . '/iu', $simplified_text, $match, PREG_OFFSET_CAPTURE, $offset)) { + return FALSE; + } + + // If we get here, we have a match. Now find the exact location of the match + // and the original text that matched. Start by splitting up the text by all + // potential starting points of the matching text and iterating through them. + $split = array_filter(preg_split('/' . $boundary . '/iu', $text, -1, PREG_SPLIT_OFFSET_CAPTURE), '_search_excerpt_match_filter'); + foreach ($split as $value) { + // Skip starting points before the offset. + if ($value[1] < $offset) { + continue; + } + + // Check a window of 80 characters after the starting point for a match, + // based on the size of the excerpt window. + $window = substr($text, $value[1], 80); + $simplified_window = search_simplify($window); + if (strpos($simplified_window, $simplified_key) === 0) { + // We have a match in this window. Store the position of the match. + $pos = $value[1]; + // Iterate through the text in the window until we find the full original + // matching text. + $length = strlen($window); + for ($i = 1; $i <= $length; $i++) { + $keyfound = substr($text, $value[1], $i); + if ($simplified_key == search_simplify($keyfound)) { + break; + } + } + break; + } + } + + return $pos ? array('where' => $pos, 'keyword' => $keyfound) : FALSE; +} + +/** + * Helper function for array_filter() in search_search_excerpt_match(). + */ +function _search_excerpt_match_filter($var) { + return strlen(trim($var[0])); +} + +/** + * Implements hook_forms(). + */ +function search_forms() { + $forms['search_block_form']= array( + 'callback' => 'search_box', + 'callback arguments' => array('search_block_form'), + ); + return $forms; +} + diff --git a/drupal7/web/modules/search/search.pages.inc b/drupal7/web/modules/search/search.pages.inc new file mode 100644 index 0000000..b24de8e --- /dev/null +++ b/drupal7/web/modules/search/search.pages.inc @@ -0,0 +1,159 @@ + ''); + // Process the search form. Note that if there is $_POST data, + // search_form_submit() will cause a redirect to search/[module path]/[keys], + // which will get us back to this page callback. In other words, the search + // form submits with POST but redirects to GET. This way we can keep + // the search query URL clean as a whistle. + if (empty($_POST['form_id']) || ($_POST['form_id'] != 'search_form' && $_POST['form_id'] != 'search_block_form')) { + $conditions = NULL; + if (isset($info['conditions_callback']) && function_exists($info['conditions_callback'])) { + // Build an optional array of more search conditions. + $conditions = call_user_func($info['conditions_callback'], $keys); + } + // Only search if there are keywords or non-empty conditions. + if ($keys || !empty($conditions)) { + if (variable_get('search_logging', TRUE)) { + // Log the search keys. + watchdog('search', 'Searched %type for %keys.', array('%keys' => $keys, '%type' => $info['title']), WATCHDOG_NOTICE, l(t('results'), 'search/' . $info['path'] . '/' . $keys)); + } + // Collect the search results. + $results = search_data($keys, $info['module'], $conditions); + } + } + // The form may be altered based on whether the search was run. + $build['search_form'] = drupal_get_form('search_form', NULL, $keys, $info['module']); + $build['search_results'] = $results; + + return $build; +} + +/** + * Process variables for search-results.tpl.php. + * + * The $variables array contains the following arguments: + * - $results: Search results array. + * - $module: Module the search results came from (module implementing + * hook_search_info()). + * + * @see search-results.tpl.php + */ +function template_preprocess_search_results(&$variables) { + $variables['search_results'] = ''; + if (!empty($variables['module'])) { + $variables['module'] = check_plain($variables['module']); + } + foreach ($variables['results'] as $result) { + $variables['search_results'] .= theme('search_result', array('result' => $result, 'module' => $variables['module'])); + } + $variables['pager'] = theme('pager', array('tags' => NULL)); + $variables['theme_hook_suggestions'][] = 'search_results__' . $variables['module']; +} + +/** + * Process variables for search-result.tpl.php. + * + * The $variables array contains the following arguments: + * - $result + * - $module + * + * @see search-result.tpl.php + */ +function template_preprocess_search_result(&$variables) { + global $language; + + $result = $variables['result']; + $variables['url'] = check_url($result['link']); + $variables['title'] = check_plain($result['title']); + if (isset($result['language']) && $result['language'] != $language->language && $result['language'] != LANGUAGE_NONE) { + $variables['title_attributes_array']['xml:lang'] = $result['language']; + $variables['content_attributes_array']['xml:lang'] = $result['language']; + } + + $info = array(); + if (!empty($result['module'])) { + $info['module'] = check_plain($result['module']); + } + if (!empty($result['user'])) { + $info['user'] = $result['user']; + } + if (!empty($result['date'])) { + $info['date'] = format_date($result['date'], 'short'); + } + if (isset($result['extra']) && is_array($result['extra'])) { + $info = array_merge($info, $result['extra']); + } + // Check for existence. User search does not include snippets. + $variables['snippet'] = isset($result['snippet']) ? $result['snippet'] : ''; + // Provide separated and grouped meta information.. + $variables['info_split'] = $info; + $variables['info'] = implode(' - ', $info); + $variables['theme_hook_suggestions'][] = 'search_result__' . $variables['module']; +} + +/** + * As the search form collates keys from other modules hooked in via + * hook_form_alter, the validation takes place in _submit. + * search_form_validate() is used solely to set the 'processed_keys' form + * value for the basic search form. + */ +function search_form_validate($form, &$form_state) { + form_set_value($form['basic']['processed_keys'], trim($form_state['values']['keys']), $form_state); +} + +/** + * Process a search form submission. + */ +function search_form_submit($form, &$form_state) { + $keys = $form_state['values']['processed_keys']; + if ($keys == '') { + form_set_error('keys', t('Please enter some keywords.')); + // Fall through to the form redirect. + } + + $form_state['redirect'] = $form_state['action'] . '/' . $keys; +} diff --git a/drupal7/web/modules/search/search.test b/drupal7/web/modules/search/search.test new file mode 100644 index 0000000..d97c3ff --- /dev/null +++ b/drupal7/web/modules/search/search.test @@ -0,0 +1,2179 @@ + 'Search engine queries', + 'description' => 'Indexes content and queries it.', + 'group' => 'Search', + ); + } + + /** + * Implementation setUp(). + */ + function setUp() { + parent::setUp('search'); + } + + /** + * Test search indexing. + */ + function testMatching() { + $this->_setup(); + $this->_testQueries(); + } + + /** + * Set up a small index of items to test against. + */ + function _setup() { + variable_set('minimum_word_size', 3); + + for ($i = 1; $i <= 7; ++$i) { + search_index($i, SEARCH_TYPE, $this->getText($i)); + } + for ($i = 1; $i <= 5; ++$i) { + search_index($i + 7, SEARCH_TYPE_2, $this->getText2($i)); + } + // No getText builder function for Japanese text; just a simple array. + foreach (array( + 13 => '以呂波耳・ほへとち。リヌルヲ。', + 14 => 'ドルーパルが大好きよ!', + 15 => 'コーヒーとケーキ', + ) as $i => $jpn) { + search_index($i, SEARCH_TYPE_JPN, $jpn); + } + search_update_totals(); + } + + /** + * _test_: Helper method for generating snippets of content. + * + * Generated items to test against: + * 1 ipsum + * 2 dolore sit + * 3 sit am ut + * 4 am ut enim am + * 5 ut enim am minim veniam + * 6 enim am minim veniam es cillum + * 7 am minim veniam es cillum dolore eu + */ + function getText($n) { + $words = explode(' ', "Ipsum dolore sit am. Ut enim am minim veniam. Es cillum dolore eu."); + return implode(' ', array_slice($words, $n - 1, $n)); + } + + /** + * _test2_: Helper method for generating snippets of content. + * + * Generated items to test against: + * 8 dear + * 9 king philip + * 10 philip came over + * 11 came over from germany + * 12 over from germany swimming + */ + function getText2($n) { + $words = explode(' ', "Dear King Philip came over from Germany swimming."); + return implode(' ', array_slice($words, $n - 1, $n)); + } + + /** + * Run predefine queries looking for indexed terms. + */ + function _testQueries() { + /* + Note: OR queries that include short words in OR groups are only accepted + if the ORed terms are ANDed with at least one long word in the rest of the query. + + e.g. enim dolore OR ut = enim (dolore OR ut) = (enim dolor) OR (enim ut) -> good + e.g. dolore OR ut = (dolore) OR (ut) -> bad + + This is a design limitation to avoid full table scans. + */ + $queries = array( + // Simple AND queries. + 'ipsum' => array(1), + 'enim' => array(4, 5, 6), + 'xxxxx' => array(), + 'enim minim' => array(5, 6), + 'enim xxxxx' => array(), + 'dolore eu' => array(7), + 'dolore xx' => array(), + 'ut minim' => array(5), + 'xx minim' => array(), + 'enim veniam am minim ut' => array(5), + // Simple OR queries. + 'dolore OR ipsum' => array(1, 2, 7), + 'dolore OR xxxxx' => array(2, 7), + 'dolore OR ipsum OR enim' => array(1, 2, 4, 5, 6, 7), + 'ipsum OR dolore sit OR cillum' => array(2, 7), + 'minim dolore OR ipsum' => array(7), + 'dolore OR ipsum veniam' => array(7), + 'minim dolore OR ipsum OR enim' => array(5, 6, 7), + 'dolore xx OR yy' => array(), + 'xxxxx dolore OR ipsum' => array(), + // Negative queries. + 'dolore -sit' => array(7), + 'dolore -eu' => array(2), + 'dolore -xxxxx' => array(2, 7), + 'dolore -xx' => array(2, 7), + // Phrase queries. + '"dolore sit"' => array(2), + '"sit dolore"' => array(), + '"am minim veniam es"' => array(6, 7), + '"minim am veniam es"' => array(), + // Mixed queries. + '"am minim veniam es" OR dolore' => array(2, 6, 7), + '"minim am veniam es" OR "dolore sit"' => array(2), + '"minim am veniam es" OR "sit dolore"' => array(), + '"am minim veniam es" -eu' => array(6), + '"am minim veniam" -"cillum dolore"' => array(5, 6), + '"am minim veniam" -"dolore cillum"' => array(5, 6, 7), + 'xxxxx "minim am veniam es" OR dolore' => array(), + 'xx "minim am veniam es" OR dolore' => array() + ); + foreach ($queries as $query => $results) { + $result = db_select('search_index', 'i') + ->extend('SearchQuery') + ->searchExpression($query, SEARCH_TYPE) + ->execute(); + + $set = $result ? $result->fetchAll() : array(); + $this->_testQueryMatching($query, $set, $results); + $this->_testQueryScores($query, $set, $results); + } + + // These queries are run against the second index type, SEARCH_TYPE_2. + $queries = array( + // Simple AND queries. + 'ipsum' => array(), + 'enim' => array(), + 'enim minim' => array(), + 'dear' => array(8), + 'germany' => array(11, 12), + ); + foreach ($queries as $query => $results) { + $result = db_select('search_index', 'i') + ->extend('SearchQuery') + ->searchExpression($query, SEARCH_TYPE_2) + ->execute(); + + $set = $result ? $result->fetchAll() : array(); + $this->_testQueryMatching($query, $set, $results); + $this->_testQueryScores($query, $set, $results); + } + + // These queries are run against the third index type, SEARCH_TYPE_JPN. + $queries = array( + // Simple AND queries. + '呂波耳' => array(13), + '以呂波耳' => array(13), + 'ほへと ヌルヲ' => array(13), + 'とちリ' => array(), + 'ドルーパル' => array(14), + 'パルが大' => array(14), + 'コーヒー' => array(15), + 'ヒーキ' => array(), + ); + foreach ($queries as $query => $results) { + $result = db_select('search_index', 'i') + ->extend('SearchQuery') + ->searchExpression($query, SEARCH_TYPE_JPN) + ->execute(); + + $set = $result ? $result->fetchAll() : array(); + $this->_testQueryMatching($query, $set, $results); + $this->_testQueryScores($query, $set, $results); + } + } + + /** + * Test the matching abilities of the engine. + * + * Verify if a query produces the correct results. + */ + function _testQueryMatching($query, $set, $results) { + // Get result IDs. + $found = array(); + foreach ($set as $item) { + $found[] = $item->sid; + } + + // Compare $results and $found. + sort($found); + sort($results); + $this->assertEqual($found, $results, "Query matching '$query'"); + } + + /** + * Test the scoring abilities of the engine. + * + * Verify if a query produces normalized, monotonous scores. + */ + function _testQueryScores($query, $set, $results) { + // Get result scores. + $scores = array(); + foreach ($set as $item) { + $scores[] = $item->calculated_score; + } + + // Check order. + $sorted = $scores; + sort($sorted); + $this->assertEqual($scores, array_reverse($sorted), "Query order '$query'"); + + // Check range. + $this->assertEqual(!count($scores) || (min($scores) > 0.0 && max($scores) <= 1.0001), TRUE, "Query scoring '$query'"); + } +} + +/** + * Tests the bike shed text on no results page, and text on the search page. + */ +class SearchPageText extends DrupalWebTestCase { + protected $searching_user; + + public static function getInfo() { + return array( + 'name' => 'Search page text', + 'description' => 'Tests the bike shed text on the no results page, and various other text on search pages.', + 'group' => 'Search' + ); + } + + function setUp() { + parent::setUp('search'); + + // Create user. + $this->searching_user = $this->drupalCreateUser(array('search content', 'access user profiles')); + } + + /** + * Tests the failed search text, and various other text on the search page. + */ + function testSearchText() { + $this->drupalLogin($this->searching_user); + $this->drupalGet('search/node'); + $this->assertText(t('Enter your keywords')); + $this->assertText(t('Search')); + $title = t('Search') . ' | Drupal'; + $this->assertTitle($title, 'Search page title is correct'); + + $edit = array(); + $edit['keys'] = 'bike shed ' . $this->randomName(); + $this->drupalPost('search/node', $edit, t('Search')); + $this->assertText(t('Consider loosening your query with OR. bike OR shed will often show more results than bike shed.'), 'Help text is displayed when search returns no results.'); + $this->assertText(t('Search')); + $this->assertTitle($title, 'Search page title is correct'); + + $edit['keys'] = $this->searching_user->name; + $this->drupalPost('search/user', $edit, t('Search')); + $this->assertText(t('Search')); + $this->assertTitle($title, 'Search page title is correct'); + + // Test that search keywords containing slashes are correctly loaded + // from the path and displayed in the search form. + $arg = $this->randomName() . '/' . $this->randomName(); + $this->drupalGet('search/node/' . $arg); + $input = $this->xpath("//input[@id='edit-keys' and @value='{$arg}']"); + $this->assertFalse(empty($input), 'Search keys with a / are correctly set as the default value in the search box.'); + + // Test a search input exceeding the limit of AND/OR combinations to test + // the Denial-of-Service protection. + $limit = variable_get('search_and_or_limit', 7); + $keys = array(); + for ($i = 0; $i < $limit + 1; $i++) { + $keys[] = $this->randomName(3); + if ($i % 2 == 0) { + $keys[] = 'OR'; + } + } + $edit['keys'] = implode(' ', $keys); + $this->drupalPost('search/node', $edit, t('Search')); + $this->assertRaw(t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', array('@count' => $limit))); + } +} + +/** + * Indexes content and tests the advanced search form. + */ +class SearchAdvancedSearchForm extends DrupalWebTestCase { + protected $node; + + public static function getInfo() { + return array( + 'name' => 'Advanced search form', + 'description' => 'Indexes content and tests the advanced search form.', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search'); + // Create and login user. + $test_user = $this->drupalCreateUser(array('access content', 'search content', 'use advanced search', 'administer nodes')); + $this->drupalLogin($test_user); + + // Create initial node. + $node = $this->drupalCreateNode(); + $this->node = $this->drupalCreateNode(); + + // First update the index. This does the initial processing. + node_update_index(); + + // Then, run the shutdown function. Testing is a unique case where indexing + // and searching has to happen in the same request, so running the shutdown + // function manually is needed to finish the indexing process. + search_update_totals(); + } + + /** + * Test using the search form with GET and POST queries. + * Test using the advanced search form to limit search to nodes of type "Basic page". + */ + function testNodeType() { + $this->assertTrue($this->node->type == 'page', 'Node type is Basic page.'); + + // Assert that the dummy title doesn't equal the real title. + $dummy_title = 'Lorem ipsum'; + $this->assertNotEqual($dummy_title, $this->node->title, "Dummy title doesn't equal node title"); + + // Search for the dummy title with a GET query. + $this->drupalGet('search/node/' . $dummy_title); + $this->assertNoText($this->node->title, 'Basic page node is not found with dummy title.'); + + // Search for the title of the node with a GET query. + $this->drupalGet('search/node/' . $this->node->title); + $this->assertText($this->node->title, 'Basic page node is found with GET query.'); + + // Search for the title of the node with a POST query. + $edit = array('or' => $this->node->title); + $this->drupalPost('search/node', $edit, t('Advanced search')); + $this->assertText($this->node->title, 'Basic page node is found with POST query.'); + + // Advanced search type option. + $this->drupalPost('search/node', array_merge($edit, array('type[page]' => 'page')), t('Advanced search')); + $this->assertText($this->node->title, 'Basic page node is found with POST query and type:page.'); + + $this->drupalPost('search/node', array_merge($edit, array('type[article]' => 'article')), t('Advanced search')); + $this->assertText('bike shed', 'Article node is not found with POST query and type:article.'); + } +} + +/** + * Indexes content and tests ranking factors. + */ +class SearchRankingTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Search engine ranking', + 'description' => 'Indexes content and tests ranking factors.', + 'group' => 'Search', + ); + } + + /** + * Implementation setUp(). + */ + function setUp() { + parent::setUp('search', 'statistics', 'comment'); + } + + function testRankings() { + // Login with sufficient privileges. + $this->drupalLogin($this->drupalCreateUser(array('skip comment approval', 'create page content'))); + + // Build a list of the rankings to test. + $node_ranks = array('sticky', 'promote', 'relevance', 'recent', 'comments', 'views'); + + // Create nodes for testing. + foreach ($node_ranks as $node_rank) { + $settings = array( + 'type' => 'page', + 'title' => 'Drupal rocks', + 'body' => array(LANGUAGE_NONE => array(array('value' => "Drupal's search rocks"))), + ); + foreach (array(0, 1) as $num) { + if ($num == 1) { + switch ($node_rank) { + case 'sticky': + case 'promote': + $settings[$node_rank] = 1; + break; + case 'relevance': + $settings['body'][LANGUAGE_NONE][0]['value'] .= " really rocks"; + break; + case 'recent': + $settings['created'] = REQUEST_TIME + 3600; + break; + case 'comments': + $settings['comment'] = 2; + break; + } + } + $nodes[$node_rank][$num] = $this->drupalCreateNode($settings); + } + } + + // Update the search index. + module_invoke_all('update_index'); + search_update_totals(); + + // Refresh variables after the treatment. + $this->refreshVariables(); + + // Add a comment to one of the nodes. + $edit = array(); + $edit['subject'] = 'my comment title'; + $edit['comment_body[' . LANGUAGE_NONE . '][0][value]'] = 'some random comment'; + $this->drupalGet('comment/reply/' . $nodes['comments'][1]->nid); + $this->drupalPost(NULL, $edit, t('Preview')); + $this->drupalPost(NULL, $edit, t('Save')); + + // Enable counting of statistics. + variable_set('statistics_count_content_views', 1); + + // Then View one of the nodes a bunch of times. + for ($i = 0; $i < 5; $i ++) { + $this->drupalGet('node/' . $nodes['views'][1]->nid); + } + + // Test each of the possible rankings. + foreach ($node_ranks as $node_rank) { + // Disable all relevancy rankings except the one we are testing. + foreach ($node_ranks as $var) { + variable_set('node_rank_' . $var, $var == $node_rank ? 10 : 0); + } + + // Do the search and assert the results. + $set = node_search_execute('rocks'); + $this->assertEqual($set[0]['node']->nid, $nodes[$node_rank][1]->nid, 'Search ranking "' . $node_rank . '" order.'); + } + } + + /** + * Test rankings of HTML tags. + */ + function testHTMLRankings() { + // Login with sufficient privileges. + $this->drupalLogin($this->drupalCreateUser(array('create page content'))); + + // Test HTML tags with different weights. + $sorted_tags = array('h1', 'h2', 'h3', 'h4', 'a', 'h5', 'h6', 'notag'); + $shuffled_tags = $sorted_tags; + + // Shuffle tags to ensure HTML tags are ranked properly. + shuffle($shuffled_tags); + $settings = array( + 'type' => 'page', + 'title' => 'Simple node', + ); + foreach ($shuffled_tags as $tag) { + switch ($tag) { + case 'a': + $settings['body'] = array(LANGUAGE_NONE => array(array('value' => l('Drupal Rocks', 'node'), 'format' => 'full_html'))); + break; + case 'notag': + $settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'Drupal Rocks'))); + break; + default: + $settings['body'] = array(LANGUAGE_NONE => array(array('value' => "<$tag>Drupal Rocks", 'format' => 'full_html'))); + break; + } + $nodes[$tag] = $this->drupalCreateNode($settings); + } + + // Update the search index. + module_invoke_all('update_index'); + search_update_totals(); + + // Refresh variables after the treatment. + $this->refreshVariables(); + + // Disable all other rankings. + $node_ranks = array('sticky', 'promote', 'recent', 'comments', 'views'); + foreach ($node_ranks as $node_rank) { + variable_set('node_rank_' . $node_rank, 0); + } + $set = node_search_execute('rocks'); + + // Test the ranking of each tag. + foreach ($sorted_tags as $tag_rank => $tag) { + // Assert the results. + if ($tag == 'notag') { + $this->assertEqual($set[$tag_rank]['node']->nid, $nodes[$tag]->nid, 'Search tag ranking for plain text order.'); + } else { + $this->assertEqual($set[$tag_rank]['node']->nid, $nodes[$tag]->nid, 'Search tag ranking for "<' . $sorted_tags[$tag_rank] . '>" order.'); + } + } + + // Test tags with the same weight against the sorted tags. + $unsorted_tags = array('u', 'b', 'i', 'strong', 'em'); + foreach ($unsorted_tags as $tag) { + $settings['body'] = array(LANGUAGE_NONE => array(array('value' => "<$tag>Drupal Rocks", 'format' => 'full_html'))); + $node = $this->drupalCreateNode($settings); + + // Update the search index. + module_invoke_all('update_index'); + search_update_totals(); + + // Refresh variables after the treatment. + $this->refreshVariables(); + + $set = node_search_execute('rocks'); + + // Ranking should always be second to last. + $set = array_slice($set, -2, 1); + + // Assert the results. + $this->assertEqual($set[0]['node']->nid, $node->nid, 'Search tag ranking for "<' . $tag . '>" order.'); + + // Delete node so it doesn't show up in subsequent search results. + node_delete($node->nid); + } + } + + /** + * Verifies that if we combine two rankings, search still works. + * + * See issue http://drupal.org/node/771596 + */ + function testDoubleRankings() { + // Login with sufficient privileges. + $this->drupalLogin($this->drupalCreateUser(array('skip comment approval', 'create page content'))); + + // See testRankings() above - build a node that will rank high for sticky. + $settings = array( + 'type' => 'page', + 'title' => 'Drupal rocks', + 'body' => array(LANGUAGE_NONE => array(array('value' => "Drupal's search rocks"))), + 'sticky' => 1, + ); + + $node = $this->drupalCreateNode($settings); + + // Update the search index. + module_invoke_all('update_index'); + search_update_totals(); + + // Refresh variables after the treatment. + $this->refreshVariables(); + + // Set up for ranking sticky and lots of comments; make sure others are + // disabled. + $node_ranks = array('sticky', 'promote', 'relevance', 'recent', 'comments', 'views'); + foreach ($node_ranks as $var) { + $value = ($var == 'sticky' || $var == 'comments') ? 10 : 0; + variable_set('node_rank_' . $var, $value); + } + + // Do the search and assert the results. + $set = node_search_execute('rocks'); + $this->assertEqual($set[0]['node']->nid, $node->nid, 'Search double ranking order.'); + } +} + +/** + * Tests the rendering of the search block. + */ +class SearchBlockTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Block availability', + 'description' => 'Check if the search form block is available.', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search'); + + // Create and login user + $admin_user = $this->drupalCreateUser(array('administer blocks', 'search content')); + $this->drupalLogin($admin_user); + } + + function testSearchFormBlock() { + // Set block title to confirm that the interface is available. + $this->drupalPost('admin/structure/block/manage/search/form/configure', array('title' => $this->randomName(8)), t('Save block')); + $this->assertText(t('The block configuration has been saved.'), 'Block configuration set.'); + + // Set the block to a region to confirm block is available. + $edit = array(); + $edit['blocks[search_form][region]'] = 'footer'; + $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); + $this->assertText(t('The block settings have been updated.'), 'Block successfully move to footer region.'); + } + + /** + * Test that the search block form works correctly. + */ + function testBlock() { + // Enable the block, and place it in the 'content' region so that it isn't + // hidden on 404 pages. + $edit = array('blocks[search_form][region]' => 'content'); + $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); + + // Test a normal search via the block form, from the front page. + $terms = array('search_block_form' => 'test'); + $this->drupalPost('node', $terms, t('Search')); + $this->assertText('Your search yielded no results'); + + // Test a search from the block on a 404 page. + $this->drupalGet('foo'); + $this->assertResponse(404); + $this->drupalPost(NULL, $terms, t('Search')); + $this->assertResponse(200); + $this->assertText('Your search yielded no results'); + + // Test a search from the block when it doesn't appear on the search page. + $edit = array('pages' => 'search'); + $this->drupalPost('admin/structure/block/manage/search/form/configure', $edit, t('Save block')); + $this->drupalPost('node', $terms, t('Search')); + $this->assertText('Your search yielded no results'); + + // Confirm that the user is redirected to the search page. + $this->assertEqual( + $this->getUrl(), + url('search/node/' . $terms['search_block_form'], array('absolute' => TRUE)), + 'Redirected to correct url.' + ); + + // Test an empty search via the block form, from the front page. + $terms = array('search_block_form' => ''); + $this->drupalPost('node', $terms, t('Search')); + $this->assertText('Please enter some keywords'); + + // Confirm that the user is redirected to the search page, when form is submitted empty. + $this->assertEqual( + $this->getUrl(), + url('search/node/', array('absolute' => TRUE)), + 'Redirected to correct url.' + ); + + // Test that after entering a too-short keyword in the form, you can then + // search again with a longer keyword. First test using the block form. + $terms = array('search_block_form' => 'a'); + $this->drupalPost('node', $terms, t('Search')); + $this->assertText('You must include at least one positive keyword with 3 characters or more'); + $terms = array('search_block_form' => 'foo'); + $this->drupalPost(NULL, $terms, t('Search')); + $this->assertNoText('You must include at least one positive keyword with 3 characters or more'); + $this->assertText('Your search yielded no results'); + + // Same test again, using the search page form for the second search this time. + $terms = array('search_block_form' => 'a'); + $this->drupalPost('node', $terms, t('Search')); + $terms = array('keys' => 'foo'); + $this->drupalPost(NULL, $terms, t('Search')); + $this->assertNoText('You must include at least one positive keyword with 3 characters or more'); + $this->assertText('Your search yielded no results'); + } +} + +/** + * Tests that searching for a phrase gets the correct page count. + */ +class SearchExactTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Search engine phrase queries', + 'description' => 'Tests that searching for a phrase gets the correct page count.', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search'); + } + + /** + * Tests that the correct number of pager links are found for both keywords and phrases. + */ + function testExactQuery() { + // Login with sufficient privileges. + $this->drupalLogin($this->drupalCreateUser(array('create page content', 'search content'))); + + $settings = array( + 'type' => 'page', + 'title' => 'Simple Node', + ); + // Create nodes with exact phrase. + for ($i = 0; $i <= 17; $i++) { + $settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'love pizza'))); + $this->drupalCreateNode($settings); + } + // Create nodes containing keywords. + for ($i = 0; $i <= 17; $i++) { + $settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'love cheesy pizza'))); + $this->drupalCreateNode($settings); + } + + // Update the search index. + module_invoke_all('update_index'); + search_update_totals(); + + // Refresh variables after the treatment. + $this->refreshVariables(); + + // Test that the correct number of pager links are found for keyword search. + $edit = array('keys' => 'love pizza'); + $this->drupalPost('search/node', $edit, t('Search')); + $this->assertLinkByHref('page=1', 0, '2nd page link is found for keyword search.'); + $this->assertLinkByHref('page=2', 0, '3rd page link is found for keyword search.'); + $this->assertLinkByHref('page=3', 0, '4th page link is found for keyword search.'); + $this->assertNoLinkByHref('page=4', '5th page link is not found for keyword search.'); + + // Test that the correct number of pager links are found for exact phrase search. + $edit = array('keys' => '"love pizza"'); + $this->drupalPost('search/node', $edit, t('Search')); + $this->assertLinkByHref('page=1', 0, '2nd page link is found for exact phrase search.'); + $this->assertNoLinkByHref('page=2', '3rd page link is not found for exact phrase search.'); + } +} + +/** + * Test integration searching comments. + */ +class SearchCommentTestCase extends DrupalWebTestCase { + protected $admin_user; + protected $comment_subject; + protected $admin_role; + protected $node; + + public static function getInfo() { + return array( + 'name' => 'Comment Search tests', + 'description' => 'Test integration searching comments.', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('comment', 'search'); + + // Create and log in an administrative user having access to the Full HTML + // text format. + $full_html_format = filter_format_load('full_html'); + $permissions = array( + 'administer filters', + filter_permission_name($full_html_format), + 'administer permissions', + 'create page content', + 'skip comment approval', + 'access comments', + ); + $this->admin_user = $this->drupalCreateUser($permissions); + $this->drupalLogin($this->admin_user); + } + + /** + * Verify that comments are rendered using proper format in search results. + */ + function testSearchResultsComment() { + $comment_body = 'Test comment body'; + + variable_set('comment_preview_article', DRUPAL_OPTIONAL); + // Enable check_plain() for 'Filtered HTML' text format. + $filtered_html_format_id = 'filtered_html'; + $edit = array( + 'filters[filter_html_escape][status]' => TRUE, + ); + $this->drupalPost('admin/config/content/formats/' . $filtered_html_format_id, $edit, t('Save configuration')); + // Allow anonymous users to search content. + $edit = array( + DRUPAL_ANONYMOUS_RID . '[search content]' => 1, + DRUPAL_ANONYMOUS_RID . '[access comments]' => 1, + DRUPAL_ANONYMOUS_RID . '[post comments]' => 1, + ); + $this->drupalPost('admin/people/permissions', $edit, t('Save permissions')); + + // Create a node. + $node = $this->drupalCreateNode(array('type' => 'article')); + // Post a comment using 'Full HTML' text format. + $edit_comment = array(); + $edit_comment['subject'] = 'Test comment subject'; + $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]'] = '

    ' . $comment_body . '

    '; + $full_html_format_id = 'full_html'; + $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][format]'] = $full_html_format_id; + $this->drupalPost('comment/reply/' . $node->nid, $edit_comment, t('Save')); + + // Invoke search index update. + $this->drupalLogout(); + $this->cronRun(); + + // Search for the comment subject. + $edit = array( + 'search_block_form' => "'" . $edit_comment['subject'] . "'", + ); + $this->drupalPost('', $edit, t('Search')); + $this->assertText($node->title, 'Node found in search results.'); + $this->assertText($edit_comment['subject'], 'Comment subject found in search results.'); + + // Search for the comment body. + $edit = array( + 'search_block_form' => "'" . $comment_body . "'", + ); + $this->drupalPost('', $edit, t('Search')); + $this->assertText($node->title, 'Node found in search results.'); + + // Verify that comment is rendered using proper format. + $this->assertText($comment_body, 'Comment body text found in search results.'); + $this->assertNoRaw(t('n/a'), 'HTML in comment body is not hidden.'); + $this->assertNoRaw(check_plain($edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]']), 'HTML in comment body is not escaped.'); + + // Hide comments. + $this->drupalLogin($this->admin_user); + $node->comment = 0; + node_save($node); + + // Invoke search index update. + $this->drupalLogout(); + $this->cronRun(); + + // Search for $title. + $this->drupalPost('', $edit, t('Search')); + $this->assertNoText($comment_body, 'Comment body text not found in search results.'); + } + + /** + * Verify access rules for comment indexing with different permissions. + */ + function testSearchResultsCommentAccess() { + $comment_body = 'Test comment body'; + $this->comment_subject = 'Test comment subject'; + $this->admin_role = $this->admin_user->roles; + unset($this->admin_role[DRUPAL_AUTHENTICATED_RID]); + $this->admin_role = key($this->admin_role); + + // Create a node. + variable_set('comment_preview_article', DRUPAL_OPTIONAL); + $this->node = $this->drupalCreateNode(array('type' => 'article')); + + // Post a comment using 'Full HTML' text format. + $edit_comment = array(); + $edit_comment['subject'] = $this->comment_subject; + $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]'] = '

    ' . $comment_body . '

    '; + $this->drupalPost('comment/reply/' . $this->node->nid, $edit_comment, t('Save')); + + $this->drupalLogout(); + $this->setRolePermissions(DRUPAL_ANONYMOUS_RID); + $this->checkCommentAccess('Anon user has search permission but no access comments permission, comments should not be indexed'); + + $this->setRolePermissions(DRUPAL_ANONYMOUS_RID, TRUE); + $this->checkCommentAccess('Anon user has search permission and access comments permission, comments should be indexed', TRUE); + + $this->drupalLogin($this->admin_user); + $this->drupalGet('admin/people/permissions'); + + // Disable search access for authenticated user to test admin user. + $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, FALSE, FALSE); + + $this->setRolePermissions($this->admin_role); + $this->checkCommentAccess('Admin user has search permission but no access comments permission, comments should not be indexed'); + + $this->setRolePermissions($this->admin_role, TRUE); + $this->checkCommentAccess('Admin user has search permission and access comments permission, comments should be indexed', TRUE); + + $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID); + $this->checkCommentAccess('Authenticated user has search permission but no access comments permission, comments should not be indexed'); + + $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE); + $this->checkCommentAccess('Authenticated user has search permission and access comments permission, comments should be indexed', TRUE); + + // Verify that access comments permission is inherited from the + // authenticated role. + $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE, FALSE); + $this->setRolePermissions($this->admin_role); + $this->checkCommentAccess('Admin user has search permission and no access comments permission, but comments should be indexed because admin user inherits authenticated user\'s permission to access comments', TRUE); + + // Verify that search content permission is inherited from the authenticated + // role. + $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE, TRUE); + $this->setRolePermissions($this->admin_role, TRUE, FALSE); + $this->checkCommentAccess('Admin user has access comments permission and no search permission, but comments should be indexed because admin user inherits authenticated user\'s permission to search', TRUE); + + } + + /** + * Set permissions for role. + */ + function setRolePermissions($rid, $access_comments = FALSE, $search_content = TRUE) { + $permissions = array( + 'access comments' => $access_comments, + 'search content' => $search_content, + ); + user_role_change_permissions($rid, $permissions); + } + + /** + * Update search index and search for comment. + */ + function checkCommentAccess($message, $assume_access = FALSE) { + // Invoke search index update. + search_touch_node($this->node->nid); + $this->cronRun(); + + // Search for the comment subject. + $edit = array( + 'search_block_form' => "'" . $this->comment_subject . "'", + ); + $this->drupalPost('', $edit, t('Search')); + $method = $assume_access ? 'assertText' : 'assertNoText'; + $verb = $assume_access ? 'found' : 'not found'; + $this->{$method}($this->node->title, "Node $verb in search results: " . $message); + $this->{$method}($this->comment_subject, "Comment subject $verb in search results: " . $message); + } + + /** + * Verify that 'add new comment' does not appear in search results or index. + */ + function testAddNewComment() { + // Create a node with a short body. + $settings = array( + 'type' => 'article', + 'title' => 'short title', + 'body' => array(LANGUAGE_NONE => array(array('value' => 'short body text'))), + ); + + $user = $this->drupalCreateUser(array('search content', 'create article content', 'access content')); + $this->drupalLogin($user); + + $node = $this->drupalCreateNode($settings); + // Verify that if you view the node on its own page, 'add new comment' + // is there. + $this->drupalGet('node/' . $node->nid); + $this->assertText(t('Add new comment'), 'Add new comment appears on node page'); + + // Run cron to index this page. + $this->drupalLogout(); + $this->cronRun(); + + // Search for 'comment'. Should be no results. + $this->drupalLogin($user); + $this->drupalPost('search/node', array('keys' => 'comment'), t('Search')); + $this->assertText(t('Your search yielded no results'), 'No results searching for the word comment'); + + // Search for the node title. Should be found, and 'Add new comment' should + // not be part of the search snippet. + $this->drupalPost('search/node', array('keys' => 'short'), t('Search')); + $this->assertText($node->title, 'Search for keyword worked'); + $this->assertNoText(t('Add new comment'), 'Add new comment does not appear on search results page'); + } + +} + +/** + * Tests search_expression_insert() and search_expression_extract(). + * + * @see http://drupal.org/node/419388 (issue) + */ +class SearchExpressionInsertExtractTestCase extends DrupalUnitTestCase { + public static function getInfo() { + return array( + 'name' => 'Search expression insert/extract', + 'description' => 'Tests the functions search_expression_insert() and search_expression_extract()', + 'group' => 'Search', + ); + } + + function setUp() { + drupal_load('module', 'search'); + parent::setUp(); + } + + /** + * Tests search_expression_insert() and search_expression_extract(). + */ + function testInsertExtract() { + $base_expression = "mykeyword"; + // Build an array of option, value, what should be in the expression, what + // should be retrieved from expression. + $cases = array( + array('foo', 'bar', 'foo:bar', 'bar'), // Normal case. + array('foo', NULL, '', NULL), // Empty value: shouldn't insert. + array('foo', ' ', 'foo:', ''), // Space as value: should insert but retrieve empty string. + array('foo', '', 'foo:', ''), // Empty string as value: should insert but retrieve empty string. + array('foo', '0', 'foo:0', '0'), // String zero as value: should insert. + array('foo', 0, 'foo:0', '0'), // Numeric zero as value: should insert. + ); + + foreach ($cases as $index => $case) { + $after_insert = search_expression_insert($base_expression, $case[0], $case[1]); + if (empty($case[2])) { + $this->assertEqual($after_insert, $base_expression, "Empty insert does not change expression in case $index"); + } + else { + $this->assertEqual($after_insert, $base_expression . ' ' . $case[2], "Insert added correct expression for case $index"); + } + + $retrieved = search_expression_extract($after_insert, $case[0]); + if (!isset($case[3])) { + $this->assertFalse(isset($retrieved), "Empty retrieval results in unset value in case $index"); + } + else { + $this->assertEqual($retrieved, $case[3], "Value is retrieved for case $index"); + } + + $after_clear = search_expression_insert($after_insert, $case[0]); + $this->assertEqual(trim($after_clear), $base_expression, "After clearing, base expression is restored for case $index"); + + $cleared = search_expression_extract($after_clear, $case[0]); + $this->assertFalse(isset($cleared), "After clearing, value could not be retrieved for case $index"); + } + } +} + +/** + * Tests that comment count display toggles properly on comment status of node + * + * Issue 537278 + * + * - Nodes with comment status set to Open should always how comment counts + * - Nodes with comment status set to Closed should show comment counts + * only when there are comments + * - Nodes with comment status set to Hidden should never show comment counts + */ +class SearchCommentCountToggleTestCase extends DrupalWebTestCase { + protected $searching_user; + protected $searchable_nodes; + + public static function getInfo() { + return array( + 'name' => 'Comment count toggle', + 'description' => 'Verify that comment count display toggles properly on comment status of node.', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search'); + + // Create searching user. + $this->searching_user = $this->drupalCreateUser(array('search content', 'access content', 'access comments', 'skip comment approval')); + + // Create initial nodes. + $node_params = array('type' => 'article', 'body' => array(LANGUAGE_NONE => array(array('value' => 'SearchCommentToggleTestCase')))); + + $this->searchable_nodes['1 comment'] = $this->drupalCreateNode($node_params); + $this->searchable_nodes['0 comments'] = $this->drupalCreateNode($node_params); + + // Login with sufficient privileges. + $this->drupalLogin($this->searching_user); + + // Create a comment array + $edit_comment = array(); + $edit_comment['subject'] = $this->randomName(); + $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]'] = $this->randomName(); + $filtered_html_format_id = 'filtered_html'; + $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][format]'] = $filtered_html_format_id; + + // Post comment to the test node with comment + $this->drupalPost('comment/reply/' . $this->searchable_nodes['1 comment']->nid, $edit_comment, t('Save')); + + // First update the index. This does the initial processing. + node_update_index(); + + // Then, run the shutdown function. Testing is a unique case where indexing + // and searching has to happen in the same request, so running the shutdown + // function manually is needed to finish the indexing process. + search_update_totals(); + } + + /** + * Verify that comment count display toggles properly on comment status of node + */ + function testSearchCommentCountToggle() { + // Search for the nodes by string in the node body. + $edit = array( + 'search_block_form' => "'SearchCommentToggleTestCase'", + ); + + // Test comment count display for nodes with comment status set to Open + $this->drupalPost('', $edit, t('Search')); + $this->assertText(t('0 comments'), 'Empty comment count displays for nodes with comment status set to Open'); + $this->assertText(t('1 comment'), 'Non-empty comment count displays for nodes with comment status set to Open'); + + // Test comment count display for nodes with comment status set to Closed + $this->searchable_nodes['0 comments']->comment = COMMENT_NODE_CLOSED; + node_save($this->searchable_nodes['0 comments']); + $this->searchable_nodes['1 comment']->comment = COMMENT_NODE_CLOSED; + node_save($this->searchable_nodes['1 comment']); + + $this->drupalPost('', $edit, t('Search')); + $this->assertNoText(t('0 comments'), 'Empty comment count does not display for nodes with comment status set to Closed'); + $this->assertText(t('1 comment'), 'Non-empty comment count displays for nodes with comment status set to Closed'); + + // Test comment count display for nodes with comment status set to Hidden + $this->searchable_nodes['0 comments']->comment = COMMENT_NODE_HIDDEN; + node_save($this->searchable_nodes['0 comments']); + $this->searchable_nodes['1 comment']->comment = COMMENT_NODE_HIDDEN; + node_save($this->searchable_nodes['1 comment']); + + $this->drupalPost('', $edit, t('Search')); + $this->assertNoText(t('0 comments'), 'Empty comment count does not display for nodes with comment status set to Hidden'); + $this->assertNoText(t('1 comment'), 'Non-empty comment count does not display for nodes with comment status set to Hidden'); + } +} + +/** + * Test search_simplify() on every Unicode character, and some other cases. + */ +class SearchSimplifyTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Search simplify', + 'description' => 'Check that the search_simply() function works as intended.', + 'group' => 'Search', + ); + } + + /** + * Tests that all Unicode characters simplify correctly. + */ + function testSearchSimplifyUnicode() { + // This test uses a file that was constructed so that the even lines are + // boundary characters, and the odd lines are valid word characters. (It + // was generated as a sequence of all the Unicode characters, and then the + // boundary chararacters (punctuation, spaces, etc.) were split off into + // their own lines). So the even-numbered lines should simplify to nothing, + // and the odd-numbered lines we need to split into shorter chunks and + // verify that simplification doesn't lose any characters. + $input = file_get_contents(DRUPAL_ROOT . '/modules/search/tests/UnicodeTest.txt'); + $basestrings = explode(chr(10), $input); + $strings = array(); + foreach ($basestrings as $key => $string) { + if ($key %2) { + // Even line - should simplify down to a space. + $simplified = search_simplify($string); + $this->assertIdentical($simplified, ' ', "Line $key is excluded from the index"); + } + else { + // Odd line, should be word characters. + // Split this into 30-character chunks, so we don't run into limits + // of truncation in search_simplify(). + $start = 0; + while ($start < drupal_strlen($string)) { + $newstr = drupal_substr($string, $start, 30); + // Special case: leading zeros are removed from numeric strings, + // and there's one string in this file that is numbers starting with + // zero, so prepend a 1 on that string. + if (preg_match('/^[0-9]+$/', $newstr)) { + $newstr = '1' . $newstr; + } + $strings[] = $newstr; + $start += 30; + } + } + } + foreach ($strings as $key => $string) { + $simplified = search_simplify($string); + $this->assertTrue(drupal_strlen($simplified) >= drupal_strlen($string), "Nothing is removed from string $key."); + } + + // Test the low-numbered ASCII control characters separately. They are not + // in the text file because they are problematic for diff, especially \0. + $string = ''; + for ($i = 0; $i < 32; $i++) { + $string .= chr($i); + } + $this->assertIdentical(' ', search_simplify($string), 'Search simplify works for ASCII control characters.'); + } + + /** + * Tests that search_simplify() does the right thing with punctuation. + */ + function testSearchSimplifyPunctuation() { + $cases = array( + array('20.03/94-28,876', '20039428876', 'Punctuation removed from numbers'), + array('great...drupal--module', 'great drupal module', 'Multiple dot and dashes are word boundaries'), + array('very_great-drupal.module', 'verygreatdrupalmodule', 'Single dot, dash, underscore are removed'), + array('regular,punctuation;word', 'regular punctuation word', 'Punctuation is a word boundary'), + ); + + foreach ($cases as $case) { + $out = trim(search_simplify($case[0])); + $this->assertEqual($out, $case[1], $case[2]); + } + } +} + + +/** + * Tests keywords and conditions. + */ +class SearchKeywordsConditions extends DrupalWebTestCase { + protected $searching_user; + + public static function getInfo() { + return array( + 'name' => 'Keywords and conditions', + 'description' => 'Verify the search pulls in keywords and extra conditions.', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search', 'search_extra_type'); + // Create searching user. + $this->searching_user = $this->drupalCreateUser(array('search content', 'access content', 'access comments', 'skip comment approval')); + // Login with sufficient privileges. + $this->drupalLogin($this->searching_user); + // Test with all search modules enabled. + variable_set('search_active_modules', array('node' => 'node', 'user' => 'user', 'search_extra_type' => 'search_extra_type')); + menu_rebuild(); + } + + /** + * Verify the kewords are captured and conditions respected. + */ + function testSearchKeyswordsConditions() { + // No keys, not conditions - no results. + $this->drupalGet('search/dummy_path'); + $this->assertNoText('Dummy search snippet to display'); + // With keys - get results. + $keys = 'bike shed ' . $this->randomName(); + $this->drupalGet("search/dummy_path/{$keys}"); + $this->assertText("Dummy search snippet to display. Keywords: {$keys}"); + $keys = 'blue drop ' . $this->randomName(); + $this->drupalGet("search/dummy_path", array('query' => array('keys' => $keys))); + $this->assertText("Dummy search snippet to display. Keywords: {$keys}"); + // Add some conditions and keys. + $keys = 'moving drop ' . $this->randomName(); + $this->drupalGet("search/dummy_path/bike", array('query' => array('search_conditions' => $keys))); + $this->assertText("Dummy search snippet to display."); + $this->assertRaw(print_r(array('search_conditions' => $keys), TRUE)); + // Add some conditions and no keys. + $keys = 'drop kick ' . $this->randomName(); + $this->drupalGet("search/dummy_path", array('query' => array('search_conditions' => $keys))); + $this->assertText("Dummy search snippet to display."); + $this->assertRaw(print_r(array('search_conditions' => $keys), TRUE)); + } +} + +/** + * Tests that numbers can be searched. + */ +class SearchNumbersTestCase extends DrupalWebTestCase { + protected $test_user; + protected $numbers; + protected $nodes; + + public static function getInfo() { + return array( + 'name' => 'Search numbers', + 'description' => 'Check that numbers can be searched', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search'); + + $this->test_user = $this->drupalCreateUser(array('search content', 'access content', 'administer nodes', 'access site reports')); + $this->drupalLogin($this->test_user); + + // Create content with various numbers in it. + // Note: 50 characters is the current limit of the search index's word + // field. + $this->numbers = array( + 'ISBN' => '978-0446365383', + 'UPC' => '036000 291452', + 'EAN bar code' => '5901234123457', + 'negative' => '-123456.7890', + 'quoted negative' => '"-123456.7890"', + 'leading zero' => '0777777777', + 'tiny' => '111', + 'small' => '22222222222222', + 'medium' => '333333333333333333333333333', + 'large' => '444444444444444444444444444444444444444', + 'gigantic' => '5555555555555555555555555555555555555555555555555', + 'over fifty characters' => '666666666666666666666666666666666666666666666666666666666666', + 'date', '01/02/2009', + 'commas', '987,654,321', + ); + + foreach ($this->numbers as $doc => $num) { + $info = array( + 'body' => array(LANGUAGE_NONE => array(array('value' => $num))), + 'type' => 'page', + 'language' => LANGUAGE_NONE, + 'title' => $doc . ' number', + ); + $this->nodes[$doc] = $this->drupalCreateNode($info); + } + + // Run cron to ensure the content is indexed. + $this->cronRun(); + $this->drupalGet('admin/reports/dblog'); + $this->assertText(t('Cron run completed'), 'Log shows cron run completed'); + } + + /** + * Tests that all the numbers can be searched. + */ + function testNumberSearching() { + $types = array_keys($this->numbers); + + foreach ($types as $type) { + $number = $this->numbers[$type]; + // If the number is negative, remove the - sign, because - indicates + // "not keyword" when searching. + $number = ltrim($number, '-'); + $node = $this->nodes[$type]; + + // Verify that the node title does not appear on the search page + // with a dummy search. + $this->drupalPost('search/node', + array('keys' => 'foo'), + t('Search')); + $this->assertNoText($node->title, $type . ': node title not shown in dummy search'); + + // Verify that the node title does appear as a link on the search page + // when searching for the number. + $this->drupalPost('search/node', + array('keys' => $number), + t('Search')); + $this->assertText($node->title, format_string('%type: node title shown (search found the node) in search for number %number.', array('%type' => $type, '%number' => $number))); + } + } +} + +/** + * Tests that numbers can be searched, with more complex matching. + */ +class SearchNumberMatchingTestCase extends DrupalWebTestCase { + protected $test_user; + protected $numbers; + protected $nodes; + + public static function getInfo() { + return array( + 'name' => 'Search number matching', + 'description' => 'Check that numbers can be searched with more complex matching', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search'); + + $this->test_user = $this->drupalCreateUser(array('search content', 'access content', 'administer nodes', 'access site reports')); + $this->drupalLogin($this->test_user); + + // Define a group of numbers that should all match each other -- + // numbers with internal punctuation should match each other, as well + // as numbers with and without leading zeros and leading/trailing + // . and -. + $this->numbers = array( + '123456789', + '12/34/56789', + '12.3456789', + '12-34-56789', + '123,456,789', + '-123456789', + '0123456789', + ); + + foreach ($this->numbers as $num) { + $info = array( + 'body' => array(LANGUAGE_NONE => array(array('value' => $num))), + 'type' => 'page', + 'language' => LANGUAGE_NONE, + ); + $this->nodes[] = $this->drupalCreateNode($info); + } + + // Run cron to ensure the content is indexed. + $this->cronRun(); + $this->drupalGet('admin/reports/dblog'); + $this->assertText(t('Cron run completed'), 'Log shows cron run completed'); + } + + /** + * Tests that all the numbers can be searched. + */ + function testNumberSearching() { + for ($i = 0; $i < count($this->numbers); $i++) { + $node = $this->nodes[$i]; + + // Verify that the node title does not appear on the search page + // with a dummy search. + $this->drupalPost('search/node', + array('keys' => 'foo'), + t('Search')); + $this->assertNoText($node->title, format_string('%number: node title not shown in dummy search', array('%number' => $i))); + + // Now verify that we can find node i by searching for any of the + // numbers. + for ($j = 0; $j < count($this->numbers); $j++) { + $number = $this->numbers[$j]; + // If the number is negative, remove the - sign, because - indicates + // "not keyword" when searching. + $number = ltrim($number, '-'); + + $this->drupalPost('search/node', + array('keys' => $number), + t('Search')); + $this->assertText($node->title, format_string('%i: node title shown (search found the node) in search for number %number', array('%i' => $i, '%number' => $number))); + } + } + + } +} + +/** + * Test config page. + */ +class SearchConfigSettingsForm extends DrupalWebTestCase { + public $search_user; + public $search_node; + + public static function getInfo() { + return array( + 'name' => 'Config settings form', + 'description' => 'Verify the search config settings form.', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search', 'search_extra_type'); + + // Login as a user that can create and search content. + $this->search_user = $this->drupalCreateUser(array('search content', 'administer search', 'administer nodes', 'bypass node access', 'access user profiles', 'administer users', 'administer blocks', 'access site reports')); + $this->drupalLogin($this->search_user); + + // Add a single piece of content and index it. + $node = $this->drupalCreateNode(); + $this->search_node = $node; + // Link the node to itself to test that it's only indexed once. The content + // also needs the word "pizza" so we can use it as the search keyword. + $langcode = LANGUAGE_NONE; + $body_key = "body[$langcode][0][value]"; + $edit[$body_key] = l($node->title, 'node/' . $node->nid) . ' pizza sandwich'; + $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save')); + + node_update_index(); + search_update_totals(); + + // Enable the search block. + $edit = array(); + $edit['blocks[search_form][region]'] = 'content'; + $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); + } + + /** + * Verify the search settings form. + */ + function testSearchSettingsPage() { + + // Test that the settings form displays the correct count of items left to index. + $this->drupalGet('admin/config/search/settings'); + $this->assertText(t('There are @count items left to index.', array('@count' => 0))); + + // Test the re-index button. + $this->drupalPost('admin/config/search/settings', array(), t('Re-index site')); + $this->assertText(t('Are you sure you want to re-index the site')); + $this->drupalPost('admin/config/search/settings/reindex', array(), t('Re-index site')); + $this->assertText(t('The index will be rebuilt')); + $this->drupalGet('admin/config/search/settings'); + $this->assertText(t('There is 1 item left to index.')); + + // Test that the form saves with the default values. + $this->drupalPost('admin/config/search/settings', array(), t('Save configuration')); + $this->assertText(t('The configuration options have been saved.'), 'Form saves with the default values.'); + + // Test that the form does not save with an invalid word length. + $edit = array( + 'minimum_word_size' => $this->randomName(3), + ); + $this->drupalPost('admin/config/search/settings', $edit, t('Save configuration')); + $this->assertNoText(t('The configuration options have been saved.'), 'Form does not save with an invalid word length.'); + + // Test logging setting. It should be on by default. + $text = $this->randomName(5); + $this->drupalPost('search/node', array('keys' => $text), t('Search')); + $this->drupalGet('admin/reports/dblog'); + $this->assertLink('Searched Content for ' . $text . '.', 0, 'Search was logged'); + + // Turn off logging. + variable_set('search_logging', FALSE); + $text = $this->randomName(5); + $this->drupalPost('search/node', array('keys' => $text), t('Search')); + $this->drupalGet('admin/reports/dblog'); + $this->assertNoLink('Searched Content for ' . $text . '.', 'Search was not logged'); + } + + /** + * Verify that you can disable individual search modules. + */ + function testSearchModuleDisabling() { + // Array of search modules to test: 'path' is the search path, 'title' is + // the tab title, 'keys' are the keywords to search for, and 'text' is + // the text to assert is on the results page. + $module_info = array( + 'node' => array( + 'path' => 'node', + 'title' => 'Content', + 'keys' => 'pizza', + 'text' => $this->search_node->title, + ), + 'user' => array( + 'path' => 'user', + 'title' => 'User', + 'keys' => $this->search_user->name, + 'text' => $this->search_user->mail, + ), + 'search_extra_type' => array( + 'path' => 'dummy_path', + 'title' => 'Dummy search type', + 'keys' => 'foo', + 'text' => 'Dummy search snippet to display', + ), + ); + $modules = array_keys($module_info); + + // Test each module if it's enabled as the only search module. + foreach ($modules as $module) { + // Enable the one module and disable other ones. + $info = $module_info[$module]; + $edit = array(); + foreach ($modules as $other) { + $edit['search_active_modules[' . $other . ']'] = (($other == $module) ? $module : FALSE); + } + $edit['search_default_module'] = $module; + $this->drupalPost('admin/config/search/settings', $edit, t('Save configuration')); + + // Run a search from the correct search URL. + $this->drupalGet('search/' . $info['path'] . '/' . $info['keys']); + $this->assertNoText('no results', $info['title'] . ' search found results'); + $this->assertText($info['text'], 'Correct search text found'); + + // Verify that other module search tab titles are not visible. + foreach ($modules as $other) { + if ($other != $module) { + $title = $module_info[$other]['title']; + $this->assertNoText($title, $title . ' search tab is not shown'); + } + } + + // Run a search from the search block on the node page. Verify you get + // to this module's search results page. + $terms = array('search_block_form' => $info['keys']); + $this->drupalPost('node', $terms, t('Search')); + $this->assertEqual( + $this->getURL(), + url('search/' . $info['path'] . '/' . $info['keys'], array('absolute' => TRUE)), + 'Block redirected to right search page'); + + // Try an invalid search path. Should redirect to our active module. + $this->drupalGet('search/not_a_module_path'); + $this->assertEqual( + $this->getURL(), + url('search/' . $info['path'], array('absolute' => TRUE)), + 'Invalid search path redirected to default search page'); + } + + // Test with all search modules enabled. When you go to the search + // page or run search, all modules should be shown. + $edit = array(); + foreach ($modules as $module) { + $edit['search_active_modules[' . $module . ']'] = $module; + } + $edit['search_default_module'] = 'node'; + + $this->drupalPost('admin/config/search/settings', $edit, t('Save configuration')); + + foreach (array('search/node/pizza', 'search/node') as $path) { + $this->drupalGet($path); + foreach ($modules as $module) { + $title = $module_info[$module]['title']; + $this->assertText($title, format_string('%title search tab is shown', array('%title' => $title))); + } + } + } +} + +/** + * Tests the search_excerpt() function. + */ +class SearchExcerptTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Search excerpt extraction', + 'description' => 'Tests that the search_excerpt() function works.', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search'); + } + + /** + * Tests search_excerpt() with several simulated search keywords. + * + * Passes keywords and a sample marked up string, "The quick + * brown fox jumps over the lazy dog", and compares it to the + * correctly marked up string. The correctly marked up string + * contains either highlighted keywords or the original marked + * up string if no keywords matched the string. + */ + function testSearchExcerpt() { + // Make some text with entities and tags. + $text = 'The quick brown fox & jumps

    over

    the lazy dog'; + // Note: The search_excerpt() function adds some extra spaces -- not + // important for HTML formatting. Remove these for comparison. + $expected = 'The quick brown fox & jumps over the lazy dog'; + $result = preg_replace('| +|', ' ', search_excerpt('nothing', $text)); + $this->assertEqual(preg_replace('| +|', ' ', $result), $expected, 'Entire string is returned when keyword is not found in short string'); + + $result = preg_replace('| +|', ' ', search_excerpt('fox', $text)); + $this->assertEqual($result, 'The quick brown fox & jumps over the lazy dog ...', 'Found keyword is highlighted'); + + $longtext = str_repeat($text . ' ', 10); + $result = preg_replace('| +|', ' ', search_excerpt('nothing', $longtext)); + $this->assertTrue(strpos($result, $expected) === 0, 'When keyword is not found in long string, return value starts as expected'); + + $entities = str_repeat('készítése ', 20); + $result = preg_replace('| +|', ' ', search_excerpt('nothing', $entities)); + $this->assertFalse(strpos($result, '&'), 'Entities are not present in excerpt'); + $this->assertTrue(strpos($result, 'í') > 0, 'Entities are converted in excerpt'); + + // The node body that will produce this rendered $text is: + // 123456789 HTMLTest +123456789+‘ +‘ +‘ +‘ +12345678    +‘ +‘ +‘ ‘ + $text = "

    123456789 HTMLTest +123456789+‘ +‘ +‘ +‘ +12345678    +‘ +‘ +‘ ‘

    \n
    "; + $result = search_excerpt('HTMLTest', $text); + $this->assertFalse(empty($result), 'Rendered Multi-byte HTML encodings are not corrupted in search excerpts'); + } + + /** + * Tests search_excerpt() with search keywords matching simplified words. + * + * Excerpting should handle keywords that are matched only after going through + * search_simplify(). This test passes keywords that match simplified words + * and compares them with strings that contain the original unsimplified word. + */ + function testSearchExcerptSimplified() { + $lorem1 = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vitae arcu at leo cursus laoreet. Curabitur dui tortor, adipiscing malesuada tempor in, bibendum ac diam. Cras non tellus a libero pellentesque condimentum. What is a Drupalism? Suspendisse ac lacus libero. Ut non est vel nisl faucibus interdum nec sed leo. Pellentesque sem risus, vulputate eu semper eget, auctor in libero.'; + $lorem2 = 'Ut fermentum est vitae metus convallis scelerisque. Phasellus pellentesque rhoncus tellus, eu dignissim purus posuere id. Quisque eu fringilla ligula. Morbi ullamcorper, lorem et mattis egestas, tortor neque pretium velit, eget eleifend odio turpis eu purus. Donec vitae metus quis leo pretium tincidunt a pulvinar sem. Morbi adipiscing laoreet mauris vel placerat. Nullam elementum, nisl sit amet scelerisque malesuada, dolor nunc hendrerit quam, eu ultrices erat est in orci.'; + + // Make some text with some keywords that will get simplified. + $text = $lorem1 . ' Number: 123456.7890 Hyphenated: one-two abc,def ' . $lorem2; + // Note: The search_excerpt() function adds some extra spaces -- not + // important for HTML formatting. Remove these for comparison. + $result = preg_replace('| +|', ' ', search_excerpt('123456.7890', $text)); + $this->assertTrue(strpos($result, 'Number: 123456.7890') !== FALSE, 'Numeric keyword is highlighted with exact match'); + + $result = preg_replace('| +|', ' ', search_excerpt('1234567890', $text)); + $this->assertTrue(strpos($result, 'Number: 123456.7890') !== FALSE, 'Numeric keyword is highlighted with simplified match'); + + $result = preg_replace('| +|', ' ', search_excerpt('Number 1234567890', $text)); + $this->assertTrue(strpos($result, 'Number: 123456.7890') !== FALSE, 'Punctuated and numeric keyword is highlighted with simplified match'); + + $result = preg_replace('| +|', ' ', search_excerpt('"Number 1234567890"', $text)); + $this->assertTrue(strpos($result, 'Number: 123456.7890') !== FALSE, 'Phrase with punctuated and numeric keyword is highlighted with simplified match'); + + $result = preg_replace('| +|', ' ', search_excerpt('"Hyphenated onetwo"', $text)); + $this->assertTrue(strpos($result, 'Hyphenated: one-two') !== FALSE, 'Phrase with punctuated and hyphenated keyword is highlighted with simplified match'); + + $result = preg_replace('| +|', ' ', search_excerpt('"abc def"', $text)); + $this->assertTrue(strpos($result, 'abc,def') !== FALSE, 'Phrase with keyword simplified into two separate words is highlighted with simplified match'); + + // Test phrases with characters which are being truncated. + $result = preg_replace('| +|', ' ', search_excerpt('"ipsum _"', $text)); + $this->assertTrue(strpos($result, 'ipsum ') !== FALSE, 'Only valid part of the phrase is highlighted and invalid part containing "_" is ignored.'); + + $result = preg_replace('| +|', ' ', search_excerpt('"ipsum 0000"', $text)); + $this->assertTrue(strpos($result, 'ipsum ') !== FALSE, 'Only valid part of the phrase is highlighted and invalid part "0000" is ignored.'); + + // Test combination of the valid keyword and keyword containing only + // characters which are being truncated during simplification. + $result = preg_replace('| +|', ' ', search_excerpt('ipsum _', $text)); + $this->assertTrue(strpos($result, 'ipsum') !== FALSE, 'Only valid keyword is highlighted and invalid keyword "_" is ignored.'); + + $result = preg_replace('| +|', ' ', search_excerpt('ipsum 0000', $text)); + $this->assertTrue(strpos($result, 'ipsum') !== FALSE, 'Only valid keyword is highlighted and invalid keyword "0000" is ignored.'); + } +} + +/** + * Test the CJK tokenizer. + */ +class SearchTokenizerTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'CJK tokenizer', + 'description' => 'Check that CJK tokenizer works as intended.', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search'); + } + + /** + * Verifies that strings of CJK characters are tokenized. + * + * The search_simplify() function does special things with numbers, symbols, + * and punctuation. So we only test that CJK characters that are not in these + * character classes are tokenized properly. See PREG_CLASS_CKJ for more + * information. + */ + function testTokenizer() { + // Set the minimum word size to 1 (to split all CJK characters) and make + // sure CJK tokenizing is turned on. + variable_set('minimum_word_size', 1); + variable_set('overlap_cjk', TRUE); + $this->refreshVariables(); + + // Create a string of CJK characters from various character ranges in + // the Unicode tables. + + // Beginnings of the character ranges. + $starts = array( + 'CJK unified' => 0x4e00, + 'CJK Ext A' => 0x3400, + 'CJK Compat' => 0xf900, + 'Hangul Jamo' => 0x1100, + 'Hangul Ext A' => 0xa960, + 'Hangul Ext B' => 0xd7b0, + 'Hangul Compat' => 0x3131, + 'Half non-punct 1' => 0xff21, + 'Half non-punct 2' => 0xff41, + 'Half non-punct 3' => 0xff66, + 'Hangul Syllables' => 0xac00, + 'Hiragana' => 0x3040, + 'Katakana' => 0x30a1, + 'Katakana Ext' => 0x31f0, + 'CJK Reserve 1' => 0x20000, + 'CJK Reserve 2' => 0x30000, + 'Bomofo' => 0x3100, + 'Bomofo Ext' => 0x31a0, + 'Lisu' => 0xa4d0, + 'Yi' => 0xa000, + ); + + // Ends of the character ranges. + $ends = array( + 'CJK unified' => 0x9fcf, + 'CJK Ext A' => 0x4dbf, + 'CJK Compat' => 0xfaff, + 'Hangul Jamo' => 0x11ff, + 'Hangul Ext A' => 0xa97f, + 'Hangul Ext B' => 0xd7ff, + 'Hangul Compat' => 0x318e, + 'Half non-punct 1' => 0xff3a, + 'Half non-punct 2' => 0xff5a, + 'Half non-punct 3' => 0xffdc, + 'Hangul Syllables' => 0xd7af, + 'Hiragana' => 0x309f, + 'Katakana' => 0x30ff, + 'Katakana Ext' => 0x31ff, + 'CJK Reserve 1' => 0x2fffd, + 'CJK Reserve 2' => 0x3fffd, + 'Bomofo' => 0x312f, + 'Bomofo Ext' => 0x31b7, + 'Lisu' => 0xa4fd, + 'Yi' => 0xa48f, + ); + + // Generate characters consisting of starts, midpoints, and ends. + $chars = array(); + $charcodes = array(); + foreach ($starts as $key => $value) { + $charcodes[] = $starts[$key]; + $chars[] = $this->code2utf($starts[$key]); + $mid = round(0.5 * ($starts[$key] + $ends[$key])); + $charcodes[] = $mid; + $chars[] = $this->code2utf($mid); + $charcodes[] = $ends[$key]; + $chars[] = $this->code2utf($ends[$key]); + } + + // Merge into a string and tokenize. + $string = implode('', $chars); + $out = trim(search_simplify($string)); + $expected = drupal_strtolower(implode(' ', $chars)); + + // Verify that the output matches what we expect. + $this->assertEqual($out, $expected, 'CJK tokenizer worked on all supplied CJK characters'); + } + + /** + * Verifies that strings of non-CJK characters are not tokenized. + * + * This is just a sanity check - it verifies that strings of letters are + * not tokenized. + */ + function testNoTokenizer() { + // Set the minimum word size to 1 (to split all CJK characters) and make + // sure CJK tokenizing is turned on. + variable_set('minimum_word_size', 1); + variable_set('overlap_cjk', TRUE); + $this->refreshVariables(); + + $letters = 'abcdefghijklmnopqrstuvwxyz'; + $out = trim(search_simplify($letters)); + + $this->assertEqual($letters, $out, 'Letters are not CJK tokenized'); + } + + /** + * Like PHP chr() function, but for unicode characters. + * + * chr() only works for ASCII characters up to character 255. This function + * converts a number to the corresponding unicode character. Adapted from + * functions supplied in comments on several functions on php.net. + */ + function code2utf($num) { + if ($num < 128) { + return chr($num); + } + + if ($num < 2048) { + return chr(($num >> 6) + 192) . chr(($num & 63) + 128); + } + + if ($num < 65536) { + return chr(($num >> 12) + 224) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128); + } + + if ($num < 2097152) { + return chr(($num >> 18) + 240) . chr((($num >> 12) & 63) + 128) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128); + } + + return ''; + } +} + +/** + * Tests that we can embed a form in search results and submit it. + */ +class SearchEmbedForm extends DrupalWebTestCase { + /** + * Node used for testing. + */ + public $node; + + /** + * Count of how many times the form has been submitted. + */ + public $submit_count = 0; + + public static function getInfo() { + return array( + 'name' => 'Embedded forms', + 'description' => 'Verifies that a form embedded in search results works', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search', 'search_embedded_form'); + + // Create a user and a node, and update the search index. + $test_user = $this->drupalCreateUser(array('access content', 'search content', 'administer nodes')); + $this->drupalLogin($test_user); + + $this->node = $this->drupalCreateNode(); + + node_update_index(); + search_update_totals(); + + // Set up a dummy initial count of times the form has been submitted. + $this->submit_count = 12; + variable_set('search_embedded_form_submitted', $this->submit_count); + $this->refreshVariables(); + } + + /** + * Tests that the embedded form appears and can be submitted. + */ + function testEmbeddedForm() { + // First verify we can submit the form from the module's page. + $this->drupalPost('search_embedded_form', + array('name' => 'John'), + t('Send away')); + $this->assertText(t('Test form was submitted'), 'Form message appears'); + $count = variable_get('search_embedded_form_submitted', 0); + $this->assertEqual($this->submit_count + 1, $count, 'Form submission count is correct'); + $this->submit_count = $count; + + // Now verify that we can see and submit the form from the search results. + $this->drupalGet('search/node/' . $this->node->title); + $this->assertText(t('Your name'), 'Form is visible'); + $this->drupalPost('search/node/' . $this->node->title, + array('name' => 'John'), + t('Send away')); + $this->assertText(t('Test form was submitted'), 'Form message appears'); + $count = variable_get('search_embedded_form_submitted', 0); + $this->assertEqual($this->submit_count + 1, $count, 'Form submission count is correct'); + $this->submit_count = $count; + + // Now verify that if we submit the search form, it doesn't count as + // our form being submitted. + $this->drupalPost('search', + array('keys' => 'foo'), + t('Search')); + $this->assertNoText(t('Test form was submitted'), 'Form message does not appear'); + $count = variable_get('search_embedded_form_submitted', 0); + $this->assertEqual($this->submit_count, $count, 'Form submission count is correct'); + $this->submit_count = $count; + } +} + +/** + * Tests that hook_search_page runs. + */ +class SearchPageOverride extends DrupalWebTestCase { + public $search_user; + + public static function getInfo() { + return array( + 'name' => 'Search page override', + 'description' => 'Verify that hook_search_page can override search page display.', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search', 'search_extra_type'); + + // Login as a user that can create and search content. + $this->search_user = $this->drupalCreateUser(array('search content', 'administer search')); + $this->drupalLogin($this->search_user); + + // Enable the extra type module for searching. + variable_set('search_active_modules', array('node' => 'node', 'user' => 'user', 'search_extra_type' => 'search_extra_type')); + menu_rebuild(); + } + + function testSearchPageHook() { + $keys = 'bike shed ' . $this->randomName(); + $this->drupalGet("search/dummy_path/{$keys}"); + $this->assertText('Dummy search snippet', 'Dummy search snippet is shown'); + $this->assertText('Test page text is here', 'Page override is working'); + } +} + +/** + * Test node search with multiple languages. + */ +class SearchLanguageTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Search language selection', + 'description' => 'Tests advanced search with different languages enabled.', + 'group' => 'Search', + ); + } + + /** + * Implementation setUp(). + */ + function setUp() { + parent::setUp('search', 'locale'); + + // Create and login user. + $test_user = $this->drupalCreateUser(array('access content', 'search content', 'use advanced search', 'administer nodes', 'administer languages', 'access administration pages')); + $this->drupalLogin($test_user); + } + + function testLanguages() { + // Check that there are initially no languages displayed. + $this->drupalGet('search/node'); + $this->assertNoText(t('Languages'), 'No languages to choose from.'); + + // Add predefined language. + $edit = array('langcode' => 'fr'); + $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); + $this->assertText('fr', 'Language added successfully.'); + + // Now we should have languages displayed. + $this->drupalGet('search/node'); + $this->assertText(t('Languages'), 'Languages displayed to choose from.'); + $this->assertText(t('English'), 'English is a possible choice.'); + $this->assertText(t('French'), 'French is a possible choice.'); + + // Ensure selecting no language does not make the query different. + $this->drupalPost('search/node', array(), t('Advanced search')); + $this->assertEqual($this->getUrl(), url('search/node/', array('absolute' => TRUE)), 'Correct page redirection, no language filtering.'); + + // Pick French and ensure it is selected. + $edit = array('language[fr]' => TRUE); + $this->drupalPost('search/node', $edit, t('Advanced search')); + $this->assertFieldByXPath('//input[@name="keys"]', 'language:fr', 'Language filter added to query.'); + + // Change the default language and disable English. + $path = 'admin/config/regional/language'; + $this->drupalGet($path); + $this->assertFieldChecked('edit-site-default-en', 'English is the default language.'); + $edit = array('site_default' => 'fr'); + $this->drupalPost(NULL, $edit, t('Save configuration')); + $this->assertNoFieldChecked('edit-site-default-en', 'Default language updated.'); + $edit = array('enabled[en]' => FALSE); + $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration')); + $this->assertNoFieldChecked('edit-enabled-en', 'Language disabled.'); + + // Check that there are again no languages displayed. + $this->drupalGet('search/node'); + $this->assertNoText(t('Languages'), 'No languages to choose from.'); + } +} + +/** + * Tests node search with node access control. + */ +class SearchNodeAccessTest extends DrupalWebTestCase { + public $test_user; + + public static function getInfo() { + return array( + 'name' => 'Search and node access', + 'description' => 'Tests search functionality with node access control.', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search', 'node_access_test'); + node_access_rebuild(); + + // Create a test user and log in. + $this->test_user = $this->drupalCreateUser(array('access content', 'search content', 'use advanced search')); + $this->drupalLogin($this->test_user); + } + + /** + * Tests that search works with punctuation and HTML entities. + */ + function testPhraseSearchPunctuation() { + $node = $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => "The bunny's ears were fuzzy."))))); + $node2 = $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => 'Dignissim Aliquam & Quieligo meus natu quae quia te. Damnum© erat— neo pneum. Facilisi feugiat ibidem ratis.'))))); + + // Update the search index. + module_invoke_all('update_index'); + search_update_totals(); + + // Refresh variables after the treatment. + $this->refreshVariables(); + + // Submit a phrase wrapped in double quotes to include the punctuation. + $edit = array('keys' => '"bunny\'s"'); + $this->drupalPost('search/node', $edit, t('Search')); + $this->assertText($node->title); + + // Search for "&" and verify entities are not broken up in the output. + $edit = array('keys' => '&'); + $this->drupalPost('search/node', $edit, t('Search')); + $this->assertNoRaw('&amp;'); + $this->assertText('You must include at least one positive keyword'); + + $edit = array('keys' => '&'); + $this->drupalPost('search/node', $edit, t('Search')); + $this->assertNoRaw('&amp;'); + $this->assertText('You must include at least one positive keyword'); + } +} + +/** + * Tests node search with query tags. + */ +class SearchNodeTagTest extends DrupalWebTestCase { + public $test_user; + + public static function getInfo() { + return array( + 'name' => 'Node search query tags', + 'description' => 'Tests Node search tags functionality.', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search', 'search_node_tags'); + node_access_rebuild(); + + // Create a test user and log in. + $this->test_user = $this->drupalCreateUser(array('search content')); + $this->drupalLogin($this->test_user); + } + + /** + * Tests that the correct tags are available and hooks invoked. + */ + function testNodeSearchQueryTags() { + $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => 'testing testing testing.'))))); + + // Update the search index. + module_invoke_all('update_index'); + search_update_totals(); + + $edit = array('keys' => 'testing'); + $this->drupalPost('search/node', $edit, t('Search')); + + $this->assertTrue(variable_get('search_node_tags_test_query_tag', FALSE), 'hook_query_alter() was invoked and the query contained the "search_node" tag.'); + $this->assertTrue(variable_get('search_node_tags_test_query_tag_hook', FALSE), 'hook_query_search_node_alter() was invoked.'); + } +} + +/** + * Tests searching with locale values set. + */ +class SearchSetLocaleTest extends DrupalWebTestCase { + + public static function getInfo() { + return array( + 'name' => 'Search with numeric locale set', + 'description' => 'Check that search works with numeric locale settings', + 'group' => 'Search', + ); + } + + function setUp() { + parent::setUp('search'); + + // Create a simple node so something will be put in the index. + $info = array( + 'body' => array(LANGUAGE_NONE => array(array('value' => 'Tapir'))), + ); + $this->drupalCreateNode($info); + + // Run cron to index. + $this->cronRun(); + } + + /** + * Verify that search works with a numeric locale set. + */ + public function testSearchWithNumericLocale() { + // French decimal point is comma. + setlocale(LC_NUMERIC, 'fr_FR'); + + // An exception will be thrown if a float in the wrong format occurs in the + // query to the database, so an assertion is not necessary here. + db_select('search_index', 'i') + ->extend('searchquery') + ->searchexpression('tapir', 'node') + ->execute(); + } +} diff --git a/drupal7/web/modules/search/tests/UnicodeTest.txt b/drupal7/web/modules/search/tests/UnicodeTest.txt new file mode 100644 index 0000000..af8a65c --- /dev/null +++ b/drupal7/web/modules/search/tests/UnicodeTest.txt @@ -0,0 +1,333 @@ + + !"#$%&'()*+,-./ +0123456789 +:;<=>?@ +ABCDEFGHIJKLMNOPQRSTUVWXYZ +[\]^_` +abcdefghijklmnopqrstuvwxyz +{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨© +ª +«¬­®¯°± +²³ +´ +µ +¶·¸ +¹º +» +¼½¾ +¿ +ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ +× +ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö +÷ +øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɂɃɄɅɆɇɈɉɊɋɌɍɎɏɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼʽʾʿˀˁ +˂˃˄˅ +ˆˇˈˉˊˋˌˍˎˏːˑ +˒˓˔˕˖˗˘˙˚˛˜˝˞˟ +ˠˡˢˣˤ +˥˦˧˨˩˪˫ +ˬ +˭ +ˮ +˯˰˱˲˳˴˵˶˷˸˹˺˻˼˽˾˿ +̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡ͰͱͲͳʹ +͵ +Ͷͷͺͻͼͽ +;΄΅ +Ά +· +ΈΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώϏϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵ +϶ +ϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁ +҂ +҃҄҅҆҇҈҉ҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӏӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹӺӻӼӽӾӿԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԐԑԒԓԔԕԖԗԘԙԚԛԜԝԞԟԠԡԢԣԤԥԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖՙ +՚՛՜՝՞՟ +աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆև +։֊ +ְֱֲֳִֵֶַָֹֺֻּֽ֑֖֛֢֣֤֥֦֧֪֚֭֮֒֓֔֕֗֘֙֜֝֞֟֠֡֨֩֫֬֯ +־ +ֿ +׀ +ׁׂ +׃ +ׅׄ +׆ +ׇאבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ +׳״؀؁؂؃؆؇؈؉؊؋،؍؎؏ +ؘؙؚؐؑؒؓؔؕؖؗ +؛؞؟ +ءآأؤإئابةتثجحخدذرزسشصضطظعغػؼؽؾؿـفقكلمنهوىيًٌٍَُِّْٕٖٜٓٔٗ٘ٙٚٛٝٞ٠١٢٣٤٥٦٧٨٩ +٪٫٬٭ +ٮٯٰٱٲٳٴٵٶٷٸٹٺٻټٽپٿڀځڂڃڄڅچڇڈډڊڋڌڍڎڏڐڑڒړڔڕږڗژڙښڛڜڝڞڟڠڡڢڣڤڥڦڧڨکڪګڬڭڮگڰڱڲڳڴڵڶڷڸڹںڻڼڽھڿۀہۂۃۄۅۆۇۈۉۊۋیۍێۏېۑےۓ +۔ +ەۖۗۘۙۚۛۜ +۝ +۞ۣ۟۠ۡۢۤۥۦۧۨ +۩ +۪ۭ۫۬ۮۯ۰۱۲۳۴۵۶۷۸۹ۺۻۼ +۽۾ +ۿ +܀܁܂܃܄܅܆܇܈܉܊܋܌܍܏ +ܐܑܒܓܔܕܖܗܘܙܚܛܜܝܞܟܠܡܢܣܤܥܦܧܨܩܪܫܬܭܮܯܱܴܷܸܹܻܼܾ݂݄݆݈ܰܲܳܵܶܺܽܿ݀݁݃݅݇݉݊ݍݎݏݐݑݒݓݔݕݖݗݘݙݚݛݜݝݞݟݠݡݢݣݤݥݦݧݨݩݪݫݬݭݮݯݰݱݲݳݴݵݶݷݸݹݺݻݼݽݾݿހށނރބޅކއވމފދތލގޏސޑޒޓޔޕޖޗޘޙޚޛޜޝޞޟޠޡޢޣޤޥަާިީުޫެޭޮޯްޱ߀߁߂߃߄߅߆߇߈߉ߊߋߌߍߎߏߐߑߒߓߔߕߖߗߘߙߚߛߜߝߞߟߠߡߢߣߤߥߦߧߨߩߪ߲߫߬߭߮߯߰߱߳ߴߵ +߶߷߸߹ +ߺࠀࠁࠂࠃࠄࠅࠆࠇࠈࠉࠊࠋࠌࠍࠎࠏࠐࠑࠒࠓࠔࠕࠖࠗ࠘࠙ࠚࠛࠜࠝࠞࠟࠠࠡࠢࠣࠤࠥࠦࠧࠨࠩࠪࠫࠬ࠭ +࠰࠱࠲࠳࠴࠵࠶࠷࠸࠹࠺࠻࠼࠽࠾ +ऀँंःऄअआइईउऊऋऌऍऎएऐऑऒओऔकखगघङचछजझञटठडढणतथदधनऩपफबभमयरऱलळऴवशषसह़ऽािीुूृॄॅॆेैॉॊोौ्ॎॐ॒॑॓॔ॕक़ख़ग़ज़ड़ढ़फ़य़ॠॡॢॣ +।॥ +०१२३४५६७८९ +॰ +ॱॲॹॺॻॼॽॾॿঁংঃঅআইঈউঊঋঌএঐওঔকখগঘঙচছজঝঞটঠডঢণতথদধনপফবভমযরলশষসহ়ঽািীুূৃৄেৈোৌ্ৎৗড়ঢ়য়ৠৡৢৣ০১২৩৪৫৬৭৮৯ৰৱ +৲৳ +৴৵৶৷৸৹ +৺৻ +ਁਂਃਅਆਇਈਉਊਏਐਓਔਕਖਗਘਙਚਛਜਝਞਟਠਡਢਣਤਥਦਧਨਪਫਬਭਮਯਰਲਲ਼ਵਸ਼ਸਹ਼ਾਿੀੁੂੇੈੋੌ੍ੑਖ਼ਗ਼ਜ਼ੜਫ਼੦੧੨੩੪੫੬੭੮੯ੰੱੲੳੴੵઁંઃઅઆઇઈઉઊઋઌઍએઐઑઓઔકખગઘઙચછજઝઞટઠડઢણતથદધનપફબભમયરલળવશષસહ઼ઽાિીુૂૃૄૅેૈૉોૌ્ૐૠૡૢૣ૦૧૨૩૪૫૬૭૮૯ +૱ +ଁଂଃଅଆଇଈଉଊଋଌଏଐଓଔକଖଗଘଙଚଛଜଝଞଟଠଡଢଣତଥଦଧନପଫବଭମଯରଲଳଵଶଷସହ଼ଽାିୀୁୂୃୄେୈୋୌ୍ୖୗଡ଼ଢ଼ୟୠୡୢୣ୦୧୨୩୪୫୬୭୮୯ +୰ +ୱஂஃஅஆஇஈஉஊஎஏஐஒஓஔகஙசஜஞடணதநனபமயரறலளழவஶஷஸஹாிீுூெேைொோௌ்ௐௗ௦௧௨௩௪௫௬௭௮௯௰௱௲ +௳௴௵௶௷௸௹௺ +ఁంఃఅఆఇఈఉఊఋఌఎఏఐఒఓఔకఖగఘఙచఛజఝఞటఠడఢణతథదధనపఫబభమయరఱలళవశషసహఽాిీుూృౄెేైొోౌ్ౕౖౘౙౠౡౢౣ౦౧౨౩౪౫౬౭౮౯౸౹౺౻౼౽౾ +౿ +ಂಃಅಆಇಈಉಊಋಌಎಏಐಒಓಔಕಖಗಘಙಚಛಜಝಞಟಠಡಢಣತಥದಧನಪಫಬಭಮಯರಱಲಳವಶಷಸಹ಼ಽಾಿೀುೂೃೄೆೇೈೊೋೌ್ೕೖೞೠೡೢೣ೦೧೨೩೪೫೬೭೮೯ +ೱೲ +ംഃഅആഇഈഉഊഋഌഎഏഐഒഓഔകഖഗഘങചഛജഝഞടഠഡഢണതഥദധനപഫബഭമയരറലളഴവശഷസഹഽാിീുൂൃൄെേൈൊോൌ്ൗൠൡൢൣ൦൧൨൩൪൫൬൭൮൯൰൱൲൳൴൵ +൹ +ൺൻർൽൾൿංඃඅආඇඈඉඊඋඌඍඎඏඐඑඒඓඔඕඖකඛගඝඞඟචඡජඣඤඥඦටඨඩඪණඬතථදධනඳපඵබභමඹයරලවශෂසහළෆ්ාැෑිීුූෘෙේෛොෝෞෟෲෳ +෴ +กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู +฿ +เแโใไๅๆ็่้๊๋์ํ๎ +๏ +๐๑๒๓๔๕๖๗๘๙ +๚๛ +ກຂຄງຈຊຍດຕຖທນບປຜຝພຟມຢຣລວສຫອຮຯະັາຳິີຶືຸູົຼຽເແໂໃໄໆ່້໊໋໌ໍ໐໑໒໓໔໕໖໗໘໙ໜໝༀ +༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗ +༘༙ +༚༛༜༝༞༟ +༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳ +༴ +༵ +༶ +༷ +༸ +༹ +༺༻༼༽ +༾༿ཀཁགགྷངཅཆཇཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ +྅ +྆྇ྈྉྊྋྐྑྒྒྷྔྕྖྗྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ +྾྿࿀࿁࿂࿃࿄࿅ +࿆ +࿇࿈࿉࿊࿋࿌࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘ +ကခဂဃငစဆဇဈဉညဋဌဍဎဏတထဒဓနပဖဗဘမယရလဝသဟဠအဢဣဤဥဦဧဨဩဪါာိီုူေဲဳဴဵံ့း္်ျြွှဿ၀၁၂၃၄၅၆၇၈၉ +၊။၌၍၎၏ +ၐၑၒၓၔၕၖၗၘၙၚၛၜၝၞၟၠၡၢၣၤၥၦၧၨၩၪၫၬၭၮၯၰၱၲၳၴၵၶၷၸၹၺၻၼၽၾၿႀႁႂႃႄႅႆႇႈႉႊႋႌႍႎႏ႐႑႒႓႔႕႖႗႘႙ႚႛႜႝ +႞႟ +ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅაბგდევზთიკლმნოპჟრსტუფქღყშჩცძწჭხჯჰჱჲჳჴჵჶჷჸჹჺ +჻ +ჼᄀᄁᄂᄃᄄᄅᄆᄇᄈᄉᄊᄋᄌᄍᄎᄏᄐᄑᄒᄓᄔᄕᄖᄗᄘᄙᄚᄛᄜᄝᄞᄟᄠᄡᄢᄣᄤᄥᄦᄧᄨᄩᄪᄫᄬᄭᄮᄯᄰᄱᄲᄳᄴᄵᄶᄷᄸᄹᄺᄻᄼᄽᄾᄿᅀᅁᅂᅃᅄᅅᅆᅇᅈᅉᅊᅋᅌᅍᅎᅏᅐᅑᅒᅓᅔᅕᅖᅗᅘᅙᅚᅛᅜᅝᅞᅟᅠᅡᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵᅶᅷᅸᅹᅺᅻᅼᅽᅾᅿᆀᆁᆂᆃᆄᆅᆆᆇᆈᆉᆊᆋᆌᆍᆎᆏᆐᆑᆒᆓᆔᆕᆖᆗᆘᆙᆚᆛᆜᆝᆞᆟᆠᆡᆢᆣᆤᆥᆦᆧᆨᆩᆪᆫᆬᆭᆮᆯᆰᆱᆲᆳᆴᆵᆶᆷᆸᆹᆺᆻᆼᆽᆾᆿᇀᇁᇂᇃᇄᇅᇆᇇᇈᇉᇊᇋᇌᇍᇎᇏᇐᇑᇒᇓᇔᇕᇖᇗᇘᇙᇚᇛᇜᇝᇞᇟᇠᇡᇢᇣᇤᇥᇦᇧᇨᇩᇪᇫᇬᇭᇮᇯᇰᇱᇲᇳᇴᇵᇶᇷᇸᇹᇺᇻᇼᇽᇾᇿሀሁሂሃሄህሆሇለሉሊላሌልሎሏሐሑሒሓሔሕሖሗመሙሚማሜምሞሟሠሡሢሣሤሥሦሧረሩሪራሬርሮሯሰሱሲሳሴስሶሷሸሹሺሻሼሽሾሿቀቁቂቃቄቅቆቇቈቊቋቌቍቐቑቒቓቔቕቖቘቚቛቜቝበቡቢባቤብቦቧቨቩቪቫቬቭቮቯተቱቲታቴትቶቷቸቹቺቻቼችቾቿኀኁኂኃኄኅኆኇኈኊኋኌኍነኑኒናኔንኖኗኘኙኚኛኜኝኞኟአኡኢኣኤእኦኧከኩኪካኬክኮኯኰኲኳኴኵኸኹኺኻኼኽኾዀዂዃዄዅወዉዊዋዌውዎዏዐዑዒዓዔዕዖዘዙዚዛዜዝዞዟዠዡዢዣዤዥዦዧየዩዪያዬይዮዯደዱዲዳዴድዶዷዸዹዺዻዼዽዾዿጀጁጂጃጄጅጆጇገጉጊጋጌግጎጏጐጒጓጔጕጘጙጚጛጜጝጞጟጠጡጢጣጤጥጦጧጨጩጪጫጬጭጮጯጰጱጲጳጴጵጶጷጸጹጺጻጼጽጾጿፀፁፂፃፄፅፆፇፈፉፊፋፌፍፎፏፐፑፒፓፔፕፖፗፘፙፚ፟ +፠፡።፣፤፥፦፧፨ +፩፪፫፬፭፮፯፰፱፲፳፴፵፶፷፸፹፺፻፼ᎀᎁᎂᎃᎄᎅᎆᎇᎈᎉᎊᎋᎌᎍᎎᎏ +᎐᎑᎒᎓᎔᎕᎖᎗᎘᎙ +ᎠᎡᎢᎣᎤᎥᎦᎧᎨᎩᎪᎫᎬᎭᎮᎯᎰᎱᎲᎳᎴᎵᎶᎷᎸᎹᎺᎻᎼᎽᎾᎿᏀᏁᏂᏃᏄᏅᏆᏇᏈᏉᏊᏋᏌᏍᏎᏏᏐᏑᏒᏓᏔᏕᏖᏗᏘᏙᏚᏛᏜᏝᏞᏟᏠᏡᏢᏣᏤᏥᏦᏧᏨᏩᏪᏫᏬᏭᏮᏯᏰᏱᏲᏳᏴ +᐀ +ᐁᐂᐃᐄᐅᐆᐇᐈᐉᐊᐋᐌᐍᐎᐏᐐᐑᐒᐓᐔᐕᐖᐗᐘᐙᐚᐛᐜᐝᐞᐟᐠᐡᐢᐣᐤᐥᐦᐧᐨᐩᐪᐫᐬᐭᐮᐯᐰᐱᐲᐳᐴᐵᐶᐷᐸᐹᐺᐻᐼᐽᐾᐿᑀᑁᑂᑃᑄᑅᑆᑇᑈᑉᑊᑋᑌᑍᑎᑏᑐᑑᑒᑓᑔᑕᑖᑗᑘᑙᑚᑛᑜᑝᑞᑟᑠᑡᑢᑣᑤᑥᑦᑧᑨᑩᑪᑫᑬᑭᑮᑯᑰᑱᑲᑳᑴᑵᑶᑷᑸᑹᑺᑻᑼᑽᑾᑿᒀᒁᒂᒃᒄᒅᒆᒇᒈᒉᒊᒋᒌᒍᒎᒏᒐᒑᒒᒓᒔᒕᒖᒗᒘᒙᒚᒛᒜᒝᒞᒟᒠᒡᒢᒣᒤᒥᒦᒧᒨᒩᒪᒫᒬᒭᒮᒯᒰᒱᒲᒳᒴᒵᒶᒷᒸᒹᒺᒻᒼᒽᒾᒿᓀᓁᓂᓃᓄᓅᓆᓇᓈᓉᓊᓋᓌᓍᓎᓏᓐᓑᓒᓓᓔᓕᓖᓗᓘᓙᓚᓛᓜᓝᓞᓟᓠᓡᓢᓣᓤᓥᓦᓧᓨᓩᓪᓫᓬᓭᓮᓯᓰᓱᓲᓳᓴᓵᓶᓷᓸᓹᓺᓻᓼᓽᓾᓿᔀᔁᔂᔃᔄᔅᔆᔇᔈᔉᔊᔋᔌᔍᔎᔏᔐᔑᔒᔓᔔᔕᔖᔗᔘᔙᔚᔛᔜᔝᔞᔟᔠᔡᔢᔣᔤᔥᔦᔧᔨᔩᔪᔫᔬᔭᔮᔯᔰᔱᔲᔳᔴᔵᔶᔷᔸᔹᔺᔻᔼᔽᔾᔿᕀᕁᕂᕃᕄᕅᕆᕇᕈᕉᕊᕋᕌᕍᕎᕏᕐᕑᕒᕓᕔᕕᕖᕗᕘᕙᕚᕛᕜᕝᕞᕟᕠᕡᕢᕣᕤᕥᕦᕧᕨᕩᕪᕫᕬᕭᕮᕯᕰᕱᕲᕳᕴᕵᕶᕷᕸᕹᕺᕻᕼᕽᕾᕿᖀᖁᖂᖃᖄᖅᖆᖇᖈᖉᖊᖋᖌᖍᖎᖏᖐᖑᖒᖓᖔᖕᖖᖗᖘᖙᖚᖛᖜᖝᖞᖟᖠᖡᖢᖣᖤᖥᖦᖧᖨᖩᖪᖫᖬᖭᖮᖯᖰᖱᖲᖳᖴᖵᖶᖷᖸᖹᖺᖻᖼᖽᖾᖿᗀᗁᗂᗃᗄᗅᗆᗇᗈᗉᗊᗋᗌᗍᗎᗏᗐᗑᗒᗓᗔᗕᗖᗗᗘᗙᗚᗛᗜᗝᗞᗟᗠᗡᗢᗣᗤᗥᗦᗧᗨᗩᗪᗫᗬᗭᗮᗯᗰᗱᗲᗳᗴᗵᗶᗷᗸᗹᗺᗻᗼᗽᗾᗿᘀᘁᘂᘃᘄᘅᘆᘇᘈᘉᘊᘋᘌᘍᘎᘏᘐᘑᘒᘓᘔᘕᘖᘗᘘᘙᘚᘛᘜᘝᘞᘟᘠᘡᘢᘣᘤᘥᘦᘧᘨᘩᘪᘫᘬᘭᘮᘯᘰᘱᘲᘳᘴᘵᘶᘷᘸᘹᘺᘻᘼᘽᘾᘿᙀᙁᙂᙃᙄᙅᙆᙇᙈᙉᙊᙋᙌᙍᙎᙏᙐᙑᙒᙓᙔᙕᙖᙗᙘᙙᙚᙛᙜᙝᙞᙟᙠᙡᙢᙣᙤᙥᙦᙧᙨᙩᙪᙫᙬ +᙭᙮ +ᙯᙰᙱᙲᙳᙴᙵᙶᙷᙸᙹᙺᙻᙼᙽᙾᙿ +  +ᚁᚂᚃᚄᚅᚆᚇᚈᚉᚊᚋᚌᚍᚎᚏᚐᚑᚒᚓᚔᚕᚖᚗᚘᚙᚚ +᚛᚜ +ᚠᚡᚢᚣᚤᚥᚦᚧᚨᚩᚪᚫᚬᚭᚮᚯᚰᚱᚲᚳᚴᚵᚶᚷᚸᚹᚺᚻᚼᚽᚾᚿᛀᛁᛂᛃᛄᛅᛆᛇᛈᛉᛊᛋᛌᛍᛎᛏᛐᛑᛒᛓᛔᛕᛖᛗᛘᛙᛚᛛᛜᛝᛞᛟᛠᛡᛢᛣᛤᛥᛦᛧᛨᛩᛪ +᛫᛬᛭ +ᛮᛯᛰᜀᜁᜂᜃᜄᜅᜆᜇᜈᜉᜊᜋᜌᜎᜏᜐᜑᜒᜓ᜔ᜠᜡᜢᜣᜤᜥᜦᜧᜨᜩᜪᜫᜬᜭᜮᜯᜰᜱᜲᜳ᜴ +᜵᜶ +ᝀᝁᝂᝃᝄᝅᝆᝇᝈᝉᝊᝋᝌᝍᝎᝏᝐᝑᝒᝓᝠᝡᝢᝣᝤᝥᝦᝧᝨᝩᝪᝫᝬᝮᝯᝰᝲᝳកខគឃងចឆជឈញដឋឌឍណតថទធនបផពភមយរលវឝឞសហឡអឣឤឥឦឧឨឩឪឫឬឭឮឯឰឱឲឳ +឴឵ +ាិីឹឺុូួើឿៀេែៃោៅំះៈ៉៊់៌៍៎៏័៑្៓ +។៕៖ +ៗ +៘៙៚៛ +ៜ៝០១២៣៤៥៦៧៨៩៰៱៲៳៴៵៶៷៸៹ +᠀᠁᠂᠃᠄᠅᠆᠇᠈᠉᠊ +᠋᠌᠍ +᠎ +᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙ᠠᠡᠢᠣᠤᠥᠦᠧᠨᠩᠪᠫᠬᠭᠮᠯᠰᠱᠲᠳᠴᠵᠶᠷᠸᠹᠺᠻᠼᠽᠾᠿᡀᡁᡂᡃᡄᡅᡆᡇᡈᡉᡊᡋᡌᡍᡎᡏᡐᡑᡒᡓᡔᡕᡖᡗᡘᡙᡚᡛᡜᡝᡞᡟᡠᡡᡢᡣᡤᡥᡦᡧᡨᡩᡪᡫᡬᡭᡮᡯᡰᡱᡲᡳᡴᡵᡶᡷᢀᢁᢂᢃᢄᢅᢆᢇᢈᢉᢊᢋᢌᢍᢎᢏᢐᢑᢒᢓᢔᢕᢖᢗᢘᢙᢚᢛᢜᢝᢞᢟᢠᢡᢢᢣᢤᢥᢦᢧᢨᢩᢪᢰᢱᢲᢳᢴᢵᢶᢷᢸᢹᢺᢻᢼᢽᢾᢿᣀᣁᣂᣃᣄᣅᣆᣇᣈᣉᣊᣋᣌᣍᣎᣏᣐᣑᣒᣓᣔᣕᣖᣗᣘᣙᣚᣛᣜᣝᣞᣟᣠᣡᣢᣣᣤᣥᣦᣧᣨᣩᣪᣫᣬᣭᣮᣯᣰᣱᣲᣳᣴᣵᤀᤁᤂᤃᤄᤅᤆᤇᤈᤉᤊᤋᤌᤍᤎᤏᤐᤑᤒᤓᤔᤕᤖᤗᤘᤙᤚᤛᤜᤠᤡᤢᤣᤤᤥᤦᤧᤨᤩᤪᤫᤰᤱᤲᤳᤴᤵᤶᤷᤸ᤻᤹᤺ +᥀᥄᥅ +᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏ᥐᥑᥒᥓᥔᥕᥖᥗᥘᥙᥚᥛᥜᥝᥞᥟᥠᥡᥢᥣᥤᥥᥦᥧᥨᥩᥪᥫᥬᥭᥰᥱᥲᥳᥴᦀᦁᦂᦃᦄᦅᦆᦇᦈᦉᦊᦋᦌᦍᦎᦏᦐᦑᦒᦓᦔᦕᦖᦗᦘᦙᦚᦛᦜᦝᦞᦟᦠᦡᦢᦣᦤᦥᦦᦧᦨᦩᦪᦫᦰᦱᦲᦳᦴᦵᦶᦷᦸᦹᦺᦻᦼᦽᦾᦿᧀᧁᧂᧃᧄᧅᧆᧇᧈᧉ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙᧚ +᧞᧟᧠᧡᧢᧣᧤᧥᧦᧧᧨᧩᧪᧫᧬᧭᧮᧯᧰᧱᧲᧳᧴᧵᧶᧷᧸᧹᧺᧻᧼᧽᧾᧿ +ᨀᨁᨂᨃᨄᨅᨆᨇᨈᨉᨊᨋᨌᨍᨎᨏᨐᨑᨒᨓᨔᨕᨖᨘᨗᨙᨚᨛ +᨞᨟ +ᨠᨡᨢᨣᨤᨥᨦᨧᨨᨩᨪᨫᨬᨭᨮᨯᨰᨱᨲᨳᨴᨵᨶᨷᨸᨹᨺᨻᨼᨽᨾᨿᩀᩁᩂᩃᩄᩅᩆᩇᩈᩉᩊᩋᩌᩍᩎᩏᩐᩑᩒᩓᩔᩕᩖᩗᩘᩙᩚᩛᩜᩝᩞ᩠ᩡᩢᩣᩤᩥᩦᩧᩨᩩᩪᩫᩬᩭᩮᩯᩰᩱᩲᩳᩴ᩿᩵᩶᩷᩸᩹᩺᩻᩼᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙ +᪠᪡᪢᪣᪤᪥᪦ +ᪧ +᪨᪩᪪᪫᪬᪭ +ᬀᬁᬂᬃᬄᬅᬆᬇᬈᬉᬊᬋᬌᬍᬎᬏᬐᬑᬒᬓᬔᬕᬖᬗᬘᬙᬚᬛᬜᬝᬞᬟᬠᬡᬢᬣᬤᬥᬦᬧᬨᬩᬪᬫᬬᬭᬮᬯᬰᬱᬲᬳ᬴ᬵᬶᬷᬸᬹᬺᬻᬼᬽᬾᬿᭀᭁᭂᭃ᭄ᭅᭆᭇᭈᭉᭊᭋ᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙ +᭚᭛᭜᭝᭞᭟᭠᭡᭢᭣᭤᭥᭦᭧᭨᭩᭪ +᭬᭫᭭᭮᭯᭰᭱᭲᭳ +᭴᭵᭶᭷᭸᭹᭺᭻᭼ +ᮀᮁᮂᮃᮄᮅᮆᮇᮈᮉᮊᮋᮌᮍᮎᮏᮐᮑᮒᮓᮔᮕᮖᮗᮘᮙᮚᮛᮜᮝᮞᮟᮠᮡᮢᮣᮤᮥᮦᮧᮨᮩ᮪ᮮᮯ᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹ᰀᰁᰂᰃᰄᰅᰆᰇᰈᰉᰊᰋᰌᰍᰎᰏᰐᰑᰒᰓᰔᰕᰖᰗᰘᰙᰚᰛᰜᰝᰞᰟᰠᰡᰢᰣᰤᰥᰦᰧᰨᰩᰪᰫᰬᰭᰮᰯᰰᰱᰲᰳᰴᰵᰶ᰷ +᰻᰼᰽᰾᰿ +᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉ᱍᱎᱏ᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙ᱚᱛᱜᱝᱞᱟᱠᱡᱢᱣᱤᱥᱦᱧᱨᱩᱪᱫᱬᱭᱮᱯᱰᱱᱲᱳᱴᱵᱶᱷᱸᱹᱺᱻᱼᱽ +᱾᱿ +᳐᳑᳒ +᳓ +᳔᳕᳖᳗᳘᳙᳜᳝᳞᳟᳚᳛᳠᳡᳢᳣᳤᳥᳦᳧᳨ᳩᳪᳫᳬ᳭ᳮᳯᳰᳱᳲᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩᴪᴫᴬᴭᴮᴯᴰᴱᴲᴳᴴᴵᴶᴷᴸᴹᴺᴻᴼᴽᴾᴿᵀᵁᵂᵃᵄᵅᵆᵇᵈᵉᵊᵋᵌᵍᵎᵏᵐᵑᵒᵓᵔᵕᵖᵗᵘᵙᵚᵛᵜᵝᵞᵟᵠᵡᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵸᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚᶛᶜᶝᶞᶟᶠᶡᶢᶣᶤᶥᶦᶧᶨᶩᶪᶫᶬᶭᶮᶯᶰᶱᶲᶳᶴᶵᶶᶷᶸᶹᶺᶻᶼᶽᶾᶿ᷐᷎᷂᷊᷏᷽᷿᷀᷁᷃᷄᷅᷆᷇᷈᷉᷋᷌᷑᷒ᷓᷔᷕᷖᷗᷘᷙᷚᷛᷜᷝᷞᷟᷠᷡᷢᷣᷤᷥᷦ᷾᷍ḀḁḂḃḄḅḆḇḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẜẝẞẟẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐốỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹỺỻỼỽỾỿἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾈᾉᾊᾋᾌᾍᾎᾏᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾞᾟᾠᾡᾢᾣᾤᾥᾦᾧᾨᾩᾪᾫᾬᾭᾮᾯᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆᾼ +᾽ +ι +᾿῀῁ +ῂῃῄῆῇῈΈῊΉῌ +῍῎῏ +ῐῑῒΐῖῗῘῙῚΊ +῝῞῟ +ῠῡῢΰῤῥῦῧῨῩῪΎῬ +῭΅` +ῲῳῴῶῷῸΌῺΏῼ +´῾           ​‌‍‎‏‐‑‒–—―‖‗‘’‚‛“”„‟†‡•‣․‥…‧

‪‫‬‭‮ ‰‱′″‴‵‶‷‸‹›※‼‽‾‿⁀⁁⁂⁃⁄⁅⁆⁇⁈⁉⁊⁋⁌⁍⁎⁏⁐⁑⁒⁓⁔⁕⁖⁗⁘⁙⁚⁛⁜⁝⁞ ⁠⁡⁢⁣⁤ +⁰ⁱ⁴⁵⁶⁷⁸⁹ +⁺⁻⁼⁽⁾ +ⁿ₀₁₂₃₄₅₆₇₈₉ +₊₋₌₍₎ +ₐₑₒₓₔ +₠₡₢₣₤₥₦₧₨₩₪₫€₭₮₯₰₱₲₳₴₵₶₷₸ +⃒⃓⃘⃙⃚⃐⃑⃔⃕⃖⃗⃛⃜⃝⃞⃟⃠⃡⃢⃣⃤⃥⃦⃪⃫⃨⃬⃭⃮⃯⃧⃩⃰ +℀℁ +ℂ +℃℄℅℆ +ℇ +℈℉ +ℊℋℌℍℎℏℐℑℒℓ +℔ +ℕ +№℗℘ +ℙℚℛℜℝ +℞℟℠℡™℣ +ℤ +℥ +Ω +℧ +ℨ +℩ +KÅℬℭ +℮ +ℯℰℱℲℳℴℵℶℷℸℹ +℺℻ +ℼℽℾℿ +⅀⅁⅂⅃⅄ +ⅅⅆⅇⅈⅉ +⅊⅋⅌⅍ +ⅎ +⅏ +⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↀↁↂↃↄↅↆↇↈ↉ +←↑→↓↔↕↖↗↘↙↚↛↜↝↞↟↠↡↢↣↤↥↦↧↨↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹↺↻↼↽↾↿⇀⇁⇂⇃⇄⇅⇆⇇⇈⇉⇊⇋⇌⇍⇎⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇚⇛⇜⇝⇞⇟⇠⇡⇢⇣⇤⇥⇦⇧⇨⇩⇪⇫⇬⇭⇮⇯⇰⇱⇲⇳⇴⇵⇶⇷⇸⇹⇺⇻⇼⇽⇾⇿∀∁∂∃∄∅∆∇∈∉∊∋∌∍∎∏∐∑−∓∔∕∖∗∘∙√∛∜∝∞∟∠∡∢∣∤∥∦∧∨∩∪∫∬∭∮∯∰∱∲∳∴∵∶∷∸∹∺∻∼∽∾∿≀≁≂≃≄≅≆≇≈≉≊≋≌≍≎≏≐≑≒≓≔≕≖≗≘≙≚≛≜≝≞≟≠≡≢≣≤≥≦≧≨≩≪≫≬≭≮≯≰≱≲≳≴≵≶≷≸≹≺≻≼≽≾≿⊀⊁⊂⊃⊄⊅⊆⊇⊈⊉⊊⊋⊌⊍⊎⊏⊐⊑⊒⊓⊔⊕⊖⊗⊘⊙⊚⊛⊜⊝⊞⊟⊠⊡⊢⊣⊤⊥⊦⊧⊨⊩⊪⊫⊬⊭⊮⊯⊰⊱⊲⊳⊴⊵⊶⊷⊸⊹⊺⊻⊼⊽⊾⊿⋀⋁⋂⋃⋄⋅⋆⋇⋈⋉⋊⋋⋌⋍⋎⋏⋐⋑⋒⋓⋔⋕⋖⋗⋘⋙⋚⋛⋜⋝⋞⋟⋠⋡⋢⋣⋤⋥⋦⋧⋨⋩⋪⋫⋬⋭⋮⋯⋰⋱⋲⋳⋴⋵⋶⋷⋸⋹⋺⋻⋼⋽⋾⋿⌀⌁⌂⌃⌄⌅⌆⌇⌈⌉⌊⌋⌌⌍⌎⌏⌐⌑⌒⌓⌔⌕⌖⌗⌘⌙⌚⌛⌜⌝⌞⌟⌠⌡⌢⌣⌤⌥⌦⌧⌨〈〉⌫⌬⌭⌮⌯⌰⌱⌲⌳⌴⌵⌶⌷⌸⌹⌺⌻⌼⌽⌾⌿⍀⍁⍂⍃⍄⍅⍆⍇⍈⍉⍊⍋⍌⍍⍎⍏⍐⍑⍒⍓⍔⍕⍖⍗⍘⍙⍚⍛⍜⍝⍞⍟⍠⍡⍢⍣⍤⍥⍦⍧⍨⍩⍪⍫⍬⍭⍮⍯⍰⍱⍲⍳⍴⍵⍶⍷⍸⍹⍺⍻⍼⍽⍾⍿⎀⎁⎂⎃⎄⎅⎆⎇⎈⎉⎊⎋⎌⎍⎎⎏⎐⎑⎒⎓⎔⎕⎖⎗⎘⎙⎚⎛⎜⎝⎞⎟⎠⎡⎢⎣⎤⎥⎦⎧⎨⎩⎪⎫⎬⎭⎮⎯⎰⎱⎲⎳⎴⎵⎶⎷⎸⎹⎺⎻⎼⎽⎾⎿⏀⏁⏂⏃⏄⏅⏆⏇⏈⏉⏊⏋⏌⏍⏎⏏⏐⏑⏒⏓⏔⏕⏖⏗⏘⏙⏚⏛⏜⏝⏞⏟⏠⏡⏢⏣⏤⏥⏦⏧⏨␀␁␂␃␄␅␆␇␈␉␊␋␌␍␎␏␐␑␒␓␔␕␖␗␘␙␚␛␜␝␞␟␠␡␢␣␤␥␦⑀⑁⑂⑃⑄⑅⑆⑇⑈⑉⑊ +①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛ +⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ +⓪⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴⓵⓶⓷⓸⓹⓺⓻⓼⓽⓾⓿ +─━│┃┄┅┆┇┈┉┊┋┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋╌╍╎╏═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬╭╮╯╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▐░▒▓▔▕▖▗▘▙▚▛▜▝▞▟■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿◀◁◂◃◄◅◆◇◈◉◊○◌◍◎●◐◑◒◓◔◕◖◗◘◙◚◛◜◝◞◟◠◡◢◣◤◥◦◧◨◩◪◫◬◭◮◯◰◱◲◳◴◵◶◷◸◹◺◻◼◽◾◿☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☔☕☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿♀♁♂♃♄♅♆♇♈♉♊♋♌♍♎♏♐♑♒♓♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚇⚈⚉⚊⚋⚌⚍⚎⚏⚐⚑⚒⚓⚔⚕⚖⚗⚘⚙⚚⚛⚜⚝⚞⚟⚠⚡⚢⚣⚤⚥⚦⚧⚨⚩⚪⚫⚬⚭⚮⚯⚰⚱⚲⚳⚴⚵⚶⚷⚸⚹⚺⚻⚼⚽⚾⚿⛀⛁⛂⛃⛄⛅⛆⛇⛈⛉⛊⛋⛌⛍⛏⛐⛑⛒⛓⛔⛕⛖⛗⛘⛙⛚⛛⛜⛝⛞⛟⛠⛡⛣⛨⛩⛪⛫⛬⛭⛮⛯⛰⛱⛲⛳⛴⛵⛶⛷⛸⛹⛺⛻⛼⛽⛾⛿✁✂✃✄✆✇✈✉✌✍✎✏✐✑✒✓✔✕✖✗✘✙✚✛✜✝✞✟✠✡✢✣✤✥✦✧✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❍❏❐❑❒❖❗❘❙❚❛❜❝❞❡❢❣❤❥❦❧❨❩❪❫❬❭❮❯❰❱❲❳❴❵ +❶❷❸❹❺❻❼❽❾❿➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓ +➔➘➙➚➛➜➝➞➟➠➡➢➣➤➥➦➧➨➩➪➫➬➭➮➯➱➲➳➴➵➶➷➸➹➺➻➼➽➾⟀⟁⟂⟃⟄⟅⟆⟇⟈⟉⟊⟌⟐⟑⟒⟓⟔⟕⟖⟗⟘⟙⟚⟛⟜⟝⟞⟟⟠⟡⟢⟣⟤⟥⟦⟧⟨⟩⟪⟫⟬⟭⟮⟯⟰⟱⟲⟳⟴⟵⟶⟷⟸⟹⟺⟻⟼⟽⟾⟿⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿⤀⤁⤂⤃⤄⤅⤆⤇⤈⤉⤊⤋⤌⤍⤎⤏⤐⤑⤒⤓⤔⤕⤖⤗⤘⤙⤚⤛⤜⤝⤞⤟⤠⤡⤢⤣⤤⤥⤦⤧⤨⤩⤪⤫⤬⤭⤮⤯⤰⤱⤲⤳⤴⤵⤶⤷⤸⤹⤺⤻⤼⤽⤾⤿⥀⥁⥂⥃⥄⥅⥆⥇⥈⥉⥊⥋⥌⥍⥎⥏⥐⥑⥒⥓⥔⥕⥖⥗⥘⥙⥚⥛⥜⥝⥞⥟⥠⥡⥢⥣⥤⥥⥦⥧⥨⥩⥪⥫⥬⥭⥮⥯⥰⥱⥲⥳⥴⥵⥶⥷⥸⥹⥺⥻⥼⥽⥾⥿⦀⦁⦂⦃⦄⦅⦆⦇⦈⦉⦊⦋⦌⦍⦎⦏⦐⦑⦒⦓⦔⦕⦖⦗⦘⦙⦚⦛⦜⦝⦞⦟⦠⦡⦢⦣⦤⦥⦦⦧⦨⦩⦪⦫⦬⦭⦮⦯⦰⦱⦲⦳⦴⦵⦶⦷⦸⦹⦺⦻⦼⦽⦾⦿⧀⧁⧂⧃⧄⧅⧆⧇⧈⧉⧊⧋⧌⧍⧎⧏⧐⧑⧒⧓⧔⧕⧖⧗⧘⧙⧚⧛⧜⧝⧞⧟⧠⧡⧢⧣⧤⧥⧦⧧⧨⧩⧪⧫⧬⧭⧮⧯⧰⧱⧲⧳⧴⧵⧶⧷⧸⧹⧺⧻⧼⧽⧾⧿⨀⨁⨂⨃⨄⨅⨆⨇⨈⨉⨊⨋⨌⨍⨎⨏⨐⨑⨒⨓⨔⨕⨖⨗⨘⨙⨚⨛⨜⨝⨞⨟⨠⨡⨢⨣⨤⨥⨦⨧⨨⨩⨪⨫⨬⨭⨮⨯⨰⨱⨲⨳⨴⨵⨶⨷⨸⨹⨺⨻⨼⨽⨾⨿⩀⩁⩂⩃⩄⩅⩆⩇⩈⩉⩊⩋⩌⩍⩎⩏⩐⩑⩒⩓⩔⩕⩖⩗⩘⩙⩚⩛⩜⩝⩞⩟⩠⩡⩢⩣⩤⩥⩦⩧⩨⩩⩪⩫⩬⩭⩮⩯⩰⩱⩲⩳⩴⩵⩶⩷⩸⩹⩺⩻⩼⩽⩾⩿⪀⪁⪂⪃⪄⪅⪆⪇⪈⪉⪊⪋⪌⪍⪎⪏⪐⪑⪒⪓⪔⪕⪖⪗⪘⪙⪚⪛⪜⪝⪞⪟⪠⪡⪢⪣⪤⪥⪦⪧⪨⪩⪪⪫⪬⪭⪮⪯⪰⪱⪲⪳⪴⪵⪶⪷⪸⪹⪺⪻⪼⪽⪾⪿⫀⫁⫂⫃⫄⫅⫆⫇⫈⫉⫊⫋⫌⫍⫎⫏⫐⫑⫒⫓⫔⫕⫖⫗⫘⫙⫚⫛⫝̸⫝⫞⫟⫠⫡⫢⫣⫤⫥⫦⫧⫨⫩⫪⫫⫬⫭⫮⫯⫰⫱⫲⫳⫴⫵⫶⫷⫸⫹⫺⫻⫼⫽⫾⫿⬀⬁⬂⬃⬄⬅⬆⬇⬈⬉⬊⬋⬌⬍⬎⬏⬐⬑⬒⬓⬔⬕⬖⬗⬘⬙⬚⬛⬜⬝⬞⬟⬠⬡⬢⬣⬤⬥⬦⬧⬨⬩⬪⬫⬬⬭⬮⬯⬰⬱⬲⬳⬴⬵⬶⬷⬸⬹⬺⬻⬼⬽⬾⬿⭀⭁⭂⭃⭄⭅⭆⭇⭈⭉⭊⭋⭌⭐⭑⭒⭓⭔⭕⭖⭗⭘⭙ +ⰀⰁⰂⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌⰍⰎⰏⰐⰑⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜⰝⰞⰟⰠⰡⰢⰣⰤⰥⰦⰧⰨⰩⰪⰫⰬⰭⰮⰰⰱⰲⰳⰴⰵⰶⰷⰸⰹⰺⰻⰼⰽⰾⰿⱀⱁⱂⱃⱄⱅⱆⱇⱈⱉⱊⱋⱌⱍⱎⱏⱐⱑⱒⱓⱔⱕⱖⱗⱘⱙⱚⱛⱜⱝⱞⱠⱡⱢⱣⱤⱥⱦⱧⱨⱩⱪⱫⱬⱭⱮⱯⱰⱱⱲⱳⱴⱵⱶⱷⱸⱹⱺⱻⱼⱽⱾⱿⲀⲁⲂⲃⲄⲅⲆⲇⲈⲉⲊⲋⲌⲍⲎⲏⲐⲑⲒⲓⲔⲕⲖⲗⲘⲙⲚⲛⲜⲝⲞⲟⲠⲡⲢⲣⲤⲥⲦⲧⲨⲩⲪⲫⲬⲭⲮⲯⲰⲱⲲⲳⲴⲵⲶⲷⲸⲹⲺⲻⲼⲽⲾⲿⳀⳁⳂⳃⳄⳅⳆⳇⳈⳉⳊⳋⳌⳍⳎⳏⳐⳑⳒⳓⳔⳕⳖⳗⳘⳙⳚⳛⳜⳝⳞⳟⳠⳡⳢⳣⳤ +⳥⳦⳧⳨⳩⳪ +ⳫⳬⳭⳮ⳯⳰⳱ +⳹⳺⳻⳼ +⳽ +⳾⳿ +ⴀⴁⴂⴃⴄⴅⴆⴇⴈⴉⴊⴋⴌⴍⴎⴏⴐⴑⴒⴓⴔⴕⴖⴗⴘⴙⴚⴛⴜⴝⴞⴟⴠⴡⴢⴣⴤⴥⴰⴱⴲⴳⴴⴵⴶⴷⴸⴹⴺⴻⴼⴽⴾⴿⵀⵁⵂⵃⵄⵅⵆⵇⵈⵉⵊⵋⵌⵍⵎⵏⵐⵑⵒⵓⵔⵕⵖⵗⵘⵙⵚⵛⵜⵝⵞⵟⵠⵡⵢⵣⵤⵥⵯⶀⶁⶂⶃⶄⶅⶆⶇⶈⶉⶊⶋⶌⶍⶎⶏⶐⶑⶒⶓⶔⶕⶖⶠⶡⶢⶣⶤⶥⶦⶨⶩⶪⶫⶬⶭⶮⶰⶱⶲⶳⶴⶵⶶⶸⶹⶺⶻⶼⶽⶾⷀⷁⷂⷃⷄⷅⷆⷈⷉⷊⷋⷌⷍⷎⷐⷑⷒⷓⷔⷕⷖⷘⷙⷚⷛⷜⷝⷞⷠⷡⷢⷣⷤⷥⷦⷧⷨⷩⷪⷫⷬⷭⷮⷯⷰⷱⷲⷳⷴⷵⷶⷷⷸⷹⷺⷻⷼⷽⷾⷿ +⸀⸁⸂⸃⸄⸅⸆⸇⸈⸉⸊⸋⸌⸍⸎⸏⸐⸑⸒⸓⸔⸕⸖⸗⸘⸙⸚⸛⸜⸝⸞⸟⸠⸡⸢⸣⸤⸥⸦⸧⸨⸩⸪⸫⸬⸭⸮ +ⸯ +⸰⸱⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠⻡⻢⻣⻤⻥⻦⻧⻨⻩⻪⻫⻬⻭⻮⻯⻰⻱⻲⻳⼀⼁⼂⼃⼄⼅⼆⼇⼈⼉⼊⼋⼌⼍⼎⼏⼐⼑⼒⼓⼔⼕⼖⼗⼘⼙⼚⼛⼜⼝⼞⼟⼠⼡⼢⼣⼤⼥⼦⼧⼨⼩⼪⼫⼬⼭⼮⼯⼰⼱⼲⼳⼴⼵⼶⼷⼸⼹⼺⼻⼼⼽⼾⼿⽀⽁⽂⽃⽄⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿⾀⾁⾂⾃⾄⾅⾆⾇⾈⾉⾊⾋⾌⾍⾎⾏⾐⾑⾒⾓⾔⾕⾖⾗⾘⾙⾚⾛⾜⾝⾞⾟⾠⾡⾢⾣⾤⾥⾦⾧⾨⾩⾪⾫⾬⾭⾮⾯⾰⾱⾲⾳⾴⾵⾶⾷⾸⾹⾺⾻⾼⾽⾾⾿⿀⿁⿂⿃⿄⿅⿆⿇⿈⿉⿊⿋⿌⿍⿎⿏⿐⿑⿒⿓⿔⿕⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻ 、。〃〄 +々〆〇 +〈〉《》「」『』【】〒〓〔〕〖〗〘〙〚〛〜〝〞〟〠 +〡〢〣〤〥〦〧〨〩〪〭〮〯〫〬 +〰 +〱〲〳〴〵 +〶〷 +〸〹〺〻〼 +〽〾〿 +ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖ゙゚ +゛゜ +ゝゞゟ +゠ +ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶヷヸヹヺ +・ +ーヽヾヿㄅㄆㄇㄈㄉㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙㄚㄛㄜㄝㄞㄟㄠㄡㄢㄣㄤㄥㄦㄧㄨㄩㄪㄫㄬㄭㄱㄲㄳㄴㄵㄶㄷㄸㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅃㅄㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎㅏㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣㅤㅥㅦㅧㅨㅩㅪㅫㅬㅭㅮㅯㅰㅱㅲㅳㅴㅵㅶㅷㅸㅹㅺㅻㅼㅽㅾㅿㆀㆁㆂㆃㆄㆅㆆㆇㆈㆉㆊㆋㆌㆍㆎ +㆐㆑ +㆒㆓㆔㆕ +㆖㆗㆘㆙㆚㆛㆜㆝㆞㆟ +ㆠㆡㆢㆣㆤㆥㆦㆧㆨㆩㆪㆫㆬㆭㆮㆯㆰㆱㆲㆳㆴㆵㆶㆷ +㇀㇁㇂㇃㇄㇅㇆㇇㇈㇉㇊㇋㇌㇍㇎㇏㇐㇑㇒㇓㇔㇕㇖㇗㇘㇙㇚㇛㇜㇝㇞㇟㇠㇡㇢㇣ +ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ +㈀㈁㈂㈃㈄㈅㈆㈇㈈㈉㈊㈋㈌㈍㈎㈏㈐㈑㈒㈓㈔㈕㈖㈗㈘㈙㈚㈛㈜㈝㈞ +㈠㈡㈢㈣㈤㈥㈦㈧㈨㈩ +㈪㈫㈬㈭㈮㈯㈰㈱㈲㈳㈴㈵㈶㈷㈸㈹㈺㈻㈼㈽㈾㈿㉀㉁㉂㉃㉄㉅㉆㉇㉈㉉㉊㉋㉌㉍㉎㉏㉐ +㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟ +㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭㉮㉯㉰㉱㉲㉳㉴㉵㉶㉷㉸㉹㉺㉻㉼㉽㉾㉿ +㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉ +㊊㊋㊌㊍㊎㊏㊐㊑㊒㊓㊔㊕㊖㊗㊘㊙㊚㊛㊜㊝㊞㊟㊠㊡㊢㊣㊤㊥㊦㊧㊨㊩㊪㊫㊬㊭㊮㊯㊰ +㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿ +㋀㋁㋂㋃㋄㋅㋆㋇㋈㋉㋊㋋㋌㋍㋎㋏㋐㋑㋒㋓㋔㋕㋖㋗㋘㋙㋚㋛㋜㋝㋞㋟㋠㋡㋢㋣㋤㋥㋦㋧㋨㋩㋪㋫㋬㋭㋮㋯㋰㋱㋲㋳㋴㋵㋶㋷㋸㋹㋺㋻㋼㋽㋾㌀㌁㌂㌃㌄㌅㌆㌇㌈㌉㌊㌋㌌㌍㌎㌏㌐㌑㌒㌓㌔㌕㌖㌗㌘㌙㌚㌛㌜㌝㌞㌟㌠㌡㌢㌣㌤㌥㌦㌧㌨㌩㌪㌫㌬㌭㌮㌯㌰㌱㌲㌳㌴㌵㌶㌷㌸㌹㌺㌻㌼㌽㌾㌿㍀㍁㍂㍃㍄㍅㍆㍇㍈㍉㍊㍋㍌㍍㍎㍏㍐㍑㍒㍓㍔㍕㍖㍗㍘㍙㍚㍛㍜㍝㍞㍟㍠㍡㍢㍣㍤㍥㍦㍧㍨㍩㍪㍫㍬㍭㍮㍯㍰㍱㍲㍳㍴㍵㍶㍷㍸㍹㍺㍻㍼㍽㍾㍿㎀㎁㎂㎃㎄㎅㎆㎇㎈㎉㎊㎋㎌㎍㎎㎏㎐㎑㎒㎓㎔㎕㎖㎗㎘㎙㎚㎛㎜㎝㎞㎟㎠㎡㎢㎣㎤㎥㎦㎧㎨㎩㎪㎫㎬㎭㎮㎯㎰㎱㎲㎳㎴㎵㎶㎷㎸㎹㎺㎻㎼㎽㎾㎿㏀㏁㏂㏃㏄㏅㏆㏇㏈㏉㏊㏋㏌㏍㏎㏏㏐㏑㏒㏓㏔㏕㏖㏗㏘㏙㏚㏛㏜㏝㏞㏟㏠㏡㏢㏣㏤㏥㏦㏧㏨㏩㏪㏫㏬㏭㏮㏯㏰㏱㏲㏳㏴㏵㏶㏷㏸㏹㏺㏻㏼㏽㏾㏿ +㐀䶵 +䷀䷁䷂䷃䷄䷅䷆䷇䷈䷉䷊䷋䷌䷍䷎䷏䷐䷑䷒䷓䷔䷕䷖䷗䷘䷙䷚䷛䷜䷝䷞䷟䷠䷡䷢䷣䷤䷥䷦䷧䷨䷩䷪䷫䷬䷭䷮䷯䷰䷱䷲䷳䷴䷵䷶䷷䷸䷹䷺䷻䷼䷽䷾䷿ +一鿋ꀀꀁꀂꀃꀄꀅꀆꀇꀈꀉꀊꀋꀌꀍꀎꀏꀐꀑꀒꀓꀔꀕꀖꀗꀘꀙꀚꀛꀜꀝꀞꀟꀠꀡꀢꀣꀤꀥꀦꀧꀨꀩꀪꀫꀬꀭꀮꀯꀰꀱꀲꀳꀴꀵꀶꀷꀸꀹꀺꀻꀼꀽꀾꀿꁀꁁꁂꁃꁄꁅꁆꁇꁈꁉꁊꁋꁌꁍꁎꁏꁐꁑꁒꁓꁔꁕꁖꁗꁘꁙꁚꁛꁜꁝꁞꁟꁠꁡꁢꁣꁤꁥꁦꁧꁨꁩꁪꁫꁬꁭꁮꁯꁰꁱꁲꁳꁴꁵꁶꁷꁸꁹꁺꁻꁼꁽꁾꁿꂀꂁꂂꂃꂄꂅꂆꂇꂈꂉꂊꂋꂌꂍꂎꂏꂐꂑꂒꂓꂔꂕꂖꂗꂘꂙꂚꂛꂜꂝꂞꂟꂠꂡꂢꂣꂤꂥꂦꂧꂨꂩꂪꂫꂬꂭꂮꂯꂰꂱꂲꂳꂴꂵꂶꂷꂸꂹꂺꂻꂼꂽꂾꂿꃀꃁꃂꃃꃄꃅꃆꃇꃈꃉꃊꃋꃌꃍꃎꃏꃐꃑꃒꃓꃔꃕꃖꃗꃘꃙꃚꃛꃜꃝꃞꃟꃠꃡꃢꃣꃤꃥꃦꃧꃨꃩꃪꃫꃬꃭꃮꃯꃰꃱꃲꃳꃴꃵꃶꃷꃸꃹꃺꃻꃼꃽꃾꃿꄀꄁꄂꄃꄄꄅꄆꄇꄈꄉꄊꄋꄌꄍꄎꄏꄐꄑꄒꄓꄔꄕꄖꄗꄘꄙꄚꄛꄜꄝꄞꄟꄠꄡꄢꄣꄤꄥꄦꄧꄨꄩꄪꄫꄬꄭꄮꄯꄰꄱꄲꄳꄴꄵꄶꄷꄸꄹꄺꄻꄼꄽꄾꄿꅀꅁꅂꅃꅄꅅꅆꅇꅈꅉꅊꅋꅌꅍꅎꅏꅐꅑꅒꅓꅔꅕꅖꅗꅘꅙꅚꅛꅜꅝꅞꅟꅠꅡꅢꅣꅤꅥꅦꅧꅨꅩꅪꅫꅬꅭꅮꅯꅰꅱꅲꅳꅴꅵꅶꅷꅸꅹꅺꅻꅼꅽꅾꅿꆀꆁꆂꆃꆄꆅꆆꆇꆈꆉꆊꆋꆌꆍꆎꆏꆐꆑꆒꆓꆔꆕꆖꆗꆘꆙꆚꆛꆜꆝꆞꆟꆠꆡꆢꆣꆤꆥꆦꆧꆨꆩꆪꆫꆬꆭꆮꆯꆰꆱꆲꆳꆴꆵꆶꆷꆸꆹꆺꆻꆼꆽꆾꆿꇀꇁꇂꇃꇄꇅꇆꇇꇈꇉꇊꇋꇌꇍꇎꇏꇐꇑꇒꇓꇔꇕꇖꇗꇘꇙꇚꇛꇜꇝꇞꇟꇠꇡꇢꇣꇤꇥꇦꇧꇨꇩꇪꇫꇬꇭꇮꇯꇰꇱꇲꇳꇴꇵꇶꇷꇸꇹꇺꇻꇼꇽꇾꇿꈀꈁꈂꈃꈄꈅꈆꈇꈈꈉꈊꈋꈌꈍꈎꈏꈐꈑꈒꈓꈔꈕꈖꈗꈘꈙꈚꈛꈜꈝꈞꈟꈠꈡꈢꈣꈤꈥꈦꈧꈨꈩꈪꈫꈬꈭꈮꈯꈰꈱꈲꈳꈴꈵꈶꈷꈸꈹꈺꈻꈼꈽꈾꈿꉀꉁꉂꉃꉄꉅꉆꉇꉈꉉꉊꉋꉌꉍꉎꉏꉐꉑꉒꉓꉔꉕꉖꉗꉘꉙꉚꉛꉜꉝꉞꉟꉠꉡꉢꉣꉤꉥꉦꉧꉨꉩꉪꉫꉬꉭꉮꉯꉰꉱꉲꉳꉴꉵꉶꉷꉸꉹꉺꉻꉼꉽꉾꉿꊀꊁꊂꊃꊄꊅꊆꊇꊈꊉꊊꊋꊌꊍꊎꊏꊐꊑꊒꊓꊔꊕꊖꊗꊘꊙꊚꊛꊜꊝꊞꊟꊠꊡꊢꊣꊤꊥꊦꊧꊨꊩꊪꊫꊬꊭꊮꊯꊰꊱꊲꊳꊴꊵꊶꊷꊸꊹꊺꊻꊼꊽꊾꊿꋀꋁꋂꋃꋄꋅꋆꋇꋈꋉꋊꋋꋌꋍꋎꋏꋐꋑꋒꋓꋔꋕꋖꋗꋘꋙꋚꋛꋜꋝꋞꋟꋠꋡꋢꋣꋤꋥꋦꋧꋨꋩꋪꋫꋬꋭꋮꋯꋰꋱꋲꋳꋴꋵꋶꋷꋸꋹꋺꋻꋼꋽꋾꋿꌀꌁꌂꌃꌄꌅꌆꌇꌈꌉꌊꌋꌌꌍꌎꌏꌐꌑꌒꌓꌔꌕꌖꌗꌘꌙꌚꌛꌜꌝꌞꌟꌠꌡꌢꌣꌤꌥꌦꌧꌨꌩꌪꌫꌬꌭꌮꌯꌰꌱꌲꌳꌴꌵꌶꌷꌸꌹꌺꌻꌼꌽꌾꌿꍀꍁꍂꍃꍄꍅꍆꍇꍈꍉꍊꍋꍌꍍꍎꍏꍐꍑꍒꍓꍔꍕꍖꍗꍘꍙꍚꍛꍜꍝꍞꍟꍠꍡꍢꍣꍤꍥꍦꍧꍨꍩꍪꍫꍬꍭꍮꍯꍰꍱꍲꍳꍴꍵꍶꍷꍸꍹꍺꍻꍼꍽꍾꍿꎀꎁꎂꎃꎄꎅꎆꎇꎈꎉꎊꎋꎌꎍꎎꎏꎐꎑꎒꎓꎔꎕꎖꎗꎘꎙꎚꎛꎜꎝꎞꎟꎠꎡꎢꎣꎤꎥꎦꎧꎨꎩꎪꎫꎬꎭꎮꎯꎰꎱꎲꎳꎴꎵꎶꎷꎸꎹꎺꎻꎼꎽꎾꎿꏀꏁꏂꏃꏄꏅꏆꏇꏈꏉꏊꏋꏌꏍꏎꏏꏐꏑꏒꏓꏔꏕꏖꏗꏘꏙꏚꏛꏜꏝꏞꏟꏠꏡꏢꏣꏤꏥꏦꏧꏨꏩꏪꏫꏬꏭꏮꏯꏰꏱꏲꏳꏴꏵꏶꏷꏸꏹꏺꏻꏼꏽꏾꏿꐀꐁꐂꐃꐄꐅꐆꐇꐈꐉꐊꐋꐌꐍꐎꐏꐐꐑꐒꐓꐔꐕꐖꐗꐘꐙꐚꐛꐜꐝꐞꐟꐠꐡꐢꐣꐤꐥꐦꐧꐨꐩꐪꐫꐬꐭꐮꐯꐰꐱꐲꐳꐴꐵꐶꐷꐸꐹꐺꐻꐼꐽꐾꐿꑀꑁꑂꑃꑄꑅꑆꑇꑈꑉꑊꑋꑌꑍꑎꑏꑐꑑꑒꑓꑔꑕꑖꑗꑘꑙꑚꑛꑜꑝꑞꑟꑠꑡꑢꑣꑤꑥꑦꑧꑨꑩꑪꑫꑬꑭꑮꑯꑰꑱꑲꑳꑴꑵꑶꑷꑸꑹꑺꑻꑼꑽꑾꑿꒀꒁꒂꒃꒄꒅꒆꒇꒈꒉꒊꒋꒌ +꒐꒑꒒꒓꒔꒕꒖꒗꒘꒙꒚꒛꒜꒝꒞꒟꒠꒡꒢꒣꒤꒥꒦꒧꒨꒩꒪꒫꒬꒭꒮꒯꒰꒱꒲꒳꒴꒵꒶꒷꒸꒹꒺꒻꒼꒽꒾꒿꓀꓁꓂꓃꓄꓅꓆ +ꓐꓑꓒꓓꓔꓕꓖꓗꓘꓙꓚꓛꓜꓝꓞꓟꓠꓡꓢꓣꓤꓥꓦꓧꓨꓩꓪꓫꓬꓭꓮꓯꓰꓱꓲꓳꓴꓵꓶꓷꓸꓹꓺꓻꓼꓽ +꓾꓿ +ꔀꔁꔂꔃꔄꔅꔆꔇꔈꔉꔊꔋꔌꔍꔎꔏꔐꔑꔒꔓꔔꔕꔖꔗꔘꔙꔚꔛꔜꔝꔞꔟꔠꔡꔢꔣꔤꔥꔦꔧꔨꔩꔪꔫꔬꔭꔮꔯꔰꔱꔲꔳꔴꔵꔶꔷꔸꔹꔺꔻꔼꔽꔾꔿꕀꕁꕂꕃꕄꕅꕆꕇꕈꕉꕊꕋꕌꕍꕎꕏꕐꕑꕒꕓꕔꕕꕖꕗꕘꕙꕚꕛꕜꕝꕞꕟꕠꕡꕢꕣꕤꕥꕦꕧꕨꕩꕪꕫꕬꕭꕮꕯꕰꕱꕲꕳꕴꕵꕶꕷꕸꕹꕺꕻꕼꕽꕾꕿꖀꖁꖂꖃꖄꖅꖆꖇꖈꖉꖊꖋꖌꖍꖎꖏꖐꖑꖒꖓꖔꖕꖖꖗꖘꖙꖚꖛꖜꖝꖞꖟꖠꖡꖢꖣꖤꖥꖦꖧꖨꖩꖪꖫꖬꖭꖮꖯꖰꖱꖲꖳꖴꖵꖶꖷꖸꖹꖺꖻꖼꖽꖾꖿꗀꗁꗂꗃꗄꗅꗆꗇꗈꗉꗊꗋꗌꗍꗎꗏꗐꗑꗒꗓꗔꗕꗖꗗꗘꗙꗚꗛꗜꗝꗞꗟꗠꗡꗢꗣꗤꗥꗦꗧꗨꗩꗪꗫꗬꗭꗮꗯꗰꗱꗲꗳꗴꗵꗶꗷꗸꗹꗺꗻꗼꗽꗾꗿꘀꘁꘂꘃꘄꘅꘆꘇꘈꘉꘊꘋꘌ +꘍꘎꘏ +ꘐꘑꘒꘓꘔꘕꘖꘗꘘꘙꘚꘛꘜꘝꘞꘟ꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩ꘪꘫꙀꙁꙂꙃꙄꙅꙆꙇꙈꙉꙊꙋꙌꙍꙎꙏꙐꙑꙒꙓꙔꙕꙖꙗꙘꙙꙚꙛꙜꙝꙞꙟꙢꙣꙤꙥꙦꙧꙨꙩꙪꙫꙬꙭꙮ꙯꙰꙱꙲ +꙳ +꙼꙽ +꙾ +ꙿꚀꚁꚂꚃꚄꚅꚆꚇꚈꚉꚊꚋꚌꚍꚎꚏꚐꚑꚒꚓꚔꚕꚖꚗꚠꚡꚢꚣꚤꚥꚦꚧꚨꚩꚪꚫꚬꚭꚮꚯꚰꚱꚲꚳꚴꚵꚶꚷꚸꚹꚺꚻꚼꚽꚾꚿꛀꛁꛂꛃꛄꛅꛆꛇꛈꛉꛊꛋꛌꛍꛎꛏꛐꛑꛒꛓꛔꛕꛖꛗꛘꛙꛚꛛꛜꛝꛞꛟꛠꛡꛢꛣꛤꛥꛦꛧꛨꛩꛪꛫꛬꛭꛮꛯ꛰꛱ +꛲꛳꛴꛵꛶꛷꜀꜁꜂꜃꜄꜅꜆꜇꜈꜉꜊꜋꜌꜍꜎꜏꜐꜑꜒꜓꜔꜕꜖ +ꜗꜘꜙꜚꜛꜜꜝꜞꜟ +꜠꜡ +ꜢꜣꜤꜥꜦꜧꜨꜩꜪꜫꜬꜭꜮꜯꜰꜱꜲꜳꜴꜵꜶꜷꜸꜹꜺꜻꜼꜽꜾꜿꝀꝁꝂꝃꝄꝅꝆꝇꝈꝉꝊꝋꝌꝍꝎꝏꝐꝑꝒꝓꝔꝕꝖꝗꝘꝙꝚꝛꝜꝝꝞꝟꝠꝡꝢꝣꝤꝥꝦꝧꝨꝩꝪꝫꝬꝭꝮꝯꝰꝱꝲꝳꝴꝵꝶꝷꝸꝹꝺꝻꝼꝽꝾꝿꞀꞁꞂꞃꞄꞅꞆꞇꞈ +꞉꞊ +Ꞌꞌꟻꟼꟽꟾꟿꠀꠁꠂꠃꠄꠅ꠆ꠇꠈꠉꠊꠋꠌꠍꠎꠏꠐꠑꠒꠓꠔꠕꠖꠗꠘꠙꠚꠛꠜꠝꠞꠟꠠꠡꠢꠣꠤꠥꠦꠧ +꠨꠩꠪꠫ +꠰꠱꠲꠳꠴꠵ +꠶꠷꠸꠹ +ꡀꡁꡂꡃꡄꡅꡆꡇꡈꡉꡊꡋꡌꡍꡎꡏꡐꡑꡒꡓꡔꡕꡖꡗꡘꡙꡚꡛꡜꡝꡞꡟꡠꡡꡢꡣꡤꡥꡦꡧꡨꡩꡪꡫꡬꡭꡮꡯꡰꡱꡲꡳ +꡴꡵꡶꡷ +ꢀꢁꢂꢃꢄꢅꢆꢇꢈꢉꢊꢋꢌꢍꢎꢏꢐꢑꢒꢓꢔꢕꢖꢗꢘꢙꢚꢛꢜꢝꢞꢟꢠꢡꢢꢣꢤꢥꢦꢧꢨꢩꢪꢫꢬꢭꢮꢯꢰꢱꢲꢳꢴꢵꢶꢷꢸꢹꢺꢻꢼꢽꢾꢿꣀꣁꣂꣃ꣄ +꣎꣏ +꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙꣠꣡꣢꣣꣤꣥꣦꣧꣨꣩꣪꣫꣬꣭꣮꣯꣰꣱ꣲꣳꣴꣵꣶꣷ +꣸꣹꣺ +ꣻ꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉ꤊꤋꤌꤍꤎꤏꤐꤑꤒꤓꤔꤕꤖꤗꤘꤙꤚꤛꤜꤝꤞꤟꤠꤡꤢꤣꤤꤥꤦꤧꤨꤩꤪ꤫꤬꤭ +꤮꤯ +ꤰꤱꤲꤳꤴꤵꤶꤷꤸꤹꤺꤻꤼꤽꤾꤿꥀꥁꥂꥃꥄꥅꥆꥇꥈꥉꥊꥋꥌꥍꥎꥏꥐꥑꥒ꥓ +꥟ +ꥠꥡꥢꥣꥤꥥꥦꥧꥨꥩꥪꥫꥬꥭꥮꥯꥰꥱꥲꥳꥴꥵꥶꥷꥸꥹꥺꥻꥼꦀꦁꦂꦃꦄꦅꦆꦇꦈꦉꦊꦋꦌꦍꦎꦏꦐꦑꦒꦓꦔꦕꦖꦗꦘꦙꦚꦛꦜꦝꦞꦟꦠꦡꦢꦣꦤꦥꦦꦧꦨꦩꦪꦫꦬꦭꦮꦯꦰꦱꦲ꦳ꦴꦵꦶꦷꦸꦹꦺꦻꦼꦽꦾꦿ꧀ +꧁꧂꧃꧄꧅꧆꧇꧈꧉꧊꧋꧌꧍ +ꧏ꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙ +꧞꧟ +ꨀꨁꨂꨃꨄꨅꨆꨇꨈꨉꨊꨋꨌꨍꨎꨏꨐꨑꨒꨓꨔꨕꨖꨗꨘꨙꨚꨛꨜꨝꨞꨟꨠꨡꨢꨣꨤꨥꨦꨧꨨꨩꨪꨫꨬꨭꨮꨯꨰꨱꨲꨳꨴꨵꨶꩀꩁꩂꩃꩄꩅꩆꩇꩈꩉꩊꩋꩌꩍ꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙ +꩜꩝꩞꩟ +ꩠꩡꩢꩣꩤꩥꩦꩧꩨꩩꩪꩫꩬꩭꩮꩯꩰꩱꩲꩳꩴꩵꩶ +꩷꩸꩹ +ꩺꩻꪀꪁꪂꪃꪄꪅꪆꪇꪈꪉꪊꪋꪌꪍꪎꪏꪐꪑꪒꪓꪔꪕꪖꪗꪘꪙꪚꪛꪜꪝꪞꪟꪠꪡꪢꪣꪤꪥꪦꪧꪨꪩꪪꪫꪬꪭꪮꪯꪰꪱꪴꪲꪳꪵꪶꪷꪸꪹꪺꪻꪼꪽꪾ꪿ꫀ꫁ꫂꫛꫜꫝ +꫞꫟ +ꯀꯁꯂꯃꯄꯅꯆꯇꯈꯉꯊꯋꯌꯍꯎꯏꯐꯑꯒꯓꯔꯕꯖꯗꯘꯙꯚꯛꯜꯝꯞꯟꯠꯡꯢꯣꯤꯥꯦꯧꯨꯩꯪ +꯫ +꯬꯭꯰꯱꯲꯳꯴꯵꯶꯷꯸꯹가힣ힰힱힲힳힴힵힶힷힸힹힺힻힼힽힾힿퟀퟁퟂퟃퟄퟅퟆퟋퟌퟍퟎퟏퟐퟑퟒퟓퟔퟕퟖퟗퟘퟙퟚퟛퟜퟝퟞퟟퟠퟡퟢퟣퟤퟥퟦퟧퟨퟩퟪퟫퟬퟭퟮퟯퟰퟱퟲퟳퟴퟵퟶퟷퟸퟹퟺퟻ + +豈更車賈滑串句龜龜契金喇奈懶癩羅蘿螺裸邏樂洛烙珞落酪駱亂卵欄爛蘭鸞嵐濫藍襤拉臘蠟廊朗浪狼郎來冷勞擄櫓爐盧老蘆虜路露魯鷺碌祿綠菉錄鹿論壟弄籠聾牢磊賂雷壘屢樓淚漏累縷陋勒肋凜凌稜綾菱陵讀拏樂諾丹寧怒率異北磻便復不泌數索參塞省葉說殺辰沈拾若掠略亮兩凉梁糧良諒量勵呂女廬旅濾礪閭驪麗黎力曆歷轢年憐戀撚漣煉璉秊練聯輦蓮連鍊列劣咽烈裂說廉念捻殮簾獵令囹寧嶺怜玲瑩羚聆鈴零靈領例禮醴隸惡了僚寮尿料樂燎療蓼遼龍暈阮劉杻柳流溜琉留硫紐類六戮陸倫崙淪輪律慄栗率隆利吏履易李梨泥理痢罹裏裡里離匿溺吝燐璘藺隣鱗麟林淋臨立笠粒狀炙識什茶刺切度拓糖宅洞暴輻行降見廓兀嗀﨎﨏塚﨑晴﨓﨔凞猪益礼神祥福靖精羽﨟蘒﨡諸﨣﨤逸都﨧﨨﨩飯飼館鶴侮僧免勉勤卑喝嘆器塀墨層屮悔慨憎懲敏既暑梅海渚漢煮爫琢碑社祉祈祐祖祝禍禎穀突節練縉繁署者臭艹艹著褐視謁謹賓贈辶逸難響頻恵𤋮舘並况全侀充冀勇勺喝啕喙嗢塚墳奄奔婢嬨廒廙彩徭惘慎愈憎慠懲戴揄搜摒敖晴朗望杖歹殺流滛滋漢瀞煮瞧爵犯猪瑱甆画瘝瘟益盛直睊着磌窱節类絛練缾者荒華蝹襁覆視調諸請謁諾諭謹變贈輸遲醙鉶陼難靖韛響頋頻鬒龜𢡊𢡄𣏕㮝䀘䀹𥉉𥳐𧻓齃龎fffiflffifflſtstﬓﬔﬕﬖﬗיִﬞײַﬠﬡﬢﬣﬤﬥﬦﬧﬨ +﬩ +שׁשׂשּׁשּׂאַאָאּבּגּדּהּוּזּטּיּךּכּלּמּנּסּףּפּצּקּרּשּתּוֹבֿכֿפֿﭏﭐﭑﭒﭓﭔﭕﭖﭗﭘﭙﭚﭛﭜﭝﭞﭟﭠﭡﭢﭣﭤﭥﭦﭧﭨﭩﭪﭫﭬﭭﭮﭯﭰﭱﭲﭳﭴﭵﭶﭷﭸﭹﭺﭻﭼﭽﭾﭿﮀﮁﮂﮃﮄﮅﮆﮇﮈﮉﮊﮋﮌﮍﮎﮏﮐﮑﮒﮓﮔﮕﮖﮗﮘﮙﮚﮛﮜﮝﮞﮟﮠﮡﮢﮣﮤﮥﮦﮧﮨﮩﮪﮫﮬﮭﮮﮯﮰﮱﯓﯔﯕﯖﯗﯘﯙﯚﯛﯜﯝﯞﯟﯠﯡﯢﯣﯤﯥﯦﯧﯨﯩﯪﯫﯬﯭﯮﯯﯰﯱﯲﯳﯴﯵﯶﯷﯸﯹﯺﯻﯼﯽﯾﯿﰀﰁﰂﰃﰄﰅﰆﰇﰈﰉﰊﰋﰌﰍﰎﰏﰐﰑﰒﰓﰔﰕﰖﰗﰘﰙﰚﰛﰜﰝﰞﰟﰠﰡﰢﰣﰤﰥﰦﰧﰨﰩﰪﰫﰬﰭﰮﰯﰰﰱﰲﰳﰴﰵﰶﰷﰸﰹﰺﰻﰼﰽﰾﰿﱀﱁﱂﱃﱄﱅﱆﱇﱈﱉﱊﱋﱌﱍﱎﱏﱐﱑﱒﱓﱔﱕﱖﱗﱘﱙﱚﱛﱜﱝﱞﱟﱠﱡﱢﱣﱤﱥﱦﱧﱨﱩﱪﱫﱬﱭﱮﱯﱰﱱﱲﱳﱴﱵﱶﱷﱸﱹﱺﱻﱼﱽﱾﱿﲀﲁﲂﲃﲄﲅﲆﲇﲈﲉﲊﲋﲌﲍﲎﲏﲐﲑﲒﲓﲔﲕﲖﲗﲘﲙﲚﲛﲜﲝﲞﲟﲠﲡﲢﲣﲤﲥﲦﲧﲨﲩﲪﲫﲬﲭﲮﲯﲰﲱﲲﲳﲴﲵﲶﲷﲸﲹﲺﲻﲼﲽﲾﲿﳀﳁﳂﳃﳄﳅﳆﳇﳈﳉﳊﳋﳌﳍﳎﳏﳐﳑﳒﳓﳔﳕﳖﳗﳘﳙﳚﳛﳜﳝﳞﳟﳠﳡﳢﳣﳤﳥﳦﳧﳨﳩﳪﳫﳬﳭﳮﳯﳰﳱﳲﳳﳴﳵﳶﳷﳸﳹﳺﳻﳼﳽﳾﳿﴀﴁﴂﴃﴄﴅﴆﴇﴈﴉﴊﴋﴌﴍﴎﴏﴐﴑﴒﴓﴔﴕﴖﴗﴘﴙﴚﴛﴜﴝﴞﴟﴠﴡﴢﴣﴤﴥﴦﴧﴨﴩﴪﴫﴬﴭﴮﴯﴰﴱﴲﴳﴴﴵﴶﴷﴸﴹﴺﴻﴼﴽ +﴾﴿ +ﵐﵑﵒﵓﵔﵕﵖﵗﵘﵙﵚﵛﵜﵝﵞﵟﵠﵡﵢﵣﵤﵥﵦﵧﵨﵩﵪﵫﵬﵭﵮﵯﵰﵱﵲﵳﵴﵵﵶﵷﵸﵹﵺﵻﵼﵽﵾﵿﶀﶁﶂﶃﶄﶅﶆﶇﶈﶉﶊﶋﶌﶍﶎﶏﶒﶓﶔﶕﶖﶗﶘﶙﶚﶛﶜﶝﶞﶟﶠﶡﶢﶣﶤﶥﶦﶧﶨﶩﶪﶫﶬﶭﶮﶯﶰﶱﶲﶳﶴﶵﶶﶷﶸﶹﶺﶻﶼﶽﶾﶿﷀﷁﷂﷃﷄﷅﷆﷇﷰﷱﷲﷳﷴﷵﷶﷷﷸﷹﷺﷻ +﷼﷽ +︀︁︂︃︄︅︆︇︈︉︊︋︌︍︎️ +︐︑︒︓︔︕︖︗︘︙ +︠︡︢︣︤︥︦ +︰︱︲︳︴︵︶︷︸︹︺︻︼︽︾︿﹀﹁﹂﹃﹄﹅﹆﹇﹈﹉﹊﹋﹌﹍﹎﹏﹐﹑﹒﹔﹕﹖﹗﹘﹙﹚﹛﹜﹝﹞﹟﹠﹡﹢﹣﹤﹥﹦﹨﹩﹪﹫ +ﹰﹱﹲﹳﹴﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ +!"#$%&'()*+,-./ +0123456789 +:;<=>?@ +ABCDEFGHIJKLMNOPQRSTUVWXYZ +[\]^_` +abcdefghijklmnopqrstuvwxyz +{|}~⦅⦆。「」、・ +ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚ᅠᄀᄁᆪᄂᆬᆭᄃᄄᄅᆰᆱᆲᆳᆴᆵᄚᄆᄇᄈᄡᄉᄊᄋᄌᄍᄎᄏᄐᄑ하ᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵ +¢£¬ ̄¦¥₩│←↑→↓■○� +𐀀 \ No newline at end of file diff --git a/drupal7/web/modules/search/tests/search_embedded_form.info b/drupal7/web/modules/search/tests/search_embedded_form.info new file mode 100644 index 0000000..5c2c65e --- /dev/null +++ b/drupal7/web/modules/search/tests/search_embedded_form.info @@ -0,0 +1,11 @@ +name = "Search embedded form" +description = "Support module for search module testing of embedded forms." +package = Testing +version = VERSION +core = 7.x +hidden = TRUE + +; Information added by Drupal.org packaging script on 2024-03-06 +version = "7.100" +project = "drupal" +datestamp = "1709734591" diff --git a/drupal7/web/modules/search/tests/search_embedded_form.module b/drupal7/web/modules/search/tests/search_embedded_form.module new file mode 100644 index 0000000..4845796 --- /dev/null +++ b/drupal7/web/modules/search/tests/search_embedded_form.module @@ -0,0 +1,70 @@ + 'Search_Embed_Form', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('search_embedded_form_form'), + 'access arguments' => array('search content'), + 'type' => MENU_CALLBACK, + ); + + return $items; +} + +/** + * Builds a form for embedding in search results for testing. + * + * @see search_embedded_form_form_submit(). + */ +function search_embedded_form_form($form, &$form_state) { + $count = variable_get('search_embedded_form_submitted', 0); + + $form['name'] = array( + '#type' => 'textfield', + '#title' => t('Your name'), + '#maxlength' => 255, + '#default_value' => '', + '#required' => TRUE, + '#description' => t('Times form has been submitted: %count', array('%count' => $count)), + ); + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Send away'), + ); + + $form['#submit'][] = 'search_embedded_form_form_submit'; + + return $form; +} + +/** + * Submit handler for search_embedded_form_form(). + */ +function search_embedded_form_form_submit($form, &$form_state) { + $count = variable_get('search_embedded_form_submitted', 0) + 1; + variable_set('search_embedded_form_submitted', $count); + drupal_set_message(t('Test form was submitted')); +} + +/** + * Adds the test form to search results. + */ +function search_embedded_form_preprocess_search_result(&$variables) { + $form = drupal_get_form('search_embedded_form_form'); + $variables['snippet'] .= drupal_render($form); +} diff --git a/drupal7/web/modules/search/tests/search_extra_type.info b/drupal7/web/modules/search/tests/search_extra_type.info new file mode 100644 index 0000000..5eb387b --- /dev/null +++ b/drupal7/web/modules/search/tests/search_extra_type.info @@ -0,0 +1,11 @@ +name = "Test search type" +description = "Support module for search module testing." +package = Testing +version = VERSION +core = 7.x +hidden = TRUE + +; Information added by Drupal.org packaging script on 2024-03-06 +version = "7.100" +project = "drupal" +datestamp = "1709734591" diff --git a/drupal7/web/modules/search/tests/search_extra_type.module b/drupal7/web/modules/search/tests/search_extra_type.module new file mode 100644 index 0000000..80c050c --- /dev/null +++ b/drupal7/web/modules/search/tests/search_extra_type.module @@ -0,0 +1,69 @@ + 'Dummy search type', + 'path' => 'dummy_path', + 'conditions_callback' => 'search_extra_type_conditions', + ); +} + +/** + * Test conditions callback for hook_search_info(). + */ +function search_extra_type_conditions() { + $conditions = array(); + + if (!empty($_REQUEST['search_conditions'])) { + $conditions['search_conditions'] = $_REQUEST['search_conditions']; + } + return $conditions; +} + +/** + * Implements hook_search_execute(). + * + * This is a dummy search, so when search "executes", we just return a dummy + * result containing the keywords and a list of conditions. + */ +function search_extra_type_search_execute($keys = NULL, $conditions = NULL) { + if (!$keys) { + $keys = ''; + } + return array( + array( + 'link' => url('node'), + 'type' => 'Dummy result type', + 'title' => 'Dummy title', + 'snippet' => "Dummy search snippet to display. Keywords: {$keys}\n\nConditions: " . print_r($conditions, TRUE), + ), + ); +} + +/** + * Implements hook_search_page(). + * + * Adds some text to the search page so we can verify that it runs. + */ +function search_extra_type_search_page($results) { + $output['prefix']['#markup'] = '

    Test page text is here

      '; + + foreach ($results as $entry) { + $output[] = array( + '#theme' => 'search_result', + '#result' => $entry, + '#module' => 'search_extra_type', + ); + } + $output['suffix']['#markup'] = '
    ' . theme('pager'); + + return $output; +} diff --git a/drupal7/web/modules/search/tests/search_node_tags.info b/drupal7/web/modules/search/tests/search_node_tags.info new file mode 100644 index 0000000..ffdf6f9 --- /dev/null +++ b/drupal7/web/modules/search/tests/search_node_tags.info @@ -0,0 +1,11 @@ +name = "Test search node tags" +description = "Support module for Node search tags testing." +package = Testing +version = VERSION +core = 7.x +hidden = TRUE + +; Information added by Drupal.org packaging script on 2024-03-06 +version = "7.100" +project = "drupal" +datestamp = "1709734591" diff --git a/drupal7/web/modules/search/tests/search_node_tags.module b/drupal7/web/modules/search/tests/search_node_tags.module new file mode 100644 index 0000000..b66dd9e --- /dev/null +++ b/drupal7/web/modules/search/tests/search_node_tags.module @@ -0,0 +1,23 @@ +hasTag('search_node')) { + variable_set('search_node_tags_test_query_tag', TRUE); + } +} + +/** + * Implements hook_query_TAG_alter(). + */ +function search_node_tags_query_search_node_alter(QueryAlterableInterface $query) { + variable_set('search_node_tags_test_query_tag_hook', TRUE); +} diff --git a/drupal7/web/modules/shortcut/shortcut-rtl.css b/drupal7/web/modules/shortcut/shortcut-rtl.css new file mode 100644 index 0000000..5dec957 --- /dev/null +++ b/drupal7/web/modules/shortcut/shortcut-rtl.css @@ -0,0 +1,48 @@ + +div#toolbar a#edit-shortcuts { + position: absolute; + left: 0; + top: 0; + padding: 5px 5px 5px 10px; +} +div#toolbar div.toolbar-shortcuts ul { + float: none; + margin-right: 5px; + margin-left: 10em; +} +div#toolbar div.toolbar-shortcuts ul li a { + margin-left: 5px; + margin-right: 0; + padding: 0 5px; +} +div#toolbar div.toolbar-shortcuts span.icon { + float: right; +} +div.add-or-remove-shortcuts a span.icon { + float: right; + margin-right: 8px; + margin-left: 0; +} +div.add-or-remove-shortcuts a span.text { + float: right; + padding-right: 10px; + padding-left: 0; +} +div.add-or-remove-shortcuts a:focus span.text, +div.add-or-remove-shortcuts a:hover span.text { + -moz-border-radius: 5px 0 0 5px; + -webkit-border-top-left-radius: 5px; + -webkit-border-bottom-left-radius: 5px; + border-radius: 5px 0 0 5px; + padding-left: 6px; +} +#shortcut-set-switch .form-item-new { + padding-right: 17px; + padding-left: 0; +} +div.add-shortcut a:hover span.icon { + background-position: 0 -24px; +} +div.remove-shortcut a:hover span.icon { + background-position: -12px -24px; +} diff --git a/drupal7/web/modules/shortcut/shortcut.admin.css b/drupal7/web/modules/shortcut/shortcut.admin.css new file mode 100644 index 0000000..8ca03be --- /dev/null +++ b/drupal7/web/modules/shortcut/shortcut.admin.css @@ -0,0 +1,8 @@ + +.shortcut-slot-hidden { + display: none; +} + +div.form-item-set div.form-item-new { + display: inline; +} diff --git a/drupal7/web/modules/shortcut/shortcut.admin.inc b/drupal7/web/modules/shortcut/shortcut.admin.inc new file mode 100644 index 0000000..2e8ddb4 --- /dev/null +++ b/drupal7/web/modules/shortcut/shortcut.admin.inc @@ -0,0 +1,788 @@ + $set) { + $options[$name] = check_plain($set->title); + } + + // Only administrators can add shortcut sets. + $add_access = user_access('administer shortcuts'); + if ($add_access) { + $options['new'] = t('New set'); + } + + if (count($options) > 1) { + $form['account'] = array( + '#type' => 'value', + '#value' => $account, + ); + + $form['set'] = array( + '#type' => 'radios', + '#title' => $user->uid == $account->uid ? t('Choose a set of shortcuts to use') : t('Choose a set of shortcuts for this user'), + '#options' => $options, + '#default_value' => $current_set->set_name, + ); + + $form['new'] = array( + '#type' => 'textfield', + '#title' => t('Name'), + '#title_display' => 'invisible', + '#description' => t('The new set is created by copying items from your default shortcut set.'), + '#access' => $add_access, + ); + + if ($user->uid != $account->uid) { + $default_set = shortcut_default_set($account); + $form['new']['#description'] = t('The new set is created by copying items from the %default set.', array('%default' => $default_set->title)); + } + + $form['#attached'] = array( + 'css' => array(drupal_get_path('module', 'shortcut') . '/shortcut.admin.css'), + 'js' => array(drupal_get_path('module', 'shortcut') . '/shortcut.admin.js'), + ); + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Change set'), + ); + } + else { + // There is only 1 option, so output a message in the $form array. + $form['info'] = array( + '#markup' => '

    ' . t('You are currently using the %set-name shortcut set.', array('%set-name' => $current_set->title)) . '

    ', + ); + } + + return $form; +} + +/** + * Validation handler for shortcut_set_switch(). + */ +function shortcut_set_switch_validate($form, &$form_state) { + if ($form_state['values']['set'] == 'new') { + // Check to prevent creating a shortcut set with an empty title. + if (trim($form_state['values']['new']) == '') { + form_set_error('new', t('The new set name is required.')); + } + // Check to prevent a duplicate title. + if (shortcut_set_title_exists($form_state['values']['new'])) { + form_set_error('new', t('The shortcut set %name already exists. Choose another name.', array('%name' => $form_state['values']['new']))); + } + } +} + +/** + * Submit handler for shortcut_set_switch(). + */ +function shortcut_set_switch_submit($form, &$form_state) { + global $user; + $account = $form_state['values']['account']; + + if ($form_state['values']['set'] == 'new') { + // Save a new shortcut set with links copied from the user's default set. + $default_set = shortcut_default_set($account); + $set = (object) array( + 'title' => $form_state['values']['new'], + 'links' => menu_links_clone($default_set->links), + ); + shortcut_set_save($set); + $replacements = array( + '%user' => $account->name, + '%set_name' => $set->title, + '@switch-url' => url(current_path()), + ); + if ($account->uid == $user->uid) { + // Only administrators can create new shortcut sets, so we know they have + // access to switch back. + drupal_set_message(t('You are now using the new %set_name shortcut set. You can edit it from this page or switch back to a different one.', $replacements)); + } + else { + drupal_set_message(t('%user is now using a new shortcut set called %set_name. You can edit it from this page.', $replacements)); + } + $form_state['redirect'] = 'admin/config/user-interface/shortcut/' . $set->set_name; + } + else { + // Switch to a different shortcut set. + $set = shortcut_set_load($form_state['values']['set']); + $replacements = array( + '%user' => $account->name, + '%set_name' => $set->title, + ); + drupal_set_message($account->uid == $user->uid ? t('You are now using the %set_name shortcut set.', $replacements) : t('%user is now using the %set_name shortcut set.', $replacements)); + } + + // Assign the shortcut set to the provided user account. + shortcut_set_assign_user($set, $account); +} + +/** + * Menu page callback: builds the page for administering shortcut sets. + */ +function shortcut_set_admin() { + $shortcut_sets = shortcut_sets(); + $header = array(t('Name'), array('data' => t('Operations'), 'colspan' => 4)); + + $rows = array(); + foreach ($shortcut_sets as $set) { + $row = array( + check_plain($set->title), + l(t('list links'), "admin/config/user-interface/shortcut/$set->set_name"), + l(t('edit set name'), "admin/config/user-interface/shortcut/$set->set_name/edit"), + ); + if (shortcut_set_delete_access($set)) { + $row[] = l(t('delete set'), "admin/config/user-interface/shortcut/$set->set_name/delete"); + } + else { + $row[] = ''; + } + + $rows[] = $row; + } + + return theme('table', array('header' => $header, 'rows' => $rows)); +} + +/** + * Form callback: builds the form for adding a shortcut set. + * + * @param $form + * An associative array containing the structure of the form. + * @param $form_state + * An associative array containing the current state of the form. + * + * @return + * An array representing the form definition. + * + * @ingroup forms + * @see shortcut_set_add_form_validate() + * @see shortcut_set_add_form_submit() + */ +function shortcut_set_add_form($form, &$form_state) { + $form['new'] = array( + '#type' => 'textfield', + '#title' => t('Set name'), + '#description' => t('The new set is created by copying items from your default shortcut set.'), + '#required' => TRUE, + ); + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Create new set'), + ); + + return $form; +} + +/** + * Validation handler for shortcut_set_add_form(). + */ +function shortcut_set_add_form_validate($form, &$form_state) { + // Check to prevent a duplicate title. + if (shortcut_set_title_exists($form_state['values']['new'])) { + form_set_error('new', t('The shortcut set %name already exists. Choose another name.', array('%name' => $form_state['values']['new']))); + } +} + +/** + * Submit handler for shortcut_set_add_form(). + */ +function shortcut_set_add_form_submit($form, &$form_state) { + // Save a new shortcut set with links copied from the user's default set. + $default_set = shortcut_default_set(); + $set = (object) array( + 'title' => $form_state['values']['new'], + 'links' => menu_links_clone($default_set->links), + ); + shortcut_set_save($set); + drupal_set_message(t('The %set_name shortcut set has been created. You can edit it from this page.', array('%set_name' => $set->title))); + $form_state['redirect'] = 'admin/config/user-interface/shortcut/' . $set->set_name; +} + +/** + * Form callback: builds the form for customizing shortcut sets. + * + * @param $form + * An associative array containing the structure of the form. + * @param $form_state + * An associative array containing the current state of the form. + * @param $shortcut_set + * An object representing the shortcut set which is being edited. + * + * @return + * An array representing the form definition. + * + * @ingroup forms + * @see shortcut_set_customize_submit() + */ +function shortcut_set_customize($form, &$form_state, $shortcut_set) { + $form['#shortcut_set_name'] = $shortcut_set->set_name; + $form['shortcuts'] = array( + '#tree' => TRUE, + '#weight' => -20, + 'enabled' => array(), + 'disabled' => array(), + ); + + foreach ($shortcut_set->links as $link) { + $mlid = $link['mlid']; + $status = $link['hidden'] ? 'disabled' : 'enabled'; + $form['shortcuts'][$status][$mlid]['name']['#markup'] = l($link['link_title'], $link['link_path']); + $form['shortcuts'][$status][$mlid]['weight'] = array( + '#type' => 'weight', + '#title' => t('Weight'), + '#delta' => 50, + '#default_value' => $link['weight'], + '#attributes' => array('class' => array('shortcut-weight')), + ); + $form['shortcuts'][$status][$mlid]['status'] = array( + '#type' => 'select', + '#title' => t('Status'), + '#options' => array('disabled' => t('Disabled'), 'enabled' => t('Enabled')), + '#default_value' => $status, + '#attributes' => array('class' => array('shortcut-status-select')), + ); + + $form['shortcuts'][$status][$mlid]['edit']['#markup'] = l(t('edit'), 'admin/config/user-interface/shortcut/link/' . $mlid); + $form['shortcuts'][$status][$mlid]['delete']['#markup'] = l(t('delete'), 'admin/config/user-interface/shortcut/link/' . $mlid . '/delete'); + } + + $form['#attached'] = array( + 'css' => array(drupal_get_path('module', 'shortcut') . '/shortcut.admin.css'), + 'js' => array(drupal_get_path('module', 'shortcut') . '/shortcut.admin.js'), + ); + + $form['actions'] = array( + '#type' => 'actions', + '#access' => !empty($shortcut_set->links), + ); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save changes'), + ); + + return $form; +} + +/** + * Submit handler for shortcut_set_customize(). + */ +function shortcut_set_customize_submit($form, &$form_state) { + foreach ($form_state['values']['shortcuts'] as $group => $links) { + foreach ($links as $mlid => $data) { + $link = menu_link_load($mlid); + $link['hidden'] = $data['status'] == 'enabled' ? 0 : 1; + $link['weight'] = $data['weight']; + menu_link_save($link); + } + } + drupal_set_message(t('The shortcut set has been updated.')); +} + +/** + * Returns HTML for a shortcut set customization form. + * + * @param $variables + * An associative array containing: + * - form: A render element representing the form. + * + * @see shortcut_set_customize() + * @ingroup themeable + */ +function theme_shortcut_set_customize($variables) { + $form = $variables['form']; + $map = array('disabled' => t('Disabled'), 'enabled' => t('Enabled')); + $shortcuts_by_status = array( + 'enabled' => element_children($form['shortcuts']['enabled']), + 'disabled' => element_children($form['shortcuts']['disabled']), + ); + // Do not add any rows to the table if there are no shortcuts to display. + $statuses = empty($shortcuts_by_status['enabled']) && empty($shortcuts_by_status['disabled']) ? array() : array_keys($shortcuts_by_status); + + $rows = array(); + foreach ($statuses as $status) { + drupal_add_tabledrag('shortcuts', 'match', 'sibling', 'shortcut-status-select'); + drupal_add_tabledrag('shortcuts', 'order', 'sibling', 'shortcut-weight'); + $rows[] = array( + 'data' => array(array( + 'colspan' => 5, + 'data' => '' . $map[$status] . '', + )), + 'class' => array('shortcut-status', 'shortcut-status-' . $status), + ); + + foreach ($shortcuts_by_status[$status] as $key) { + $shortcut = &$form['shortcuts'][$status][$key]; + $row = array(); + $row[] = drupal_render($shortcut['name']); + $row[] = drupal_render($shortcut['weight']); + $row[] = drupal_render($shortcut['status']); + $row[] = drupal_render($shortcut['edit']); + $row[] = drupal_render($shortcut['delete']); + $rows[] = array( + 'data' => $row, + 'class' => array('draggable'), + ); + } + + if ($status == 'enabled') { + for ($i = 0; $i < shortcut_max_slots(); $i++) { + $rows['empty-' . $i] = array( + 'data' => array(array( + 'colspan' => 5, + 'data' => '' . t('Empty') . '', + )), + 'class' => array('shortcut-slot-empty'), + ); + } + $count_shortcuts = count($shortcuts_by_status[$status]); + if (!empty($count_shortcuts)) { + for ($i = 0; $i < min($count_shortcuts, shortcut_max_slots()); $i++) { + $rows['empty-' . $i]['class'][] = 'shortcut-slot-hidden'; + } + } + } + } + + $header = array(t('Name'), t('Weight'), t('Status'), array('data' => t('Operations'), 'colspan' => 2)); + $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'shortcuts'), 'empty' => t('No shortcuts available. Add a shortcut.', array('@link' => url('admin/config/user-interface/shortcut/' . $form['#shortcut_set_name'] . '/add-link'))))); + $output .= drupal_render($form['actions']); + $output = drupal_render_children($form) . $output; + return $output; +} + +/** + * Form callback: builds the form for adding a new shortcut link. + * + * @param $form + * An associative array containing the structure of the form. + * @param $form_state + * An associative array containing the current state of the form. + * @param $shortcut_set + * An object representing the shortcut set to which the link will be added. + * + * @return + * An array representing the form definition. + * + * @ingroup forms + * @see shortcut_link_edit_validate() + * @see shortcut_link_add_submit() + */ +function shortcut_link_add($form, &$form_state, $shortcut_set) { + drupal_set_title(t('Add new shortcut')); + $form['shortcut_set'] = array( + '#type' => 'value', + '#value' => $shortcut_set, + ); + $form += _shortcut_link_form_elements(); + return $form; +} + +/** + * Form callback: builds the form for editing a shortcut link. + * + * @param $form + * An associative array containing the structure of the form. + * @param $form_state + * An associative array containing the current state of the form. + * @param $shortcut_link + * An array representing the link that is being edited. + * + * @return + * An array representing the form definition. + * + * @ingroup forms + * @see shortcut_link_edit_validate() + * @see shortcut_link_edit_submit() + */ +function shortcut_link_edit($form, &$form_state, $shortcut_link) { + drupal_set_title(t('Editing @shortcut', array('@shortcut' => $shortcut_link['link_title']))); + $form['original_shortcut_link'] = array( + '#type' => 'value', + '#value' => $shortcut_link, + ); + $form += _shortcut_link_form_elements($shortcut_link); + return $form; +} + +/** + * Helper function for building a form for adding or editing shortcut links. + * + * @param $shortcut_link + * (optional) An array representing the shortcut link that will be edited. If + * not provided, a new link will be created. + * + * @return + * An array of form elements. + */ +function _shortcut_link_form_elements($shortcut_link = NULL) { + if (!isset($shortcut_link)) { + $shortcut_link = array( + 'link_title' => '', + 'link_path' => '' + ); + } + else { + $shortcut_link['link_path'] = ($shortcut_link['link_path'] == '') ? '' : drupal_get_path_alias($shortcut_link['link_path']); + } + + $form['shortcut_link']['#tree'] = TRUE; + $form['shortcut_link']['link_title'] = array( + '#type' => 'textfield', + '#title' => t('Name'), + '#description' => t('The name of the shortcut.'), + '#size' => 40, + '#maxlength' => 255, + '#default_value' => $shortcut_link['link_title'], + '#required' => TRUE, + ); + + $form['shortcut_link']['link_path'] = array( + '#type' => 'textfield', + '#title' => t('Path'), + '#description' => t('The path to the shortcut.'), + '#size' => 40, + '#maxlength' => 255, + '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='), + '#default_value' => $shortcut_link['link_path'], + ); + + $form['#validate'][] = 'shortcut_link_edit_validate'; + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save'), + ); + + return $form; +} + +/** + * Validation handler for the shortcut link add and edit forms. + */ +function shortcut_link_edit_validate($form, &$form_state) { + if (!shortcut_valid_link($form_state['values']['shortcut_link']['link_path'])) { + form_set_error('shortcut_link][link_path', t('The link must correspond to a valid path on the site.')); + } +} + +/** + * Submit handler for shortcut_link_edit(). + */ +function shortcut_link_edit_submit($form, &$form_state) { + // Normalize the path in case it is an alias. + $shortcut_path = drupal_get_normal_path($form_state['values']['shortcut_link']['link_path']); + if (empty($shortcut_path)) { + $shortcut_path = ''; + } + $form_state['values']['shortcut_link']['link_path'] = $shortcut_path; + + $shortcut_link = array_merge($form_state['values']['original_shortcut_link'], $form_state['values']['shortcut_link']); + + menu_link_save($shortcut_link); + $form_state['redirect'] = 'admin/config/user-interface/shortcut/' . $shortcut_link['menu_name']; + drupal_set_message(t('The shortcut %link has been updated.', array('%link' => $shortcut_link['link_title']))); +} + +/** + * Submit handler for shortcut_link_add(). + */ +function shortcut_link_add_submit($form, &$form_state) { + // Add the shortcut link to the set. + $shortcut_set = $form_state['values']['shortcut_set']; + $shortcut_link = $form_state['values']['shortcut_link']; + $shortcut_link['menu_name'] = $shortcut_set->set_name; + shortcut_admin_add_link($shortcut_link, $shortcut_set, shortcut_max_slots()); + shortcut_set_save($shortcut_set); + $form_state['redirect'] = 'admin/config/user-interface/shortcut/' . $shortcut_link['menu_name']; + drupal_set_message(t('Added a shortcut for %title.', array('%title' => $shortcut_link['link_title']))); +} + +/** + * Adds a link to the end of a shortcut set, keeping within a prescribed limit. + * + * @param $link + * An array representing a shortcut link. + * @param $shortcut_set + * An object representing the shortcut set which the link will be added to. + * The links in the shortcut set will be re-weighted so that the new link is + * at the end, and some existing links may be disabled (if the $limit + * parameter is provided). + * @param $limit + * (optional) The maximum number of links that are allowed to be enabled for + * this shortcut set. If provided, existing links at the end of the list that + * exceed the limit will be automatically disabled. If not provided, no limit + * will be enforced. + */ +function shortcut_admin_add_link($shortcut_link, &$shortcut_set, $limit = NULL) { + if (isset($limit)) { + // Disable any existing links at the end of the list that would cause the + // limit to be exceeded. Take into account whether or not the new link will + // be enabled and count towards the total. + $number_enabled = !empty($shortcut_link['hidden']) ? 0 : 1; + foreach ($shortcut_set->links as &$link) { + if (!$link['hidden']) { + $number_enabled++; + if ($number_enabled > $limit) { + $link['hidden'] = 1; + } + } + } + } + + // Normalize the path in case it is an alias. + $shortcut_link['link_path'] = drupal_get_normal_path($shortcut_link['link_path']); + if (empty($shortcut_link['link_path'])) { + $shortcut_link['link_path'] = ''; + } + + // Add the link to the end of the list. + $shortcut_set->links[] = $shortcut_link; + shortcut_set_reset_link_weights($shortcut_set); +} + +/** + * Form callback: builds the form for editing the shortcut set name. + * + * @param $form + * An associative array containing the structure of the form. + * @param $form_state + * An associative array containing the current state of the form. + * @param object $shortcut_set + * An object representing the shortcut set, as returned from + * shortcut_set_load(). + * + * @return + * An array representing the form definition. + * + * @ingroup forms + * @see shortcut_set_edit_form_validate() + * @see shortcut_set_edit_form_submit() + */ +function shortcut_set_edit_form($form, &$form_state, $shortcut_set) { + $form['shortcut_set'] = array( + '#type' => 'value', + '#value' => $shortcut_set, + ); + $form['title'] = array( + '#type' => 'textfield', + '#title' => t('Set name'), + '#default_value' => $shortcut_set->title, + '#maxlength' => 255, + '#required' => TRUE, + '#weight' => -5, + ); + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save'), + '#weight' => 5, + ); + + return $form; +} + +/** + * Validation handler for shortcut_set_edit_form(). + */ +function shortcut_set_edit_form_validate($form, &$form_state) { + // Check to prevent a duplicate title, if the title was edited from its + // original value. + if ($form_state['values']['title'] != $form_state['values']['shortcut_set']->title && shortcut_set_title_exists($form_state['values']['title'])) { + form_set_error('title', t('The shortcut set %name already exists. Choose another name.', array('%name' => $form_state['values']['title']))); + } +} + +/** + * Submit handler for shortcut_set_edit_form(). + */ +function shortcut_set_edit_form_submit($form, &$form_state) { + $shortcut_set = $form_state['values']['shortcut_set']; + $shortcut_set->title = $form_state['values']['title']; + shortcut_set_save($shortcut_set); + drupal_set_message(t('Updated set name to %set-name.', array('%set-name' => $shortcut_set->title))); + $form_state['redirect'] = "admin/config/user-interface/shortcut/$shortcut_set->set_name"; +} + +/** + * Form callback: builds the confirmation form for deleting a shortcut set. + * + * @param $form + * An associative array containing the structure of the form. + * @param $form_state + * An associative array containing the current state of the form. + * @param object $shortcut_set + * An object representing the shortcut set, as returned from + * shortcut_set_load(). + * + * @return + * An array representing the form definition. + * + * @ingroup forms + * @see shortcut_set_delete_form_submit() + */ +function shortcut_set_delete_form($form, &$form_state, $shortcut_set) { + $form['shortcut_set'] = array( + '#type' => 'value', + '#value' => $shortcut_set->set_name, + ); + + // Find out how many users are directly assigned to this shortcut set, and + // make a message. + $number = db_query('SELECT COUNT(*) FROM {shortcut_set_users} WHERE set_name = :name', array(':name' => $shortcut_set->set_name))->fetchField(); + $info = ''; + if ($number) { + $info .= '

    ' . format_plural($number, + '1 user has chosen or been assigned to this shortcut set.', + '@count users have chosen or been assigned to this shortcut set.') . '

    '; + } + + // Also, if a module implements hook_shortcut_default_set(), it's possible + // that this set is being used as a default set. Add a message about that too. + if (count(module_implements('shortcut_default_set')) > 0) { + $info .= '

    ' . t('If you have chosen this shortcut set as the default for some or all users, they may also be affected by deleting it.') . '

    '; + } + + $form['info'] = array( + '#markup' => $info, + ); + + return confirm_form( + $form, + t('Are you sure you want to delete the shortcut set %title?', array('%title' => $shortcut_set->title)), + 'admin/config/user-interface/shortcut/' . $shortcut_set->set_name, + t('This action cannot be undone.'), + t('Delete'), + t('Cancel') + ); +} + +/** + * Submit handler for shortcut_set_delete_form(). + */ +function shortcut_set_delete_form_submit($form, &$form_state) { + $shortcut_set = shortcut_set_load($form_state['values']['shortcut_set']); + shortcut_set_delete($shortcut_set); + $form_state['redirect'] = 'admin/config/user-interface/shortcut'; + drupal_set_message(t('The shortcut set %title has been deleted.', array('%title' => $shortcut_set->title))); +} + +/** + * Form callback: builds the confirmation form for deleting a shortcut link. + * + * @param $form + * An associative array containing the structure of the form. + * @param $form_state + * An associative array containing the current state of the form. + * @param $shortcut_link + * An array representing the link that will be deleted. + * + * @return + * An array representing the form definition. + * + * @ingroup forms + * @see shortcut_link_delete_submit() + */ +function shortcut_link_delete($form, &$form_state, $shortcut_link) { + $form['shortcut_link'] = array( + '#type' => 'value', + '#value' => $shortcut_link, + ); + + return confirm_form( + $form, + t('Are you sure you want to delete the shortcut %title?', array('%title' => $shortcut_link['link_title'])), + 'admin/config/user-interface/shortcut/' . $shortcut_link['menu_name'], + t('This action cannot be undone.'), + t('Delete'), + t('Cancel') + ); +} + +/** + * Submit handler for shortcut_link_delete_submit(). + */ +function shortcut_link_delete_submit($form, &$form_state) { + $shortcut_link = $form_state['values']['shortcut_link']; + menu_link_delete($shortcut_link['mlid']); + $form_state['redirect'] = 'admin/config/user-interface/shortcut/' . $shortcut_link['menu_name']; + drupal_set_message(t('The shortcut %title has been deleted.', array('%title' => $shortcut_link['link_title']))); +} + +/** + * Menu page callback: creates a new link in the provided shortcut set. + * + * After completion, redirects the user back to where they came from. + * + * @param $shortcut_set + * Returned from shortcut_set_load(). + */ +function shortcut_link_add_inline($shortcut_set) { + if (isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'shortcut-add-link') && shortcut_valid_link($_GET['link'])) { + $item = menu_get_item($_GET['link']); + $title = ($item && $item['title']) ? $item['title'] : $_GET['name']; + $link = array( + 'link_title' => $title, + 'link_path' => $_GET['link'], + ); + shortcut_admin_add_link($link, $shortcut_set, shortcut_max_slots()); + if (shortcut_set_save($shortcut_set)) { + drupal_set_message(t('Added a shortcut for %title.', array('%title' => $link['link_title']))); + } + else { + drupal_set_message(t('Unable to add a shortcut for %title.', array('%title' => $link['link_title']))); + } + drupal_goto(); + } + + return MENU_ACCESS_DENIED; +} diff --git a/drupal7/web/modules/shortcut/shortcut.admin.js b/drupal7/web/modules/shortcut/shortcut.admin.js new file mode 100644 index 0000000..422cc4c --- /dev/null +++ b/drupal7/web/modules/shortcut/shortcut.admin.js @@ -0,0 +1,115 @@ +(function ($) { + +/** + * Handle the concept of a fixed number of slots. + * + * This behavior is dependent on the tableDrag behavior, since it uses the + * objects initialized in that behavior to update the row. + */ +Drupal.behaviors.shortcutDrag = { + attach: function (context, settings) { + if (Drupal.tableDrag) { + var table = $('table#shortcuts'), + visibleLength = 0, + slots = 0, + tableDrag = Drupal.tableDrag.shortcuts; + $('> tbody > tr, > tr', table) + .filter(':visible') + .filter(':odd').filter('.odd') + .removeClass('odd').addClass('even') + .end().end() + .filter(':even').filter('.even') + .removeClass('even').addClass('odd') + .end().end() + .end() + .filter('.shortcut-slot-empty').each(function(index) { + if ($(this).is(':visible')) { + visibleLength++; + } + slots++; + }); + + // Add a handler for when a row is swapped. + tableDrag.row.prototype.onSwap = function (swappedRow) { + var disabledIndex = $(table).find('tr').index($(table).find('tr.shortcut-status-disabled')) - slots - 2, + count = 0; + $(table).find('tr.shortcut-status-enabled').nextAll(':not(.shortcut-slot-empty)').each(function(index) { + if (index < disabledIndex) { + count++; + } + }); + var total = slots - count; + if (total == -1) { + var disabled = $(table).find('tr.shortcut-status-disabled'); + // To maintain the shortcut links limit, we need to move the last + // element from the enabled section to the disabled section. + var changedRow = disabled.prevAll(':not(.shortcut-slot-empty)').not($(this.element)).get(0); + disabled.after(changedRow); + if ($(changedRow).hasClass('draggable')) { + // The dropped element will automatically be marked as changed by + // the tableDrag system. However, the row that swapped with it + // has moved to the "disabled" section, so we need to force its + // status to be disabled and mark it also as changed. + var changedRowObject = new tableDrag.row(changedRow, 'mouse', false, 0, true); + changedRowObject.markChanged(); + tableDrag.rowStatusChange(changedRowObject); + } + } + else if (total != visibleLength) { + if (total > visibleLength) { + // Less slots on screen than needed. + $('.shortcut-slot-empty:hidden:last').show(); + visibleLength++; + } + else { + // More slots on screen than needed. + $('.shortcut-slot-empty:visible:last').hide(); + visibleLength--; + } + } + }; + + // Add a handler so when a row is dropped, update fields dropped into new regions. + tableDrag.onDrop = function () { + tableDrag.rowStatusChange(this.rowObject); + return true; + }; + + tableDrag.rowStatusChange = function (rowObject) { + // Use "status-message" row instead of "status" row because + // "status-{status_name}-message" is less prone to regexp match errors. + var statusRow = $(rowObject.element).prevAll('tr.shortcut-status').get(0); + var statusName = statusRow.className.replace(/([^ ]+[ ]+)*shortcut-status-([^ ]+)([ ]+[^ ]+)*/, '$2'); + var statusField = $('select.shortcut-status-select', rowObject.element); + statusField.val(statusName); + }; + + tableDrag.restripeTable = function () { + // :even and :odd are reversed because jQuery counts from 0 and + // we count from 1, so we're out of sync. + // Match immediate children of the parent element to allow nesting. + $('> tbody > tr:visible, > tr:visible', this.table) + .filter(':odd').filter('.odd') + .removeClass('odd').addClass('even') + .end().end() + .filter(':even').filter('.even') + .removeClass('even').addClass('odd'); + }; + } + } +}; + +/** + * Make it so when you enter text into the "New set" textfield, the + * corresponding radio button gets selected. + */ +Drupal.behaviors.newSet = { + attach: function (context, settings) { + var selectDefault = function() { + $(this).closest('form').find('.form-item-set .form-type-radio:last input').attr('checked', 'checked'); + }; + $('div.form-item-new input').focus(selectDefault).keyup(selectDefault); + } +}; + +})(jQuery); diff --git a/drupal7/web/modules/shortcut/shortcut.api.php b/drupal7/web/modules/shortcut/shortcut.api.php new file mode 100644 index 0000000..717a7c9 --- /dev/null +++ b/drupal7/web/modules/shortcut/shortcut.api.php @@ -0,0 +1,42 @@ +roles)) { + return variable_get('mymodule_shortcut_admin_default_set'); + } +} + +/** + * @} End of "addtogroup hooks". + */ diff --git a/drupal7/web/modules/shortcut/shortcut.css b/drupal7/web/modules/shortcut/shortcut.css new file mode 100644 index 0000000..3afcb94 --- /dev/null +++ b/drupal7/web/modules/shortcut/shortcut.css @@ -0,0 +1,106 @@ +div#toolbar a#edit-shortcuts { + float: right; + padding: 5px 10px 5px 5px; + line-height: 24px; + color: #fefefe; +} +div#toolbar a#edit-shortcuts:focus, +div#toolbar a#edit-shortcuts:hover, +div#toolbar a#edit-shortcuts.active { + color: #fff; + text-decoration: underline; +} + +div#toolbar div.toolbar-shortcuts ul { + padding: 5px 0 2px 0; + height: 28px; + line-height: 24px; + float: left; /* LTR */ + margin-left:5px; /* LTR */ +} + +div#toolbar div.toolbar-shortcuts ul li a { + padding: 0 5px 0 5px; + margin-right: 5px; /* LTR */ + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; +} + +div#toolbar div.toolbar-shortcuts ul li a:focus, +div#toolbar div.toolbar-shortcuts ul li a:hover, +div#toolbar div.toolbar-shortcuts ul li a.active:focus { + background: #555; +} + +div#toolbar div.toolbar-shortcuts ul li a.active:hover, +div#toolbar div.toolbar-shortcuts ul li a.active { + background: #000; +} + +div#toolbar div.toolbar-shortcuts span.icon { + float: left; /* LTR */ + background: #444; + width: 30px; + height: 30px; + margin-right: 5px; /* LTR */ + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; +} + +div.add-or-remove-shortcuts { + padding-top: 5px; +} + +div.add-or-remove-shortcuts a span.icon { + display: block; + width: 12px; + background: transparent url(shortcut.png) no-repeat scroll 0 0; + height: 12px; + float: left; + margin-left:8px; +} + +div.add-shortcut a:focus span.icon, +div.add-shortcut a:hover span.icon { + background-position: 0 -12px; +} +div.remove-shortcut a span.icon { + background-position: -12px 0; +} +div.remove-shortcut a:focus span.icon, +div.remove-shortcut a:hover span.icon { + background-position: -12px -12px; +} + +div.add-or-remove-shortcuts a span.text { + float: left; + padding-left:10px; + display: none; +} + +div.add-or-remove-shortcuts a:focus span.text, +div.add-or-remove-shortcuts a:hover span.text { + font-size: 10px; + line-height: 12px; + color: #fff; + background-color: #5f605b; + display: block; + padding-right: 6px; /* LTR */ + cursor: pointer; + -moz-border-radius: 0 5px 5px 0; /* LTR */ + -webkit-border-top-right-radius: 5px; /* LTR */ + -webkit-border-bottom-right-radius: 5px; /* LTR */ + border-radius: 0 5px 5px 0; /* LTR */ +} + +#shortcut-set-switch .form-type-radios { + padding-bottom: 0; + margin-bottom: 0; +} + +#shortcut-set-switch .form-item-new { + padding-top: 0; + padding-left: 17px; /* LTR */ +} diff --git a/drupal7/web/modules/shortcut/shortcut.info b/drupal7/web/modules/shortcut/shortcut.info new file mode 100644 index 0000000..e502fc1 --- /dev/null +++ b/drupal7/web/modules/shortcut/shortcut.info @@ -0,0 +1,12 @@ +name = Shortcut +description = Allows users to manage customizable lists of shortcut links. +package = Core +version = VERSION +core = 7.x +files[] = shortcut.test +configure = admin/config/user-interface/shortcut + +; Information added by Drupal.org packaging script on 2024-03-06 +version = "7.100" +project = "drupal" +datestamp = "1709734591" diff --git a/drupal7/web/modules/shortcut/shortcut.install b/drupal7/web/modules/shortcut/shortcut.install new file mode 100644 index 0000000..60ee6be --- /dev/null +++ b/drupal7/web/modules/shortcut/shortcut.install @@ -0,0 +1,115 @@ +title = $t('Default'); + $shortcut_set->links = array( + array( + 'link_path' => 'node/add', + 'link_title' => $t('Add content'), + 'weight' => -20, + ), + array( + 'link_path' => 'admin/content', + 'link_title' => $t('Find content'), + 'weight' => -19, + ), + ); + // If Drupal is being installed, rebuild the menu before saving the shortcut + // set, to make sure the links defined above can be correctly saved. (During + // installation, the menu might not have been built at all yet, or it might + // have been built but without the node module's links in it.) + if (drupal_installation_attempted()) { + menu_rebuild(); + } + shortcut_set_save($shortcut_set); +} + +/** + * Implements hook_uninstall(). + */ +function shortcut_uninstall() { + drupal_load('module', 'shortcut'); + // Delete the menu links associated with each shortcut set. + foreach (shortcut_sets() as $shortcut_set) { + menu_delete_links($shortcut_set->set_name); + } +} + +/** + * Implements hook_schema(). + */ +function shortcut_schema() { + $schema['shortcut_set'] = array( + 'description' => 'Stores information about sets of shortcuts links.', + 'fields' => array( + 'set_name' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + 'description' => "Primary Key: The {menu_links}.menu_name under which the set's links are stored.", + ), + 'title' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The title of the set.', + ), + ), + 'primary key' => array('set_name'), + 'foreign keys' => array( + 'menu_name' => array( + 'table' => 'menu_links', + 'columns' => array('set_name' => 'menu_name'), + ), + ), + ); + + $schema['shortcut_set_users'] = array( + 'description' => 'Maps users to shortcut sets.', + 'fields' => array( + 'uid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The {users}.uid for this set.', + ), + 'set_name' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + 'description' => "The {shortcut_set}.set_name that will be displayed for this user.", + ), + ), + 'primary key' => array('uid'), + 'indexes' => array( + 'set_name' => array('set_name'), + ), + 'foreign keys' => array( + 'set_user' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), + 'set_name' => array( + 'table' => 'shortcut_set', + 'columns' => array('set_name' => 'set_name'), + ), + ), + ); + + return $schema; +} diff --git a/drupal7/web/modules/shortcut/shortcut.module b/drupal7/web/modules/shortcut/shortcut.module new file mode 100644 index 0000000..2f6db0a --- /dev/null +++ b/drupal7/web/modules/shortcut/shortcut.module @@ -0,0 +1,767 @@ +' . t('About') . ''; + $output .= '

    ' . t('The Shortcut module allows users to create sets of shortcut links to commonly-visited pages of the site. Shortcuts are contained within sets. Each user with Select any shortcut set permission can select a shortcut set created by anyone at the site. For more information, see the online handbook entry for Shortcut module.', array('@shortcut' => 'http://drupal.org/documentation/modules/shortcut/')) . '

    '; + $output .= '

    ' . t('Uses') . '

    '; + $output .= '
    ' . t('Administering shortcuts') . '
    '; + $output .= '
    ' . t('Users with the Administer shortcuts permission can manage shortcut sets and edit the shortcuts within sets from the Shortcuts administration page.', array('@shortcuts' => url('admin/config/user-interface/shortcut'))) . '
    '; + $output .= '
    ' . t('Choosing shortcut sets') . '
    '; + $output .= '
    ' . t('Users with permission to switch shortcut sets can choose a shortcut set to use from the Shortcuts tab of their user account page.') . '
    '; + $output .= '
    ' . t('Adding and removing shortcuts') . '
    '; + $output .= '
    ' . t('The Shortcut module creates an add/remove link for each page on your site; the link lets you add or remove the current page from the currently-enabled set of shortcuts (if your theme displays it and you have permission to edit your shortcut set). The core Seven administration theme displays this link next to the page title, as a small + or - sign. If you click on the + sign, you will add that page to your preferred set of shortcuts. If the page is already part of your shortcut set, the link will be a - sign, and will allow you to remove the current page from your shortcut set.') . '
    '; + $output .= '
    ' . t('Displaying shortcuts') . '
    '; + $output .= '
    ' . t('You can display your shortcuts by enabling the Shortcuts block on the Blocks administration page. Certain administrative modules also display your shortcuts; for example, the core Toolbar module displays them near the top of the page, along with an Edit shortcuts link.', array('@blocks' => url('admin/structure/block'), '@toolbar-help' => url('admin/help/toolbar'))) . '
    '; + $output .= '
    '; + return $output; + + case 'admin/config/user-interface/shortcut': + case 'admin/config/user-interface/shortcut/%': + if (user_access('switch shortcut sets')) { + $output = '

    ' . t('Define which shortcut set you are using on the Shortcuts tab of your account page.', array('@shortcut-link' => url("user/{$user->uid}/shortcuts"))) . '

    '; + return $output; + } + } +} + +/** + * Implements hook_permission(). + */ +function shortcut_permission() { + return array( + 'administer shortcuts' => array( + 'title' => t('Administer shortcuts'), + ), + 'customize shortcut links' => array( + 'title' => t('Edit current shortcut set'), + 'description' => t('Editing the current shortcut set will affect other users if that set has been assigned to or selected by other users. Granting "Select any shortcut set" permission along with this permission will grant permission to edit any shortcut set.'), + ), + 'switch shortcut sets' => array( + 'title' => t('Select any shortcut set'), + 'description' => t('From all shortcut sets, select one to be own active set. Without this permission, an administrator selects shortcut sets for users.'), + ), + ); +} + +/** + * Implements hook_menu(). + */ +function shortcut_menu() { + $items['admin/config/user-interface/shortcut'] = array( + 'title' => 'Shortcuts', + 'description' => 'Add and modify shortcut sets.', + 'page callback' => 'shortcut_set_admin', + 'access arguments' => array('administer shortcuts'), + 'file' => 'shortcut.admin.inc', + ); + $items['admin/config/user-interface/shortcut/add-set'] = array( + 'title' => 'Add shortcut set', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('shortcut_set_add_form'), + 'access arguments' => array('administer shortcuts'), + 'type' => MENU_LOCAL_ACTION, + 'file' => 'shortcut.admin.inc', + ); + $items['admin/config/user-interface/shortcut/%shortcut_set'] = array( + 'title' => 'Edit shortcuts', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('shortcut_set_customize', 4), + 'title callback' => 'shortcut_set_title_callback', + 'title arguments' => array(4), + 'access callback' => 'shortcut_set_edit_access', + 'access arguments' => array(4), + 'file' => 'shortcut.admin.inc', + ); + $items['admin/config/user-interface/shortcut/%shortcut_set/links'] = array( + 'title' => 'List links', + 'type' => MENU_DEFAULT_LOCAL_TASK, + ); + $items['admin/config/user-interface/shortcut/%shortcut_set/edit'] = array( + 'title' => 'Edit set name', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('shortcut_set_edit_form', 4), + 'access callback' => 'shortcut_set_edit_access', + 'access arguments' => array(4), + 'type' => MENU_LOCAL_TASK, + 'file' => 'shortcut.admin.inc', + 'weight' => 10, + ); + $items['admin/config/user-interface/shortcut/%shortcut_set/delete'] = array( + 'title' => 'Delete shortcut set', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('shortcut_set_delete_form', 4), + 'access callback' => 'shortcut_set_delete_access', + 'access arguments' => array(4), + 'file' => 'shortcut.admin.inc', + ); + $items['admin/config/user-interface/shortcut/%shortcut_set/add-link'] = array( + 'title' => 'Add shortcut', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('shortcut_link_add', 4), + 'access callback' => 'shortcut_set_edit_access', + 'access arguments' => array(4), + 'type' => MENU_LOCAL_ACTION, + 'file' => 'shortcut.admin.inc', + ); + $items['admin/config/user-interface/shortcut/%shortcut_set/add-link-inline'] = array( + 'title' => 'Add shortcut', + 'page callback' => 'shortcut_link_add_inline', + 'page arguments' => array(4), + 'access callback' => 'shortcut_set_edit_access', + 'access arguments' => array(4), + 'type' => MENU_CALLBACK, + 'file' => 'shortcut.admin.inc', + ); + $items['admin/config/user-interface/shortcut/link/%menu_link'] = array( + 'title' => 'Edit shortcut', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('shortcut_link_edit', 5), + 'access callback' => 'shortcut_link_access', + 'access arguments' => array(5), + 'file' => 'shortcut.admin.inc', + ); + $items['admin/config/user-interface/shortcut/link/%menu_link/delete'] = array( + 'title' => 'Delete shortcut', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('shortcut_link_delete', 5), + 'access callback' => 'shortcut_link_access', + 'access arguments' => array(5), + 'file' => 'shortcut.admin.inc', + ); + $items['user/%user/shortcuts'] = array( + 'title' => 'Shortcuts', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('shortcut_set_switch', 1), + 'access callback' => 'shortcut_set_switch_access', + 'access arguments' => array(1), + 'type' => MENU_LOCAL_TASK, + 'file' => 'shortcut.admin.inc', + ); + + return $items; +} + +/** + * Implements hook_admin_paths(). + */ +function shortcut_admin_paths() { + $paths = array( + 'user/*/shortcuts' => TRUE, + ); + return $paths; +} + +/** + * Implements hook_theme(). + */ +function shortcut_theme() { + return array( + 'shortcut_set_customize' => array( + 'render element' => 'form', + 'file' => 'shortcut.admin.inc', + ), + ); +} + +/** + * Implements hook_block_info(). + */ +function shortcut_block_info() { + $blocks['shortcuts']['info'] = t('Shortcuts'); + // Shortcut blocks can't be cached because each menu item can have a custom + // access callback. menu.inc manages its own caching. + $blocks['shortcuts']['cache'] = DRUPAL_NO_CACHE; + return $blocks; +} + +/** + * Implements hook_block_view(). + */ +function shortcut_block_view($delta = '') { + if ($delta == 'shortcuts') { + $shortcut_set = shortcut_current_displayed_set(); + $data['subject'] = t('@shortcut_set shortcuts', array('@shortcut_set' => $shortcut_set->title)); + $data['content'] = shortcut_renderable_links($shortcut_set); + return $data; + } +} + +/** + * Access callback for editing a shortcut set. + * + * @param object $shortcut_set + * (optional) The shortcut set to be edited. If not set, the current user's + * shortcut set will be used. + * + * @return + * TRUE if the current user has access to edit the shortcut set, FALSE + * otherwise. + */ +function shortcut_set_edit_access($shortcut_set = NULL) { + // Sufficiently-privileged users can edit their currently displayed shortcut + // set, but not other sets. Shortcut administrators can edit any set. + if (user_access('administer shortcuts')) { + return TRUE; + } + if (user_access('customize shortcut links')) { + return !isset($shortcut_set) || $shortcut_set == shortcut_current_displayed_set(); + } + return FALSE; +} + +/** + * Access callback for deleting a shortcut set. + * + * @param $shortcut_set + * The shortcut set to be deleted. + * + * @return + * TRUE if the current user has access to delete shortcut sets and this is + * not the site-wide default set; FALSE otherwise. + */ +function shortcut_set_delete_access($shortcut_set) { + // Only admins can delete sets. + if (!user_access('administer shortcuts')) { + return FALSE; + } + + // Never let the default shortcut set be deleted. + if ($shortcut_set->set_name == SHORTCUT_DEFAULT_SET_NAME) { + return FALSE; + } + + return TRUE; +} + +/** + * Access callback for switching the shortcut set assigned to a user account. + * + * @param object $account + * (optional) The user account whose shortcuts will be switched. If not set, + * permissions will be checked for switching the logged-in user's own + * shortcut set. + * + * @return + * TRUE if the current user has access to switch the shortcut set of the + * provided account, FALSE otherwise. + */ +function shortcut_set_switch_access($account = NULL) { + global $user; + + if (user_access('administer shortcuts')) { + // Administrators can switch anyone's shortcut set. + return TRUE; + } + + if (!user_access('switch shortcut sets')) { + // The user has no permission to switch anyone's shortcut set. + return FALSE; + } + + if (!isset($account) || $user->uid == $account->uid) { + // Users with the 'switch shortcut sets' permission can switch their own + // shortcuts sets. + return TRUE; + } + + return FALSE; +} + +/** + * Access callback for editing a link in a shortcut set. + */ +function shortcut_link_access($menu_link) { + // The link must belong to a shortcut set that the current user has access + // to edit. + if ($shortcut_set = shortcut_set_load($menu_link['menu_name'])) { + return shortcut_set_edit_access($shortcut_set); + } + return FALSE; +} + +/** + * Loads the data for a shortcut set. + * + * @param $set_name + * The name of the shortcut set to load. + * + * @return object + * If the shortcut set exists, an object containing the following properties: + * - 'set_name': The internal name of the shortcut set. + * - 'title': The title of the shortcut set. + * - 'links': An array of links associated with this shortcut set. + * If the shortcut set does not exist, the function returns FALSE. + */ +function shortcut_set_load($set_name) { + $set = db_select('shortcut_set', 'ss') + ->fields('ss') + ->condition('set_name', $set_name) + ->execute() + ->fetchObject(); + if (!$set) { + return FALSE; + } + $set->links = menu_load_links($set_name); + return $set; +} + +/** + * Saves a shortcut set. + * + * @param $shortcut_set + * An object containing the following properties: + * - 'title': The title of the shortcut set. + * - 'set_name': (optional) The internal name of the shortcut set. If + * omitted, a new shortcut set will be created, and the 'set_name' property + * will be added to the passed-in object. + * - 'links': (optional) An array of menu links to save for the shortcut set. + * Each link is an array containing at least the following keys (which will + * be expanded to fill in other default values after the shortcut set is + * saved): + * - 'link_path': The Drupal path or external path that the link points to. + * - 'link_title': The title of the link. + * Any other keys accepted by menu_link_save() may also be provided. + * + * @return + * A constant which is either SAVED_NEW or SAVED_UPDATED depending on whether + * a new set was created or an existing one was updated. + * + * @see menu_link_save() + */ +function shortcut_set_save(&$shortcut_set) { + // First save the shortcut set itself. + if (isset($shortcut_set->set_name)) { + $return = drupal_write_record('shortcut_set', $shortcut_set, 'set_name'); + } + else { + $shortcut_set->set_name = shortcut_set_get_unique_name(); + $return = drupal_write_record('shortcut_set', $shortcut_set); + } + // If links were provided for the set, save them. + if (isset($shortcut_set->links)) { + foreach ($shortcut_set->links as &$link) { + // Do not specifically associate these links with the shortcut module, + // since other modules may make them editable via the menu system. + // However, we do need to specify the correct menu name. + $link['menu_name'] = $shortcut_set->set_name; + $link['plid'] = 0; + menu_link_save($link); + } + // Make sure that we have a return value, since if the links were updated + // but the shortcut set was not, the call to drupal_write_record() above + // would not return an indication that anything had changed. + if (empty($return)) { + $return = SAVED_UPDATED; + } + } + return $return; +} + +/** + * Deletes a shortcut set. + * + * Note that the default set cannot be deleted. + * + * @param $shortcut_set + * An object representing the shortcut set to delete. + * + * @return + * TRUE if the set was deleted, FALSE otherwise. + */ +function shortcut_set_delete($shortcut_set) { + // Don't allow deletion of the system default shortcut set. + if ($shortcut_set->set_name == SHORTCUT_DEFAULT_SET_NAME) { + return FALSE; + } + + // First, delete any user assignments for this set, so that each of these + // users will go back to using whatever default set applies. + db_delete('shortcut_set_users') + ->condition('set_name', $shortcut_set->set_name) + ->execute(); + + // Next, delete the menu links for this set. + menu_delete_links($shortcut_set->set_name); + + // Finally, delete the set itself. + $deleted = db_delete('shortcut_set') + ->condition('set_name', $shortcut_set->set_name) + ->execute(); + + return (bool) $deleted; +} + +/** + * Resets the link weights in a shortcut set to match their current order. + * + * This function can be used, for example, when a new shortcut link is added to + * the set. If the link is added to the end of the array and this function is + * called, it will force that link to display at the end of the list. + * + * @param object $shortcut_set + * An object representing a shortcut set. The link weights of the passed-in + * object will be reset as described above. + */ +function shortcut_set_reset_link_weights(&$shortcut_set) { + $weight = -50; + foreach ($shortcut_set->links as &$link) { + $link['weight'] = $weight; + $weight++; + } +} + +/** + * Assigns a user to a particular shortcut set. + * + * @param $shortcut_set + * An object representing the shortcut set. + * @param $account + * A user account that will be assigned to use the set. + */ +function shortcut_set_assign_user($shortcut_set, $account) { + db_merge('shortcut_set_users') + ->key(array('uid' => $account->uid)) + ->fields(array('set_name' => $shortcut_set->set_name)) + ->execute(); + drupal_static_reset('shortcut_current_displayed_set'); +} + +/** + * Unassigns a user from any shortcut set they may have been assigned to. + * + * The user will go back to using whatever default set applies. + * + * @param $account + * A user account that will be removed from the shortcut set assignment. + * + * @return + * TRUE if the user was previously assigned to a shortcut set and has been + * successfully removed from it. FALSE if the user was already not assigned + * to any set. + */ +function shortcut_set_unassign_user($account) { + $deleted = db_delete('shortcut_set_users') + ->condition('uid', $account->uid) + ->execute(); + return (bool) $deleted; +} + +/** + * Returns the current displayed shortcut set for the provided user account. + * + * @param $account + * (optional) The user account whose shortcuts will be returned. Defaults to + * the currently logged-in user. + * + * @return + * An object representing the shortcut set that should be displayed to the + * current user. If the user does not have an explicit shortcut set defined, + * the default set is returned. + */ +function shortcut_current_displayed_set($account = NULL) { + $shortcut_sets = &drupal_static(__FUNCTION__, array()); + global $user; + if (!isset($account)) { + $account = $user; + } + // Try to return a shortcut set from the static cache. + if (isset($shortcut_sets[$account->uid])) { + return $shortcut_sets[$account->uid]; + } + // If none was found, try to find a shortcut set that is explicitly assigned + // to this user. + $query = db_select('shortcut_set', 's'); + $query->addField('s', 'set_name'); + $query->join('shortcut_set_users', 'u', 's.set_name = u.set_name'); + $query->condition('u.uid', $account->uid); + $shortcut_set_name = $query->execute()->fetchField(); + if ($shortcut_set_name) { + $shortcut_set = shortcut_set_load($shortcut_set_name); + } + // Otherwise, use the default set. + else { + $shortcut_set = shortcut_default_set($account); + } + + $shortcut_sets[$account->uid] = $shortcut_set; + return $shortcut_set; +} + +/** + * Returns the default shortcut set for a given user account. + * + * @param object $account + * (optional) The user account whose default shortcut set will be returned. + * If not provided, the function will return the currently logged-in user's + * default shortcut set. + * + * @return + * An object representing the default shortcut set. + */ +function shortcut_default_set($account = NULL) { + global $user; + if (!isset($account)) { + $account = $user; + } + + // Allow modules to return a default shortcut set name. Since we can only + // have one, we allow the last module which returns a valid result to take + // precedence. If no module returns a valid set, fall back on the site-wide + // default, which is the lowest-numbered shortcut set. + $suggestions = array_reverse(module_invoke_all('shortcut_default_set', $account)); + $suggestions[] = SHORTCUT_DEFAULT_SET_NAME; + foreach ($suggestions as $name) { + if ($shortcut_set = shortcut_set_load($name)) { + break; + } + } + + return $shortcut_set; +} + +/** + * Returns a unique, machine-readable shortcut set name. + */ +function shortcut_set_get_unique_name() { + // Shortcut sets are numbered sequentially, so we keep trying until we find + // one that is available. For better performance, we start with a number + // equal to one more than the current number of shortcut sets, so that if + // no shortcut sets have been deleted from the database, this will + // automatically give us the correct one. + $number = db_query("SELECT COUNT(*) FROM {shortcut_set}")->fetchField() + 1; + do { + $name = shortcut_set_name($number); + $number++; + } while ($shortcut_set = shortcut_set_load($name)); + return $name; +} + +/** + * Returns the name of a shortcut set, based on a provided number. + * + * All shortcut sets have names like "shortcut-set-N" so that they can be + * matched with a properly-namespaced entry in the {menu_links} table. + * + * @param $number + * A number representing the shortcut set whose name should be retrieved. + * + * @return + * A string representing the expected shortcut name. + */ +function shortcut_set_name($number) { + return "shortcut-set-$number"; +} + +/** + * Returns an array of all shortcut sets, keyed by the set name. + * + * @return + * An array of shortcut sets. Note that only the basic shortcut set + * properties (name and title) are returned by this function, not the list + * of menu links that belong to the set. + */ +function shortcut_sets() { + return db_select('shortcut_set', 'ss') + ->fields('ss') + ->execute() + ->fetchAllAssoc('set_name'); +} + +/** + * Check to see if a shortcut set with the given title already exists. + * + * @param $title + * Human-readable name of the shortcut set to check. + * + * @return + * TRUE if a shortcut set with that title exists; FALSE otherwise. + */ +function shortcut_set_title_exists($title) { + return (bool) db_query_range('SELECT 1 FROM {shortcut_set} WHERE title = :title', 0, 1, array(':title' => $title))->fetchField(); +} + +/** + * Determines if a path corresponds to a valid shortcut link. + * + * @param $path + * The path to the link. + * @return + * TRUE if the shortcut link is valid, FALSE otherwise. Valid links are ones + * that correspond to actual paths on the site. + * + * @see menu_edit_item_validate() + */ +function shortcut_valid_link($path) { + // Do not use URL aliases. + $normal_path = drupal_get_normal_path($path); + if ($path != $normal_path) { + $path = $normal_path; + } + // An empty path is valid too and will be converted to . + return (!url_is_external($path) && menu_get_item($path)) || empty($path) || $path == ''; +} + +/** + * Returns an array of shortcut links, suitable for rendering. + * + * @param $shortcut_set + * (optional) An object representing the set whose links will be displayed. + * If not provided, the user's current set will be displayed. + * @return + * An array of shortcut links, in the format returned by the menu system. + * + * @see menu_tree() + */ +function shortcut_renderable_links($shortcut_set = NULL) { + if (!isset($shortcut_set)) { + $shortcut_set = shortcut_current_displayed_set(); + } + return menu_tree($shortcut_set->set_name); +} + +/** + * Implements hook_preprocess_page(). + */ +function shortcut_preprocess_page(&$variables) { + // Only display the shortcut link if the user has the ability to edit + // shortcuts and if the page's actual content is being shown (for example, + // we do not want to display it on "access denied" or "page not found" + // pages). + if (shortcut_set_edit_access() && ($item = menu_get_item()) && $item['access']) { + $link = $_GET['q']; + $query_parameters = drupal_get_query_parameters(); + if (!empty($query_parameters)) { + $link .= '?' . drupal_http_build_query($query_parameters); + } + $query = array( + 'link' => $link, + 'name' => drupal_get_title(), + ); + $query += drupal_get_destination(); + + $shortcut_set = shortcut_current_displayed_set(); + + // Check if $link is already a shortcut and set $link_mode accordingly. + foreach ($shortcut_set->links as $shortcut) { + if ($link == $shortcut['link_path']) { + $mlid = $shortcut['mlid']; + break; + } + } + $link_mode = isset($mlid) ? "remove" : "add"; + + if ($link_mode == "add") { + $query['token'] = drupal_get_token('shortcut-add-link'); + $link_text = shortcut_set_switch_access() ? t('Add to %shortcut_set shortcuts', array('%shortcut_set' => $shortcut_set->title)) : t('Add to shortcuts'); + $link_path = 'admin/config/user-interface/shortcut/' . $shortcut_set->set_name . '/add-link-inline'; + } + else { + $query['mlid'] = $mlid; + $link_text = shortcut_set_switch_access() ? t('Remove from %shortcut_set shortcuts', array('%shortcut_set' => $shortcut_set->title)) : t('Remove from shortcuts'); + $link_path = 'admin/config/user-interface/shortcut/link/' . $mlid . '/delete'; + } + + if (theme_get_setting('shortcut_module_link')) { + $variables['title_suffix']['add_or_remove_shortcut'] = array( + '#attached' => array('css' => array(drupal_get_path('module', 'shortcut') . '/shortcut.css')), + '#prefix' => '', + ); + } + } +} + +/** + * Implements hook_page_alter(). + */ +function shortcut_page_alter(&$page) { + if (isset($page['page_top']['toolbar'])) { + // If the toolbar is available, add a pre-render function to display the + // current shortcuts in the toolbar drawer. + $page['page_top']['toolbar']['#pre_render'][] = 'shortcut_toolbar_pre_render'; + } +} + +/** + * Pre-render function for adding shortcuts to the toolbar drawer. + */ +function shortcut_toolbar_pre_render($toolbar) { + $links = shortcut_renderable_links(); + $links['#attached'] = array('css' => array(drupal_get_path('module', 'shortcut') . '/shortcut.css')); + $links['#prefix'] = '
    '; + $links['#suffix'] = '
    '; + $shortcut_set = shortcut_current_displayed_set(); + $configure_link = NULL; + if (shortcut_set_edit_access($shortcut_set)) { + $configure_link = array( + '#type' => 'link', + '#title' => t('Edit shortcuts'), + '#href' => 'admin/config/user-interface/shortcut/' . $shortcut_set->set_name, + '#options' => array('attributes' => array('id' => 'edit-shortcuts')), + ); + } + + $drawer = array( + 'shortcuts' => $links, + 'configure' => $configure_link, + ); + + $toolbar['toolbar_drawer'][] = $drawer; + return $toolbar; +} + +/** + * Returns the sanitized title of a shortcut set. + * + * Deprecated. This function was previously used as a menu item title callback + * but has been replaced by shortcut_set_title_callback() (which does not + * sanitize the title, since the menu system does that automatically). In + * Drupal 7, use that function for title callbacks, and call check_plain() + * directly if you need a sanitized title. In Drupal 8, this function will be + * restored as a title callback and therefore will no longer sanitize its + * output. + * + * @param $shortcut_set + * An object representing the shortcut set, as returned by + * shortcut_set_load(). + */ +function shortcut_set_title($shortcut_set) { + return check_plain($shortcut_set->title); +} + +/** + * Returns the title of a shortcut set. + * + * Title callback for the editing pages for shortcut sets. + * + * @param $shortcut_set + * An object representing the shortcut set, as returned by + * shortcut_set_load(). + */ +function shortcut_set_title_callback($shortcut_set) { + return $shortcut_set->title; +} diff --git a/drupal7/web/modules/shortcut/shortcut.png b/drupal7/web/modules/shortcut/shortcut.png new file mode 100644 index 0000000..2924557 Binary files /dev/null and b/drupal7/web/modules/shortcut/shortcut.png differ diff --git a/drupal7/web/modules/shortcut/shortcut.test b/drupal7/web/modules/shortcut/shortcut.test new file mode 100644 index 0000000..2bd9605 --- /dev/null +++ b/drupal7/web/modules/shortcut/shortcut.test @@ -0,0 +1,371 @@ +admin_user = $this->drupalCreateUser(array('access toolbar', 'administer shortcuts', 'view the administration theme', 'create article content', 'create page content', 'access content overview')); + $this->shortcut_user = $this->drupalCreateUser(array('customize shortcut links', 'switch shortcut sets')); + + // Create a node. + $this->node = $this->drupalCreateNode(array('type' => 'article')); + + // Log in as admin and grab the default shortcut set. + $this->drupalLogin($this->admin_user); + $this->set = shortcut_set_load(SHORTCUT_DEFAULT_SET_NAME); + shortcut_set_assign_user($this->set, $this->admin_user); + } + + /** + * Creates a generic shortcut set. + */ + function generateShortcutSet($title = '', $default_links = TRUE) { + $set = new stdClass(); + $set->title = empty($title) ? $this->randomName(10) : $title; + if ($default_links) { + $set->links = array(); + $set->links[] = $this->generateShortcutLink('node/add'); + $set->links[] = $this->generateShortcutLink('admin/content'); + } + shortcut_set_save($set); + + return $set; + } + + /** + * Creates a generic shortcut link. + */ + function generateShortcutLink($path, $title = '') { + $link = array( + 'link_path' => $path, + 'link_title' => !empty($title) ? $title : $this->randomName(10), + ); + + return $link; + } + + /** + * Extracts information from shortcut set links. + * + * @param object $set + * The shortcut set object to extract information from. + * @param string $key + * The array key indicating what information to extract from each link: + * - 'link_path': Extract link paths. + * - 'link_title': Extract link titles. + * - 'mlid': Extract the menu link item ID numbers. + * + * @return array + * Array of the requested information from each link. + */ + function getShortcutInformation($set, $key) { + $info = array(); + foreach ($set->links as $link) { + $info[] = $link[$key]; + } + return $info; + } +} + +/** + * Defines shortcut links test cases. + */ +class ShortcutLinksTestCase extends ShortcutTestCase { + + public static function getInfo() { + return array( + 'name' => 'Shortcut link functionality', + 'description' => 'Create, view, edit, delete, and change shortcut links.', + 'group' => 'Shortcut', + ); + } + + /** + * Tests that creating a shortcut works properly. + */ + function testShortcutLinkAdd() { + $set = $this->set; + + // Create an alias for the node so we can test aliases. + $path = array( + 'source' => 'node/' . $this->node->nid, + 'alias' => $this->randomName(8), + ); + path_save($path); + + // Create some paths to test. + $test_cases = array( + array('path' => ''), + array('path' => 'admin'), + array('path' => 'admin/config/system/site-information'), + array('path' => "node/{$this->node->nid}/edit"), + array('path' => $path['alias']), + ); + + // Check that each new shortcut links where it should. + foreach ($test_cases as $test) { + $title = $this->randomName(10); + $form_data = array( + 'shortcut_link[link_title]' => $title, + 'shortcut_link[link_path]' => $test['path'], + ); + $this->drupalPost('admin/config/user-interface/shortcut/' . $set->set_name . '/add-link', $form_data, t('Save')); + $this->assertResponse(200); + $saved_set = shortcut_set_load($set->set_name); + $paths = $this->getShortcutInformation($saved_set, 'link_path'); + $test_path = empty($test['path']) ? '' : $test['path']; + $this->assertTrue(in_array(drupal_get_normal_path($test_path), $paths), 'Shortcut created: '. $test['path']); + $this->assertLink($title, 0, 'Shortcut link found on the page.'); + } + } + + /** + * Tests that the "add to shortcut" link changes to "remove shortcut". + */ + function testShortcutQuickLink() { + $this->drupalGet($this->set->links[0]['link_path']); + $this->assertRaw(t('Remove from %title shortcuts', array('%title' => $this->set->title)), '"Add to shortcuts" link properly switched to "Remove from shortcuts".'); + } + + /** + * Tests that shortcut links can be renamed. + */ + function testShortcutLinkRename() { + $set = $this->set; + + // Attempt to rename shortcut link. + $new_link_name = $this->randomName(10); + + $this->drupalPost('admin/config/user-interface/shortcut/link/' . $set->links[0]['mlid'], array('shortcut_link[link_title]' => $new_link_name, 'shortcut_link[link_path]' => $set->links[0]['link_path']), t('Save')); + $saved_set = shortcut_set_load($set->set_name); + $titles = $this->getShortcutInformation($saved_set, 'link_title'); + $this->assertTrue(in_array($new_link_name, $titles), 'Shortcut renamed: ' . $new_link_name); + $this->assertLink($new_link_name, 0, 'Renamed shortcut link appears on the page.'); + } + + /** + * Tests that changing the path of a shortcut link works. + */ + function testShortcutLinkChangePath() { + $set = $this->set; + + // Tests changing a shortcut path. + $new_link_path = 'admin/config'; + + $this->drupalPost('admin/config/user-interface/shortcut/link/' . $set->links[0]['mlid'], array('shortcut_link[link_title]' => $set->links[0]['link_title'], 'shortcut_link[link_path]' => $new_link_path), t('Save')); + $saved_set = shortcut_set_load($set->set_name); + $paths = $this->getShortcutInformation($saved_set, 'link_path'); + $this->assertTrue(in_array($new_link_path, $paths), 'Shortcut path changed: ' . $new_link_path); + $this->assertLinkByHref($new_link_path, 0, 'Shortcut with new path appears on the page.'); + } + + /** + * Tests deleting a shortcut link. + */ + function testShortcutLinkDelete() { + $set = $this->set; + + $this->drupalPost('admin/config/user-interface/shortcut/link/' . $set->links[0]['mlid'] . '/delete', array(), 'Delete'); + $saved_set = shortcut_set_load($set->set_name); + $mlids = $this->getShortcutInformation($saved_set, 'mlid'); + $this->assertFalse(in_array($set->links[0]['mlid'], $mlids), 'Successfully deleted a shortcut.'); + } + + /** + * Tests that the add shortcut link is not displayed for 404/403 errors. + * + * Tests that the "Add to shortcuts" link is not displayed on a page not + * found or a page the user does not have access to. + */ + function testNoShortcutLink() { + // Change to a theme that displays shortcuts. + variable_set('theme_default', 'seven'); + + $this->drupalGet('page-that-does-not-exist'); + $this->assertNoRaw('add-shortcut', 'Add to shortcuts link was not shown on a page not found.'); + + // The user does not have access to this path. + $this->drupalGet('admin/modules'); + $this->assertNoRaw('add-shortcut', 'Add to shortcuts link was not shown on a page the user does not have access to.'); + + // Verify that the testing mechanism works by verifying the shortcut + // link appears on admin/content/node. + $this->drupalGet('admin/content/node'); + $this->assertRaw('add-shortcut', 'Add to shortcuts link was shown on a page the user does have access to.'); + } +} + +/** + * Defines shortcut set test cases. + */ +class ShortcutSetsTestCase extends ShortcutTestCase { + + public static function getInfo() { + return array( + 'name' => 'Shortcut set functionality', + 'description' => 'Create, view, edit, delete, and change shortcut sets.', + 'group' => 'Shortcut', + ); + } + + /** + * Tests creating a shortcut set. + */ + function testShortcutSetAdd() { + $new_set = $this->generateShortcutSet($this->randomName(10)); + $sets = shortcut_sets(); + $this->assertTrue(isset($sets[$new_set->set_name]), 'Successfully created a shortcut set.'); + $this->drupalGet('user/' . $this->admin_user->uid . '/shortcuts'); + $this->assertText($new_set->title, 'Generated shortcut set was listed as a choice on the user account page.'); + } + + /** + * Tests switching a user's own shortcut set. + */ + function testShortcutSetSwitchOwn() { + $new_set = $this->generateShortcutSet($this->randomName(10)); + + // Attempt to switch the default shortcut set to the newly created shortcut + // set. + $this->drupalPost('user/' . $this->admin_user->uid . '/shortcuts', array('set' => $new_set->set_name), t('Change set')); + $this->assertResponse(200); + $current_set = shortcut_current_displayed_set($this->admin_user); + $this->assertTrue($new_set->set_name == $current_set->set_name, 'Successfully switched own shortcut set.'); + } + + /** + * Tests switching another user's shortcut set. + */ + function testShortcutSetAssign() { + $new_set = $this->generateShortcutSet($this->randomName(10)); + + shortcut_set_assign_user($new_set, $this->shortcut_user); + $current_set = shortcut_current_displayed_set($this->shortcut_user); + $this->assertTrue($new_set->set_name == $current_set->set_name, "Successfully switched another user's shortcut set."); + } + + /** + * Tests switching a user's shortcut set and creating one at the same time. + */ + function testShortcutSetSwitchCreate() { + $edit = array( + 'set' => 'new', + 'new' => $this->randomName(10), + ); + $this->drupalPost('user/' . $this->admin_user->uid . '/shortcuts', $edit, t('Change set')); + $current_set = shortcut_current_displayed_set($this->admin_user); + $this->assertNotEqual($current_set->set_name, $this->set->set_name, 'A shortcut set can be switched to at the same time as it is created.'); + $this->assertEqual($current_set->title, $edit['new'], 'The new set is correctly assigned to the user.'); + } + + /** + * Tests switching a user's shortcut set without providing a new set name. + */ + function testShortcutSetSwitchNoSetName() { + $edit = array('set' => 'new'); + $this->drupalPost('user/' . $this->admin_user->uid . '/shortcuts', $edit, t('Change set')); + $this->assertText(t('The new set name is required.')); + $current_set = shortcut_current_displayed_set($this->admin_user); + $this->assertEqual($current_set->set_name, $this->set->set_name, 'Attempting to switch to a new shortcut set without providing a set name does not succeed.'); + } + + /** + * Tests that shortcut_set_save() correctly updates existing links. + */ + function testShortcutSetSave() { + $set = $this->set; + $old_mlids = $this->getShortcutInformation($set, 'mlid'); + + $set->links[] = $this->generateShortcutLink('admin', $this->randomName(10)); + shortcut_set_save($set); + $saved_set = shortcut_set_load($set->set_name); + + $new_mlids = $this->getShortcutInformation($saved_set, 'mlid'); + $this->assertTrue(count(array_intersect($old_mlids, $new_mlids)) == count($old_mlids), 'shortcut_set_save() did not inadvertently change existing mlids.'); + } + + /** + * Tests renaming a shortcut set. + */ + function testShortcutSetRename() { + $set = $this->set; + + $new_title = $this->randomName(10); + $this->drupalPost('admin/config/user-interface/shortcut/' . $set->set_name . '/edit', array('title' => $new_title), t('Save')); + $set = shortcut_set_load($set->set_name); + $this->assertTrue($set->title == $new_title, 'Shortcut set has been successfully renamed.'); + } + + /** + * Tests renaming a shortcut set to the same name as another set. + */ + function testShortcutSetRenameAlreadyExists() { + $set = $this->generateShortcutSet($this->randomName(10)); + $existing_title = $this->set->title; + $this->drupalPost('admin/config/user-interface/shortcut/' . $set->set_name . '/edit', array('title' => $existing_title), t('Save')); + $this->assertRaw(t('The shortcut set %name already exists. Choose another name.', array('%name' => $existing_title))); + $set = shortcut_set_load($set->set_name); + $this->assertNotEqual($set->title, $existing_title, format_string('The shortcut set %title cannot be renamed to %new-title because a shortcut set with that title already exists.', array('%title' => $set->title, '%new-title' => $existing_title))); + } + + /** + * Tests unassigning a shortcut set. + */ + function testShortcutSetUnassign() { + $new_set = $this->generateShortcutSet($this->randomName(10)); + + shortcut_set_assign_user($new_set, $this->shortcut_user); + shortcut_set_unassign_user($this->shortcut_user); + $current_set = shortcut_current_displayed_set($this->shortcut_user); + $default_set = shortcut_default_set($this->shortcut_user); + $this->assertTrue($current_set->set_name == $default_set->set_name, "Successfully unassigned another user's shortcut set."); + } + + /** + * Tests deleting a shortcut set. + */ + function testShortcutSetDelete() { + $new_set = $this->generateShortcutSet($this->randomName(10)); + + $this->drupalPost('admin/config/user-interface/shortcut/' . $new_set->set_name . '/delete', array(), t('Delete')); + $sets = shortcut_sets(); + $this->assertFalse(isset($sets[$new_set->set_name]), 'Successfully deleted a shortcut set.'); + } + + /** + * Tests deleting the default shortcut set. + */ + function testShortcutSetDeleteDefault() { + $this->drupalGet('admin/config/user-interface/shortcut/' . SHORTCUT_DEFAULT_SET_NAME . '/delete'); + $this->assertResponse(403); + } +} diff --git a/drupal7/web/modules/simpletest/drupal_web_test_case.php b/drupal7/web/modules/simpletest/drupal_web_test_case.php new file mode 100644 index 0000000..08b8dfe --- /dev/null +++ b/drupal7/web/modules/simpletest/drupal_web_test_case.php @@ -0,0 +1,4097 @@ + 0, + '#fail' => 0, + '#exception' => 0, + '#debug' => 0, + ); + + /** + * Assertions thrown in that test case. + * + * @var Array + */ + protected $assertions = array(); + + /** + * This class is skipped when looking for the source of an assertion. + * + * When displaying which function an assert comes from, it's not too useful + * to see "drupalWebTestCase->drupalLogin()', we would like to see the test + * that called it. So we need to skip the classes defining these helper + * methods. + */ + protected $skipClasses = array(__CLASS__ => TRUE); + + /** + * Flag to indicate whether the test has been set up. + * + * The setUp() method isolates the test from the parent Drupal site by + * creating a random prefix for the database and setting up a clean file + * storage directory. The tearDown() method then cleans up this test + * environment. We must ensure that setUp() has been run. Otherwise, + * tearDown() will act on the parent Drupal site rather than the test + * environment, destroying live data. + */ + protected $setup = FALSE; + + protected $setupDatabasePrefix = FALSE; + + protected $setupEnvironment = FALSE; + + /** + * Constructor for DrupalTestCase. + * + * @param $test_id + * Tests with the same id are reported together. + */ + public function __construct($test_id = NULL) { + $this->testId = $test_id; + } + + /** + * Internal helper: stores the assert. + * + * @param $status + * Can be 'pass', 'fail', 'exception'. + * TRUE is a synonym for 'pass', FALSE for 'fail'. + * @param $message + * The message string. + * @param $group + * Which group this assert belongs to. + * @param $caller + * By default, the assert comes from a function whose name starts with + * 'test'. Instead, you can specify where this assert originates from + * by passing in an associative array as $caller. Key 'file' is + * the name of the source file, 'line' is the line number and 'function' + * is the caller function itself. + */ + protected function assert($status, $message = '', $group = 'Other', array $caller = NULL) { + // Convert boolean status to string status. + if (is_bool($status)) { + $status = $status ? 'pass' : 'fail'; + } + + // Increment summary result counter. + $this->results['#' . $status]++; + + // Get the function information about the call to the assertion method. + if (!$caller) { + $caller = $this->getAssertionCall(); + } + + // Creation assertion array that can be displayed while tests are running. + $this->assertions[] = $assertion = array( + 'test_id' => $this->testId, + 'test_class' => get_class($this), + 'status' => $status, + 'message' => $message, + 'message_group' => $group, + 'function' => $caller['function'], + 'line' => $caller['line'], + 'file' => $caller['file'], + ); + + // Store assertion for display after the test has completed. + self::getDatabaseConnection() + ->insert('simpletest') + ->fields($assertion) + ->execute(); + + // We do not use a ternary operator here to allow a breakpoint on + // test failure. + if ($status == 'pass') { + return TRUE; + } + else { + return FALSE; + } + } + + /** + * Returns the database connection to the site running Simpletest. + * + * @return DatabaseConnection + * The database connection to use for inserting assertions. + */ + public static function getDatabaseConnection() { + try { + $connection = Database::getConnection('default', 'simpletest_original_default'); + } + catch (DatabaseConnectionNotDefinedException $e) { + // If the test was not set up, the simpletest_original_default + // connection does not exist. + $connection = Database::getConnection('default', 'default'); + } + + return $connection; + } + + /** + * Store an assertion from outside the testing context. + * + * This is useful for inserting assertions that can only be recorded after + * the test case has been destroyed, such as PHP fatal errors. The caller + * information is not automatically gathered since the caller is most likely + * inserting the assertion on behalf of other code. In all other respects + * the method behaves just like DrupalTestCase::assert() in terms of storing + * the assertion. + * + * @return + * Message ID of the stored assertion. + * + * @see DrupalTestCase::assert() + * @see DrupalTestCase::deleteAssert() + */ + public static function insertAssert($test_id, $test_class, $status, $message = '', $group = 'Other', array $caller = array()) { + // Convert boolean status to string status. + if (is_bool($status)) { + $status = $status ? 'pass' : 'fail'; + } + + $caller += array( + 'function' => t('Unknown'), + 'line' => 0, + 'file' => t('Unknown'), + ); + + $assertion = array( + 'test_id' => $test_id, + 'test_class' => $test_class, + 'status' => $status, + 'message' => $message, + 'message_group' => $group, + 'function' => $caller['function'], + 'line' => $caller['line'], + 'file' => $caller['file'], + ); + + return self::getDatabaseConnection() + ->insert('simpletest') + ->fields($assertion) + ->execute(); + } + + /** + * Delete an assertion record by message ID. + * + * @param $message_id + * Message ID of the assertion to delete. + * @return + * TRUE if the assertion was deleted, FALSE otherwise. + * + * @see DrupalTestCase::insertAssert() + */ + public static function deleteAssert($message_id) { + return (bool) self::getDatabaseConnection() + ->delete('simpletest') + ->condition('message_id', $message_id) + ->execute(); + } + + /** + * Cycles through backtrace until the first non-assertion method is found. + * + * @return + * Array representing the true caller. + */ + protected function getAssertionCall() { + $backtrace = debug_backtrace(); + + // The first element is the call. The second element is the caller. + // We skip calls that occurred in one of the methods of our base classes + // or in an assertion function. + while (($caller = $backtrace[1]) && + ((isset($caller['class']) && isset($this->skipClasses[$caller['class']])) || + substr($caller['function'], 0, 6) == 'assert')) { + // We remove that call. + array_shift($backtrace); + } + + return _drupal_get_last_caller($backtrace); + } + + /** + * Check to see if a value is not false (not an empty string, 0, NULL, or FALSE). + * + * @param $value + * The value on which the assertion is to be done. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * TRUE if the assertion succeeded, FALSE otherwise. + */ + protected function assertTrue($value, $message = '', $group = 'Other') { + return $this->assert((bool) $value, $message ? $message : t('Value @value is TRUE.', array('@value' => var_export($value, TRUE))), $group); + } + + /** + * Check to see if a value is false (an empty string, 0, NULL, or FALSE). + * + * @param $value + * The value on which the assertion is to be done. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * TRUE if the assertion succeeded, FALSE otherwise. + */ + protected function assertFalse($value, $message = '', $group = 'Other') { + return $this->assert(!$value, $message ? $message : t('Value @value is FALSE.', array('@value' => var_export($value, TRUE))), $group); + } + + /** + * Check to see if a value is NULL. + * + * @param $value + * The value on which the assertion is to be done. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * TRUE if the assertion succeeded, FALSE otherwise. + */ + protected function assertNull($value, $message = '', $group = 'Other') { + return $this->assert(!isset($value), $message ? $message : t('Value @value is NULL.', array('@value' => var_export($value, TRUE))), $group); + } + + /** + * Check to see if a value is not NULL. + * + * @param $value + * The value on which the assertion is to be done. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * TRUE if the assertion succeeded, FALSE otherwise. + */ + protected function assertNotNull($value, $message = '', $group = 'Other') { + return $this->assert(isset($value), $message ? $message : t('Value @value is not NULL.', array('@value' => var_export($value, TRUE))), $group); + } + + /** + * Check to see if two values are equal. + * + * @param $first + * The first value to check. + * @param $second + * The second value to check. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * TRUE if the assertion succeeded, FALSE otherwise. + */ + protected function assertEqual($first, $second, $message = '', $group = 'Other') { + return $this->assert($first == $second, $message ? $message : t('Value @first is equal to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group); + } + + /** + * Check to see if two values are not equal. + * + * @param $first + * The first value to check. + * @param $second + * The second value to check. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * TRUE if the assertion succeeded, FALSE otherwise. + */ + protected function assertNotEqual($first, $second, $message = '', $group = 'Other') { + return $this->assert($first != $second, $message ? $message : t('Value @first is not equal to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group); + } + + /** + * Check to see if two values are identical. + * + * @param $first + * The first value to check. + * @param $second + * The second value to check. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * TRUE if the assertion succeeded, FALSE otherwise. + */ + protected function assertIdentical($first, $second, $message = '', $group = 'Other') { + return $this->assert($first === $second, $message ? $message : t('Value @first is identical to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group); + } + + /** + * Check to see if two values are not identical. + * + * @param $first + * The first value to check. + * @param $second + * The second value to check. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * TRUE if the assertion succeeded, FALSE otherwise. + */ + protected function assertNotIdentical($first, $second, $message = '', $group = 'Other') { + return $this->assert($first !== $second, $message ? $message : t('Value @first is not identical to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group); + } + + /** + * Fire an assertion that is always positive. + * + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * TRUE. + */ + protected function pass($message = NULL, $group = 'Other') { + return $this->assert(TRUE, $message, $group); + } + + /** + * Fire an assertion that is always negative. + * + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * FALSE. + */ + protected function fail($message = NULL, $group = 'Other') { + return $this->assert(FALSE, $message, $group); + } + + /** + * Fire an error assertion. + * + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @param $caller + * The caller of the error. + * @return + * FALSE. + */ + protected function error($message = '', $group = 'Other', array $caller = NULL) { + if ($group == 'User notice') { + // Since 'User notice' is set by trigger_error() which is used for debug + // set the message to a status of 'debug'. + return $this->assert('debug', $message, 'Debug', $caller); + } + + return $this->assert('exception', $message, $group, $caller); + } + + /** + * Logs a verbose message in a text file. + * + * The link to the verbose message will be placed in the test results as a + * passing assertion with the text '[verbose message]'. + * + * @param $message + * The verbose message to be stored. + * + * @see simpletest_verbose() + */ + protected function verbose($message) { + if ($id = simpletest_verbose($message)) { + $class_safe = str_replace('\\', '_', get_class($this)); + $url = $this->verboseDirectoryUrl . '/' . $class_safe . '-' . $id . '.html'; + // Not using l() to avoid invoking the theme system, so that unit tests + // can use verbose() as well. + $link = '' . t('Verbose message') . ''; + $this->error($link, 'User notice'); + } + } + + /** + * Run all tests in this class. + * + * Regardless of whether $methods are passed or not, only method names + * starting with "test" are executed. + * + * @param $methods + * (optional) A list of method names in the test case class to run; e.g., + * array('testFoo', 'testBar'). By default, all methods of the class are + * taken into account, but it can be useful to only run a few selected test + * methods during debugging. + */ + public function run(array $methods = array()) { + // Initialize verbose debugging. + $class = get_class($this); + simpletest_verbose(NULL, variable_get('file_public_path', conf_path() . '/files'), str_replace('\\', '_', $class)); + + // HTTP auth settings (:) for the simpletest browser + // when sending requests to the test site. + $this->httpauth_method = variable_get('simpletest_httpauth_method', CURLAUTH_BASIC); + $username = variable_get('simpletest_httpauth_username', NULL); + $password = variable_get('simpletest_httpauth_password', NULL); + if ($username && $password) { + $this->httpauth_credentials = $username . ':' . $password; + } + + set_error_handler(array($this, 'errorHandler')); + // Iterate through all the methods in this class, unless a specific list of + // methods to run was passed. + $class_methods = get_class_methods($class); + if ($methods) { + $class_methods = array_intersect($class_methods, $methods); + } + foreach ($class_methods as $method) { + // If the current method starts with "test", run it - it's a test. + if (strtolower(substr($method, 0, 4)) == 'test') { + // Insert a fail record. This will be deleted on completion to ensure + // that testing completed. + $method_info = new ReflectionMethod($class, $method); + $caller = array( + 'file' => $method_info->getFileName(), + 'line' => $method_info->getStartLine(), + 'function' => $class . '->' . $method . '()', + ); + $completion_check_id = DrupalTestCase::insertAssert($this->testId, $class, FALSE, t('The test did not complete due to a fatal error.'), 'Completion check', $caller); + $this->setUp(); + if ($this->setup) { + try { + $this->$method(); + // Finish up. + } + catch (Throwable $e) { + $this->exceptionHandler($e); + } + catch (Exception $e) { + // Cater for older PHP versions. + $this->exceptionHandler($e); + } + $this->tearDown(); + } + else { + $this->fail(t("The test cannot be executed because it has not been set up properly.")); + } + // Remove the completion check record. + DrupalTestCase::deleteAssert($completion_check_id); + } + } + // Clear out the error messages and restore error handler. + drupal_get_messages(); + restore_error_handler(); + } + + /** + * Handle errors during test runs. + * + * Because this is registered in set_error_handler(), it has to be public. + * @see set_error_handler + */ + public function errorHandler($severity, $message, $file = NULL, $line = NULL) { + if ($severity & error_reporting()) { + $error_map = array( + E_STRICT => 'Run-time notice', + E_WARNING => 'Warning', + E_NOTICE => 'Notice', + E_CORE_ERROR => 'Core error', + E_CORE_WARNING => 'Core warning', + E_USER_ERROR => 'User error', + E_USER_WARNING => 'User warning', + E_USER_NOTICE => 'User notice', + E_RECOVERABLE_ERROR => 'Recoverable error', + ); + + // PHP 5.3 adds new error logging constants. Add these conditionally for + // backwards compatibility with PHP 5.2. + if (defined('E_DEPRECATED')) { + $error_map += array( + E_DEPRECATED => 'Deprecated', + E_USER_DEPRECATED => 'User deprecated', + ); + } + + $backtrace = debug_backtrace(); + $this->error($message, $error_map[$severity], _drupal_get_last_caller($backtrace)); + } + return TRUE; + } + + /** + * Handle exceptions. + * + * @see set_exception_handler + */ + protected function exceptionHandler($exception) { + $backtrace = $exception->getTrace(); + // Push on top of the backtrace the call that generated the exception. + array_unshift($backtrace, array( + 'line' => $exception->getLine(), + 'file' => $exception->getFile(), + )); + require_once DRUPAL_ROOT . '/includes/errors.inc'; + // The exception message is run through check_plain() by _drupal_decode_exception(). + $this->error(t('%type: !message in %function (line %line of %file).', _drupal_decode_exception($exception)), 'Uncaught exception', _drupal_get_last_caller($backtrace)); + } + + /** + * Generates a random string of ASCII characters of codes 32 to 126. + * + * The generated string includes alpha-numeric characters and common + * miscellaneous characters. Use this method when testing general input + * where the content is not restricted. + * + * Do not use this method when special characters are not possible (e.g., in + * machine or file names that have already been validated); instead, + * use DrupalWebTestCase::randomName(). + * + * @param $length + * Length of random string to generate. + * + * @return + * Randomly generated string. + * + * @see DrupalWebTestCase::randomName() + */ + public static function randomString($length = 8) { + $str = ''; + for ($i = 0; $i < $length; $i++) { + $str .= chr(mt_rand(32, 126)); + } + return $str; + } + + /** + * Generates a random string containing letters and numbers. + * + * The string will always start with a letter. The letters may be upper or + * lower case. This method is better for restricted inputs that do not + * accept certain characters. For example, when testing input fields that + * require machine readable values (i.e. without spaces and non-standard + * characters) this method is best. + * + * Do not use this method when testing unvalidated user input. Instead, use + * DrupalWebTestCase::randomString(). + * + * @param $length + * Length of random string to generate. + * + * @return + * Randomly generated string. + * + * @see DrupalWebTestCase::randomString() + */ + public static function randomName($length = 8) { + $values = array_merge(range(65, 90), range(97, 122), range(48, 57)); + $max = count($values) - 1; + $str = chr(mt_rand(97, 122)); + for ($i = 1; $i < $length; $i++) { + $str .= chr($values[mt_rand(0, $max)]); + } + return $str; + } + + /** + * Converts a list of possible parameters into a stack of permutations. + * + * Takes a list of parameters containing possible values, and converts all of + * them into a list of items containing every possible permutation. + * + * Example: + * @code + * $parameters = array( + * 'one' => array(0, 1), + * 'two' => array(2, 3), + * ); + * $permutations = DrupalTestCase::generatePermutations($parameters) + * // Result: + * $permutations == array( + * array('one' => 0, 'two' => 2), + * array('one' => 1, 'two' => 2), + * array('one' => 0, 'two' => 3), + * array('one' => 1, 'two' => 3), + * ) + * @endcode + * + * @param $parameters + * An associative array of parameters, keyed by parameter name, and whose + * values are arrays of parameter values. + * + * @return + * A list of permutations, which is an array of arrays. Each inner array + * contains the full list of parameters that have been passed, but with a + * single value only. + */ + public static function generatePermutations($parameters) { + $all_permutations = array(array()); + foreach ($parameters as $parameter => $values) { + $new_permutations = array(); + // Iterate over all values of the parameter. + foreach ($values as $value) { + // Iterate over all existing permutations. + foreach ($all_permutations as $permutation) { + // Add the new parameter value to existing permutations. + $new_permutations[] = $permutation + array($parameter => $value); + } + } + // Replace the old permutations with the new permutations. + $all_permutations = $new_permutations; + } + return $all_permutations; + } +} + +/** + * Test case for Drupal unit tests. + * + * These tests can not access the database nor files. Calling any Drupal + * function that needs the database will throw exceptions. These include + * watchdog(), module_implements(), module_invoke_all() etc. + */ +class DrupalUnitTestCase extends DrupalTestCase { + + /** + * Constructor for DrupalUnitTestCase. + */ + function __construct($test_id = NULL) { + parent::__construct($test_id); + $this->skipClasses[__CLASS__] = TRUE; + } + + /** + * Sets up unit test environment. + * + * Unlike DrupalWebTestCase::setUp(), DrupalUnitTestCase::setUp() does not + * install modules because tests are performed without accessing the database. + * Any required files must be explicitly included by the child class setUp() + * method. + */ + protected function setUp() { + global $conf, $language; + + // Store necessary current values before switching to the test environment. + $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files'); + $this->verboseDirectoryUrl = file_create_url($this->originalFileDirectory . '/simpletest/verbose'); + + // Set up English language. + $this->originalLanguage = $language; + $this->originalLanguageDefault = variable_get('language_default'); + unset($conf['language_default']); + $language = language_default(); + + // Reset all statics so that test is performed with a clean environment. + drupal_static_reset(); + + // Generate temporary prefixed database to ensure that tests have a clean starting point. + $this->databasePrefix = Database::getConnection()->prefixTables('{simpletest' . mt_rand(1000, 1000000) . '}'); + + // Create test directory. + $public_files_directory = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10); + file_prepare_directory($public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + $conf['file_public_path'] = $public_files_directory; + + // Clone the current connection and replace the current prefix. + $connection_info = Database::getConnectionInfo('default'); + Database::renameConnection('default', 'simpletest_original_default'); + foreach ($connection_info as $target => $value) { + $connection_info[$target]['prefix'] = array( + 'default' => $value['prefix']['default'] . $this->databasePrefix, + ); + } + Database::addConnectionInfo('default', 'default', $connection_info['default']); + + // Set user agent to be consistent with web test case. + $_SERVER['HTTP_USER_AGENT'] = $this->databasePrefix; + + // If locale is enabled then t() will try to access the database and + // subsequently will fail as the database is not accessible. + $module_list = module_list(); + if (isset($module_list['locale'])) { + // Transform the list into the format expected as input to module_list(). + foreach ($module_list as &$module) { + $module = array('filename' => drupal_get_filename('module', $module)); + } + $this->originalModuleList = $module_list; + unset($module_list['locale']); + module_list(TRUE, FALSE, FALSE, $module_list); + } + $this->setup = TRUE; + } + + protected function tearDown() { + global $conf, $language; + + // Get back to the original connection. + Database::removeConnection('default'); + Database::renameConnection('simpletest_original_default', 'default'); + + $conf['file_public_path'] = $this->originalFileDirectory; + // Restore modules if necessary. + if (isset($this->originalModuleList)) { + module_list(TRUE, FALSE, FALSE, $this->originalModuleList); + } + + // Reset language. + $language = $this->originalLanguage; + if ($this->originalLanguageDefault) { + $GLOBALS['conf']['language_default'] = $this->originalLanguageDefault; + } + } +} + +/** + * Test case for typical Drupal tests. + */ +class DrupalWebTestCase extends DrupalTestCase { + /** + * The profile to install as a basis for testing. + * + * @var string + */ + protected $profile = 'standard'; + + /** + * The URL currently loaded in the internal browser. + * + * @var string + */ + protected $url; + + /** + * The handle of the current cURL connection. + * + * @var resource + */ + protected $curlHandle; + + /** + * The headers of the page currently loaded in the internal browser. + * + * @var Array + */ + protected $headers; + + /** + * The content of the page currently loaded in the internal browser. + * + * @var string + */ + protected $content; + + /** + * The content of the page currently loaded in the internal browser (plain text version). + * + * @var string + */ + protected $plainTextContent; + + /** + * The value of the Drupal.settings JavaScript variable for the page currently loaded in the internal browser. + * + * @var Array + */ + protected $drupalSettings; + + /** + * The parsed version of the page. + * + * @var SimpleXMLElement + */ + protected $elements = NULL; + + /** + * The current user logged in using the internal browser. + * + * @var bool + */ + protected $loggedInUser = FALSE; + + /** + * The current cookie file used by cURL. + * + * We do not reuse the cookies in further runs, so we do not need a file + * but we still need cookie handling, so we set the jar to NULL. + */ + protected $cookieFile = NULL; + + /** + * The cookies of the page currently loaded in the internal browser. + * + * @var array + */ + protected $cookies = array(); + + /** + * Additional cURL options. + * + * DrupalWebTestCase itself never sets this but always obeys what is set. + */ + protected $additionalCurlOptions = array(); + + /** + * The original user, before it was changed to a clean uid = 1 for testing purposes. + * + * @var object + */ + protected $originalUser = NULL; + + /** + * The original shutdown handlers array, before it was cleaned for testing purposes. + * + * @var array + */ + protected $originalShutdownCallbacks = array(); + + /** + * HTTP authentication method + */ + protected $httpauth_method = CURLAUTH_BASIC; + + /** + * HTTP authentication credentials (:). + */ + protected $httpauth_credentials = NULL; + + /** + * The current session name, if available. + */ + protected $session_name = NULL; + + /** + * The current session ID, if available. + */ + protected $session_id = NULL; + + /** + * Whether the files were copied to the test files directory. + */ + protected $generatedTestFiles = FALSE; + + /** + * The number of redirects followed during the handling of a request. + */ + protected $redirect_count; + + /** + * The original language URL. + */ + protected $originalLanguageUrl; + + /** + * The original active installation profile. + */ + protected $originalProfile; + + /** + * The original clean_url variable value. + */ + protected $originalCleanUrl; + + /** + * The public files directory created for testing purposes. + */ + protected $public_files_directory; + + /** + * The private files directory created for testing purposes. + */ + protected $private_files_directory; + + /** + * The temporary files directory created for testing purposes. + */ + protected $temp_files_directory; + + /** + * Constructor for DrupalWebTestCase. + */ + function __construct($test_id = NULL) { + parent::__construct($test_id); + $this->skipClasses[__CLASS__] = TRUE; + } + + /** + * Get a node from the database based on its title. + * + * @param $title + * A node title, usually generated by $this->randomName(). + * @param $reset + * (optional) Whether to reset the internal node_load() cache. + * + * @return + * A node object matching $title. + */ + function drupalGetNodeByTitle($title, $reset = FALSE) { + $nodes = node_load_multiple(array(), array('title' => $title), $reset); + // Load the first node returned from the database. + $returned_node = reset($nodes); + return $returned_node; + } + + /** + * Creates a node based on default settings. + * + * @param $settings + * An associative array of settings to change from the defaults, keys are + * node properties, for example 'title' => 'Hello, world!'. + * @return + * Created node object. + */ + protected function drupalCreateNode($settings = array()) { + // Populate defaults array. + $settings += array( + 'title' => $this->randomName(8), + 'comment' => 2, + 'changed' => REQUEST_TIME, + 'moderate' => 0, + 'promote' => 0, + 'revision' => 1, + 'log' => '', + 'status' => 1, + 'sticky' => 0, + 'type' => 'page', + 'revisions' => NULL, + 'language' => LANGUAGE_NONE, + ); + + // Add the body after the language is defined so that it may be set + // properly. + $settings += array( + 'body' => array($settings['language'] => array(array())), + ); + + // Use the original node's created time for existing nodes. + if (isset($settings['created']) && !isset($settings['date'])) { + $settings['date'] = format_date($settings['created'], 'custom', 'Y-m-d H:i:s O'); + } + + // If the node's user uid is not specified manually, use the currently + // logged in user if available, or else the user running the test. + if (!isset($settings['uid'])) { + if ($this->loggedInUser) { + $settings['uid'] = $this->loggedInUser->uid; + } + else { + global $user; + $settings['uid'] = $user->uid; + } + } + + // Merge body field value and format separately. + $body = array( + 'value' => $this->randomName(32), + 'format' => filter_default_format(), + ); + $settings['body'][$settings['language']][0] += $body; + + $node = (object) $settings; + node_save($node); + + // Small hack to link revisions to our test user. + db_update('node_revision') + ->fields(array('uid' => $node->uid)) + ->condition('vid', $node->vid) + ->execute(); + return $node; + } + + /** + * Creates a custom content type based on default settings. + * + * @param $settings + * An array of settings to change from the defaults. + * Example: 'type' => 'foo'. + * @return + * Created content type. + */ + protected function drupalCreateContentType($settings = array()) { + // Find a non-existent random type name. + do { + $name = strtolower($this->randomName(8)); + } while (node_type_get_type($name)); + + // Populate defaults array. + $defaults = array( + 'type' => $name, + 'name' => $name, + 'base' => 'node_content', + 'description' => '', + 'help' => '', + 'title_label' => 'Title', + 'has_title' => 1, + ); + // Imposed values for a custom type. + $forced = array( + 'orig_type' => '', + 'old_type' => '', + 'module' => 'node', + 'custom' => 1, + 'modified' => 1, + 'locked' => 0, + ); + $type = $forced + $settings + $defaults; + $type = (object) $type; + + $saved_type = node_type_save($type); + node_types_rebuild(); + menu_rebuild(); + node_add_body_field($type); + + $this->assertEqual($saved_type, SAVED_NEW, t('Created content type %type.', array('%type' => $type->type))); + + // Reset permissions so that permissions for this content type are available. + $this->checkPermissions(array(), TRUE); + + return $type; + } + + /** + * Get a list files that can be used in tests. + * + * @param $type + * File type, possible values: 'binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'. + * @param $size + * File size in bytes to match. Please check the tests/files folder. + * @return + * List of files that match filter. + */ + protected function drupalGetTestFiles($type, $size = NULL) { + if (empty($this->generatedTestFiles)) { + // Generate binary test files. + $lines = array(64, 1024); + $count = 0; + foreach ($lines as $line) { + simpletest_generate_file('binary-' . $count++, 64, $line, 'binary'); + } + + // Generate text test files. + $lines = array(16, 256, 1024, 2048, 20480); + $count = 0; + foreach ($lines as $line) { + simpletest_generate_file('text-' . $count++, 64, $line, 'text'); + } + + // Copy other test files from simpletest. + $original = drupal_get_path('module', 'simpletest') . '/files'; + $files = file_scan_directory($original, '/(html|image|javascript|php|sql)-.*/'); + foreach ($files as $file) { + file_unmanaged_copy($file->uri, variable_get('file_public_path', conf_path() . '/files')); + } + + $this->generatedTestFiles = TRUE; + } + + $files = array(); + // Make sure type is valid. + if (in_array($type, array('binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'))) { + $files = file_scan_directory('public://', '/' . $type . '\-.*/'); + + // If size is set then remove any files that are not of that size. + if ($size !== NULL) { + foreach ($files as $file) { + $stats = stat($file->uri); + if ($stats['size'] != $size) { + unset($files[$file->uri]); + } + } + } + } + usort($files, array($this, 'drupalCompareFiles')); + return $files; + } + + /** + * Compare two files based on size and file name. + */ + protected function drupalCompareFiles($file1, $file2) { + $compare_size = filesize($file1->uri) - filesize($file2->uri); + if ($compare_size) { + // Sort by file size. + return $compare_size; + } + else { + // The files were the same size, so sort alphabetically. + return strnatcmp($file1->name, $file2->name); + } + } + + /** + * Create a user with a given set of permissions. + * + * @param array $permissions + * Array of permission names to assign to user. Note that the user always + * has the default permissions derived from the "authenticated users" role. + * + * @return object|false + * A fully loaded user object with pass_raw property, or FALSE if account + * creation fails. + */ + protected function drupalCreateUser(array $permissions = array()) { + // Create a role with the given permission set, if any. + $rid = FALSE; + if ($permissions) { + $rid = $this->drupalCreateRole($permissions); + if (!$rid) { + return FALSE; + } + } + + // Create a user assigned to that role. + $edit = array(); + $edit['name'] = $this->randomName(); + $edit['mail'] = $edit['name'] . '@example.com'; + $edit['pass'] = user_password(); + $edit['status'] = 1; + if ($rid) { + $edit['roles'] = array($rid => $rid); + } + + $account = user_save(drupal_anonymous_user(), $edit); + + $this->assertTrue(!empty($account->uid), t('User created with name %name and pass %pass', array('%name' => $edit['name'], '%pass' => $edit['pass'])), t('User login')); + if (empty($account->uid)) { + return FALSE; + } + + // Add the raw password so that we can log in as this user. + $account->pass_raw = $edit['pass']; + return $account; + } + + /** + * Creates a role with specified permissions. + * + * @param $permissions + * Array of permission names to assign to role. + * @param $name + * (optional) String for the name of the role. Defaults to a random string. + * @return + * Role ID of newly created role, or FALSE if role creation failed. + */ + protected function drupalCreateRole(array $permissions, $name = NULL) { + // Generate random name if it was not passed. + if (!$name) { + $name = $this->randomName(); + } + + // Check the all the permissions strings are valid. + if (!$this->checkPermissions($permissions)) { + return FALSE; + } + + // Create new role. + $role = new stdClass(); + $role->name = $name; + user_role_save($role); + user_role_grant_permissions($role->rid, $permissions); + + $this->assertTrue(isset($role->rid), t('Created role of name: @name, id: @rid', array('@name' => $name, '@rid' => (isset($role->rid) ? $role->rid : t('-n/a-')))), t('Role')); + if ($role && !empty($role->rid)) { + $count = db_query('SELECT COUNT(*) FROM {role_permission} WHERE rid = :rid', array(':rid' => $role->rid))->fetchField(); + $this->assertTrue($count == count($permissions), t('Created permissions: @perms', array('@perms' => implode(', ', $permissions))), t('Role')); + return $role->rid; + } + else { + return FALSE; + } + } + + /** + * Check to make sure that the array of permissions are valid. + * + * @param $permissions + * Permissions to check. + * @param $reset + * Reset cached available permissions. + * @return + * TRUE or FALSE depending on whether the permissions are valid. + */ + protected function checkPermissions(array $permissions, $reset = FALSE) { + $available = &drupal_static(__FUNCTION__); + + if (!isset($available) || $reset) { + $available = array_keys(module_invoke_all('permission')); + } + + $valid = TRUE; + foreach ($permissions as $permission) { + if (!in_array($permission, $available)) { + $this->fail(t('Invalid permission %permission.', array('%permission' => $permission)), t('Role')); + $valid = FALSE; + } + } + return $valid; + } + + /** + * Log in a user with the internal browser. + * + * If a user is already logged in, then the current user is logged out before + * logging in the specified user. + * + * Please note that neither the global $user nor the passed-in user object is + * populated with data of the logged in user. If you need full access to the + * user object after logging in, it must be updated manually. If you also need + * access to the plain-text password of the user (set by drupalCreateUser()), + * e.g. to log in the same user again, then it must be re-assigned manually. + * For example: + * @code + * // Create a user. + * $account = $this->drupalCreateUser(array()); + * $this->drupalLogin($account); + * // Load real user object. + * $pass_raw = $account->pass_raw; + * $account = user_load($account->uid); + * $account->pass_raw = $pass_raw; + * @endcode + * + * @param $account + * User object representing the user to log in. + * + * @see drupalCreateUser() + */ + protected function drupalLogin(stdClass $account) { + if ($this->loggedInUser) { + $this->drupalLogout(); + } + + $edit = array( + 'name' => $account->name, + 'pass' => $account->pass_raw + ); + $this->drupalPost('user', $edit, t('Log in')); + + // If a "log out" link appears on the page, it is almost certainly because + // the login was successful. + $pass = $this->assertLink(t('Log out'), 0, t('User %name successfully logged in.', array('%name' => $account->name)), t('User login')); + + if ($pass) { + $this->loggedInUser = $account; + } + } + + /** + * Generate a token for the currently logged in user. + */ + protected function drupalGetToken($value = '') { + $private_key = drupal_get_private_key(); + return drupal_hmac_base64($value, $this->session_id . $private_key); + } + + /* + * Logs a user out of the internal browser, then check the login page to confirm logout. + */ + protected function drupalLogout() { + // Make a request to the logout page, and redirect to the user page, the + // idea being if you were properly logged out you should be seeing a login + // screen. + $this->drupalGet('user/logout'); + $this->drupalGet('user'); + $pass = $this->assertField('name', t('Username field found.'), t('Logout')); + $pass = $pass && $this->assertField('pass', t('Password field found.'), t('Logout')); + + if ($pass) { + $this->loggedInUser = FALSE; + } + } + + /** + * Generates a database prefix for running tests. + * + * The generated database table prefix is used for the Drupal installation + * being performed for the test. It is also used as user agent HTTP header + * value by the cURL-based browser of DrupalWebTestCase, which is sent + * to the Drupal installation of the test. During early Drupal bootstrap, the + * user agent HTTP header is parsed, and if it matches, all database queries + * use the database table prefix that has been generated here. + * + * @see DrupalWebTestCase::curlInitialize() + * @see drupal_valid_test_ua() + * @see DrupalWebTestCase::setUp() + */ + protected function prepareDatabasePrefix() { + $this->databasePrefix = 'simpletest' . mt_rand(1000, 1000000); + + // As soon as the database prefix is set, the test might start to execute. + // All assertions as well as the SimpleTest batch operations are associated + // with the testId, so the database prefix has to be associated with it. + db_update('simpletest_test_id') + ->fields(array('last_prefix' => $this->databasePrefix)) + ->condition('test_id', $this->testId) + ->execute(); + } + + /** + * Changes the database connection to the prefixed one. + * + * @see DrupalWebTestCase::setUp() + */ + protected function changeDatabasePrefix() { + if (empty($this->databasePrefix)) { + $this->prepareDatabasePrefix(); + // If $this->prepareDatabasePrefix() failed to work, return without + // setting $this->setupDatabasePrefix to TRUE, so setUp() methods will + // know to bail out. + if (empty($this->databasePrefix)) { + return; + } + } + + // Clone the current connection and replace the current prefix. + $connection_info = Database::getConnectionInfo('default'); + Database::renameConnection('default', 'simpletest_original_default'); + foreach ($connection_info as $target => $value) { + $connection_info[$target]['prefix'] = array( + 'default' => $value['prefix']['default'] . $this->databasePrefix, + ); + } + Database::addConnectionInfo('default', 'default', $connection_info['default']); + + // Indicate the database prefix was set up correctly. + $this->setupDatabasePrefix = TRUE; + } + + /** + * Prepares the current environment for running the test. + * + * Backups various current environment variables and resets them, so they do + * not interfere with the Drupal site installation in which tests are executed + * and can be restored in tearDown(). + * + * Also sets up new resources for the testing environment, such as the public + * filesystem and configuration directories. + * + * @see DrupalWebTestCase::setUp() + * @see DrupalWebTestCase::tearDown() + */ + protected function prepareEnvironment() { + global $user, $language, $language_url, $conf, $theme, $theme_key, $theme_path; + + // Store necessary current values before switching to prefixed database. + $this->originalLanguage = $language; + $this->originalLanguageUrl = $language_url; + $this->originalLanguageDefault = variable_get('language_default'); + $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files'); + $this->verboseDirectoryUrl = file_create_url($this->originalFileDirectory . '/simpletest/verbose'); + $this->originalProfile = drupal_get_profile(); + $this->originalCleanUrl = variable_get('clean_url', 0); + $this->originalUser = $user; + + // Set to English to prevent exceptions from utf8_truncate() from t() + // during install if the current language is not 'en'. + // The following array/object conversion is copied from language_default(). + $language_url = $language = (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => ''); + + // Reset the theme globals. + $this->originalTheme = $theme; + $this->originalThemeKey = $theme_key; + $this->originalThemePath = $theme_path; + $theme = $theme_key = $theme_path = NULL; + + // Save and clean the shutdown callbacks array because it is static cached + // and will be changed by the test run. Otherwise it will contain callbacks + // from both environments and the testing environment will try to call the + // handlers defined by the original one. + $callbacks = &drupal_register_shutdown_function(); + $this->originalShutdownCallbacks = $callbacks; + $callbacks = array(); + + // Create test directory ahead of installation so fatal errors and debug + // information can be logged during installation process. + // Use temporary files directory with the same prefix as the database. + $this->public_files_directory = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10); + $this->private_files_directory = $this->public_files_directory . '/private'; + $this->temp_files_directory = $this->private_files_directory . '/temp'; + + // Create the directories + file_prepare_directory($this->public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + file_prepare_directory($this->private_files_directory, FILE_CREATE_DIRECTORY); + file_prepare_directory($this->temp_files_directory, FILE_CREATE_DIRECTORY); + $this->generatedTestFiles = FALSE; + + // Log fatal errors. + ini_set('log_errors', 1); + ini_set('error_log', $this->public_files_directory . '/error.log'); + + // Set the test information for use in other parts of Drupal. + $test_info = &$GLOBALS['drupal_test_info']; + $test_info['test_run_id'] = $this->databasePrefix; + $test_info['in_child_site'] = FALSE; + + // Indicate the environment was set up correctly. + $this->setupEnvironment = TRUE; + } + + /** + * Copies the cached tables and files for a cached installation setup. + * + * @param string $cache_key_prefix + * (optional) Additional prefix for the cache key. + * + * @return bool + * TRUE when the cache was usable and loaded, FALSE when cache was not + * available. + * + * @see DrupalWebTestCase::setUp() + */ + protected function loadSetupCache($cache_key_prefix = '') { + $cache_key = $this->getSetupCacheKey($cache_key_prefix); + $cache_file = $this->originalFileDirectory . '/simpletest/' . $cache_key . '/simpletest-cache-setup'; + + if (file_exists($cache_file)) { + return $this->copySetupCache($cache_key, substr($this->databasePrefix, 10)); + } + + return FALSE; + } + + /** + * Returns the cache key used for the setup caching. + * + * @param string $cache_key_prefix + * (optional) Additional prefix for the cache key. + * + * @return string + * The cache key to use, by default only based on the profile used by the + * test. + */ + protected function getSetupCacheKey($cache_key_prefix = '') { + // The cache key needs to start with a numeric character, so that the cached + // installation gets cleaned up properly. + $cache_key_prefix = hash('crc32b', $cache_key_prefix . $this->profile); + return '1c' . $cache_key_prefix; + } + + /** + * Store the installation setup to a cache. + * + * @param string $cache_key_prefix + * (optional) Additional prefix for the cache key. + * + * @return bool + * TRUE if the installation was stored in the cache, FALSE otherwise. + */ + protected function storeSetupCache($cache_key_prefix = '') { + $cache_key = $this->getSetupCacheKey($cache_key_prefix); + $lock_key = 'simpletest_store_cache_' . $cache_key . '_' . $this->testId; + + // All concurrent tests share the same test id. Therefore it is possible to + // use the lock to ensure that only one process will store the cache. This + // is important as else DB tables created by one process could be deleted + // by another as the cache copying is idempotent. + if (! lock_acquire($lock_key)) { + return FALSE; + } + + // Try to copy the installation to the setup cache - now that we have a + // lock to do so. + if (!$this->copySetupCache(substr($this->databasePrefix, 10), $cache_key)) { + // It is non-fatal if the cache cannot be copied as the next test run + // will try it again. + $this->assert('debug', t('Storing cache with key @key failed', array('@key' => $cache_key)), 'storeSetupCache'); + + lock_release($lock_key); + return FALSE; + } + + // Inform others that this cache is usable now. + $cache_file = $this->originalFileDirectory . '/simpletest/' . $cache_key . '/simpletest-cache-setup'; + file_put_contents($cache_file, time()); + + lock_release($lock_key); + return TRUE; + } + + /** + * Copy the setup cache from/to another table and files directory. + * + * @param string $from + * The prefix_id / cache_key from where to copy. + * @param string $to + * The prefix_id / cache_key to where to copy. + * + * @return bool + * TRUE if the setup cache was copied to the current installation, FALSE + * otherwise. + */ + protected function copySetupCache($from, $to) { + $from_prefix = 'simpletest' . $from; + $to_prefix = 'simpletest' . $to; + + try { + $tables = db_query("SHOW TABLES LIKE :prefix", array(':prefix' => db_like($from_prefix) . '%' ))->fetchCol(); + + if (count($tables) == 0) { + return FALSE; + } + + foreach ($tables as $from_table) { + $table = substr($from_table, strlen($from_prefix)); + $to_table = $to_prefix . $table; + + // Remove the table in case the copying process was interrupted. + db_query('DROP TABLE IF EXISTS ' . $to_table); + db_query('CREATE TABLE ' . $to_table . ' LIKE ' . $from_table); + db_query('ALTER TABLE ' . $to_table . ' DISABLE KEYS'); + db_query('INSERT ' . $to_table . ' SELECT * FROM ' . $from_table); + db_query('ALTER TABLE ' . $to_table . ' ENABLE KEYS'); + } + } + catch (Exception $e) { + return FALSE; + } + + $from_dir = $this->originalFileDirectory . '/simpletest/' . $from; + $to_dir = $this->originalFileDirectory . '/simpletest/' . $to; + $this->recursiveDirectoryCopy($from_dir, $to_dir); + + return TRUE; + } + + /** + * Recursively copy one directory to another. + * + * @param $src + * The source directory. + * @param $dest + * The destination directory. + */ + protected function recursiveDirectoryCopy($src, $dst) { + $dir = opendir($src); + + if (!file_exists($dst)){ + mkdir($dst); + } + while (($file = readdir($dir)) !== FALSE) { + if ($file != '.' && $file != '..') { + if (is_dir($src . '/' . $file)) { + $this->recursiveDirectoryCopy($src . '/' . $file, $dst . '/' . $file); + } + else { + copy($src . '/' . $file, $dst . '/' . $file); + } + } + } + closedir($dir); + } + + /** + * Sets up a Drupal site for running functional and integration tests. + * + * Generates a random database prefix and installs Drupal with the specified + * installation profile in DrupalWebTestCase::$profile into the + * prefixed database. Afterwards, installs any additional modules specified by + * the test. + * + * After installation all caches are flushed and several configuration values + * are reset to the values of the parent site executing the test, since the + * default values may be incompatible with the environment in which tests are + * being executed. + * + * @param ... + * List of modules to enable for the duration of the test. This can be + * either a single array or a variable number of string arguments. + * + * @see DrupalWebTestCase::prepareDatabasePrefix() + * @see DrupalWebTestCase::changeDatabasePrefix() + * @see DrupalWebTestCase::prepareEnvironment() + */ + protected function setUp() { + global $user, $language, $language_url, $conf; + + // Create the database prefix for this test. + $this->prepareDatabasePrefix(); + + // Prepare the environment for running tests. + $this->prepareEnvironment(); + if (!$this->setupEnvironment) { + return FALSE; + } + + // Reset all statics and variables to perform tests in a clean environment. + $conf = array(); + drupal_static_reset(); + + // Change the database prefix. + // All static variables need to be reset before the database prefix is + // changed, since DrupalCacheArray implementations attempt to + // write back to persistent caches when they are destructed. + $this->changeDatabasePrefix(); + if (!$this->setupDatabasePrefix) { + return FALSE; + } + + // Preset the 'install_profile' system variable, so the first call into + // system_rebuild_module_data() (in drupal_install_system()) will register + // the test's profile as a module. Without this, the installation profile of + // the parent site (executing the test) is registered, and the test + // profile's hook_install() and other hook implementations are never invoked. + $conf['install_profile'] = $this->profile; + + $has_installation_cache = FALSE; + $has_modules_cache = FALSE; + + if ($this->useSetupModulesCache) { + $modules = func_get_args(); + // Modules can be either one parameter or multiple. + if (isset($modules[0]) && is_array($modules[0])) { + $modules = $modules[0]; + } + $modules = array_unique($modules); + sort($modules); + + $modules_cache_key_prefix = hash('crc32b', serialize($modules)) . '_'; + $has_modules_cache = $this->loadSetupCache($modules_cache_key_prefix); + } + + if (!$has_modules_cache && $this->useSetupInstallationCache) { + $has_installation_cache = $this->loadSetupCache(); + } + + if ($has_modules_cache || $has_installation_cache) { + // Reset path variables. + variable_set('file_public_path', $this->public_files_directory); + variable_set('file_private_path', $this->private_files_directory); + variable_set('file_temporary_path', $this->temp_files_directory); + $this->refreshVariables(); + + // Load all enabled modules + module_load_all(); + + $this->pass(t('Using cache: @cache (@key)', array( + '@cache' => $has_modules_cache ? t('Modules Cache') : t('Installation Cache'), + '@key' => $this->getSetupCacheKey($has_modules_cache ? $modules_cache_key_prefix : ''), + ))); + } + else { + // Perform the actual Drupal installation. + include_once DRUPAL_ROOT . '/includes/install.inc'; + drupal_install_system(); + + $this->preloadRegistry(); + + // Set path variables. + variable_set('file_public_path', $this->public_files_directory); + variable_set('file_private_path', $this->private_files_directory); + variable_set('file_temporary_path', $this->temp_files_directory); + + // Set the 'simpletest_parent_profile' variable to add the parent profile's + // search path to the child site's search paths. + // @see drupal_system_listing() + // @todo This may need to be primed like 'install_profile' above. + variable_set('simpletest_parent_profile', $this->originalProfile); + + // Include the testing profile. + variable_set('install_profile', $this->profile); + $profile_details = install_profile_info($this->profile, 'en'); + + // Install the modules specified by the testing profile. + module_enable($profile_details['dependencies'], FALSE); + + if ($this->useSetupInstallationCache) { + $this->storeSetupCache(); + } + } + + if (!$has_modules_cache) { + // Install modules needed for this test. This could have been passed in as + // either a single array argument or a variable number of string arguments. + // @todo Remove this compatibility layer in Drupal 8, and only accept + // $modules as a single array argument. + $modules = func_get_args(); + if (isset($modules[0]) && is_array($modules[0])) { + $modules = $modules[0]; + } + if ($modules) { + $success = module_enable($modules, TRUE); + $this->assertTrue($success, t('Enabled modules: %modules', array('%modules' => implode(', ', $modules)))); + } + + // Run the profile tasks. + $install_profile_module_exists = db_query("SELECT 1 FROM {system} WHERE type = 'module' AND name = :name", array( + ':name' => $this->profile, + ))->fetchField(); + if ($install_profile_module_exists) { + module_enable(array($this->profile), FALSE); + } + + // Reset/rebuild all data structures after enabling the modules. + $this->resetAll(); + + // Run cron once in that environment, as install.php does at the end of + // the installation process. + drupal_cron_run(); + + if ($this->useSetupModulesCache) { + $this->storeSetupCache($modules_cache_key_prefix); + } + } + else { + // Reset/rebuild all data structures after enabling the modules. + $this->resetAll(); + } + + // Ensure that the session is not written to the new environment and replace + // the global $user session with uid 1 from the new test site. + drupal_save_session(FALSE); + // Login as uid 1. + $user = user_load(1); + + // Restore necessary variables. + variable_set('install_task', 'done'); + variable_set('clean_url', $this->originalCleanUrl); + variable_set('site_mail', 'simpletest@example.com'); + variable_set('date_default_timezone', date_default_timezone_get()); + + // Set up English language. + unset($conf['language_default']); + $language_url = $language = language_default(); + + // Use the test mail class instead of the default mail handler class. + variable_set('mail_system', array('default-system' => 'TestingMailSystem')); + + drupal_set_time_limit($this->timeLimit); + $this->setup = TRUE; + } + + /** + * Preload the registry from the testing site. + * + * This method is called by DrupalWebTestCase::setUp(), and preloads the + * registry from the testing site to cut down on the time it takes to + * set up a clean environment for the current test run. + */ + protected function preloadRegistry() { + // Use two separate queries, each with their own connections: copy the + // {registry} and {registry_file} tables over from the parent installation + // to the child installation. + $original_connection = Database::getConnection('default', 'simpletest_original_default'); + $test_connection = Database::getConnection(); + + foreach (array('registry', 'registry_file') as $table) { + // Find the records from the parent database. + $source_query = $original_connection + ->select($table, array(), array('fetch' => PDO::FETCH_ASSOC)) + ->fields($table); + + $dest_query = $test_connection->insert($table); + + $first = TRUE; + foreach ($source_query->execute() as $row) { + if ($first) { + $dest_query->fields(array_keys($row)); + $first = FALSE; + } + // Insert the records into the child database. + $dest_query->values($row); + } + + $dest_query->execute(); + } + } + + /** + * Reset all data structures after having enabled new modules. + * + * This method is called by DrupalWebTestCase::setUp() after enabling + * the requested modules. It must be called again when additional modules + * are enabled later. + */ + protected function resetAll() { + // Reset all static variables. + drupal_static_reset(); + // Reset the list of enabled modules. + module_list(TRUE); + + // Reset cached schema for new database prefix. This must be done before + // drupal_flush_all_caches() so rebuilds can make use of the schema of + // modules enabled on the cURL side. + drupal_get_schema(NULL, TRUE); + + // Perform rebuilds and flush remaining caches. + drupal_flush_all_caches(); + + // Reload global $conf array and permissions. + $this->refreshVariables(); + $this->checkPermissions(array(), TRUE); + } + + /** + * Refresh the in-memory set of variables. Useful after a page request is made + * that changes a variable in a different thread. + * + * In other words calling a settings page with $this->drupalPost() with a changed + * value would update a variable to reflect that change, but in the thread that + * made the call (thread running the test) the changed variable would not be + * picked up. + * + * This method clears the variables cache and loads a fresh copy from the database + * to ensure that the most up-to-date set of variables is loaded. + */ + protected function refreshVariables() { + global $conf; + cache_clear_all('variables', 'cache_bootstrap'); + $conf = variable_initialize(); + } + + /** + * Delete created files and temporary files directory, delete the tables created by setUp(), + * and reset the database prefix. + */ + protected function tearDown() { + global $user, $language, $language_url, $theme, $theme_key, $theme_path; + + // In case a fatal error occurred that was not in the test process read the + // log to pick up any fatal errors. + simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE); + + $emailCount = count(variable_get('drupal_test_email_collector', array())); + if ($emailCount) { + $message = format_plural($emailCount, '1 e-mail was sent during this test.', '@count e-mails were sent during this test.'); + $this->pass($message, t('E-mail')); + } + + // Delete temporary files directory. + file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10)); + + // Remove all prefixed tables. + $tables = db_find_tables_d8('%'); + if (empty($tables)) { + $this->fail('Failed to find test tables to drop.'); + } + foreach ($tables as $table) { + if (db_drop_table($table)) { + unset($tables[$table]); + } + } + if (!empty($tables)) { + $this->fail('Failed to drop all prefixed tables.'); + } + + // In PHP 8 some tests encounter problems when shutdown code tries to + // access the database connection after it's been explicitly closed, for + // example the destructor of DrupalCacheArray. We avoid this by not fully + // destroying the test database connection. + $close = \PHP_VERSION_ID < 80000; + + // Get back to the original connection. + Database::removeConnection('default', $close); + Database::renameConnection('simpletest_original_default', 'default'); + + // Restore original shutdown callbacks array to prevent original + // environment of calling handlers from test run. + $callbacks = &drupal_register_shutdown_function(); + $callbacks = $this->originalShutdownCallbacks; + + // Return the user to the original one. + $user = $this->originalUser; + drupal_save_session(TRUE); + + // Ensure that internal logged in variable and cURL options are reset. + $this->loggedInUser = FALSE; + $this->additionalCurlOptions = array(); + + // Reload module list and implementations to ensure that test module hooks + // aren't called after tests. + module_list(TRUE); + module_implements('', FALSE, TRUE); + + // Reset the Field API. + field_cache_clear(); + + // Rebuild caches. + $this->refreshVariables(); + + // Reset public files directory. + $GLOBALS['conf']['file_public_path'] = $this->originalFileDirectory; + + // Reset language. + $language = $this->originalLanguage; + $language_url = $this->originalLanguageUrl; + if ($this->originalLanguageDefault) { + $GLOBALS['conf']['language_default'] = $this->originalLanguageDefault; + } + + // Reset theme. + $theme = $this->originalTheme; + $theme_key = $this->originalThemeKey; + $theme_path = $this->originalThemePath; + + // Close the CURL handler and reset the cookies array so test classes + // containing multiple tests are not polluted. + $this->curlClose(); + $this->cookies = array(); + } + + /** + * Initializes the cURL connection. + * + * If the simpletest_httpauth_credentials variable is set, this function will + * add HTTP authentication headers. This is necessary for testing sites that + * are protected by login credentials from public access. + * See the description of $curl_options for other options. + */ + protected function curlInitialize() { + global $base_url; + + if (!isset($this->curlHandle)) { + $this->curlHandle = curl_init(); + + // Some versions/configurations of cURL break on a NULL cookie jar, so + // supply a real file. + if (empty($this->cookieFile)) { + $this->cookieFile = $this->public_files_directory . '/cookie.jar'; + } + + $curl_options = array( + CURLOPT_COOKIEJAR => $this->cookieFile, + CURLOPT_URL => $base_url, + CURLOPT_FOLLOWLOCATION => FALSE, + CURLOPT_RETURNTRANSFER => TRUE, + CURLOPT_SSL_VERIFYPEER => FALSE, // Required to make the tests run on HTTPS. + CURLOPT_SSL_VERIFYHOST => FALSE, // Required to make the tests run on HTTPS. + CURLOPT_HEADERFUNCTION => array(&$this, 'curlHeaderCallback'), + CURLOPT_USERAGENT => $this->databasePrefix, + ); + if (isset($this->httpauth_credentials)) { + $curl_options[CURLOPT_HTTPAUTH] = $this->httpauth_method; + $curl_options[CURLOPT_USERPWD] = $this->httpauth_credentials; + } + // curl_setopt_array() returns FALSE if any of the specified options + // cannot be set, and stops processing any further options. + $result = curl_setopt_array($this->curlHandle, $this->additionalCurlOptions + $curl_options); + if (!$result) { + throw new Exception('One or more cURL options could not be set.'); + } + + // By default, the child session name should be the same as the parent. + $this->session_name = session_name(); + } + // We set the user agent header on each request so as to use the current + // time and a new uniqid. + if (preg_match('/simpletest\d+/', $this->databasePrefix, $matches)) { + curl_setopt($this->curlHandle, CURLOPT_USERAGENT, drupal_generate_test_ua($matches[0])); + } + } + + /** + * Initializes and executes a cURL request. + * + * @param $curl_options + * An associative array of cURL options to set, where the keys are constants + * defined by the cURL library. For a list of valid options, see + * http://www.php.net/manual/function.curl-setopt.php + * @param $redirect + * FALSE if this is an initial request, TRUE if this request is the result + * of a redirect. + * + * @return + * The content returned from the call to curl_exec(). + * + * @see curlInitialize() + */ + protected function curlExec($curl_options, $redirect = FALSE) { + $this->curlInitialize(); + + if (!empty($curl_options[CURLOPT_URL])) { + // Forward XDebug activation if present. + if (isset($_COOKIE['XDEBUG_SESSION'])) { + $options = drupal_parse_url($curl_options[CURLOPT_URL]); + $options += array('query' => array()); + $options['query'] += array('XDEBUG_SESSION_START' => $_COOKIE['XDEBUG_SESSION']); + $curl_options[CURLOPT_URL] = url($options['path'], $options); + } + + // cURL incorrectly handles URLs with a fragment by including the + // fragment in the request to the server, causing some web servers + // to reject the request citing "400 - Bad Request". To prevent + // this, we strip the fragment from the request. + // TODO: Remove this for Drupal 8, since fixed in curl 7.20.0. + if (strpos($curl_options[CURLOPT_URL], '#')) { + $original_url = $curl_options[CURLOPT_URL]; + $curl_options[CURLOPT_URL] = strtok($curl_options[CURLOPT_URL], '#'); + } + } + + $url = empty($curl_options[CURLOPT_URL]) ? curl_getinfo($this->curlHandle, CURLINFO_EFFECTIVE_URL) : $curl_options[CURLOPT_URL]; + + if (!empty($curl_options[CURLOPT_POST])) { + // This is a fix for the Curl library to prevent Expect: 100-continue + // headers in POST requests, that may cause unexpected HTTP response + // codes from some webservers (like lighttpd that returns a 417 error + // code). It is done by setting an empty "Expect" header field that is + // not overwritten by Curl. + $curl_options[CURLOPT_HTTPHEADER][] = 'Expect:'; + } + curl_setopt_array($this->curlHandle, $this->additionalCurlOptions + $curl_options); + + if (!$redirect) { + // Reset headers, the session ID and the redirect counter. + $this->session_id = NULL; + $this->headers = array(); + $this->redirect_count = 0; + } + + $content = curl_exec($this->curlHandle); + $status = curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE); + + // cURL incorrectly handles URLs with fragments, so instead of + // letting cURL handle redirects we take of them ourselves to + // to prevent fragments being sent to the web server as part + // of the request. + // TODO: Remove this for Drupal 8, since fixed in curl 7.20.0. + if (in_array($status, array(300, 301, 302, 303, 305, 307)) && $this->redirect_count < variable_get('simpletest_maximum_redirects', 5)) { + if ($this->drupalGetHeader('location')) { + $this->redirect_count++; + $curl_options = array(); + $curl_options[CURLOPT_URL] = $this->drupalGetHeader('location'); + $curl_options[CURLOPT_HTTPGET] = TRUE; + return $this->curlExec($curl_options, TRUE); + } + } + + $this->drupalSetContent($content, isset($original_url) ? $original_url : curl_getinfo($this->curlHandle, CURLINFO_EFFECTIVE_URL)); + $message_vars = array( + '!method' => !empty($curl_options[CURLOPT_NOBODY]) ? 'HEAD' : (empty($curl_options[CURLOPT_POSTFIELDS]) ? 'GET' : 'POST'), + '@url' => isset($original_url) ? $original_url : $url, + '@status' => $status, + '!length' => format_size(strlen($this->drupalGetContent())) + ); + $message = t('!method @url returned @status (!length).', $message_vars); + $this->assertTrue($this->drupalGetContent() !== FALSE, $message, t('Browser')); + return $this->drupalGetContent(); + } + + /** + * Reads headers and registers errors received from the tested site. + * + * @see _drupal_log_error(). + * + * @param $curlHandler + * The cURL handler. + * @param $header + * An header. + */ + protected function curlHeaderCallback($curlHandler, $header) { + // Header fields can be extended over multiple lines by preceding each + // extra line with at least one SP or HT. They should be joined on receive. + // Details are in RFC2616 section 4. + if ($header[0] == ' ' || $header[0] == "\t") { + // Normalize whitespace between chucks. + $this->headers[] = array_pop($this->headers) . ' ' . trim($header); + } + else { + $this->headers[] = $header; + } + + // Errors are being sent via X-Drupal-Assertion-* headers, + // generated by _drupal_log_error() in the exact form required + // by DrupalWebTestCase::error(). + if (preg_match('/^X-Drupal-Assertion-[0-9]+: (.*)$/', trim($header), $matches)) { + // Call DrupalWebTestCase::error() with the parameters from the header. + call_user_func_array(array(&$this, 'error'), unserialize(urldecode($matches[1]))); + } + + // Save cookies. + if (preg_match('/^Set-Cookie: ([^=]+)=(.+)/', $header, $matches)) { + $name = $matches[1]; + $parts = array_map('trim', explode(';', $matches[2])); + $value = array_shift($parts); + $this->cookies[$name] = array('value' => $value, 'secure' => in_array('secure', $parts)); + if ($name == $this->session_name) { + if ($value != 'deleted') { + $this->session_id = $value; + } + else { + $this->session_id = NULL; + } + } + } + + // This is required by cURL. + return strlen($header); + } + + /** + * Close the cURL handler and unset the handler. + */ + protected function curlClose() { + if (isset($this->curlHandle)) { + curl_close($this->curlHandle); + unset($this->curlHandle); + } + } + + /** + * Parse content returned from curlExec using DOM and SimpleXML. + * + * @return + * A SimpleXMLElement or FALSE on failure. + */ + protected function parse() { + if (!$this->elements) { + // DOM can load HTML soup. But, HTML soup can throw warnings, suppress + // them. + $htmlDom = new DOMDocument(); + @$htmlDom->loadHTML($this->drupalGetContent()); + if ($htmlDom) { + $this->pass(t('Valid HTML found on "@path"', array('@path' => $this->getUrl())), t('Browser')); + // It's much easier to work with simplexml than DOM, luckily enough + // we can just simply import our DOM tree. + $this->elements = simplexml_import_dom($htmlDom); + } + } + if (!$this->elements) { + $this->fail(t('Parsed page successfully.'), t('Browser')); + } + + return $this->elements; + } + + /** + * Retrieves a Drupal path or an absolute path. + * + * @param $path + * Drupal path or URL to load into internal browser + * @param $options + * Options to be forwarded to url(). + * @param $headers + * An array containing additional HTTP request headers, each formatted as + * "name: value". + * @return + * The retrieved HTML string, also available as $this->drupalGetContent() + */ + protected function drupalGet($path, array $options = array(), array $headers = array()) { + $options['absolute'] = TRUE; + + // We re-using a CURL connection here. If that connection still has certain + // options set, it might change the GET into a POST. Make sure we clear out + // previous options. + $out = $this->curlExec(array(CURLOPT_HTTPGET => TRUE, CURLOPT_URL => url($path, $options), CURLOPT_NOBODY => FALSE, CURLOPT_HTTPHEADER => $headers)); + $this->refreshVariables(); // Ensure that any changes to variables in the other thread are picked up. + + // Replace original page output with new output from redirected page(s). + if ($new = $this->checkForMetaRefresh()) { + $out = $new; + } + $this->verbose('GET request to: ' . $path . + '
    Ending URL: ' . $this->getUrl() . + '
    ' . $out); + return $out; + } + + /** + * Retrieve a Drupal path or an absolute path and JSON decode the result. + */ + protected function drupalGetAJAX($path, array $options = array(), array $headers = array()) { + return drupal_json_decode($this->drupalGet($path, $options, $headers)); + } + + /** + * Execute a POST request on a Drupal page. + * It will be done as usual POST request with SimpleBrowser. + * + * @param $path + * Location of the post form. Either a Drupal path or an absolute path or + * NULL to post to the current page. For multi-stage forms you can set the + * path to NULL and have it post to the last received page. Example: + * + * @code + * // First step in form. + * $edit = array(...); + * $this->drupalPost('some_url', $edit, t('Save')); + * + * // Second step in form. + * $edit = array(...); + * $this->drupalPost(NULL, $edit, t('Save')); + * @endcode + * @param $edit + * Field data in an associative array. Changes the current input fields + * (where possible) to the values indicated. A checkbox can be set to + * TRUE to be checked and FALSE to be unchecked. Note that when a form + * contains file upload fields, other fields cannot start with the '@' + * character. + * + * Multiple select fields can be set using name[] and setting each of the + * possible values. Example: + * @code + * $edit = array(); + * $edit['name[]'] = array('value1', 'value2'); + * @endcode + * @param $submit + * Value of the submit button whose click is to be emulated. For example, + * t('Save'). The processing of the request depends on this value. For + * example, a form may have one button with the value t('Save') and another + * button with the value t('Delete'), and execute different code depending + * on which one is clicked. + * + * This function can also be called to emulate an Ajax submission. In this + * case, this value needs to be an array with the following keys: + * - path: A path to submit the form values to for Ajax-specific processing, + * which is likely different than the $path parameter used for retrieving + * the initial form. Defaults to 'system/ajax'. + * - triggering_element: If the value for the 'path' key is 'system/ajax' or + * another generic Ajax processing path, this needs to be set to the name + * of the element. If the name doesn't identify the element uniquely, then + * this should instead be an array with a single key/value pair, + * corresponding to the element name and value. The callback for the + * generic Ajax processing path uses this to find the #ajax information + * for the element, including which specific callback to use for + * processing the request. + * + * This can also be set to NULL in order to emulate an Internet Explorer + * submission of a form with a single text field, and pressing ENTER in that + * textfield: under these conditions, no button information is added to the + * POST data. + * @param $options + * Options to be forwarded to url(). + * @param $headers + * An array containing additional HTTP request headers, each formatted as + * "name: value". + * @param $form_html_id + * (optional) HTML ID of the form to be submitted. On some pages + * there are many identical forms, so just using the value of the submit + * button is not enough. For example: 'trigger-node-presave-assign-form'. + * Note that this is not the Drupal $form_id, but rather the HTML ID of the + * form, which is typically the same thing but with hyphens replacing the + * underscores. + * @param $extra_post + * (optional) A string of additional data to append to the POST submission. + * This can be used to add POST data for which there are no HTML fields, as + * is done by drupalPostAJAX(). This string is literally appended to the + * POST data, so it must already be urlencoded and contain a leading "&" + * (e.g., "&extra_var1=hello+world&extra_var2=you%26me"). + */ + protected function drupalPost($path, $edit, $submit, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) { + $submit_matches = FALSE; + $ajax = is_array($submit); + if (isset($path)) { + $this->drupalGet($path, $options); + } + if ($this->parse()) { + $edit_save = $edit; + // Let's iterate over all the forms. + $xpath = "//form"; + if (!empty($form_html_id)) { + $xpath .= "[@id='" . $form_html_id . "']"; + } + $forms = $this->xpath($xpath); + foreach ($forms as $form) { + // We try to set the fields of this form as specified in $edit. + $edit = $edit_save; + $post = array(); + $upload = array(); + $submit_matches = $this->handleForm($post, $edit, $upload, $ajax ? NULL : $submit, $form); + $action = isset($form['action']) ? $this->getAbsoluteUrl((string) $form['action']) : $this->getUrl(); + if ($ajax) { + $action = $this->getAbsoluteUrl(!empty($submit['path']) ? $submit['path'] : 'system/ajax'); + // Ajax callbacks verify the triggering element if necessary, so while + // we may eventually want extra code that verifies it in the + // handleForm() function, it's not currently a requirement. + $submit_matches = TRUE; + } + + // We post only if we managed to handle every field in edit and the + // submit button matches. + if (!$edit && ($submit_matches || !isset($submit))) { + $post_array = $post; + if ($upload) { + // TODO: cURL handles file uploads for us, but the implementation + // is broken. This is a less than elegant workaround. Alternatives + // are being explored at #253506. + foreach ($upload as $key => $file) { + $file = drupal_realpath($file); + if ($file && is_file($file)) { + // Use the new CurlFile class for file uploads when using PHP + // 5.5 or higher. + if (class_exists('CurlFile')) { + $post[$key] = curl_file_create($file); + } + else { + $post[$key] = '@' . $file; + } + } + } + } + else { + foreach ($post as $key => $value) { + // Encode according to application/x-www-form-urlencoded + // Both names and values needs to be urlencoded, according to + // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 + $post[$key] = urlencode($key) . '=' . urlencode($value); + } + $post = implode('&', $post) . $extra_post; + } + $out = $this->curlExec(array(CURLOPT_URL => $action, CURLOPT_POST => TRUE, CURLOPT_POSTFIELDS => $post, CURLOPT_HTTPHEADER => $headers)); + // Ensure that any changes to variables in the other thread are picked up. + $this->refreshVariables(); + + // Replace original page output with new output from redirected page(s). + if ($new = $this->checkForMetaRefresh()) { + $out = $new; + } + $this->verbose('POST request to: ' . $path . + '
    Ending URL: ' . $this->getUrl() . + '
    Fields: ' . highlight_string('' . $out); + return $out; + } + } + // We have not found a form which contained all fields of $edit. + foreach ($edit as $name => $value) { + $this->fail(t('Failed to set field @name to @value', array('@name' => $name, '@value' => $value))); + } + if (!$ajax && isset($submit)) { + $this->assertTrue($submit_matches, t('Found the @submit button', array('@submit' => $submit))); + } + $this->fail(t('Found the requested form fields at @path', array('@path' => $path))); + } + } + + /** + * Execute an Ajax submission. + * + * This executes a POST as ajax.js does. It uses the returned JSON data, an + * array of commands, to update $this->content using equivalent DOM + * manipulation as is used by ajax.js. It also returns the array of commands. + * + * @param $path + * Location of the form containing the Ajax enabled element to test. Can be + * either a Drupal path or an absolute path or NULL to use the current page. + * @param $edit + * Field data in an associative array. Changes the current input fields + * (where possible) to the values indicated. + * @param $triggering_element + * The name of the form element that is responsible for triggering the Ajax + * functionality to test. May be a string or, if the triggering element is + * a button, an associative array where the key is the name of the button + * and the value is the button label. i.e.) array('op' => t('Refresh')). + * @param $ajax_path + * (optional) Override the path set by the Ajax settings of the triggering + * element. In the absence of both the triggering element's Ajax path and + * $ajax_path 'system/ajax' will be used. + * @param $options + * (optional) Options to be forwarded to url(). + * @param $headers + * (optional) An array containing additional HTTP request headers, each + * formatted as "name: value". Forwarded to drupalPost(). + * @param $form_html_id + * (optional) HTML ID of the form to be submitted, use when there is more + * than one identical form on the same page and the value of the triggering + * element is not enough to identify the form. Note this is not the Drupal + * ID of the form but rather the HTML ID of the form. + * @param $ajax_settings + * (optional) An array of Ajax settings which if specified will be used in + * place of the Ajax settings of the triggering element. + * + * @return + * An array of Ajax commands. + * + * @see drupalPost() + * @see ajax.js + */ + protected function drupalPostAJAX($path, $edit, $triggering_element, $ajax_path = NULL, array $options = array(), array $headers = array(), $form_html_id = NULL, $ajax_settings = NULL) { + // Get the content of the initial page prior to calling drupalPost(), since + // drupalPost() replaces $this->content. + if (isset($path)) { + $this->drupalGet($path, $options); + } + $content = $this->content; + $drupal_settings = $this->drupalSettings; + + // Get the Ajax settings bound to the triggering element. + if (!isset($ajax_settings)) { + if (is_array($triggering_element)) { + $xpath = '//*[@name="' . key($triggering_element) . '" and @value="' . current($triggering_element) . '"]'; + } + else { + $xpath = '//*[@name="' . $triggering_element . '"]'; + } + if (isset($form_html_id)) { + $xpath = '//form[@id="' . $form_html_id . '"]' . $xpath; + } + $element = $this->xpath($xpath); + $element_id = (string) $element[0]['id']; + $ajax_settings = $drupal_settings['ajax'][$element_id]; + } + + // Add extra information to the POST data as ajax.js does. + $extra_post = ''; + if (isset($ajax_settings['submit'])) { + foreach ($ajax_settings['submit'] as $key => $value) { + $extra_post .= '&' . urlencode($key) . '=' . urlencode($value); + } + } + foreach ($this->xpath('//*[@id]') as $element) { + $id = (string) $element['id']; + $extra_post .= '&' . urlencode('ajax_html_ids[]') . '=' . urlencode($id); + } + if (isset($drupal_settings['ajaxPageState'])) { + $extra_post .= '&' . urlencode('ajax_page_state[theme]') . '=' . urlencode($drupal_settings['ajaxPageState']['theme']); + $extra_post .= '&' . urlencode('ajax_page_state[theme_token]') . '=' . urlencode($drupal_settings['ajaxPageState']['theme_token']); + foreach ($drupal_settings['ajaxPageState']['css'] as $key => $value) { + $extra_post .= '&' . urlencode("ajax_page_state[css][$key]") . '=1'; + } + foreach ($drupal_settings['ajaxPageState']['js'] as $key => $value) { + $extra_post .= '&' . urlencode("ajax_page_state[js][$key]") . '=1'; + } + } + + // Unless a particular path is specified, use the one specified by the + // Ajax settings, or else 'system/ajax'. + if (!isset($ajax_path)) { + $ajax_path = isset($ajax_settings['url']) ? $ajax_settings['url'] : 'system/ajax'; + } + + // Submit the POST request. + $return = drupal_json_decode($this->drupalPost(NULL, $edit, array('path' => $ajax_path, 'triggering_element' => $triggering_element), $options, $headers, $form_html_id, $extra_post)); + $this->assertIdentical($this->drupalGetHeader('X-Drupal-Ajax-Token'), '1', 'Ajax response header found.'); + + // Change the page content by applying the returned commands. + if (!empty($ajax_settings) && !empty($return)) { + // ajax.js applies some defaults to the settings object, so do the same + // for what's used by this function. + $ajax_settings += array( + 'method' => 'replaceWith', + ); + // DOM can load HTML soup. But, HTML soup can throw warnings, suppress + // them. + $dom = new DOMDocument(); + @$dom->loadHTML($content); + // XPath allows for finding wrapper nodes better than DOM does. + $xpath = new DOMXPath($dom); + foreach ($return as $command) { + switch ($command['command']) { + case 'settings': + $drupal_settings = drupal_array_merge_deep($drupal_settings, $command['settings']); + break; + + case 'insert': + $wrapperNode = NULL; + // When a command doesn't specify a selector, use the + // #ajax['wrapper'] which is always an HTML ID. + if (!isset($command['selector'])) { + $wrapperNode = $xpath->query('//*[@id="' . $ajax_settings['wrapper'] . '"]')->item(0); + } + // @todo Ajax commands can target any jQuery selector, but these are + // hard to fully emulate with XPath. For now, just handle 'head' + // and 'body', since these are used by ajax_render(). + elseif (in_array($command['selector'], array('head', 'body'))) { + $wrapperNode = $xpath->query('//' . $command['selector'])->item(0); + } + if ($wrapperNode) { + // ajax.js adds an enclosing DIV to work around a Safari bug. + $newDom = new DOMDocument(); + // DOM can load HTML soup. But, HTML soup can throw warnings, + // suppress them. + $newDom->loadHTML('
    ' . $command['data'] . '
    '); + // Suppress warnings thrown when duplicate HTML IDs are + // encountered. This probably means we are replacing an element + // with the same ID. + $newNode = @$dom->importNode($newDom->documentElement->firstChild->firstChild, TRUE); + $method = isset($command['method']) ? $command['method'] : $ajax_settings['method']; + // The "method" is a jQuery DOM manipulation function. Emulate + // each one using PHP's DOMNode API. + switch ($method) { + case 'replaceWith': + $wrapperNode->parentNode->replaceChild($newNode, $wrapperNode); + break; + case 'append': + $wrapperNode->appendChild($newNode); + break; + case 'prepend': + // If no firstChild, insertBefore() falls back to + // appendChild(). + $wrapperNode->insertBefore($newNode, $wrapperNode->firstChild); + break; + case 'before': + $wrapperNode->parentNode->insertBefore($newNode, $wrapperNode); + break; + case 'after': + // If no nextSibling, insertBefore() falls back to + // appendChild(). + $wrapperNode->parentNode->insertBefore($newNode, $wrapperNode->nextSibling); + break; + case 'html': + foreach ($wrapperNode->childNodes as $childNode) { + $wrapperNode->removeChild($childNode); + } + $wrapperNode->appendChild($newNode); + break; + } + } + break; + + case 'updateBuildId': + $buildId = $xpath->query('//input[@name="form_build_id" and @value="' . $command['old'] . '"]')->item(0); + if ($buildId) { + $buildId->setAttribute('value', $command['new']); + } + break; + + // @todo Add suitable implementations for these commands in order to + // have full test coverage of what ajax.js can do. + case 'remove': + break; + case 'changed': + break; + case 'css': + break; + case 'data': + break; + case 'restripe': + break; + case 'add_css': + break; + } + } + $content = $dom->saveHTML(); + } + $this->drupalSetContent($content); + $this->drupalSetSettings($drupal_settings); + + $verbose = 'AJAX POST request to: ' . $path; + $verbose .= '
    AJAX callback path: ' . $ajax_path; + $verbose .= '
    Ending URL: ' . $this->getUrl(); + $verbose .= '
    ' . $this->content; + + $this->verbose($verbose); + + return $return; + } + + /** + * Runs cron in the Drupal installed by Simpletest. + */ + protected function cronRun() { + $this->drupalGet($GLOBALS['base_url'] . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => variable_get('cron_key', 'drupal')))); + } + + /** + * Check for meta refresh tag and if found call drupalGet() recursively. This + * function looks for the http-equiv attribute to be set to "Refresh" + * and is case-sensitive. + * + * @return + * Either the new page content or FALSE. + */ + protected function checkForMetaRefresh() { + if (strpos($this->drupalGetContent(), 'parse()) { + $refresh = $this->xpath('//meta[@http-equiv="Refresh"]'); + if (!empty($refresh)) { + // Parse the content attribute of the meta tag for the format: + // "[delay]: URL=[page_to_redirect_to]". + if (preg_match('/\d+;\s*URL=(?P.*)/i', $refresh[0]['content'], $match)) { + return $this->drupalGet($this->getAbsoluteUrl(decode_entities($match['url']))); + } + } + } + return FALSE; + } + + /** + * Retrieves only the headers for a Drupal path or an absolute path. + * + * @param $path + * Drupal path or URL to load into internal browser + * @param $options + * Options to be forwarded to url(). + * @param $headers + * An array containing additional HTTP request headers, each formatted as + * "name: value". + * @return + * The retrieved headers, also available as $this->drupalGetContent() + */ + protected function drupalHead($path, array $options = array(), array $headers = array()) { + $options['absolute'] = TRUE; + $out = $this->curlExec(array(CURLOPT_NOBODY => TRUE, CURLOPT_URL => url($path, $options), CURLOPT_HTTPHEADER => $headers)); + $this->refreshVariables(); // Ensure that any changes to variables in the other thread are picked up. + return $out; + } + + /** + * Handle form input related to drupalPost(). Ensure that the specified fields + * exist and attempt to create POST data in the correct manner for the particular + * field type. + * + * @param $post + * Reference to array of post values. + * @param $edit + * Reference to array of edit values to be checked against the form. + * @param $submit + * Form submit button value. + * @param $form + * Array of form elements. + * @return + * Submit value matches a valid submit input in the form. + */ + protected function handleForm(&$post, &$edit, &$upload, $submit, $form) { + // Retrieve the form elements. + $elements = $form->xpath('.//input[not(@disabled)]|.//textarea[not(@disabled)]|.//select[not(@disabled)]'); + $submit_matches = FALSE; + foreach ($elements as $element) { + // SimpleXML objects need string casting all the time. + $name = (string) $element['name']; + // This can either be the type of or the name of the tag itself + // for