Drupal 维护
Drupal 组态设置
drush config-getChoose a configuration: [0 ] Cancel [1 ] antibot.settings [2 ] automated_cron.settings [3 ] block.block.bannerspace ...
drush config:get system.sitelangcode: en uuid: d01d9a08-4a68-87bb-a98f-f21552ec5f2e name: 'Vicsys Systems' mail: slogan: '' page: 403: '' 404: '' front: /home.html ...
uuid,在组态的结构中主键为 uuid。drush config:get system.site uuidpage 内的 front,主键值为 page.front。drush config:get system.site page.frontdrush config-set system.site name 'Vicsys Systems' -y修改 Performance 中的 Aggregate CSS files、Aggregate JavaScript files (<Site>/zh-hant/admin/config/development/performance)。
drush config-get system.performancecache:
  page:
    max_age: 600
    use_internal: false
css:
  preprocess: false
  gzip: false
fast_404:
  ...
js:
  preprocess: false
  gzip: false
...
css.preprocess、js.preprocess,另外设置值为 Boolean,需以 0、1 来设置。drush config:set system.performance css.preprocess 1 -y
drush config:set system.performance js.preprocess 1 -yDrupal 组态导出及导入
drush config-export (cex),config-export 可用缩写 cex。mkdir /var/www/files/config/all -p
drush config-export --destination=/var/www/files/config/all导入时可能只需要其中一些配置,而 drush config:import (cim) 会导入所有配置,这不是我们想要的,导入时采用 --partial 参数可允许从来源目录中导入部分配置。
假设要导入站台名称,那么搜索站台名称,得到文件名:/var/www/files/config/all/system.site.yml。
system.site.yml)mkdir /var/www/files/config/site -p cp /var/www/files/config/all/system.site.yml /var/www/files/config/site/system.site.yml
drush config:import --partial --source=/var/www/files/config/site --preview=diff (1)| 1 | --preview=diff可得知组态文件跟站台配置有那些不同。 | 
导入单一文件时,可运行 Drupal console (drupal) 的 config:import:single (cis) 来导入,其组态是由导入文件名来决定;如 system.site.yml 是导入至组态 system.site。
drupal config:import:single --directory=/var/www/files/config/all --file=system.site.yml (1) drupal cis --file=/var/www/files/config/all/system.site.yml (1)
| 1 | 上面两个指令是相同的。 Drupal 9 会出现下列错误,不过文件会正确导入。 ArgumentCountError: Too few arguments to function Drupal\Core\Config\ConfigImporter::__construct(), 9 | 
以 Drupal console 导出单一配置时,依据命令中的参数说明,实测运作方式不如预期!
--directory 无作用,不指定参数时为询问。
drupal config:export:single -h
Usage:
 config:export:single [options]
 ces
Options:
     --name[=NAME]            Configuration name. (multiple values allowed)
     --directory[=DIRECTORY]  Define the export directory to save the configuration output.
     --module[=MODULE]        The Module name.
     --include-dependencies   Export dependencies of the configuration as well.
     --optional               Export config as an optional YAML configuration in your module
     --remove-uuid            If set, the configuration will be exported without uuid key.
     --remove-config-hash     If set, the configuration will be exported without the default site hash key.
system.performance,把全部的参数填入 (除了 --include-dependencies)drupal config:export:single --name=system.performance --directory=dummy --module=system --optional --remove-uuid --remove-config-hash找不到有 dummy 的文件夹,--directory 并无作用。
--optional 会固定输出至 core/modules/system/config/optional。Configuration(s) exported successfully - core/modules/system/config/optional/system.performance.yml
--optional 则会讯问:drupal config:export:single --name=system.performance --module=system --remove-uuid --remove-config-hashExport config in module as an optional configuration (yes/no) [yes]: > no Configuration(s) exported successfully - core/modules/system/config/install/system.performance.yml (1)
| 1 | 无 optional则输出至core/modules/system/config/install | 
上述的 system.performance.yml 内容都是 YAML 格式,跟 drush config-export 导出的文件大致相同,文件内容缺少 default_config_hash 及 uuid。
default_config_hash 还是可以正确导入:drupal config:import:single --file=core/modules/system/config/optional/system.performance.yml[OK] Configuration(s) "system.performance", has been imported successfully.
drush config-get system.performance
cache:
  page:
    max_age: 600
    use_internal: false
css:
  preprocess: false
  gzip: false
...
image.settings 导出,其中的 --module=system 不需要更改。printf '%s\n' no no | drupal config:export:single --name=image.settings --module=system --optionalDo you want to remove the uuid from this export? (yes/no) [yes]: > Do you want to remove the default site hash from this export? (yes/no) [yes]: > Configuration(s) exported successfully - core/modules/system/config/optional/image.settings.yml
default_config_hash 的 image.settings.yml 拷贝成 system.performance.yml,再导入,会发生什么?cp core/modules/system/config/optional/image.settings.yml system.performance.yml
drupal config:import:single --file=system.performance.yml[OK] Configuration(s) "system.performance", has been imported successfully.
drush config-get system.performance _core: default_config_hash: k-yDFHbqNfpe-Srg4sdCSqaosCl2D8uwyEY5esF8gEw preview_image: core/modules/image/sample.png allow_insecure_derivatives: false suppress_itok_output: false
结果是 system.performance 是依据 system.performance.yml 文件内容来变更,并不依据 default_config_hash。
system.site 的组态比较特殊,system.site.yml 文件内必须要 uuid,而且必须跟网站的 uuid 相同。否则会出现下列错误:
[ERROR] An error occurred while trying to write the config file: "commands.config.import.messages.import-fail
         Site UUID in source storage does not match the target storage."
