Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • project/recurring_events
  • issue/recurring_events-3183502
  • issue/recurring_events-3183463
  • issue/recurring_events-3183483
  • issue/recurring_events-3190526
  • issue/recurring_events-3191715
  • issue/recurring_events-3190833
  • issue/recurring_events-3188808
  • issue/recurring_events-3180479
  • issue/recurring_events-3122823
  • issue/recurring_events-3196649
  • issue/recurring_events-3196428
  • issue/recurring_events-3196702
  • issue/recurring_events-3196704
  • issue/recurring_events-3198532
  • issue/recurring_events-3164409
  • issue/recurring_events-3206960
  • issue/recurring_events-3115678
  • issue/recurring_events-3218496
  • issue/recurring_events-3207435
  • issue/recurring_events-3219082
  • issue/recurring_events-3217367
  • issue/recurring_events-3229514
  • issue/recurring_events-3231841
  • issue/recurring_events-3238591
  • issue/recurring_events-3282502
  • issue/recurring_events-3283128
  • issue/recurring_events-3240862
  • issue/recurring_events-3247034
  • issue/recurring_events-3071679
  • issue/recurring_events-3264621
  • issue/recurring_events-3266436
  • issue/recurring_events-3268690
  • issue/recurring_events-3269555
  • issue/recurring_events-3271328
  • issue/recurring_events-3272361
  • issue/recurring_events-3163804
  • issue/recurring_events-3297681
  • issue/recurring_events-3299575
  • issue/recurring_events-3300786
  • issue/recurring_events-3302916
  • issue/recurring_events-3304286
  • issue/recurring_events-3298679
  • issue/recurring_events-3309652
  • issue/recurring_events-3310360
  • issue/recurring_events-3311843
  • issue/recurring_events-3311712
  • issue/recurring_events-3312003
  • issue/recurring_events-3312084
  • issue/recurring_events-3312242
  • issue/recurring_events-3316080
  • issue/recurring_events-3295367
  • issue/recurring_events-3196417
  • issue/recurring_events-3309859
  • issue/recurring_events-3318590
  • issue/recurring_events-3244975
  • issue/recurring_events-3318998
  • issue/recurring_events-3321269
  • issue/recurring_events-3320512
  • issue/recurring_events-3321235
  • issue/recurring_events-3321550
  • issue/recurring_events-3322998
  • issue/recurring_events-3315836
  • issue/recurring_events-3324055
  • issue/recurring_events-3328907
  • issue/recurring_events-3318490
  • issue/recurring_events-3339288
  • issue/recurring_events-3345618
  • issue/recurring_events-3347935
  • issue/recurring_events-3362297
  • issue/recurring_events-3359696
  • issue/recurring_events-3318666
  • issue/recurring_events-3366907
  • issue/recurring_events-3366910
  • issue/recurring_events-3403064
  • issue/recurring_events-3404311
  • issue/recurring_events-3405567
  • issue/recurring_events-3376639
  • issue/recurring_events-3384836
  • issue/recurring_events-3382387
  • issue/recurring_events-3384389
  • issue/recurring_events-3315503
  • issue/recurring_events-3411229
  • issue/recurring_events-3415222
  • issue/recurring_events-3415308
  • issue/recurring_events-3172514
  • issue/recurring_events-3419694
  • issue/recurring_events-3178696
  • issue/recurring_events-3408924
  • issue/recurring_events-3447130
  • issue/recurring_events-3416436
  • issue/recurring_events-3451613
  • issue/recurring_events-3452632
  • issue/recurring_events-3453086
  • issue/recurring_events-3452641
  • issue/recurring_events-3454012
  • issue/recurring_events-3455716
  • issue/recurring_events-3456300
  • issue/recurring_events-3456641
  • issue/recurring_events-3462327
  • issue/recurring_events-3463467
  • issue/recurring_events-3463979
  • issue/recurring_events-3462480
  • issue/recurring_events-3464792
  • issue/recurring_events-3456045
  • issue/recurring_events-3468300
  • issue/recurring_events-3468521
  • issue/recurring_events-3475611
  • issue/recurring_events-3477247
  • issue/recurring_events-3477047
  • issue/recurring_events-3477650
  • issue/recurring_events-3257502
  • issue/recurring_events-3090186
  • issue/recurring_events-3478802
  • issue/recurring_events-3479449
  • issue/recurring_events-3479843
  • issue/recurring_events-3479860
  • issue/recurring_events-3480495
  • issue/recurring_events-3480500
  • issue/recurring_events-3480746
  • issue/recurring_events-3480973
  • issue/recurring_events-3481021
  • issue/recurring_events-3481722
  • issue/recurring_events-3482804
  • issue/recurring_events-3483283
  • issue/recurring_events-3484209
  • issue/recurring_events-3170156
  • issue/recurring_events-3484558
  • issue/recurring_events-3485904
  • issue/recurring_events-3485935
  • issue/recurring_events-3487412
  • issue/recurring_events-3496270
  • issue/recurring_events-3480508
  • issue/recurring_events-3499792
  • issue/recurring_events-3500920
  • issue/recurring_events-3510919
  • issue/recurring_events-3510942
  • issue/recurring_events-3478268
