Drupal 8 主题开发指南

原文链接:Theming Drupal 8

这个指南说的是如何在Drupal 8 中创建模板。

比较值得关注的是 Drupal 8 和 7 之间模板系统的变化。 这个指南包括 Drupal 7 和 Drupal 8 之间的关联,然后解释一些新的概念和方法。

除了 drupal.org, 您还可以在别的地方找到模板开发指南,比如 sqndr's excellent Drupal 8 theming guide

对主题开发者来说想要收集比较有用的材料,请查看 Theming and Front End Development with Drupal


以下是英文原文,可对照阅读

Last updated May 16, 2015. Created on February 3, 2013.
Edited by sqndr, wusel, pbulebar, batigolix. You can edit this page, too.

This guide is about creating themes for Drupal 8.

Quite significant changes were introduced in the theme system between Drupal 8 and 7. This guide contains information that applies to Drupal 8 and refers sometimes to Drupal 7 in order to explain new concepts and approaches.

Outside drupal.org other theming guides can be found, like for example sqndr's excellent Drupal 8 theming guide.

For a collection of useful materials for themers, see Theming and Front End Development with Drupal.

Taxonomy upgrade extras: 

用 .info.yml 定义一个 Drupal 8 主题

原文链接:Defining a theme with an .info.yml file

想要创建一个 Drupal 8 模板, 第一步你需要建一个 THEMENAME.info.yml 文件,此文件提供了主题的一些元数据给给 Drupal 调用。比较类似于 模块 和安装 profiles 中的定义,但是在.info.yml文件里将“type”键设为“theme”用来区分他们的不同,这个比较重要。

此页面提供演示的 THEMNAME.info.yml 文件和一些此文件可使用的功能概述。

创建一个 .info.yml 文件