解决方式是把 UUID 调整一致才能导入。
可将组态档中存在的 uuid 覆盖当前站台的 uuid。
drush config-get system.site uuid
drush config-set system.site uuid 'd01d9a08-4a68-87bb-a98f-f21552ec5f2e' -y
Configuration Split
导出模块组态或翻译,采用 Configuration Split 
的原因在于时能选取某些模块或项目,不像 drush config-export 只能全部导出。
modi config_split- <site>/admin/config/development/configuration/config-split
- Configuration Split setting
- 
+Add Configuration Split setting 
- Add configuration split setting
- 
- Label
- 
csbak 
- Folder
- 
/var/www/files/config/split 
- COMPLETE SPLIT
- 
选取要备份的模块。 (采用这个比较容易理解) 
- Configuration items
- 
选取要备份的模块项目。 
 
# 找出命令是什么
drush | grep config-split
# config-split:
#  config-split:export (csex) Export only split configuration to a directory.
#  config-split:import (csim) Import only config from a split.
mkdir /var/www/files/config/split -p
# 导出
drush csex csbak -y
# 导入
drush csim csbak -y建置测试环境
- 建置测试(备份)环境问题分析与解决
- 
- 
建置环境需要测试,Drupal 安装组件或更新时有时会出状况。 
 drupal 在测试前,先clone-sitefrom drupal to newsite,有问题回复clone-sitefrom newsite to drupal。
- 
删除 newsite 则运行 drop-sitenewsite。
- 
Apache alias 命名 
 测试站台命名:try1 ~ try9
 范例站台命名:demo1 ~ demo9
 备份站台命名:bak1 ~ bak9
 Apache trySites.confAlias /try1 /var/www/try1 ... Alias /try9 /var/www/try9 Alias /demo1 /var/www/bak1 ... Alias /demo9 /var/www/bak9 Alias /bak1 /var/www/demo1 ... Alias /bak9 /var/www/demo9 # 开放 www <Directory /var/www> Options FollowSymLinks AllowOverride All Require ip 192.168.1.1/255.255.255.0 </Directory>
 
- 
拷贝站台
clone-site [OPTION] [<remote>:]<source> [<destination>] options: -y Never prompt
clone-site drupal try1
clone-site 192.168.1.1:druapl
- MySQL 不可小于远程站台的版本,否则会出现下列错误
- 
ERROR 1118 (42000) at line 259: The size of BLOB/TEXT data inserted in one transaction is greater than 10% of redo log size. Increase the redo log size using innodb_log_file_size. 
bind "set disable-completion on"
bash -c "cat > /var/www/bin/clone-site" << "EOF2"
#!/bin/bash
usage="Usage: ${0##*/} [OPTION] [<remote>:]<source> [<destination>]
Clone site
options:
  -y		Never prompt"
Prompt=1
POSITIONAL=()
while (( "$#" )); do
  case "$1" in
    -h|--h*) echo "$usage"; exit 1;;
    -y|--y) Prompt=0; shift;;
    *)
    POSITIONAL+=("$1") # save it in an array for later
    shift;;
  esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters
