mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #5943 from open-sausages/pulls/4.0/template-lookup-redux
API Update template lookup to late resolution for performance reasons
This commit is contained in:
commit
3b71b7731a
@ -15,6 +15,7 @@ use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\Queries\SQLSelect;
|
||||
use SilverStripe\ORM\ManyManyList;
|
||||
use SilverStripe\MSSQL\MSSQLDatabase;
|
||||
use SSViewer;
|
||||
use TemplateGlobalProvider;
|
||||
use Deprecation;
|
||||
use i18n;
|
||||
@ -1541,8 +1542,10 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
||||
$dateFormatMap
|
||||
)
|
||||
);
|
||||
$formatClass = get_class($dateFormatField);
|
||||
$dateFormatField->setValue($self->DateFormat);
|
||||
$dateFormatField->setDescriptionTemplate('forms/MemberDatetimeOptionsetField_description_date');
|
||||
$dateTemplate = SSViewer::get_templates_by_class($formatClass, '_description_date', $formatClass);
|
||||
$dateFormatField->setDescriptionTemplate($dateTemplate);
|
||||
|
||||
$defaultTimeFormat = Zend_Locale_Format::getTimeFormat(new Zend_Locale($self->Locale));
|
||||
$timeFormatMap = array(
|
||||
@ -1559,7 +1562,8 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
||||
)
|
||||
);
|
||||
$timeFormatField->setValue($self->TimeFormat);
|
||||
$timeFormatField->setDescriptionTemplate('forms/MemberDatetimeOptionsetField_description_time');
|
||||
$timeTemplate = SSViewer::get_templates_by_class($formatClass,'_description_time', $formatClass);
|
||||
$timeFormatField->setDescriptionTemplate($timeTemplate);
|
||||
});
|
||||
|
||||
return parent::getCMSFields();
|
||||
|
@ -972,7 +972,8 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
* @return array
|
||||
*/
|
||||
public function getTemplatesWithSuffix($suffix) {
|
||||
return SSViewer::get_templates_by_class(get_class($this), $suffix, 'SilverStripe\\Admin\\LeftAndMain');
|
||||
$templates = SSViewer::get_templates_by_class(get_class($this), $suffix, __CLASS__);
|
||||
return SSViewer::chooseTemplate($templates);
|
||||
}
|
||||
|
||||
public function Content() {
|
||||
|
@ -165,7 +165,7 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
|
||||
// Add import capabilities. Limit to admin since the import logic can affect assigned permissions
|
||||
if(Permission::check('ADMIN')) {
|
||||
$fields->addFieldsToTab('Root.Users', array(
|
||||
new HeaderField(_t('SecurityAdmin.IMPORTUSERS', 'Import users'), 3),
|
||||
new HeaderField('ImportUsersHeader', _t('SecurityAdmin.IMPORTUSERS', 'Import users'), 3),
|
||||
new LiteralField(
|
||||
'MemberImportFormIframe',
|
||||
sprintf(
|
||||
@ -176,7 +176,7 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
|
||||
)
|
||||
));
|
||||
$fields->addFieldsToTab('Root.Groups', array(
|
||||
new HeaderField(_t('SecurityAdmin.IMPORTGROUPS', 'Import groups'), 3),
|
||||
new HeaderField('ImportGroupsHeader', _t('SecurityAdmin.IMPORTGROUPS', 'Import groups'), 3),
|
||||
new LiteralField(
|
||||
'GroupImportFormIframe',
|
||||
sprintf(
|
||||
|
@ -1,12 +1,10 @@
|
||||
<?php
|
||||
|
||||
|
||||
use SilverStripe\ORM\SS_List;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\FieldType\DBField;
|
||||
use SilverStripe\ORM\FieldType\DBHTMLText;
|
||||
|
||||
|
||||
/**
|
||||
* RSSFeed class
|
||||
*
|
||||
@ -91,9 +89,11 @@ class RSSFeed extends ViewableData {
|
||||
protected $etag;
|
||||
|
||||
/**
|
||||
* Custom template
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $template = 'RSSFeed';
|
||||
protected $template = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -219,7 +219,7 @@ class RSSFeed extends ViewableData {
|
||||
|
||||
Config::inst()->update('SSViewer', 'source_file_comments', $prevState);
|
||||
|
||||
return $this->renderWith($this->getTemplate());
|
||||
return $this->renderWith($this->getTemplates());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -240,6 +240,21 @@ class RSSFeed extends ViewableData {
|
||||
public function getTemplate() {
|
||||
return $this->template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ordered list of preferred templates for rendering this object.
|
||||
* Will prioritise any custom template first, and then templates based on class hiearchy next.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTemplates() {
|
||||
$templates = SSViewer::get_templates_by_class(get_class($this), '', __CLASS__);
|
||||
// Prefer any custom template
|
||||
if($this->getTemplate()) {
|
||||
array_unshift($templates, $this->getTemplate());
|
||||
}
|
||||
return $templates;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,15 +87,17 @@ class ClassInfo {
|
||||
*
|
||||
* @todo Move this into {@see DataObjectSchema}
|
||||
*
|
||||
* @param string|object $class
|
||||
* @param string|object $nameOrObject Class or object instance
|
||||
* @return array
|
||||
*/
|
||||
public static function dataClassesFor($class) {
|
||||
if(is_string($class) && !class_exists($class)) return array();
|
||||
public static function dataClassesFor($nameOrObject) {
|
||||
if(is_string($nameOrObject) && !class_exists($nameOrObject)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$result = array();
|
||||
|
||||
$class = self::class_name($class);
|
||||
$class = self::class_name($nameOrObject);
|
||||
|
||||
$classes = array_merge(
|
||||
self::ancestry($class),
|
||||
@ -134,17 +136,17 @@ class ClassInfo {
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $class string of the classname or instance of the class
|
||||
* @param string|object $nameOrObject The classname or object
|
||||
* @return array Names of all subclasses as an associative array.
|
||||
*/
|
||||
public static function subclassesFor($class) {
|
||||
if(is_string($class) && !class_exists($class)) {
|
||||
public static function subclassesFor($nameOrObject) {
|
||||
if(is_string($nameOrObject) && !class_exists($nameOrObject)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
//normalise class case
|
||||
$className = self::class_name($class);
|
||||
$descendants = SS_ClassLoader::instance()->getManifest()->getDescendantsOf($class);
|
||||
$className = self::class_name($nameOrObject);
|
||||
$descendants = SS_ClassLoader::instance()->getManifest()->getDescendantsOf($className);
|
||||
$result = array($className => $className);
|
||||
|
||||
if ($descendants) {
|
||||
@ -174,14 +176,16 @@ class ClassInfo {
|
||||
* Returns the passed class name along with all its parent class names in an
|
||||
* array, sorted with the root class first.
|
||||
*
|
||||
* @param string $class
|
||||
* @param bool $tablesOnly Only return classes that have a table in the db.
|
||||
* @param string|object $nameOrObject Class or object instance
|
||||
* @param bool $tablesOnly Only return classes that have a table in the db.
|
||||
* @return array
|
||||
*/
|
||||
public static function ancestry($class, $tablesOnly = false) {
|
||||
if(is_string($class) && !class_exists($class)) return array();
|
||||
public static function ancestry($nameOrObject, $tablesOnly = false) {
|
||||
if(is_string($nameOrObject) && !class_exists($nameOrObject)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$class = self::class_name($class);
|
||||
$class = self::class_name($nameOrObject);
|
||||
|
||||
$lClass = strtolower($class);
|
||||
|
||||
|
@ -201,11 +201,13 @@ You can use inequalities like `<`, `<=`, `>`, `>=` to compare numbers.
|
||||
## Includes
|
||||
|
||||
Within SilverStripe templates we have the ability to include other templates using the `<% include %>` tag. The includes
|
||||
will be searched for using the same filename look-up rules as a regular template, so this will include
|
||||
`templates/Includes/Sidebar.ss`
|
||||
will be searched for using the same filename look-up rules as a regular template. However in the case of the include tag
|
||||
an additional `Includes` directory will be inserted into the resolved path just prior to the filename.
|
||||
|
||||
:::ss
|
||||
<% include Includes\SideBar %>
|
||||
E.g.
|
||||
|
||||
* `<% include SideBar %>` will include `templates/Includes/Sidebar.ss`
|
||||
* `<% include MyNamespace/SideBar %>` will include `templates/MyNamespace/Includes/Sidebar.ss`
|
||||
|
||||
Note that in SilverStripe 3, you didn't have to specify a namespace in your `include` tag, as the template engine didn't
|
||||
use namespaces. As of SilverStripe 4, the template namespaces need to match the folder structure of your template files.
|
||||
|
@ -63,6 +63,7 @@
|
||||
* Search filter classes (e.g. `ExactMatchFilter`) are now registered with `Injector`
|
||||
via a new `DataListFilter.` prefix convention.
|
||||
see [search filter documentation](/developer_guides/model/searchfilters) for more information.
|
||||
* FormField templates no longer look in the 'forms' folder for templates.
|
||||
|
||||
## New API
|
||||
|
||||
@ -126,6 +127,9 @@
|
||||
* `PopoverField` added to provide popup-menu behaviour in react forms (currently not available for
|
||||
non-react forms).
|
||||
* Admin URL can now be configured via custom Director routing rule
|
||||
* Templates now use a standard template lookup system via `SSViewer::get_templates_by_class`
|
||||
which builds a candidate list for a given class. Actual resolution of existing templates
|
||||
for any list of candidates is actually performed by `SSViewer::chooseTemplate`
|
||||
|
||||
### Front-end build tooling for CMS interface
|
||||
|
||||
@ -415,8 +419,14 @@ require manual intervention, please see the below upgrading notes.
|
||||
Templates are now much more strict about their locations. You can no longer put a template in an arbitrary
|
||||
folder and have it be found. Case is now also checked on case-sensitive filesystems.
|
||||
|
||||
Either include the folder in the template name (`renderWith('Field.ss')` => `renderWith('forms/Field.ss')`), move the
|
||||
template into the correct directory, or both.
|
||||
Either include the folder in the template name (`renderWith('MyEmail.ss')` => `renderWith('emails/MyEmail.ss')`),
|
||||
move the template into the correct directory, or both.
|
||||
|
||||
When using `<% include %>` template tag you should continue to leave out the `Includes` folder.
|
||||
`<% include Sidebar %>` will match only match `Includes/Sidebar.ss`, not `Sidebar.ss`.
|
||||
Please refer to our [template syntax](/developer_guides/templates/syntax) for details.
|
||||
|
||||
The `forms` template folder is no longer used to lookup templates for `FormField` instances.
|
||||
|
||||
### Update code that uses SQLQuery
|
||||
|
||||
|
@ -479,9 +479,11 @@ class Email extends ViewableData {
|
||||
if($this->ss_template && !$isPlain) {
|
||||
// Requery data so that updated versions of To, From, Subject, etc are included
|
||||
$data = $this->templateData();
|
||||
|
||||
$template = new SSViewer('email/'.$this->ss_template);
|
||||
|
||||
$candidateTemplates = [
|
||||
$this->ss_template,
|
||||
[ 'type' => 'email', $this->ss_template ]
|
||||
];
|
||||
$template = new SSViewer($candidateTemplates);
|
||||
if($template->exists()) {
|
||||
$fullBody = $template->process($data);
|
||||
}
|
||||
|
@ -1025,14 +1025,27 @@ class Form extends RequestHandler {
|
||||
|
||||
/**
|
||||
* Return the template to render this form with.
|
||||
* If the template isn't set, then default to the
|
||||
* form class name e.g "Form".
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplate() {
|
||||
if($this->template) return $this->template;
|
||||
else return $this->class;
|
||||
return $this->template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returs the ordered list of preferred templates for rendering this form
|
||||
* If the template isn't set, then default to the
|
||||
* form class name e.g "Form".
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTemplates() {
|
||||
$templates = SSViewer::get_templates_by_class(get_class($this), '', __CLASS__);
|
||||
// Prefer any custom template
|
||||
if($this->getTemplate()) {
|
||||
array_unshift($templates, $this->getTemplate());
|
||||
}
|
||||
return $templates;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1639,10 +1652,7 @@ class Form extends RequestHandler {
|
||||
* @return DBHTMLText
|
||||
*/
|
||||
public function forTemplate() {
|
||||
$return = $this->renderWith(array_merge(
|
||||
(array)$this->getTemplate(),
|
||||
array('Includes/Form')
|
||||
));
|
||||
$return = $this->renderWith($this->getTemplates());
|
||||
|
||||
// Now that we're rendered, clear message
|
||||
$this->clearMessage();
|
||||
@ -1742,7 +1752,7 @@ class Form extends RequestHandler {
|
||||
|
||||
/**
|
||||
* Get a list of all actions, including those in the main "fields" FieldList
|
||||
*
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getAllActions() {
|
||||
|
@ -761,7 +761,6 @@ class FormField extends RequestHandler {
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param null|array|DataObject $data {@see Form::loadDataFrom}
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setValue($value) {
|
||||
@ -1048,22 +1047,14 @@ class FormField extends RequestHandler {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function _templates($customTemplate = null, $customTemplateSuffix = null) {
|
||||
$matches = array();
|
||||
|
||||
foreach(array_reverse(ClassInfo::ancestry($this)) as $className) {
|
||||
$matches[] = 'forms/'. $className . $customTemplateSuffix;
|
||||
|
||||
if($className == "FormField") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function _templates($customTemplate = null, $customTemplateSuffix = null) {
|
||||
$templates = SSViewer::get_templates_by_class(get_class($this), $customTemplateSuffix, __CLASS__);
|
||||
// Prefer any custom template
|
||||
if($customTemplate) {
|
||||
array_unshift($matches, 'forms/'.$customTemplate);
|
||||
// Prioritise direct template
|
||||
array_unshift($templates, $customTemplate);
|
||||
}
|
||||
|
||||
return $matches;
|
||||
return $templates;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,13 +25,6 @@ use SilverStripe\ORM\DataList;
|
||||
class GridFieldAddExistingAutocompleter
|
||||
implements GridField_HTMLProvider, GridField_ActionProvider, GridField_DataManipulator, GridField_URLHandler {
|
||||
|
||||
/**
|
||||
* Which template to use for rendering
|
||||
*
|
||||
* @var string $itemClass
|
||||
*/
|
||||
protected $itemClass = 'GridFieldAddExistingAutocompleter';
|
||||
|
||||
/**
|
||||
* The HTML fragment to write this component into
|
||||
*/
|
||||
@ -84,6 +77,7 @@ class GridFieldAddExistingAutocompleter
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $targetFragment
|
||||
* @param array $searchFields Which fields on the object in the list should be searched
|
||||
*/
|
||||
public function __construct($targetFragment = 'before', $searchFields = null) {
|
||||
@ -97,7 +91,7 @@ class GridFieldAddExistingAutocompleter
|
||||
* @return string[] - HTML
|
||||
*/
|
||||
public function getHTMLFragments($gridField) {
|
||||
$dataClass = $gridField->getList()->dataClass();
|
||||
$dataClass = $gridField->getModelClass();
|
||||
|
||||
$forTemplate = new ArrayData(array());
|
||||
$forTemplate->Fields = new FieldList();
|
||||
@ -130,8 +124,9 @@ class GridFieldAddExistingAutocompleter
|
||||
$forTemplate->Fields->setForm($form);
|
||||
}
|
||||
|
||||
$template = SSViewer::get_templates_by_class($this, '', __CLASS__);
|
||||
return array(
|
||||
$this->targetFragment => $forTemplate->renderWith('Includes/'.$this->itemClass)
|
||||
$this->targetFragment => $forTemplate->renderWith($template)
|
||||
);
|
||||
}
|
||||
|
||||
@ -174,7 +169,7 @@ class GridFieldAddExistingAutocompleter
|
||||
if(empty($objectID)) {
|
||||
return $dataList;
|
||||
}
|
||||
$object = DataObject::get_by_id($dataList->dataclass(), $objectID);
|
||||
$object = DataObject::get_by_id($gridField->getModelClass(), $objectID);
|
||||
if($object) {
|
||||
$dataList->add($object);
|
||||
}
|
||||
@ -198,9 +193,10 @@ class GridFieldAddExistingAutocompleter
|
||||
*
|
||||
* @param GridField $gridField
|
||||
* @param SS_HTTPRequest $request
|
||||
* @return string
|
||||
*/
|
||||
public function doSearch($gridField, $request) {
|
||||
$dataClass = $gridField->getList()->dataClass();
|
||||
$dataClass = $gridField->getModelClass();
|
||||
$allList = $this->searchList ? $this->searchList : DataList::create($dataClass);
|
||||
|
||||
$searchFields = ($this->getSearchFields())
|
||||
@ -269,6 +265,7 @@ class GridFieldAddExistingAutocompleter
|
||||
|
||||
/**
|
||||
* @param array $fields
|
||||
* @return $this
|
||||
*/
|
||||
public function setSearchFields($fields) {
|
||||
$this->searchFields = $fields;
|
||||
|
@ -43,8 +43,9 @@ class GridFieldAddNewButton implements GridField_HTMLProvider {
|
||||
'ButtonName' => $this->buttonName,
|
||||
));
|
||||
|
||||
$templates = SSViewer::get_templates_by_class($this, '', __CLASS__);
|
||||
return array(
|
||||
$this->targetFragment => $data->renderWith('Includes/GridFieldAddNewButton'),
|
||||
$this->targetFragment => $data->renderWith($templates),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,9 @@ class GridFieldButtonRow implements GridField_HTMLProvider {
|
||||
"RightFragment" => "\$DefineFragment(buttons-{$this->targetFragment}-right)",
|
||||
));
|
||||
|
||||
$templates = SSViewer::get_templates_by_class($this, '', __CLASS__);
|
||||
return array(
|
||||
$this->targetFragment => $data->renderWith('Includes/GridFieldButtonRow')
|
||||
$this->targetFragment => $data->renderWith($templates)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ class GridFieldDetailForm implements GridField_URLHandler {
|
||||
$class,
|
||||
array($gridField, $this, $record, $requestHandler, $this->name)
|
||||
);
|
||||
$handler->setTemplate($this->template);
|
||||
$handler->setTemplate($this->getTemplate());
|
||||
$this->extend('updateItemRequestHandler', $handler);
|
||||
return $handler;
|
||||
}
|
||||
@ -214,7 +214,7 @@ class GridFieldDetailForm implements GridField_URLHandler {
|
||||
} else if(ClassInfo::exists(get_class($this) . "_ItemRequest")) {
|
||||
return get_class($this) . "_ItemRequest";
|
||||
} else {
|
||||
return 'GridFieldDetailForm_ItemRequest';
|
||||
return __CLASS__ . '_ItemRequest';
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,7 +256,7 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
|
||||
|
||||
/**
|
||||
*
|
||||
* @var GridField_URLHandler
|
||||
* @var GridFieldDetailForm
|
||||
*/
|
||||
protected $component;
|
||||
|
||||
@ -283,7 +283,7 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
|
||||
/**
|
||||
* @var String
|
||||
*/
|
||||
protected $template = 'GridFieldItemEditView';
|
||||
protected $template = null;
|
||||
|
||||
private static $url_handlers = array(
|
||||
'$Action!' => '$Action',
|
||||
@ -293,7 +293,7 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
|
||||
/**
|
||||
*
|
||||
* @param GridFIeld $gridField
|
||||
* @param GridField_URLHandler $component
|
||||
* @param GridFieldDetailForm $component
|
||||
* @param DataObject $record
|
||||
* @param RequestHandler $requestHandler
|
||||
* @param string $popupFormName
|
||||
@ -319,14 +319,14 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
|
||||
|
||||
$controller = $this->getToplevelController();
|
||||
|
||||
$form = $this->ItemEditForm($this->gridField, $request);
|
||||
$form = $this->ItemEditForm();
|
||||
$form->makeReadonly();
|
||||
|
||||
$data = new ArrayData(array(
|
||||
'Backlink' => $controller->Link(),
|
||||
'ItemEditForm' => $form
|
||||
));
|
||||
$return = $data->renderWith($this->template);
|
||||
$return = $data->renderWith($this->getTemplates());
|
||||
|
||||
if($request->isAjax()) {
|
||||
return $return;
|
||||
@ -337,12 +337,12 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
|
||||
|
||||
public function edit($request) {
|
||||
$controller = $this->getToplevelController();
|
||||
$form = $this->ItemEditForm($this->gridField, $request);
|
||||
$form = $this->ItemEditForm();
|
||||
|
||||
$return = $this->customise(array(
|
||||
'Backlink' => $controller->hasMethod('Backlink') ? $controller->Backlink() : $controller->Link(),
|
||||
'ItemEditForm' => $form,
|
||||
))->renderWith($this->template);
|
||||
))->renderWith($this->getTemplates());
|
||||
|
||||
if($request->isAjax()) {
|
||||
return $return;
|
||||
@ -751,6 +751,21 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
|
||||
return $this->template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of templates to use
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTemplates()
|
||||
{
|
||||
$templates = SSViewer::get_templates_by_class($this, '', __CLASS__);
|
||||
// Prefer any custom template
|
||||
if($this->getTemplate()) {
|
||||
array_unshift($templates, $this->getTemplate());
|
||||
}
|
||||
return $templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Controller
|
||||
*/
|
||||
|
@ -49,6 +49,7 @@ class GridFieldEditButton implements GridField_ColumnProvider {
|
||||
if($columnName == 'Actions') {
|
||||
return array('title' => '');
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,8 +76,7 @@ class GridFieldEditButton implements GridField_ColumnProvider {
|
||||
* @param GridField $gridField
|
||||
* @param DataObject $record
|
||||
* @param string $columnName
|
||||
*
|
||||
* @return string - the HTML for the column
|
||||
* @return string The HTML for the column
|
||||
*/
|
||||
public function getColumnContent($gridField, $record, $columnName) {
|
||||
// No permission checks, handled through GridFieldDetailForm,
|
||||
@ -86,7 +86,8 @@ class GridFieldEditButton implements GridField_ColumnProvider {
|
||||
'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit')
|
||||
));
|
||||
|
||||
return $data->renderWith('Includes/GridFieldEditButton');
|
||||
$template = SSViewer::get_templates_by_class($this, '', __CLASS__);
|
||||
return $data->renderWith($template);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,8 +161,9 @@ class GridFieldFilterHeader implements GridField_HTMLProvider, GridField_DataMan
|
||||
$forTemplate->Fields->push($fields);
|
||||
}
|
||||
|
||||
$templates = SSViewer::get_templates_by_class($this, '_Row', __CLASS__);
|
||||
return array(
|
||||
'header' => $forTemplate->renderWith('Includes/GridFieldFilterHeader_Row'),
|
||||
'header' => $forTemplate->renderWith($templates),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,8 @@ class GridFieldFooter implements GridField_HTMLProvider {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $message - a message to display in the footer
|
||||
* @param string $message A message to display in the footer
|
||||
* @param bool $showrecordcount
|
||||
*/
|
||||
public function __construct($message = null, $showrecordcount = true) {
|
||||
if($message) {
|
||||
@ -46,9 +47,10 @@ class GridFieldFooter implements GridField_HTMLProvider {
|
||||
'NumRecords' => $count
|
||||
));
|
||||
|
||||
$template = SSViewer::get_templates_by_class($this, '', __CLASS__);
|
||||
return array(
|
||||
'footer' => $forTemplate->renderWith(
|
||||
'Includes/GridFieldFooter',
|
||||
$template,
|
||||
array(
|
||||
'Colspan' => count($gridField->getColumns())
|
||||
)
|
||||
|
@ -35,40 +35,48 @@ class GridFieldLevelup extends Object implements GridField_HTMLProvider {
|
||||
* @param integer $currentID - The ID of the current item; this button will find that item's parent
|
||||
*/
|
||||
public function __construct($currentID) {
|
||||
if($currentID && is_numeric($currentID)) $this->currentID = $currentID;
|
||||
parent::__construct();
|
||||
if($currentID && is_numeric($currentID)) {
|
||||
$this->currentID = $currentID;
|
||||
}
|
||||
}
|
||||
|
||||
public function getHTMLFragments($gridField) {
|
||||
$modelClass = $gridField->getModelClass();
|
||||
$parentID = 0;
|
||||
|
||||
if($this->currentID) {
|
||||
$modelObj = DataObject::get_by_id($modelClass, $this->currentID);
|
||||
|
||||
if($modelObj->hasMethod('getParent')) {
|
||||
$parent = $modelObj->getParent();
|
||||
} elseif($modelObj->ParentID) {
|
||||
$parent = $modelObj->Parent();
|
||||
}
|
||||
|
||||
if($parent) $parentID = $parent->ID;
|
||||
|
||||
// Attributes
|
||||
$attrs = array_merge($this->attributes, array(
|
||||
'href' => sprintf($this->linkSpec, $parentID),
|
||||
'class' => 'cms-panel-link ss-ui-button font-icon-level-up no-text grid-levelup'
|
||||
));
|
||||
$attrsStr = '';
|
||||
foreach($attrs as $k => $v) $attrsStr .= " $k=\"" . Convert::raw2att($v) . "\"";
|
||||
|
||||
$forTemplate = new ArrayData(array(
|
||||
'UpLink' => DBField::create_field('HTMLFragment', sprintf('<a%s></a>', $attrsStr))
|
||||
));
|
||||
|
||||
return array(
|
||||
'before' => $forTemplate->renderWith('Includes/GridFieldLevelup'),
|
||||
);
|
||||
if(!$this->currentID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$modelObj = DataObject::get_by_id($modelClass, $this->currentID);
|
||||
|
||||
$parent = null;
|
||||
if($modelObj->hasMethod('getParent')) {
|
||||
$parent = $modelObj->getParent();
|
||||
} elseif($modelObj->ParentID) {
|
||||
$parent = $modelObj->Parent();
|
||||
}
|
||||
|
||||
if ($parent) {
|
||||
$parentID = $parent->ID;
|
||||
}
|
||||
|
||||
// Attributes
|
||||
$attrs = array_merge($this->attributes, array(
|
||||
'href' => sprintf($this->linkSpec, $parentID),
|
||||
'class' => 'cms-panel-link ss-ui-button font-icon-level-up no-text grid-levelup'
|
||||
));
|
||||
$linkTag = FormField::create_tag('a', $attrs);
|
||||
|
||||
$forTemplate = new ArrayData(array(
|
||||
'UpLink' => DBField::create_field('HTMLFragment', $linkTag)
|
||||
));
|
||||
|
||||
$template = SSViewer::get_templates_by_class($this, '', __CLASS__);
|
||||
return array(
|
||||
'before' => $forTemplate->renderWith($template),
|
||||
);
|
||||
}
|
||||
|
||||
public function setAttributes($attrs) {
|
||||
|
@ -16,14 +16,7 @@ class GridFieldPageCount implements GridField_HTMLProvider {
|
||||
protected $targetFragment;
|
||||
|
||||
/**
|
||||
* Which template to use for rendering
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $itemClass = 'GridFieldPageCount';
|
||||
|
||||
/**
|
||||
* @param string $targetFrament The fragment indicating the placement of this page count
|
||||
* @param string $targetFragment The fragment indicating the placement of this page count
|
||||
*/
|
||||
public function __construct($targetFragment = 'before') {
|
||||
$this->targetFragment = $targetFragment;
|
||||
@ -62,14 +55,16 @@ class GridFieldPageCount implements GridField_HTMLProvider {
|
||||
* @return array
|
||||
*/
|
||||
public function getHTMLFragments($gridField) {
|
||||
|
||||
// Retrieve paging parameters from the directing paginator component
|
||||
$paginator = $this->getPaginator($gridField);
|
||||
if ($paginator && ($forTemplate = $paginator->getTemplateParameters($gridField))) {
|
||||
$template = SSViewer::get_templates_by_class($this, '', __CLASS__);
|
||||
return array(
|
||||
$this->targetFragment => $forTemplate->renderWith('Includes/'.$this->itemClass)
|
||||
$this->targetFragment => $forTemplate->renderWith($template)
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,13 +25,6 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu
|
||||
*/
|
||||
protected $itemsPerPage;
|
||||
|
||||
/**
|
||||
* Which template to use for rendering
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $itemClass = 'GridFieldPaginator_Row';
|
||||
|
||||
/**
|
||||
* See {@link setThrowExceptionOnBadDataType()}
|
||||
*/
|
||||
@ -255,18 +248,22 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu
|
||||
* @return array
|
||||
*/
|
||||
public function getHTMLFragments($gridField) {
|
||||
|
||||
$forTemplate = $this->getTemplateParameters($gridField);
|
||||
if($forTemplate) {
|
||||
return array(
|
||||
'footer' => $forTemplate->renderWith('Includes/'.$this->itemClass,
|
||||
array('Colspan'=>count($gridField->getColumns())))
|
||||
);
|
||||
if(!$forTemplate) {
|
||||
return null;
|
||||
}
|
||||
$template = SSViewer::get_templates_by_class($this, '_Row', __CLASS__);
|
||||
return array(
|
||||
'footer' => $forTemplate->renderWith(
|
||||
$template,
|
||||
array('Colspan' => count($gridField->getColumns()))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Int
|
||||
* @param int $num
|
||||
* @return $this
|
||||
*/
|
||||
public function setItemsPerPage($num) {
|
||||
$this->itemsPerPage = $num;
|
||||
|
@ -121,8 +121,10 @@ class GridFieldPrintButton implements GridField_HTMLProvider, GridField_ActionPr
|
||||
Requirements::css(FRAMEWORK_DIR . '/client/dist/styles/GridField_print.css');
|
||||
|
||||
if($data = $this->generatePrintData($gridField)){
|
||||
return $data->renderWith("GridField_print");
|
||||
return $data->renderWith(get_class($gridField)."_print");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -163,8 +163,9 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM
|
||||
$forTemplate->Fields->push($field);
|
||||
}
|
||||
|
||||
$template = SSViewer::get_templates_by_class($this, '_Row', __CLASS__);
|
||||
return array(
|
||||
'header' => $forTemplate->renderWith('Includes/GridFieldSortableHeader_Row'),
|
||||
'header' => $forTemplate->renderWith($template),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -11,9 +11,14 @@
|
||||
*/
|
||||
class GridFieldToolbarHeader implements GridField_HTMLProvider {
|
||||
|
||||
public function getHTMLFragments( $gridField) {
|
||||
/**
|
||||
* @param GridField $gridField
|
||||
* @return array
|
||||
*/
|
||||
public function getHTMLFragments($gridField) {
|
||||
$templates = SSViewer::get_templates_by_class($this, '', __CLASS__);
|
||||
return array(
|
||||
'header' => $gridField->renderWith('Includes/GridFieldToolbarHeader')
|
||||
'header' => $gridField->renderWith($templates)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,9 @@
|
||||
class GridFieldViewButton implements GridField_ColumnProvider {
|
||||
|
||||
public function augmentColumns($field, &$cols) {
|
||||
if(!in_array('Actions', $cols)) $cols[] = 'Actions';
|
||||
if(!in_array('Actions', $cols)) {
|
||||
$cols[] = 'Actions';
|
||||
}
|
||||
}
|
||||
|
||||
public function getColumnsHandled($field) {
|
||||
@ -18,12 +20,14 @@ class GridFieldViewButton implements GridField_ColumnProvider {
|
||||
}
|
||||
|
||||
public function getColumnContent($field, $record, $col) {
|
||||
if($record->canView()) {
|
||||
$data = new ArrayData(array(
|
||||
'Link' => Controller::join_links($field->Link('item'), $record->ID, 'view')
|
||||
));
|
||||
return $data->renderWith('Includes/GridFieldViewButton');
|
||||
if(!$record->canView()) {
|
||||
return null;
|
||||
}
|
||||
$data = new ArrayData(array(
|
||||
'Link' => Controller::join_links($field->Link('item'), $record->ID, 'view')
|
||||
));
|
||||
$template = SSViewer::get_templates_by_class($this, '', __CLASS__);
|
||||
return $data->renderWith($template);
|
||||
}
|
||||
|
||||
public function getColumnAttributes($field, $record, $col) {
|
||||
|
@ -1,5 +0,0 @@
|
||||
<% if Backlink %>
|
||||
<a href="$Backlink"><%t GridFieldItemEditView.Go_back 'Go back' %></a>
|
||||
<% end_if %>
|
||||
|
||||
$ItemEditForm
|
@ -76,7 +76,7 @@ class RSSFeedTest extends SapphireTest {
|
||||
$content = $rssFeed->outputToBrowser();
|
||||
$this->assertContains('<title>Test Custom Template</title>', $content);
|
||||
|
||||
$rssFeed->setTemplate('RSSFeed');
|
||||
$rssFeed->setTemplate(null);
|
||||
$content = $rssFeed->outputToBrowser();
|
||||
$this->assertNotContains('<title>Test Custom Template</title>', $content);
|
||||
}
|
||||
|
@ -123,6 +123,31 @@ class ThemeResourceLoaderTest extends SapphireTest {
|
||||
);
|
||||
}
|
||||
|
||||
public function testFindTemplatesByPath() {
|
||||
// Items given as full paths are returned directly
|
||||
$this->assertEquals(
|
||||
"$this->base/themes/theme/templates/Page.ss",
|
||||
$this->loader->findTemplate("$this->base/themes/theme/templates/Page.ss", ['theme'])
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"$this->base/themes/theme/templates/Page.ss",
|
||||
$this->loader->findTemplate([
|
||||
"$this->base/themes/theme/templates/Page.ss",
|
||||
"Page"
|
||||
], ['theme'])
|
||||
);
|
||||
|
||||
// Ensure checks for file_exists
|
||||
$this->assertEquals(
|
||||
"$this->base/themes/theme/templates/Page.ss",
|
||||
$this->loader->findTemplate([
|
||||
"$this->base/themes/theme/templates/NotAPage.ss",
|
||||
"$this->base/themes/theme/templates/Page.ss",
|
||||
], ['theme'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that 'main' and 'Layout' templates are loaded from set theme
|
||||
*/
|
||||
|
@ -110,7 +110,7 @@ class MemberDatetimeOptionsetFieldTest extends SapphireTest {
|
||||
$field->setDescription('Test description');
|
||||
$this->assertEquals('Test description', $field->getDescription());
|
||||
|
||||
$field->setDescriptionTemplate('forms/MemberDatetimeOptionsetField_description_time');
|
||||
$field->setDescriptionTemplate('MemberDatetimeOptionsetField_description_time');
|
||||
$this->assertNotEmpty($field->getDescription());
|
||||
$this->assertNotEquals('Test description', $field->getDescription());
|
||||
}
|
||||
|
@ -1154,22 +1154,57 @@ after')
|
||||
$this->useTestTheme(dirname(__FILE__), 'layouttest', function() use ($self) {
|
||||
// Test passing a string
|
||||
$templates = SSViewer::get_templates_by_class(
|
||||
'TestNamespace\SSViewerTestModel_Controller',
|
||||
'TestNamespace\\SSViewerTestModel_Controller',
|
||||
'',
|
||||
'Controller'
|
||||
);
|
||||
$self->assertEquals([
|
||||
'TestNamespace\SSViewerTestModel_Controller',
|
||||
'TestNamespace\\SSViewerTestModel_Controller',
|
||||
[
|
||||
'type' => 'Includes',
|
||||
'TestNamespace\\SSViewerTestModel_Controller',
|
||||
],
|
||||
'TestNamespace\\SSViewerTestModel',
|
||||
'Controller',
|
||||
[
|
||||
'type' => 'Includes',
|
||||
'Controller',
|
||||
],
|
||||
], $templates);
|
||||
|
||||
// Test to ensure we're stopping at the base class.
|
||||
$templates = SSViewer::get_templates_by_class('TestNamespace\SSViewerTestModel_Controller', '', 'TestNamespace\SSViewerTestModel_Controller');
|
||||
$self->assertCount(1, $templates);
|
||||
$templates = SSViewer::get_templates_by_class(
|
||||
'TestNamespace\SSViewerTestModel_Controller',
|
||||
'',
|
||||
'TestNamespace\SSViewerTestModel_Controller'
|
||||
);
|
||||
$self->assertEquals([
|
||||
'TestNamespace\\SSViewerTestModel_Controller',
|
||||
[
|
||||
'type' => 'Includes',
|
||||
'TestNamespace\\SSViewerTestModel_Controller',
|
||||
],
|
||||
'TestNamespace\\SSViewerTestModel',
|
||||
], $templates);
|
||||
|
||||
// Make sure we can filter our templates by suffix.
|
||||
$templates = SSViewer::get_templates_by_class('TestNamespace\SSViewerTestModel', '_Controller');
|
||||
$self->assertCount(1, $templates);
|
||||
// Make sure we can search templates by suffix.
|
||||
$templates = SSViewer::get_templates_by_class(
|
||||
'TestNamespace\\SSViewerTestModel',
|
||||
'_Controller',
|
||||
'SilverStripe\\ORM\\DataObject'
|
||||
);
|
||||
$self->assertEquals([
|
||||
'TestNamespace\\SSViewerTestModel_Controller',
|
||||
[
|
||||
'type' => 'Includes',
|
||||
'TestNamespace\\SSViewerTestModel_Controller',
|
||||
],
|
||||
'SilverStripe\\ORM\\DataObject_Controller',
|
||||
[
|
||||
'type' => 'Includes',
|
||||
'SilverStripe\\ORM\\DataObject_Controller',
|
||||
],
|
||||
], $templates);
|
||||
|
||||
// Let's throw something random in there.
|
||||
$self->setExpectedException('InvalidArgumentException');
|
||||
|
@ -859,51 +859,52 @@ class SSViewer implements Flushable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses the given the given class context looking for templates with the relevant name.
|
||||
*
|
||||
* @param $className string - valid class name
|
||||
* @param $suffix string
|
||||
* @param $baseClass string
|
||||
* Traverses the given the given class context looking for candidate template names
|
||||
* which match each item in the class hierarchy. The resulting list of template candidates
|
||||
* may or may not exist, but you can invoke {@see SSViewer::chooseTemplate} on any list
|
||||
* to determine the best candidate based on the current themes.
|
||||
*
|
||||
* @param string|object $classOrObject Valid class name, or object
|
||||
* @param string $suffix
|
||||
* @param string $baseClass Class to halt ancestry search at
|
||||
* @return array
|
||||
*/
|
||||
public static function get_templates_by_class($className, $suffix = '', $baseClass = null) {
|
||||
public static function get_templates_by_class($classOrObject, $suffix = '', $baseClass = null) {
|
||||
// Figure out the class name from the supplied context.
|
||||
if(!is_string($className) || !class_exists($className)) {
|
||||
if (!is_object($classOrObject) && !(
|
||||
is_string($classOrObject) && class_exists($classOrObject)
|
||||
)) {
|
||||
throw new InvalidArgumentException(
|
||||
'SSViewer::get_templates_by_class() expects a valid class name as its first parameter.'
|
||||
);
|
||||
}
|
||||
$templates = array();
|
||||
$classes = array_reverse(ClassInfo::ancestry($className));
|
||||
$classes = array_reverse(ClassInfo::ancestry($classOrObject));
|
||||
foreach($classes as $class) {
|
||||
$template = $class . $suffix;
|
||||
if(SSViewer::hasTemplate($template)) {
|
||||
$templates[] = $template;
|
||||
} elseif(SSViewer::hasTemplate($template = ['type' => 'Includes', $template])) {
|
||||
$templates[] = $template;
|
||||
}
|
||||
$templates[] = $template;
|
||||
$templates[] = ['type' => 'Includes', $template];
|
||||
|
||||
// If the class is "Page_Controller", look for Page.ss
|
||||
if(stripos($class,'_controller') !== false) {
|
||||
$template = str_ireplace('_controller','',$class) . $suffix;
|
||||
if(SSViewer::hasTemplate($template)) {
|
||||
$templates[] = $template;
|
||||
}
|
||||
if (stripos($class, '_controller') !== false) {
|
||||
$templates[] = str_ireplace('_controller', '', $class) . $suffix;
|
||||
}
|
||||
|
||||
if($baseClass && $class == $baseClass) break;
|
||||
if($baseClass && $class == $baseClass) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $templateList If passed as a string with .ss extension, used as the "main" template.
|
||||
* @param string|array $templates If passed as a string with .ss extension, used as the "main" template.
|
||||
* If passed as an array, it can be used for template inheritance (first found template "wins").
|
||||
* Usually the array values are PHP class names, which directly correlate to template names.
|
||||
* <code>
|
||||
* array('MySpecificPage', 'MyPage', 'Page')
|
||||
* </code>
|
||||
* @param TemplateParser $parser
|
||||
*/
|
||||
public function __construct($templates, TemplateParser $parser = null) {
|
||||
if ($parser) {
|
||||
@ -929,10 +930,20 @@ class SSViewer implements Flushable {
|
||||
|
||||
public function setTemplate($templates) {
|
||||
$this->templates = $templates;
|
||||
$this->chosen = ThemeResourceLoader::instance()->findTemplate($templates, self::get_themes());
|
||||
$this->chosen = $this->chooseTemplate($templates);
|
||||
$this->subTemplates = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the template to use for a given list
|
||||
*
|
||||
* @param array|string $templates
|
||||
* @return string
|
||||
*/
|
||||
public static function chooseTemplate($templates) {
|
||||
return ThemeResourceLoader::instance()->findTemplate($templates, self::get_themes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the template parser that will be used in template generation
|
||||
* @param \TemplateParser $parser
|
||||
|
@ -155,13 +155,6 @@ class ThemeResourceLoader {
|
||||
$templateList = array($template);
|
||||
}
|
||||
|
||||
// If we have an .ss extension, this is a path, not a template name. We should
|
||||
// pass in templates without extensions in order for template manifest to find
|
||||
// files dynamically.
|
||||
if(count($templateList) == 1 && is_string($templateList[0]) && substr($templateList[0], -3) == '.ss') {
|
||||
return $templateList[0];
|
||||
}
|
||||
|
||||
foreach($templateList as $i => $template) {
|
||||
// Check if passed list of templates in array format
|
||||
if (is_array($template)) {
|
||||
@ -172,6 +165,13 @@ class ThemeResourceLoader {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we have an .ss extension, this is a path, not a template name. We should
|
||||
// pass in templates without extensions in order for template manifest to find
|
||||
// files dynamically.
|
||||
if(substr($template, -3) == '.ss' && file_exists($template)) {
|
||||
return $template;
|
||||
}
|
||||
|
||||
// Check string template identifier
|
||||
$template = str_replace('\\', '/', $template);
|
||||
$parts = explode('/', $template);
|
||||
|
Loading…
Reference in New Issue
Block a user