diff --git a/docs/en/02_Developer_Guides/05_Extending/01_Extensions.md b/docs/en/02_Developer_Guides/05_Extending/01_Extensions.md
index e872a092c..1ece0cd4a 100644
--- a/docs/en/02_Developer_Guides/05_Extending/01_Extensions.md
+++ b/docs/en/02_Developer_Guides/05_Extending/01_Extensions.md
@@ -10,6 +10,11 @@ or even their own code to make it more reusable.
Extensions are defined as subclasses of either [api:DataExtension] for extending a [api:DataObject] subclass or
the [api:Extension] class for non DataObject subclasses (such as [api:Controllers])
+
+For performance reasons a few classes are excluded from receiving extensions, including `Object`, `ViewableData`
+and `RequestHandler`. You can still apply extensions to descendants of these classes.
+
+
**mysite/code/extensions/MyMemberExtension.php**
:::php
diff --git a/docs/en/02_Developer_Guides/07_Debugging/03_Template_debugging.md b/docs/en/02_Developer_Guides/07_Debugging/03_Template_debugging.md
new file mode 100644
index 000000000..fe1cf8654
--- /dev/null
+++ b/docs/en/02_Developer_Guides/07_Debugging/03_Template_debugging.md
@@ -0,0 +1,19 @@
+title: Template debugging
+summary: Track down which template rendered a piece of html
+
+# Debugging templates
+
+## Source code comments
+
+If there is a problem with the rendered html your page is outputting you may need
+to track down a template or two. The template engine can help you along by displaying
+source code comments indicating which template is responsible for rendering each
+block of html on your page.
+
+ ::::yaml
+ ---
+ Only:
+ environment: 'dev'
+ ---
+ SSViewer:
+ source_file_comments: true
diff --git a/docs/en/02_Developer_Guides/08_Performance/00_Partial_Caching.md b/docs/en/02_Developer_Guides/08_Performance/00_Partial_Caching.md
index 5c7d3a281..ddb63074b 100644
--- a/docs/en/02_Developer_Guides/08_Performance/00_Partial_Caching.md
+++ b/docs/en/02_Developer_Guides/08_Performance/00_Partial_Caching.md
@@ -74,34 +74,36 @@ Note the use of both `.max('LastEdited')` and `.count()` - this takes care of bo
edited since the cache was last built, and also when an object has been deleted since the cache was last built.
-We can also calculate aggregates on relationships. A block that shows the current member's favorites needs to update
-whenever the relationship `Member::$has_many = array('Favourites' => Favourite')` changes.
-
- :::ss
- <% cached 'favourites', $CurrentMember.ID, $CurrentMember.Favourites.max('LastEdited') %>
+We can also calculate aggregates on relationships. The logic for that can get a bit complex, so we can extract that on
+to the controller so it's not cluttering up our template.
## Cache key calculated in controller
-In the previous example the cache key is getting a bit large, and is complicating our template up. Better would be to
-extract that logic into the controller.
+If your caching logic is complex or re-usable, you can define a method on your controller to generate a cache key
+fragment.
+
+For example, a block that shows a collection of rotating slides needs to update whenever the relationship
+`Page::$many_many = array('Slides' => 'Slide')` changes. In Page_Controller:
:::php
- public function FavouriteCacheKey() {
- $member = Member::currentUser();
-
- return implode('_', array(
- 'favourites',
- $member->ID,
- $member->Favourites()->max('LastEdited')
- ));
+ public function SliderCacheKey() {
+ $fragments = array(
+ 'Page-Slides',
+ $this->ID,
+ // identify which objects are in the list and their sort order
+ implode('-', $this->Slides()->Column('ID')),
+ $this->Slides()->max('LastEdited')
+ );
+ return implode('-_-', $fragments);
}
-Then using that function in the cache key:
+Then reference that function in the cache key:
:::ss
- <% cached $FavouriteCacheKey %>
+ <% cached $SliderCacheKey %>
+The example above would work for both a has_many and many_many relationship.
## Cache blocks and template changes
@@ -207,8 +209,8 @@ could also write the last example as:
<% end_cached %>
-Currently cached blocks can not be contained within if or loop blocks. The template engine will throw an error
-letting you know if you've done this. You can often get around this using aggregates.
+Currently a nested cache block can not be contained within an if or loop block. The template engine will throw an error
+letting you know if you've done this. You can often get around this using aggregates or by un-nesting the block.
Failing example:
@@ -217,7 +219,7 @@ Failing example:
<% cached $LastEdited %>
<% loop $Children %>
- <% cached LastEdited %>
+ <% cached $LastEdited %>
$Name
<% end_cached %>
<% end_loop %>
@@ -236,3 +238,29 @@ Can be re-written as:
<% end_cached %>
<% end_cached %>
+
+Or:
+
+ :::ss
+ <% cached $LastEdited %>
+ (other code)
+ <% end_cached %>
+
+ <% loop $Children %>
+ <% cached $LastEdited %>
+ $Name
+ <% end_cached %>
+ <% end_loop %>
+
+## Cache expiry
+
+The default expiry for partial caches is 10 minutes. The advantage of a short cache expiry is that if you have a problem
+with your caching logic, the window in which stale content may be shown is short. The disadvantage, particularly for
+low-traffic sites, is that cache blocks may expire before they can be utilised. If you're confident that you're caching
+logic is sound, you could increase the expiry dramatically.
+
+**mysite/_config.php**
+
+ :::php
+ // Set partial cache expiry to 7 days
+ SS_Cache::set_cache_lifetime('cacheblock', 60 * 60 * 24 * 7);