if [[ $1 = *:* ]]; then
  remoteHost=${1%%:*}
  remoteHost=${remoteHost,,}
  sourceSite=${1##*:}
  if [[ -z $sourceSite ]]; then
    echo "$usage"
    exit 1
  fi
  if [[ -z $2 ]]; then
    localSite=$sourceSite
  else
    localSite=$2
  fi
  sourceLocation=$remoteHost:$sourceSite
  # Will continue when resolveip fails
  if [[ $remoteHost = ${HOSTNAME,,} ]] || [[ $HOSTNAME = $(resolveip -s $remoteHost) ]]; then
    echo "Error: The remote host is the current host!" >&2
    exit 1
  fi
else
  if [[ -z $1 ]] || [[ -z $2 ]]; then
    echo "$usage"
    exit 1
  fi
  if [[ $1 = $2 ]]; then
    echo "Error: The source destination is the same!" >&2
    exit 1
  fi
  sourceSite=$1
  localSite=$2
  sourceLocation=$sourceSite
fi
if [[ $Prompt -eq 1 ]]; then
  read -p "clone-site $sourceLocation to $localSite? [Y/n] " yn
  yn=${yn:-y}
  if [[ $yn != Y ]] && [[ $yn != y ]]; then
    exit 1
  fi
fi
if [[ -z $remoteHost ]]; then
  sudo sh -c "chown -R www-data:www-data /var/www/$localSite; chmod -Rf 774 /var/www/$localSite" 2> /dev/null
fi
# Exit immediately on error
set -e
echo rsync $sourceLocation to $localSite
if [[ -n $remoteHost ]]; then
  sudo rsync -az --info=progress2 root@$remoteHost:/var/www/$sourceSite/ \
  --delete --chmod=775 --chown=www-data:www-data /var/www/$localSite
  echo Export soruce database $sourceLocation
  ssh root@$remoteHost "mysqldump -uroot -p$mysqlpw --host=localhost --port=3306 $sourceSite" > /var/www/$localSite/_srcdb.sql
else
  sudo rsync -a --chown=www-data:www-data --chmod=775 --info=progress2 --delete /var/www/$sourceSite/ /var/www/$localSite
  echo Export soruce database $sourceLocation
  mysqldump -uroot -p$mysqlpw --host=localhost --port=3306 $sourceSite > /var/www/$localSite/_srcdb.sql
fi
NC='\033[0m' # No Color
YELLOW='\033[1;33m'
echo -e "${YELLOW}***Now you can maintain the original site.***${NC}"
echo Create database $localSite
mysql -uroot -p$mysqlpw --host=localhost --port=3306 --protocol=tcp -n<<EOF
drop database if exists $localSite;
create database $localSite;
grant all privileges on $localSite.* to '$localSite'@'localhost' identified by '$webdbpw';
use mysql;
update user set plugin='mysql_native_password' where user='$localSite';
flush privileges;
EOF
echo Import table $localSite
mysql -u$localSite -p$webdbpw --host=localhost --port=3306 $localSite < /var/www/$localSite/_srcdb.sql
echo rm /var/www/$localSite/_srcdb.sql
if [[ $sourceSite != $localSite ]]; then
  echo -e "${YELLOW}"
  if [ -f "/var/www/$localSite/sites/default/settings.php" ]; then
    echo change settings.php
    src="\n *\$databases *\[ *'default' *\] *\[ *'default' *\] *= *array *(\n *'database' *=> *'[0-9a-zA-Z]\+' *, *\n *'username' *=> *'[0-9a-zA-Z]\+' *, *"
    trs="\n\$databases\['default'\]\['default'\] = array (\n  'database' => '$localSite',\n  'username' => '$localSite',"
    sed -z -i "s/$src/$trs/" /var/www/$localSite/sites/default/settings.php
    sudo chown www-data:www-data /var/www/$localSite/sites/default/settings.php
    dbProfile=$(sed -n "/\$databases\['default'\]\['default'\] = array (/ {N;/'database' => '$localSite'/!q;N;/'username' => '$localSite'/!q;N;p}" /var/www/$localSite/sites/default/settings.php)
    if [[ -z $dbProfile ]]; then
      echo "*** Can not find database parameters! ***"
      echo $(grep "^ *\$databases\[ *'default' *\] *\[ *'default' *\] *= *array *(" -A3 /var/www/$localSite/sites/default/settings.php)
    else
      echo $dbProfile
      cd /var/www/$localSite
      drush cr
    fi
  else
    echo "*** Can not find /var/www/$localSite/sites/default/settings.php! ***"
  fi
fi
EOF2
bind "set disable-completion off"
sudo chmod +x /var/www/bin/clone-site删除站台
bind "set disable-completion on"
bash -c "cat > /var/www/bin/drop-site" << "EOF2"
#!/bin/bash
tab='	'
nl='
'
IFS=" $tab$nl"
usage="Usage: ${0##*/} [OPTION] site
Drop site
options:
  -y		Never prompt"
Prompt=1
POSITIONAL=()
while (( "$#" )); do
  case "$1" in
    -h|--h*) echo "$usage"; exit 1;;
    -y|--y) Prompt=0; shift;;
    *)
    POSITIONAL+=("$1") # save it in an array for later
    shift;;
  esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters
if [ "$1" = "" ]; then
  echo "$usage"
  exit 1
fi
if [ $Prompt -eq 1 ]; then
  read -p "drop-site $1? [Y/n] " yn
  yn=${yn:-y}
  if [ "$yn" != "Y" ] && [ "$yn" != "y" ]; then
    exit 1
  fi
fi
echo rm $1
sudo rm /var/www/$1 -r
echo Drop database $1
mysql -uroot -p$mysqlpw --host=localhost --port=3306 --protocol=tcp -N<<EOF
drop database if exists $1;
drop user if exists '$1'@'localhost';
flush privileges;
EOF
EOF2
bind "set disable-completion off"
sudo chmod +x /var/www/bin/drop-site站台重建
站台更新失败,或者站台在更新 (含内核) 模块之后,可能会发现某个模块的运作不如预期,高度怀疑是因为模块出错。
| 若模块的运作不如预期,先运行 drush cr再次检查避免误判。 | 
- 
创建新站台,安装跟原站台一样模块及版本,并激活跟原站台一样模块。 
 运行modc-site比对站台模块。注:该脚本亦可比对测试站台安装了那些新模块。
- 
测试新站台的运作是否合乎预期。 
- 
将原始数据 以 clone-db导入至新新站台。
site=try1
refsite=drupal
diff /var/www/$site /var/www/$refsite \
  -qr --exclude="*.yml" --exclude="*.po" \
  | sort > _diff.txt (1)
