Skip to content

Commit

Permalink
API Refactor template layer into its own module
Browse files Browse the repository at this point in the history
Includes the following large-scale changes:
- Impoved barrier between model and view layers
- Improved casting of scalar to relevant DBField types
- Improved capabilities for rendering arbitrary data in templates
  • Loading branch information
GuySartorelli committed Oct 14, 2024
1 parent 6bb9a0b commit 56db608
Show file tree
Hide file tree
Showing 186 changed files with 6,217 additions and 4,643 deletions.
6 changes: 6 additions & 0 deletions _config/view.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
Name: view-config
---
SilverStripe\Core\Injector\Injector:
SilverStripe\View\TemplateEngine:
class: 'SilverStripe\View\SSTemplateEngine'
2 changes: 1 addition & 1 deletion src/Control/ContentNegotiator.php
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ public function html(HTTPResponse $response)
// Fix base tag
$content = preg_replace(
'/<base href="([^"]*)" \/>/',
'<base href="$1"><!--[if lte IE 6]></base><![endif]-->',
'<base href="$1">',
$content ?? ''
);

Expand Down
36 changes: 29 additions & 7 deletions src/Control/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
namespace SilverStripe\Control;

use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\Debug;
use SilverStripe\Model\ModelData;
use SilverStripe\ORM\FieldType\DBHTMLText;
use SilverStripe\Security\Member;
use SilverStripe\Security\Security;
use SilverStripe\View\SSViewer;
use SilverStripe\View\TemplateEngine;
use SilverStripe\View\TemplateGlobalProvider;

/**
Expand Down Expand Up @@ -88,6 +91,8 @@ class Controller extends RequestHandler implements TemplateGlobalProvider
'handleIndex',
];

protected ?TemplateEngine $templateEngine = null;

public function __construct()
{
parent::__construct();
Expand Down Expand Up @@ -401,7 +406,7 @@ public function getViewer($action)
$templates = array_unique(array_merge($actionTemplates, $classTemplates));
}

return SSViewer::create($templates);
return SSViewer::create($templates, $this->getTemplateEngine());
}

/**
Expand Down Expand Up @@ -453,9 +458,10 @@ protected function definingClassForAction($action)
}

$class = static::class;
while ($class != 'SilverStripe\\Control\\RequestHandler') {
$engine = $this->getTemplateEngine();
while ($class !== RequestHandler::class) {
$templateName = strtok($class ?? '', '_') . '_' . $action;
if (SSViewer::hasTemplate($templateName)) {
if ($engine->hasTemplate($templateName)) {
return $class;
}

Expand Down Expand Up @@ -487,17 +493,25 @@ public function hasActionTemplate($action)
$parentClass = get_parent_class($parentClass ?? '');
}

return SSViewer::hasTemplate($templates);
$engine = $this->getTemplateEngine();
return $engine->hasTemplate($templates);
}

public function renderWith($template, ModelData|array $customFields = []): DBHTMLText
{
// Ensure template engine is used, unless the viewer was already explicitly instantiated
if (!($template instanceof SSViewer)) {
$template = SSViewer::create($template, $this->getTemplateEngine());
}
return parent::renderWith($template, $customFields);
}

/**
* Render the current controller with the templates determined by {@link getViewer()}.
*
* @param array $params
*
* @return string
*/
public function render($params = null)
public function render($params = null): DBHTMLText
{
$template = $this->getViewer($this->getAction());

Expand Down Expand Up @@ -737,4 +751,12 @@ public static function get_template_global_variables()
'CurrentPage' => 'curr',
];
}

protected function getTemplateEngine(): TemplateEngine
{
if (!$this->templateEngine) {
$this->templateEngine = Injector::inst()->create(TemplateEngine::class);
}
return $this->templateEngine;
}
}
7 changes: 2 additions & 5 deletions src/Control/Email/Email.php
Original file line number Diff line number Diff line change
Expand Up @@ -398,16 +398,13 @@ public function removeData(string $name)
return $this;
}

public function getHTMLTemplate(): string
public function getHTMLTemplate(): string|array
{
if ($this->HTMLTemplate) {
return $this->HTMLTemplate;
}

return ThemeResourceLoader::inst()->findTemplate(
SSViewer::get_templates_by_class(static::class, '', Email::class),
SSViewer::get_themes()
);
return SSViewer::get_templates_by_class(static::class, '', Email::class);
}