在模板文件夹下,创建一个自己的文件夹,我们拿 mahonghong 举例,然后在 mahonghong 文件夹下,创建一个 mahonghong.info.yml,此时在你的网站内的 Manage > Appearance (http://example.com/admin/appearance),应该就能看到这个模板了。

Example

name: Fluffiness
type: theme
description: 'A cuddly theme that offers extra fluffiness.'
package: Custom
core: 8.x
libraries:
  - fluffiness/globalstyling
stylesheets-remove:
  - '@classy/css/layout.css'
  - core/assets/vendor/normalize-css/normalize.css
regions:
  header: Header
  content: Content
  sidebar_first: 'Sidebar first'
  footer: Footer

在你的 Drupal 网站中,你可以查看系统自带的主题,所以你可以找到更多关于 .info.yml 的例子。比如说在 core/themes/stark 文件夹下,你可以找到 stark.info.yml。

Keys

下面的几点,将包含关于主题的信息和基本的功能。

name: Fluffiness

必填字段, 这个名字将显示在你需要激活主题的界面。

description: 'An extra cuddly Drupal theme available in grey and blue.'

必填字段, 描述和上面的名字相同,都是要显示在主题选择界面的。

package: Custom

这个 Package 将允许你在管理主题界面归类。

type: theme

必填字段,类型,告诉 Drupal 你这是什么类型的扩展文件。比如,模块,模板。对于主题来说,这里永远设置成 "theme"。

base theme: classy

主题可以继承其他的主题,设置成 base 主题,详情查看 https://www.drupal.org/node/2165673

core: 8.x

必填字段,具体说明你的主题和 Drupal 核心兼容的版本。

version: 8.x-1.0

对于 drupal.org 网站上的模块,版本号会被脚本自动填写。不建议手动去填写,可忽略。

screenshot: fluffiness.png

在你的主题管理界面显示截图,如果您未使用此项,drupal 会自动查找并显示文件夹下的 screenshot.png。

libraries:
  - fluffiness/globalstyling

此库文件可以包含 css 和 js 到所有的页面,详情,请阅读 https://www.drupal.org/node/2216195

如果库文件被定义为:

globalstyling:
  version: 1.x
  css:
    theme:
      css/style.css: {}
      css/print.css: { media: print }

在 html 中将得到如下结果:
 

<link rel="stylesheet" href="css/style.css" media="all" />
<link rel="stylesheet" href="css/print.css" media="print" />
stylesheets-remove:
  - core/assets/vendor/normalize-css/normalize.css # 1
  - '@bartik/css/style.css' # 2

注:

- #1: stylesheets-remove 将移除由另一个模板或模块添加的样式表。 CSS 文件完整路径将会显示,上面的只是文件名,为了适应多个文件的名称相同。

- #2:在删除Drupal核心文件(如jQuery UI中的CSS文件)时,要用完整文件路径。当文件属于某个模块或主题的组成部分时,要使用标记。注意,使用标记时需要进行引用,因为@在YAML中是一个保留标志。

regions:
  - header: Header
  - content: Content # required!
  - sidebar_first: 'Sidebar first'
  - footer: Footer

 下面的 header,content,sidebar_first,footer 区块隶属于 regions。
 content 是必须要存在的区域(region)。

更多信息

Drupal 8 主题文件夹结构

原文链接:Theme folder structure

主题就是包含了一系列定义前端层展示的文件集合。你还可以创建一个或更更多“子主题”,或者基于一个主题的变体。只有.info.yml 这个文件是必须有的,但是大多数主题和子主题也会用到其他一些文件。这个页面将会在典型的主题或子主题中列出常用的文件和文件夹。

主题位置

你必须将主题放在你的 Drupal 安装包的“themes”文件夹里。注意 Drupal 的核心主题如 Bartik 和 Seven 是放置在安装包的 core/themes 文件夹里。

最好的做法(good practice)是把贡献主题放在一个叫做“contrib”的子目录里,而你自定义的主题放置在“custom”文件夹里。

你的 Drupal 安装包的结构可能会是像这样的:

  
  |-core
  |  |-modules
  |  |-themes
  |  |  |-bartik
  |  |  |-seven
  ..
  |-modules
  |-themes
  |  |-contrib
  |  |  |-zen
  |  |  |-basic
  |  |  |-bluemarine
  |  |-custom
  |  |  |-fluffiness

主题文件夹结构

这是一个典型的主题目录结构示例,其包含的文件和文件夹:

  
  |-fluffiness.breakpoints.yml
  |-fluffiness.info.yml
  |-fluffiness.libraries.yml
  |-fluffiness.theme
  |-config
  |  |-install
  |  |  |-fluffiness.settings.yml
  |  |-schema
  |  |  |-fluffiness.schema.yml
  |-css
  |  |-style.css
  |-js
  |  |-fluffiness.js
  |-images
  |  |-buttons.png
  |-logo.png
  |-screenshot.png
  |-templates
  |  |-maintenance-page.html.twig
  |  |-node.html.twig

下面是一些在主题中常见的文件的描述。

*.info.yml

一个主题必须包含一个 .info.yml 文件用于定义该主题。同时 .info.yml 还定义了一些元数据(meta data),样式表(style sheets),以及区块区域。这是主题中唯一的必须包含的文件。

*.libraries.yml

.libraries.yml 文件是用于定于JavaScript库的 那些可以由主题来加载的。

*.breakpoints.yml

断点(Breakpoints)定义在响应式设计中需要针对不同设备去响应的位置。断点定义在*.breakpoints.yml 文件里

*.theme

.theme文件是一个包含了所有条件逻辑和要输出的(预)处理数据的PHP文件。

css

最好的做法(good practice)是将 .css 文件放在“css”子目录里。Drupal 8 核心主题组织 CSS 文件是遵循 SMACCS 样式指南的

js

主题特定的 JavaScript 文件放在“js”文件夹里。一个主题想要加载 JavaScript 文件必须先定义在 .libraries.yml文件里

Images

最好的做法(good practice)是将图片放在“images”子目录里。

Screenshot

如果 screenshot.png 文件在一个主题里被发现,那么它将被用在外观(Appearance)页面。通常你可以在 .info.yml file中定义缩略图

如果 logo.svg 文件在一个主题里被发现,那么它将被用在网站的头部。Logo也可以通过“外观” > “设置”( Appearance > Settings)里上传。

Templates

模板(Templates)通常包含了 HTML 标记和一些逻辑。相比 Drupal 7,Drupal 8 模板(*.html.twig 文件)必须要放在“templates”子目录里。如果你遵循特定的命名约定,那么 Drupal 将会用你提供的模板来替换默认的模板,允许你去覆写默认的输出。

核心主题Bartik文件夹结构

举个例子,看一下 Bartik 文件夹结构是放在 core/themes/bartik 里。

  
  |-bartik.breakpoints.yml
  |-bartik.info.yml
  |-bartik.libraries.yml
  |-bartik.theme
  |-color
  |  |-color.inc
  |  |-preview.css
  |  |-preview.html
  |  |-preview.js
  |-config
  |  |-schema
  |  |  |-bartik.schema.yml
  |-css
  |  |-colors.css
  |  |-layout.css
  |  |-maintenance-page.css
  |  |-print.css
  |-images
  |  |-add.png
  |  |-required.svg
  |  |-tabs-border.png
  |-logo.svg
  |-screenshot.png
  |-templates
  |  |-block--search-form-block.html.twig
  |  |-block--system-branding-block.html.twig
  |  |-block--system-menu-block.html.twig
  |  |-block.html.twig
  |  |-comment.html.twig
  |  |-field--taxonomy-term-reference.html.twig
  |  |-maintenance-page.html.twig
  |  |-node.html.twig
  |  |-page.html.twig
  |  |-status-messages.html.twig

更多信息

Coding standards: CSS file organization


以下是英文原文,可对照阅读

Last updated May 5, 2015. Created on October 3, 2014.
Edited by rteijeiro, pachabhaiya, zakiya, nitishchopra. You can edit this page, too.

A theme is a collection of files that define the presentation layer. You can also create one or more "sub-themes" or variations on a theme. Only the .info.yml file is required, but most themes and sub-themes will use other files as well. This page lists the files and folders that are found in a typical theme or sub-theme.

Location of themes

You must place themes in the "themes" folder of your Drupal installation. Note that Drupal core themes such as Bartik and Seven are located in the core/themes folder of your installation.

It is good practice to place the contributed themes in a sub folder named "contrib" and your own themes in a folder named "custom".

The (partial) structure of your Drupal installation could look as follows:

  
  |-core
  |  |-modules
  |  |-themes
  |  |  |-bartik
  |  |  |-seven
  ..
  |-modules
  |-themes
  |  |-contrib
  |  |  |-zen
  |  |  |-basic
  |  |  |-bluemarine
  |  |-custom
  |  |  |-fluffiness

Theme folder structure

This is an example of the files and folders that are found in typical theme folder structure:

  
  |-fluffiness.breakpoints.yml
  |-fluffiness.info.yml
  |-fluffiness.libraries.yml
  |-fluffiness.theme
  |-config
  |  |-install
  |  |  |-fluffiness.settings.yml
  |  |-schema
  |  |  |-fluffiness.schema.yml
  |-css
  |  |-style.css
  |-js
  |  |-fluffiness.js
  |-images
  |  |-buttons.png
  |-logo.png
  |-screenshot.png
  |-templates
  |  |-maintenance-page.html.twig
  |  |-node.html.twig

Below is a description of the most common files that you can find in a theme.

*.info.yml

A theme must contain an .info.yml file to define the theme. Among other things the .info.yml files defines meta data, style sheets, and block regions. This is the only required file in the theme.

*.libraries.yml

The .libraries.yml file is used to define JavaScript libraries that can be loaded by the theme.

*.breakpoints.yml

Breakpoints define where a responsive design needs to change in order to respond to different devices. Breakpoints are defined in a .breakpoints.yml file

*.theme

The .theme file is a PHP file that contains all the conditional logic and data (pre)processing of the output.

css

It is good practice to store .css files in the 'css' sub folder. Drupal 8 core themes organize CSS files following the SMACCS style guide. For a theme to load CSS files they must be defined in .libraries.yml file and can be overridden or removed in .info.yml file.

js

Theme specific JavaScript files are stored in the 'js' folder. For a theme to load JavaScript files they must be defined in .libraries.yml file.

Images

It is good practice to store images in the 'images' sub folder.

Screenshot

If a screenshot.png file is found in the theme folder then it will be used on the Appearance page. Alternatively you can define a screenshot in .info.yml file.

If a logo.svg file is found in the theme folder, then it will be used in the header of the website. Logos can also be uploaded at Appearance > Settings.

Templates

Templates usually provide HTML markup and some logic. Contrary to Drupal 7, in Drupal 8 templates (*.html.twig files) must be stored in the 'templates' sub folder. If you follow particular naming conventions, then Drupal will replace the default templates by the ones you provide, allowing you to override the default output.

Core theme Bartik folder structure

For an example, look at the Bartik folder structure that is located at core/themes/bartik:

  
  |-bartik.breakpoints.yml
  |-bartik.info.yml
  |-bartik.libraries.yml
  |-bartik.theme
  |-color
  |  |-color.inc
  |  |-preview.css
  |  |-preview.html
  |  |-preview.js
  |-config
  |  |-schema
  |  |  |-bartik.schema.yml
  |-css
  |  |-colors.css
  |  |-layout.css
  |  |-maintenance-page.css
  |  |-print.css
  |-images
  |  |-add.png
  |  |-required.svg
  |  |-tabs-border.png
  |-logo.svg
  |-screenshot.png
  |-templates
  |  |-block--search-form-block.html.twig
  |  |-block--system-branding-block.html.twig
  |  |-block--system-menu-block.html.twig
  |  |-block.html.twig
  |  |-comment.html.twig
  |  |-field--taxonomy-term-reference.html.twig
  |  |-maintenance-page.html.twig
  |  |-node.html.twig
  |  |-page.html.twig
  |  |-status-messages.html.twig

More information

Coding standards: CSS file organization

给主题添加区域

原文链接:Adding Regions to a Theme

给主题添加区域(Region)需要满足以下两点:

- 给你的THEMENAME.info.yml文件添加区域标记。

- 编辑你的page.html.twig文件并输出新的区域。

在info文件里添加区域

首先需要在你的THEMENAME.info.yml文件里声明新的区域。区域的声明方式如下(声明在“regions:”下方):

# Regions
regions:
  header: 'Header'
  content: 'Content'
  footer: 'Footer'

区域的键名必须是字母或数字,可以包含下划线(_)。键名必须以英文字母开头。在你的THEMENAME.info.yml文件中引用区域时,键名会被作为区域机器名,当然在代码里引用时也是如此。键值则作为人类可读的名字会被使用在区域的管理界面上。

在模板文件里添加区域:

为了把区域里的内容显示出来,你首先要确保你的区域已经添加到了page.html.twig文件中。

区域需要用Twig变量代替,以字符串page拼接区域的键名。

例如:

header: 'Header'

在你的Twig模板里会变成:

{{ page.header }}

这之后,你就可以把区域当作任何Twig变量一样,把内容输出到任何你需要输出的地方,以及用任何有意义的结构包裹。

默认的区域

在page.html.twig文档中可以看到一系列默认区域。

  • page.header: header区域。
  • page.primary_menu: 主导航菜单区域。
  • page.secondary_menu: 副导航菜单区域.
  • page.highlighted: 高亮内容区域。
  • page.help: 动态帮助文字,大多数给后台页面。
  • page.content: 当前页面的内容。
  • page.sidebar_first: 第一个边栏。
  • page.sidebar_second: 第二个边栏。
  • page.footer: footer的区域。
  • page.breadcrumb: 面包屑区域。

现在主导航菜单、副导航菜单和面包屑都有他们自己的区域了。

如果你的主题没有声明任何区域那么Drupal会使用这些默认区域。这些默认区域和默认的core/modules/system/templates/page.html.twig模板文件相对应。还有两个额外的区域,page_top和page_bottom,他们是在默认的html.html.twig模板里输出的。

如果你在自己的主题里声明了任何区域,哪怕是一个,所有默认的区域就不会有效了。你必须要声明你需要用到的所有区域。注意:大多数情况下你需要确保你声明了page_top和page_bottom这两个区域,因为它们是在html.html.twig文件里输出的并且有些模块需要他们存在。


以下是英文原文,可对照阅读

Last updated May 26, 2015. Created on April 11, 2015.
Edited by camorim, Verscienta, eojthebrave. You can edit this page, too.

Adding regions to a theme requires:

- Adding region meta-data to your THEMENAME.info.yml file
- Editing your page.html.twig file and printing the new regions

Adding Regions to Your Info File

Start by declaring any new regions in your THEMENAME.info.yml file. Regions are declared as children of the regions key like so:

# Regions
regions:
  header: 'Header'
  content: 'Content'
  footer: 'Footer'

Region keys should be alphanumeric, and and include underscores (_). Keys should begin with a letter. When referencing regions the key from your THEMENAME.info.yml file will be used as the machine readable name of the region, and is how you will reference the region in code. The value is the human readable name of the region is what will be used in the user interface for anyone administering regions.

Adding Regions to Your Templates

In order for regions to display any content placed into them you'll need to make sure your new regions are also added to your page.html.twig file. Regions will be represented as Twig variables whose name corresponds with the key used in your THEMENAME.info.yml file with the string page. prepended.

Example:

header: 'Header'

Will become:

{{ page.header }}

In your Twig templates.

Beyond that, you can treat them just likey any other Twig variable, output the content wherever you need to, and wrap them with whatever markup makes sense for your use case.

Default Regions

See the page.html.twig documentation for a list of default regions.

  • page.header: Items for the header region.
  • page.primary_menu: Items for the primary menu region.
  • page.secondary_menu: Items for the secondary menu region.
  • page.highlighted: Items for the highlighted content region.
  • page.help: Dynamic help text, mostly for admin pages.
  • page.content: The main content of the current page.
  • page.sidebar_first: Items for the first sidebar.
  • page.sidebar_second: Items for the second sidebar.
  • page.footer: Items for the footer region.
  • page.breadcrumb: Items for the breadcrumb region.

Now, main menu, secondary menu and breadcrumb have their own regions.
If your theme does not declare any regions Drupal will assume a set of default regions. These regions correspond with what the default core/modules/system/templates/page.html.twig file expects, as well as two additional regions, page_top, and page_bottom, which are output in the default html.html.twig template.

If you declare any regions in your theme, even just one, all the default regions will no longer be applied and you assume resopnsibility for declaring any and all regions you want to use. Note: in most cases you'll want to make sure you declare the page_top and page_bottom regions in your theme since those are output by the html.html.twig file and some modules might expect them to be present.

在Drupal 8主题中加入CSS和JavaScript

原文链接:https://www.drupal.org/theme-guide/8/assets

在Drupal8中,CSS和JS通过加载模块和主题的系统加载,组件库(asset libraries)。

Drupal用一个高级的原则:CSS/JS 只有在你告知Drupal加载时才被加载。Drupal不会在所有页面加载全部的CSS/JS,因为这样不利于前端性能。

与Drupal 7的不同

主要4点:

  1. 不再有THEME.info 文件,但是有包含相同内容的THEME.info.yml 文件
  2. Stylessheets属性不再存在于THEME.info.yml文件,因为现在只使用库了(stylesheets-override 和 stylesheets-remove 依然存在并和原来一样工作)
  3. Scripts 属性也不存在于THEME.info.yml文件,因为现在只使用库了
  4. 只有页面需要的JS会被加入到该页面。 因为,Drupal默认不需要给不登陆的用户提供大部分JS。这意味着jQuery不会自动被所有页面加载
  5. jQuery或其他JS(已经在组件库中定义了),你需要用“在需要的组件库声明需求”的方式告诉Drupal。

过程

通常步骤:

  1. 保存CSS/JS文件
  2. 定义一个包含CSS和JS文件的库
  3. 将这个库附到钩子函数中一个呈递的数组中

但是,在主题中,第三部有另一个方法,主题可以选择加载任意数量的组件库在所有的页面中

定义库

要定义一个库,往主题文件夹中加入 *.libraries.yml 文件。(如果你的主题名是fluffiness, 那么文件名应该是fluffiness.libraries.yml)每个库都是包含CSS/JS详细信息的一个目录,例如:

cuddly-slider:
  version: 1.x
  css:
    theme:
      css/cuddly-slider.css: {}
  js:
    js/cuddly-slider.js: {}

这个例子中,假设JS (cuddly-slider.js)是位于你主题的js子文件夹中。你也可以从外部链接引入JS, CSS 和其他可能。详细信息在https://www.drupal.org/node/2274843 或 https://www.drupal.org/node/2201089 

但 是,记住Drupal8不再默认对所有页面加载jQuery,只是在需要的时候加载。 因此,我们必须声明我们主题的cuddly-slider库声明了对包含jQuery的库的需求。这不是一个模块或主题提供jQuery,而是 Drupal的核心core/jQuery是我们需要声明的需求。(这是一个跟着/和库名的扩展命名,所以,如果有其他库想依赖我们cuddly- slider这个库,它必须要声明对fluffiness/cuddly-slider的需求。因为fluffiness是我们的主题名)

所以,要确保jQuery在js/cuddly-slider.js中可用,我们需要更新代码:

cuddly-slider:
  version: 1.x
  css:
    theme:
      css/cuddly-slider.css: {}
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery

当然,一个库可以只包含CSS组件,或JS组件。绝大多数主题都会想要一个global-styling组件库,为了需要被全范围加载的CSS (比如,应用主题的所有页 – 通常是整个站点,出了管理员页面):

global-styling:
  version: 1.x
  css:
    theme:
      css/layout.css: {}
      css/style.css: {}
      css/colors.css: {}
      css/print.css: { media: print }

当然,如你所期望,CSS在这里的顺序也是它们被加载的顺序

在Drupal 7中,你需要指出媒体属性(screen,print,all)作为stylesheets属性的子键值,现在,对任意CSS组件,你可以把它定义成一个值,例如:

      css/print.css: { media: print }

给页面附加库

根据你需要加载的组件,你会想用不同的方式附加相关的组件库。毕竟,一些组件库所有页面都需要,一些只被少数需要,还有一些被大多数但不是全部需要。

附加到所有页面

要为所有使用你主题的页面附加库,需要在你主题的*.info.yml文件中声明它,在libraires键值的下面:

name: Example
type: theme
core: 8.x
libraries:
  - fluffiness/cuddly-slider

你想列多少库就可以列多少库,它们都会在每一页被加载。

然后,清除缓存让Drupal加载新信息

为一个子集的页面附加库

在一些情况下,你不需要你的库被所有页面加载,而是只需要一部分(子集)页面加载。例如,你可以只需要你的库在某个特定区块(block)出现时或者某个特定节点类型显示时被激活。

一个主题可以通过在.theme文件中置入THEME_preprocess_HOOK()函数实现。需要用你的主题的机器名替换THEME和主题的钩子函数的机器名替换HOOK。

例如,如果你想给维护页附加JS,钩子部分就是maintenance_page,你的函数应该是这样:

<?php
function fluffiness_preprocess_maintenance_page(&$variables) {
  $variables['#attached']['library'][] = 'fluffiness/cuddly-slider';
}
?>

你可以对其他主题的钩子做类似的事情,当然,你的函数也可以包含逻辑部分,例如,检测哪个区块被区块钩子预处理了,哪个节点类型被节点类型预处理了,等等。

在twig模板中附加库

你可以在twig模板中附加一个库,使用的是attach_library() 这个twig函数。

所以,在任何*.html.twig中:

{{ attach_library('fluffiness/cuddly-slider') }}
<div>Some fluffy markup {{ message }}</div>

附加可设置的JS

在某些情况下,你可能希望根据一些PHP的计算信息来附加JS到页面上。

这 时,建立一个JS文件,像以前一样定义并附加一个库,但是也要附上JS设置并通过drupalSettings(Drupal 7的Drupal.settings的继承者)让JS读取这些设置。不过,要想让drupalSettings对我们的JS可用,我们需要做让 jQuery可用的同样工作:我们需要声明需求。

所以就变成了:

cuddly-slider:
  version: 1.x
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery
    - core/drupalSettings

<?php
function fluffiness_page_attachments_alter(&$page) {
  $page['#attached']['library'][] = 'fluffiness/cuddly-slider';
  $page['#attached']['drupalSettings']['fluffiness']['cuddlySlider']['foo'] = 'bar';
}
?>

“bar”是一些计算的值。

这样,cuddl-slider.js就可以访问drupalSettings.fluffiness.cuddlySlider.foo了,并===’bar’

内置JS

不鼓励内置JS,建议把需要内置的JS存到一个文件里,因为这样可以在客户端缓存JS,也可以审查JS。

内置生成附加物的JS

例如广告,社交媒体分享按钮,社交媒体插件。这些会用到内置JS。但是他们只是一类特定的内容/附加物,因为它们不装饰站点内容或让内容产生交互,而是用过JS从外部输入内容。

你需要这些或在一个自定义的区块中,或直接在Twig模板中。

例如:

<script type="text/javascript"><!--
ad_client_id = "some identifier"
ad_width = 160;
ad_height = 90;
//--></script>
<script type="text/javascript" src="http://adserver.com/ad.js"></script>

<a class="twitter-timeline" href="https://twitter.com/wimleers" data-widget-id="307116909013368833">Tweets by @wimleers</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>

影响整个页面的内置JS

例如: 分析插件,和提供的字体服务。影响整个页面的内置JS可以分为两种,前端设计或逻辑性的。

前端设计的情况下,JS属于主题。把JS直接放置到html.html.twig文件中。在字体的情况下,也允许将KS放置到对的地方会给你最好的终端用户体验,因为它允许你防止字体加载中的FOUT(未设计字体的闪现)

(更多内容在此:“Async Typekit & the Micro-FOUT”

其他情况,JS属于模块,请看“Adding stylesheets (CSS) and JavaScript (JS) to a Drupal 8 module”

Drupal 7的不同

  • 在Drupal 7中,库需要用hook_library_info()定义,这被*.libraries.yml文件取代。
  • 在Drupal 8中,drupal_add_css(), drupal_add_js(), 和drupal_add_library()被去除以支持#attached

更多信息:


以下是英文原文,可对照阅读

Last updated May 29, 2015. Created on March 12, 2014.
Edited by almaudoh, mpgeek, TR, Sutharsan. You can edit this page, too.

In Drupal 8, stylesheets (CSS) and JavaScript (JS) are loaded through the same system for modules (code) and themes, for everything: asset libraries.

Drupal uses a high-level principle: assets (CSS or JS) are still only loaded if you tell Drupal it should load them. Drupal does not load all assets (CSS/JS) on all pages, because this is bad for front-end performance.

Differences compares to Drupal 7

There are four important differences compared to Drupal 7 for themers:

  1. We no longer have a file, but a file, but it still contains the same data.
  2. The property (for adding CSS assets) in files no longer exists, because we use only libraries now ( and still exist, and still work in exactly the same way).
  3. The property (for adding JS assets) in files no longer exists, because we use only libraries now.
  4. Only the JavaScript required on a particular page will be added to that page. In particular, 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.
    So, if your theme requires jQuery or some other JavaScript to be present (which also is defined in an asset library), you need to tell Drupal that this is the case, by declaring a dependency on the needed asset library.

The process

The general steps for loading assets (CSS/JS) are:

  1. Save the CSS or JS to a file.
  2. Define a "library", which can contain both CSS and JS files.
  3. "Attach" the library to a render array in a hook.

But in the case of themes, there is an alternative to step 3: themes can choose to load any number of asset libraries on all pages.

Defining a library

To define one or more (asset) libraries, add a file to your theme folder. (If your theme is named , then the file name should be ). Each "library" in the file is an entry detailing CSS and JS files (assets), like this:

cuddly-slider:
  version: 1.x
  css:
    theme:
      css/cuddly-slider.css: {}
  js:
    js/cuddly-slider.js: {}

This example assumes that the actual JavaScript is located in the subfolder of your theme. You can also have the JS come from an external URL, include CSS files, and there are other possibilities. See https://www.drupal.org/node/2274843 or https://www.drupal.org/node/2201089 for details.

However, remember that Drupal 8 no longer loads jQuery on all pages by default; Drupal 8 only loads what's necessary. Therefore, we must declare that our theme's library declares a dependency on the library that contains jQuery. It is neither a module nor a theme that provides jQuery, it's Drupal core: is the dependency we want to declare. (This is an extension name followed by a slash, followed by the library name, so if some other library wanted to depend on our library, it'd have to declare a dependency on , because is the name of of our theme.)

So, to ensure jQuery is available for , we update the above to:

cuddly-slider:
  version: 1.x
  css:
    theme:
      css/cuddly-slider.css: {}
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery

Of course, a library could just as well only contain CSS assets, or only contain JS assets. Most themes will probably want to have a asset library, for the stylesheets (CSS assets) that it wants to be loaded globally (i.e. on all pages where the theme is active — usually the whole site except the administration pages):

global-styling:
  version: 1.x
  css:
    theme:
      css/layout.css: {}
      css/style.css: {}
      css/colors.css: {}
      css/print.css: { media: print }

Of course, as you'd expect, the order the CSS assets are listed here is also the order in which they will be loaded.

In Drupal 7, you used to be able to specify the media property (, , ) as a subkey of the property, now you can define it as a value for any given CSS asset. E.g.:

      css/print.css: { media: print }

Attaching a library to page(s)

Depending on which assets you need to have loaded, you'll want to attach the corresponding asset library in a different way. After all, some asset libraries are needed on all pages, others only very rarely, and yet others on most, but not quite all.

The sub-sections here show examples of how to do this.

Attaching to all pages

To attach a library to all the pages on the site that use your theme, declare it in your theme's file, under the key:

name: Example
type: theme
core: 8.x
libraries:
  - fluffiness/cuddly-slider

You can list as many libraries as you want, all of them will be loaded on every page.

Then clear the cache so that the new information is loaded into Drupal.

Attaching a library to a subset of pages

In some cases, you do not need your library to be active for all pages, but just a subset of pages. For example, you might need your library to be active only when a certain block is being shown, or when a certain node type is being displayed.

A theme can make this happen by implementing a function in the file, replacing "THEME" with the machine name of your theme and "HOOK" by the machine name of the theme hook.

For instance, if you want to attach JavaScript to the maintenance page, the "HOOK" part is "maintenance_page", and your function would look like this:

<?php
function fluffiness_preprocess_maintenance_page(&$variables) {
  $variables['#attached']['library'][] = 'fluffiness/cuddly-slider';
}
?>

You can do something similar for other theme hooks, and of course your function can have logic in it -- for instance to detect which block is being preprocessed in the "block" hook, which node type for the "node" hook, etc.

Attaching a library in a twig template

You can also attach a library in a twig template by using the twig function
So in any *.html.twig:

{{ attach_library('fluffiness/cuddly-slider') }}
<div>Some fluffy markup {{ message }}</div>

Attaching configurable JavaScript

In some cases, you may want to add JavaScript to a page that depends on some computed PHP information.

In this case, create a JavaScript file, define and attach a library just like before, but also attach JavaScript settings and have that JavaScript file read those settings, via (the successor to Drupal 7's ). However, to make available to our JavaScript file, we have to do the same work as we had to do to make jQuery available: we have to declare a dependency on it.

So that then becomes:

cuddly-slider:
  version: 1.x
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery
    - core/drupalSettings

and

<?php
function fluffiness_page_attachments_alter(&$page) {
  $page['#attached']['library'][] = 'fluffiness/cuddly-slider';
  $page['#attached']['drupalSettings']['fluffiness']['cuddlySlider']['foo'] = 'bar';
}
?>

Where is some calculated value.

Then will be able to access (and it will ).

Inline JavaScript

Inline JavaScript is discouraged. It's recommended to put the JS you want to use inline in a file instead, because that allows that JavaScript to be cached on the client side. It also allows JavaScript code to be reviewed and linted.

Inline JavaScript that generates markup

Examples of this are ads, social media sharing buttons, social media listing widgets. These do use inline JavaScript. But they are just a special kind of content/markup, since they're not about decorating the site's content or making it interactive, instead they are about pulling in external content through JavaScript.

You want to put these in either a custom block or even directly in a Twig template.

E.g.:

<script type="text/javascript"><!--
ad_client_id = "some identifier"
ad_width = 160;
ad_height = 90;
//--></script>
<script type="text/javascript" src="http://adserver.com/ad.js"></script>
<a class="twitter-timeline" href="https://twitter.com/wimleers" data-widget-id="307116909013368833">Tweets by @wimleers</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>

Inline JavaScript that affects the entire page

Examples of inline JavaScript that affects the entire page are analytics (e.g. Google Analytics) and hosted font services. Inline JavaScript that affects the entire page can be in either of two categories: front-end/styling, or logical.

In the case of front-end/styling (e.g. hosted font services), the JS belongs in the theme. Put the JS directly in your file. In the case of fonts, this will also allow you to put it right in the place that gives you the best (and fastest) end user experience, because it allows you to prevent a FOUT (Flash Of Unstyled Text) while the font is still loading (fonts loaded through JS must be listed in the HTML before the CSS)!
(Read more about this in the excellent article “Async Typekit & the Micro-FOUT” article.)

In the other case, it belongs in the module, and for that, please see “Adding stylesheets (CSS) and JavaScript (JS) to a Drupal 8 module”.

Differences with Drupal 7

More information

Drupal 8 中的 Twig

原文:Twig in Drupal 8

Twig 是PHP的一个模板引擎,是Symfony2框架的一部分。

在Drupal 8中,Twig代替PHP模板而成为默认的模板引擎。这种变化的结果之一是,所有 theme_* 函数和基于 PHP 模板的 *.tpl.php 文件都由 *.html.twig 模板文件所代替。

 


 以下是英文原文,可对照阅读

Last updated October 19, 2014. Created on October 3, 2014.
Edited by batigolix, jOksanen, LoMo. You can edit this page, too.

Twig is a template engine for PHP and it is part of the Symfony2 framework.

In Drupal 8 Twig replaces PHPTemplate as the default templating engine. One of the results of this change is that all of the theme_* functions and PHPTemplate based *.tpl.php files have been replaced in by *.html.twig template files.

Twig 模版的运用

原文:Working with Twig Template

Drupal允许您覆写用于生成HTML标记的所有模板,这样您就能完全控制定制主题中输出的标记。从高级HTML到小的字段,每个页面元素都有模板。

覆写模板(Overriding templates)

通过向遵循一定命名约定的主题文件夹添加模板,您就可以覆写Drupal核心模板。

要覆写模板,您:

  1. 找到想覆写的模板;
  2. 把模板文件从其基本位置复制到您的主题文件夹;
  3. (可选地)根据命名约定重新名命模板,以便针对要使用模板的更具体区域;
  4. 按您的喜好修改模板。

一旦您把模板文件复制进您的主题并清除缓存,Drupal就会开始用您的模板文件实例而不是基础模板。对于页面的任何部分,您都可以用Twig调试工具来找到其使用的模板。

主题钩子建议(Theme hook suggestions)

有时您想对模板文件进行修改,但仅限于使用到的一些地方。常见的例子是仅对特定类型的结点修改其结点模板文件。Drupal的主题层允许您遵循一定的名命约定来针对模板文件的特殊使用情况。当表现文章结点时,Drupal会首先寻找node--article.html.twig模板文件,如果它存在就加以使用。如果它不存在,Drupal就退回去使用默认的node.html.twig模板。Drupal用以决定一个模板可能使用的名称的过程叫做主题钩子建议。

主题钩子建议允许您用一定的命名约定,在您的主题中针对模板文件实现想要的覆写。

从核心、模块、主题引擎到主题的所有层次都可以提出建议。您可以用钩子来添加或修改建议:

  •     hook_theme_suggestions_HOOK(array $variables)
  •     hook_theme_suggestions_alter(array &$suggestions, array $variables, $hook)
  •     hook_theme_suggestions_HOOK_alter(array &$suggestions, array $variables)

重建缓存(Rebuild cache)

使用主题钩子建议时,有可能Drupal在使用自己的缓存而非新模板所建议的。如果您遇到此问题,就清除缓存。要清除缓存,选择清除Drupal缓存中描述的方法之一。

背景信息(Background information)

您可以把建议想像成命名暗示,告诉系统根据正确的环境进行挑选。模板建议通过可修改的主题建议钩子进行设定。这些钩子允许任何模块或主题提供替代主题函数或模板命称建议,并重排或移除hook_theme_suggestions_HOOK()或该钩子的早期调用所提供的建议。

Drupal如何根据路径来确定页面主题钩子建议

这里是基于theme_get_suggestions()函数的另一种解释:

一个页面可能的模板列表由Drupal通过theme_get_suggestions函数生成,该函数由sytem_theme_suggestions_page()函数调用。

该页面的Drupal路历首先分解为其组件。如上所提及,Drupal路径并非其任意的别名:一个页面只有唯一一条Drupal路径。例如,"http://www.example.com/node/1/edit"和"http://www.example.com/mysitename?q=node/1/edit",其Drupal路径为node/1/edit,而其组件为"node"、1和 "edit"。

接着,将前缀设定为”page”。然后,对每个组件,遵循以下逻辑:

  1. 如果组件为数字,则向建议列表添加前缀和”__%”。
  2. 无论组件是数字与否,都向建议列表添加前缀和”__%”。
  3. 如果组件不是数字,则向前缀附加”__”和组件。

注意,要最终把建议变成实际文件名,”__”会变为”--“,而”.html.twig”则附加在建议后。于是,对于node/1/edit,我们得到下列建议:

  1.     page.html.twig (这总是一条建议)
  2.     page--node.html.twig (前缀设定为page__node)
  3.     page--node--%.html.twig
  4.     page--node--1.html.twig (因为是数字,前缀不变)
  5.     page--node--edit.html.twig (前缀设定为 page__node__edit)
  6.     page--front.html.twig (但只有当node/1/edit是首页时)

当页面实际渲染时,查看最后的建议。如果它存在,就使用该建议。否则,查看前一条建议,如此等等。当然,如果不存在覆写建议,那么page.html.twig就是最终的建议。这也解释了为什么page--front.html.twig存在的话会为首页覆写任何其它建议:它始终是指定为首页的页面的最后建议。

与Drupal 7的区别(Differences with Drupal 7)

以前,要改变模板建议,您就在预处理函数中修改$variables['theme_hook_suggestion']和 $variables['theme_hook_suggestions']来引入建议。在Drupal 8中,现在模块和主题在自己的专用钩子中定义和修改主题建议。

更多信息(More Information)

变更记录主题建议的新钩子

在Twig模板里提取和检索变量

原文链接:Discovering and Inspecting Variables in Twig Templates>

在一个Twig模板文件里,你可以发现注释里有很多可用的变量。然而,当变量不是记录在模板文件中,或者是当主题、模块引入新的变量时,我们就需要一种方法来检索和提取模板里所有可用变量的和特定范围的变量,Twig支持在模板文件里使用dump() 函数(转存函数)来提取和检索变量。

Dump函数在启用调试之前不会显示输出的变量内容。学习如何启用Twig调试

开启调试后,dump函数就可以输出模板里的一个变量或多个变量的相关信息。

检索单个变量

假设你的模板里有一个title变量,可以按如下形式,将其内容dump到你的模板:

{{ dump(title) }}

检索模板内所有可用变量

将模板内所有可用的变量和变量的内容添加到你的模板,可用如下示例代码:

{{ dump() }}

Dump(转存)一个只有一个可用变量keys使用如下代码:

{{ dump(_context|keys) }}

以下全局变量可用于所有的Twig模板:

  • _self引用了当前模板以及模板包含的高级信息,也就是已编译的模板类名和Twig的环境信息。
  • _context 引用当前的上下文和上下文所包含的所有变量,然后传递给模板,例如一个来自theme()的变量,由预处理得来,或来自模板。增加一个{{ dump() }} 却没有定义一个变量,相当于添加了一个{{ dump(_context) }}。
  • _charset 用于引用当前的字符集。

使用dump()需注意:

如果你想看到模板里所有的变量,但是因dump()(转存函数)的递归或其他类似递归的算法逻辑而导致内存不足,你可以遍历_context来引用当前上下文所包含的所有变量,从而看到所有的keys,示例如下:

<ol>
    {% for key, value in _context  %}
      <li>{{ key }}</li>
    {% endfor %}
</ol>

然后用一个条件判断来检查结果(例如 {{if loop.index=2%}}),然后dump(转存)有需要的变量即可。

更多信息

查看Twig的dump()函数文档了解更多关于dump()函数的知识。


附上英文版对照查看:

When working with a Twig template file most variables are documented in the comments for the template file. However, when they are not, or when themes or modules introduce new variables we need a way to discover all the variables available within the scope of a template. Twig provides the dump function for discovering and inspecting variables in template files.

The dump function will not display any output unless debugging is enabled. Learn how to enable Twig debugging.

Once enabled, the dump function can be used to output information about a variable or variables within a template.

Inspecting a single variable

If your template has a title variable available, the following will dump its contents to your template:

{{ dump(title) }}

Discovering all available variables in a template

To dump all available variables and their contents in a template, add the following to your template:

{{ dump() }}

To dump only the available variable keys use:

{{ dump(_context|keys) }}

There are additional global variables available in all Twig templates:

  • _self references the current template and contains advanced information about a template, i.e. the compiled template class name and information about the Twig environment.
  • _context references the current context and contains all variables passed to the template such as variables sent from theme(), prepared by preprocess, or set in the template. Adding {{ dump() }} without specifying a variable is equivalent to {{ dump(_context) }}.
  • _charset references the current charset.

Beware of dump()

If you want to see all variables, but dump() results in exhausted memory because of recursion or the like, you can loop through _context to see the all the keys in it:

&lt;ol&gt;<br style="box-sizing: border-box;" />
&nbsp;&nbsp;&nbsp; {% for key, value in _context&nbsp; %}<br style="box-sizing: border-box;" />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;li&gt;{{ key }}&lt;/li&gt;<br style="box-sizing: border-box;" />
&nbsp;&nbsp;&nbsp; {% endfor %}<br style="box-sizing: border-box;" />
&lt;/ol&gt;

Then use a conditional to check (for example {{ if loop.index = 2 %}), and dump that value only if necessary.

More information

See Twig's dump function documentation to learn more about the dump function.

模板命名规则

Template naming conventions

Drupal基于某些命名规则载入模板。 把这些模板加到你的主题并赋予具体的名字可以使你覆写核心模板。

每次覆写一个模板都需要清缓存以便Drupal发现新的模板。

在输出任何元素块时,你都可以调试Twig模板来找出那些模板正在被调用。

该页面列出了基本html结构,page,区域,区块,节点,字段和其他核心组件的模板。

HTML(<head>

HTML模板提供了基本的HTMl页面结构,包含<head><title><body>标签。

基础模板:html.html.twig(内核位置:core/modules/system/templates/html.html.twig)

以下是两个覆写基础模板的例子:

  1. html--站点路径.html.twig
  2. html--node--节点id.html.twig

具体可以查看html.html.twig的API文档

Page 模板

规则:page--[front|内部路径].html.twig

基础模板:page.html.twig(内核位置:core/modules/system/templates/page.html.twig)

该规则不是单一的。首页优先,而后是内部路径。首页设置是在"管理> 配置> 系统> 站点信息."(http://example.com/admin/config/system/site-information),首页的模板是page--front.html.twig。不要把内部路径和路径别名混淆,这里说的不是别名。

页面的模板列表是根据内部路径而不同的。一个模板推荐是对当前页面所有元素而言的,尽管某些元素并不会出现在后续的模板中。例如, "http://www.example.com/node/1/edit"的模板列表会是这样的:

  1. page--node--edit.html.twig
  2. page--node--1.html.twig
  3. page--node.html.twig
  4. page.html.twig

具体可以查看page.html.twig API文档.

区域

规则: region--[区域].html.twig

基础模板: region.html.twig (内核位置: core/modules/system/templates/region.html.twig)

区域模板在区域有内容时, 区块系统或者函数如hook_page_build()调用时被使用. 可用的区域名是从主题的.info.yml文件中获取的。

具体查看region.html.twigAPI文档

区块

规则:block--[module|-delta]].html.twig

基础模板:block.html.twig(内核位置:core/modules/block/templates/block.html.twig)

  1. block--module--delta.html.twig
  2. block--module.html.twig

“module”是模块的名字,“delta”是该模块下这个区块的id。

例如,“block--block--1.html.twig”就是在去看管理页面创建的第一个区块的模板,因为它是有block模块创建的并且id为1。指定区域的区块模板在Drupal8中不再适用。

若你有一个自定义模块叫做”custom“,并且模块id是“my-block”,那么模板命名就是“block--custom--my-block.html.twig”。

下面是对视图的例子。若你有一个视图区块,视图名字是“front_news”,显示id是“block_1”,那么模板命名就是:block--views-block--front-news-block-1.html.twig (注意视图或者显示id里的下划线“_”都需要转成中横杠“-”)。

请注意本文中模块名字是区分大小写的,如果你的模块名字叫做“MyModule”,那么模板的命名一般就会成这样“block--MyModule.html.twig”。

节点

规则:node-[type|nodeid].html.twig

基础模板:node.html.twig(核心位置:core/modules/node/templates/node.html.twig)

主题模板是基于以下因素, 从详细到次要, Drupal会使用最具体的模板:

  1. node--nodeid.html.twig
  2. node--type.html.twig
  3. node.html.twig

要注意的是内容类型的机器名下划线需用连字符替换。

更多具体信息可以查看Drupal node.html.twig API文档

分类

规则:taxonomy-term--[vocabulary-machine-name|tid].html.twig

基础模板:taxonomy-term.html.twig (核心位置: core/modules/taxonomy/templates/taxonomy-term.html.twig)

主题模板是基于以下因素, 从详细到次要, Drupal会使用最具体的模板:

  1. taxonomy-term--tid.html.twig
  2. taxonomy-term--vocabulary-machine-name.html.twig
  3. taxonomy-term.html.twig

要注意的是词汇的机器名下划线需用连字符替换。

更多具体信息可以查看Drupal taxonomy-term.html.twig API文档

字段

规则: field--[type|name[--content-type]|content-type].html.twig

基础模板: field.html.twig (核心位置: core/modules/system/templates/field.html.twig)

主题模板是基于以下因素, 从详细到次要, Drupal会使用最具体的模板:

  1. field--field-name--content-type.html.twig
  2. field--content-type.html.twig
  3. field--field-name.html.twig
  4. field--field-type.html.twig

要注意的是字段的机器名下划线需用连字符替换。不要忘了加上“field-”, 例如field--field-phone.html.twig。

评论

规则: comment--node-[type].html.twig

基础模板: comment.html.twig (核心位置: core/modules/comment/template/comment.html.twig)

comment--node-[type].html.twig支持站点内特定内容类型的评论模板。例如,对文章类型的节点评论的模板就是“comment--node-article.html.twig”。

对与包装后的评论模板,类似规则如下:

规则: comment-wrapper--node-[type].html.twig

基础模板: comment-wrapper.html.twig

论坛

规则: forums--[[container|topic]--forumID].html.twig

基础模板: forums.html.twig (内核位置: core/modules/forum/templates/forums.html.twig)

主题模板是基于以下因素, 从详细到次要, Drupal会使用最具体的模板:

论坛容器:

  1. forums--containers--forumID.html.twig
  2. forums--forumID.html.twig
  3. forums--containers.html.twig

论坛主题:

  1. forums--topics--forumID.html.twig
  2. forums--forumID.html.twig
  3. forums--topics.html.twig

维护页面

规则: maintenance-page--[offline].html.twig

基础模板: maintenance-page.html.twig (内核位置: core/modules/system/templates/maintenance-page.html.twig)

数据库挂的时候会应用这个模板,提供了一个友好错误提示页面,不过前提是正确安装。

搜索结果

规则: search-result--[searchType].html.twig

基础模板: search-result.html.twig (核心位置: core/modules/search/templates/search-result.html.twig)

search-result.html.twig是单个搜索结果的默认模板。根据搜索的不同类型有不同的模板,例如“example.com/search/node/Search+Term”的模板就是“search-result--node.html.twig”,然后“example.com/search/user/bob”就有所不同了,应该是“search-result--user.html.twig”。模块可以通过扩展搜索类型增加更多的模板。


以下是英文原文,可对照阅读

Last updated May 7, 2015. Created on October 11, 2014.
Edited by rteijeirotebbNonProfitkiwad. You can edit this page, too.

Drupal loads templates based on certain naming conventions. This allows you to override templates by adding them to your theme and giving them specific names.

After replacing a template you must rebuild the cache in order for Drupal to discover your new template.

You can debug Twig templates to figure out which templates are being used to output the markup for any given element.

This page lists the conventions used for the base html structure, the page, regions, blocks, nodes, fields, and other core components.

HTML (<head> template)

The HTML template provides markup for the basic structure of the HTML-page including the <head>, <title> and <body> tags.

Base template: html.html.twig (base location: core/modules/system/templates/html.html.twig)

The following are some examples of how you may override the base template:

  1. html--internalviewpath.html.twig
  2. html--node--id.html.twig

See the html.html.twig API documentation.

Page template

Pattern: page--[front|internal/path].html.twig
Base template: page.html.twig (base location: core/modules/system/templates/page.html.twig)

The suggestions are numerous. The front page takes precedence. The rest are based on the internal path of the current page. The front page can be set at "Administration > Configuration > System > Site information." (http://example.com/admin/config/system/site-information). The front page template is page--front.html.twig.
Do not confuse the internal path with path aliases, which are not accounted for.

The list of suggested template files is in order of specificity based on internal paths. One suggestion is made for every element of the current path, though numeric elements are not carried to subsequent suggestions. For example, "http://www.example.com/node/1/edit" would result in the following suggestions:

  1. page--node--edit.html.twig
  2. page--node--1.html.twig
  3. page--node.html.twig
  4. page.html.twig

See the page.html.twig API documentation.

Regions

Pattern: region--[region].html.twig
Base template: region.html.twig (base location: core/modules/system/templates/region.html.twig)

The region template is used when a page region has content, either from the Block system or a function like hook_page_build(). Possible region names are determined by the theme's .info.yml file.

See the API documentation for region.html.twig

Blocks

Pattern: block--[module|-delta]].html.twig
Base template: block.html.twig (base location: core/modules/block/templates/block.html.twig)

  1. block--module--delta.html.twig
  2. block--module.html.twig

"module" being the name of the module and "delta", the internal id assigned to the block by the module.

For example, "block--block--1.html.twig" would be used for the first user-submitted block added from the block administration screen since it was created by the block module with the id of 1. Region-specific block templates are not available in Drupal 8.

If you had a block created by a custom module called "custom" and a delta of "my-block", the theme hook suggestion would be called "block--custom--my-block.html.twig."

Also one more example with Views, if you have a block created by views with a view name "front_news" and display id "block_1" then the theme hook suggestion would be: block--views-block--front-news-block-1.html.twig (notice, when you have underscores in a display id or in a view name - you have to transform them in to a single dash)

Be aware that module names are case sensitive in this context. For instance if your module is called 'MyModule', the most general theme hook suggestion for this module would be "block--MyModule.html.twig."

Nodes

Pattern: node--[type|nodeid].html.twig
Base template: node.html.twig (base location: core/modules/node/templates/node.html.twig)

Theme hook suggestions are made based on these factors, listed from the most specific template to the least. Drupal will use the most specific template it finds:

  1. node--nodeid.html.twig
  2. node--type.html.twig
  3. node.html.twig

Note that underscores in a content type's machine name are replaced by hyphens.

See node.html.twig in the Drupal API documentation for more information.

Taxonomy terms

Pattern: taxonomy-term--[vocabulary-machine-name|tid].html.twig
Base template: taxonomy-term.html.twig (base location: core/modules/taxonomy/templates/taxonomy-term.html.twig)

Theme hook suggestions are made based on these factors, listed from the most specific template to the least. Drupal will use the most specific template it finds:

  1. taxonomy-term--tid.html.twig
  2. taxonomy-term--vocabulary-machine-name.html.twig
  3. taxonomy-term.html.twig

Note that underscores in a vocabulary's machine name are replaced by hyphens.

See the API documentation for taxonomy-term.html.twig.

Fields

Pattern: field--[type|name[--content-type]|content-type].html.twig
Base template: field.html.twig (base location: core/modules/system/templates/field.html.twig)

Theme hook suggestions are made based on these factors, listed from the most specific template to the least. Drupal will use the most specific template it finds:

  1. field--field-name--content-type.html.twig
  2. field--content-type.html.twig
  3. field--field-name.html.twig
  4. field--field-type.html.twig

Note that underscores in a Field's machine name are replaced by hyphens. Also remember to include "field-" in custom field names, e.g: field--field-phone.html.twig.

Comments

Pattern: comment--node-[type].html.twig
Base template: comment.html.twig (base location: core/modules/comment/template/comment.html.twig

Support was added to create comment--node-type.html.twig files, to format comments of a certain node type differently than other comments in the site. For example, a comment made on an article-type node would be "comment--node-article.html.twig".

Pattern: comment-wrapper--node-[type].html.twig
Base template: comment-wrapper.html.twig

Similar to the above but for the wrapper template.

Forums

Pattern: forums--[[container|topic]--forumID].html.twig
Base template: forums.html.twig (base location: core/modules/forum/templates/forums.html.twig)

Theme hook suggestions are made based on these factors, listed from the most specific template to the least. Drupal will use the most specific template it finds:

For forum containers:

  1. forums--containers--forumID.html.twig
  2. forums--forumID.html.twig
  3. forums--containers.html.twig

For forum topics:

  1. forums--topics--forumID.html.twig
  2. forums--forumID.html.twig
  3. forums--topics.html.twig

Maintenance page

Pattern: maintenance-page--[offline].html.twig
Base template: maintenance-page.html.twig (base location: core/modules/system/templates/maintenance-page.html.twig)

This applies when the database fails. Useful for presenting a friendlier page without error messages. Theming the maintenance page must be properly setup first.

Search result

Pattern: search-result--[searchType].html.twig
Base template: search-result.html.twig (base location: core/modules/search/templates/search-result.html.twig)

search-result.html.twig is the default wrapper for individual search results. Depending on type of search different suggestions are made. For example, "example.com/search/node/Search+Term" would result in "search-result--node.html.twig" being used. Compare that with "example.com/search/user/bob" resulting in "search-result--user.html.twig". Modules can extend search types adding more suggestions of their type.

在 Twig 模板 的函数

TBD

连接和路径函数比较类似,函数可以在以下路径找到 \Symfony\Bridge\Twig\Extension\RoutingExtension

url()

path()

url_from_path()

link()

file_url()

attach_library()

// 如果检测到数组, 这个函数会收到一个可渲染的数组.
render_var(), 内部使用,最好别用.

模板文件的位置

如果你想知道哪个模板生成了指定元素, 你可以使用 Twig 的 debug 功能.
主题名字旁边也显示了文件的位置.

Twing debugging 可以在 sites/default/services.yml 文件内开启, 将twig.config 下的 debug 变量改为 true.
设置后,请清除缓存.

如果你查看当前页面下的源文件, 你将看到 debug 的输出类似下面 Twing templates 被渲染.

<!-- THEME DEBUG -->
<!-- CALL: theme('block') -->
<!-- FILE NAME SUGGESTIONS:
   * block--system.html.twig
   * block--system-menu-block.html.twig
   * block--system-menu-block--tools.html.twig
   * block--bartik-tools.html.twig
   x block.html.twig
-->
<!-- BEGIN OUTPUT from 'core/modules/block/templates/block.html.twig' -->
<div class="block block-system contextual-region block-menu" id="block-bartik-tools" role="navigation">
 
      <h2>Tools</h2>
    <div data-contextual-id="block:block=bartik_tools:|menu:menu=tools:"></div>

  <div class="content">
    <ul class="menu clearfix"><li class="first collapsed"><a href="/node/add">Add content</a></li>
<li class="last leaf"><a href="/block/add" title="Add custom block">Add custom block</a></li>
</ul>
  </div>
</div>

<!-- END OUTPUT from 'core/modules/block/templates/block.html.twig' -->

下面是比较有用的

<!-- BEGIN OUTPUT from 'core/modules/block/templates/block.html.twig' -->
<!-- END OUTPUT from 'core/modules/block/templates/block.html.twig' -->

所有核心模板都在模块 /core/modules/system 文件夹下, Drupal 8 有一个子目录,名字为 "templates"

在这个文件夹内,我们可以找到所有类型的 模板文件 和 page.html.twig

system
  ...
  |-templates
  |  |-admin-block-content.html.twig
  |  |-admin-block.html.twig
  |  |-admin-page.html.twig
  |  |-block--system-branding-block.html.twig
  |  |-block--system-menu-block.html.twig
  |  |-breadcrumb.html.twig
  ...
  |  |-menu-local-action.html.twig
  |  |-menu-local-task.html.twig
  |  |-menu-local-tasks.html.twig
  |  |-menu.html.twig
  |  |-page.html.twig
  |  |-pager.html.twig
  |  |-progress-bar.html.twig
  ...

 

Twig 最佳实践 -- 预处理函数和模板

原文链接:https://www.drupal.org/node/1920746#utility

为了使Drupal8的主题尽可能的性能高并且允许更多的定制模板,请遵循以下这些最佳实践:

从预处理函数返回渲染数组

在模板中调用过滤器和功能函数

从预处理函数返回渲染数组

在预处理方法中返回渲染数组代替 theme() 或是 drupal_render().

Twig渲染自动渲染不需要调用drupal_render()或是theme()预处理函数。相反,渲染数组应该传到模板因为这允许得到更多已渲染的HTML字符串。

移除theme()预处理函数:

<?php
// Before - passing a string of rendered HTML to the template.
$variables['table'] = theme('table', array('header' => $header, 'rows' => $rows));
// After - passing a render array to the template.
$variables['table'] = array(
  '#theme' => 'table',
  '#header' => $header,
  '#rows' => $rows,
);
?>

移除drupal_render()预处理函数是一个删除调用的问题:

<?php
// Before, unnecessary call to drupal_render().
$variables['teaser'] = drupal_render($node_teaser);
// After, with drupal_render() removed.
$variables['teaser'] = $node_teaser;
?>

通常是drupal_render()加入数据的时候被调用.

<?php
// Before, unnecessary call to drupal_render().
$row[] = drupal_render($display['title']);

// After, with drupal_render() removed. 
$row[]['data'] = $display['title'];
?>

在模板中调用过滤器和控制函数

当渲染数组可寻址,可变数据的模板,不是所有的变量需要渲染数组。尽可能长时间的提供原始数据模板,主题开发应该调用过滤器例如t和控制函数例如Twig模板中的url().在Twig模板中调用预处理函数能够减少函数调用因为参数传到模板可能不在模板中显示。

以前:

在预处理函数中:

<?php
$variables['no_content_text'] = t('You have not created any content types yet. Go to the <a href="@create-content">content type creation page</a> to add a new content type.', array('@create-content' => url('admin/structure/types/add')));
?>

在模板中:

<p>{{ no_content_text }}</p>

以后:

在模板中:

<p>{{ 'You have not created any content types yet. Go to the <a href="@create-content">content type creation page</a> to add a new content type.'|t({'@create-content': url('admin/structure/types/add')}) }}</p>

显示/隐藏 & 移动 drupal_render_children 和 element_children

如果hide() 调用原始模板,drupal_render_children用于渲染“剩余”数据,我们需要将分离的变量进行预处理。

以前(php模板文件):

<?php
hide($form['advanced']);
hide($form['actions']);
?>
<div class="layout-node-form clearfix">
<div class="layout-region layout-region-node-main">
<?php print drupal_render_children($form); ?>
</div>
<div class="layout-region layout-region-node-secondary">
<?php print render($form['advanced']); ?>
</div>
<div class="layout-region layout-region-node-footer">
<?php print render($form['actions']); ?>
</div>
</div> 

预处理成分离的参数,传递给模板。你可能需要在呈现之前渲染变量全部元素。完全按照预期的展示模板。

以后:(预处理)

<?php
function template_preprocess_node_edit_form(&$variables) {
  $form = $variables['form'];
 
  // @todo Update this once drupal.org/node/1920886 is resolved.
  $variables['advanced'] = $form['advanced'];
  $variables['actions'] = $form['actions'];
  unset($form['advanced'], $form['actions']);
  $variables['form'] = drupal_render_children($form);
}
?>

以后:(Twig模板)

<div class="layout-node-form clearfix">
  <div class="layout-region layout-region-node-main">
    {{ form|without('advanced', 'actions') }}
  </div>
  <div class="layout-region layout-region-node-secondary">
    {{ form.advanced }}
  </div>
  <div class="layout-region layout-region-node-footer">
    {{ form.actions }}
  </div>
</div>

调试Twig模板

原文链接:Debugging Twig templates

Twig模板引擎提供了一个调试工具。

Drupal 8 实现并且增加了一个额外的工具,可以让你找到输出标记(markup)的模板

开启调试(Enable debugging)

你要开启 Twig 的调试功能是在 sites/default/services.yml 里。

设置该文件中的debug变量为 true

parameters:
  twig.config:
    debug: true 

如果你用的是火狐,请确保“HTML”->“Show Comments”选项是开启(选中)的。

Firebug setting for twig debug

自动重新加载编译过的Twig模板

编译过的Twig模板是作为php类文件保存在硬盘中的,这样可以提升性能,但是这意味着你更改了模板源文件后它们不会及时更新显示出来。要开启Twig模板自动重新加载,在 services.yml 文件中设置 debug 为开启即可,关于此更多信息,可以查阅:https://drupal.org/node/1903374

输出变量

{{ dump() }}
{{ dump(var) }}

如果你安装了 Devel 子模块kint,你可以在twig模板中加入以下代码以获得可折叠显示的变量:

{{ kint() }}

以下是英文原文,可对照阅读

Last updated June 29, 2015. Created on February 3, 2013.
Edited by wesruv, dawehner, Cottser, rpayanm. You can edit this page, too.

The Twig templating engine offers a debug tool.

The Drupal 8 implementation also adds an additional tool that allows you to locate the template that outputs the markup.

Enable debugging

You enable Twig Debugging in sites/default/services.yml.

Set the debug variable to true:

parameters:
  twig.config:
    debug: true 

In firebug make sure that "Show Comments" is enabled:

Firebug setting for twig debug

Automatic reloading Twig templates are compiled

Twig templates are compiled to PHP classes on disk for better performance, but this means by default your templates are not refreshed when you make changes. To enable automatic reloading of Twig templates, enable debug in services.yml. For more information, see https://drupal.org/node/1903374.

Printing variables

{{ dump() }}
{{ dump(var) }}

If you have Devel's kint submodule you can get an accordion display of the variables available to twig with:

调试编译Twig模板

原文链接:Debugging compiled Twig templates

Twig模板引擎提供了配置调试的选项,自动启动(编译)模板,在文件系统里缓存编译的模板。默认的,Twig模板引擎编译到php代码中并且存储编译代码到内存中。编译后的代码不利于开发,因为改变Twig模板引擎不会立刻更新到你的Drupal站点。Twig缓存可以通过清除Drupal缓存接口清除,但对于正在开发的项目更容易改变Drupal的设置使Twig不缓存任何东西。 

注意大部分情况下你会想禁用渲染API缓存。如下指令。如果你不这样做仍然需要刷新缓存才能看到Twig模板引擎的改变因为渲染API使用关联Twig模板生成HTML然后缓存生成的HTML和你的Twig模板后续试图中不被使用。不管Twig调试的设置。

在Drupal控制台中怎样启用调试

drupal site:mode dev

这将改变你的下列sites/default/services.yml中的值

twig.config: { debug: true, auto_reload: true, cache: false }

你将看到一些相似的:

qqtu_pian_20151202160547.png

怎样手动启用调试

1.找到你站点的services.yml文件,可能在sites/default/services.yml

2.如果services.yml;复制default.services.yml并重命名为services.yml

3.编辑services.yml文件不存在启用一个或更多的下面的调试选项

4.重建缓存

找到services.yml中的twig.config参数并修改,例如:

parameters:

  twig.config:

    debug: true

Twig调试选项

debug:true

当调试启用:

  • 每一个Twig模板都有HTML注释包含主题信息,例如模板文件名称的建议。
  • 注意这将导致自动测试直接检测HTML渲染失败。自动检测,’twig_debug’也将设置成FALSE.
  • Dump()方法可以用于Twig引擎输出模板信息。
  • 当源码改变的时候Twig模板引擎自动重新编译(见下面的twig_auto_reload)
  • 不推荐用于生产环境。(默认:false)

Twig auto-reload

auto_reload:true

  • 当源码改变的时候自动重新编译Twig 模板引擎。如果不给Twig_auto_reload提供一个值,它根据Twig_debug的值确定一个值。
  • 不推荐用于生产环境。(默认:null)

Twig 缓存

cache:false

  • 默认的,Twig模板引擎编译和存储在系统文件增加性能。启用Twig缓存将在每次使用源码的时候重新编译模板。大部分的twig_auto_reload设置应该启用而不是禁用Twig缓存。
  • 不推荐用于生产环境。(默认:true)

Drupal 渲染缓存

默认的,Drupal缓存渲染区块和实体,加快后续页面加速问题。这意味着改变Twig模板引擎不会立即生效。设置渲染缓存使用空缓存后端有效禁用。 禁用渲染缓存,去掉下面的注释settings.php,复制sites/example.setings.local.php到sites/default/settings.local.php,并且清除Drupal缓存:

<?php
if (file_exists(__DIR__ . '/settings.local.php')) {
  include __DIR__ . '/settings.local.php';
}
?>

也去掉你刚刚复制的后端示例文件中的注释:

<?php
$settings['cache']['bins']['render'] = 'cache.backend.null';
$settings['cache']['bins']['dynamic_page_cache'] = 'cache.backend.null';
?>

更多的信息见#1843034: Make Twig settings configurable

Drupal渲染缓存不被Drupal控制台支持更多相关设置信息见
https://github.com/hechoendrupal/DrupalConsole/issues/1309

创建Drupal 8子主题

原文:Creating a Drupal 8 sub-theme

子主题和其它主题是一样的,但有一点区别:它们继承父主题的资源。子主题链接到其父主题的能力是没有限制的。子主题可以是另一子主题之子。它可以分叉,任您组织。这就给子主题以巨大的潜能。要创建子主题,您先像其它主题一样定义您的主题,用"base theme"键声明基主题。请注意这个键没有下划线。

例子 my_theme.info.yml(Example my_theme.info.yml)

这是一个子主题的例子,它用Classy作为基主题

name: Fluffiness
type: theme
description: This is a fluffy sub theme of Classy
core: 8.x
# Defines the base theme
base theme: classy
# Defines libraries group in which we can add css/js.
libraries:
  - my_theme/global-styling
# Regions
regions:
  header: Header
  featured: Featured
  content: Content
  sidebar_first: First sidebar
  sidebar_second: Second sidebar
  footer: Footer

包含my_theme.libraries.yml文件以把css/js加入到`global-style`(定义在上面的`libraries:`键)中。

global-styling:
  css:
    theme:
      css/style.css: {}

添加Javascript:怎样添加JavaScript

与 Drupal 7 的区别(Differences with Drupal 7)

与 Drupal 7最明显的区别是 .info文件变成.info.yml文件,后者使用YAML语法。


以下是英文原文,可对照阅读

Sub-themes are just like any other theme, with one difference: they inherit the parent theme's resources. There are no limits on the chaining capabilities connecting sub-themes to their parents. A sub-theme can be a child of another sub-theme, and it can be branched and organized however you see fit. This is what gives sub-themes great potential. To create a sub-theme you define your theme like any other theme and declare the base theme using the "base theme" key. Note that the key has no underscore.

Example my_theme.info.yml

This is an example of a sub-theme that uses Classy as a base theme

name: Fluffiness
type: theme
description: This is a fluffy sub theme of Classy
core: 8.x
# Defines the base theme
base theme: classy
# Defines libraries group in which we can add css/js.
libraries:
  - my_theme/global-styling
# Regions
regions:
  header: Header
  featured: Featured
  content: Content
  sidebar_first: First sidebar
  sidebar_second: Second sidebar
  footer: Footer

Include my_theme.libraries.yml file to add css/js in `global-style` group (defined above in `libraries:` key).

global-styling:
  css:
    theme:
      css/style.css: {}

How to add JavaScript

Differences with Drupal 7

The most notable difference with Drupal 7 is that .info files have become .info.yml files that use the YAML syntax.

用Classy作为子主题

原文:Using Classy as a base theme

Classy是Drupal 8核心中的一个主题,用主Bartik和Seven的子主题。它的目的是在标记中提供许多类,以帮助注释和描述在页面上渲染的标记元素。过去,Drupal在对元素如此进行注释方面所做的额外辅助,有的人认为是有益的,而其他人则认为是有害的。在Drupal 8中,您现在可以选择是否包含此种额外辅助。如果您想在您的元素上包含Drupal类,就把Classy定义为您的基主题。更多信息请阅读:学习如何创建子主题。 如果您想您的主题含有尽可能少的类,那么就不要用Classy作为基主题。当您不想Drupal定义的类与您的主题正在使用的CSS和JavaScript框架冲突时,不使用Classy是一个好主意。不包含Classy可让您完全负责您的主题拥有的类。

更多信息(More information)


以下是英文原文,可参照阅读:

Last updated November 7, 2014. Created on October 9, 2014.
Edited by richgilbert, batigolix, cosmicdreams, lieb. You can edit this page, too.

Classy is a theme in Drupal 8 core that is used as a base theme for Bartik and Seven. Its purpose is to provide many classes throughout the markup that help annotate and describe markup elements that render on the page. In the past, the extra effort that Drupal applies to elements in annotating them this way has been viewed as helpful by some and a hindrance by others. In Drupal 8, you now have the option of either including or excluding this extra help. If you want your theme to include Drupal's classes on your elements, define Classy as your base theme. Read more here: Learn how to create a sub theme. If you want your theme to have the minimum amount of classes, then do not use Classy as a base theme. Not using Classy is a great idea when you don't want the classes defined by Drupal to conflict with CSS and JavaScript frameworks being used by your theme. Excluding Classy gives you full responsibility over the classes your theme has.

More information

创建高级主题设置

原文链接: Creating advanced theme settings  译者:Fishfree

每个主题在Drupal的管理后台都有自己的设置页,路径是admin/appearance/settings/THEMENAME。设置页上一般都有表单,带有诸如Logo图像和Favicon图标等标准的设置。

在Drupal 8里,通过在“THEMENAME.theme” 文件或在“theme-settings.php”文件里添加THEMENAME_form_system_theme_settings_alter(&$form, $form_state)钩子函数,可更改主题设置表单。详细用法可参考“Drupal 8的Form API”、“表单和渲染表单元素”一览表、hook_form_FORM_ID_alter() 等文档。

举个例子:有个“foo”主题,要在它里面添加一个单行文本字段,其默认值是“blue bikeshed”。则可在foo/foo.theme文件或foo/theme-settings.php文件里添加如下代码:

function foo_form_system_theme_settings_alter(&$form, \Drupal\Core\Form\FormStateInterface &$form_state, $form_id = NULL) {
  // 一个变通方法,为了解决核心的一个影响到管理主体的bug,详见 issue #943212.
  if (isset($form_id)) {
    return;
  }

  $form['foo_example'] = array(
    '#type'          => 'textfield',
    '#title'         => t('Widget'),
    '#default_value' => theme_get_setting('foo_example'),
    '#description'   => t("Place this text in the widget spot on your site."),
  );
}

为了给添加的表单元素设置默认值,需要创建一个文件config/install/THEME.settings.yml文件,只需要在里面添加一行:SETTING_NAME: DEFAULT_VALUE。对于例子中的foo主题,需要编辑foo/config/install/foo.settings.yml文件并添加这行:

foo_example: blue bikeshed

在主题里的任意PHP文件里,都可以像这样取到用户设置的值:

$foo_example = theme_get_setting('foo_example');

注意:主题作者可以使用高级Form API来创建复杂、动态化的表单(如:自动完成、可折叠的fieldset)。

在主题文件中获取设置值

在THEMENAME.theme文件里添加preprocess函数可在Twig文件中添加新变量。

$variables['varname'] = theme_get_setting('varname')

例如:欲在node.html.twig文件里添加foo_example设置,先要在foo.theme文件里添加:

<?php
function foo_preprocess_node(&$variables) {
$variables['foo_example'] = theme_get_setting('foo_example');
}

然后在node.html.twig文件里就可以像访问任何普通的Twig变量一样来访问foo_example:

{{ foo_example }}

 

Drupal6, 7和8在主题化上的不同

原文链接:Theming differences between Drupal 6, 7 & 8

下面是一个 Drupal 8 在主题方面最值得一提的一些变化列表。

更多信息


以下是英文原文,可对照阅读

Last updated November 30, 2014. Created on October 15, 2014.
Edited by brunodbo, mbrett5062, djac, heather. You can edit this page, too.

This is a list of some of the most noteworthy changes in Drupal 8 that affect theming.

More information

D8 和 D7 在CSS Class 变化

原文链接:CSS Class Changes between D7 and D8

菜单

D7:

.menu li.expanded
.menu li.collapsed
.menu li.leaf
.menu li.active-trail
.menu li.first
.menu li.last

D8:

.menu-item--expanded
.menu-item--collapsed
.menu-item--active-trail
.menu-item:first-child
.menu-item:last-child

.menu-item--leaf 用的非常少,所以移除了. 也是可以再加上的。 @看一下例子 https://www.drupal.org/node/2425691#comment-9629101
@todo 这篇文档中的链接还会更新。


以下是英文原文,可对照阅读

Last updated February 17, 2015. Created on February 17, 2015.
Edited by joelpittet. You can edit this page, too.

Menu

D7:

.menu li.expanded
.menu li.collapsed
.menu li.leaf
.menu li.active-trail
.menu li.first
.menu li.last

D8:

.menu-item--expanded
.menu-item--collapsed
.menu-item--active-trail
.menu-item:first-child
.menu-item:last-child

.menu-item--leaf was rarely used so it was removed. It can be added back. @see example https://www.drupal.org/node/2425691#comment-9629101
@todo replace with link in this documentation.

升级 7.x 模板到 8.x

Upgrading 7.x themes to 8.x

Drupal 7 介绍了一些新的类如: element-hidden, element-invisible and element-focusable. 从名字上比较难以理解他们的作用, 所以我们改了下名字. 新名字应该比较容易明白而且和 知名的html 5框架 Boilerplate 保持一致, 改变和使用方法如下:

注. 屏幕阅读器 - 是指盲人使用的设备!

Before (7.x) After (8.x) Notes
element-hidden hidden 同时隐藏可见和屏幕阅读器 (例. -------- 或者你会用 JavaScript 隐藏的元素)
element-invisible visually-hidden 隐藏可见,但屏幕阅读器可见. (例. 一个菜单标题!).
element-focusable visually-hidden focusable 允许使用键盘操作,但是属于隐藏 (例. skip-to-content(跳过内容) links). focusable 类, 必须和 visually-hidden 一起使用.
  invisible 这个类是新添加到 Drupal 8 中的. 此类同时隐藏可见和屏幕阅读器,但是始终保持布局可见.


这些类被应用到了 core/modules/system/css/system.module.css.

未来更多消息,请使用 the HTML 5 Boilerplate CSS documentationCSS in Action: Invisible Content Just for Screen Reader Users,和 Hiding Content for Accessibility.


以下是英文原文,可参照阅读:

Last updated November 27, 2013. Created on June 19, 2013.
Edited by joelpittet, mparker17, mgifford. You can edit this page, too.

Changes to core classes

Simplified names of "element-x" helper classes

Drupal 7 introduced some new classes: element-hidden, element-invisible and element-focusable. It was difficult to understand exactly what these classes did from their names, so the class names were changed. The new names should make the classes easier to understand and are more consistent with HTML 5 Boilerplate, a popular HTML 5 framework. Here is a conversion and usage table:

Before (7.x) After (8.x) Notes
element-hidden hidden Hide both visually and from screenreaders (e.g.: an element that will be populated later with JavaScript or an element you will hide with JavaScript).
element-invisible visually-hidden Hide visually, but available for screenreaders (e.g.: a menu title).
element-focusable visually-hidden focusable Allows a visually-hidden element to be navigated to using the keyboard (e.g.: skip-to-content links). The focusable class must be used with visually-hidden.
  invisible This class is new in Drupal 8. It hides the element both visually and from screenreaders, but maintains the visual layout.

These classes are implemented in core/modules/system/css/system.module.css.

For further information, please refer the HTML 5 Boilerplate CSS documentation, CSS in Action: Invisible Content Just for Screen Reader Users, and Hiding Content for Accessibility.

PHP模板与Twig制作主题范例的比较

原文链接:https://www.drupal.org/node/1918824

本页面比较了PHP模板和Twig。Twig工具还在制作过程中,所以本文将会有改动和添加。

更多信息:

在核心问题队列中的Twig问题

Drupal.org上的Twig转换指南

Twig文档

关于Twig

Twig是基于PHP编译的模板语言。当渲染你的网页时,Twig引擎接管模板并将其转化为编译的PHP模板,并存储在一个被保护的目录下sites/default/files/php_storage/… 编译被执行一次。模板文件将被缓存,或在清除Twig缓存后重新编译。

Drupal的Twig行动与Symfony行动有同样的目的: 采用一个现代的,强力的,基于OOP的引擎可以让开发者集中于Drupal本身

  1. Docblock

PHP模板:

  <?php
  /**
   * @file
   * File description
   */
  ?>

Twig:

 {#
  /**
   * @file
   * File description
   */
  #}
  1. 文件和函数名:

PHP模板文件: node--article.tpl.php

Twig文件: node--article.html.twig

PHP模板函数: theme_node_links()

Twig函数: node-links.html.twig

  1. 变量

输出一个变量:

PHP模板: 

<div class="content"><?php print $content; ?></div>

Twig:

<div class="content">{{ content }}</div>

输出一个hash key对象:

PHP模板: 

<?php print $item['#item']['alt']; ?>

Twig:

{{ item['#item'].alt }}

分配变量:

PHP模板: <?php $custom_var = $content->comments; ?>

Twig:{% set custom_var = content.comments %}

分配数组:

PHP模板: 

<?php $args = array('!author' => $author, '!date' => $created); ?>

Twig:

{% set args = {'!author': author, '!date': created} %}
  1. 条件

PHP模板:

 <?php if ($content->comments): endif; ?>

Twig:

{% if content.comments %} {% endif %}

PHP模板:

 <?php if (!empty($content->comments)): endif; ?>

Twig:

{% if content.comments is not empty %} {% endif %}

PHP模板:

 <?php if (isset($content->comments)): endif; ?>

Twig:

{% if content.comments is defined %} {% endif %}

PHP模板: 

<?php if ($count > 0): endif; ?>

Twig:

{% if count > 0 %} {% endif %}
  1. 控制结构

PHP模板: <?php foreach ($users as $user) {} ?>

Twig:{% for user in users %} {% endfor %}

  1. 过滤器

Check_plain:

PHP模板:

 <?php print check_plain($title); ?>

Twig:

{{ title|striptags }}

翻译:

PHP模板: 

<?php print t('Home'); ?>

Twig:

{{ 'Home'|t }}

有替代变量的翻译:

PHP模板: 

<?php print t('Welcome, @username', array('@username' => $user->name)); ?>

Twig:

{{ 'Welcome, @username'|t({ '@username': user.name }) }}

Drupal 8 Twig (有翻译标签扩展):

{% set username = user.name %}
{% trans %}
  Welcome, {{ username }}
{% endtrans %}

展开列表:

PHP模板:

<?php echo implode(', ', $usernames); ?>

Twig:

{{ usernames | join(', ') }}

这个PHP模板的例子需要$usernames是一个字符数组。同样,原始的Twig例子中usernames也是一个字符数组。Drupal 8 Twig的例子则需要一个可渲染的对象。这个实际上是Drupal 8 Twig 和原始Twig的根本不同。Drupal 8 Twig 输出纯文本和可渲染数组。

这个例子的另一方面是3个例子被期待输出同样的结果,但是实际默认不是,看这个例子:

<?php
  {% set numbers = [{'#markup': 'One'}, {'#markup':'Two'}, {'#markup':'Three'}] %}
  {{ numbers }}
?>

上面企图输出被逗号隔开的元素,但实际输出是:OneTwoThree。

异常:

PHP模板:

<?php echo check_plain($title); ?>

原始Twig:

{{ title|e }}

Drupal 8 Twig:

{{ title }}
  1. 空白控制

Twig拥有空白控制功能,它允许将建立模板文件的空白移除。

<div class="body">
  {{- block.content -}}
</div>

等同于:

<div class="body">{{ block.content }}</div>

注意

  1. Hash key元素的例子在未来可能改变
  2. 在第二个例子中,我们展示了Twig也会负责净化数据。此前,这是模板文件或预处理函数的事。这个最后的改变值得所有希望为Drupal 8制作PHP模板主题的人着重关注——你需要自己净化你的数据,所有。

为自定义主题创建自动化工具(Gulpjs)

原文地址:Creating automation tools for custom themes (Gulpjs)

使用自动化工具简化主题创建任务,这里我们将使用gulpjs给自定义主题创建自动化工具。

步骤:

  1. 安装Node.js

从nodejs.org下载并安装最新版NodeJS,不同操作系统安装过程有差异。

  1. 配置 gulpfile.js

进入你的自定义主题目录。

通常使用 cmd/terminal 创建一个gulpfile.js或者右键点击创建新文件,打开gulpfile.js文件,复制并粘贴整个代码(到新建空白文件中)。

注意:我把我的原始文件放在“src”文件夹中,已经新建好的文件放入主题跟目录,因此,当sass文件从“my_sustom_theme_folder/src/scss”目录编译时,它们将被编译并放入“my_custom_theme_folder/css”.

var gulp = require('gulp'),
	livereload = require('gulp-livereload'),
	gulpIf = require('gulp-if'),
	eslint = require('gulp-eslint'),
	sass = require('gulp-sass'),
	autoprefixer = require('gulp-autoprefixer'),
	sourcemaps = require('gulp-sourcemaps'),
	imagemin = require('gulp-imagemin'),
	pngquant = require('imagemin-pngquant');
	 
gulp.task('imagemin', function () {
    return gulp.src('./src/images/*')
        .pipe(imagemin({
            progressive: true,
            svgoPlugins: [{removeViewBox: false}],
            use: [pngquant()]
        }))
        .pipe(gulp.dest('./images'));
});


gulp.task('sass', function () {
  gulp.src('./src/sass/**/*.scss')
    .pipe(sourcemaps.init())
        .pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
        .pipe(autoprefixer('last 2 version'))
    .pipe(sourcemaps.write('./'))
    .pipe(gulp.dest('./css'));
});

function isFixed(file) {
	// Has ESLint fixed the file contents?
	return file.eslint != null && file.eslint.fixed;
}

gulp.task('eslint', function(){
	gulp.src(['./src/js/*.js'])
		.pipe(eslint({
			
  				"extends": "eslint:recommended",
 				"env": {
    				"browser": true
  				},
  				"globals": {
    				"Drupal": true,
    				"drupalSettings": true,
   				"drupalTranslations": true,
    				"domready": true,
    				"jQuery": true,
   				"_": true,
    				"matchMedia": true,
    				"Backbone": true,
    				"Modernizr": true,
    				"CKEDITOR": true
 				 },
 			 "rules": {
    			// Errors.
    			"array-bracket-spacing": [2, "never"],
    			"block-scoped-var": 2,
   			"brace-style": [2, "stroustrup", {"allowSingleLine": true}],
    			"comma-dangle": [2, "never"],
    			"comma-spacing": 2,
    			"comma-style": [2, "last"],
    			"computed-property-spacing": [2, "never"],
    			"curly": [2, "all"],
    			"eol-last": 2,
    			"eqeqeq": [2, "smart"],
    			"guard-for-in": 2,
    			"indent": [2, 2, {"SwitchCase": 1}],
   			"key-spacing": [2, {"beforeColon": false, "afterColon": true}],
    			"linebreak-style": [2, "unix"],
    			"lines-around-comment": [2, {"beforeBlockComment": true, "afterBlockComment": false}],
    			"new-parens": 2,
    			"no-array-constructor": 2,
    			"no-caller": 2,
    			"no-catch-shadow": 2,
    			"no-empty-label": 2,
    			"no-eval": 2,
    			"no-extend-native": 2,
    			"no-extra-bind": 2,
    			"no-extra-parens": [2, "functions"],
    			"no-implied-eval": 2,
    			"no-iterator": 2,
    			"no-label-var": 2,
    			"no-labels": 2,
    			"no-lone-blocks": 2,
    			"no-loop-func": 2,
    			"no-multi-spaces": 2,
    			"no-multi-str": 2,
    			"no-native-reassign": 2,
    			"no-nested-ternary": 2,
    			"no-new-func": 2,
    			"no-new-object": 2,
   			"no-new-wrappers": 2,
    			"no-octal-escape": 2,
    			"no-process-exit": 2,
    			"no-proto": 2,
    			"no-return-assign": 2,
    			"no-script-url": 2,
    			"no-sequences": 2,
    			"no-shadow-restricted-names": 2,
    			"no-spaced-func": 2,
    			"no-trailing-spaces": 2,
    			"no-undef-init": 2,
    			"no-undefined": 2,
    			"no-unused-expressions": 2,
    			"no-unused-vars": [2, {"vars": "all", "args": "none"}],
   			"no-with": 2,
    			"object-curly-spacing": [2, "never"],
    			"one-var": [2, "never"],
    			"quote-props": [2, "consistent-as-needed"],
    			"semi": [2, "always"],
    			"semi-spacing": [2, {"before": false, "after": true}],
    			"space-after-keywords": [2, "always"],
    			"space-before-blocks": [2, "always"],
    			"space-before-function-paren": [2, {"anonymous": "always", "named": "never"}],
    			"space-in-parens": [2, "never"],
    			"space-infix-ops": 2,
    			"space-return-throw-case": 2,
    			"space-unary-ops": [2, { "words": true, "nonwords": false }],
    			"spaced-comment": [2, "always"],
    			"strict": 2,
    			"yoda": [2, "never"],
    			// Warnings.
    			"max-nested-callbacks": [1, 3],
    			"valid-jsdoc": [1, {
      			"prefer": {
       		 		"returns": "return",
        				"property": "prop"
      			},
     	 		"requireReturn": false
    			}]
 	 		},
 	 		fix: true,
			
	}))
	.pipe(eslint.format())
	// if fixed, write the file to dest
	.pipe(gulpIf(isFixed, gulp.dest('./js/')));;
});

gulp.task('watch', function(){
    livereload.listen();

    gulp.watch('./src/sass/**/*.scss', ['sass']);
    gulp.watch('./src/js/**/*.js', ['eslint']);
    gulp.watch(['./css/style.css', './**/*.html.twig', './js/*.js'], function (files){
        livereload.changed(files)
    });
});
  1. 设置包管理器

在自定义主题目录中在terminal中键入 npm init,并填写在创建package.json时被请求的必要信息。

  1. 安装依赖包

在自定义主题目录中在terminal中键入 npm install <packagename> .这将下载gulp文件和它的关联依赖包并将创建一个目录“node_modules”.

  1. 从 cmd/terminal 运行自动化工具

现在你已经准备好运行gulp自动化工具了。

键入 gulp watch 命令。

Drupal 模板引擎Twig 转换说明(由tpl.php到html.twig)

原文地址:https://www.drupal.org/docs/8/theming/drupal-twig-conversion-instructions-tplphp-to-htmltwig

Drupal 模板引擎Twig 转换说明(由tpl.php到html.twig)

这篇文档常用于drupal8 Twig模板转换过程,对于在drupal8中用Twig模板引擎更新主题和模块来说也是很有帮助的。

说明:在Drupal的核心问题中Twig的工作已经完成,只是用Twig的转换沙箱找到以前转换的模板和函数。

关键步骤:

  • 找到核心问题打补丁
  • 不能在沙箱中打补丁
  • 不能给沙箱创建补丁
  • 只用沙箱测试或检索以前的转换代码
  • 首先,在优酷视频网上观看这个过程的概览

 

安装

首先下载

git clone -b 8.0.x的下载地址: http://git.drupal.org/project/drupal.git d8

将当前版本的安装在文件夹(文件夹的名字可以自己指定)

  1. 正常安装Drupal(使用标准的安装文件)
  2. 在services.yml中设置twig的3个属性(debugging, cache, auto_reload)的值为True

转换

主题函数

将主题函数转换成模板文件和预处理函数:

  1. 确定主题函数文件的路径:(theme.inc? a core/modules/color/?)
  2. 为主题函数创建一个模板文件X.html.twig
  • 正确命名新文件
  • 移除函数开始的"theme_", 以" .html.twig"的文件名结尾
  • 将下划线“­­_”转换成中间线”-”
  • 例如:
  • theme_link()变成link.html.twig
  • theme_user_signature()变成user-signature.html.twig

 

  1. 将新的Twig模板放在stark主题的模板文件夹中(沙箱中):

来自特定的模块stark/templates/comment, etc

来自theme.inc, stark/templates/theme.inc

来自form.inc, stark/templates/form.inc

  1. the Drupal 8 API的文档中搜索函数

电子表格中有所有函数的链接

  1. 在文件的头部添加PHP格式的代码块{##},并包装在Twig的评论中
  • 在每行的开头增加@file标记
  • 在@file行下面复制函数的定义,用“Default theme implementation ...”替换“Returns HTML ...”,写在一行上。
  • 增加“Available variables:”行(替换 @param 变量)
  • 在api.drupal.org上的参数文档中,复制指定的变量
  • 如果有@see template_preprocess()行,就删除它
  • 增加@see template_preprocess_THEME_HOOK()行
  • 增加@ingroup themeable行(见下面的代码块示例)
  1. 在下面的代码块中复制函数的源代码(见下面的示例)
  2. 将PHP代码转换成常见的HTML代码,并注释说明)
  1. )在HTML代码中删除PHP代码,例如:

function whatever() {

// …

return $output; }

  1. )删除PHP的注释说明并用{{ }}代替,

把转换为简单的名字:$variables['title'] 变成 {{ title }}

用点语法代替数组语法:$variables['page']['tabs'] 变成 {{ page.tabs }}

  1. )用twig的{% %}代替PHP的<?php  ?>
  2. )用twig的注释语法{##}代替php的注释
  3. )用t 过滤器代替文字函数:{{ 'text in quotes'|t }}
  4. )移除预处理函数变量的PHP格式(见下面的预处理说明)

8.做了这些之后,如果你要改进将冗余的模板合并成一个,或者改进标记或者变量名这些事,就要在电子表格中做注释,或者在沙箱中做一个焦点,例如:http://drupal.org/node/180591

转换或者合并预处理函数

说明;

  1. 预处理函数将替代所有的模板函数
  2. 如果模板文件中有PHP的语法影响输出变量,这些代码需要移动到预处理函数中
  3. 如果模板以主题函数开始,主题函数需要转换成预处理函数
  4. 如果主题函数已经有相关的预处理函数,主题函数中处理代码的变量需要移动到预处理函数中
  5. 不要在hook_theme的实现中加一条线表明drupal使用模板文件而不是主题函数
  6. 说明
  7. 将template_preprocess_YOURFUNCTION重命名为template_preprocess_YOURFUNCTION
  8. 通过加&传递变量,关键是theme_select($variables)变成template_preprocess_select(&$variables)
  9. 编辑函数处理变量的处理逻辑,去掉任何标记(关键是 $output)

如果在模板中有丢失的函数

如果在模板中需要的过滤器或者函数的入口还没有工作,把它加在开放的焦点里。记住大部分或者函数应该移动到预处理函数中。只有你认为主题驱动需要这个函数的入口,那么此函数应该留在模板中。

简单的转换示例

  1. 代码来源http://api.drupal.org/api/drupal/core!includes!theme.inc/function/theme_...

function theme_link($variables) { return '' . ($variables['options']['html'] ? $variables['text'] : check_plain($variables['text'])) . ''; }

  • 模板(文件名:link.html.twig)

{# /** * @file * Default theme implementation to display a link. * * Available variables: * - text: The link text for the anchor tag. * - url: The complete URL being linked to, such as * "/node/34" or "http://example.com/foo". * - attributes: Remaining HTML attributes for the containing element. * * @see template_preprocess_link() * * @ingroup themeable */ #} <a href="{{ url }}" class="{{ attributes.class }}"{{ attributes }}>{{ text }}</a>

改成预处理函数

/** * Prepares variables for link templates. * * Default template: link.html.twig. * * @param array $variables * An associative array containing: * - text: The translated link text for the anchor tag. * - path: The internal path or external URL being linked to. * - options: An associative array of additional options. */ function template_preprocess_link(&$variables) { $variables['url'] = url($variables['path'], $variables['options']); }

评论:

Andriy Podanenko: http://drupal.org/node/1783130如何重命名变量

  • 以 {# 和 #}为开始和结束的标志
  • 标准的PHP代码块标记生成器要遵循Twig注释标记标准
  • 从api.drupal.org复制粘贴这个定义

James Wilson:如果从函数中复制这个定义,应该用"Default theme implementation"重写"Returns HTML ..."

  • 从api.drupal.org复制粘贴参数

James Wilson:如果需要从文档中引用另一个变量块,变量要使用单引号,在变量名称中去掉$标志,。(见讨论http://drupal.org/node/1804710])

  • 在twig中使用{{ }}语法输出变量
  • 可以参考属性可以修改的类
  • 像url()一样大部分函数可以从模板文件中移动到预处理函数中

引用部分模板

原文地址:

  1. 许多主题开发者喜欢将header/footer代码放在单独的文件中,并在page.html.twig中调用
  2. 假设在如下的文件夹中创建header文件/THEME_NAME/includes/header.html.twig
  3. 用如下的代码格式引用header文件{% include directory ~ '/includes/header.html.twig' %}