grep -v -E  "files/js|files/css|files/color|default/files: config|files/languages: |default/settings.php" \
  _diff.txt (2)
grep ".php" _diff.txt (3)| 1 | 先创建 _diff.txt | 
| 2 | 比对文件 (排除已知不同文件)。 | 
| 3 | 只比对 .php | 
比对站台模块
bind "set disable-completion on"
bash -c "cat > /var/www/bin/modc-site" << "EOF"
#!/bin/bash -e
tab='	'
nl='
'
IFS=" $tab$nl"
usage="Usage: ${0##*/} [OPTION] source target
Compare modules
options:
  -c, --cr   		Clear cache"
clearCache=0
POSITIONAL=()
while (( "$#" )); do
  case "$1" in
    -h|--h*) echo "$usage"; exit 1;;
    -c|--cr) clearCache=1; shift;;
    *)
    POSITIONAL+=("$1") # save it in an array for later
    shift;;
  esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters
if [ -z "$1" ] || [ -z "$2" ]; then
  echo "$usage"
  exit 1
fi
if [ "$1" = "$2" ]; then
  echo "Error: The source target is the same!" >&2
  exit 1
fi
fileName=_Module.txt
cd /var/www/$1
if [ $clearCache -eq 1 ]; then
  drush cr
fi
drush pm-list --fields display_name,version,status --type=module > $fileName
cd /var/www/$2
if [ $clearCache -eq 1 ]; then
  drush cr
fi
drush pm-list --fields display_name,version,status --type=module > $fileName
diff -y <(sed 's/\([ -]\)*/\1/g' /var/www/$1/$fileName | fold -s -w80) \
<(sed 's/\([ -]\)*/\1/g' /var/www/$2/$fileName | fold -s -w80) -W 160 | grep '[<|>]'
rm /var/www/$1/$fileName
rm /var/www/$2/$fileName
EOF
bind "set disable-completion off"
sudo chmod +x /var/www/bin/modc-site拷贝站台数据库
bind "set disable-completion on"
bash -c "cat > /var/www/bin/clone-db" << "EOF2"
#!/bin/bash -e
tab='	'
nl='
'
IFS=" $tab$nl"
usage="Usage: ${0##*/} [OPTION] source target
Clone database
options:
  -y		Never prompt"
Prompt=1
POSITIONAL=()
while (( "$#" )); do
  case "$1" in
    -h|--h*) echo "$usage"; exit 1;;
    -y|--y) Prompt=0; shift;;
    *)
    POSITIONAL+=("$1") # save it in an array for later
    shift;;
  esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters
if [ "$1" = "" ] || [ "$2" = "" ]; then
  echo "$usage"
  exit 1
fi
if [ "$1" = "$2" ]; then
  echo "Error: The source target is the same!" >&2
  exit 1
fi
if [ $Prompt -eq 1 ]; then
  read -p "clone-db $1 to $2? [Y/n] " yn
  yn=${yn:-y}
  if [ "$yn" != "Y" ] && [ "$yn" != "y" ]; then
    exit 1
  fi
fi
echo mysqldump $1
mysqldump -uroot -p$mysqlpw --host=localhost --port=3306 $1 > /var/www/$2/_srcdb.sql
echo create database $2
mysql -uroot -p$mysqlpw --host=localhost --port=3306 --protocol=tcp -n<<EOF
drop database if exists $2;
create database $2;
grant all privileges on $2.* to '$2'@'localhost' identified by '$webdbpw';
use mysql;
update user set plugin='mysql_native_password' where user='$2';
flush privileges;
EOF
echo Import table $2
mysql -u$2 -p$webdbpw --host=localhost --port=3306 $2 < /var/www/$2/_srcdb.sql
rm /var/www/$2/_srcdb.sql
cd /var/www/$2
drush cr
EOF2
bind "set disable-completion off"
sudo chmod +x /var/www/bin/clone-dbDrupal 8 升级至 Drupal 9
- 先在测试站台准备升级(重要)
- 
安装 upgrade_statusclone-site drupal try9 composer require drupal/upgrade_status (1) drush en upgrade_status drush cr1 安装 Upgrade Status 可事先检查兼容情况。但实际的站台升级又可能因为 upgrade_status造成升级失败,upgrade_status仅可用来检查兼容性,依据兼容性调整原始站台,当然若不放心可clone-site原始站台至另一站台再调整。进入 <site>/admin/reports/upgrade-status 检查一下情况。When using MariaDB, minimum version is 10.3.7 Alternatively, install the MariaDB 10.1 driver for Drupal 9 for now. MariaDB 最低需求版本为 10.3.7 可升级至 10.3.27 或者安装 MariaDB 10.1 驱动程序。 
# 先升级 composer.json
composer require drupal/core-recommended:^9.0.0 drupal/core-composer-scaffold:^9.0.0 \
drupal/core-project-message:^9.0.0 --update-with-dependencies --no-update
# ./composer.json has been updated
# If you have drupal/core-dev installed.
# composer require drupal/core-dev:^9.0.0 --dev --update-with-dependencies --no-update
composer update- 若需要安装 MariaDB 10.1 驱动程序
- 
安装驱动程序composer require drupal/mysql56在 sites/default/settings.php 加入下列$databases['default']['default']['namespace'] = 'Drupal\\Driver\\Database\\mysql';
drush updatedb (1) drush cr chwww .
| 1 | 注意如果出现红底消息 [error] * The database server version x.x.x is less than the minimum required version x.x.x. 表示有误,千万别继续运行。 | 
- Reference
- 
Upgrading from Drupal 8 to Drupal 9 (or higher) | Upgrading Drupal | Drupal guide on Drupal.org 
 Upgrading Drupal 8 to Drupal 9: The real-world experience | Touch4IT
