How can I globally set variables? Originally at Include a template that sets variables (global template variables).

I have a number of templates and layouts but every layout includes a global header in which the meta data is displayed. 

That data always comes from the entry you are viewing and currently I'm setting the variables the global header needs in each template, such as /news/index.html:

{% extends "_listing_layout" %}

{% set metaTitle = (entry.metaTitle) ? entry.metaTitle : entry.title %}
{% set metaDescription = entry.metaDescription %}
{% set metaKeywords = entry.metaKeywords %}
{% set metaRobots = entry.metaRobots %}
{% set openGraphImage = entry.openGraphImage.first() %}

{% if openGraphImage %}

    {% set ogCrop = {
        mode: 'crop',
        width: 1200,
        height: 630,
        quality: 75,
        position: 'center'
    } %}

    {% set openGraphImage = openGraphImage.getUrl(ogCrop) %}

{% endif %}

{% set listingSection = "newsArticles" %}

{% block content %}

    &lt;h1&gt;{{ entry.title }}&lt;/h1&gt;

    {% include '_partials/_isotope.html' %}

{% endblock %}


The layout is super simple:

{% include '_partials/_global_header.html' %}

    {% block content %}{% endblock %}

{% if craft.request.firstSegment %}

    {% include '_partials/_global_footer.html' %}

{% else %}

    {% include '_partials/_home_footer.html' %}

{% endif %}


And then the global header partial:

&lt;!DOCTYPE html&gt;
&lt;!--[if lt IE 7]&gt;      &lt;html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"&gt; &lt;![endif]--&gt;
&lt;!--[if IE 7]&gt;         &lt;html class="no-js lt-ie9 lt-ie8" lang="en"&gt; &lt;![endif]--&gt;
&lt;!--[if IE 8]&gt;         &lt;html class="no-js lt-ie9" lang="en"&gt; &lt;![endif]--&gt;
&lt;!--[if gt IE 8]&gt;&lt;!--&gt; &lt;html lang="en-gb" dir="ltr" class="no-js"&gt; &lt;!--&lt;![endif]--&gt;

    &lt;head&gt;

        {# meta #}
        &lt;meta charset="utf-8"&gt;
        &lt;title&gt;{% if metaTitle is defined %}{{ metaTitle }} - {% endif %}{{ siteName }}&lt;/title&gt;

        {# seo #}
        &lt;meta name="description" content="{% if metaDescription is defined %}{{ metaDescription }}{% endif %}"&gt;
        &lt;meta name="keywords" content="{% if metaKeywords is defined %}{{ metaKeywords }}{% endif %}"&gt;
        &lt;link rel="canonical" href="{{ siteUrl }}"&gt;
        &lt;meta name="robots" content="{% if metaRobots is defined %}{{ metaRobots }}{% else %}index,follow{% endif %}"&gt;

        {# open graph #}
        &lt;meta property="og:site_name" content="{{ siteName }}"&gt;
        &lt;meta property="og:type" content="website"&gt;
        &lt;meta property="og:title" content="{% if metaTitle is defined %}{{ metaTitle }} - {% endif %}{{ siteName }}"&gt;
        &lt;meta property="og:url" content="{{ siteUrl }}"&gt;
        &lt;meta property="og:image" content="{% if openGraphImage is defined %}{{ openGraphImage }}{% else %}{{ siteUrl }}images/og-default.jpg{% endif %}"&gt;
        &lt;meta property="og:description" content="{% if metaDescription is defined %}{{ metaDescription }}{% endif %}"&gt;

...etc


How can I keep this variable code DRY? I've tried putting direct in the header partial and layouts, but that doesn't work. And putting it in an included partial within each template throws a template error on account of the fact the template extends a layout and can't have a body.
There are two basic issues with using includes in this scenario, both of which you have discovered.

The first is that included templates do not have access to variables defined in master templates. You would have to specifically pass the variables to the template in the include statement using the with clause.

The second is that extended templates cannot have a body outside of a declaration block, and includes are by definition expected to return html, and thus throw an error.

A better solution would be to use hierarchical extensions vs includes, placing all shared content together in the parent template. All variables in child templates should bubble up through the template hierarchy and be available to the parent templates. Something like the following for example (I've left out much of the content for brevity).

In _layout.html:

&lt;!DOCTYPE html&gt;
&lt;!--[if lt IE 7]&gt;      &lt;html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"&gt; &lt;![endif]--&gt;
&lt;!--[if IE 7]&gt;         &lt;html class="no-js lt-ie9 lt-ie8" lang="en"&gt; &lt;![endif]--&gt;
&lt;!--[if IE 8]&gt;         &lt;html class="no-js lt-ie9" lang="en"&gt; &lt;![endif]--&gt;
&lt;!--[if gt IE 8]&gt;&lt;!--&gt; &lt;html lang="en-gb" dir="ltr" class="no-js"&gt; &lt;!--&lt;![endif]--&gt;

&lt;head&gt;
    {# meta #}
    &lt;meta charset="utf-8"&gt;
    &lt;title&gt;{% if metaTitle is defined %}{{ metaTitle }} - {% endif %}{{ siteName }}&lt;/title&gt;

    {# set seo #}
    &lt;meta name="description" content="{% if metaDescription is defined %}{{ metaDescription }}{% endif %}"&gt;
    &lt;meta name="keywords" content="{% if metaKeywords is defined %}{{ metaKeywords }}{% endif %}"&gt;
    &lt;link rel="canonical" href="{{ siteUrl }}"&gt;
    &lt;meta name="robots" content="{% if metaRobots is defined %}{{ metaRobots }}{% else %}index,follow{% endif %}"&gt;

    {# set open graph #}
    &lt;meta property="og:site_name" content="{{ siteName }}"&gt;
    &lt;meta property="og:type" content="website"&gt;
    &lt;meta property="og:title" content="{% if metaTitle is defined %}{{ metaTitle }} - {% endif %}{{ siteName }}"&gt;
    &lt;meta property="og:url" content="{{ siteUrl }}"&gt;
    &lt;meta property="og:image" content="{% if openGraphImage is defined %}{{ openGraphImage }}{% else %}{{ siteUrl }}images/og-default.jpg{% endif %}"&gt;
    &lt;meta property="og:description" content="{% if metaDescription is defined %}{{ metaDescription }}{% endif %}"&gt;

    ...etc

    {# set default site meta variables, if any #}

    {% block head %}{% endblock %}

&lt;/head&gt;    

&lt;body&gt;

    {% block content %}{% endblock %}

    {% block footer %}{% endblock %}

&lt;/body&gt;

&lt;/html&gt;


In _home-layout.html:

{% extends "_layout" %}

{% block head %}
    {# additional home head content #}
{% endblock %}

{% block content %}{% endblock %}

{% block footer %}
    {# home footer content #}
{% endblock %}


In _page-layout.html:

{% extends "_layout" %}

{% block head %}
    {# additional page head content #}
{% endblock %}

{% block content %}{% endblock %}

{% block footer %}
    {# page footer content #}
{% endblock %}


In your index.html template:

{% extends "_home-layout" %}

{# set home meta variables, etc #}

{% block content %}
    {# home body content #}
{% endblock %}


In your page.html template:

{% extends "_page-layout" %}

{# set page meta variables, etc #}

{% block content %}
    {# page body content #}
{% endblock %}


In this way you can group shared content in the parent template and specific content further down the template hierarchy. How best to structure this hierarchy to keep you code 'dry' will be heavily dependent on the needs of your project so I won't even begin to guess.

Keep in mind it is also possible to define blocks of html in variables using conditionals and then place those variables into the block declarations.

{% if craft.request.firstSegment %}

    {% set footer %}
        {# page footer content #}
    {% endset %}

{% else %}

    {% set footer %}
        {# home footer content #}
    {% endset %}

{% endif %}

{% block footer %}
    {{ footer }}
{% endblock %}