├── 2018-10-22-all-exercises.pdf ├── README.md ├── clear-cache.md ├── exercise_02-add-content.md ├── exercise_03-contrib-themes.md ├── exercise_04-dot-info.md ├── exercise_05-libraries.md ├── exercise_06-intro-to-twig.md ├── exercise_07-twig-new-region.md ├── exercise_08-twig-dot-syntax.md ├── exercise_09-twig-classes.md ├── exercise_10-twig-filters.md ├── exercise_11-twig-block.md ├── exercise_12-twig-include-svg.md ├── exercise_13-preprocess.md ├── exercise_14-new-template-suggestions.md ├── exercise_15-preprocess-add-classses.md ├── exercise_16-form-alter.md ├── exercise_17-responsive.md ├── exercise_18-theme-settings1.md ├── exercise_19-theme-settings2.md ├── local.services.yml └── settings.local.php /2018-10-22-all-exercises.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chapter-three/drupal-8-theming/97127208c7720aa54357f1d1b0748ede0bc5ecfe/2018-10-22-all-exercises.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Drupal 8 Theming Training 2 | 3 | ## Description 4 | 5 | Are you struggling with Drupal 8 theming? New to Drupal or Drupal 8? 6 | 7 | Let's take it from the top! 8 | 9 | This hands-on training will take you step-by-step through the process of creating a custom theme in Drupal 8. There will also be time for Drupal Q&A. 10 | 11 | 12 | Along the way, we'll cover: 13 | 14 | - Setting up Your local development environment 15 | - Working with YAML files 16 | - Adding assets responsibly 17 | - Getting, modifying and display Drupal data in the template 18 | - Cool Twig tips and Tricks 19 | - Preprocessing template functions and hooks 20 | - Basic OOPHP principles 21 | - Kint, theme_debug and other changes to debugging 22 | - Leveraging new Drupal 8 site building paradigms to make theming easier 23 | - Where to find help 24 | 25 | 26 | ## Notes: 27 | 28 | * All terminal commands are run from the Drupal root. 29 | 30 | * `$` indicates a prompt. You do not need to type it into the terminal window. 31 | 32 | * `MYDRUPAL` refers to the Drupal root directory or base URL. 33 | 34 | * You'll have an easier time if you configure your editor to use spaces instead of tabs. 35 | 36 | * Feel free to ask any of the "Questions you may have ..." someone probably asked the question before! 37 | 38 | * If you're using Lando, `composer` becomes `lando composer`, `drush` becomes `lando drush`. 39 | 40 | * See a mistake or typo? Submit a pull request on Github! 41 | 42 | * Common hiccups are: 43 | * Syntax errors 44 | * Too many or not enough spaces in .yml files 45 | * Cache not cleared 46 | * PHP memory limit not high enough (>= 256) 47 | * settings.php 48 | - `ini_set('memory_limit', '512M');` 49 | * php.ini 50 | - `'memory_limit' = '512M'` 51 | 52 | ## Basic terminal commands used. 53 | 54 | Change Directory. 55 | 56 | ```cd ``` 57 | 58 | Create File. 59 | 60 | ```touch``` (`new-item` on Windows PowerShell) 61 | 62 | Create Folder. 63 | 64 | ```mkdir``` 65 | 66 | Move file from one location to another. 67 | 68 | ```mv``` 69 | 70 | Copy file to a new location. 71 | 72 | ```cp``` (`copy` on Windows PowerShell) 73 | 74 | 75 | ## Final Folder Structure 76 | 77 | ``` 78 | MYDRUPAL/themes/custom 79 | └── acme 80 | ├── acme.breakpoints.yml 81 | ├── acme.info.yml 82 | ├── acme.libraries.yml 83 | ├── acme.settings.yml 84 | ├── acme.theme 85 | ├── css 86 | │   ├── css-stuff-print.css 87 | │   ├── css-stuff.css 88 | │   └── custom-widget.css 89 | ├── images 90 | │   └── mysvg.svg 91 | ├── js 92 | │   └── custom-widget.js 93 | ├── templates 94 | │   ├── block 95 | │   │   └── block--system-powered-by-block.html.twig 96 | │   ├── html 97 | │   │   └── html.html.twig 98 | │   ├── node 99 | │   │   └── node.html.twig 100 | │   └── page 101 | │   └── page.html.twig 102 | └── theme-settings.php 103 | ``` 104 | 105 | 106 | ## Exercises 107 | 108 | [Exercise 1 - Local Setup](https://docs.google.com/document/d/1KZsdw7u4KoMo2HZqdWz-1gS8pDeV5JhWbIxGe-dt97g/edit?usp=sharing) 109 | 110 | [Exercise 2 - Add Content](exercise_02-add-content.md) 111 | 112 | [Exercise 3 - Contrib Themes](exercise_03-contrib-themes.md) 113 | 114 | [Exercise 4 - Dot Info File](exercise_04-dot-info.md) 115 | 116 | [Exercise 5 - Libraries](exercise_05-libraries.md) 117 | 118 | [Exercise 6 - Intro to Twig](exercise_06-intro-to-twig.md) 119 | 120 | [Exercise 7 - Regions](exercise_07-twig-new-region.md) 121 | 122 | [Exercise 8 - Dot Syntax](exercise_08-twig-dot-syntax.md) 123 | 124 | [Exercise 9 - Twig Classes](exercise_09-twig-classes.md) 125 | 126 | [Exercise 10 - Twig Filters](exercise_10-twig-filters.md) 127 | 128 | [Exercise 11 - Twig Blocks](exercise_11-twig-block.md) 129 | 130 | [Exercise 12 - Include SVG](exercise_12-twig-include-svg.md) 131 | 132 | [Exercise 13 - Preprocess Function](exercise_13-preprocess.md) 133 | 134 | [Exercise 14 - Template Suggestions](exercise_14-new-template-suggestions.md) 135 | 136 | [Exercise 15 - Add Classes with PHP](exercise_15-preprocess-add-classses.md) 137 | 138 | [Exercise 16 - Form Alter](exercise_16-form-alter.md) 139 | 140 | [Exercise 17 - Responsive Images](exercise_17-responsive.md) 141 | 142 | [Exercise 18 - Custom Theme Settings 1](exercise_18-theme-settings1.md) 143 | 144 | [Exercise 19 - Custom Theme Settings 2](exercise_19-theme-settings2.md) 145 | 146 | ## Style Guides for Contributors 147 | 148 | ###### Path to files and directories. 149 | 150 | **MYDRUPAL/themes** Path to files and directories. 151 | 152 | ###### Name of file or directory 153 | **node.html.twig** 154 | 155 | ###### Code. 156 | 157 | ```bash 158 | $ cd drupal 159 | ``` 160 | 161 | ###### Url 162 | *http://MYDRUPAL/admin/config* 163 | 164 | 165 | ## Done ☺ 166 | -------------------------------------------------------------------------------- /clear-cache.md: -------------------------------------------------------------------------------- 1 | # Clearing the Registry 2 | Throughout these exercises you'll be asked to clear cache or registry in order to see changes. 3 | Ways to clear registry (aka "Clear Cache"): 4 | 5 | 1. use $ drush cr in the terminal 6 | 2. go to Configuration > Performance and Clear All Caches. 7 | 3, with admin_toolbar_tools enabled, hover over the Drupal icon and choose Flush all Caches. 8 | 9 | Done ☺ 10 | -------------------------------------------------------------------------------- /exercise_02-add-content.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 2: 4 | 5 | ## Auto Generate Content 6 | 7 | You can easily create one or two items of content in Drupal, but what if you need to test out what 50 nodes look like in a view? What if you need to know how the page is going to look with a couple dozen comments all at different levels? Are you going to create each one of those items by hand? Hopefully not. Luckily, we have a handy module that has already been ported to D8 and is (mostly) able to take care of this work for us. Enter the "Devel" and "Devel Generate" module. 8 | 9 | 1. Enable the **Devel generate** module if you have not already. 10 | 11 | ### For vocabularies and taxonomy terms 12 | 1. Click "Configuration" in the Admin menu. 13 | 2. In the `Development` group select `Generate vocabularies`. 14 | 1. Number of vocabularies?: `2` 15 | 2. Maximum number of characters in vocabulary names: `20` 16 | 3. Click the **Generate** button. 17 | 3. Click "Configuration" in the Admin menu 18 | 4. In the `Development` group select `Generate terms`. 19 | 1. Vocabularies: **Select "Tags" and one other vocabulary** 20 | 2. Number of terms?: `10` 21 | 2. Maximum number of characters in vocabulary names: `15` 22 | 3. Click the **Generate** button. 23 | 24 | 25 | ### For content 26 | 27 | 1. Click "Configuration" in the Admin menu. 28 | 2. In the `Development` group select `Generate content`. 29 | 1. Select Content Types `Article` and `Basic Page` 30 | 2. How many nodes would you like to generate?: `25` 31 | 3. How far back in time should the nodes be dated?: `1 year` 32 | 4. (If comments enabled) Maximum number of comments per node: `5` 33 | 4. Maximum number of words in titles: `10` 34 | 5. Leave the rest at their default values 35 | 3. Click the **Generate** button. 36 | 37 | 38 | ### For users and menus 39 | 1. Feel free to generate users and menus using the same procedure. 40 | 41 | ## Questions you may have... 42 | + Can I generate content from the command line? 43 | 44 | ## Done ☺ 45 | [Exercise 3 - Contrib Themes](exercise_03-contrib-themes.md) is next! -------------------------------------------------------------------------------- /exercise_03-contrib-themes.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 3: 4 | 5 | ## Contrib themes 6 | 7 | This is where most themers will start their career. Let someone else do the work for you and download something generic that can be modified to fit your needs. Contributed themes can be a great resource for learning new techniques and functionality, which you can implement in your custom themes. 8 | 9 | You can find themes at [http://drupal.org/project/themes](http://drupal.org/project/themes), as well as some external sites. Make sure the theme is compatible with Drupal 8. 10 | 11 | If you run into problems, check the theme's issue queue and search the forums. If your problem hasn't already been addressed, post a question, and someone will try to help you out. 12 | 13 | 14 | ## Install our first contrib... 15 | 1. Download and add the latest recommended release of *Radix*: http://drupal.org/project/radix 16 | 17 | Remember if you're using Lando, `composer` becomes `lando composer`; please see the [README](https://github.com/chapter-three/drupal-8-theming/blob/master/README.md) for more details 18 | 19 | `$ composer require drupal/radix` 20 | 21 | 2. Also, download and add the latest recommended release of the *Mayo* theme: https://www.drupal.org/project/mayo 22 | 23 | `$ composer require drupal/mayo` 24 | 25 | 3. Read `README.txt` inside of the *mayo* folder to know what to expect. 26 | 27 | 4. Enable the *Mayo* theme: 28 | 1. Click on **Appearance** at the top. 29 | 2. Find *Mayo* near the bottom and click **Install and set default**. 30 | 31 | 5. [Clear cache](clear-cache.md). 32 | 33 | 34 | > ### Typical structure of a theme 35 | >**MYTHEME.info.yml** - A theme must contain an .info.yml file to define the theme. Among other things, the .info.yml files define metadata, style sheets, and block regions. This is the only required file in the theme. 36 | > 37 | >**MYTHEME.libraries.yml** - The .libraries.yml file is used to define JavaScript and CSS libraries that can be loaded by the theme. 38 | > 39 | >**MYTHEME.breakpoints.yml** - Breakpoints define where a design changes in response to different devices. While the theme can use this file, its info can also be used by Drupal core to make adjustments to data it sends to the theme. 40 | > 41 | >**MYTHEME.theme** - The .theme file is equivalent to the old Drupal 7 template.php file. It is a PHP file that contains conditional logic and data (pre)processing of the output. 42 | > 43 | >**screenshot.png** - If a screenshot.png file is found in the theme folder, it will be used on the Appearance page. You can also define a screenshot image in .info.yml file. 44 | > 45 | >**logo.svg** - New to Drupal 8, logos are best saved as svg files and placed in the main level of your theme. Logos can also be uploaded at Appearance > Settings. 46 | > 47 | >**css/** or **styles/** - Drupal 8 core themes organize CSS files following the SMACCS style guide. For CSS files to be loaded, they must be defined in your .libraries.yml file. You can override or remove core and module CSS in your themes .info.yml file. 48 | > 49 | >**js/** or **script/** - JavaScript files are stored in the 'js' folder. For a theme to load JavaScript files, they must be defined in .libraries.yml file. 50 | > 51 | >**img/** or **images/** - It is good practice to store images in the 'images' subfolder. 52 | > 53 | >**templates/** - Templates provide HTML markup and some presentation logic. It is customary to place template files into subcategory folders, such as **templates/node/** for node based templates. 54 | 55 | ## ... then we configure our theme 56 | 1. Revisit the Appearance page and click **Settings** under the _Mayo_ theme. 57 | 2. Play with some of the configuration settings. 58 | 3. Try to complete the following tasks: 59 | 1. Tweak the color scheme by utilizing the color wheel. 60 | 2. Change the base font selection to be `Verdana, Geneva, Arial, ...` 61 | 3. Set both sidebars to appear on the left side on big screens. 62 | 3. Set sidebar to use round corners. 63 | 64 | 4. Many settings are theme-specific. Visit the settings pages of other themes, like *Seven* to compare. 65 | 66 | 67 | ## Questions you may have... 68 | + How do I choose a theme? 69 | 70 | ## Done ☺ 71 | Woo hoo! Time for [Exercise 4 - Dot Info File](exercise_04-dot-info.md)! :) 72 | 73 | -------------------------------------------------------------------------------- /exercise_04-dot-info.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 4: 4 | 5 | ## The .info.yml file 6 | 7 | To create a new theme for Drupal 8, the only real requirement is to make sure you have implemented the .info.yml file (YaML, like Camel). Drupal 8 runs off of YaML files much the way that Drupal 7 ran off of .info files. 8 | 9 | As long as you have your .info.yml file in place with a few keys in place, you have a theme (although if that's all you have in place, it's not going to be a pretty theme). 10 | 11 | ### Create the theme folder and "YaML" file 12 | 1. Locate the **MYDRUPAL/themes** folder in your Drupal installation. In Drupal 8, Contrib and Custom modules and themes, are saved at the root level **MYDRUPAL/modules** and **MYDRUPAL/themes** folders. 13 | 14 | 15 | 2. Create a new folder called `custom` and inside it a folder called `acme` 16 | 17 | ```bash 18 | $ cd MYDRUPAL 19 | $ mkdir themes/custom 20 | $ mkdir themes/custom/acme 21 | ``` 22 | 3. In that folder create a new file. It should be called **acme.info.yml** 23 | 24 | ```bash 25 | $ touch themes/custom/acme/acme.info.yml 26 | ``` 27 | 4. Add the following lines to that file: 28 | 29 | ``` 30 | name: Acme 31 | description: My first Drupal 8 theme. 32 | type: theme 33 | base theme: classy 34 | core: 8.x 35 | version: VERSION 36 | 37 | regions: 38 | header: Header 39 | primary_menu: 'Primary menu' 40 | secondary_menu: 'Secondary menu' 41 | breadcrumb: Breadcrumb 42 | help: Help 43 | hightlighted: Highlighted 44 | content: Content 45 | sidebar_first: 'Left sidebar' 46 | sidebar_second: 'Right Sidebar' 47 | footer: Footer 48 | page_top: 'Page top' 49 | page_bottom: 'Page Bottom' 50 | ``` 51 | 52 | 5. Clear cache. 53 | 54 | 6. Go to /admin/appearance and under the Acme theme choose `Install and set as default`. 55 | 56 | 7. Go to the home page and observe your beautiful new theme. 57 | 58 | 59 | >## Things we might find in a .info.yml file 60 | The following keys are items that we will often find in a `*.info.yml` file. Some are optional; some are required. These keys provide metadata about your theme and define some of the basic functionality. 61 | 62 | >`name` **Required** 63 | The human-readable name will appear on the Appearance page, where you can activate your theme. 64 | > 65 | >`description` **Required** The description is displayed on the Appearance page. 66 | > 67 | >`type` **Required** The type key indicates the type of extension, e.g., module, theme or profile. For themes this should always be set to `theme`. 68 | > 69 | >`base theme` The theme can inherit the resources from another theme by defining it as a base theme. Not declaring this, will default to using "Stable" as the base theme. 70 | > 71 | >`core` **Required** The core key specifies the version of Drupal core that your theme is compatible with. 72 | > 73 | >`version` For modules hosted on drupal.org, the version number will be filled in by the packaging script. You should not specify it manually, but leave out the version line entirely. 74 | > 75 | >`regions` Regions are declared as children of the regions key. You are required to have a content region. The regions we just declared are also the default regions that are enabled by core if you do not declare any regions in your .info.yml file. 76 | 77 | ### Other items 78 | >`regions_hidden` will allow you to remove default regions from the output if you don't specifically declare any regions. 79 | > 80 | >`screenshot: IMAGE_NAME.png` With the screenshot key you define a screenshot that is shown on the Appearance page. If you do not define this key, then Drupal will look for a file named 'screenshot.png' or 'screenshot.svg' in the theme folder to display. 81 | > 82 | >`libraries` The libraries key can be used to add asset libraries to all pages where the theme is active. Theme libraries contain CSS and/or javascript. Theme libraries are declared in another type of YaML file (THEME.libraries.yml). We will cover libraries in later exercises. 83 | > 84 | > ```yml 85 | > libraries: 86 | > - THEMENAME/global-styling 87 | > - THEMENAME/LIBRARY-NAME 88 | > ``` 89 | > 90 | >`libraries-override` and `libraries-extend` keys can be used to take control over the components of a library, remove items or the complete library, or add additional elements to a library. 91 | > 92 | >`stylesheets-remove` key is used to stop the addition of CSS components for core and contrib modules. However, it is better to do this with `libraries-override`. 93 | > 94 | >``` 95 | > stylesheets-remove: 96 | > - core/assets/vendor/normalize-css/normalize.css 97 | > - '@classy/css/components/tabs.css' 98 | > ``` 99 | > 100 | >`ckeditor_stylesheets` will allow you to have custom ckeditor styles attached to your theme 101 | > 102 | >`quickedit_stylesheets` will allow you to have custom styles and javascript attached to the quickedit functionality 103 | 104 | ## Questions you may have... 105 | + What happens if I don't declare any regions? 106 | + Why are some regions in `''` and others not? 107 | 108 | ## Done ☺ 109 | [Exercise 5 - Libraries](exercise_05-libraries.md) awaits! 110 | 111 | -------------------------------------------------------------------------------- /exercise_05-libraries.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 5: 4 | 5 | ## Drupal asset libraries: adding CSS and JavaScript to your site 6 | 7 | In Drupal 8, "Asset Libraries" are used for loading stylesheets (CSS) and JavaScript (JS). The libraries system that you use in your theme is the same framework used by modules and core. Asset libraries can contain one or more CSS assets, one or more JS assets, and one or more JS settings. 8 | 9 | Drupal follows this high-level principle: CSS and JavaScript are only loaded if you tell Drupal it should load them. Drupal has stopped assuming that it should load all assets on all pages, because this is bad for front-end performance. 10 | 11 | The biggest difference from Drupal 7 is when it comes to JavaScript loaded on pages. By default Drupal doesn't need JavaScript on most pages that anonymous users can see. This means that jQuery is not automatically loaded on all pages anymore. If your theme does need it, you can declare it in one of your theme's libraries, and it will be added to every page. 12 | 13 | ### The general process for adding CSS and JavaScript 14 | 15 | The basic process breaks down into 3 steps 16 | 17 | >1. Save the CSS or JS to a file. 18 | >2. Define a "library" in a `*.libraries.yml` file, which contains a reference to the CSS and/or JS files. 19 | >3. "Attach" the library to a render array in a hook using the `#attached` attribute, by including it in a template, or add the library as a dependency to the theme's `*.info.yml` file. 20 | 21 | 22 | ## Create a library 23 | 24 | In the following steps, we will create a libraries.yml file, declare our library some CSS files and create the CSS files in our theme. 25 | 26 | 1. Navigate to your theme's root directory 27 | 28 | ```bash 29 | $ cd MYDRUPAL 30 | ``` 31 | 2. Create a file called **acme.libraries.yml** and open that file in your preferred code editor. 32 | 33 | ```bash 34 | $ touch themes/custom/acme/acme.libraries.yml 35 | ``` 36 | 37 | 3. Add the following code to that file: 38 | 39 | ```yml 40 | css-stuff: 41 | version: VERSION 42 | css: 43 | theme: 44 | css/css-stuff.css: {} 45 | css/css-stuff-print.css: { media: print } 46 | //fonts.googleapis.com/css?family=Abhaya+Libre|Open+Sans: { type: external } 47 | 48 | ``` 49 | 50 | We just declared our library called `css-stuff`. We can give it any name we want as long as another module or theme hasn't declared it. A best practice for theme libraries is to use some word(s) to describe what the library will do. A name like `global-styles` would be more accurate for this library, but I'm a free spirit and I like to run with scissors. 51 | 52 | 4. Navigate back to your theme's root directory, and create a folder called **"css"** (if one doesn't already exist). 53 | 54 | ```bash 55 | $ mkdir themes/custom/acme/css 56 | ``` 57 | 58 | 5. Create two files in that directory. One called **css-stuff.css** and one called **css-stuff-print.css** 59 | 60 | 61 | ```bash 62 | $ touch themes/custom/acme/css/css-stuff.css 63 | $ touch themes/custom/acme/css/css-stuff-print.css 64 | ``` 65 | 66 | 6. Open them both and add any high level css you want. For **css-stuff.css** you could add: 67 | 68 | ```css 69 | body { 70 | background-color: #e7e7e7; 71 | font-family: 'Open Sans', serif; 72 | padding: 30px; 73 | } 74 | 75 | h2 a { 76 | background-color: #0c89af; 77 | color: white; 78 | font-family: 'Abhaya Libre', serif; 79 | padding: 3px; 80 | text-decoration: none; 81 | } 82 | 83 | h4 { 84 | background-color: #3baf8a; 85 | font-size: 1.5em; 86 | padding: 3px; 87 | } 88 | ``` 89 | and for **css-stuff-print.css**, you could add: 90 | 91 | ```css 92 | body { 93 | font-family: sans-serif; 94 | color: #888; 95 | } 96 | ``` 97 | Go crazy if you want! 98 | 99 | 7. Navigate back to your theme root directory and open your **acme.info.yml** file 100 | 101 | 8. We need to make our theme aware of our newly declared library before anything happens. 102 | 103 | 9. Add the following code to your **acme.info.yml** file 104 | 105 | ``` 106 | libraries: 107 | - acme/css-stuff 108 | ``` 109 | 10. [Clear cache](clear-cache.md) and you should now see your css styles in place. 110 | 111 | If everything worked, you should see output like the following in the head section in the source code on all pages of your theme. 112 | 113 | ```html 114 | @import url("/themes/acme/css/css-stuff.css?of7sd1"); 115 | ... 116 | 119 | ``` 120 | 121 | ## Attach Library to a template 122 | In Drupal 8, it is recommended that libraries only be applied when needed. Unless a style is being used on every page, we should use the `attach_library()` function in twig to add JS or CSS. 123 | 124 | 1. Create a folder called **js** in your theme root and add a JavaScript file called **custom-widget.js** 125 | 126 | ```bash 127 | $ mkdir themes/custom/acme/js 128 | $ touch themes/custom/acme/js/custom-widget.js 129 | 130 | ``` 131 | 132 | 2. Create a CSS file in the **css** folder called **custom-widget.css**. 133 | 134 | ```bash 135 | $ touch themes/custom/acme/css/custom-widget.css 136 | 137 | ``` 138 | 139 | 140 | 3. Add some sample CSS and js to each file: 141 | 142 | **custom-widget.js:** 143 | 144 | ```js 145 | console.log('It Works'); 146 | ``` 147 | 148 | **custom-widget.css:** 149 | 150 | ```css 151 | article { 152 | background: white; 153 | padding: 10px; 154 | } 155 | ``` 156 | 157 | 3. Define your new library in **acme.libraries.yml** 158 | 159 | ``` 160 | widget: 161 | version: VERSION 162 | js: 163 | js/custom-widget.js: {} 164 | css: 165 | theme: 166 | css/custom-widget.css: {} 167 | dependencies: 168 | - core/drupal 169 | - core/jquery 170 | ``` 171 | 172 | 1. Navigate to **MYDRUPAL/core/themes/classy/templates/content/** and look for **node.html.twig**. 173 | 174 | 2. Make a copy of **node.html.twig** and paste it in at **MYDRUPAL/themes/custom/acme/templates/node**. Then, because you have added a new file, you'll need to clear cache. 175 | 176 | ```bash 177 | $ mkdir themes/custom/acme/templates 178 | $ mkdir themes/custom/acme/templates/node 179 | $ cp core/themes/classy/templates/content/node.html.twig themes/custom/acme/templates/node/node.html.twig 180 | $ drush cr 181 | ``` 182 | 183 | 4. Add the following to your theme's new **node.html.twig** near the top 184 | 185 | ```twig 186 | {% if node.bundle == 'article' %} 187 | {{ attach_library('acme/widget') }} 188 | {% endif %} 189 | ``` 190 | 6. Verify that **custom-widget.js** and **custom-widget.css** have loaded on the article pages but NOT on basic pages. 191 | 192 | ## Overriding and removing libraries 193 | 194 | ### Removing CSS or javascript 195 | 196 | 197 | If we are using a base theme, it is very possible that some CSS or javascript is coming over from the base theme that we just do not want to use in our theme. Or sometimes there stylesheets coming from core that we just don't want. Even though Drupal tries to follow the rule of only "load stuff only if needed," you may find yourself in a situation where a contrib module or other library is just adding too much. We can remove stylesheets from output at the theme level by using the `stylesheets-remove` key in our *.info.yml* file. 198 | 199 | **The following method is for removing individual CSS or javascript files** 200 | 201 | 1. Take a look at the CSS that is being loaded on your site as a logged in user. Note that **core/assets/vendor/jquery.ui/themes/base/core.css** and 202 | **/core/themes/classy/css/components/breadcrumb.css** are being loaded. 203 | 204 | 2. Navigate to your theme root and open the **acme.info.yml** file. 205 | 206 | 3. Place the following code in your **acme.info.yml** file. 207 | 208 | ``` 209 | stylesheets-remove: 210 | - core/assets/vendor/jquery.ui/themes/base/core.css 211 | - '@classy/css/components/breadcrumb.css' 212 | ``` 213 | 214 | In this example, we are removing the extra CSS file that core tries to add with one of its JQuery libraries. We are also removing a stylesheet from our parent (base) theme, classy. You probably notice the `@` symbol at the start of the third line. This is a placeholder token. @classy, or @node, or @whatever is the equivalent to `drupal_get_path()` in Drupal 7. Note that `stylesheets-remove` is technically deprecated and will be removed in Drupal 9. 215 | 216 | 4. [Clear cache](clear-cache.md) 217 | 218 | 219 | ### Overriding CSS or javascript 220 | 221 | Sometimes we don't want to remove a file or a library, but we do have a better file for it to use. We can override whole libraries or components of them. Below is an example of libraries-override from drupal.org. You would place this in your *.info.yml file. 222 | 223 | Note: The methods for modifying libraries have changed during the development of Drupal 8. Check [Drupal.org's Theming Guide](https://www.drupal.org/docs/8/theming-drupal-8/adding-stylesheets-css-and-javascript-js-to-a-drupal-8-theme#override-extend) for the latest documentation. 224 | 225 | **This code is just for show.** 226 | 227 | >``` 228 | >libraries-override: 229 | > # Replace an entire library. 230 | > core/drupal.collapse: mytheme/collapse 231 | > 232 | > # Replace an asset with another. 233 | > subtheme/library: 234 | > css: 235 | > theme: 236 | > css/layout.css: css/my-layout.css 237 | > 238 | > # Replace a core module JavaScript asset. 239 | > toolbar/toolbar: 240 | > js: 241 | > js/views/BodyVisualView.js: js/views/BodyVisualView.js 242 | > 243 | > # Remove an asset. 244 | > drupal/dialog: 245 | > css: 246 | > theme: 247 | > dialog.theme.css: false 248 | > 249 | > # Remove an entire library. 250 | > core/modernizr: false 251 | >``` 252 | > 253 | > **Libraries Extend Example** 254 | > 255 | > ``` 256 | > # Extend drupal.user: add assets from classy's user libraries. 257 | > libraries-extend: 258 | > core/drupal.user: 259 | > - classy/user1 260 | > - classy/user2 261 | >``` 262 | 263 | ## Questions you may have... 264 | + Why is the word `theme` in the library definition? 265 | + Is `//fonts.googleapis.com/...` a comment? 266 | + Why do some lines start with `-` in .yml files and others don't? 267 | 268 | 269 | ## Done ☺ 270 | You're doing great! Proceed to [Exercise 6 - Intro to Twig](exercise_06-intro-to-twig.md) 271 | 272 | -------------------------------------------------------------------------------- /exercise_06-intro-to-twig.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 6: 4 | 5 | ## Intro to Twig 6 | 7 | Twig is a modern, advanced, templating language for PHP. Twig is also the new templating engine for Drupal 8. It replaces the old and antiquated phptemplate engine. Twig is fast, secure, and incredibly flexible, but one of its best attributes is that it does not allow some of the bad habits in Drupal theming that phptemplate allowed in the past. (I'm referring to stuff like database queries and data preprocessing in the template files. We've all done it at some point, and yes, we are all ashamed.) 8 | 9 | **If debugging is not enabled, please see "Exercise 1"** 10 | 11 | ## Basic Twig 12 | 13 | There are 3 Basic Syntaxes of Twig. Mostly, we will use just 2 of them. 14 | 15 | ###The “Say Something” Syntax: {{ ... }} 16 | 17 | The double-curly-brace (`{{`) is always used to **print** something. If whatever you need to do will result in something being printed to the screen, then you’ll use this syntax. I call this the “say something” tag, because it’s how you “speak” in Twig. 18 | 19 | + Printing a variable 20 | 21 | ```twig 22 | {{ content }} 23 | ``` 24 | 25 | ###The “Do Something” Syntax: {% ... %} 26 | 27 | The curly-percent (`{%`) is the other syntax, which we call the “do something” syntax. It’s used for things like **if** and **for** tags as well as other things that “do” something. There are only a handful of things that can be used inside of it. See [Twig’s documentation](https://twig.symfony.com/doc/2.x/) for all Twig functionality. Twig tags can be used inside of a “do something” statement. The only ones you need to worry about now are **if** and **for**. We’ll talk about a bunch more of these later. 28 | 29 | + Running a conditional check (in this case, check if the variable 'logo' is set, if so print the logo) 30 | 31 | ```twig 32 | {% if logo %} 33 | {{ logo }} 34 | {% endif %} 35 | ``` 36 | 37 | + Running a loop and printing a value (in this case, for each product item in products array, it'll print out the product item content) 38 | 39 | ```twig 40 | {% for product in products %} 41 |