- Drupal core 9.0.9/9.1.0 有下列错误
- 
drupal site:mode devPHP Fatal error: Uncaught Error: Call to undefined method Drupal\Core\DrupalKernel::prepareLegacyRequest() in /var/www/try9/vendor/drupal/console/src/Utils/DrupalApi.php:266 在 vendor/drupal/console/src/Utils/DrupalApi.php:266 删除该行原代码$kernel->prepareLegacyRequest($request);
Drupal patch
当发现某个模块的运作不如预期,除了 Google 之外,可进入 https://www.drupal.org/project/<Machine name>,如 ds (Display Suite) 为
https://www.drupal.org/project/ds  按下网页中的 Bug report (Issues for Display Suite | Drupal.org )。
#9 2887778-9.patch #3 ds-2887778-3-D8.patch (1)
| 1 | 有点奇怪,激活 NoScript 有 ds-2887778-3-D8.patch。 | 
Needs review Project: Display Suite Version: 8.x-4.x-dev (1)
| 1 | 在 8.x-4.x-dev版本中尚未修正,安装8.x-4.x-dev也无作用。 | 
composer show | grep drupal/ds
# drupal/ds 3.9.0mkdir -p /var/www/files/patch (1)
wget -P /var/www/files/patch "https://www.drupal.org/files/issues/2018-08-27/2887778-9.patch"| 1 | 创建 patch 目录保留 patch 文件及被 patch 的文件,当下次更新时,若不正确可试着修改。 | 
--- ds.module | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ds.module b/ds.module (1) index 8dc1de5d..0d03e4c6 100644 --- a/ds.module +++ b/ds.module ...
| 1 | 得知是 patch ds.module | 
cp modules/contrib/ds/ds.module /var/www/files/patch/ds.module.src (1)| 1 | 重要!重新 patch 时,有源文件案可随时回复或测试。 | 
git apply /var/www/files/patch/2887778-9.patch --directory modules/contrib/ds
# warning: modules/contrib/ds/ds.module has type 100755, expected 100644
# 上述有警告, 实际上已运行 patch。
# 修改模块应重建缓存
drush cr
# 回复 patch
# git apply /var/www/files/patch/2887778-9.patch --directory modules/contrib/ds --reverse# 回复 patch
git apply /var/www/files/patch/2887778-9.patch --directory modules/contrib/ds --reverse
wget -P /var/www/files/patch "https://www.drupal.org/files/issues/ds-2887778-3-D8.patch"
git apply /var/www/files/patch/ds-2887778-3-D8.patch --directory modules/contrib/ds
# error: patch fragment without header at line 5: @@ -14,13 +14,13 @@
# git 出错改用 patch
patch -i /var/www/files/patch/ds-2887778-3-D8.patch modules/contrib/ds/ds.module
# patching file modules/contrib/ds/ds.module
# patch 结果无误
drush cr
#重建缓存后测试,不正确。
# 运行 2887778-9.patch
patch -i /var/www/files/patch/2887778-9.patch modules/contrib/ds/ds.module
# patching file modules/contrib/ds/ds.module
# Hunk #1 succeeded at 635 (offset 2 lines).
drush cr
#重建缓存后测试,结果正确。
# 保留本次修正的结果
cp modules/contrib/ds/ds.module /var/www/files/patch/ds.module.patchedcp /var/www/files/patch/ds.module.src modules/contrib/ds/ds.module
git apply /var/www/files/patch/2887778-9.patch --directory modules/contrib/ds
drush cr (1)
cp /var/www/files/patch/ds.module.src modules/contrib/ds/ds.module
patch -i /var/www/files/patch/ds-2887778-3-D8.patch modules/contrib/ds/ds.module (2)
patch -i /var/www/files/patch/2887778-9.patch modules/contrib/ds/ds.module
drush cr (3)| 1 | 重建缓存后测试,确定不正确。 | 
| 2 | 再次 patch 两个文件 | 
| 3 | 重建缓存后测试,确定正确,要 patch 两个文件。 | 
Admin toolbar z-index
由于网页的 z-index 必须为 1031, 而 Druapl 的 Admin toolbar 管理接口为 502,网页会覆盖 Admin toolbar,将 Admin toolbar 的 z-index 修改该为 1032。
/* Layer the bar just above the trays and above contextual link triggers. */
.toolbar-oriented .toolbar-bar {
  z-index: 502; (1)
}/* Layer the bar just above the trays and above contextual link triggers. */
/*** MY-PATCH 502 to 1032 ***/
.toolbar-oriented .toolbar-bar {
  z-index: 1032;
}# 先备份源文件案
cp core/modules/toolbar/css/toolbar.module.css /var/www/files/patch/toolbar.module.css.src
nano core/modules/toolbar/css/toolbar.module.css
# 502 修改为  1032
# 产生 patch
diff -u /var/www/files/patch/toolbar.module.css.src \
core/modules/toolbar/css/toolbar.module.css \
> /var/www/files/patch/toolbar.module.css.patch
# 备份本次修改结果
cp core/modules/toolbar/css/toolbar.module.css /var/www/files/patch/toolbar.module.css.patched
# 下次更新时, 再次运行
patch -i /var/www/files/patch/toolbar.module.css.patch core/modules/toolbar/css/toolbar.module.css
# 检查 .z-index
grep -A 4 'Layer the bar just above the trays and above contextual link triggers' \
core/modules/toolbar/css/toolbar.module.css重导网页 (Redirect page)
401 Authorization failed 授权失败。用户输入的帐号密码未得到授权。 403 Forbidden 访问控制机制拒绝用户的请求,不可读取该文件。 404 File not found 被要求的网页不存在于这个服务器上,找不到文件。 500 Internal Server Error 服务器内部错误;可能是网站服务器出错。 501 Not Implemented 服务器不了解数据传递的方式。 503 Service Unavailable 服务器暂停服务
禁止进入管理页面
由 Durpal 正规将 403 转成 404,网页虽然可显示成 404 的样式,但状态码还是 403,只能由 apache 重导至 php,再由 php 改变状态码为 404。
<VirtualHost *:80>
  DocumentRoot /var/www/drupal
  RewriteEngine on
  ErrorDocument 404 /html/404.php (1)
  ErrorDocument 403 /html/404.php (1)
  RewriteCond expr "%{REMOTE_ADDR} -ipmatch '192.168.1.0/24'" (2)
  RewriteRule .? - [L] (2)
  RewriteCond %{REQUEST_URI} (/[uU][sS][eE][rR]|/[aA][dD][mM][iI][nN]) (3)
  RewriteRule .? - [L,R=404] (3)
  <FilesMatch "(install|update).php"> (4)
    Order deny,allow
    deny from all
    Allow from 192.168.1.0/24 (5)
  </FilesMatch>
  <Directory /var/www/drupal>
    AllowOverride None
    Include /var/www/drupal/.htaccess
  </Directory>
