Index: composer.json
===================================================================
--- composer.json
+++ composer.json
@@ -28,6 +28,10 @@
   },
   "require": {
     "composer/installers": "~1.0",
-    "php": ">=5.3.0"
+    "php": ">=5.3.0",
+    "zordius/lightncandy": "~1.2.0"
+  },
+  "config": {
+    "vendor-dir": "src/vendor"
   }
 }
Index: composer.lock
new file mode 100644
===================================================================
--- /dev/null
+++ composer.lock
@@ -0,0 +1,192 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "1d6253895db52527fe0fb1009a6e5da4",
+    "packages": [
+        {
+            "name": "composer/installers",
+            "version": "v1.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/composer/installers.git",
+                "reference": "049797d727261bf27f2690430d935067710049c2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/composer/installers/zipball/049797d727261bf27f2690430d935067710049c2",
+                "reference": "049797d727261bf27f2690430d935067710049c2",
+                "shasum": ""
+            },
+            "require": {
+                "composer-plugin-api": "^1.0"
+            },
+            "replace": {
+                "roundcube/plugin-installer": "*",
+                "shama/baton": "*"
+            },
+            "require-dev": {
+                "composer/composer": "1.0.*@dev",
+                "phpunit/phpunit": "^4.8.36"
+            },
+            "type": "composer-plugin",
+            "extra": {
+                "class": "Composer\\Installers\\Plugin",
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Composer\\Installers\\": "src/Composer/Installers"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Kyle Robinson Young",
+                    "email": "kyle@dontkry.com",
+                    "homepage": "https://github.com/shama"
+                }
+            ],
+            "description": "A multi-framework Composer library installer",
+            "homepage": "https://composer.github.io/installers/",
+            "keywords": [
+                "Craft",
+                "Dolibarr",
+                "Eliasis",
+                "Hurad",
+                "ImageCMS",
+                "Kanboard",
+                "Lan Management System",
+                "MODX Evo",
+                "Mautic",
+                "Maya",
+                "OXID",
+                "Plentymarkets",
+                "Porto",
+                "RadPHP",
+                "SMF",
+                "Thelia",
+                "WolfCMS",
+                "agl",
+                "aimeos",
+                "annotatecms",
+                "attogram",
+                "bitrix",
+                "cakephp",
+                "chef",
+                "cockpit",
+                "codeigniter",
+                "concrete5",
+                "croogo",
+                "dokuwiki",
+                "drupal",
+                "eZ Platform",
+                "elgg",
+                "expressionengine",
+                "fuelphp",
+                "grav",
+                "installer",
+                "itop",
+                "joomla",
+                "kohana",
+                "laravel",
+                "lavalite",
+                "lithium",
+                "magento",
+                "majima",
+                "mako",
+                "mediawiki",
+                "modulework",
+                "modx",
+                "moodle",
+                "osclass",
+                "phpbb",
+                "piwik",
+                "ppi",
+                "puppet",
+                "pxcms",
+                "reindex",
+                "roundcube",
+                "shopware",
+                "silverstripe",
+                "sydes",
+                "symfony",
+                "typo3",
+                "wordpress",
+                "yawik",
+                "zend",
+                "zikula"
+            ],
+            "time": "2017-12-29T09:13:20+00:00"
+        },
+        {
+            "name": "zordius/lightncandy",
+            "version": "v1.2.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/zordius/lightncandy.git",
+                "reference": "448cb2855d7301fa5380d8228255d46e9be92903"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/zordius/lightncandy/zipball/448cb2855d7301fa5380d8228255d46e9be92903",
+                "reference": "448cb2855d7301fa5380d8228255d46e9be92903",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "4.8.35"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.2.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "LightnCandy\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Zordius Chen",
+                    "email": "zordius@gmail.com"
+                }
+            ],
+            "description": "An extremely fast PHP implementation of handlebars ( http://handlebarsjs.com/ ) and mustache ( http://mustache.github.io/ ).",
+            "homepage": "https://github.com/zordius/lightncandy",
+            "keywords": [
+                "handlebars",
+                "logicless",
+                "mustache",
+                "php",
+                "template"
+            ],
+            "time": "2018-03-19T07:06:10+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": {
+        "php": ">=5.3.0"
+    },
+    "platform-dev": []
+}
Index: src/bp-core/bp-core-functions.php
===================================================================
--- src/bp-core/bp-core-functions.php
+++ src/bp-core/bp-core-functions.php
@@ -3309,25 +3309,36 @@
  * @return string
  */
 function bp_core_replace_tokens_in_text( $text, $tokens ) {
-	$unescaped = array();
-	$escaped   = array();
+	// Use LightnCandy library to render mustache tags.
+	$render = \LightnCandy\LightnCandy::prepare(
+		\LightnCandy\LightnCandy::compile( $text )
+	);
 
-	foreach ( $tokens as $token => $value ) {
-		if ( ! is_string( $value ) && is_callable( $value ) ) {
-			$value = call_user_func( $value );
-		}
+	/*
+	 * Dot notation backward-compatibilty parser.
+	 *
+	 * @see https://buddypress.trac.wordpress.org/ticket/6921#comment:8
+	 */
+	foreach ( $tokens as $token => $val ) {
+		if ( false !== strpos( $token, '.' ) && ! is_array( $val ) ) {
+			$split = explode( '.', $token );
+			if ( empty( $split[1] ) ) {
+				continue;
+			}
 
-		// Tokens could be objects or arrays.
-		if ( ! is_scalar( $value ) ) {
-			continue;
-		}
+			if ( empty( $tokens[ $split[0] ] ) ) {
+				$tokens[ $split[0] ] = array();
+			}
 
-		$unescaped[ '{{{' . $token . '}}}' ] = $value;
-		$escaped[ '{{' . $token . '}}' ]     = esc_html( $value );
+			// We currently only handle one level of dot notation for conversion.
+			$tokens[ $split[0] ][ $split[1] ] = $val;
+
+			// Get rid of the old token.
+			unset( $tokens[$token] );
+		}
 	}
 
-	$text = strtr( $text, $unescaped );  // Do first.
-	$text = strtr( $text, $escaped );
+	$text = $render( $tokens );
 
 	/**
 	 * Filters text that has had tokens replaced.
Index: src/bp-loader.php
===================================================================
--- src/bp-loader.php
+++ src/bp-loader.php
@@ -73,7 +73,10 @@
 	add_action( 'network_admin_notices', 'bp_php_requirements_notice' );
 	return;
 } else {
-	require dirname( __FILE__ ) . '/class-buddypress.php';
+	if ( file_exists( dirname( __FILE__ ) . '/vendor/autoload.php' ) ) {
+		require_once dirname( __FILE__ ) . '/vendor/autoload.php';
+	}
+	require_once dirname( __FILE__ ) . '/class-buddypress.php';
 
 	/*
 	 * Hook BuddyPress early onto the 'plugins_loaded' action.
Index: src/bp-templates/bp-legacy/buddypress/assets/emails/single-bp-email.php
===================================================================
--- src/bp-templates/bp-legacy/buddypress/assets/emails/single-bp-email.php
+++ src/bp-templates/bp-legacy/buddypress/assets/emails/single-bp-email.php
@@ -215,8 +215,11 @@
 						?>
 
 						<span class="footer_text"><?php echo nl2br( stripslashes( $settings['footer_text'] ) ); ?></span>
-						<br><br>
-						<a href="{{{unsubscribe}}}" style="text-decoration: underline;"><?php _ex( 'unsubscribe', 'email', 'buddypress' ); ?></a>
+
+						{{#unsubscribe}}
+							<br><br>
+							<a href="{{{.}}}" style="text-decoration: underline;"><?php _ex( 'unsubscribe', 'email', 'buddypress' ); ?></a>
+						{{/unsubscribe}}
 
 						<?php
 						/**
Index: src/bp-templates/bp-nouveau/buddypress/assets/emails/single-bp-email.php
===================================================================
--- src/bp-templates/bp-nouveau/buddypress/assets/emails/single-bp-email.php
+++ src/bp-templates/bp-nouveau/buddypress/assets/emails/single-bp-email.php
@@ -215,8 +215,11 @@
 						?>
 
 						<span class="footer_text"><?php echo nl2br( stripslashes( $settings['footer_text'] ) ); ?></span>
-						<br><br>
-						<a href="{{{unsubscribe}}}" style="text-decoration: underline;"><?php _ex( 'unsubscribe', 'email', 'buddypress' ); ?></a>
+
+						{{#unsubscribe}}
+							<br><br>
+							<a href="{{{.}}}" style="text-decoration: underline;"><?php _ex( 'unsubscribe', 'email', 'buddypress' ); ?></a>
+						{{/unsubscribe}}
 
 						<?php
 						/**
Index: tests/phpunit/testcases/core/class-bp-email.php
===================================================================
--- tests/phpunit/testcases/core/class-bp-email.php
+++ tests/phpunit/testcases/core/class-bp-email.php
@@ -293,7 +293,7 @@
 		$email->set_subject( '{{poster.name}}' )->set_tokens( array( 'poster.name' => $token ) );
 
 		// Subject always has to have tokens replaced before sending.
-		$this->assertSame( $name, $email->get_subject( 'replace-tokens' ) );
+		$this->assertSame( $token, $email->get_subject( 'replace-tokens' ) );
 	}
 
 }
Index: tests/phpunit/testcases/core/functions/bpCoreReplaceTokensInText.php
new file mode 100644
===================================================================
--- /dev/null
+++ tests/phpunit/testcases/core/functions/bpCoreReplaceTokensInText.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @group core
+ * @group functions
+ * @group bp_core_replace_tokens_in_text
+ */
+class BP_Tests_Core_Functions_BPCoreReplaceTokensInText extends BP_UnitTestCase {
+	public function test_bp_core_replace_tokens_in_text_sections() {
+		$text = '{{#person}}Show!{{/person}}';
+
+		$parsed = bp_core_replace_tokens_in_text( $text, array() );
+		$this->assertEmpty( $parsed );
+
+		$parsed = bp_core_replace_tokens_in_text( $text, array( 'person' => 'I exist' ) );
+		$this->assertEquals( 'Show!', $parsed );
+	}
+
+	public function test_bp_core_replace_tokens_in_text_section_unescaped() {
+		$text = '{{#person}}{{{.}}}{{/person}}';
+
+		$parsed = bp_core_replace_tokens_in_text( $text, array( 'person' => '<b>I am awesome</b>' ) );
+		$this->assertEquals( '<b>I am awesome</b>', $parsed );
+	}
+
+	public function test_bp_core_replace_tokens_in_text_section_escaped() {
+		$text = '{{#person}}{{.}}{{/person}}';
+
+		$parsed = bp_core_replace_tokens_in_text( $text, array( 'person' => '<b>I am awesome</b>' ) );
+		$this->assertEquals( '&lt;b&gt;I am awesome&lt;/b&gt;', $parsed );
+	}
+}