{{product}}

42 | {% endfor %} 43 | ``` 44 | 45 | ###The Comment Syntax: {# ... #} 46 | 47 | There is a third syntax, used for comments: `{#`. Just like with the “say something” and “do something” syntaxes, write the opening {# and also the closing #} at the end of your comments: 48 | 49 | + An example comment 50 | 51 | ```twig 52 | {# This is a comment for you to enjoy :) #} 53 | ``` 54 | 55 | 56 | ## What's in a twig template 57 | 58 | 3. Open your theme's **node.html.twig** file in a text editor and add one of the following lines somewhere at the end of the twig template `{{ dump(date) }}` or `{{ kint(content_attributes) }}` 59 | 60 | 4. Visit a node page, and lets see what it gives us. 61 | 62 | ## Use Twig 63 | ### Print out the bundle type for the node: 64 | 65 | 1. Inspect the **node.html.twig** template, review the comments and variables. 66 | 67 | 3. Add a line to output the the node's bundle type before `` 68 | ``: 69 | 70 | ```twig 71 |

{{ node.bundle}}

72 | ``` 73 | 74 | 4. Clear cache, visit (or refresh) a node. 75 | 76 | ##Questions you may have 77 | 78 | + Can I modify a variable through Twig? 79 | 80 | 81 | ## Done ☺ 82 | 83 | Get your [Exercise 7 - Regions](exercise_07-twig-new-region.md) on. 84 | -------------------------------------------------------------------------------- /exercise_07-twig-new-region.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 7: 4 | 5 | ## Adding a new region 6 | 7 | The client wants a new region for special messages at the top of the footer, so we’ll give them a new region called `Footer top`. Eventually, it would be great to style this region to look a little nicer, but for now, lets just put the content in place. 8 | 9 | 1. Update **acme.info.yml** to include the new Footer top region: 10 | 11 | ``` 12 | regions: 13 | ... 14 | footer_top: 'Footer top' 15 | ... 16 | ``` 17 | 18 | 2. Now that Drupal knows that a new region is available, we need to print that region to the page by modifying **page.html.twig**. 19 | 3. Navigate to the Classy theme in **MYDRUPAL/core/themes/classy/templates/layout** and locate **page.html.twig**. 20 | 4. Make a copy of **page.html.twig** and put it in the **acme** theme folder. We like to keep it organized, so we'll put it in **MYDRUPAL/themes/custom/acme/templates/page**. 21 | 22 | ```bash 23 | $ cd MYDRUPAL 24 | $ mkdir themes/custom/acme/templates/page 25 | $ cp core/themes/classy/templates/layout/page.html.twig themes/custom/acme/templates/page/page.html.twig 26 | ``` 27 | 28 | 29 | 5. Edit your theme’s new version of **page.html.twig**: 30 | Look for and copy the following lines of code around line 81: 31 | 32 | ```twig 33 | ... 34 | 35 | {% if page.footer %} 36 | 39 | {% endif %} 40 | 41 | ... 42 | ``` 43 | 44 | 6. Above this footer callout code, paste your copy. 45 | 7. In your pasted code, Replace `footer` with `footer_top`, It should look like this when you’re finished: 46 | 47 | 48 | ```twig 49 | ... 50 | 51 | {% if page.footer_top %} 52 | 55 | {% endif %} 56 | 57 | {% if page.footer %} 58 | 61 | {% endif %} 62 | 63 | ... 64 | ``` 65 | 66 | 8. Flush your cache and try putting a block in the new region. 67 | 68 | ## Questions you may have... 69 | + Can I add more regions? Can I remove ones I don’t need? 70 | + How do I style my new region? 71 | 72 | 73 | ## Done ☺ 74 | Sometimes you feel like a nut; sometimes you [Exercise 8 - Dot Syntax](exercise_08-twig-dot-syntax.md). 75 | 76 | -------------------------------------------------------------------------------- /exercise_08-twig-dot-syntax.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 8: 4 | 5 | ## Manipulating variables in the template 6 | 7 | Look at the comments at the top of the **node.html.twig** template. The comments detail the variables that are available to this particular template. In twig, we can use certain filters to manipulate the output of variables. 8 | 9 | ### Use `kint()` to inspect the content variable. 10 | 11 | 1. Add `{{ kint(content) }}` to the bottom of your **node.html.twig** field. 12 | 13 | 2. Go to an Article node page. 14 | 15 | 3. Inspect the content variable. Note that body, field_tags, and field_image are available. 16 | 17 | _Make sure your [php memory limit](https://www.drupal.org/docs/7/managing-site-performance-and-scalability/changing-php-memory-limits) is high (>= 256), or you may see a white screen of death._ 18 | 19 | 20 | ### Print content without certain fields. 21 | 22 | 1. Delete the kint statement. 23 | 24 | 1. Verify that the article you're looking at has Tags. 25 | 26 | 1. In **node.html.twig**, change ```{{ content }}``` to ```{{ content|without('field_tags') }}``` 27 | 28 | 2. View an article node page. Now all fields are printed except for the tags field. 29 | 30 | ### Print fields individually. 31 | 32 | 1. Find ```{{ content | without('field_tags') }} ``` from the previous step and change it to ```{{ content | without ('field_tags', 'field_image') }}``` 33 | 34 | This removes the tags and the image fields. 35 | 36 | 37 | 2. Add the following directly above `{{ content | without ('field_tags', 'field_image') }}`. 38 | 39 | ```twig 40 | 44 | ``` 45 | 46 | You should now see your Tags and Image fields inside a div with class `sidebar`. 47 | 48 | 3. Add a little styling to **css-stuff.css** to get the full effect. 49 | 50 | ```css 51 | .node__content { 52 | display: grid; 53 | grid-template-columns: 1fr 3fr; 54 | } 55 | ``` 56 | 57 | 58 | ### Explore. 59 | The `.` syntax in twig is a shorthand for some PHP methods and functions. Below is a list of methods Twig will check in the order they will be checked in. 60 | 61 | >```twig 62 | >{{ sandwich.cheese }} 63 | >``` 64 | 65 | >```php 66 | >// Array key. 67 | >$sandwich['cheese']; 68 | >// Object property. 69 | >$sandwich->cheese; 70 | >// Also works for magic get (provided you implement magic isset). 71 | >$sandwich->__isset('cheese'); && $sandwich->__get('cheese'); 72 | >// Object method. 73 | >$sandwich->cheese(); 74 | >// Object get method convention. 75 | >$sandwich->getCheese(); 76 | >// Object is method convention. 77 | >$sandwich->isCheese(); 78 | >// Method doesn't exist/dynamic method. 79 | >$sandwich->__call('cheese'); 80 | >``` 81 | 82 | 1. Spend a few moments trying to print out variables and their children. 83 | 84 | ## Questions you may have... 85 | + What if I only want the body text without the surrounding markup? 86 | + Why do you keep telling me to delete my kint statements? 87 | 88 | ## Done ☺ 89 | Next stop: [Exercise 9 - Twig Classes](exercise_09-twig-classes.md) 90 | -------------------------------------------------------------------------------- /exercise_09-twig-classes.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 9: 4 | 5 | Drupal has some handy functions specifically designed for the manipulation of HTML elements. 6 | 7 | 8 | ## Manipulating Classes with Twig 9 | 10 | ### Inspect the `attributes` array. 11 | 1. Add `{{ kint(attributes) }}` in your theme's **node.html.twig**. Review the object and its properties and methods. Note that `addClass()` is an available public method. 12 | 13 | ### Add a class. 14 | 2. Change `` to 15 | ``````. Observe the new value in kint as well as the new class in your site's markup. 16 | 17 | 18 | ### Add multiple classes to the body tag. 19 | 1. Remove all kint() statements from **node.html.twig**. 20 | 21 | 2. Copy classy's **html.html.twig** into your theme. 22 | 23 | ```bash 24 | $ cd MYDRUPAL 25 | $ mkdir themes/custom/acme/templates/html 26 | $ cp core/themes/classy/templates/layout/html.html.twig themes/custom/acme/templates/html/html.html.twig 27 | ``` 28 | 3. Clear Cache 29 | 30 | 3. Above the DOCTYPE declaration and below the comments, add the following code. 31 | 32 | ```twig 33 | {% set myclasses = ['red', 'green', 'blue'] %} 34 | ``` 35 | 36 | 5. Find the line `` and change it to 37 | 38 | ```twig 39 | 40 | ``` 41 | 42 | You should now see classes `red`, `green` and `blue` attached to the body tag. 43 | 44 | ### Create a new custom variable. 45 | Let's create a special body class for the user role. 46 | 47 | 1. Note that `logged_in` is one of the variables available in **html.html.twig** according to its comments. 48 | 49 | 2. Use `{{ kint(user) }}` to inspect the user variable in **html.html.twig**. Search the kint for `account`. Note that the method `getAccount()` is public. If we look back at Exercise 8, we see that we can use `{{ user.account }}` because of that sweet, sweet Twig magic. 50 | 51 | 4. Use `{{ kint(user.account }}`. Search for `roles`. Look at that! Our friends at D.O. also made `getAccount()` public. Now we can do the thing. 52 | 53 | 2. Below ```{% set myclasses = ['red', 'green', 'blue'] %}``` add 54 | 55 | ```twig 56 | {% if logged_in %} 57 | {% set roles = user.account.roles %} 58 | {% endif %} 59 | ``` 60 | 61 | 3. Change the body tag to 62 | 63 | ```twig 64 | 65 | ``` 66 | 67 | 3. Compare the `` tag for logged in and logged out users. What are the results when you remove the `if` statement? 68 | 69 | ------------------- 70 | 71 | 72 | ## Questions you may have... 73 | + How do I remove a class? 74 | + Where can I find other cool twig functions? 75 | + What happened to preprocess functions and the template.php file? 76 | + Why do we copy our template files from the classy theme? 77 | + How did you know the `user` variable was available? 78 | + Why did we do that? 79 | 80 | 81 | ## Done ☺ 82 | We're getting pretty good at this!. Let's see what [Exercise 10 - Twig Filters](exercise_10-twig-filters.md) has to offer... 83 | -------------------------------------------------------------------------------- /exercise_10-twig-filters.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 10: 4 | 5 | ## Advanced Twig 6 | 7 | ### Using filters 8 | 9 | [Twig filters](https://twig.symfony.com/doc/2.x/filters/index.html) allow you to make changes to markup right in the template. Let's use our node title to test some string filters. 10 | 11 | 1. First, we want to get the node title as a string. 12 | 13 | 2. Try `{{ kint(node) }}` in your theme's **node.html.twig** file. Under `Available methods` observe `label()`. It is a public function so we can use it in our twig template. 14 | 15 | 1. Near your other variable declaration in your theme's **node.html.twig** add: 16 | 17 | ```twig 18 | {% set title_string = node.label %} 19 | ``` 20 | 21 | 22 | 2. Test it with the following filters. 23 | 24 | 25 | ```twig 26 | clean_class: {{ title_string|clean_class }}
27 | upper: {{ title_string|upper }}
28 | length: {{ title_string|length }}
29 | ``` 30 | 31 | 32 | 3. If you have time, try creating arrays, loops and object to test some of the other amazing filters for twig See: https://twig.symfony.com/doc/2.x/filters/index.html. 33 | 34 | ## Questions you may have... 35 | + When should one use Twig filters instead of CSS or PHP? 36 | 37 | ## Done ☺ 38 | ¡No Pare! !Sigue! !Sigue! [Exercise 11 - Twig Blocks](exercise_11-twig-block.md) -------------------------------------------------------------------------------- /exercise_11-twig-block.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 11: 4 | 5 | ## Twig blocks and Drupal blocks 6 | 7 | Twig introduces another "block" concept within Drupal. However, a Twig "block" is not the same as a Drupal block. 8 | 9 | A Twig "block" is an element in the template file that can be overridden, independently from its inherited (parent) template. This means I can reuse the majority of a parent template and just change the one part I need to change. In Drupal 7, you would have to copy the whole template into a new file and be stuck trying to manage two independent templates or use PHP to conditionally include a new file. 10 | Twig "blocks" are used for inheritance and act as placeholders and replacements at the same time. 11 | 12 | [Twig block official documentation](http://twig.sensiolabs.org/doc/tags/extends.html) 13 | http://twig.sensiolabs.org/doc/tags/extends.html 14 | 15 | ### Using twig block inheritence 16 | 17 | In Drupal 8 core, we will use the original example Drupal block, "Powered by Drupal" block, to show you an example of using twig blocks. The "Powered by Drupal" Block utilizes the default block template. This template contains a twig block inside of it named `content`. 18 | 19 | We are going to copy the default block twig template into our theme, rename it and set it to only alter the value of the twig block. This way our new template will follow the default template, but only change the value inside of our Twig block. 20 | 21 | 1. First thing is to make sure that the "Powered by Drupal" block is displayed. If not, go to the block admin page and place the "Powered by Drupal" block into any of your theme's regions. 22 | 23 | 2. **Save** the configuration, and visit a page on the front end of your site. **Make sure you can see the block somewhere on your site**. 24 | 25 | 3. Locate and copy the **block.html.twig** template located at **MYDRUPAL/core/themes/classy/templates/block/block.html.twig** and copy it into your **/templates/block** folder of your custom theme. 26 | 27 | ```bash 28 | $ cd MYDRUPAL 29 | $ mkdir themes/custom/acme/templates/block 30 | $ cp core/themes/classy/templates/block/block.html.twig themes/custom/acme/templates/block/block.html.twig 31 | ``` 32 | 33 | 4. Open the new **block.html.twig** file in your preferred code editor. You should see the following code: 34 | 35 | ```twig 36 | {% 37 | set classes = [ 38 | 'block', 39 | 'block-' ~ configuration.provider|clean_class, 40 | 'block-' ~ plugin_id|clean_class, 41 | ] 42 | %} 43 | 44 | {{ title_prefix }} 45 | {% if label %} 46 | {{ label }} 47 | {% endif %} 48 | {{ title_suffix }} 49 | {% block content %} 50 | {{ content }} 51 | {% endblock %} 52 | 53 | ``` 54 | 55 | 5. We are going to copy and rename this file using information from our debug setup. View the source code on a page where the "Powered by Drupal" block is visible. Locate the code for the block in your browser's dev tools. You should see some additional code that looks like this: 56 | 57 | ```html 58 | 64 | ``` 65 | 66 | These are the template suggestions for different components of the page. These come from our theme and twig debug enabling. Drupal is telling you exactly how you can name your new template files to make sure they affect that component. The `x` at the start of a name refers to the template that is currently being used to build that component. Items higher on the list override lower ones. 67 | 68 | 6. So that it only affects the "Powered by Drupal" block, rename the `block.html.twig` file to `block--system-powered-by-block.html.twig` 69 | 70 | 71 | ```bash 72 | $ cd MYDRUPAL 73 | $ mv themes/custom/acme/templates/block/block.html.twig themes/custom/acme/templates/block/block--system-powered-by-block.html.twig 74 | ``` 75 | 76 | 77 | 7. Open the `block--system-powered-by-block.html.twig` file and replace all the code in it with the following code: 78 | 79 | ```twig 80 | {% extends '/core/themes/classy/templates/block/block.html.twig' %} 81 | 82 | {% block content %} 83 |
{{ 'I am powered by Drupal 8, haha!'|t }}
84 | {% endblock %} 85 | ``` 86 | 87 | 7. Clear cache. 88 | 89 | We only override the content inside the twig block name **"content"**, The rest of the items are still controlled by the default block.html.twig template file. If we had to make a change to all Drupal blocks, like add a wrapping div or a class to all blocks, we could make that change in our default template, and it would still apply to our overridden template. 90 | 91 | 92 | ## Questions you may have... 93 | + What is the `|t` in our twig template? 94 | + What is the `{% trans %}` component in twig? 95 | + Can I use all the Twig functions from SensioLabs? 96 | 97 | ## Done ☺ 98 | ... [Exercise 12 - Include SVG](exercise_12-twig-include-svg.md) ... 99 | -------------------------------------------------------------------------------- /exercise_12-twig-include-svg.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 12: 4 | 5 | ## Include an SVG inline. 6 | Often we want to include SVGs in our project because they load fast and can be easily manipulated using CSS. In this example, we use the Twig `include` function to add svg code to our template inline. This method can be used to include any snippet of HTML that needs to be accessed across templates. 7 | 8 | 9 | 1. Create a folder called **images** in your theme root. 10 | 11 | ```bash 12 | $ cd MYDRUPAL 13 | $ mkdir themes/custom/acme/images 14 | ``` 15 | 16 | 2. Copy and paste svg code into a file called **mysvg.svg** inside your newly created **images** folder 17 | 18 | ```bash 19 | $ touch themes/custom/acme/images/mysvg.svg 20 | ``` 21 | 22 | You can use an svg on your machine or the example below. 23 | 24 | ```svg 25 | 26 | 27 | 28 | ``` 29 | 30 | 3. Paste the following code into .twig file. 31 | 32 | ```twig 33 | {% include active_theme_path() ~ '/images/mysvg.svg' %} 34 | ``` 35 | 36 | 4. View a page that include the template you modified. 37 | 38 | 39 | ## Questions you may have... 40 | + Can other types of files can be included? 41 | + Where does the function `active_theme_path()` come from? 42 | 43 | 44 | ## Done ☺ 45 | Onward! [Exercise 13 - Preprocess Functions](exercise_13-preprocess.md) 46 | -------------------------------------------------------------------------------- /exercise_13-preprocess.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 13: 4 | 5 | ## .theme file and preprocess_page 6 | 7 | Don't make yourself responsible for updating every site on January 1st. Let the computer do the work for you and automatically set items, like the copyright date, to the current year. We will begin to dive into some more advanced elements of Drupal theming including preprocess functions and template variables. 8 | 9 | What is a preprocess function? A preprocess function is a function that allows us to manipulate (add, edit, or remove) our data before our template processes it. Hence, the "pre" in preprocess. It's powered by `hook_preprocess()` function. `hook_preprocess_HOOK()` allows modules to preprocess theme variables, but for a specific theme hook, like node, page, or menu\_link. For our example, we will manipulate the data before it goes to the `page` twig template **page.html.twig**, so we will use 10 | "hook\_preprocess\_**page**()" 11 | 12 | ### Create our .theme file 13 | 14 | 1. Create a file at the root of the Acme theme named **acme.theme** and open in your preferred code editor 15 | 16 | ```bash 17 | $ cd MYDRUPAL 18 | $ touch themes/custom/acme/acme.theme 19 | ``` 20 | 21 | 2. Write the following code in your new **acme.theme** file: 22 | 23 | ```php 24 | at the end, but there should be a line of whitespace.** 31 | 32 | 4. Open up **page.html.twig** and near the closing footer element, print out our new variable using the following code: 33 | 34 | ```twig 35 | 38 | ``` 39 | 3. Clear cache and verify your copyright is on the page. 40 | 41 | 42 | ### What year is it? 43 | 44 | Great, it’s printing our variable, but we’re still not much better off than simply adding a block to say the copyright date manually. Let's start adding some logic in **acme.theme**. 45 | 46 | 1. Open **acme.theme** and add some php around our new variable so that it can print the current year instead of our filler message: 47 | 48 | ```php 49 | date('Y')) 53 | ); 54 | } 55 | ``` 56 | **Note that there is not a closing ?> at the end, but there should be a line of whitespace.** 57 | 58 | ##Questions you may have... 59 | + Where did you find that function name? I would never have guessed that. 60 | + Why don’t I close the PHP tags? 61 | + Why does the new variable have a t() function around it? 62 | + What’s the best way to add markup and styles to our variable? 63 | + Could I have done this on the twig template? 64 | 65 | ##Done ☺ 66 | [Exercise 14 - Template Suggestions](exercise_14-new-template-suggestions.md) is ready to bat. 67 | -------------------------------------------------------------------------------- /exercise_14-new-template-suggestions.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 14: 4 | 5 | ## Create new template suggestions 6 | 7 | In Drupal 7, we used to create new template suggestions in our preprocess functions. In Drupal 8, we use two new functions to create new suggestions for Twig templates. 8 | 9 | These functions are `hook_theme_suggestions_alter()` function and the more targeted, `hook_theme_suggestions_HOOK_alter()` function. The `HOOK` refers to the theme hook being used, like `node` or `page` or `menu_link`. This `HOOK` helps to focus in your suggestions to a specific theme component. In this exercise, we will create new suggestions for our 'node' templates based off if the user is logged in or not. 10 | 11 | 1. Open the **acme.theme** file and add the following function: 12 | 13 | ```php 14 | function acme_theme_suggestions_node_alter(&$suggestions, $variables, $hook) { 15 | // Our code will go here. 16 | } 17 | ``` 18 | 19 | 2. Inside of our new function, add the following code: 20 | 21 | ```php 22 | ... 23 | if (\Drupal::currentUser()->isAuthenticated()) { 24 | $bundle = $variables['elements']['#node']->bundle(); 25 | $mode = $variables['elements']['#view_mode']; 26 | $view_mode = strtr($mode, '.', '_'); 27 | 28 | $suggestions[] = 'node__' . $bundle . '__logged_in'; 29 | $suggestions[] = 'node__' . $view_mode . '__logged_in'; 30 | } 31 | ... 32 | ``` 33 | This will create a new suggestion for content types and node view modes for when the viewer is logged in. 34 | 35 | 3. Clear your caches and visit a node page. View the source code and see if you can find your new template suggestions at the start of your node output. 36 | 37 | 38 | ## Questions you may have... 39 | + What is a theme suggestion HOOK? 40 | + What do I do with these new theme suggestions? 41 | 42 | 43 | ## Done ☺ 44 | Yes, there's more. [Exercise 15 - Add Classes with PHP](exercise_15-preprocess-add-classses.md) 45 | -------------------------------------------------------------------------------- /exercise_15-preprocess-add-classses.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 15: 4 | 5 | ## Adding classes in .theme 6 | 7 | A major job of any theme is to give us control over classes. We use classes to control styling, layouts, and javascript. Drupal and Twig give us many different ways to do this. 8 | 9 | In this exercise, we are going to use `hook_preprocess_html` to gain control over the classes attached to our body tag. 10 | 11 | ### Add classes to our body tag 12 | 13 | 1. Navigate to your theme root, and open your **acme.theme** file. 14 | 15 | 2. Add the following code: 16 | 17 | ```php 18 | function acme_preprocess_html(&$variables) { 19 | // Our code will go here 20 | } 21 | ``` 22 | 23 | 3. We want to add our own classes to the body tag. This first one will be for all pages. The second one will set a class for each region that has content. Once completed, save and clear caches. Then view the source code of a page and confirm that our new class is applied to the body tag. 24 | 25 | ```php 26 | // Add 'my-class' to all pages. 27 | function acme_preprocess_html(&$variables) { 28 | $variables['attributes']['class'][] = 'my-excellent-class'; 29 | } 30 | ``` 31 | 32 | 4. Next, add the following line at the top of your **acme.theme** below `getActiveTheme()->getName(); 42 | 43 | $regions = system_region_list($theme); 44 | 45 | foreach ($regions as $region => $region_name) { 46 | $region_class = Html::getClass($region); 47 | if (!empty($variables['page'][$region])) { 48 | $variables['attributes']['class'][] = $region_class . '-active'; 49 | } 50 | } 51 | ... 52 | ``` 53 | 54 | We are going to use the `Html` PHP Class and its `getClass` method to process our values and make sure they are properly formatted class names. This is the replacement to drupal\_html\_class() and drupal\_clean\_css_identifier(). Because we use this, we have to make sure to declare our dependency on that class. That is why we added the code in part 4. Otherwise, PHP would not know what you are talking about and we would get a delightful PHP fatal error. 55 | 56 | 57 | Go back to your website and refresh. Check out our body tag. Do you see the active region classes? This may be helpful if you want to trigger javascript or CSS in one region based on if there is stuff in another region. 58 | 59 | ## Questions you may have... 60 | + How would I know to use `use Drupal\Component\Utility\Html;`? 61 | 62 | 63 | ## Done ☺ 64 | Can you believe it? [Exercise 16 - Form Alter](exercise_16-form-alter.md) 65 | -------------------------------------------------------------------------------- /exercise_16-form-alter.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 16: 4 | 5 | ## Simple form altering 6 | 7 | A big part of Drupal are forms. For example, content entry forms to add and manage content, search forms to find content, and settings forms to control the numerous parts of our site. Default forms are pretty awesome, but sometimes they just aren't enough. Now and then we need to alter a label, default value, or add libraries for styling or cool javascript functionality. 8 | 9 | Form API and Drupal form alters are still in Drupal 8 and are still incredibly powerful tools to control Drupal. When using a form_alter for theming it is best to keep it to simple alterations such as changing labels, adding/removing classes, editing div structure, and adding HTML5 placeholders like in our following examples. Anything else should really be done in a module form alter. 10 | 11 | **For this exercise, make sure the search block is in a region and visible.** 12 | 13 | ### Simple form altering with `hook_form_alter` function 14 | 15 | 1. Navigate to your theme root 16 | 17 | 2. Locate and open your **acme.theme** file, and add the following code. 18 | 19 | ```php 20 | function acme_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) { 21 | if ($form_id == 'search_block_form') { 22 | // Add placeholder text 23 | $form['keys']['#attributes']['placeholder'] = t('Search'); 24 | } 25 | } 26 | ``` 27 | 2. Clear cache, and observe your search block. 28 | 29 | ### Simple form altering with `hook_form_FORM_ID_alter` function 30 | 31 | This function is refined to only affect the form with the matching **FORM_ID** in the function name. 32 | 33 | 1. Replace the above function with the following code: 34 | 35 | ```php 36 | function acme_form_search_block_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) { 37 | // Add placeholder text 38 | $form['keys']['#attributes']['placeholder'] = t('Search this site.'); 39 | } 40 | ``` 41 | 42 | ### Adding a javascript via a library to a form 43 | Let's say we want to leverage one of the libraries from core or contrib when our form is visible. 44 | 45 | 1. Search and remove any other instances of kint() in your theme. Make sure the search_kint module is enabled. 46 | 47 | 1. View your site as an anonymous user by opening a new browser or private window. Verify that `form.js` is not included. 48 | 49 | 2. Add the following to the `acme_form_search_block_form_alter` function in **acme.theme** 50 | 51 | ```php 52 | kint(\Drupal::service('library.discovery')->getLibrariesByExtension('core')); 53 | ``` 54 | 55 | 3. Search for `form.js` in the the search form provided by kint. You'll see it highlighted under parent `drupal.form`. 56 | 57 | 7. Navigate back to your theme root directory and open your **acme.theme** file. 58 | 59 | 9. Add the following code to your `acme_form_search_block_form_alter` function: 60 | 61 | ```php 62 | // Attach a library from our theme to a form. 63 | $form['#attached']['library'][] = 'core/drupal.form'; 64 | ``` 65 | 66 | 10. Clear your caches. 67 | 68 | If everything worked, the form.js file will be loaded for whenever the search block is present even for anonymous users. 69 | 70 | 71 | 72 | 73 | ## Questions you may have 74 | * What is the benefit of using **hook\_form\_FORM\_ID\_alter** rather than just **hook\_form\_alter**? 75 | * What else can I do with form alters? 76 | * Can I use `[#attached]` in any function? 77 | 78 | ## Done ☺ 79 | You're on fire! Don't stop now! [Exercise 17 - Responsive Images](exercise_17-responsive.md) 80 | -------------------------------------------------------------------------------- /exercise_17-responsive.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 17: 4 | 5 | ## Breakpoints and setting up responsive images 6 | 7 | Using the core Breakpoint module, you can now define your theme’s breakpoints in code. There is no UI for doing this, but we have a solid API from the Breakpoint module that allows modules and themes to define breakpoints and breakpoint groups, as well as resolution modifiers that come in handy when targeting devices with HD/Retina displays. To keep this exercise a little simpler, we won't worry about modifiers for HD displays. 8 | 9 | Find **toolbar.breakpoints.yml** for and observe how the breakpoints correspond to the administration menu styles. 10 | 11 | **Make sure Breakpoint (breakpoint) and Responsive Image (responsive_image) modules are enabled** 12 | 13 | 1. Visit your modules page, under the "Extend" button in the admin menu. 14 | 15 | 2. Enable the Breakpoint and Responsive Image module if they are not enabled 16 | 17 | ### Create the breakpoints 18 | 19 | 1. Navigate to your theme root directory 20 | 21 | 2. Create a new file called **acme.breakpoints.yml** 22 | 23 | ```bash 24 | $ cd MYDRUPAL 25 | $ touch themes/custom/acme/acme.breakpoints.yml 26 | ``` 27 | 3. Open the file in your preferred code editor and add the following. 28 | 29 | ``` 30 | acme.mobile: 31 | label: mobile 32 | mediaQuery: '(min-width: 0px)' 33 | weight: 0 34 | multipliers: 35 | - 1x 36 | acme.tablet: 37 | label: tablet 38 | mediaQuery: '(min-width: 1040px)' 39 | weight: 1 40 | multipliers: 41 | - 1x 42 | acme.desktop: 43 | label: desktop 44 | mediaQuery: '(min-width: 1200px)' 45 | weight: 2 46 | multipliers: 47 | - 1x 48 | ``` 49 | 50 | 3. Clear cache. 51 | 52 | Together, all of these breakpoints are referred to as a **breakpoint group**. 53 | The weight of these is crucial so that the proper image styles get swapped in depending on viewport size and screen resolution. A breakpoint’s weight should be listed from smallest min-width to largest min-width. Also, note the “multipliers” section. Breakpoint allows for different pixel density multipliers for displaying crisper images on HD/Retina displays: 1x, 1.5x and 2x. 54 | 55 | 56 | ### Create your Responsive image style 57 | 58 | We now need to bring the breakpoint group information and the image styles all together. We do this with a **Responsive image style**. 59 | 60 | 1. Navigate to **/admin/config/media/responsive-image-style** and "Add responsive image style" 61 | 62 | 2. Create the style using the following information: 63 | + Label: `Acme Image` 64 | + Breakpoint group: select `Acme` 65 | + For each breakpoint, choose: `Select a single image style` and set the image style that you want along with each breakpoint. 66 | + 1X DESKTOP [(MIN-WIDTH: 961PX)] => Large (480x480) 67 | + 1X TABLET [(MIN-WIDTH: 601PX)] => Medium (220x220) 68 | + 1X MOBILE [(MIN-WIDTH: 0PX)] => Thumbnail (100x100) 69 | + Fallback image style: Medium (220x220) . 70 | 71 | We now have a Responsive image style, with the breakpoints matched to an image style. Now it is time to apply our Responsive image style to an image field 72 | 73 | ### Configure the display of our image field 74 | 75 | 1. Navigate to the Manage display page for the "Article" content type. 76 | 77 | 2. Change the format of the `Image` field to use `Responsive image`. 78 | 79 | 3. Select `Acme Image` as the Responsive image style we want to use. 80 | 81 | 4. Click **Update**, and then click **Save** 82 | 83 | 5. Clear caches, and then visit an article node with an image. 84 | 85 | If you have responsive testing tools in your browser, use those to emulate different screen sizes. If not just make your window smaller until you can start to see the images changing size. 86 | 87 | ## Done ☺ 88 | Almost there! [Exercise 18 - Custom Theme Settings 1](exercise_18-theme-settings1.md) 89 | -------------------------------------------------------------------------------- /exercise_18-theme-settings1.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 18: 4 | 5 | ## Creating a theme settings file 6 | 7 | Often we need to create small, simple little settings for our theme that can make it more reusable and customizable across sites. We can create new theme settings on our theme's settings page that we can use to help other users configure and customize our theme. For this, we will utilize two additional files. **acme.settings.yml** and **theme-settings.php** file. 8 | 9 | 1. Go to MYDRUPAL.LOCAL/admin/appearance/settings/acme in your browser to see what settings are available by default. 10 | 11 | 2. Create a new file called **acme.settings.yml** in your theme root and open in your preferred code editor. 12 | 13 | ```bash 14 | $ cd MYDRUPAL 15 | $ touch themes/custom/acme/acme.settings.yml 16 | ``` 17 | 18 | 3. Add the following code: 19 | 20 | ``` 21 | features: 22 | copyright_holder: '' 23 | search_placeholder: 'Search site' 24 | ``` 25 | 26 | 5. Create a new file in your theme directory called **theme-settings.php** 27 | 28 | ```bash 29 | $ cd MYDRUPAL 30 | $ touch themes/custom/acme/theme-settings.php 31 | ``` 32 | 33 | 6. Add the following code to that file. 34 | 35 | ```php 36 | 'textfield', 42 | '#title' => t('Copyright Holder'), 43 | '#default_value' => theme_get_setting('copyright_holder'), 44 | '#description' => t('This appears in the footer.'), 45 | '#weight' => -10, 46 | ]; 47 | $form['color'] = [ 48 | '#type' => 'select', 49 | '#title' => t('Color'), 50 | '#options' => [ 51 | 'blue' => t('Blue'), 52 | 'green' => t('Green'), 53 | 'yellow' => t('Yellow') 54 | ], 55 | '#default_value' => theme_get_setting('color'), 56 | '#description' => t('Choose a color'), 57 | '#weight' => -10, 58 | ]; 59 | } 60 | ``` 61 | 62 | 7. Clear cache, then go to our **Appearance** page. Click on **Settings** for your custom theme. You should see two new options for the settings we added. 63 | 64 | 8. Feel free to customize those values. We will use them in the next exercises. 65 | 66 | ## Questions you may have... 67 | + What is `features` in the ***.settings.yml**? 68 | + Where do I find documentation for creating forms? 69 | 70 | 71 | ## Done ☺ 72 | One more to go! [Exercise 19 - Custom Theme Settings 2](exercise_19-theme-settings2.md) 73 | -------------------------------------------------------------------------------- /exercise_19-theme-settings2.md: -------------------------------------------------------------------------------- 1 | #### [Drupal 8 Theming](README.md) 2 | 3 | # Exercise 19: 4 | 5 | ## Apply our theme settings to our custom variables 6 | 7 | Now that we have created some custom theme settings and we can customize them through the UI, let's put them to use. We will capitalize on the functionality we created in an earlier exercise. We will alter our **preprocess_page()** to include the "copyright_holder" variable that will be customizable with one of our new theme settings. 8 | 9 | ### Customize the copyright holder 10 | 11 | 2. In our **acme.theme** file, locate the `acme_preprocess_page()` function. 12 | 13 | 5. Modify the code to include the copyright_holder setting. 14 | 15 | ```php 16 | $variables['copyright'] = t("Copyright @date @holder", 17 | array( 18 | '@date' => date('Y'), 19 | '@holder' => theme_get_setting('copyright_holder') 20 | ) 21 | ); 22 | ``` 23 | 24 | 6. Clear caches and see if it worked. Change the value on the theme settings page if you haven't already (otherwise nothing will really change). 25 | 26 | We have declared our copyright_holder in our theme's settings, and it will override the site name as being the default copyright holder. A simple example, but powerful. 27 | 28 | ## Questions you may have... 29 | + How can I get one of the default theme settings, like the logo? 30 | + What would steps are needed to makek the color switcher on the theme settings page do something? 31 | 32 | ## Done ☺ 33 | 34 | Congratulations! You're all done! -------------------------------------------------------------------------------- /local.services.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | session.storage.options: 3 | # Default ini options for sessions. 4 | # 5 | # Some distributions of Linux (most notably Debian) ship their PHP 6 | # installations with garbage collection (gc) disabled. Since Drupal depends 7 | # on PHP's garbage collection for clearing sessions, ensure that garbage 8 | # collection occurs by using the most common settings. 9 | # @default 1 10 | gc_probability: 1 11 | # @default 100 12 | gc_divisor: 100 13 | # 14 | # Set session lifetime (in seconds), i.e. the time from the user's last 15 | # visit to the active session may be deleted by the session garbage 16 | # collector. When a session is deleted, authenticated users are logged out, 17 | # and the contents of the user's $_SESSION variable is discarded. 18 | # @default 200000 19 | gc_maxlifetime: 200000 20 | # 21 | # Set session cookie lifetime (in seconds), i.e. the time from the session 22 | # is created to the cookie expires, i.e. when the browser is expected to 23 | # discard the cookie. The value 0 means "until the browser is closed". 24 | # @default 2000000 25 | cookie_lifetime: 2000000 26 | # 27 | # Drupal automatically generates a unique session cookie name based on the 28 | # full domain name used to access the site. This mechanism is sufficient 29 | # for most use-cases, including multi-site deployments. However, if it is 30 | # desired that a session can be reused across different subdomains, the 31 | # cookie domain needs to be set to the shared base domain. Doing so assures 32 | # that users remain logged in as they cross between various subdomains. 33 | # To maximize compatibility and normalize the behavior across user agents, 34 | # the cookie domain should start with a dot. 35 | # 36 | # @default none 37 | # cookie_domain: '.example.com' 38 | # 39 | twig.config: 40 | # Twig debugging: 41 | # 42 | # When debugging is enabled: 43 | # - The markup of each Twig template is surrounded by HTML comments that 44 | # contain theming information, such as template file name suggestions. 45 | # - Note that this debugging markup will cause automated tests that directly 46 | # check rendered HTML to fail. When running automated tests, 'debug' 47 | # should be set to FALSE. 48 | # - The dump() function can be used in Twig templates to output information 49 | # about template variables. 50 | # - Twig templates are automatically recompiled whenever the source code 51 | # changes (see auto_reload below). 52 | # 53 | # For more information about debugging Twig templates, see 54 | # https://www.drupal.org/node/1906392. 55 | # 56 | # Not recommended in production environments 57 | # @default false 58 | debug: true 59 | # Twig auto-reload: 60 | # 61 | # Automatically recompile Twig templates whenever the source code changes. 62 | # If you don't provide a value for auto_reload, it will be determined 63 | # based on the value of debug. 64 | # 65 | # Not recommended in production environments 66 | # @default null 67 | auto_reload: null 68 | # Twig cache: 69 | # 70 | # By default, Twig templates will be compiled and stored in the filesystem 71 | # to increase performance. Disabling the Twig cache will recompile the 72 | # templates from source each time they are used. In most cases the 73 | # auto_reload setting above should be enabled rather than disabling the 74 | # Twig cache. 75 | # 76 | # Not recommended in production environments 77 | # @default true 78 | cache: true 79 | renderer.config: 80 | # Renderer required cache contexts: 81 | # 82 | # The Renderer will automatically associate these cache contexts with every 83 | # render array, hence varying every render array by these cache contexts. 84 | # 85 | # @default ['languages:language_interface', 'theme', 'user.permissions'] 86 | required_cache_contexts: ['languages:language_interface', 'theme', 'user.permissions'] 87 | # Renderer automatic placeholdering conditions: 88 | # 89 | # Drupal allows portions of the page to be automatically deferred when 90 | # rendering to improve cache performance. That is especially helpful for 91 | # cache contexts that vary widely, such as the active user. On some sites 92 | # those may be different, however, such as sites with only a handful of 93 | # users. If you know what the high-cardinality cache contexts are for your 94 | # site, specify those here. If you're not sure, the defaults are fairly safe 95 | # in general. 96 | # 97 | # For more information about rendering optimizations see 98 | # https://www.drupal.org/developing/api/8/render/arrays/cacheability#optimizing 99 | auto_placeholder_conditions: 100 | # Max-age at or below which caching is not considered worthwhile. 101 | # 102 | # Disable by setting to -1. 103 | # 104 | # @default 0 105 | max-age: 0 106 | # Cache contexts with a high cardinality. 107 | # 108 | # Disable by setting to []. 109 | # 110 | # @default ['session', 'user'] 111 | contexts: ['session', 'user'] 112 | # Tags with a high invalidation frequency. 113 | # 114 | # Disable by setting to []. 115 | # 116 | # @default [] 117 | tags: [] 118 | # Cacheability debugging: 119 | # 120 | # Responses with cacheability metadata (CacheableResponseInterface instances) 121 | # get X-Drupal-Cache-Tags and X-Drupal-Cache-Contexts headers. 122 | # 123 | # For more information about debugging cacheable responses, see 124 | # https://www.drupal.org/developing/api/8/response/cacheable-response-interface 125 | # 126 | # Not recommended in production environments 127 | # @default false 128 | http.response.debug_cacheability_headers: false 129 | factory.keyvalue: 130 | {} 131 | # Default key/value storage service to use. 132 | # @default keyvalue.database 133 | # default: keyvalue.database 134 | # Collection-specific overrides. 135 | # state: keyvalue.database 136 | factory.keyvalue.expirable: 137 | {} 138 | # Default key/value expirable storage service to use. 139 | # @default keyvalue.database.expirable 140 | # default: keyvalue.database.expirable 141 | # Allowed protocols for URL generation. 142 | filter_protocols: 143 | - http 144 | - https 145 | - ftp 146 | - news 147 | - nntp 148 | - tel 149 | - telnet 150 | - mailto 151 | - irc 152 | - ssh 153 | - sftp 154 | - webcal 155 | - rtsp 156 | 157 | # Configure Cross-Site HTTP requests (CORS). 158 | # Read https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS 159 | # for more information about the topic in general. 160 | # Note: By default the configuration is disabled. 161 | cors.config: 162 | enabled: false 163 | # Specify allowed headers, like 'x-allowed-header'. 164 | allowedHeaders: [] 165 | # Specify allowed request methods, specify ['*'] to allow all possible ones. 166 | allowedMethods: [] 167 | # Configure requests allowed from specific origins. 168 | allowedOrigins: ['*'] 169 | # Sets the Access-Control-Expose-Headers header. 170 | exposedHeaders: false 171 | # Sets the Access-Control-Max-Age header. 172 | maxAge: false 173 | # Sets the Access-Control-Allow-Credentials header. 174 | supportsCredentials: false 175 | services: 176 | cache.backend.null: 177 | class: Drupal\Core\Cache\NullBackendFactory 178 | -------------------------------------------------------------------------------- /settings.local.php: -------------------------------------------------------------------------------- 1 |