</VirtualHost>
| 1 | 404 及 403 网页为 /var/www/drupal/html/404.php | 
| 2 | 允许局域网路进入 user 或 admin。 | 
| 3 | 若不为局域网路则重导至 404。 | 
| 4 | 若不为局域网路,禁止进入 install.php update.php …,apache 会重导至 403,不过有语言 url 就破功了。 | 
| 5 | 若为局域网路,可进入「禁止」网页,不过,如果采用 composer drush 实际上并不需要由网页来处理。 | 
Require ip 192.168.1.1 Require ip 192.168.1.0/255.255.255.0 Require ip 192.168.1.0/24
由于 drupal 并不允许在其他路径中运行自定的 php,需修改根目录中的 .htaccess 文件。
- 解决无法在 Drupal 目录中运行自定的 php
- 
# 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$ ### MY-PATCH Allow for my_custom_directory folder to run php ### (1) RewriteCond %{REQUEST_URI} !/html/[^/]*\.php$ (1)1 加入允许 php 规则。 
# 先备份源文件案
cp .htaccess /var/www/files/patch/.htaccess.src
nano .htaccess
# 加入允许 php 规则。
# 产生 patch
diff -u /var/www/files/patch/.htaccess.src \
.htaccess \
> /var/www/files/patch/.htaccess.patch
# 备份本次修改结果
cp .htaccess /var/www/files/patch/.htaccess.patched
# 下次 drupal 更新时, 再次运行
patch -i /var/www/files/patch/.htaccess.patch .htaccess
# 检查结果
grep -A 5 '# custom modules or to run another PHP application in the same directory.' \
.htaccess<?php
  http_response_code(404); (1)
  $lant='en';
  $url=$_SERVER['REQUEST_URI'];
  if (preg_match('~^/zh-hant(?:/|$)~i', $url)) {
      $lang='zh-TW';
  } elseif (preg_match('~^/zh-hans(?:/|$)~i', $url)) {
      $lang='zh-CN';
  } else {
      $lang=strtok($_SERVER['HTTP_ACCEPT_LANGUAGE'], ',');
  }
  switch ($lang) {
      case 'zh-TW':
          include '404_tw.html'; (2)
          break;
      case 'zh-CN':
          include '404_cn.html';
          break;
      default:
          include '404_en.html';
  }| 1 | 一律回传 404 状态码给浏览器,跟网页一致 (403 也回传 404)。 | 
