core-bundle/src/Resources/contao/dca/tl_templates.php
public function addNewTemplate()
	{
		$arrAllTemplates = array();

		/** @var SplFileInfo[] $files */
		$files = Contao\System::getContainer()->get('contao.resource_finder')->findIn('templates')->files()->name('/\.html5$/');

		foreach ($files as $file)
		{
			$strRelpath = Contao\StringUtil::stripRootDir($file->getPathname());
			$strModule = preg_replace('@^(vendor/([^/]+/[^/]+)/|system/modules/([^/]+)/).*$@', '$2$3', strtr($strRelpath, '\\', '/'));
			$arrAllTemplates[$strModule][$strRelpath] = basename($strRelpath);
		}

		$strError = '';

		// Copy an existing template
		if (Contao\Input::post('FORM_SUBMIT') == 'tl_create_template')
		{
			$strOriginal = Contao\Input::post('original', true);

			if (Contao\Validator::isInsecurePath($strOriginal))
			{
				throw new RuntimeException('Invalid path ' . $strOriginal);
			}

			$strTarget = Contao\Input::post('target', true);

			if (Contao\Validator::isInsecurePath($strTarget))
			{
				throw new RuntimeException('Invalid path ' . $strTarget);
			}

			$rootDir = Contao\System::getContainer()->getParameter('kernel.project_dir');

			// Validate the target path
			if (strncmp($strTarget, 'templates', 9) !== 0 || !is_dir($rootDir . '/' . $strTarget))
			{
				$strError = sprintf($GLOBALS['TL_LANG']['tl_templates']['invalid'], $strTarget);
			}
			else
			{
				$blnFound = false;

				// Validate the source path
				foreach ($arrAllTemplates as $arrTemplates)
				{
					if (isset($arrTemplates[$strOriginal]))
					{
						$blnFound = true;
						break;
					}
				}

				if (!$blnFound)
				{
					$strError = sprintf($GLOBALS['TL_LANG']['tl_templates']['invalid'], $strOriginal);
				}
				else
				{
					$strTarget .= '/' . basename($strOriginal);

					// Check whether the target file exists
					if (file_exists($rootDir . '/' . $strTarget))
					{
						$strError = sprintf($GLOBALS['TL_LANG']['tl_templates']['exists'], $strTarget);
					}
					else
					{
						$this->import('Contao\Files', 'Files');
						$this->Files->copy($strOriginal, $strTarget);
						$this->redirect($this->getReferer());
					}
				}
			}
		}

		$strAllTemplates = '';

		// Group the templates by module
		foreach ($arrAllTemplates as $k=>$v)
		{
			$strAllTemplates .= '<optgroup label="' . $k . '">';

			foreach ($v as $kk=>$vv)
			{
				$strAllTemplates .= sprintf('<option value="%s"%s>%s</option>', $kk, ((Contao\Input::post('original') == $kk) ? ' selected="selected"' : ''), $vv);
			}

			$strAllTemplates .= '</optgroup>';
		}

		// Show form
		return ($strError ? '
<div class="tl_message">
<p class="tl_error">'.$strError.'</p>
</div>' : '').'

<div id="tl_buttons">
<a href="'.$this->getReferer(true).'" class="header_back" title="'.Contao\StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['backBTTitle']).'" accesskey="b" onclick="Backend.getScrollOffset()">'.$GLOBALS['TL_LANG']['MSC']['backBT'].'</a>
</div>

<form action="'.ampersand(Contao\Environment::get('request')).'" id="tl_create_template" class="tl_form tl_edit_form" method="post">
<div class="tl_formbody_edit">
<input type="hidden" name="FORM_SUBMIT" value="tl_create_template">
<input type="hidden" name="REQUEST_TOKEN" value="'.REQUEST_TOKEN.'">
<div class="tl_tbox cf">
<div class="w50 widget">
  <h3><label for="ctrl_original">'.$GLOBALS['TL_LANG']['tl_templates']['original'][0].'</label></h3>
  <select name="original" id="ctrl_original" class="tl_select tl_chosen" onfocus="Backend.getScrollOffset()">'.$strAllTemplates.'</select>'.(($GLOBALS['TL_LANG']['tl_templates']['original'][1] && Contao\Config::get('showHelp')) ? '
  <p class="tl_help tl_tip">'.$GLOBALS['TL_LANG']['tl_templates']['original'][1].'</p>' : '').'
</div>
<div class="w50 widget">
  <h3><label for="ctrl_target">'.$GLOBALS['TL_LANG']['tl_templates']['target'][0].'</label></h3>
  <select name="target" id="ctrl_target" class="tl_select" onfocus="Backend.getScrollOffset()"><option value="templates">templates</option>'. $this->getTargetFolders('templates') .'</select>'.(($GLOBALS['TL_LANG']['tl_templates']['target'][1] && Contao\Config::get('showHelp')) ? '
  <p class="tl_help tl_tip">'.$GLOBALS['TL_LANG']['tl_templates']['target'][1].'</p>' : '').'
</div>
</div>
</div>

<div class="tl_formbody_submit">
<div class="tl_submit_container">
  <button type="submit" name="create" id="create" class="tl_submit" accesskey="s">'.$GLOBALS['TL_LANG']['tl_templates']['newTpl'].'</button>
</div>
</div>
</form>';
	}
public function addNewTemplate()
	{
		$arrAllTemplates = array();

		/** @var SplFileInfo[] $files */
		$files = Contao\System::getContainer()->get('contao.resource_finder')->findIn('templates')->files()->name('/\.html5$/');

		foreach ($files as $file)
		{
			$strRelpath = Contao\StringUtil::stripRootDir($file->getPathname());
			$strModule = preg_replace('@^(vendor/([^/]+/[^/]+)/|system/modules/([^/]+)/).*$@', '$2$3', strtr($strRelpath, '\\', '/'));
			$arrAllTemplates[$strModule][$strRelpath] = basename($strRelpath);
		}

		$strError = '';

		// Copy an existing template
		if (Contao\Input::post('FORM_SUBMIT') == 'tl_create_template')
		{
			$strOriginal = Contao\Input::post('original', true);

			if (Contao\Validator::isInsecurePath($strOriginal))
			{
				throw new RuntimeException('Invalid path ' . $strOriginal);
			}

			$strTarget = Contao\Input::post('target', true);

			if (Contao\Validator::isInsecurePath($strTarget))
			{
				throw new RuntimeException('Invalid path ' . $strTarget);
			}

			$rootDir = Contao\System::getContainer()->getParameter('kernel.project_dir');

			// Validate the target path
			if (strncmp($strTarget, 'templates', 9) !== 0 || !is_dir($rootDir . '/' . $strTarget))
			{
				$strError = sprintf($GLOBALS['TL_LANG']['tl_templates']['invalid'], $strTarget);
			}
			else
			{
				$blnFound = false;

				// Validate the source path
				foreach ($arrAllTemplates as $arrTemplates)
				{
					if (isset($arrTemplates[$strOriginal]))
					{
						$blnFound = true;
						break;
					}
				}

				if (!$blnFound)
				{
					$strError = sprintf($GLOBALS['TL_LANG']['tl_templates']['invalid'], $strOriginal);
				}
				else
				{
					$strTarget .= '/' . basename($strOriginal);

					// Check whether the target file exists
					if (file_exists($rootDir . '/' . $strTarget))
					{
						$strError = sprintf($GLOBALS['TL_LANG']['tl_templates']['exists'], $strTarget);
					}
					else
					{
						$this->import('Contao\Files', 'Files');
						$this->Files->copy($strOriginal, $strTarget);
						$this->redirect($this->getReferer());
					}
				}
			}
		}

		$strAllTemplates = '';

		// Group the templates by module
		foreach ($arrAllTemplates as $k=>$v)
		{
			$strAllTemplates .= '<optgroup label="' . $k . '">';

			foreach ($v as $kk=>$vv)
			{
				$strAllTemplates .= sprintf('<option value="%s"%s>%s</option>', $kk, ((Contao\Input::post('original') == $kk) ? ' selected="selected"' : ''), $vv);
			}

			$strAllTemplates .= '</optgroup>';
		}

		// Show form
		return ($strError ? '
<div class="tl_message">
<p class="tl_error">'.$strError.'</p>
</div>' : '').'

<div id="tl_buttons">
<a href="'.$this->getReferer(true).'" class="header_back" title="'.Contao\StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['backBTTitle']).'" accesskey="b" onclick="Backend.getScrollOffset()">'.$GLOBALS['TL_LANG']['MSC']['backBT'].'</a>
</div>

<form action="'.ampersand(Contao\Environment::get('request')).'" id="tl_create_template" class="tl_form tl_edit_form" method="post">
<div class="tl_formbody_edit">
<input type="hidden" name="FORM_SUBMIT" value="tl_create_template">
<input type="hidden" name="REQUEST_TOKEN" value="'.REQUEST_TOKEN.'">
<div class="tl_tbox cf">
<div class="w50 widget">
  <h3><label for="ctrl_original">'.$GLOBALS['TL_LANG']['tl_templates']['original'][0].'</label></h3>
  <select name="original" id="ctrl_original" class="tl_select tl_chosen" onfocus="Backend.getScrollOffset()">'.$strAllTemplates.'</select>'.(($GLOBALS['TL_LANG']['tl_templates']['original'][1] && Contao\Config::get('showHelp')) ? '
  <p class="tl_help tl_tip">'.$GLOBALS['TL_LANG']['tl_templates']['original'][1].'</p>' : '').'
</div>
<div class="w50 widget">
  <h3><label for="ctrl_target">'.$GLOBALS['TL_LANG']['tl_templates']['target'][0].'</label></h3>
  <select name="target" id="ctrl_target" class="tl_select" onfocus="Backend.getScrollOffset()"><option value="templates">templates</option>'. $this->getTargetFolders('templates') .'</select>'.(($GLOBALS['TL_LANG']['tl_templates']['target'][1] && Contao\Config::get('showHelp')) ? '
  <p class="tl_help tl_tip">'.$GLOBALS['TL_LANG']['tl_templates']['target'][1].'</p>' : '').'
</div>
</div>
</div>

<div class="tl_formbody_submit">
<div class="tl_submit_container">
  <button type="submit" name="create" id="create" class="tl_submit" accesskey="s">'.$GLOBALS['TL_LANG']['tl_templates']['newTpl'].'</button>
</div>
</div>
</form>';
	}
Create a new template

@return string