/**
Expand Down
7 changes: 3 additions & 4 deletions src/Control/HTTPResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\View\Requirements;
use Stringable;

/**
* Represents a response returned by a controller.
*/
class HTTPResponse
class HTTPResponse implements Stringable
{
use Injectable;

Expand Down Expand Up @@ -444,10 +445,8 @@ public function isRedirect()

/**
* The HTTP response represented as a raw string
*
* @return string
*/
public function __toString()
public function __toString(): string
{
$headers = [];
foreach ($this->getHeaders() as $header => $values) {
Expand Down
10 changes: 5 additions & 5 deletions src/Control/RSS/RSSFeed_Entry.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class RSSFeed_Entry extends ModelData
*/
public function __construct($entry, $titleField, $descriptionField, $authorField)
{
$this->failover = $entry;
$this->setFailover($entry);
$this->titleField = $titleField;
$this->descriptionField = $descriptionField;
$this->authorField = $authorField;
Expand All @@ -58,7 +58,7 @@ public function __construct($entry, $titleField, $descriptionField, $authorField
/**
* Get the description of this entry
*
* @return DBField Returns the description of the entry.
* @return DBField|null Returns the description of the entry.
*/
public function Title()
{
Expand All @@ -68,7 +68,7 @@ public function Title()
/**
* Get the description of this entry
*
* @return DBField Returns the description of the entry.
* @return DBField|null Returns the description of the entry.
*/
public function Description()
{
Expand All @@ -85,7 +85,7 @@ public function Description()
/**
* Get the author of this entry
*
* @return DBField Returns the author of the entry.
* @return DBField|null Returns the author of the entry.
*/
public function Author()
{
Expand All @@ -96,7 +96,7 @@ public function Author()
* Return the safely casted field
*
* @param string $fieldName Name of field
* @return DBField
* @return DBField|null
*/
public function rssField($fieldName)
{
Expand Down
7 changes: 3 additions & 4 deletions src/Core/Manifest/ModuleResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
use InvalidArgumentException;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Path;
use Stringable;

/**
* This object represents a single resource file attached to a module, and can be used
* as a reference to this to be later turned into either a URL or file path.
*/
class ModuleResource
class ModuleResource implements Stringable
{
/**
* @var Module
Expand Down Expand Up @@ -114,10 +115,8 @@ public function exists()

/**
* Get relative path
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return $this->getRelativePath();
}
Expand Down
6 changes: 3 additions & 3 deletions src/Dev/Backtrace.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,11 @@ public static function full_func_name($item, $showArgs = false, $argCharLimit =
if ($showArgs && isset($item['args'])) {
$args = [];
foreach ($item['args'] as $arg) {
if (!is_object($arg) || method_exists($arg, '__toString')) {
if (is_object($arg)) {
$args[] = get_class($arg);
} else {
$sarg = is_array($arg) ? 'Array' : strval($arg);
$args[] = (strlen($sarg ?? '') > $argCharLimit) ? substr($sarg, 0, $argCharLimit) . '...' : $sarg;
} else {
$args[] = get_class($arg);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Forms/DropdownField.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
* DropdownField::create(
* 'Country',
* 'Country',
* singleton(MyObject::class)->dbObject('Country')->enumValues()
* singleton(MyObject::class)->dbObject('Country')?->enumValues()
* );
* </code>
*
Expand Down
2 changes: 1 addition & 1 deletion src/Forms/FieldGroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ public function getMessage()
/** @var FormField $subfield */
$messages = [];
foreach ($dataFields as $subfield) {
$message = $subfield->obj('Message')->forTemplate();
$message = $subfield->obj('Message')?->forTemplate();
if ($message) {
$messages[] = rtrim($message ?? '', ".");
}
Expand Down
4 changes: 2 additions & 2 deletions src/Forms/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -899,10 +899,10 @@ public function setLegend($legend)
}

/**
* Set the SS template that this form should use
* Set the template or template candidates that this form should use
* to render with. The default is "Form".
*
* @param string|array $template The name of the template (without the .ss extension) or array form
* @param string|array $template The name of the template (without the file extension) or array of candidates
* @return $this
*/
public function setTemplate($template)
Expand Down
19 changes: 12 additions & 7 deletions src/Forms/FormField.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use SilverStripe\View\AttributesHTML;
use SilverStripe\View\SSViewer;
use SilverStripe\Model\ModelData;
use SilverStripe\ORM\DataObject;

/**
* Represents a field in a form.
Expand Down Expand Up @@ -273,6 +274,8 @@ class FormField extends RequestHandler
'Title' => 'Text',
'RightTitle' => 'Text',
'Description' => 'HTMLFragment',
// This is an associative arrays, but we cast to Text so we can get a JSON string representation
'SchemaData' => 'Text',
];

/**
Expand Down Expand Up @@ -458,7 +461,7 @@ public function Value()
*
* By default, makes use of $this->dataValue()
*
* @param ModelData|DataObjectInterface $record Record to save data into
* @param DataObjectInterface $record Record to save data into
*/
public function saveInto(DataObjectInterface $record)
{
Expand All @@ -469,7 +472,9 @@ public function saveInto(DataObjectInterface $record)
if (($pos = strrpos($this->name ?? '', '.')) !== false) {
$relation = substr($this->name ?? '', 0, $pos);
$fieldName = substr($this->name ?? '', $pos + 1);
$component = $record->relObject($relation);
if ($record instanceof DataObject) {
$component = $record->relObject($relation);
}
}

if ($fieldName && $component) {
Expand Down Expand Up @@ -1027,7 +1032,7 @@ public function getSmallFieldHolderTemplates()
*/
protected function _templates($customTemplate = null, $customTemplateSuffix = null)
{
$templates = SSViewer::get_templates_by_class(static::class, $customTemplateSuffix, __CLASS__);
$templates = SSViewer::get_templates_by_class(static::class, $customTemplateSuffix ?? '', __CLASS__);
// Prefer any custom template
if ($customTemplate) {
// Prioritise direct template
Expand Down Expand Up @@ -1469,12 +1474,12 @@ public function getSchemaDataDefaults()
'schemaType' => $this->getSchemaDataType(),
'component' => $this->getSchemaComponent(),
'holderId' => $this->HolderID(),
'title' => $this->obj('Title')->getSchemaValue(),
'title' => $this->obj('Title')?->getSchemaValue(),
'source' => null,
'extraClass' => $this->extraClass(),
'description' => $this->obj('Description')->getSchemaValue(),
'rightTitle' => $this->obj('RightTitle')->getSchemaValue(),
'leftTitle' => $this->obj('LeftTitle')->getSchemaValue(),
'description' => $this->obj('Description')?->getSchemaValue(),
'rightTitle' => $this->obj('RightTitle')?->getSchemaValue(),
'leftTitle' => $this->obj('LeftTitle')?->getSchemaValue(),
'readOnly' => $this->isReadonly(),
'disabled' => $this->isDisabled(),
'customValidationMessage' => $this->getCustomValidationMessage(),
Expand Down
4 changes: 2 additions & 2 deletions src/Forms/FormScaffolder.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public function getFieldList()
$fieldObject = $this
->obj
->dbObject($fieldName)
->scaffoldFormField(null, $this->getParamsArray());
?->scaffoldFormField(null, $this->getParamsArray());
}
// Allow fields to opt-out of scaffolding
if (!$fieldObject) {
Expand Down Expand Up @@ -145,7 +145,7 @@ public function getFieldList()
$fieldClass = $this->fieldClasses[$fieldName];
$hasOneField = new $fieldClass($fieldName);
} else {
$hasOneField = $this->obj->dbObject($fieldName)->scaffoldFormField(null, $this->getParamsArray());
$hasOneField = $this->obj->dbObject($fieldName)?->scaffoldFormField(null, $this->getParamsArray());
}
if (empty($hasOneField)) {
continue; // Allow fields to opt out of scaffolding
Expand Down
10 changes: 8 additions & 2 deletions src/Forms/GridField/GridFieldAddExistingAutocompleter.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
use SilverStripe\View\SSViewer;
use LogicException;
use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\View\TemplateEngine;
use SilverStripe\View\ViewLayerData;

/**
* This class is is responsible for adding objects to another object's has_many
Expand Down Expand Up @@ -283,12 +286,15 @@ public function doSearch($gridField, $request)
$json = [];
Config::nest();
SSViewer::config()->set('source_file_comments', false);
$viewer = SSViewer::fromString($this->resultsFormat);

$engine = Injector::inst()->create(TemplateEngine::class);
foreach ($results as $result) {
if (!$result->canView()) {
continue;
}
$title = Convert::html2raw($viewer->process($result));
$title = Convert::html2raw(
$engine->renderString($this->resultsFormat, ViewLayerData::create($result), cache: false)
);
$json[] = [
'label' => $title,
'value' => $title,
Expand Down
Loading

0 comments on commit 56db608

Please sign in to comment.