| 2 | 按 drupal.conf 中的 DocumentRoot 及 ErrorDocument 的路径,/var/www/drupal/html/404_tw.html 为正体中文的 404 网页。 | 
那么要写 404 网页?不用!只需产生网站的 404 网页即可。
wget --content-on-error <site>/zh-hant/notexists -O /var/www/drupal/html/404_tw.html wget --content-on-error <site>/zh-hans/notexists -O /var/www/drupal/html/404_cn.html wget --content-on-error <site>/en/notexists -O /var/www/drupal/html/404_en.html
维护页面
当网站在「维护」时,不进实际网站页面,其原因在于维护时会更新文件及数据库,若再进入网站页面会造成不预期的结果,可能进入 install.php 的页面,比较理想的方式是由 Apache 重导至维护页面 (不进入实际网站)。
<VirtualHost *:80>
  DocumentRoot /var/www/files/site/html (1)
  RewriteEngine on
  RewriteCond %{REQUEST_URI} !=/favicon.ico (2)
  ErrorDocument 503 /503.php (3)
  RewriteCond %{REQUEST_URI} !/503.php$ [NC] (4)
  RewriteRule .* - [R=503,L] (5)
</VirtualHost>
| 1 | 维护站台的根目录是在 /var/www/files/site/html。 | 
| 2 | 允许读取 favicon.ico。如果 url 不为 favicon.ico,进行后续规则,另一说法是如果 url 为 favicon.ico,则直接读取。 | 
| 3 | 维护页面采用 php 来实作具弹性。 | 
| 4 | 如果 url 为 503.php,则直接读取,503.php 要跟 ErrorDocument 一样,不一样会出现 Service Unavailable。 | 
| 5 | 不为 503.php 则重导 503。 | 
<?php
  $lant='en';
  $url=$_SERVER['REQUEST_URI'];
  if (preg_match('~^/zh-hant(?:/|$)~i', $url)) {
      $lang='zh-TW'; (1)
  } elseif (preg_match('~^/zh-hans(?:/|$)~i', $url)) {
      $lang='zh-CN'; (2)
  } else {
      $lang=strtok($_SERVER['HTTP_ACCEPT_LANGUAGE'], ','); (3)
  }
  switch ($lang) {
      case 'zh-TW':
          include '503_tw.html'; (4)
          break;
      case 'zh-CN':
          include '503_cn.html';
          break;
      default:
          include '503_en.html';
  }| 1 | 网址为 <site>\zh-hant 则为 zh-TW。 | 
| 2 | 网址为 <site>\zh-hans 则为 zh-CN。 | 
| 3 | 其他情况,取浏览器的语言。 | 
| 4 | 按 drupal503.conf 中的 DocumentRoot,则 /var/www/files/site/html/503_tw.html 为正体中文的 503 网页。 | 
那么要写 503 网页?不用!只需产生网站的 503 网页即可。
# 设置网站维护 drush state:set system.maintenance_mode 1 --input-format=integer drush cr wget --content-on-error <site>/zh-hant/ -O /var/www/files/site/html/503_tw.html wget --content-on-error <site>/zh-hans/ -O /var/www/files/site/html/503_cn.html wget --content-on-error <site>/en/ -O /var/www/files/site/html/503_en.html # 回复正常 drush state:set system.maintenance_mode 0 --input-format=integer drush cr
不过,由于「维护网页」并未由 Drupal 提供 css,需要将 css 改成由网页提供。
<!DOCTYPE html>
<html lang="zh-hant" dir="ltr"
  prefix="content: http://purl.org/rss/1.0/modules/content/  dc: http://purl.org/dc/terms/  foaf: http://xmlns.com/foaf/0.1/  og: http://ogp.me/ns#  rdfs: http://www.w3.org/2000/01/rdf-schema#  schema: http://schema.org/  sioc: http://rdfs.org/sioc/ns#  sioct: http://rdfs.org/sioc/types#  skos: http://www.w3.org/2004/02/skos/core#  xsd: http://www.w3.org/2001/XMLSchema# ">
<head>
  <meta charset="utf-8" />
  <meta name="MobileOptimized" content="width" />
  <meta name="HandheldFriendly" content="true" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="x-ua-compatible" content="ie=edge" />
  <link rel="shortcut icon" href="/favicon.ico" type="image/vnd.microsoft.icon" />
  <title>网站正在进行维护 | 我的网站</title>
  <style type="text/css">
    body {
      font-family: helvetica, arial, sans-serif;
      font-size: 16px;
    }
    #maintenance-page {
      margin-top: 40px;
      border: 0px;
      width: 800px;
      margin-left: auto;
      margin-right: auto;
    }
  </style>
</head>
<body>
  <div id="maintenance-page">
    <strong>
      <span style="color: #9d408d">我的网站</span>
    </strong>
    <section>
      <h1>网站正在进行维护</h1>
      我的网站 正在维护中,很快就会回来,请稍候。
    </section>
  </div>
