├── .github
└── CODEOWNERS
├── db-seed
└── .gitignore
├── cms
├── web
│ ├── cpresources
│ │ └── .gitignore
│ ├── assets
│ │ └── site
│ │ │ ├── vanilla-scoop.jpg
│ │ │ ├── chocolate-scoop.jpg
│ │ │ └── strawberry-scoop.png
│ ├── img
│ │ └── site
│ │ │ ├── login-background-image.png
│ │ │ └── nys-logo.svg
│ ├── index.php
│ ├── .htaccess
│ └── web.config
├── config
│ ├── project
│ │ ├── fieldGroups
│ │ │ ├── 1aeaf5aa-fe37-42b5-aef9-f4556b92c43a.yaml
│ │ │ ├── 9265d2f0-4fa5-4187-8ee4-b6eb8808e295.yaml
│ │ │ ├── 86e92ff9-5119-4b99-b222-f3279dcc7818.yaml
│ │ │ └── 92425d88-9e0a-4213-b474-f553f37261c2.yaml
│ │ ├── siteGroups
│ │ │ └── f89601e9-4ba9-4a48-9e99-350aa9914912.yaml
│ │ ├── graphql
│ │ │ ├── graphql.yaml
│ │ │ └── schemas
│ │ │ │ └── 6005c2f9-5d85-4442-b712-22e070096ac8.yaml
│ │ ├── users
│ │ │ ├── users.yaml
│ │ │ └── fieldLayouts
│ │ │ │ └── 34d1d67d-a82b-4439-a563-3193f2964a51.yaml
│ │ ├── sites
│ │ │ └── default--5da841b1-ca0d-46ff-8bb1-04d6c889ac54.yaml
│ │ ├── sections
│ │ │ ├── pages--7c3fb4f3-2ba5-4834-9056-6712aa5c18f4.yaml
│ │ │ ├── orders--1f2ba12a-f04e-47d9-af2b-3e876c5adbce.yaml
│ │ │ └── flavors--10571998-751e-449a-98c5-d48adf32034b.yaml
│ │ ├── fields
│ │ │ ├── flavorCalories--2b2ed478-8e77-4c39-a69a-56ad864557d1.yaml
│ │ │ ├── flavorPrice--3d7dba4f-8e70-4965-acec-4e70f830a016.yaml
│ │ │ ├── scoops--c6fb33da-e3f0-4f50-a4c5-4cce12415e80.yaml
│ │ │ ├── disableMatrixFacades--858b290d-45ae-4da8-8e51-2c03caefeee3.yaml
│ │ │ ├── colorsSwatches--ade97714-e4c4-4c73-9322-2c6b398cef04.yaml
│ │ │ └── flavorImage--682126f6-e053-4b1d-b707-82879cb43392.yaml
│ │ ├── volumes
│ │ │ └── site--5c642d7e-b16b-4836-9575-668d75d242e5.yaml
│ │ ├── entryTypes
│ │ │ ├── default--d2e0ba28-01c7-4060-8087-0c12e978341b.yaml
│ │ │ ├── default--46862260-8c13-44f7-9a4e-a3cb6e1a22b3.yaml
│ │ │ └── default--88ffed5d-fece-4914-9e1a-a275577840d3.yaml
│ │ └── project.yaml
│ ├── redactor
│ │ ├── Simple.json
│ │ └── Default.json
│ ├── license.key
│ ├── license.key.1
│ ├── htmlpurifier
│ │ └── Default.json
│ ├── app.console.php
│ ├── db.php
│ ├── routes.php
│ ├── app.web.php
│ ├── general.php
│ └── app.php
├── templates
│ └── index.twig
├── modules
│ └── sitemodule
│ │ ├── src
│ │ ├── fields
│ │ │ ├── FacadeInterface.php
│ │ │ ├── FacadeTrait.php
│ │ │ └── MatrixFacade.php
│ │ ├── assetbundles
│ │ │ └── sitemodule
│ │ │ │ ├── dist
│ │ │ │ ├── js
│ │ │ │ │ └── SiteModule.js
│ │ │ │ ├── css
│ │ │ │ │ └── SiteModule.css
│ │ │ │ └── img
│ │ │ │ │ └── SiteModule-icon.svg
│ │ │ │ └── SiteModuleAsset.php
│ │ ├── translations
│ │ │ └── en
│ │ │ │ └── site-module.php
│ │ ├── services
│ │ │ └── Helper.php
│ │ ├── variables
│ │ │ └── SiteVariable.php
│ │ ├── templates
│ │ │ └── _components
│ │ │ │ ├── fields
│ │ │ │ └── MatrixFacade
│ │ │ │ │ └── settings.html
│ │ │ │ └── matrixfacades
│ │ │ │ ├── scoops
│ │ │ │ ├── tablecell.html
│ │ │ │ ├── input-variables.html
│ │ │ │ └── input.html
│ │ │ │ └── colorswatches
│ │ │ │ └── input.html
│ │ ├── config.php
│ │ ├── helpers
│ │ │ └── Config.php
│ │ ├── behaviors
│ │ │ ├── CpVariableBehavior.php
│ │ │ └── MatrixCriteriaBehavior.php
│ │ └── SiteModule.php
│ │ ├── CHANGELOG.md
│ │ ├── .craftplugin
│ │ ├── .gitignore
│ │ ├── LICENSE.md
│ │ └── README.md
├── craft
├── craft.bat
├── bootstrap.php
├── example.env
└── composer.json
├── docker-config
├── redis
│ └── Dockerfile
├── mysql
│ └── Dockerfile
├── nginx
│ ├── Dockerfile
│ └── default.conf
├── php-prod-craft
│ ├── composer_install.sh
│ ├── run_queue.sh
│ └── Dockerfile
└── php-dev-craft
│ └── Dockerfile
├── docs
└── img
│ ├── entry-scoops-matrix-facade.png
│ ├── entry-scoops-matrix-field.png
│ ├── entry-swatches-matrix-facade.png
│ ├── entry-swatches-matrix-field.png
│ └── user-disable-matrix-facades.png
├── Makefile
├── CHANGELOG.md
├── docker-compose.yml
└── README.md
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @khalwat
2 |
--------------------------------------------------------------------------------
/db-seed/.gitignore:
--------------------------------------------------------------------------------
1 | !.gitignore
2 |
--------------------------------------------------------------------------------
/cms/web/cpresources/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/docker-config/redis/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM redis:5-alpine
2 |
--------------------------------------------------------------------------------
/docker-config/mysql/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mysql/mysql-server:latest
2 |
--------------------------------------------------------------------------------
/cms/config/project/fieldGroups/1aeaf5aa-fe37-42b5-aef9-f4556b92c43a.yaml:
--------------------------------------------------------------------------------
1 | name: User
2 |
--------------------------------------------------------------------------------
/cms/config/project/fieldGroups/9265d2f0-4fa5-4187-8ee4-b6eb8808e295.yaml:
--------------------------------------------------------------------------------
1 | name: Pages
2 |
--------------------------------------------------------------------------------
/cms/config/project/fieldGroups/86e92ff9-5119-4b99-b222-f3279dcc7818.yaml:
--------------------------------------------------------------------------------
1 | name: Flavors
2 |
--------------------------------------------------------------------------------
/cms/config/project/fieldGroups/92425d88-9e0a-4213-b474-f553f37261c2.yaml:
--------------------------------------------------------------------------------
1 | name: Orders
2 |
--------------------------------------------------------------------------------
/cms/config/project/siteGroups/f89601e9-4ba9-4a48-9e99-350aa9914912.yaml:
--------------------------------------------------------------------------------
1 | name: default
2 |
--------------------------------------------------------------------------------
/cms/config/project/graphql/graphql.yaml:
--------------------------------------------------------------------------------
1 | publicToken:
2 | enabled: false
3 | expiryDate: null
4 |
--------------------------------------------------------------------------------
/docker-config/nginx/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx:1.19-alpine
2 |
3 | COPY ./default.conf /etc/nginx/conf.d/default.conf
4 |
--------------------------------------------------------------------------------
/cms/config/project/graphql/schemas/6005c2f9-5d85-4442-b712-22e070096ac8.yaml:
--------------------------------------------------------------------------------
1 | isPublic: true
2 | name: 'Public Schema'
3 |
--------------------------------------------------------------------------------
/cms/web/assets/site/vanilla-scoop.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nystudio107/matrixfacades/develop/cms/web/assets/site/vanilla-scoop.jpg
--------------------------------------------------------------------------------
/cms/config/redactor/Simple.json:
--------------------------------------------------------------------------------
1 | {
2 | "buttons": ["bold", "italic"],
3 | "toolbarFixed": true,
4 | "plugins": ["richvariables"]
5 | }
6 |
--------------------------------------------------------------------------------
/cms/web/assets/site/chocolate-scoop.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nystudio107/matrixfacades/develop/cms/web/assets/site/chocolate-scoop.jpg
--------------------------------------------------------------------------------
/docs/img/entry-scoops-matrix-facade.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nystudio107/matrixfacades/develop/docs/img/entry-scoops-matrix-facade.png
--------------------------------------------------------------------------------
/docs/img/entry-scoops-matrix-field.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nystudio107/matrixfacades/develop/docs/img/entry-scoops-matrix-field.png
--------------------------------------------------------------------------------
/cms/web/assets/site/strawberry-scoop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nystudio107/matrixfacades/develop/cms/web/assets/site/strawberry-scoop.png
--------------------------------------------------------------------------------
/docs/img/entry-swatches-matrix-facade.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nystudio107/matrixfacades/develop/docs/img/entry-swatches-matrix-facade.png
--------------------------------------------------------------------------------
/docs/img/entry-swatches-matrix-field.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nystudio107/matrixfacades/develop/docs/img/entry-swatches-matrix-field.png
--------------------------------------------------------------------------------
/docs/img/user-disable-matrix-facades.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nystudio107/matrixfacades/develop/docs/img/user-disable-matrix-facades.png
--------------------------------------------------------------------------------
/cms/web/img/site/login-background-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nystudio107/matrixfacades/develop/cms/web/img/site/login-background-image.png
--------------------------------------------------------------------------------
/cms/config/project/users/users.yaml:
--------------------------------------------------------------------------------
1 | allowPublicRegistration: false
2 | defaultGroup: null
3 | photoSubpath: ''
4 | photoVolumeUid: null
5 | requireEmailVerification: true
6 |
--------------------------------------------------------------------------------
/cms/config/project/sites/default--5da841b1-ca0d-46ff-8bb1-04d6c889ac54.yaml:
--------------------------------------------------------------------------------
1 | baseUrl: '@web/'
2 | enabled: true
3 | handle: default
4 | hasUrls: true
5 | language: en-US
6 | name: English
7 | primary: true
8 | siteGroup: f89601e9-4ba9-4a48-9e99-350aa9914912 # default
9 | sortOrder: 1
10 |
--------------------------------------------------------------------------------
/cms/config/license.key:
--------------------------------------------------------------------------------
1 | A3S$GQM+SJC1+3APM!TGIA!FK1OW=1%9&YA7SMMX3JDCI52NCR
2 | MD5=Q3/OCM/#KJOX#5!PGI=##CHBR&I!$N$XGS+S=#KEH2EJF#
3 | CORIJI=B$QYX8Q$V*#RKO7TFPR*6+VFBT&Q+4G&MI+I^A4E3V#
4 | XM9L/HZH/VO5LEDBB=XL^$AFBT+B4Q6HRYE0V3==*8NCN/#IMM
5 | YGZ=SD8NJ^9MBVEXP&B=YIRQ5LDPLEA6C2$#^KRUPUHT^4X$J=
6 |
--------------------------------------------------------------------------------
/cms/config/license.key.1:
--------------------------------------------------------------------------------
1 | &N=KGVRGUXUB%9W33#F02KG8PS4LOIQCICG+4++8VCQO#D9BHP
2 | PHIDJ2Q$$2$D5A0XNL41I1BZG4ZJ1^%E=BKT0/K03/WC6W#3GV
3 | 2L4SF*P^+JMYJ*E0MA$V09^H^3U!9I#OYRPQNV+3$NKG+M!JGH
4 | D#WL&YXSH49DIJYPB*WQBVT8JN8IH0V6B!R9XB5S26M9%/QIQ*
5 | Y#R*$H3QPH9TN&VFYWH$5T!WNHC=UERI39!ZM^W/4PN8^/BPHC
6 |
--------------------------------------------------------------------------------
/cms/templates/index.twig:
--------------------------------------------------------------------------------
1 | {% set orders = craft.entries
2 | .section('orders')
3 | .matrixCriteria('scoops', {
4 | 'type': 'default',
5 | 'nuts': true
6 | })
7 | .all() %}
8 |
9 | {% for order in orders %}
10 |
Order with nuts: {{ order.title }}
11 | {% endfor %}
12 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/fields/FacadeInterface.php:
--------------------------------------------------------------------------------
1 | run();
13 |
--------------------------------------------------------------------------------
/cms/config/htmlpurifier/Default.json:
--------------------------------------------------------------------------------
1 | {
2 | "Attr.AllowedFrameTargets": [
3 | "_blank"
4 | ],
5 | "Attr.EnableID": true,
6 | "HTML.AllowedComments": [
7 | "pagebreak"
8 | ],
9 | "HTML.SafeIframe": true,
10 | "URI.SafeIframeRegexp": "%^(https?:)?//(www.youtube.com/embed/|player.vimeo.com/video/)%"
11 | }
12 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Site Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
6 |
7 | ## 1.0.0 - 2018-01-19
8 | ### Added
9 | - Initial release
10 |
--------------------------------------------------------------------------------
/cms/web/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | RewriteEngine On
3 |
4 | # Send would-be 404 requests to Craft
5 | RewriteCond %{REQUEST_FILENAME} !-f
6 | RewriteCond %{REQUEST_FILENAME} !-d
7 | RewriteCond %{REQUEST_URI} !^/(favicon\.ico|apple-touch-icon.*\.png)$ [NC]
8 | RewriteRule (.+) index.php?p=$1 [QSA,L]
9 |
10 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/.craftplugin:
--------------------------------------------------------------------------------
1 | {"pluginName":"Site","pluginDescription":"An example module for Craft CMS 3 that lets you enhance your websites with a custom site module","pluginVersion":"1.0.0","pluginAuthorName":"nystudio107","pluginVendorName":"nystudio107","pluginAuthorUrl":"https://nystudio107.com/","codeComments":[],"pluginComponents":[],"apiVersion":"module_api_version_3_0"}
--------------------------------------------------------------------------------
/cms/craft:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | run();
14 | exit($exitCode);
15 |
--------------------------------------------------------------------------------
/cms/config/redactor/Default.json:
--------------------------------------------------------------------------------
1 | {
2 | "buttons": [
3 | "html",
4 | "formatting",
5 | "bold",
6 | "italic",
7 | "unorderedlist",
8 | "orderedlist",
9 | "link",
10 | "image",
11 | "video"
12 | ],
13 | "plugins": [
14 | "fullscreen",
15 | "video",
16 | "richvariables"
17 | ],
18 | "linkNewTab": true,
19 | "toolbarFixed": true
20 | }
21 |
--------------------------------------------------------------------------------
/cms/config/project/sections/pages--7c3fb4f3-2ba5-4834-9056-6712aa5c18f4.yaml:
--------------------------------------------------------------------------------
1 | defaultPlacement: end
2 | enableVersioning: true
3 | handle: pages
4 | name: Pages
5 | propagationMethod: all
6 | siteSettings:
7 | 5da841b1-ca0d-46ff-8bb1-04d6c889ac54: # English
8 | enabledByDefault: true
9 | hasUrls: true
10 | template: pages/_entry
11 | uriFormat: 'pages/{slug}'
12 | type: channel
13 |
--------------------------------------------------------------------------------
/cms/craft.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | rem -------------------------------------------------------------
4 | rem Craft command line bootstrap script for Windows
5 | rem -------------------------------------------------------------
6 |
7 | @setlocal
8 |
9 | set CRAFT_PATH=%~dp0
10 |
11 | if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
12 |
13 | "%PHP_COMMAND%" "%CRAFT_PATH%craft" %*
14 |
15 | @endlocal
16 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/translations/en/site-module.php:
--------------------------------------------------------------------------------
1 | 'Site plugin loaded',
18 | ];
19 |
--------------------------------------------------------------------------------
/cms/config/project/users/fieldLayouts/34d1d67d-a82b-4439-a563-3193f2964a51.yaml:
--------------------------------------------------------------------------------
1 | tabs:
2 | -
3 | elements:
4 | -
5 | fieldUid: 858b290d-45ae-4da8-8e51-2c03caefeee3 # Disable Matrix Facades
6 | instructions: null
7 | label: null
8 | required: false
9 | tip: null
10 | type: craft\fieldlayoutelements\CustomField
11 | warning: null
12 | width: 100
13 | name: 'User Settings'
14 | sortOrder: 1
15 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/assetbundles/sitemodule/dist/css/SiteModule.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Site module for Craft CMS
3 | *
4 | * Site CSS
5 | *
6 | * @author nystudio107
7 | * @copyright Copyright (c) 2022 nystudio107
8 | * @link https://nystudio107.com/
9 | * @package SiteModule
10 | * @since 1.0.0
11 | */
12 |
13 | body.login {
14 | background-color: #FFF;
15 | background-image: url('/img/site/login-background-image.png');
16 | }
17 |
18 | body.login label, body.login #forgot-password {
19 | background-color: #FFF;
20 | }
21 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/.gitignore:
--------------------------------------------------------------------------------
1 | # CRAFT ENVIRONMENT
2 | .env.php
3 | .env.sh
4 | .env
5 |
6 | # COMPOSER
7 | /vendor
8 |
9 | # BUILD FILES
10 | /bower_components/*
11 | /node_modules/*
12 | /build/*
13 | /yarn-error.log
14 |
15 | # MISC FILES
16 | .cache
17 | .DS_Store
18 | .idea
19 | .project
20 | .settings
21 | *.esproj
22 | *.sublime-workspace
23 | *.sublime-project
24 | *.tmproj
25 | *.tmproject
26 | .vscode/*
27 | !.vscode/settings.json
28 | !.vscode/tasks.json
29 | !.vscode/launch.json
30 | !.vscode/extensions.json
31 | config.codekit3
32 | prepros-6.config
33 |
--------------------------------------------------------------------------------
/cms/config/project/fields/flavorCalories--2b2ed478-8e77-4c39-a69a-56ad864557d1.yaml:
--------------------------------------------------------------------------------
1 | columnSuffix: cihpxaws
2 | contentColumnType: integer(10)
3 | fieldGroup: 86e92ff9-5119-4b99-b222-f3279dcc7818 # Flavors
4 | handle: flavorCalories
5 | instructions: ''
6 | name: 'Flavor Calories'
7 | searchable: false
8 | settings:
9 | decimals: 0
10 | defaultValue: null
11 | max: null
12 | min: 0
13 | prefix: null
14 | previewCurrency: ''
15 | previewFormat: decimal
16 | size: null
17 | suffix: null
18 | translationKeyFormat: null
19 | translationMethod: none
20 | type: craft\fields\Number
21 |
--------------------------------------------------------------------------------
/cms/config/project/fields/flavorPrice--3d7dba4f-8e70-4965-acec-4e70f830a016.yaml:
--------------------------------------------------------------------------------
1 | columnSuffix: rsfzwrwz
2 | contentColumnType: 'decimal(12,2)'
3 | fieldGroup: 86e92ff9-5119-4b99-b222-f3279dcc7818 # Flavors
4 | handle: flavorPrice
5 | instructions: ''
6 | name: 'Flavor Price'
7 | searchable: false
8 | settings:
9 | decimals: '2'
10 | defaultValue: null
11 | max: null
12 | min: 0
13 | prefix: null
14 | previewCurrency: ''
15 | previewFormat: decimal
16 | size: null
17 | suffix: null
18 | translationKeyFormat: null
19 | translationMethod: none
20 | type: craft\fields\Number
21 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/services/Helper.php:
--------------------------------------------------------------------------------
1 | safeLoad();
16 | }
17 |
18 | // Define additional PHP constants
19 | // (see https://craftcms.com/docs/3.x/config/#php-constants)
20 | define('CRAFT_ENVIRONMENT', getenv('ENVIRONMENT') ?: 'production');
21 |
--------------------------------------------------------------------------------
/cms/config/project/sections/orders--1f2ba12a-f04e-47d9-af2b-3e876c5adbce.yaml:
--------------------------------------------------------------------------------
1 | defaultPlacement: end
2 | enableVersioning: true
3 | handle: orders
4 | name: Orders
5 | previewTargets:
6 | -
7 | __assoc__:
8 | -
9 | - label
10 | - 'Primary entry page'
11 | -
12 | - urlFormat
13 | - '{url}'
14 | -
15 | - refresh
16 | - '1'
17 | propagationMethod: all
18 | siteSettings:
19 | 5da841b1-ca0d-46ff-8bb1-04d6c889ac54: # English
20 | enabledByDefault: true
21 | hasUrls: true
22 | template: orders/_entry
23 | uriFormat: 'orders/{slug}'
24 | type: channel
25 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/config.php:
--------------------------------------------------------------------------------
1 | App::env('DB_DSN') ?: null,
15 | 'driver' => 'mysql',
16 | 'server' => 'mysql',
17 | 'port' => 3306,
18 | 'database' => App::env('DB_DATABASE'),
19 | 'user' => App::env('DB_USER'),
20 | 'password' => App::env('DB_PASSWORD'),
21 | 'schema' => App::env('DB_SCHEMA'),
22 | 'tablePrefix' => App::env('DB_TABLE_PREFIX'),
23 | ];
24 |
--------------------------------------------------------------------------------
/cms/config/project/fields/colorsSwatches--ade97714-e4c4-4c73-9322-2c6b398cef04.yaml:
--------------------------------------------------------------------------------
1 | columnSuffix: null
2 | contentColumnType: string
3 | fieldGroup: 9265d2f0-4fa5-4187-8ee4-b6eb8808e295 # Pages
4 | handle: colorsSwatches
5 | instructions: 'Pick the color combination to use for the page theme'
6 | name: 'Colors Swatches'
7 | searchable: false
8 | settings:
9 | contentTable: '{{%matrixcontent_colorsswatches}}'
10 | inputTemplatePath: site-module/_components/matrixfacades/colorswatches/input
11 | maxBlocks: ''
12 | minBlocks: ''
13 | propagationKeyFormat: null
14 | propagationMethod: all
15 | translationKeyFormat: null
16 | translationMethod: site
17 | type: modules\sitemodule\fields\MatrixFacade
18 |
--------------------------------------------------------------------------------
/cms/example.env:
--------------------------------------------------------------------------------
1 | # Craft general settings
2 | ALLOW_UPDATES=1
3 | ALLOW_ADMIN_CHANGES=1
4 | BACKUP_ON_UPDATE=0
5 | DEV_MODE=1
6 | ENABLE_TEMPLATE_CACHING=0
7 | ENVIRONMENT=dev
8 | RUN_QUEUE_AUTOMATICALLY=0
9 | SECURITY_KEY=EOdiVBONceb8zFGJP7InMui2pMkvNACz
10 | SITE_NAME=matrixfacades
11 |
12 | # Craft database settings
13 | DB_USER=project
14 | DB_PASSWORD=project
15 | DB_DATABASE=project
16 | DB_SCHEMA=public
17 | DB_TABLE_PREFIX=
18 |
19 | # URL & path settings
20 | SITE_URL=http://localhost:8888
21 | WEB_ROOT_PATH=/var/www/project/cms/web
22 |
23 | # Redis settings
24 | REDIS_HOSTNAME=redis
25 | REDIS_PORT=6379
26 | REDIS_DEFAULT_DB=0
27 | REDIS_CRAFT_DB=1
28 |
29 | # Xdebug Settings
30 | DBGP_IDEKEY=phpstorm
31 |
--------------------------------------------------------------------------------
/cms/config/routes.php:
--------------------------------------------------------------------------------
1 | ' => ['template' => 'blog/_archive'],
15 | *
16 | * That example would match URIs such as `/blog/archive/2012`, and pass the
17 | * request along to the `blog/_archive` template, providing it a `year` variable
18 | * set to the value `2012`.
19 | */
20 |
21 | return [
22 | 'amp' => ['template' => 'amp-index'],
23 | 'api' => 'graphql/api',
24 | ];
25 |
--------------------------------------------------------------------------------
/cms/config/project/fields/flavorImage--682126f6-e053-4b1d-b707-82879cb43392.yaml:
--------------------------------------------------------------------------------
1 | columnSuffix: null
2 | contentColumnType: string
3 | fieldGroup: 86e92ff9-5119-4b99-b222-f3279dcc7818 # Flavors
4 | handle: flavorImage
5 | instructions: ''
6 | name: 'Flavor Image'
7 | searchable: false
8 | settings:
9 | allowSelfRelations: false
10 | allowUploads: true
11 | allowedKinds: null
12 | defaultUploadLocationSource: 'volume:5c642d7e-b16b-4836-9575-668d75d242e5' # Site
13 | defaultUploadLocationSubpath: ''
14 | limit: ''
15 | localizeRelations: false
16 | previewMode: full
17 | restrictFiles: ''
18 | selectionLabel: ''
19 | showSiteMenu: false
20 | showUnpermittedFiles: false
21 | showUnpermittedVolumes: false
22 | singleUploadLocationSource: 'volume:5c642d7e-b16b-4836-9575-668d75d242e5' # Site
23 | singleUploadLocationSubpath: ''
24 | source: null
25 | sources: '*'
26 | targetSiteId: null
27 | useSingleFolder: false
28 | validateRelatedElements: false
29 | viewMode: list
30 | translationKeyFormat: null
31 | translationMethod: site
32 | type: craft\fields\Assets
33 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2022 nystudio107
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Determine the docker compose API version to get the separator character
2 | VERSION?=$(shell docker-compose -v)
3 | ifneq (,$(findstring v2.,$(VERSION)))
4 | SEPARATOR:=-
5 | else
6 | SEPARATOR:=_
7 | endif
8 | CONTAINER?=$(shell basename $(CURDIR))$(SEPARATOR)php$(SEPARATOR)1
9 |
10 | .PHONY: dev clean composer craft nuke ssh up
11 |
12 | dev: up
13 | clean:
14 | rm -f cms/composer.lock
15 | rm -rf cms/vendor/
16 | composer: up
17 | docker exec -it $(CONTAINER) su-exec www-data composer \
18 | $(filter-out $@,$(MAKECMDGOALS))
19 | craft: up
20 | docker exec -it $(CONTAINER) su-exec www-data php craft \
21 | $(filter-out $@,$(MAKECMDGOALS))
22 | nuke:
23 | docker-compose down -v
24 | rm -f cms/composer.lock
25 | rm -rf cms/vendor/
26 | docker-compose up --build --force-recreate
27 | ssh: up
28 | docker exec -it $(CONTAINER) su-exec www-data /bin/sh
29 | up:
30 | if [ ! "$$(docker ps -q -f name=$(CONTAINER))" ]; then \
31 | cp -n cms/example.env cms/.env; \
32 | docker-compose up; \
33 | fi
34 | %:
35 | @:
36 | # ref: https://stackoverflow.com/questions/6273608/how-to-pass-argument-to-makefile-from-command-line
37 |
--------------------------------------------------------------------------------
/cms/config/project/volumes/site--5c642d7e-b16b-4836-9575-668d75d242e5.yaml:
--------------------------------------------------------------------------------
1 | fieldLayouts:
2 | e73b4186-2fad-4c0f-8eae-bbb884932c58:
3 | tabs:
4 | -
5 | elements:
6 | -
7 | autocapitalize: true
8 | autocomplete: false
9 | autocorrect: true
10 | class: null
11 | disabled: false
12 | id: null
13 | instructions: null
14 | label: null
15 | max: null
16 | min: null
17 | name: null
18 | orientation: null
19 | placeholder: null
20 | readonly: false
21 | requirable: false
22 | size: null
23 | step: null
24 | tip: null
25 | title: null
26 | type: craft\fieldlayoutelements\AssetTitleField
27 | warning: null
28 | width: 100
29 | name: Content
30 | sortOrder: 1
31 | handle: site
32 | hasUrls: true
33 | name: Site
34 | settings:
35 | path: '@webroot/assets/site'
36 | sortOrder: 0
37 | titleTranslationKeyFormat: null
38 | titleTranslationMethod: site
39 | type: craft\volumes\Local
40 | url: '@web/assets/site'
41 |
--------------------------------------------------------------------------------
/docker-config/php-prod-craft/composer_install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Composer Install shell script
4 | #
5 | # This shell script runs `composer install` if either the `composer.lock` file or
6 | # the `vendor/` directory is not present`
7 | #
8 | # @author nystudio107
9 | # @copyright Copyright (c) 2022 nystudio107
10 | # @link https://nystudio107.com/
11 | # @license MIT
12 |
13 | # Ensure permissions on directories Craft needs to write to
14 | chown -R www-data:www-data /var/www/project/cms/storage
15 | chown -R www-data:www-data /var/www/project/cms/web/cpresources
16 | # Check for `composer.lock` & `vendor/autoload.php`
17 | cd /var/www/project/cms
18 | if [ ! -f "composer.lock" ] || [ ! -f "vendor/autoload.php" ]; then
19 | su-exec www-data composer install --verbose --no-progress --no-scripts --optimize-autoloader --no-interaction
20 | # Wait until the MySQL db container responds
21 | echo "### Waiting for MySQL database"
22 | until eval "mysql -h mysql -u $DB_USER -p$DB_PASSWORD $DB_DATABASE -e 'select 1' > /dev/null 2>&1"
23 | do
24 | sleep 1
25 | done
26 | # Run any pending migrations/project config changes
27 | su-exec www-data composer craft-update
28 | fi
29 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/assetbundles/sitemodule/SiteModuleAsset.php:
--------------------------------------------------------------------------------
1 | sourcePath = "@modules/sitemodule/assetbundles/sitemodule/dist";
33 |
34 | $this->depends = [
35 | CpAsset::class,
36 | ];
37 |
38 | $this->js = [
39 | 'js/SiteModule.js',
40 | ];
41 |
42 | $this->css = [
43 | 'css/SiteModule.css',
44 | ];
45 |
46 | parent::init();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/cms/web/web.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/cms/config/app.web.php:
--------------------------------------------------------------------------------
1 | [
22 | 'session' => static function() {
23 | // Get the default component config
24 | $config = App::sessionConfig();
25 | // Override the class to use Redis' session class and our config settings
26 | $config['class'] = yii\redis\Session::class;
27 | $config['keyPrefix'] = App::env('APP_ID') ?: 'CraftCMS';
28 | $config['redis'] = [
29 | 'hostname' => App::env('REDIS_HOSTNAME'),
30 | 'port' => App::env('REDIS_PORT'),
31 | 'database' => App::env('REDIS_DEFAULT_DB'),
32 | ];
33 | // Instantiate and return it
34 | return Craft::createObject($config);
35 | },
36 | ],
37 | ];
38 |
--------------------------------------------------------------------------------
/docker-config/php-prod-craft/run_queue.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Run Queue shell script
4 | #
5 | # This shell script runs the Craft CMS queue via `php craft queue/listen`
6 | # It waits until the database container responds, then runs any pending
7 | # migrations / project config changes via the `craft-update` Composer script,
8 | # then runs the queue listener that listens for and runs pending queue jobs
9 | #
10 | # @author nystudio107
11 | # @copyright Copyright (c) 2022 nystudio107
12 | # @link https://nystudio107.com/
13 | # @license MIT
14 |
15 | cd /var/www/project/cms
16 | # Wait until the MySQL db container responds
17 | echo "### Waiting for MySQL database"
18 | until eval "mysql -h mysql -u $DB_USER -p$DB_PASSWORD $DB_DATABASE -e 'select 1' > /dev/null 2>&1"
19 | do
20 | sleep 1
21 | done
22 | # Wait until the `composer install` is done by looking for the `vendor/autoload.php` and `composer.lock` files
23 | echo "### Waiting for vendor/autoload.php"
24 | while [ ! -f "vendor/autoload.php" ] || [ ! -f "composer.lock" ]
25 | do
26 | sleep 1
27 | done
28 | # Ensure permissions on directories Craft needs to write to
29 | chown -R www-data:www-data /var/www/project/cms/storage
30 | chown -R www-data:www-data /var/www/project/cms/web/cpresources
31 | # Run any pending migrations/project config changes
32 | su-exec www-data composer craft-update
33 | # Run a queue listener
34 | su-exec www-data php craft queue/listen 10
35 |
--------------------------------------------------------------------------------
/cms/config/project/entryTypes/default--d2e0ba28-01c7-4060-8087-0c12e978341b.yaml:
--------------------------------------------------------------------------------
1 | fieldLayouts:
2 | f9551994-37fd-4761-95f0-9e5083121608:
3 | tabs:
4 | -
5 | elements:
6 | -
7 | autocapitalize: true
8 | autocomplete: false
9 | autocorrect: true
10 | class: null
11 | disabled: false
12 | id: null
13 | instructions: null
14 | label: null
15 | max: null
16 | min: null
17 | name: null
18 | orientation: null
19 | placeholder: null
20 | readonly: false
21 | requirable: false
22 | size: null
23 | step: null
24 | tip: null
25 | title: null
26 | type: craft\fieldlayoutelements\EntryTitleField
27 | warning: null
28 | width: 100
29 | -
30 | fieldUid: c6fb33da-e3f0-4f50-a4c5-4cce12415e80 # Scoops
31 | instructions: null
32 | label: null
33 | required: false
34 | tip: null
35 | type: craft\fieldlayoutelements\CustomField
36 | warning: null
37 | width: 100
38 | name: Content
39 | sortOrder: 1
40 | handle: default
41 | hasTitleField: true
42 | name: Default
43 | section: 1f2ba12a-f04e-47d9-af2b-3e876c5adbce # Orders
44 | sortOrder: 1
45 | titleFormat: null
46 | titleTranslationKeyFormat: null
47 | titleTranslationMethod: site
48 |
--------------------------------------------------------------------------------
/cms/config/project/entryTypes/default--46862260-8c13-44f7-9a4e-a3cb6e1a22b3.yaml:
--------------------------------------------------------------------------------
1 | fieldLayouts:
2 | 539f02dd-1bc6-43f1-ad1b-db44ad1bf0d2:
3 | tabs:
4 | -
5 | elements:
6 | -
7 | autocapitalize: true
8 | autocomplete: false
9 | autocorrect: true
10 | class: null
11 | disabled: false
12 | id: null
13 | instructions: null
14 | label: null
15 | max: null
16 | min: null
17 | name: null
18 | orientation: null
19 | placeholder: null
20 | readonly: false
21 | requirable: false
22 | size: null
23 | step: null
24 | tip: null
25 | title: null
26 | type: craft\fieldlayoutelements\EntryTitleField
27 | warning: null
28 | width: 100
29 | -
30 | fieldUid: ade97714-e4c4-4c73-9322-2c6b398cef04 # Colors Swatches
31 | instructions: null
32 | label: null
33 | required: false
34 | tip: null
35 | type: craft\fieldlayoutelements\CustomField
36 | warning: null
37 | width: 100
38 | name: Content
39 | sortOrder: 1
40 | handle: default
41 | hasTitleField: true
42 | name: Default
43 | section: 7c3fb4f3-2ba5-4834-9056-6712aa5c18f4 # Pages
44 | sortOrder: 1
45 | titleFormat: null
46 | titleTranslationKeyFormat: null
47 | titleTranslationMethod: site
48 |
--------------------------------------------------------------------------------
/cms/config/general.php:
--------------------------------------------------------------------------------
1 | [
16 | '@web' => App::env('SITE_URL'),
17 | '@webroot' => App::env('WEB_ROOT_PATH'),
18 | ],
19 | 'allowUpdates' => (bool)App::env('ALLOW_UPDATES'),
20 | 'allowAdminChanges' => (bool)App::env('ALLOW_ADMIN_CHANGES'),
21 | 'backupOnUpdate' => (bool)App::env('BACKUP_ON_UPDATE'),
22 | 'devMode' => (bool)App::env('DEV_MODE'),
23 | 'enableTemplateCaching' => (bool)App::env('ENABLE_TEMPLATE_CACHING'),
24 | 'resourceBasePath' => App::env('WEB_ROOT_PATH') . '/cpresources',
25 | 'runQueueAutomatically' => (bool)App::env('RUN_QUEUE_AUTOMATICALLY'),
26 | 'securityKey' => App::env('SECURITY_KEY'),
27 | // Craft config settings from constants
28 | 'cacheDuration' => false,
29 | 'defaultSearchTermOptions' => [
30 | 'subLeft' => true,
31 | 'subRight' => true,
32 | ],
33 | 'defaultTokenDuration' => 'P2W',
34 | 'enableCsrfProtection' => true,
35 | 'generateTransformsBeforePageLoad' => true,
36 | 'maxCachedCloudImageSize' => 3000,
37 | 'maxUploadFileSize' => '100M',
38 | 'omitScriptNameInUrls' => true,
39 | 'useEmailAsUsername' => false,
40 | 'usePathInfo' => true,
41 | 'useProjectConfigFile' => true,
42 | ];
43 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/README.md:
--------------------------------------------------------------------------------
1 | # Site module for Craft CMS 3.x
2 |
3 | An example module for Craft CMS 3 that lets you enhance your websites with a custom site module
4 |
5 | ## Requirements
6 |
7 | This module requires Craft CMS 3.0.0-RC1 or later.
8 |
9 | ## Installation
10 |
11 | To install the module, follow these instructions.
12 |
13 | First, you'll need to add the contents of the `app.php` file to your `config/app.php` (or just copy it there if it does not exist). This ensures that your module will get loaded for each request. The file might look something like this:
14 | ```
15 | return [
16 | 'modules' => [
17 | 'site-module' => [
18 | 'class' => \modules\sitemodule\SiteModule::class,
19 | ],
20 | ],
21 | 'bootstrap' => ['site-module'],
22 | ];
23 | ```
24 | You'll also need to make sure that you add the following to your project's `composer.json` file so that Composer can find your module:
25 |
26 | "autoload": {
27 | "psr-4": {
28 | "modules\\sitemodule\\": "modules/sitemodule/src/"
29 | }
30 | },
31 |
32 | After you have added this, you will need to do:
33 |
34 | composer dump-autoload
35 |
36 | …from the project’s root directory, to rebuild the Composer autoload map. This will happen automatically any time you do a `composer install` or `composer update` as well.
37 |
38 | ## Site Overview
39 |
40 | -Insert text here-
41 |
42 | ## Using Site
43 |
44 | -Insert text here-
45 |
46 | ## Site Roadmap
47 |
48 | Some things to do, and ideas for potential features:
49 |
50 | * Release it
51 |
52 | Brought to you by [nystudio107](https://nystudio107.com/)
53 |
--------------------------------------------------------------------------------
/cms/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nystudio107/matrixfacades",
3 | "description": "This is a project demonstrates how to improve authoring experience Matrix Façades.",
4 | "require": {
5 | "craftcms/cms": "^3.7.0",
6 | "nystudio107/craft-autocomplete": "^1.0.0",
7 | "vlucas/phpdotenv": "^5.4.0",
8 | "yiisoft/yii2-redis": "^2.0.6"
9 | },
10 | "require-dev": {
11 | "yiisoft/yii2-shell": "^2.0.3"
12 | },
13 | "autoload": {
14 | "psr-4": {
15 | "modules\\sitemodule\\": "modules/sitemodule/src/"
16 | }
17 | },
18 | "config": {
19 | "allow-plugins": {
20 | "craftcms/plugin-installer": true,
21 | "yiisoft/yii2-composer": true
22 | },
23 | "optimize-autoloader": true,
24 | "sort-packages": true,
25 | "platform": {
26 | "php": "7.2.5"
27 | }
28 | },
29 | "scripts": {
30 | "craft-update": [
31 | "@pre-craft-update",
32 | "@post-craft-update"
33 | ],
34 | "pre-craft-update": [
35 | ],
36 | "post-craft-update": [
37 | "Composer\\Config::disableProcessTimeout",
38 | "@php craft install/check && php craft clear-caches/all --interactive=0 || exit 0",
39 | "@php craft install/check && php craft migrate/all --interactive=0 || exit 0",
40 | "@php craft install/check && php craft project-config/apply --interactive=0 || exit 0"
41 | ],
42 | "post-root-package-install": [
43 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
44 | ],
45 | "post-create-project-cmd": [
46 | "@php craft setup/welcome"
47 | ],
48 | "pre-update-cmd": "@pre-craft-update",
49 | "pre-install-cmd": "@pre-craft-update",
50 | "post-update-cmd": "@post-craft-update",
51 | "post-install-cmd": "@post-craft-update"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/docker-config/php-dev-craft/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nystudio107/php-dev-base:7.4-alpine
2 |
3 | # dependencies required for running "phpize"
4 | # these get automatically installed and removed by "docker-php-ext-*" (unless they're already installed)
5 | ENV PHPIZE_DEPS \
6 | autoconf \
7 | dpkg-dev \
8 | dpkg \
9 | file \
10 | g++ \
11 | gcc \
12 | libc-dev \
13 | make \
14 | pkgconf \
15 | re2c \
16 | wget
17 |
18 | # Install packages
19 | RUN set -eux; \
20 | # Packages needed only for build
21 | apk add --no-cache --virtual .build-deps \
22 | $PHPIZE_DEPS \
23 | && \
24 | # Packages to install
25 | apk add --no-cache \
26 | su-exec \
27 | gifsicle \
28 | jpegoptim \
29 | libwebp-tools \
30 | nano \
31 | optipng \
32 | mysql-client \
33 | mariadb-connector-c \
34 | postgresql-client \
35 | postgresql-dev \
36 | && \
37 | # Install PHP extensions
38 | docker-php-ext-install \
39 | pdo_mysql \
40 | pdo_pgsql \
41 | pgsql \
42 | soap \
43 | && \
44 | # Install Composer
45 | curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin/ --filename=composer \
46 | && \
47 | # Remove the build deps
48 | apk del .build-deps \
49 | && \
50 | # Clean out directories that don't need to be part of the image
51 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
52 |
53 | WORKDIR /var/www/project
54 |
55 | RUN mkdir -p /var/www/project/cms/storage \
56 | && \
57 | mkdir -p /var/www/project/cms/web/cpresources \
58 | && \
59 | chown -R www-data:www-data /var/www/project
60 |
61 | WORKDIR /var/www/project/cms
62 |
63 | # Start php-fpm
64 | CMD php-fpm
65 |
--------------------------------------------------------------------------------
/cms/config/app.php:
--------------------------------------------------------------------------------
1 | App::env('APP_ID') ?: 'CraftCMS',
25 | 'modules' => [
26 | 'site-module' => [
27 | 'class' => SiteModule::class,
28 | ],
29 | ],
30 | 'bootstrap' => ['site-module'],
31 | 'components' => [
32 | 'cache' => [
33 | 'class' => yii\redis\Cache::class,
34 | 'keyPrefix' => App::env('APP_ID') ?: 'CraftCMS',
35 | 'redis' => [
36 | 'hostname' => App::env('REDIS_HOSTNAME'),
37 | 'port' => App::env('REDIS_PORT'),
38 | 'database' => App::env('REDIS_CRAFT_DB'),
39 | ],
40 | ],
41 | 'deprecator' => [
42 | 'throwExceptions' => true,
43 | ],
44 | 'queue' => [
45 | 'class' => craft\queue\Queue::class,
46 | 'ttr' => 10 * 60,
47 | ],
48 | 'redis' => [
49 | 'class' => yii\redis\Connection::class,
50 | 'hostname' => App::env('REDIS_HOSTNAME'),
51 | 'port' => App::env('REDIS_PORT'),
52 | 'database' => App::env('REDIS_DEFAULT_DB'),
53 | ],
54 | ],
55 | ];
56 |
--------------------------------------------------------------------------------
/docker-config/nginx/default.conf:
--------------------------------------------------------------------------------
1 | # default Docker DNS server
2 | resolver 127.0.0.11;
3 |
4 | # If a cookie doesn't exist, it evaluates to an empty string, so if neither cookie exists, it'll match :
5 | # (empty string on either side of the :), but if either or both cookies are set, it won't match, and will hit the default rule
6 | map $cookie_XDEBUG_SESSION:$cookie_XDEBUG_PROFILE $my_fastcgi_pass {
7 | default php_xdebug;
8 | ':' php;
9 | }
10 |
11 | server {
12 | listen 80;
13 | listen [::]:80;
14 |
15 | server_name _;
16 | root /var/www/project/cms/web;
17 | index index.html index.htm index.php;
18 | charset utf-8;
19 |
20 | gzip_static on;
21 |
22 | ssi on;
23 |
24 | client_max_body_size 0;
25 |
26 | error_page 404 /index.php?$query_string;
27 |
28 | access_log off;
29 | error_log /dev/stdout info;
30 |
31 | location = /favicon.ico { access_log off; log_not_found off; }
32 |
33 | location / {
34 | try_files $uri/index.html $uri $uri/ /index.php?$query_string;
35 | }
36 |
37 | location ~ [^/]\.php(/|$) {
38 | try_files $uri $uri/ /index.php?$query_string;
39 | fastcgi_split_path_info ^(.+\.php)(/.+)$;
40 | fastcgi_pass $my_fastcgi_pass:9000;
41 | fastcgi_index index.php;
42 | include fastcgi_params;
43 | fastcgi_param PATH_INFO $fastcgi_path_info;
44 | fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
45 | fastcgi_param DOCUMENT_ROOT $realpath_root;
46 | fastcgi_param HTTP_PROXY "";
47 |
48 | add_header Last-Modified $date_gmt;
49 | add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
50 | if_modified_since off;
51 | expires off;
52 | etag off;
53 |
54 | fastcgi_intercept_errors off;
55 | fastcgi_buffer_size 16k;
56 | fastcgi_buffers 4 16k;
57 | fastcgi_connect_timeout 300;
58 | fastcgi_send_timeout 300;
59 | fastcgi_read_timeout 300;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/assetbundles/sitemodule/dist/img/SiteModule-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
14 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/cms/config/project/entryTypes/default--88ffed5d-fece-4914-9e1a-a275577840d3.yaml:
--------------------------------------------------------------------------------
1 | fieldLayouts:
2 | 0e78f5fb-bf35-4e56-9189-7aa945434937:
3 | tabs:
4 | -
5 | elements:
6 | -
7 | autocapitalize: true
8 | autocomplete: false
9 | autocorrect: true
10 | class: null
11 | disabled: false
12 | id: null
13 | instructions: null
14 | label: null
15 | max: null
16 | min: null
17 | name: null
18 | orientation: null
19 | placeholder: null
20 | readonly: false
21 | requirable: false
22 | size: null
23 | step: null
24 | tip: null
25 | title: null
26 | type: craft\fieldlayoutelements\EntryTitleField
27 | warning: null
28 | width: 100
29 | -
30 | fieldUid: 682126f6-e053-4b1d-b707-82879cb43392 # Flavor Image
31 | instructions: null
32 | label: null
33 | required: false
34 | tip: null
35 | type: craft\fieldlayoutelements\CustomField
36 | warning: null
37 | width: 100
38 | -
39 | fieldUid: 2b2ed478-8e77-4c39-a69a-56ad864557d1 # Flavor Calories
40 | instructions: null
41 | label: null
42 | required: false
43 | tip: null
44 | type: craft\fieldlayoutelements\CustomField
45 | warning: null
46 | width: 100
47 | -
48 | fieldUid: 3d7dba4f-8e70-4965-acec-4e70f830a016 # Flavor Price
49 | instructions: null
50 | label: null
51 | required: false
52 | tip: null
53 | type: craft\fieldlayoutelements\CustomField
54 | warning: null
55 | width: 100
56 | name: Content
57 | sortOrder: 1
58 | handle: default
59 | hasTitleField: true
60 | name: Default
61 | section: 10571998-751e-449a-98c5-d48adf32034b # Flavors
62 | sortOrder: 1
63 | titleFormat: null
64 | titleTranslationKeyFormat: null
65 | titleTranslationMethod: site
66 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # nystudio107/matrixfacades Change Log
2 |
3 | ## 1.0.11 - 2022.05.03
4 | ### Changed
5 | * Use `docker-compose` to test the version, rather than the newer `docker compose`, to be backwards compatible
6 |
7 | ## 1.0.10 - 2022.05.03
8 | ### Changed
9 | * Determine the separator character by checking the Docker Compose API version at runtime
10 |
11 | ## 1.0.9 - 2022.05.02
12 | ### Changed
13 | * Use `su-exec` for the `craft`, `composer`, and `ssh` commands from the Makefile
14 |
15 | ## 1.0.8 - 2022.05.02
16 | ### Fixed
17 | * Clean up initial check for `composer.lock` or `vendor/autoload.php` to ensure the first-time install is done
18 |
19 | ## 1.0.7 - 2022.04.22
20 | ### Fixed
21 | * Ensure that the [mysql/mysql-server](https://hub.docker.com/r/mysql/mysql-server/tags) image can pick from the ARM64 architecture by using `latest` tag
22 |
23 | ## 1.0.6 - 2022.03.26
24 | ### Added
25 | * Added more logging to indicate when a container is waiting for another service to start up, and when seeding a database is complete
26 | * Run `composer craft-update` after a `composer install` is done via `composer_install.sh`
27 |
28 | ### Changed
29 | * Moved permissions setting to Docker image creation
30 |
31 | ## 1.0.5 - 2022.03.26
32 | ### Changed
33 | * Updated to Craft CMS 3.7.37
34 |
35 | ## 1.0.4 - 2022.03.26
36 | ### Added
37 | * Dramatically sped up the startup time for the PHP containers by moving the permissions setting script to run asynchronously in the queue container via the `run_queue.sh` script
38 |
39 | ## 1.0.3 - 2022.03.17
40 |
41 | ### Added
42 | * Significantly increased startup times via a `composer_install.sh` script that only runs `composer install` at container startup time if `composer.lock` or `vendor/` is missing
43 | * Run migrations / project config changes via the `run_queue.sh` script, only after the db container responds
44 |
45 | ## 1.0.2 - 2022.02.11
46 |
47 | ### Changed
48 |
49 | * Minor tweaks to the config so it can run with other local dev environments still running
50 |
51 | ## 1.0.1 - 2022.01.22
52 |
53 | ### Added
54 |
55 | * Added a new "Color Swatches" Matrix Façade and the "Landing Pages" channel to show off how it works
56 |
57 | ## 1.0.0 - 2022.01.19
58 |
59 | ### Added
60 |
61 | * Initial release
62 |
63 | Brought to you by [nystudio107](https://nystudio107.com/)
64 |
--------------------------------------------------------------------------------
/cms/config/project/project.yaml:
--------------------------------------------------------------------------------
1 | dateModified: 1648326190
2 | email:
3 | fromEmail: andrew@nystudio107.com
4 | fromName: Craft
5 | transportType: craft\mail\transportadapters\Sendmail
6 | meta:
7 | __names__:
8 | 1aeaf5aa-fe37-42b5-aef9-f4556b92c43a: User # User
9 | 1f2ba12a-f04e-47d9-af2b-3e876c5adbce: Orders # Orders
10 | 1f716763-7f9e-4ffd-9f8c-2c0e7ccf15f7: Selected # Selected
11 | 2b2ed478-8e77-4c39-a69a-56ad864557d1: 'Flavor Calories' # Flavor Calories
12 | 3d7dba4f-8e70-4965-acec-4e70f830a016: 'Flavor Price' # Flavor Price
13 | 5c642d7e-b16b-4836-9575-668d75d242e5: Site # Site
14 | 5da841b1-ca0d-46ff-8bb1-04d6c889ac54: English # English
15 | 7a591e66-18df-490d-92b5-3fd3a3986f09: default # default
16 | 7c3fb4f3-2ba5-4834-9056-6712aa5c18f4: Pages # Pages
17 | 8f804a45-ad1d-4a71-b0b4-6df2040dc205: Nuts # Nuts
18 | 071aeb2b-e8eb-4bcf-825c-dd016745f107: Flavor # Flavor
19 | 86e92ff9-5119-4b99-b222-f3279dcc7818: Flavors # Flavors
20 | 88ffed5d-fece-4914-9e1a-a275577840d3: Default # Default
21 | 858b290d-45ae-4da8-8e51-2c03caefeee3: 'Disable Matrix Facades' # Disable Matrix Facades
22 | 4828c563-32cf-4021-afeb-1d5e38f31daf: 'Accent Color' # Accent Color
23 | 6005c2f9-5d85-4442-b712-22e070096ac8: 'Public Schema' # Public Schema
24 | 6359fe58-db81-4ee6-94eb-e9f4e3c61da6: Notes # Notes
25 | 9265d2f0-4fa5-4187-8ee4-b6eb8808e295: Pages # Pages
26 | 92425d88-9e0a-4213-b474-f553f37261c2: Orders # Orders
27 | 682126f6-e053-4b1d-b707-82879cb43392: 'Flavor Image' # Flavor Image
28 | 10571998-751e-449a-98c5-d48adf32034b: Flavors # Flavors
29 | 46862260-8c13-44f7-9a4e-a3cb6e1a22b3: Default # Default
30 | ade97714-e4c4-4c73-9322-2c6b398cef04: 'Colors Swatches' # Colors Swatches
31 | c2e2b69d-127d-4afe-8032-0209247e70de: default # default
32 | c6fb33da-e3f0-4f50-a4c5-4cce12415e80: Scoops # Scoops
33 | d2e0ba28-01c7-4060-8087-0c12e978341b: Default # Default
34 | dd87a305-fb8e-4bc0-965b-8afadc876934: Syrup # Syrup
35 | e7500fc4-6a5e-4656-8947-27c47c0c9dd8: Whip # Whip
36 | f652db8c-edc2-4a66-8241-2fb0787110ee: 'Primary Color' # Primary Color
37 | f89601e9-4ba9-4a48-9e99-350aa9914912: default # default
38 | system:
39 | edition: solo
40 | live: true
41 | name: matrixfacades
42 | retryDuration: 60
43 | schemaVersion: 3.7.33
44 | timeZone: America/New_York
45 |
--------------------------------------------------------------------------------
/docker-config/php-prod-craft/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nystudio107/php-prod-base:7.4-alpine
2 |
3 | # dependencies required for running "phpize"
4 | # these get automatically installed and removed by "docker-php-ext-*" (unless they're already installed)
5 | ENV PHPIZE_DEPS \
6 | autoconf \
7 | dpkg-dev \
8 | dpkg \
9 | file \
10 | g++ \
11 | gcc \
12 | libc-dev \
13 | make \
14 | pkgconf \
15 | re2c \
16 | wget
17 |
18 | # Install packages
19 | RUN set -eux; \
20 | # Packages needed only for build
21 | apk add --no-cache --virtual .build-deps \
22 | $PHPIZE_DEPS \
23 | && \
24 | # Packages to install
25 | apk add --no-cache \
26 | su-exec \
27 | gifsicle \
28 | jpegoptim \
29 | libwebp-tools \
30 | nano \
31 | optipng \
32 | mysql-client \
33 | mariadb-connector-c \
34 | postgresql-client \
35 | postgresql-dev \
36 | && \
37 | # Install PHP extensions
38 | docker-php-ext-install \
39 | pdo_mysql \
40 | pdo_pgsql \
41 | pgsql \
42 | soap \
43 | && \
44 | # Install Composer
45 | curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin/ --filename=composer \
46 | && \
47 | # Remove the build deps
48 | apk del .build-deps \
49 | && \
50 | # Clean out directories that don't need to be part of the image
51 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
52 |
53 | WORKDIR /var/www/project
54 |
55 | COPY ./run_queue.sh .
56 | RUN chmod a+x run_queue.sh \
57 | && \
58 | mkdir -p /var/www/project/cms/storage \
59 | && \
60 | mkdir -p /var/www/project/cms/web/cpresources \
61 | && \
62 | chown -R www-data:www-data /var/www/project
63 | COPY ./composer_install.sh .
64 | RUN chmod a+x composer_install.sh
65 |
66 | WORKDIR /var/www/project/cms
67 |
68 | # Run the composer_install.sh script that will do a `composer install`:
69 | # - If `composer.lock` is missing
70 | # - If `vendor/` is missing
71 | # ...then start up php-fpm. The `run_queue.sh` script in the queue container
72 | # will take care of running any pending migrations and apply any Project Config changes,
73 | # as well as set permissions via an async CLI process
74 | CMD /var/www/project/composer_install.sh \
75 | && \
76 | php-fpm
77 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/helpers/Config.php:
--------------------------------------------------------------------------------
1 | getConfig()->env;
44 |
45 | // Try craft/config first
46 | $path = Craft::getAlias('@config/'.$fileName, false);
47 | if ($path === false || !file_exists($path)) {
48 | // Now try our own internal config
49 | $path = Craft::getAlias('@modules/sitemodule/'.$fileName, false);
50 | if ($path === false || !file_exists($path)) {
51 | return [];
52 | }
53 | }
54 |
55 | if (!is_array($config = @include $path)) {
56 | return [];
57 | }
58 |
59 | // If it's not a multi-environment config, return the whole thing
60 | if (!array_key_exists('*', $config)) {
61 | return $config;
62 | }
63 |
64 | // If no environment was specified, just look in the '*' array
65 | if ($currentEnv === null) {
66 | return $config['*'];
67 | }
68 |
69 | $mergedConfig = [];
70 | foreach ($config as $env => $envConfig) {
71 | if ($env === '*' || StringHelper::contains($currentEnv, $env)) {
72 | $mergedConfig = ArrayHelper::merge($mergedConfig, $envConfig);
73 | }
74 | }
75 |
76 | return $mergedConfig;
77 | }
78 |
79 | // Private Methods
80 | // =========================================================================
81 | }
82 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/templates/_components/matrixfacades/scoops/tablecell.html:
--------------------------------------------------------------------------------
1 | {# Variables passed in:
2 | #
3 | # @var string name the namespaced name of the field being rendered
4 | # @var boolean static whether this is a static or editable table
5 | # @var array col the column that the tablecell is in
6 | # @var mixed value the value of the table cell being rendered
7 | #
8 | # Also any parent variables from the parent editableTable Craft include will be present
9 | #}
10 | {% import '_includes/forms.html' as forms %}
11 |
12 | {%- switch col.type -%}
13 | {%- case 'flavorPicker' -%}
14 | {{ hiddenInput("#{name}[sortOrder][]", value.blockId) }}
15 | {% namespace "#{name}[blocks][#{value.blockId}]" %}
16 | {{ hiddenInput('type', value.blockType) }}
17 | {{ hiddenInput('enabled', 1) }}
18 | {% endnamespace %}
19 | {% set elementSelectConfig = {
20 | id: 'flavor',
21 | name: 'flavor',
22 | fieldClass: 'last',
23 | elements: [],
24 | elementType: value.elementType,
25 | selectionLabel: 'Select Scoop'|t('site-module'),
26 | sources: value.elementSources,
27 | limit: 1,
28 | } %}
29 | {% namespace "#{name}[blocks][#{value.blockId}][fields]" %}
30 | {% if value.flavor %}
31 | {% set elementSelectConfig = elementSelectConfig | merge({
32 | elements: [craft.entries().id(value.flavor.id).one()],
33 | }) %}
34 | {% set flavorImage = value.flavor.flavorImage.one() ?? null %}
35 | {% if flavorImage %}
36 |
37 | {% endif %}
38 | {% endif %}
39 |
40 | {{ forms.elementSelectfield(elementSelectConfig) }}
41 |
42 | {% endnamespace %}
43 | {%- case 'text' -%}
44 | {{ value }}
45 | {%- case 'lightswitch' -%}
46 | {% namespace "#{name}[blocks][#{value.blockId}][fields]" %}
47 | {{ forms.lightswitch({
48 | name: value.id,
49 | value: value.id,
50 | on: value.on,
51 | disabled: static,
52 | }) }}
53 | {% endnamespace %}
54 | {%- case 'editabletext' -%}
55 | {% namespace "#{name}[blocks][#{value.blockId}][fields]" %}
56 | {{ forms.text({
57 | name: value.id,
58 | value: value.text,
59 | disabled: static,
60 | }) }}
61 | {% endnamespace %}
62 | {%- default -%}
63 | {%- endswitch -%}
64 |
65 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.7'
2 |
3 | services:
4 | # nginx - web server
5 | nginx:
6 | build:
7 | context: ./docker-config/nginx
8 | dockerfile: ./Dockerfile
9 | env_file: &env
10 | - ./cms/.env
11 | init: true
12 | ports:
13 | - "8888:80"
14 | volumes:
15 | - cpresources:/var/www/project/cms/web/cpresources:delegated
16 | - ./cms/web:/var/www/project/cms/web:cached
17 | # php - run php-fpm
18 | php:
19 | build: &php-build
20 | context: ./docker-config/php-prod-craft
21 | dockerfile: ./Dockerfile
22 | depends_on:
23 | - "mysql"
24 | - "redis"
25 | env_file:
26 | *env
27 | init: true
28 | tty: true
29 | volumes: &php-volumes
30 | - cpresources:/var/www/project/cms/web/cpresources:delegated
31 | - storage:/var/www/project/cms/storage:delegated
32 | - ./cms:/var/www/project/cms:cached
33 | # Specific directories that need to be bind-mounted
34 | - ./cms/storage/logs:/var/www/project/cms/storage/logs:delegated
35 | - ./cms/storage/runtime/compiled_templates:/var/www/project/cms/storage/runtime/compiled_templates:delegated
36 | - ./cms/storage/runtime/compiled_classes:/var/www/project/cms/storage/runtime/compiled_classes:delegated
37 | - ./cms/vendor:/var/www/project/cms/vendor:delegated
38 | - ./cms/storage/rebrand:/var/www/project/cms/storage/rebrand:delegated
39 | # php - run php-fpm with xdebug
40 | php_xdebug:
41 | build:
42 | context: ./docker-config/php-dev-craft
43 | dockerfile: ./Dockerfile
44 | depends_on:
45 | - "php"
46 | env_file:
47 | *env
48 | init: true
49 | tty: true
50 | volumes:
51 | *php-volumes
52 | # queue - runs queue jobs via php craft queue/listen
53 | queue:
54 | build:
55 | *php-build
56 | command: /var/www/project/run_queue.sh
57 | depends_on:
58 | - "php"
59 | env_file:
60 | *env
61 | init: true
62 | tty: true
63 | volumes:
64 | *php-volumes
65 | # mysql - database
66 | mysql:
67 | build:
68 | context: ./docker-config/mysql
69 | dockerfile: ./Dockerfile
70 | cap_add:
71 | - SYS_NICE # CAP_SYS_NICE
72 | env_file:
73 | *env
74 | environment:
75 | MYSQL_ROOT_PASSWORD: secret
76 | MYSQL_DATABASE: project
77 | MYSQL_USER: project
78 | MYSQL_PASSWORD: project
79 | init: true
80 | expose:
81 | - "3306"
82 | volumes:
83 | - db-data:/var/lib/mysql
84 | - ./db-seed:/docker-entrypoint-initdb.d
85 | # redis - key/value database for caching & php sessions
86 | redis:
87 | build:
88 | context: ./docker-config/redis
89 | dockerfile: ./Dockerfile
90 | expose:
91 | - "6379"
92 | init: true
93 |
94 | volumes:
95 | db-data:
96 | cpresources:
97 | storage:
98 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/templates/_components/matrixfacades/colorswatches/input.html:
--------------------------------------------------------------------------------
1 | {# Global variables available in all Twig blocks
2 | #
3 | # @var string id the normalized id of the Matrix field
4 | # @var string name handle of the Matrix field
5 | # @var \craft\elements\MatrixBlock[] blocks the MatrixBlock elements in the Matrix field
6 | # @var bool static whether the field is static or editable
7 | # @var \craft\base\ElementInterface element the parent element that contains the Matrix block
8 | #
9 | # The namespace will be set appropriately for the field
10 | #}
11 |
12 |
32 |
33 | {% set containerId = "#{name}-container" %}
34 |
35 | {% for block in blocks %}
36 | {% set swatchClass = "swatch-background" %}
37 | {% if block.selected %}
38 | {% set swatchClass = swatchClass ~ " swatch-background-selected" %}
39 | {% endif %}
40 | {{ hiddenInput("#{name}[sortOrder][]", block.id) }}
41 | {% namespace "#{name}[blocks][#{block.id}]" %}
42 | {{ hiddenInput('type', block.type) }}
43 | {{ hiddenInput('enabled', 1) }}
44 | {% endnamespace %}
45 |
46 | {% namespace "#{name}[blocks][#{block.id}][fields]" %}
47 | {{ hiddenInput('selected', block.selected) }}
48 | {% endnamespace %}
49 |
50 |
51 |
52 | {% endfor %}
53 |
54 |
55 |
77 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/behaviors/CpVariableBehavior.php:
--------------------------------------------------------------------------------
1 | getView()->getCpTemplateRoots();
32 | foreach($roots as $handle => $paths) {
33 | foreach ($paths as $path) {
34 | $suggestions = array_merge($suggestions, $this->getTemplateSuggestions($handle, $path));
35 | }
36 | }
37 |
38 | return [
39 | [
40 | 'label' => Craft::t('app', 'Templates'),
41 | 'data' => $suggestions,
42 | ],
43 | ];
44 | }
45 |
46 | /**
47 | * Returns the available template path suggestions for passed in $root directory.
48 | *
49 | * @return string[]
50 | */
51 | private function getTemplateSuggestions(string $handle, string $root): array
52 | {
53 | if (!is_dir($root)) {
54 | return [];
55 | }
56 |
57 | $directory = new \RecursiveDirectoryIterator($root);
58 |
59 | $filter = new \RecursiveCallbackFilterIterator($directory, function($current) {
60 | // Skip hidden files and directories, as well as node_modules/ folders
61 | if ($current->getFilename()[0] === '.' || $current->getFilename() === 'node_modules') {
62 | return false;
63 | }
64 | return true;
65 | });
66 |
67 | $iterator = new \RecursiveIteratorIterator($filter);
68 | /** @var \SplFileInfo[] $files */
69 | $files = [];
70 | $pathLengths = [];
71 |
72 | foreach ($iterator as $file) {
73 | /** @var \SplFileInfo $file */
74 | if (!$file->isDir() && $file->getFilename()[0] !== '.') {
75 | $files[] = $file;
76 | $pathLengths[] = strlen($file->getRealPath());
77 | }
78 | }
79 |
80 | array_multisort($pathLengths, SORT_NUMERIC, $files);
81 |
82 | // Now build the suggestions array
83 | $suggestions = [];
84 | $templates = [];
85 | $config = Craft::$app->getConfig()->getGeneral();
86 | $rootLength = strlen($root);
87 |
88 | foreach ($files as $file) {
89 | $template = substr($file->getRealPath(), $rootLength + 1);
90 |
91 | // Can we chop off the extension?
92 | $extension = $file->getExtension();
93 | if (in_array($extension, $config->defaultTemplateExtensions, true)) {
94 | $template = substr($template, 0, strlen($template) - (strlen($extension) + 1));
95 | }
96 | $template = $handle . '/' . $template;
97 |
98 | $hint = $handle;
99 |
100 | // Avoid listing the same template path twice (considering localized templates)
101 | if (isset($templates[$template])) {
102 | continue;
103 | }
104 |
105 | $templates[$template] = true;
106 | $suggestions[] = [
107 | 'name' => $template,
108 | 'hint' => $hint,
109 | ];
110 | }
111 |
112 | ArrayHelper::multisort($suggestions, 'name');
113 |
114 | return $suggestions;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/templates/_components/matrixfacades/scoops/input-variables.html:
--------------------------------------------------------------------------------
1 | {# Global variables available in all Twig blocks
2 | #
3 | # @var string id the normalized id of the Matrix field
4 | # @var string name handle of the Matrix field
5 | # @var \craft\elements\MatrixBlock[] blocks the MatrixBlock elements in the Matrix field
6 | # @var bool static whether the field is static or editable
7 | # @var \craft\base\ElementInterface element the parent element that contains the Matrix block
8 | #
9 | # The namespace will be set appropriately for the field
10 | #}
11 |
12 | {# Set the columns for our custom table #}
13 | {% set cols = {
14 | flavorPicker: {
15 | type: 'flavorPicker',
16 | heading: 'Scoops'|t('site-module') ~ " (#{ blocks|length })",
17 | class: 'leftalign',
18 | },
19 | flavorCalories: {
20 | type: 'text',
21 | heading: 'Calories'|t('site-module'),
22 | class: 'leftalign',
23 | },
24 | flavorPrice: {
25 | type: 'text',
26 | heading: 'Price'|t('site-module'),
27 | class: 'leftalign',
28 | },
29 | scoopNuts: {
30 | type: 'lightswitch',
31 | heading: 'Nuts'|t('site-module'),
32 | },
33 | orderNotes: {
34 | type: 'editabletext',
35 | heading: 'Notes'|t('site-module'),
36 | },
37 | } %}
38 |
39 | {# Set up the elementType and ElementSources #}
40 | {% set elementType = 'craft\\elements\\Entry' %}
41 | {% set elementSources = craft.app.getElementIndexes().getSources(elementType, 'modal') %}
42 | {% set elementSources = elementSources|filter(v => v.criteria.sectionId is defined and v.criteria.sectionId == 7) %}
43 | {% set elementSources = elementSources|map(v => { label: v.label, value: v.key }) %}
44 | {% set newBlock = 'NEWBLOCK' %}
45 | {# Set the rows from the passed in Matric blocks #}
46 | {% set rows = [] %}
47 | {% for block in blocks %}
48 | {% set flavor = block.flavor.one() %}
49 | {% set newBlockName = newBlock ~ loop.index %}
50 | {% set row = {
51 | flavorPicker: {
52 | value: {
53 | flavor: flavor,
54 | blockId: block.id ?? newBlockName,
55 | blockType: block.type,
56 | elementType: elementType,
57 | elementSources: elementSources|first,
58 | },
59 | hasErrors: block.hasErrors('flavor'),
60 | },
61 | flavorCalories: {
62 | value: flavor.flavorCalories ?? '-',
63 | },
64 | flavorPrice: {
65 | value: flavor.flavorPrice ?? '-',
66 | },
67 | scoopNuts: {
68 | value: {
69 | id: 'nuts',
70 | on: block.nuts,
71 | blockId: block.id ?? newBlockName,
72 | }
73 | },
74 | orderNotes: {
75 | value: {
76 | id: 'notes',
77 | text: block.notes ?? '',
78 | blockId: block.id ?? newBlockName,
79 | }
80 | },
81 | } %}
82 | {% set rows = rows|merge([row]) %}
83 | {% endfor %}
84 |
85 | {# Default values that are used for newly added rows #}
86 | {% set defaultValues = {
87 | flavorPicker: {
88 | value: {
89 | flavor: null,
90 | blockId: newBlock,
91 | blockType: 'default',
92 | elementType: elementType,
93 | elementSources: elementSources|first,
94 | },
95 | },
96 | flavorCalories: {
97 | value: '-',
98 | },
99 | flavorPrice: {
100 | value: '-',
101 | },
102 | scoopNuts: {
103 | value: {
104 | id: 'nuts',
105 | on: false,
106 | blockId: newBlock,
107 | }
108 | },
109 | orderNotes: {
110 | value: {
111 | id: 'notes',
112 | text: '',
113 | blockId: newBlock,
114 | }
115 | },
116 | } %}
117 |
118 | {# Render the Matrix Facade's input html #}
119 | {% block input %}
120 | {% endblock input %}
121 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/templates/_components/matrixfacades/scoops/input.html:
--------------------------------------------------------------------------------
1 | {# Global variables available in all Twig blocks
2 | #
3 | # @var string id the normalized id of the Matrix field
4 | # @var string name handle of the Matrix field
5 | # @var \craft\elements\MatrixBlock[] blocks the MatrixBlock elements in the Matrix field
6 | # @var bool static whether the field is static or editable
7 | # @var \craft\base\ElementInterface element the parent element that contains the Matrix block
8 | #
9 | # The namespace will be set appropriately for the field
10 | #}
11 | {% extends 'site-module/_components/matrixfacades/scoops/input-variables' %}
12 |
13 | {# Render the Matrix Facade's input html #}
14 | {% block input %}
15 |
16 | {# Embed the Craft editableTable with our overrides #}
17 | {% embed '_includes/forms/editableTable' with {
18 | id: id,
19 | name: name,
20 | cols: cols,
21 | rows: rows,
22 | defaultValues: defaultValues,
23 | static: static,
24 | initJs: false,
25 | addRowLabel: 'Add a Scoop'|t('site-module'),
26 | } %}
27 | {# Override the tablecell block to handle rendering that ourselves #}
28 | {% block tablecell %}
29 | {% include 'site-module/_components/matrixfacades/scoops/tablecell' with {
30 | name: name,
31 | static: static,
32 | col: col,
33 | value: value,
34 | } %}
35 | {% endblock tablecell %}
36 | {% endembed %}
37 |
38 | {# Template used as a factory for newly added rows #}
39 | {% set templateId = "#{id}-row-template" %}
40 | {% set movable = false %}
41 |
42 | {% for colId,col in cols %}
43 |
44 | {% include 'site-module/_components/matrixfacades/scoops/tablecell' with {
45 | name: name,
46 | static: static,
47 | col: col,
48 | value: defaultValues[colId].value,
49 | } %}
50 |
51 | {% endfor %}
52 | {% if not static %}
53 |
54 |
55 |
56 |
57 |
58 |
59 | {% endif %}
60 |
61 |
62 | {# Initialize the JavaScript for the table #}
63 | {% set templateId = templateId|namespaceInputId %}
64 | {% set jsId = id|namespaceInputId|e('js') %}
65 | {% set jsName = name|namespaceInputName|e('js') %}
66 | {% set jsCols = cols|json_encode %}
67 | {% set defaultValues = defaultValues ?? null %}
68 | {% js %}
69 | window.flavorIncrement = 1;
70 | new Craft.EditableTable("{{ jsId }}", "{{ jsName }}", {{ jsCols|raw }}, {
71 | defaultValues: {{ defaultValues ? defaultValues|json_encode|raw : '{}' }},
72 | onAddRow: function($tr) {
73 | var template = $('#{{ templateId }}').html().trim();
74 | var id = 'new' + window.flavorIncrement++;
75 | template = template.replaceAll('NEWBLOCK', id);
76 | $tr.html(template);
77 | new Craft.BaseElementSelectInput({
78 | id: `fields-scoops-blocks-${id}-fields-flavor`,
79 | name: `fields[scoops][blocks][${id}][fields][flavor]`,
80 | elementType: 'craft\\elements\\Entry',
81 | sources: {{ elementSources|first|json_encode|raw }},
82 | criteria: null,
83 | allowSelfRelations: false,
84 | sourceElementId: null,
85 | disabledElementIds: null,
86 | viewMode: 'list',
87 | single: false,
88 | limit: 1,
89 | showSiteMenu: false,
90 | modalStorageKey: null,
91 | fieldId: `fields-scoops-blocks-${id}-fields-flavor-field`,
92 | sortable: true,
93 | prevalidate: false,
94 | modalSettings: {},
95 | });
96 | },
97 | });
98 | {% endjs %}
99 | {% endblock input %}
100 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/behaviors/MatrixCriteriaBehavior.php:
--------------------------------------------------------------------------------
1 | function($event) {
51 | $this->applyMatrixCriteriaParams($event);
52 | },
53 | ];
54 | }
55 |
56 | /**
57 | * Limit the ElementQuery to elements that match the passed in Matrix criteria
58 | *
59 | * @param string $matrixFieldHandle the handle of the Matrix field to match the criteria in
60 | * @param array $matrixCriteria the criteria for the MatrixBlock query
61 | * @return ElementQueryInterface
62 | */
63 | public function matrixCriteria(string $matrixFieldHandle, array $matrixCriteria): ElementQueryInterface
64 | {
65 | $this->matrixFieldHandle = $matrixFieldHandle;
66 | $this->matrixCriteria = $matrixCriteria;
67 | /* @var ElementQueryInterface $elementQuery */
68 | $elementQuery = $this->owner;
69 |
70 | return $elementQuery;
71 | }
72 |
73 | // Private Methods
74 | // =========================================================================
75 |
76 | /**
77 | * Apply the 'matrixFieldHandle' & 'matrixCriteria' params to select the ids
78 | * of the elements that own matrix blocks that match, and then add them to the
79 | * id parameter of the ElementQuery
80 | *
81 | * @param CancelableEvent $event
82 | */
83 | private function applyMatrixCriteriaParams(CancelableEvent $event): void
84 | {
85 | if (!$this->matrixFieldHandle || empty($this->matrixCriteria)) {
86 | return;
87 | }
88 | /* @var ElementQueryInterface $elementQuery */
89 | $elementQuery = $this->owner;
90 | // Get the id of the matrix field from the handle
91 | $matrixField = Craft::$app->getFields()->getFieldByHandle($this->matrixFieldHandle);
92 | if ($matrixField === null) {
93 | return;
94 | }
95 | // Set up the matrix block query
96 | $matrixQuery = MatrixBlock::find();
97 | // Mix in any criteria for the matrix block query
98 | Craft::configure($matrixQuery, $this->matrixCriteria);
99 | // Get the ids of the elements that contain matrix blocks that match the matrix block query
100 | $ownerIds = $matrixQuery
101 | ->fieldId($matrixField->id)
102 | ->select('matrixblocks.ownerId')
103 | ->orderBy(null)
104 | ->distinct()
105 | ->column();
106 | // If the original query's `id` is not empty, use the intersection
107 | if (!empty($elementQuery->id)) {
108 | $originalIds = $elementQuery->id;
109 | if (!is_array($originalIds)) {
110 | $originalIds = [(int)$originalIds];
111 | }
112 | $ownerIds = array_intersect($originalIds, $ownerIds);
113 | }
114 | // Ensure the parent query returns nothing if no ids were found
115 | if (empty($ownerIds)) {
116 | $ownerIds = null;
117 | $elementQuery->uid = self::NO_MATCHING_MATRIX_CRITERIA;
118 | }
119 | // Add them to the original query that was passed in
120 | $elementQuery->id($ownerIds);
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/fields/MatrixFacade.php:
--------------------------------------------------------------------------------
1 | disableMatrixFacades()) {
49 | return parent::getStaticHtml($value, $element);
50 | }
51 |
52 | /** @var MatrixBlockQuery $value */
53 | $value = $value->all();
54 |
55 | /** @var MatrixBlock[] $value */
56 | if (empty($value)) {
57 | return '' . Craft::t('app', 'No blocks.') . '
';
58 | }
59 |
60 | $id = StringHelper::randomString();
61 |
62 | return Craft::$app->getView()->renderTemplate(
63 | $this->inputTemplatePath,
64 | [
65 | 'id' => $id,
66 | 'name' => $id,
67 | 'blocks' => $value,
68 | 'static' => true,
69 | 'element' => $element,
70 | ]);
71 | }
72 |
73 | // Protected Methods
74 | // ===================================================================================
75 |
76 | /**
77 | * Should we disable facades, and display regular Matrix blocks?
78 | *
79 | * @return bool
80 | */
81 | protected function disableMatrixFacades(): bool
82 | {
83 | // See if facades have been globally disabled
84 | if ($this->disableFacades()) {
85 | return true;
86 | }
87 |
88 | // Ensure there is a value specified in inputTemplatePath
89 | if (empty($this->inputTemplatePath)) {
90 | return true;
91 | }
92 |
93 | // Check permissions
94 | $currentUser = Craft::$app->getUser()->getIdentity();
95 | if ($currentUser->disableMatrixFacades->contains('disableMatrixFacades')) {
96 | return true;
97 | }
98 |
99 | return false;
100 | }
101 |
102 | /**
103 | * @inheritdoc
104 | */
105 | public function getSettingsHtml()
106 | {
107 | $matrixSettings = parent::getSettingsHtml();
108 |
109 | // Render the settings template
110 | return Craft::$app->getView()->renderTemplate(
111 | 'site-module/_components/fields/MatrixFacade/settings',
112 | [
113 | 'matrixField' => $this,
114 | ]
115 | ) . $matrixSettings;
116 | }
117 |
118 | /**
119 | * @inheritdoc
120 | */
121 | protected function inputHtml($value, ElementInterface $element = null): string
122 | {
123 | // See if we should render as a Matrix field
124 | if ($this->disableMatrixFacades()) {
125 | return parent::inputHtml($value, $element);
126 | }
127 |
128 | if ($value instanceof MatrixBlockQuery) {
129 | $value = $value->getCachedResult() ?? $value->limit(null)->anyStatus()->all();
130 | }
131 |
132 | return Craft::$app->getView()->renderTemplate(
133 | $this->inputTemplatePath,
134 | [
135 | 'id' => Html::id($this->handle),
136 | 'name' => $this->handle,
137 | 'blocks' => $value,
138 | 'static' => false,
139 | 'element' => $element,
140 | ]);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/cms/web/img/site/nys-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
9 |
10 |
12 |
14 |
20 |
21 |
23 |
25 |
26 |
28 |
32 |
34 |
36 |
37 |
38 |
40 |
46 |
47 |
49 |
50 |
51 |
55 |
57 |
59 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/cms/modules/sitemodule/src/SiteModule.php:
--------------------------------------------------------------------------------
1 | getBasePath());
63 | $this->controllerNamespace = 'modules\sitemodule\controllers';
64 |
65 | // Translation category
66 | $i18n = Craft::$app->getI18n();
67 | /** @noinspection UnSafeIsSetOverArrayInspection */
68 | if (!isset($i18n->translations[$id]) && !isset($i18n->translations[$id.'*'])) {
69 | $i18n->translations[$id] = [
70 | 'class' => PhpMessageSource::class,
71 | 'sourceLanguage' => 'en-US',
72 | 'basePath' => '@modules/sitemodule/translations',
73 | 'forceTranslation' => true,
74 | 'allowOverrides' => true,
75 | ];
76 | }
77 |
78 | // Base template directory
79 | Event::on(View::class, View::EVENT_REGISTER_CP_TEMPLATE_ROOTS, function (RegisterTemplateRootsEvent $e) {
80 | if (is_dir($baseDir = $this->getBasePath().DIRECTORY_SEPARATOR.'templates')) {
81 | $e->roots[$this->id] = $baseDir;
82 | }
83 | });
84 |
85 |
86 | // Set this as the global instance of this module class
87 | static::setInstance($this);
88 |
89 | parent::__construct($id, $parent, $config);
90 | }
91 |
92 | /**
93 | * @inheritdoc
94 | */
95 | public function init()
96 | {
97 | parent::init();
98 | self::$instance = $this;
99 |
100 | // Register our components
101 | $this->setComponents([
102 | 'helper' => [
103 | 'class' => Helper::class,
104 | ]
105 | ]);
106 |
107 | // Add the CpVariableBehavior behavior to the Cp variable
108 | Event::on(
109 | CraftVariable::class,
110 | CraftVariable::EVENT_DEFINE_BEHAVIORS,
111 | static function(DefineBehaviorsEvent $event) {
112 | $cpVariable = $event->sender->cp;
113 | $cpVariable->attachBehaviors([
114 | CpVariableBehavior::class,
115 | ]);
116 | }
117 | );
118 | // Add the MatrixCriteriaBehavior behavior to the ElementQuery base class
119 | Event::on(
120 | ElementQuery::class,
121 | ElementQuery::EVENT_DEFINE_BEHAVIORS,
122 | static function(DefineBehaviorsEvent $event) {
123 | $event->sender->attachBehaviors([
124 | MatrixCriteriaBehavior::class,
125 | ]);
126 | }
127 | );
128 | Event::on(
129 | Fields::class,
130 | Fields::EVENT_REGISTER_FIELD_TYPES,
131 | static function(RegisterComponentTypesEvent $event) {
132 | $event->types[] = MatrixFacadeField::class;
133 | });
134 |
135 | // Register our variables
136 | Event::on(
137 | CraftVariable::class,
138 | CraftVariable::EVENT_INIT,
139 | function (Event $event) {
140 | /** @var CraftVariable $variable */
141 | $variable = $event->sender;
142 | $variable->set('site', SiteVariable::class);
143 | }
144 | );
145 |
146 | // Register our Asset bundle for CP requests
147 | if (Craft::$app->getRequest()->getIsCpRequest()) {
148 | Event::on(
149 | View::class,
150 | View::EVENT_BEFORE_RENDER_TEMPLATE,
151 | function (TemplateEvent $event) {
152 | try {
153 | Craft::$app->getView()->registerAssetBundle(SiteModuleAsset::class);
154 | } catch (InvalidConfigException $e) {
155 | Craft::error(
156 | 'Error registering AssetBundle - '.$e->getMessage(),
157 | __METHOD__
158 | );
159 | }
160 | }
161 | );
162 | }
163 |
164 | Craft::info(
165 | Craft::t(
166 | 'site-module',
167 | '{name} module loaded',
168 | ['name' => 'Site']
169 | ),
170 | __METHOD__
171 | );
172 | }
173 |
174 | // Protected Methods
175 | // =========================================================================
176 | }
177 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## About nystudio107/matrixfacades
4 |
5 | This is a project demonstrates how to improve authoring experience Matrix Façades
6 |
7 | ## Using nystudio107/matrixfacades
8 |
9 | ### Initial setup
10 |
11 | You'll need [Docker desktop](https://www.docker.com/products/docker-desktop) for your platform installed to run this project
12 |
13 | To get started:
14 |
15 | 1. Clone the git repo with:
16 |
17 | ```
18 | git clone https://github.com/nystudio107/matrixfacades.git
19 | ```
20 |
21 | 2. Go into the project's directory:
22 |
23 | ```
24 | cd matrixfacades
25 | ```
26 |
27 | 3. Start up the site by typing this in the project's root directory:
28 |
29 | ```
30 | make dev
31 | ```
32 |
33 | If it appears to hang at `Building php_xdebug`, your PhpStorm or other IDE is likely waiting for an Xdebug connection;
34 | quit PhpStorm or stop it from listening for Xdebug during the initial build.
35 |
36 | 4. Once the site is up and running (see below), navigate to:
37 |
38 | ```
39 | http://localhost:8888
40 | ```
41 |
42 | The first time you do `make dev` it will be slow, because it has to build all the Docker images.
43 |
44 | Subsequent `make dev` commands will be much faster..
45 |
46 | Wait until you see the following to indicate that the PHP container is ready:
47 |
48 | ```
49 | php_1 | Craft is installed.
50 | php_1 | Applying changes from your project config files ... done
51 | php_1 | [01-Dec-2020 18:38:46] NOTICE: fpm is running, pid 22
52 | php_1 | [01-Dec-2020 18:38:46] NOTICE: ready to handle connections
53 | ```
54 |
55 | ### Login
56 |
57 | The default login is:
58 |
59 | **URL:** `http://localhost:8888/admin`
60 | **User:** `admin` \
61 | **Password:** `password`
62 |
63 | ### Matrix Façades in Action
64 |
65 | The project comes pre-populated with example content that demonstrates an example Matrix Façades.
66 |
67 | #### Color Swatches Matrix Façade
68 |
69 | Navigate to **Entries → Pages → Landing Page** and you'll see an example entry:
70 |
71 | 
72 |
73 | While this looks like some kind of custom UX, it's actually a Matrix Façade field which subclasses Craft Matrix Block
74 | fields, and returns custom HTML for the user input.
75 |
76 | Imagine you have a Page builder where the designers or admins can modify the available color schemes, and this custom UX
77 | is presented to the content authors who can pick from the available choices.
78 |
79 | To see behind the façade, click on the User icon in the upper-right corner of the CP, and click on the **Admin** user.
80 | Then click on **User Settings**:
81 |
82 | 
83 |
84 | Check the **Disable Matrix Façades** checkbox, and click on **Save**.
85 |
86 | Then navigate back to **Entries → Pages → Landing Page** and you'll see the example entry as it really is:
87 |
88 | 
89 |
90 | ...a series of Matrix blocks 🪄
91 |
92 | You can perform [Matrix Block Queries](https://craftcms.com/docs/3.x/matrix-blocks.html) on the data stored in them just
93 | as normal.
94 |
95 | You can even use the Matrix Criteria Behavior discussed in
96 | the [Searching Craft CMS Matrix Blocks](https://nystudio107.com/blog/searching-craft-cms-matrix-blocks) to find entries
97 | based on data stored in the Matrix Block fields.
98 |
99 | The Matrix Criteria Behavior comes bundled with this project as well, so you can do things like:
100 |
101 | ```twig
102 | {% set orders = craft.entries
103 | .section('pages')
104 | .matrixCriteria('colorsSwatches', {
105 | 'type': 'default',
106 | 'selected': true
107 | })
108 | .all()
109 | %}
110 | ```
111 |
112 | #### Scoops Matrix Façade
113 |
114 | Navigate to **Entries → Orders → Some order** and you'll see an example entry:
115 |
116 | 
117 |
118 | While this looks like a table, it's actually a Matrix Façade field which subclasses Craft Matrix Block fields, and
119 | returns custom HTML for the user input.
120 |
121 | You can modify the table by adding or removing items, and upon saving the entry, Craft will take care of updating the
122 | Matrix Block data behind the scenes.
123 |
124 | To see behind the façade, click on the User icon in the upper-right corner of the CP, and click on the **Admin** user.
125 | Then click on **User Settings**:
126 |
127 | 
128 |
129 | Check the **Disable Matrix Façades** checkbox, and click on **Save**.
130 |
131 | Then navigate back to **Entries → Orders → Some order** and you'll see the example entry as it really is:
132 |
133 | 
134 |
135 | ...a series of Matrix blocks 🪄
136 |
137 | You can perform [Matrix Block Queries](https://craftcms.com/docs/3.x/matrix-blocks.html) on the data stored in them just
138 | as normal.
139 |
140 | You can even use the Matrix Criteria Behavior discussed in
141 | the [Searching Craft CMS Matrix Blocks](https://nystudio107.com/blog/searching-craft-cms-matrix-blocks) to find entries
142 | based on data stored in the Matrix Block fields.
143 |
144 | The Matrix Criteria Behavior comes bundled with this project as well, so you can do things like:
145 |
146 | ```twig
147 | {% set orders = craft.entries
148 | .section('orders')
149 | .matrixCriteria('scoops', {
150 | 'type': 'default',
151 | 'nuts': true
152 | })
153 | .all()
154 | %}
155 | ```
156 |
157 | ### Makefile Project Commands
158 |
159 | This project uses Docker to shrink-wrap the devops it needs to run around the project.
160 |
161 | To make using it easier, we're using a Makefile and the built-in `make` utility to create local aliases. You can run the
162 | following from terminal in the project directory:
163 |
164 | - `make dev` - starts up the local dev server listening on `http://localhost:8888/`
165 | - `make clean` - removes the `cms/composer.lock` & the entire `cms/vendor/` directory
166 | - `make composer xxx` - runs the `composer` command passed in, e.g. `make composer install` in the php container
167 | - `make craft xxx` - runs the `craft` [console command](https://craftcms.com/docs/3.x/console-commands.html) passed in,
168 | e.g. `make craft project-config/apply` in the php container
169 | - `make nuke` - restarts the project from scratch by running `make clean` (above), then shuts down the Docker containers, removes any mounted volumes (including the database), and then rebuilds the containers from scratch
170 |
171 | **Tip:** If you try a command like `make craft project-config/apply --force` you’ll see an error, because the shell thinks the `--force` flag should be applied to the `make` command. To side-step this, use the `--` (double-dash) to disable further option processing, like this: `make -- craft project-config/apply --force`
172 |
173 | Brought to you by [nystudio107](https://nystudio107.com/)
174 |
--------------------------------------------------------------------------------