138 results
Show changes
Commits on Source (307)
Showing
with 1726 additions and 121 deletions
#!/usr/bin/env bash
##
# Build Drupal site using SQLite database, install current module and serve
# using in-built PHP server.
#
# Allows to use the latest Drupal core as well as specified versions (for
# testing backward compatibility).
#
# - builds Drupal site codebase with current module and it's dependencies.
# - installs Drupal using SQLite database.
# - starts in-built PHP-server
# - enables module
# - serves site
# - generates one-time login link
#
# This script will re-build everything from scratch every time it runs.
# shellcheck disable=SC2015,SC2094
set -e
#-------------------------------------------------------------------------------
# Variables (passed from environment; provided for reference only).
#-------------------------------------------------------------------------------
# Directory where Drupal site will be built.
BUILD_DIR="${BUILD_DIR:-build}"
# Webserver hostname.
WEBSERVER_HOST="${WEBSERVER_HOST:-localhost}"
# Webserver port.
WEBSERVER_PORT="${WEBSERVER_PORT:-8000}"
# Drupal core version to use. If not provided - latest version will be used.
# Must be coupled with DRUPAL_PROJECT_SHA below.
DRUPAL_VERSION="${DRUPAL_VERSION:-}"
# Commit SHA of the drupal-project to install custom core version. If not
# provided - the latest version will be used.
# Must be coupled with DRUPAL_VERSION above.
DRUPAL_PROJECT_SHA="${DRUPAL_PROJECT_SHA:-}"
# Drupal profile to use when installing site.
DRUPAL_PROFILE="${DRUPAL_PROFILE:-standard}"
# Module name, taken from .info file.
MODULE="$(basename -s .info.yml -- ./*.info.yml)"
# Database file path.
DB_FILE="${DB_FILE:-/tmp/site_${MODULE}.sqlite}"
#-------------------------------------------------------------------------------
echo "==> Validate requirements."
! command -v git > /dev/null && echo "ERROR: Git is required for this script to run." && exit 1
! command -v php > /dev/null && echo "ERROR: PHP is required for this script to run." && exit 1
! command -v composer > /dev/null && echo "ERROR: Composer (https://getcomposer.org/) is required for this script to run." && exit 1
! command -v jq > /dev/null && echo "ERROR: jq (https://stedolan.github.io/jq/) is required for this script to run." && exit 1
echo "==> Validate Composer config."
composer validate --ansi --strict
[ -d "${BUILD_DIR}" ] && echo "==> Remove existing ${BUILD_DIR} directory." && chmod -Rf 777 "${BUILD_DIR}" && rm -rf "${BUILD_DIR}"
# Allow installing custom version of Drupal core, but only coupled with
# drupal-project SHA (required to get correct dependencies).
if [ -n "${DRUPAL_VERSION}" ] && [ -n "${DRUPAL_PROJECT_SHA}" ]; then
echo "==> Initialise Drupal site from the scaffold commit ${DRUPAL_PROJECT_SHA}."
git clone -n https://github.com/drupal-composer/drupal-project.git "${BUILD_DIR}"
git --git-dir="${BUILD_DIR}/.git" --work-tree="${BUILD_DIR}" checkout "${DRUPAL_PROJECT_SHA}"
rm -rf "${BUILD_DIR}/.git" > /dev/null
echo "==> Pin Drupal to a specific version ${DRUPAL_VERSION}."
sed_opts=(-i) && [ "$(uname)" == "Darwin" ] && sed_opts=(-i '')
sed "${sed_opts[@]}" 's|\(.*"drupal\/core"\): "\(.*\)",.*|\1: '"\"$DRUPAL_VERSION\",|" "${BUILD_DIR}/composer.json"
cat "${BUILD_DIR}/composer.json"
echo "==> Install dependencies."
php -d memory_limit=-1 "$(command -v composer)" --working-dir="${BUILD_DIR}" install
else
echo "==> Initialise Drupal site from the latest scaffold."
php -d memory_limit=-1 "$(command -v composer)" create-project drupal-composer/drupal-project:9.x-dev "${BUILD_DIR}" --no-interaction
fi
echo "==> Install additional dev dependencies from module's composer.json."
cat <<< "$(jq --indent 4 -M -s '.[0] * .[1]' composer.json "${BUILD_DIR}/composer.json")" > "${BUILD_DIR}/composer.json"
php -d memory_limit=-1 "$(command -v composer)" --working-dir="${BUILD_DIR}" update --lock
echo "==> Install other dev dependencies."
cat <<< "$(jq --indent 4 '.extra["phpcodesniffer-search-depth"] = 10' "${BUILD_DIR}/composer.json")" > "${BUILD_DIR}/composer.json"
php -d memory_limit=-1 "$(command -v composer)" --working-dir="${BUILD_DIR}" require --dev dealerdirect/phpcodesniffer-composer-installer
php -d memory_limit=-1 "$(command -v composer)" --working-dir="${BUILD_DIR}" require --dev phpspec/prophecy-phpunit:^2
echo "==> Start inbuilt PHP server at http://${WEBSERVER_HOST}:${WEBSERVER_PORT} in $(pwd)/${BUILD_DIR}/web."
killall -9 php > /dev/null 2>&1 || true
nohup php -S "${WEBSERVER_HOST}:${WEBSERVER_PORT}" -t "$(pwd)/${BUILD_DIR}/web" "$(pwd)/${BUILD_DIR}/web/.ht.router.php" > /tmp/php.log 2>&1 &
sleep 4 # Waiting for the server to be ready.
netstat_opts='-tulpn'; [ "$(uname)" == "Darwin" ] && netstat_opts='-anv' || true;
netstat "${netstat_opts[@]}" | grep -q "${WEBSERVER_PORT}" || (echo "ERROR: Unable to start inbuilt PHP server" && cat /tmp/php.log && exit 1)
curl -s -o /dev/null -w "%{http_code}" -L -I "http://${WEBSERVER_HOST}:${WEBSERVER_PORT}" | grep -q 200 || (echo "ERROR: Server is started, but site cannot be served" && exit 1)
echo "==> Install Drupal into SQLite database ${DB_FILE}."
"${BUILD_DIR}/vendor/bin/drush" -r "${BUILD_DIR}/web" si "${DRUPAL_PROFILE}" -y --db-url "sqlite://${DB_FILE}" --account-name=admin install_configure_form.enable_update_status_module=NULL install_configure_form.enable_update_status_emails=NULL
"${BUILD_DIR}/vendor/bin/drush" -r "$(pwd)/${BUILD_DIR}/web" status
echo "==> Symlink module code."
rm -rf "${BUILD_DIR}/web/modules/${MODULE}"/* > /dev/null
mkdir -p "${BUILD_DIR}/web/modules/${MODULE}"
ln -s "$(pwd)"/* "${BUILD_DIR}/web/modules/${MODULE}" && rm "${BUILD_DIR}/web/modules/${MODULE}/${BUILD_DIR}"
echo "==> Enable module ${MODULE}."
"${BUILD_DIR}/vendor/bin/drush" -r "${BUILD_DIR}/web" pm:enable "${MODULE}" -y
"${BUILD_DIR}/vendor/bin/drush" -r "${BUILD_DIR}/web" cr
# Visit site to pre-warm caches.
curl -s "http://${WEBSERVER_HOST}:${WEBSERVER_PORT}" > /dev/null
echo -n "==> One-time login link: "
"${BUILD_DIR}/vendor/bin/drush" -r "${BUILD_DIR}/web" -l "http://${WEBSERVER_HOST}:${WEBSERVER_PORT}" uli --no-browser
echo
echo "==> Build finished. The site is available at http://${WEBSERVER_HOST}:${WEBSERVER_PORT}."
echo
# @see https://github.com/integratedexperts/drupal_circleci
version: 2
aliases:
# SSH deployment key fingerprint from CircleCI App -> Project -> Settings -> SSH Permissions.
# Replace the value for your project.
- &deploy_ssh_fingerprint "2d:71:4d:aa:4d:34:38:b5:8f:af:ca:3b:d4:82:6a:21"
- &container_config
working_directory: ~/project
docker:
- image: circleci/php:7.4-cli-browsers
job-build: &job-build
steps:
- checkout
- run: |
sudo -E apt-get --allow-releaseinfo-change update && sudo -E apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libpng-dev jq \
&& sudo -E docker-php-ext-install -j$(nproc) iconv \
&& if [ "$(php -r "echo PHP_MAJOR_VERSION;")" -gt 5 ] ; then sudo -E docker-php-ext-configure gd --with-freetype --with-jpeg; else sudo -E docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/; fi \
&& sudo -E docker-php-ext-install -j$(nproc) gd
- run: .circleci/build.sh
- run: .circleci/lint.sh
- run: .circleci/test.sh
- run:
command: .circleci/process-artifacts.sh
when: always
- store_test_results:
path: /tmp/test_results
when: always
- store_artifacts:
path: /tmp/artifacts
when: always
jobs:
build-php-7.4:
<<: *container_config
<<: *job-build
build-php-8.0:
<<: *container_config
docker:
- image: circleci/php:8.0-cli-browsers
<<: *job-build
build-php-7.4-legacy:
<<: *container_config
environment:
DRUPAL_VERSION: 9.3.5
DRUPAL_PROJECT_SHA: 9.x
<<: *job-build
build-php-8.0-legacy:
<<: *container_config
docker:
- image: circleci/php:8.0-cli-browsers
environment:
DRUPAL_VERSION: 9.3.5
DRUPAL_PROJECT_SHA: 9.x
<<: *job-build
deploy:
<<: *container_config
environment:
DEPLOY_SSH_FINGERPRINT: *deploy_ssh_fingerprint
steps:
- checkout
- add_ssh_keys:
fingerprints:
- *deploy_ssh_fingerprint
- run: DEPLOY_BRANCH=${CIRCLE_BRANCH} .circleci/deploy.sh
workflows:
version: 2
main:
jobs:
- build-php-7.4:
filters:
tags:
only: /.*/
- build-php-8.0:
filters:
tags:
only: /.*/
- build-php-7.4-legacy:
filters:
tags:
only: /.*/
- build-php-8.0-legacy:
filters:
tags:
only: /.*/
#!/usr/bin/env bash
##
# Deploy code to a remote repository.
#
# - configures local git
# - adds deployment SSH key to SSH agent
# - force-pushes code to a remote code repository branch
#
# It is a good practice to create a separate Deployer user with own SSH key for
# every project.
#
# Add the following variables through CircleCI UI.
# - DEPLOY_USER_NAME - name of the user who will be committing to a remote repository.
# - DEPLOY_USER_EMAIL - email address of the user who will be committing to a remote repository.
# - DEPLOY_REMOTE - remote repository to push code to.
# - DEPLOY_PROCEED - set to 1 if the deployment should proceed. Useful for testing CI configuration before an actual code push.
set -e
#-------------------------------------------------------------------------------
# Variables (passed from environment; provided for reference only).
#-------------------------------------------------------------------------------
# Name of the user who will be committing to a remote repository.
DEPLOY_USER_NAME="${DEPLOY_USER_NAME}"
# Email address of the user who will be committing to a remote repository.
DEPLOY_USER_EMAIL="${DEPLOY_USER_EMAIL}"
# Remote repository to push code to.
DEPLOY_REMOTE="${DEPLOY_REMOTE:-}"
# Git branch to deploy. If not provided - current branch will be used.
DEPLOY_BRANCH="${DEPLOY_BRANCH:-}"
# The fingerprint of the SSH key of the user on behalf of which the deployment
# is performed.
DEPLOY_SSH_FINGERPRINT="${DEPLOY_SSH_FINGERPRINT:-}"
# Set to 1 if the deployment should proceed. Useful for testing CI configuration
# before an actual code push.
DEPLOY_PROCEED="${DEPLOY_PROCEED:-0}"
#-------------------------------------------------------------------------------
[ -z "${DEPLOY_USER_NAME}" ] && echo "ERROR: Missing required value for DEPLOY_USER_NAME" && exit 1
[ -z "${DEPLOY_USER_EMAIL}" ] && echo "ERROR: Missing required value for DEPLOY_USER_EMAIL" && exit 1
[ -z "${DEPLOY_REMOTE}" ] && echo "ERROR: Missing required value for DEPLOY_REMOTE" && exit 1
[ -z "${DEPLOY_SSH_FINGERPRINT}" ] && echo "ERROR: Missing required value for DEPLOY_SSH_FINGERPRINT" && exit 1
[ "${DEPLOY_PROCEED}" != "1" ] && echo "==> Skipping deployment because \$DEPLOY_PROCEED is not set to 1" && exit 0
# Configure git and SSH to connect to remote servers for deployment.
mkdir -p "${HOME}/.ssh/"
echo -e "Host *\n\tStrictHostKeyChecking no\n" > "${HOME}/.ssh/config"
DEPLOY_SSH_FILE="${DEPLOY_SSH_FINGERPRINT//:}"
DEPLOY_SSH_FILE="${HOME}/.ssh/id_rsa_${DEPLOY_SSH_FILE//\"}"
[ ! -f "${DEPLOY_SSH_FILE}" ] && echo "ERROR: Unable to find Deploy SSH key file ${DEPLOY_SSH_FILE}." && exit 1
if [ -z "${SSH_AGENT_PID}" ]; then eval "$(ssh-agent)"; fi
ssh-add -D > /dev/null
ssh-add "${DEPLOY_SSH_FILE}"
# Configure git user name and email, but only if not already set.
[ "$(git config --global user.name)" == "" ] && echo "==> Configuring global git user name ${DEPLOY_USER_NAME}." && git config --global user.name "${DEPLOY_USER_NAME}"
[ "$(git config --global user.email)" == "" ] && echo "==> Configuring global git user email ${DEPLOY_USER_EMAIL}." && git config --global user.email "${DEPLOY_USER_EMAIL}"
# Set git to push to a matching remote branch.
git config --global push.default matching
echo "==> Adding remote ${DEPLOY_REMOTE}."
git remote add deployremote "${DEPLOY_REMOTE}"
echo "==> Deploying to remote ${DEPLOY_REMOTE}."
# shellcheck disable=SC2086
git push --force --tags deployremote ${DEPLOY_BRANCH}
echo
echo "==> Deployment finished."
echo
#!/usr/bin/env bash
##
# Run lint checks.
#
set -e
#-------------------------------------------------------------------------------
# Variables (passed from environment; provided for reference only).
#-------------------------------------------------------------------------------
# Directory where Drupal site will be built.
BUILD_DIR="${BUILD_DIR:-build}"
# Module name, taken from .info file.
MODULE="$(basename -s .info.yml -- ./*.info.yml)"
#-------------------------------------------------------------------------------
echo "==> Lint code."
build/vendor/bin/phpcs \
-s \
--standard=Drupal,DrupalPractice \
--extensions=module,php,install,inc,test,info.yml,js \
"build/web/modules/${MODULE}"
#!/usr/bin/env bash
##
# Process test artifacts.
#
# This runs only in CircleCI.
#
set -e
if [ -d "$(pwd)/build/web/sites/simpletest/browser_output" ]; then
echo "==> Copying Simpletest test artifacts"
mkdir -p /tmp/artifacts/simpletest
cp -Rf "$(pwd)/build/web/sites/simpletest/browser_output/." /tmp/artifacts/simpletest
fi
#!/usr/bin/env bash
##
# Run tests.
#
set -e
#-------------------------------------------------------------------------------
# Variables (passed from environment; provided for reference only).
#-------------------------------------------------------------------------------
# Directory where Drupal site will be built.
BUILD_DIR="${BUILD_DIR:-build}"
# Webserver hostname.
WEBSERVER_HOST="${WEBSERVER_HOST:-localhost}"
# Webserver port.
WEBSERVER_PORT="${WEBSERVER_PORT:-8000}"
# Module name, taken from .info file.
MODULE="$(basename -s .info.yml -- ./*.info.yml)"
# Test database file path.
TEST_DB_FILE="${TEST_DB_FILE:-/tmp/test.sqlite}"
# Directory to store test results.
TEST_RESULTS_DIR="${TEST_RESULTS_DIR:-/tmp/test_results/simpletest}"
#-------------------------------------------------------------------------------
echo "==> Run tests."
# Do not fail if there are no tests.
[ ! -d "tests" ] && echo "==> No tests were found. Skipping." && exit 0
# Re-create test results directory.
rm -rf "${TEST_RESULTS_DIR}" > /dev/null
mkdir -p "${TEST_RESULTS_DIR}"
# Remove existing test DB file.
rm -f "${TEST_DB_FILE}" > /dev/null
php "./${BUILD_DIR}/web/core/scripts/run-tests.sh" \
--sqlite "${TEST_DB_FILE}" \
--dburl "sqlite://localhost/${TEST_DB_FILE}" \
--url "http://${WEBSERVER_HOST}:${WEBSERVER_PORT}" \
--non-html \
--xml "${TEST_RESULTS_DIR}" \
--color \
--verbose \
--suppress-deprecations \
--module "${MODULE}"
BYDAY
BYEASTER
BYHOUR
BYMINUTE
BYMONTH
BYMONTHDAY
BYSECOND
BYSETPOS
BYWEEKDAY
BYWEEKNO
BYYEARDAY
datecompact
datetimes
DTEND
DTSTAMP
DTSTART
~eventseries
~eventinstance
~eventinstances
EXDATE
EXRULE
fromto
~fullcalendar
INCDATE
langname
LINELENGTH
monthday
monthdays
nonwaitlisted
PRODID
recreator
~rrule
~rrule's
~rrules
VCALENDAR
VEVENT
~waitlist
~waitlisted
~webform
WKST
################
# GitLabCI template for Drupal projects.
#
# This template is designed to give any Contrib maintainer everything they need to test, without requiring modification.
# It is also designed to keep up to date with Core Development automatically through the use of include files that can be centrally maintained.
# As long as you include the project, ref and three files below, any future updates added by the Drupal Association will be used in your
# pipelines automatically. However, you can modify this template if you have additional needs for your project.
# The full documentation is on https://project.pages.drupalcode.org/gitlab_templates/
################
# For information on alternative values for 'ref' see https://project.pages.drupalcode.org/gitlab_templates/info/templates-version/
# To test a Drupal 7 project, change the first include filename from .main.yml to .main-d7.yml
include:
- project: $_GITLAB_TEMPLATES_REPO
ref: $_GITLAB_TEMPLATES_REF
file:
- "/includes/include.drupalci.main.yml"
- "/includes/include.drupalci.variables.yml"
- "/includes/include.drupalci.workflows.yml"
################
# Pipeline configuration variables are defined with default values and descriptions in the file
# https://git.drupalcode.org/project/gitlab_templates/-/blob/main/includes/include.drupalci.variables.yml
################
variables:
# Keep testing on Drupal 10 until our dependencies are D11 ready.
CORE_STABLE: $CORE_PREVIOUS_STABLE
phpcs:
allow_failure: false
language: php
# The Travis CI container mode has random functional test fails, so we must use
# sudo here.
sudo: true
php:
- 7.1
- 7.2
- 7.3
services:
- mysql
env:
global:
- MODULE=recurring_events
matrix:
- DRUPAL_CORE=8.8.x DRUSH_VER=9.0
- DRUPAL_CORE=8.9.x DRUSH_VER=9.0
- DRUPAL_CORE=9.0.x DRUSH_VER=10.0
matrix:
fast_finish: true
exclude:
- php: 7.1
env: DRUPAL_CORE=9.0.x DRUSH_VER=10.0
- php: 7.2
env: DRUPAL_CORE=9.0.x DRUSH_VER=10.0
# Be sure to cache composer downloads.
cache:
directories:
- $HOME/.composer
before_script:
# Remove Xdebug as we don't need it and it causes
# PHP Fatal error: Maximum function nesting level of '256' reached.
# We also don't care if that file exists or not on PHP 7.
- phpenv config-rm xdebug.ini || true
# Navigate out of module directory to prevent blown stack by recursive module
# lookup.
- cd ..
# Create database.
- mysql -e "create database $MODULE"
# Export database variable for kernel tests.
- export SIMPLETEST_DB=mysql://root:@127.0.0.1/$MODULE
# Create a new Drupal project using the appropriate version using Composer.
- COMPOSER_MEMORY_LIMIT=-1 composer create-project drupal/recommended-project:$DRUPAL_CORE drupal --stability dev --no-interaction
- cd drupal
- DRUPAL_ROOT=$(pwd)
# Add the necessary third party dependencies to run these tests.
- COMPOSER_MEMORY_LIMIT=-1 composer require drupal/field_inheritance
- COMPOSER_MEMORY_LIMIT=-1 composer require drush/drush:^$DRUSH_VER
- COMPOSER_MEMORY_LIMIT=-1 composer require mglaman/drupal-check --dev
- COMPOSER_MEMORY_LIMIT=-1 composer require drupal/group:1.x-dev
- cd $DRUPAL_ROOT
# The recurring events module artifact is already available, so copy it in
# to the contrib module directory so we can run our tests.
- cp -R $TRAVIS_BUILD_DIR web/modules/contrib/$MODULE
# Coder is already installed as part of composer install. We just need to set
# the installed_paths to pick up the Drupal standards.
- $DRUPAL_ROOT/vendor/bin/phpcs --config-set installed_paths $DRUPAL_ROOT/vendor/drupal/coder/coder_sniffer
# Export web server URL for browser tests.
- export SIMPLETEST_BASE_URL=http://localhost:8888
script:
# Install the site using Drush and set up some configuration.
- ./vendor/bin/drush site-install standard --yes --account-pass=admin --db-url=$SIMPLETEST_DB
- ./vendor/bin/drush config-set system.performance css.preprocess 0 --yes
- ./vendor/bin/drush config-set system.performance js.preprocess 0 --yes
- ./vendor/bin/drush config-set system.logging error_level all --yes
# Enable the modules we care to run the tests.
- ./vendor/bin/drush en simpletest $MODULE recurring_events_registration recurring_events_views --yes
# Start up a HTTP server for tests.
- ./vendor/bin/drush runserver --default-server=builtin 8888 > /dev/null &
# Run the PHPUnit tests.
- ./vendor/bin/phpunit -c ./web/core/phpunit.xml.dist --verbose --group=$MODULE ./web/modules/contrib/$MODULE
# Run the deprecation checks.
- ./vendor/bin/drupal-check ./web/modules/contrib/$MODULE
# Check for coding standards. First change directory to our module.
- cd $DRUPAL_ROOT/web/modules/contrib/$MODULE
# Show the violations in detail and do not fail for any errors or warnings.
- $DRUPAL_ROOT/vendor/bin/phpcs --standard=drupal,drupalPractice --ignore=vendor --report-width=130 --colors --runtime-set ignore_warnings_on_exit 1 --runtime-set ignore_errors_on_exit 0 .
# Run again to give a summary and total count.
- $DRUPAL_ROOT/vendor/bin/phpcs --standard=drupal,drupalPractice --ignore=vendor --report-width=130 --colors --runtime-set ignore_warnings_on_exit 1 --runtime-set ignore_errors_on_exit 0 --report=summary .
services:
php:
image: q0rban/tugboat-drupal:9.0
default: true
http: false
depends: mysql
commands:
update: |
set -eux
# Check out a branch using the unique Tugboat ID for this repository, to
# ensure we don't clobber an existing branch.
git checkout -b $TUGBOAT_REPO_ID
# Composer is hungry. You need a Tugboat project with a pretty sizeable
# chunk of memory.
export COMPOSER_MEMORY_LIMIT=-1
# This is an environment variable we added in the Dockerfile that
# provides the path to Drupal composer root (not the web root).
cd $DRUPAL_COMPOSER_ROOT
# We configure the Drupal project to use the checkout of the module as a
# Composer package repository.
composer config repositories.tugboat vcs $TUGBOAT_ROOT
# Now we can require this module, specifing the branch name we created
# above that uses the $TUGBOAT_REPO_ID environment variable.
composer require drupal/recurring_events:dev-$TUGBOAT_REPO_ID
# Install Drupal on the site.
vendor/bin/drush \
--yes \
--db-url=mysql://tugboat:tugboat@mysql:3306/tugboat \
--site-name="Live preview for ${TUGBOAT_PREVIEW_NAME}" \
--account-pass=admin \
site:install standard
# Set up the files directory permissions.
mkdir -p $DRUPAL_DOCROOT/sites/default/files
chgrp -R www-data $DRUPAL_DOCROOT/sites/default/files
chmod 2775 $DRUPAL_DOCROOT/sites/default/files
chmod -R g+w $DRUPAL_DOCROOT/sites/default/files
# Enable the module.
vendor/bin/drush --yes pm:enable recurring_events
build: |
set -eux
# Delete and re-check out this branch in case this is built from a Base Preview.
git branch -D $TUGBOAT_REPO_ID && git checkout -b $TUGBOAT_REPO_ID || true
export COMPOSER_MEMORY_LIMIT=-1
cd $DRUPAL_COMPOSER_ROOT
composer install --optimize-autoloader
# Update this module, including all dependencies.
composer update drupal/recurring_events --with-all-dependencies
vendor/bin/drush --yes updb
vendor/bin/drush cache:rebuild
mysql:
image: tugboatqa/mariadb
[![Build Status](https://travis-ci.org/owenbush/recurring_events.svg?branch=2.0.x)](https://travis-ci.org/owenbush/recurring_events)
Introduction
------------
The recurring events module offers the basic building blocks to build a
recurring events management system, and the APIs and integrations enable
developers and site builders to extend and enhance the core functionality.
### Module Hierarchy
- `recurring_events` is the main/core module which contains all the base
functionality for the module.
- `recurring_events_registration` is a submodule which enables registrations.
- `recurring_events_views` is a submodule which replaces all entity lists with
views for flexibility.
### Data Modelling Concepts
There are several data models relating to the Recurring Events module.
1. **Event Series**
The `eventseries` custom entity type is used as a wrapper around a series of
event instances. The `eventseries` entity holds all the data necessary to
build out and schedule event instances on particular dates at set times. The
eventseries entity also contains the title, and description for all the
events in the series (unless they are overridden - see below).
2. **Event Instances**
The `eventinstance` custom entity type represents a single event occurrence.
For every date/time that an event takes place on, there will be an
associated `eventinstance`. This makes querying, calendar display and list
displays much simpler as they are single entities - we do not have to do any
special display-time logic to generate instances on the fly. Out-of-the-box,
the `eventinstance` entity type is very basic - the date/time can be
changed, and an additional description can be appended to the series
description. The title, and main description, are inherited directly from
the `eventseries`.
3. **Event Series and Event Instance Types**
The module ships with a single bundle, called `default`, for series and
instances, and new bundles, or event types, can be created. Types can only
be created for `eventseries` but when they are an equivalent type is created
for the `eventinstance` entity. If
the `recurring_events_registration` submodule is enabled, creating an
`eventseries` type will also create an equivalent `registrant` type.
### Adding Events
When creating an event, what is really happening is an `eventseries` entity is
being created which will then automatically generate the
associated `eventinstance` entities for each occurrence. There is no way to
create an `eventinstance` outside of an `eventseries` entity. If more dates need
to be added, then the `eventseries` will need to be modified, or
an `eventinstance` will need to be cloned.
Creating an `eventseries` involves choosing what type of recurrence the event
will have. There are 3 options.
1. **Consecutive Event**
A `consecutive event` is an event that takes place multiple times during a
day. Events are given a `duration` and an optional `buffer` between events.
An example would be parent-teacher conferences at a school, where one
teacher will see multiple parents in one day for 20 minutes, and have a
10-minute break between conferences. In this example, there would be an
`eventinstance` every 30 minutes, lasting 20-minutes with a 10-minute break
afterward.
2. **Daily Event**
A `daily event` is an event that takes place once per day. It is the
equivalent of a `weekly event` which recurs every single day of the week.
3. **Weekly Event**
A `weekly event` is an event that recurs based on the day of the week, every
week. For example, if you wanted to run a Toddler Story Time every Monday at
10am, you would set that up as a weekly event recurring on Mondays. With
a `weekly event` you are asked to select a date range, between which the
events will take place. This means that all Mondays between two particular
dates will have an `eventinstance` created for this event. You will also
enter a start time of the event, and an event duration. The system will then
create event instances on every occurrence of the weekdays you specify,
between the date range, starting at the time you specify and lasting for as
long as the duration set. To achieve an `every day` or `daily event`, a user
could create a `weekly event` that recurs on every day of the week.
4. **Monthly Event**
A `monthly recurring event` is one which recurs on a monthly basis, as
opposed to weekly. There are 2 types of `monthly recurring event`.
1. **Weekday Occurrence**
You can choose for an event to take place on
the `first`, `second`, `third`, `fourth` or `last` weekday of a month.
As an example you can have an event take place every first and third
Monday and Friday of every month.
2. **Day of Month Occurrence**
You can alternatively choose for an event to take place on a specific
day of the month, for example the 6th of every month, or the last day of
every month.
**Note:** If the day of the month selected does not actually occur in
any given month then no event will be scheduled that month. As an
example, if you choose to run an event on the 31st of every month
between January 1st and September 30th of a particular year, then no
events would be scheduled in February, April, June or September because
those months do not have a 31st day.
5. **Custom Dates**
A `custom dates eventseries` allows a user to create one or
more `eventinstance` entities without a repeatable or structured recurrence
pattern. Despite what the name may suggest, there can multiple occurrences
of a `custom dates eventseries`. The difference between a `custom dates
eventseries` and any other type of recurring event is that the dates and
times are individual and do not recur. So, the event could take place at
noon on a Wednesday in June, then a 10am on a Monday in August. There is no
structure to how and when the events take place.
When creating a `custom dates eventseries` you will be asked to specify a
date and time for each individual occurrence of the event.
### Modifying and Deleting Events
Providing a user has the appropriate permissions to do so, events can be
modified or deleted after they have been created. When modifying an event, a
warning will be displayed to show that any changes made to date configurations
or recurring types will cause all `eventinstance` entities in
that `eventseries` to be deleted, and recreated. All other changes are
non-breaking changes and will not result in any instances being
removed. `eventinstance` entities inherit their title, and description from
the `eventseries`, so any changes made to those fields will cascade down to
all `eventinstances` in the `eventseries`.
Deleting an `eventseries` will also remove all the `eventinstance` entities in
that `eventseries`. This action cannot be undone.
### Modifying and Deleting Event Instances
Individual `eventinstance` entities can be modified or deleted providing the
user has the appropriate permissions to do so. When modifying
an `eventinstance`, no changes will be made upstream to the `eventseries` or the
other `eventinstance` entities in the series. This way an individual occurrence
of an event can be moved to a different day (in case of a booking conflict, or
public holiday/close day for example) or a different time to the rest of the
events in that series. Equally, an individual occurrence can be removed without
affecting any of the other instances in that series.
**Note:** If an `eventinstance` is removed, and then the `eventseries` is
modified, the removed `eventinstance` may be recreated if the date recurrence
configuration gets changed.
### Field Inheritance from Series to Instance
The `recurring_events` module has one contrib-space dependency for the core
module, which is the `field_inheritance` module which facilitates inheriting
field data from an `eventseries` into an `eventinstance`.
There are two fields which are automatically inherited from
the `eventseries` down to the `eventinstance`. All fields created using the
Field API may be inherited, providing there is a Field Inheritance Plugin
written for that particular field type.
Below are the field inheritance fields that come out-of-the-box with the module.
|Field|Strategy|Notes|
|:----|:-------|:----|
|Title|Inherit|The title for an event is always controlled by the series and cannot be changed on a per-instance basis. The field is inherited directly to the `eventinstance` entities within that series.|
|Body|Append|The body field from the `eventinstance` (if set) is appended to the end of the body field from the `eventseries` (if set).|
### Publishing Workflow and Revisions
Out-of-the-box the module supports Drupal core content moderation and workflow
modules, although the module does not depend on those modules being enabled.
Both `eventseries` and `eventinstance` entities support revisioning.
Revisioning and Content Moderation do play a conceptually interesting role when
it comes to `eventseries` entities due to the way that `eventinstance` entities
are automatically created, or recreated. If an event series is already
published, and has a number of `eventinstance` entities associated with it, then
if a user creates a new draft of the `eventseries` and that draft contains data
recurrence configuration changes, then we must not recreate
any `eventinstances` until that revision is published. The module handles this
concept as part of its core offering.
### Hooks, extensibility and APIs
All `eventseries` and `eventinstance` entities are compatible with the Drupal
core translation API.
The `recurring_events` module exposes its own hooks to use to modify core
functionality. These hooks are defined in `recurring_events.api.php`. Custom
modules can be written to modify, or enhance the core functionality
of `recurring_events` by making use of these hooks.
The `recurring_events` module also has a number of Field Inheritance plugins
written to handle core fields, and custom plugins can be created providing they
implement the `FieldInheritance` annotation and extend
the `FieldInheritancePluginBase` class. The core plugins are defined
in `src/Plugin/FieldInheritance`.
### Dependencies
This module only relies on the following two core modules, and one contrib
module being enabled:
- drupal:datetime\_range
- drupal:options
- field\_inheritance:field\_inheritance
### Similar Modules
The closest comparison would be the `date_recur` module, which adds a field type
which allows RRule compliant date recurrence configuration to be added. While
that module does a really great job, this module approaches things differently.
With `date_recur`, a content editor would have a single entity with a recurrence
field that builds instances of that event at display time.
With `recurring_events`, `eventinstances` are separate entities completely, and
therefore can be overridden or extended, without affecting the rest of the
series. This module also comes with a registration submodule, including the
ability to register either for an entire series, or individual events.
Using `date_recur` that would not be possible as there is only one entity.
### Getting Started
- Configure the Events Series and Event Instances at /admin/structure/events.
- Add an event entity by going to Content -> Events -> Add Event (/events/add). Note: If you already have an Events node type, there may be some route collisions, and you may now see two menu items with a title of "Events". See the recurring_events.routing.yml for the existing routes.
- Note: If you try to add the newly created Event fields types (Consecutive Event, Daily Event, Monthly Event, Weekly Event) directly on a node/entity, you're doing it wrong and it will complain about missing plugin types.
......@@ -3,7 +3,7 @@
"type": "drupal-module",
"description": "Recurring Events and Registration Management Module",
"keywords": ["Drupal"],
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"homepage": "https://www.drupal.org/project/recurring_events",
"minimum-stability": "dev",
"support": {
......@@ -11,6 +11,9 @@
"source": "http://cgit.drupalcode.org/recurring_events"
},
"require": {
"drupal/field_inheritance": "^1"
"drupal/field_inheritance": "^2"
},
"require-dev": {
"drupal/token": "^1.11"
}
}
......@@ -28,7 +28,7 @@ content:
third_party_settings: { }
custom_date:
type: daterange_default
weight: 7
weight: 8
region: content
settings: { }
third_party_settings: { }
......@@ -40,13 +40,13 @@ content:
third_party_settings: { }
excluded_dates:
type: daterange_default
weight: 8
weight: 9
settings: { }
region: content
third_party_settings: { }
included_dates:
type: daterange_default
weight: 9
weight: 10
settings: { }
region: content
third_party_settings: { }
......@@ -66,7 +66,7 @@ content:
type: boolean_checkbox
settings:
display_label: true
weight: 12
weight: 13
region: content
third_party_settings: { }
title:
......@@ -79,7 +79,7 @@ content:
third_party_settings: { }
uid:
type: entity_reference_autocomplete
weight: 11
weight: 12
settings:
match_operator: CONTAINS
size: 60
......@@ -93,4 +93,10 @@ content:
region: content
settings: { }
third_party_settings: { }
yearly_recurring_date:
type: yearly_recurring_date
weight: 7
region: content
settings: { }
third_party_settings: { }
hidden: { }
......@@ -51,3 +51,4 @@ hidden:
event_registration: true
monthly_recurring_date: true
weekly_recurring_date: true
yearly_recurring_date: true
......@@ -36,3 +36,4 @@ hidden:
monthly_recurring_date: true
recur_type: true
weekly_recurring_date: true
yearly_recurring_date: true
......@@ -7,8 +7,9 @@ days: 'monday,tuesday,wednesday,thursday,friday,saturday,sunday'
limit: 10
excludes: 1
includes: 1
enabled_fields: 'consecutive_recurring_date,daily_recurring_date,weekly_recurring_date,monthly_recurring_date,custom'
enabled_fields: 'consecutive_recurring_date,daily_recurring_date,weekly_recurring_date,monthly_recurring_date,yearly_recurring_date,custom'
threshold_warning: 1
threshold_count: 200
threshold_message: 'Saving this series will create up to @total event instances. This could result in memory exhaustion or site instability.'
threshold_prevent_save: 0
creator_plugin: recurring_events_eventinstance_recreator
......@@ -44,6 +44,9 @@ recurring_events.eventseries.config:
threshold_prevent_save:
type: integer
label: 'Prevent saving a series if too many instances are being created'
creator_plugin:
type: string
label: 'The creator plugin used when creating event instances'
recurring_events.eventinstance.config:
type: config_object
......
......@@ -2,8 +2,8 @@
* @file
* Javascript functionality for the recurring events create form.
*/
(function ($) {
/* eslint-disable */
(function ($, once) {
'use strict';
/**
......@@ -30,12 +30,12 @@
// Remove all the weekday recurrence options.
$('#edit-weekly-recurring-date-0-days').find('input').each(function (key, item) {
$(item).prop('checked', false);
$(item).prop('checked', FALSE);
});
// Set the event to recur on the same day of the week as the start
// date.
$('#edit-weekly-recurring-date-0-days-' + weekday).prop('checked', true);
$('#edit-weekly-recurring-date-0-days-' + weekday).prop('checked', TRUE);
}
});
......@@ -49,12 +49,12 @@
// Remove all the monthly recurrence options.
$('#edit-monthly-recurring-date-0-days').find('input').each(function (key, item) {
$(item).prop('checked', false);
$(item).prop('checked', FALSE);
});
// Set the event to recur on the same day of the week as the start
// date.
$('#edit-monthly-recurring-date-0-days-' + weekday).prop('checked', true);
$('#edit-monthly-recurring-date-0-days-' + weekday).prop('checked', TRUE);
}
});
}
......@@ -65,7 +65,7 @@
*/
Drupal.behaviors.recurring_events_excluded_included_dates = {
attach: function (context, settings) {
$('#edit-excluded-dates-wrapper, #edit-included-dates-wrapper').find('input.form-date').once().on('change', function (e) {
$(once('edit_excluded_date', $('#edit-excluded-dates-wrapper, #edit-included-dates-wrapper').find('input.form-date'))).on('change', function (e) {
if ($(this).attr('name').includes('[value][date]')) {
var start_date = this;
var parent = $(this).closest('.form-wrapper');
......@@ -81,4 +81,4 @@
}
};
}(jQuery));
}(jQuery, once));
......@@ -2,8 +2,8 @@
* @file
* Javascript functionality for the included/excluded date forms.
*/
(function ($) {
/* eslint-disable */
(function ($, once) {
'use strict';
/**
......@@ -12,7 +12,7 @@
Drupal.behaviors.recurring_events_excluded_included_config_dates = {
attach: function (context, settings) {
$('#edit-start').once().on('change', function (e) {
$(once('edit_recurring_events_excluded_included_config_dates', $('#edit-start'))).on('change', function (e) {
if ($('#edit-end').val() == '') {
$('#edit-end').val($(this).val());
}
......@@ -20,4 +20,4 @@
}
};
}(jQuery));
}(jQuery, once));