</body>
</html>sudo a2dissite drupal sudo a2ensite drupal503 sudo service apache2 reload
sudo a2dissite drupal503 sudo a2ensite drupal sudo service apache2 reload
Xdebug
sudo apt install php-xdebug (1)
find /etc/php -iname '*xdebug.ini' | sort (2)
find /usr/lib/php -iname 'xdebug.so' (3)| 1 | 各种不同的系统及版本可参阅 Xdebug: Documentation > Installation 。 | 
| 2 | 安装后 php 各版本内有 xdebug.ini 不过,还是要自行设置,可参阅 Xdebug: Documentation > All settings 。 | 
| 3 | 有不少以日期为目录的版本,但 php 7.3 只有 /usr/lib/php/20180731/xdebug.so(版本 3.0.2) 能正常运行。 | 
sudo bash -c 'cat > /etc/php/7.3/mods-available/xdebug.ini' << 'EOF'
zend_extension=/usr/lib/php/20180731/xdebug.so
xdebug.mode=debug (1)
xdebug.discover_client_host=1 (2)
# xdebug.client_host=192.168.0.1
xdebug.client_port=9000
xdebug.start_with_request=yes
EOF| 1 | xdebug.mode=debug 激活调试,xdebug.mode=off 取消调试。 | 
| 2 | 如果 PHP / Xdebug 在同一子网中的另一台主机上运行,并且您的浏览器与 IDE 在同一主机上运行,可将 xdebug.discover_client_host 设置为 1,或者设置 xdebug.client_host=192.168.0.1 指定用户计算机 IP。 | 
cd ~ (1)
echo "<?php phpinfo();" > phpinfo.php
cat > debug.php  << 'EOF'
<?php
$myvar1 = 1;
$myvar2 = 2;
echo $myvar1.' '.$myvar2;
EOF
# sudo service nginx stop
# sudo service apache2 stop
sudo php -S 0.0.0.0:3000 (2)| 1 | 范例为家目录。 | 
| 2 | 运行 PHP 内置的 Web server。 | 
进入 http://<YourSite>:3000/phpinfo.php 网页,搜索 xdebug 如果无误会看到下列图例。


Visual Studio Code 设置 Xdebug 调试
VSCODE 先安装 PHP Debug 。
在 Windows 的「资源管理器」运行「以 Code  打开」网站文件夹,该目录即为 Samba 的分享文件夹。
VSCODE 会将该文件夹作为 工作区 (workspace)。
若没有「以 Code 打开」则先移除 VSCODE 再重新安装 (重新安装时所有的 Extensions 会保留),在安装画面会有下列选项:
- 
将「以 Code 打开」动作加入 Windows 资源管理器目录的操作功能表中 
在 VSCOD 的菜单 (menu) 按下  出现 Select Environment 选 (输入) PHP,VSCOD  会在 工作区 中创建一个子目录
.vscode 及在子目录中创建文件 launch.json。
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for XDebug",
            "type": "php",
            "request": "launch",
            "port": 9000,
            "pathMappings": { (1)
                "/home/ubuntu": "${workspaceRoot}",
              }
        },
        {
            "name": "Launch currently open script",
            "type": "php",
            "request": "launch",
            "program": "${file}",
            "cwd": "${fileDirname}",
            "port": 9000
        }
    ]
}| 1 | 加入 pathMappings 的设置,其他为原始内容,pathMappings 的格式为 主机路径对应本机路径。主机路径很明确就是 Linux 的文件路径如 /var/wwww/drupal、/home/ubuntu,而 本机路径采用变量${workspaceRoot}这表示需将调试的网站文件夹「以 Code 打开」。 | 
- 浏览网页时会在运行 PHP Web server 的 CLI 会看到下列消息
- 
Xdebug: [Step Debug] Could not connect to debugging client. Tried: <YourSite>:9000 (from REMOTE_ADDR HTTP header), localhost:9000 (fallback through xdebug.client_host/xdebug.client_port) :-( 
 这表示客户端没有启动调试。
VSCODE 运行调试,在菜单 (menu) 按下 ,
再次浏览网页 http://<YourSite>:3000/debug.php,应该就没有上述消息了,这也表示 Xdebug 已建置完成了。
如果中断点无效,可能是因为 pathMappings 没设置正确,重设置后,在 VSCODE 的菜单按下  即可,不需要去重启 PHP Web server。
由于 VSCODE 并不知道 Druapl 的 .module 也是 PHP 文件,在 VSCODE 中打开 .module 文件后按下
Ctrl+Shift+p 后输入 change language mode,再选 Configure File Association for '.module'… 最后输入 PHP。
VSCODE 会将设置档保存在 %USERPROFILE%\AppData\Roaming\Code\User\settings.json,可将 settings.json 移至工作区内的 .vscode 子目录。
停用 Xdebug
看看 Xdebug 配置了那些文件
/etc/php/7.3/apache2/conf.d/20-xdebug.ini (1) /etc/php/7.3/cli/conf.d/20-xdebug.ini (1) /etc/php/7.3/fpm/conf.d/20-xdebug.ini (1) /etc/php/7.3/mods-available/xdebug.ini
| 1 | 这些文件是是以「软链接」连接至 mods-available/xdebug.ini可以删除它,或者用phpdismod。 | 
sudo phpdismod xdebug
sudo service php7.3-fpm restartsudo netstat -tlpn
# 确定已关闭 PHP Web server 再重启 Web 服务器
# sudo service nginx start
# sudo service apache2 startsudo phpenmod xdebug (1)
sudo ln -s /etc/php/7.3/mods-available/xdebug.ini /etc/php/7.3/cli/conf.d/20-xdebug.ini (2)
sudo service php7.3-fpm restart| 1 | 激活所有 Xdebug | 
| 2 | 或者您可以只在 CLI 启动 Xdebug,则加入「软链接」。 |