Creating Content


Your Content

When building your project, Tapestry buckets files into collections based upon them matching filters for configured content types; by default Tapestry comes with just two content types: blog and default.

Essentially if a file doesn't match any configured content type filters then it gets placed into the default collection – there is no limit on how many content types you add to Tapestry, click here for more information on how they work and examples on how to configure them.

Supported file extensions

Out of the box Tapestry comes with content renderers that support the following file extensions:

  • .md/.markdown
  • .htm/.html
  • .phtml

Any file not matching the above extensions will be copied with the same relative path to the build directory.

Writing posts

Posts are looked for as files within the source/_blog folder of your project. Tapestry expects the _blog folder to have a flat structure containing markdown files with a filename format of: YYYY-MM-DD-post-title.md – Tapestry does however support omitting the date in preference of including it within the files front matter.

A typical post

The standard blog style post file contains optional front matter and the posts content. If your filename is formatted with the date of publish then including date and title within the files front matter is optional; however it will take precedent if set.

---
title: "My first post"
date: 2017-03-30
categories:
        - first post
        - update
tags:
        - example
---
# My first post
Paste this within a `.md` file inside your projects `_posts` directory and build your project to see how it gets generated.

Post Layouts

The default blog content type is configured to look for a blog.phtml file inside the _views directory of your project. Tapestry will compile the markdown content into HTML and then inject it and the post meta data into the blog.phtml.

You can use built in content helpers to display post content and meta data, e.g:

<?php
// $title is equal to the posts title; this will need passing on to the parent template
// if you want it to be used for the <title> tag.
$this->layout('_templates/default', ['title' => $title]);
?>
<article>
    <header>
        <h1><?= $title ?></h1>
        <small>Published on <?= $date->format('F jS Y') ?></small>
    </header>
    <?= $this->getContent() ?>
</article>

Displaying an index of posts

Collections are accessed by templates through the use front matter property for example:

---
use:
    - blog
    - blog_categories
---

The above will prompt Tapestry to inject the template with $blog_items and $blog_categories_items. Note the _items postfix; any content type, or its taxonomy can be requested in this manor, for more details on this mechanism see here.

Both variables will be arrays and therefore provide you with the ability to slice, filter and order however you would like, either within the template itself or within a custom view helper function that you have created.

For example to get the first six posts and pass them to a partial template you can do the following:

<?php
/**
 * @var \Tapestry\Entities\ViewFile[] $blog_items
 * @var \Tapestry\Entities\ViewFile $item
 */
foreach(array_slice($blog_items, 0, 6) as $item) {
    echo $this->fetch('_views/article_tile', ['item' => $item]);
}

Within the _views/article_tile template you can use all the same helpers and variables as you could with the blog.phtml template to access the posts information, except on $item rather than $this – this is because the $this variable will reference the article_tile file while the $item variable will be the actual blog post in question.

Creating pages

Pages are content files that end up getting placed into the default collection; they can be placed anywhere within your source folder so long as the path is not within you configured ignore list.

Tapestry supports pages with any of the supported file extensions; if a file isn't supported by a configured renderer then Tapestry will copy it instead.

Flattening pages from subfolders into the root directory

If you have your projects pages organised into sub-folders but would like to flatted them into the root folder on build, you need to set the permalink property within the files front matter:

---
title: A super awesome page
permalink: super.html
---

This will prompt Tapestry to output the rendered file content to super.html within the root of the build destination folder.

Additional Content

In addition to assets, pages, posts and other content types that you may choose to configure; you will likely have additional content files that require generating such as your robots.txt and atom.xml files. Normally due to their extension Tapestry treats these as assets and marks them for copying.

However there is a way to have Tapestry generate these files and that is to have them in your source as .phtml and set their permalink to the final file extension. For example the below produces an atom.xml file containing the most recent 10 items from the posts collection:

---
use:
- blog
permalink: atom.xml
---
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="https://www.w3.org/2005/Atom">
    <title><![CDATA[<?= $this->site('title') ?>]]></title>
    <link href="<?= $this->url('/atom.xml') ?>" rel="self"/>
    <link href="<?= $this->url('/') ?>"/>
    <updated><?= date('c') ?></updated>
    <id><?= $this->url('/') ?></id>
    <?php if ($this->site('author', false) || $this->site('email', false)) { ?>
        <author>
            <?php if ($author = $this->site('author', null)){ ?>
                <name><![CDATA[<?= $author ?>]]></name>
            <?php } ?>
            <?php if ($email = $this->site('email', null)){ ?>
                <email><![CDATA[<?= $email ?>]]></email>
            <?php } ?>
        </author>
    <?php } ?>
    <generator uri="<?= $this->url('/') ?>"><?= $this->site('title') ?></generator>
    <?php
    /** @var \Tapestry\Entities\ViewFile $item */
    foreach(array_slice($blog_items,0, 10) as $item ){ ?>
        <entry>
            <title type="html"><![CDATA[<?= $item->getData('title') ?>]]></title>
            <link href="<?= $item->getUrl() ?>"/>
            <updated><?= $item->getDate()->format('c') ?></updated>
            <id><?= $item->getUrl() ?></id>
            <content type="html"><![CDATA[<?= $item->getContent() ?>]]></content>
        </entry>
    <?php } ?>
</feed>

Working with drafts

Drafts are files with draft: true set within their front matter. Tapestry will not publish files marked as draft unless the environment configuration for publishing drafts is set to true.