Merged pull request #25 from ajshort/3a1c2df4e73390c18b98902f8e07d5d36472c945.

Rename DataObjectDecorator to DataExtension
This commit is contained in:
Will Rossiter 2011-04-26 02:02:54 -07:00
commit 4f31f48783
36 changed files with 486 additions and 458 deletions

View File

@ -86,7 +86,7 @@ class LeftAndMain extends Controller {
// cms menus only for logged-in members // cms menus only for logged-in members
if(!$member) return false; if(!$member) return false;
// alternative decorated checks // alternative extended checks
if($this->hasMethod('alternateAccessCheck')) { if($this->hasMethod('alternateAccessCheck')) {
$alternateAllowed = $this->alternateAccessCheck(); $alternateAllowed = $this->alternateAccessCheck();
if($alternateAllowed === FALSE) return false; if($alternateAllowed === FALSE) return false;
@ -102,8 +102,8 @@ class LeftAndMain extends Controller {
} }
/** /**
* @uses LeftAndMainDecorator->init() * @uses LeftAndMainExtension->init()
* @uses LeftAndMainDecorator->accessedCMS() * @uses LeftAndMainExtension->accessedCMS()
* @uses CMSMenu * @uses CMSMenu
*/ */
function init() { function init() {
@ -124,7 +124,7 @@ class LeftAndMain extends Controller {
self::$help_link self::$help_link
); );
// Allow customisation of the access check by a decorator // Allow customisation of the access check by a extension
// Also all the canView() check to execute Director::redirect() // Also all the canView() check to execute Director::redirect()
if(!$this->canView() && !$this->response->isFinished()) { if(!$this->canView() && !$this->response->isFinished()) {
// When access /admin/, we should try a redirect to another part of the admin rather than be locked out // When access /admin/, we should try a redirect to another part of the admin rather than be locked out

View File

@ -1,21 +1,17 @@
<?php <?php
/** /**
* Plug-ins for additional functionality in your LeftAndMain classes. * @package sapphire
* * @subpackage admin
* @package cms * @deprecated 3.0 Use {@link LeftAndMainExtension}
* @subpackage core
*/ */
abstract class LeftAndMainDecorator extends Extension { abstract class LeftAndMainDecorator extends LeftAndMainExtension {
function init() { public function __construct() {
} user_error(
'LeftAndMainDecorator is deprecated, please use LeftAndMainExtension instead.',
function accessedCMS() { E_USER_NOTICE
} );
parent::__construct();
function augmentNewSiteTreeItem(&$item) {
} }
} }
?>

View File

@ -0,0 +1,19 @@
<?php
/**
* Plug-ins for additional functionality in your LeftAndMain classes.
*
* @package cms
* @subpackage core
*/
abstract class LeftAndMainExtension extends Extension {
function init() {
}
function accessedCMS() {
}
function augmentNewSiteTreeItem(&$item) {
}
}

View File

@ -87,7 +87,7 @@
* @todo URL parameter namespacing for search-fields, limit, fields, add_fields (might all be valid dataobject properties) * @todo URL parameter namespacing for search-fields, limit, fields, add_fields (might all be valid dataobject properties)
* e.g. you wouldn't be able to search for a "limit" property on your subclass as its overlayed with the search logic * e.g. you wouldn't be able to search for a "limit" property on your subclass as its overlayed with the search logic
* @todo i18n integration (e.g. Page/1.xml?lang=de_DE) * @todo i18n integration (e.g. Page/1.xml?lang=de_DE)
* @todo Access to decoratable methods/relations like SiteTree/1/Versions or SiteTree/1/Version/22 * @todo Access to extendable methods/relations like SiteTree/1/Versions or SiteTree/1/Version/22
* @todo Respect $api_access array notation in search contexts * @todo Respect $api_access array notation in search contexts
* *
* @package sapphire * @package sapphire

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Add extension that can be added to an object with {@link Object::add_extension()}. * Add extension that can be added to an object with {@link Object::add_extension()}.
* For {@link DataObject} extensions, use {@link DataObjectDecorator}. * For {@link DataObject} extensions, use {@link DataExtension}.
* Each extension instance has an "owner" instance, accessible through * Each extension instance has an "owner" instance, accessible through
* {@link getOwner()}. * {@link getOwner()}.
* Every object instance gets its own set of extension instances, * Every object instance gets its own set of extension instances,
@ -19,8 +19,9 @@ abstract class Extension {
public static $allowed_actions = null; public static $allowed_actions = null;
/** /**
* The DataObject that owns this decorator. * The object this extension is applied to.
* @var DataObject *
* @var Object
*/ */
protected $owner; protected $owner;
@ -43,7 +44,7 @@ abstract class Extension {
} }
/** /**
* Set the owner of this decorator. * Set the owner of this extension.
* @param Object $owner The owner object, * @param Object $owner The owner object,
* @param string $ownerBaseClass The base class that the extension is applied to; this may be * @param string $ownerBaseClass The base class that the extension is applied to; this may be
* the class of owner, or it may be a parent. For example, if Versioned was applied to SiteTree, * the class of owner, or it may be a parent. For example, if Versioned was applied to SiteTree,
@ -65,7 +66,7 @@ abstract class Extension {
} }
/** /**
* Returns the owner of this decorator * Returns the owner of this extension.
* *
* @return Object * @return Object
*/ */

View File

@ -277,7 +277,6 @@ abstract class Object {
} }
if(!isset(self::$cached_statics[$class][$name]) || $uncached) { if(!isset(self::$cached_statics[$class][$name]) || $uncached) {
//if($class == 'DataObjectDecoratorTest_MyObject') Debug::message("$class - $name");
$extra = $builtIn = $break = $replacedAt = false; $extra = $builtIn = $break = $replacedAt = false;
$ancestry = array_reverse(ClassInfo::ancestry($class)); $ancestry = array_reverse(ClassInfo::ancestry($class));
@ -500,7 +499,7 @@ abstract class Object {
* Keep in mind that the extension will only be applied to new * Keep in mind that the extension will only be applied to new
* instances, not existing ones (including all instances created through {@link singleton()}). * instances, not existing ones (including all instances created through {@link singleton()}).
* *
* @param string $class Class that should be decorated - has to be a subclass of {@link Object} * @param string $class Class that should be extended - has to be a subclass of {@link Object}
* @param string $extension Subclass of {@link Extension} with optional parameters * @param string $extension Subclass of {@link Extension} with optional parameters
* as a string, e.g. "Versioned" or "Translatable('Param')" * as a string, e.g. "Versioned" or "Translatable('Param')"
*/ */
@ -538,11 +537,11 @@ abstract class Object {
// load statics now for DataObject classes // load statics now for DataObject classes
if(is_subclass_of($class, 'DataObject')) { if(is_subclass_of($class, 'DataObject')) {
if(is_subclass_of($extensionClass, 'DataObjectDecorator')) { if(is_subclass_of($extensionClass, 'DataExtension')) {
DataObjectDecorator::load_extra_statics($class, $extension); DataExtension::load_extra_statics($class, $extension);
} }
else { else {
user_error("$extensionClass cannot be applied to $class without being a DataObjectDecorator", E_USER_ERROR); user_error("$extensionClass cannot be applied to $class without being a DataExtension", E_USER_ERROR);
} }
} }
} }
@ -568,11 +567,11 @@ abstract class Object {
$extensionClass = $matches[1]; $extensionClass = $matches[1];
} }
if(is_subclass_of($extensionClass, 'DataObjectDecorator')) { if(is_subclass_of($extensionClass, 'DataExtension')) {
DataObjectDecorator::load_extra_statics($class, $extension); DataExtension::load_extra_statics($class, $extension);
} }
else { else {
user_error("$extensionClass cannot be applied to $class without being a DataObjectDecorator", E_USER_ERROR); user_error("$extensionClass cannot be applied to $class without being a DataExtension", E_USER_ERROR);
} }
} }
} }
@ -624,7 +623,7 @@ abstract class Object {
* @param string $class * @param string $class
* @param bool $includeArgumentString Include the argument string in the return array, * @param bool $includeArgumentString Include the argument string in the return array,
* FALSE would return array("Versioned"), TRUE returns array("Versioned('Stage','Live')"). * FALSE would return array("Versioned"), TRUE returns array("Versioned('Stage','Live')").
* @return array Numeric array of either {@link DataObjectDecorator} classnames, * @return array Numeric array of either {@link DataExtension} classnames,
* or eval'ed classname strings with constructor arguments. * or eval'ed classname strings with constructor arguments.
*/ */
function get_extensions($class, $includeArgumentString = false) { function get_extensions($class, $includeArgumentString = false) {
@ -950,7 +949,7 @@ abstract class Object {
* you wanted to return results, you're hosed * you wanted to return results, you're hosed
* *
* Currently returns an array, with an index resulting every time the function is called. Only adds returns if * Currently returns an array, with an index resulting every time the function is called. Only adds returns if
* they're not NULL, to avoid bogus results from methods just defined on the parent decorator. This is important for * they're not NULL, to avoid bogus results from methods just defined on the parent extension. This is important for
* permission-checks through extend, as they use min() to determine if any of the returns is FALSE. As min() doesn't * permission-checks through extend, as they use min() to determine if any of the returns is FALSE. As min() doesn't
* do type checking, an included NULL return would fail the permission checks. * do type checking, an included NULL return would fail the permission checks.
* *
@ -992,7 +991,7 @@ abstract class Object {
* in {@link $extension_instances}. Extension instances are initialized * in {@link $extension_instances}. Extension instances are initialized
* at constructor time, meaning if you use {@link add_extension()} * at constructor time, meaning if you use {@link add_extension()}
* afterwards, the added extension will just be added to new instances * afterwards, the added extension will just be added to new instances
* of the decorated class. Use the static method {@link has_extension()} * of the extended class. Use the static method {@link has_extension()}
* to check if a class (not an instance) has a specific extension. * to check if a class (not an instance) has a specific extension.
* Caution: Don't use singleton(<class>)->hasExtension() as it will * Caution: Don't use singleton(<class>)->hasExtension() as it will
* give you inconsistent results based on when the singleton was first * give you inconsistent results based on when the singleton was first
@ -1010,7 +1009,7 @@ abstract class Object {
* See {@link get_extensions()} to get all applied extension classes * See {@link get_extensions()} to get all applied extension classes
* for this class (not the instance). * for this class (not the instance).
* *
* @return array Map of {@link DataObjectDecorator} instances, keyed by classname. * @return array Map of {@link DataExtension} instances, keyed by classname.
*/ */
public function getExtensionInstances() { public function getExtensionInstances() {
return $this->extension_instances; return $this->extension_instances;

View File

@ -688,9 +688,9 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
$dbConn = DB::getConn(); $dbConn = DB::getConn();
$dbName = $dbConn->currentDatabase(); $dbName = $dbConn->currentDatabase();
if($dbName && DB::getConn()->databaseExists($dbName)) { if($dbName && DB::getConn()->databaseExists($dbName)) {
// Some DataObjectsDecorators keep a static cache of information that needs to // Some DataExtensions keep a static cache of information that needs to
// be reset whenever the database is killed // be reset whenever the database is killed
foreach(ClassInfo::subclassesFor('DataObjectDecorator') as $class) { foreach(ClassInfo::subclassesFor('DataExtension') as $class) {
$toCall = array($class, 'on_db_reset'); $toCall = array($class, 'on_db_reset');
if(is_callable($toCall)) call_user_func($toCall); if(is_callable($toCall)) call_user_func($toCall);
} }
@ -709,9 +709,9 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
$dbadmin = new DatabaseAdmin(); $dbadmin = new DatabaseAdmin();
$dbadmin->clearAllData(); $dbadmin->clearAllData();
// Some DataObjectsDecorators keep a static cache of information that needs to // Some DataExtensions keep a static cache of information that needs to
// be reset whenever the database is cleaned out // be reset whenever the database is cleaned out
foreach(array_merge(ClassInfo::subclassesFor('DataObjectDecorator'), ClassInfo::subclassesFor('DataObject')) as $class) { foreach(array_merge(ClassInfo::subclassesFor('DataExtension'), ClassInfo::subclassesFor('DataObject')) as $class) {
$toCall = array($class, 'on_db_reset'); $toCall = array($class, 'on_db_reset');
if(is_callable($toCall)) call_user_func($toCall); if(is_callable($toCall)) call_user_func($toCall);
} }

View File

@ -1,22 +1,22 @@
# DataObjectDecorator # DataExtension
## Introduction ## Introduction
Extensions (also referred to as decorators) allow for adding additional functionality to a `[api:DataObject]`. Extensions allow for adding additional functionality to a `[api:DataObject]`.
In some cases, it can be easier to completely replace the used class throughout the core with your custom In some cases, it can be easier to completely replace the used class throughout the core with your custom
implementation. Have a look at `[api:Object->useCustomClass()]`. implementation. Have a look at `[api:Object->useCustomClass()]`.
## Usage ## Usage
Your Decorator will nee to be a subclass of `[api:DataObjectDecorator]` or the `[api:Extension]` class. Your extension will nee to be a subclass of `[api:DataExtension]` or the `[api:Extension]` class.
:::php :::php
<?php <?php
// mysite/code/CustomMember.php // mysite/code/CustomMember.php
class CustomMember extends DataObjectDecorator { class CustomMember extends DataExtension {
} }
@ -24,10 +24,10 @@ This defines your own extension where you can add your own functions, database f
After you create this extension however it does not yet apply it to your object. Next you need to tell SilverStripe what After you create this extension however it does not yet apply it to your object. Next you need to tell SilverStripe what
class you want to extend. class you want to extend.
### Adding a decorator to a built-in class ### Adding a extension to a built-in class
Sometimes you will want to add decorators to classes that you didn't make. For example, you might want to add the Sometimes you will want to add extension to classes that you didn't make. For example, you might want to add the
`[api:ForumRole]` decorator to the `[api:Member]` object. `[api:ForumRole]` extension to the `[api:Member]` object.
:::php :::php
@ -45,12 +45,12 @@ For example above we want to override Member with a Custom Member so we would wr
### Adding extra database fields ### Adding extra database fields
Extra database fields can be added with a decorator by defining an **extraStatics()** method. These will be added to the table of the base object - the decorator will actually edit the $db, $has_one, etc static variables on load. Extra database fields can be added with a extension by defining an **extraStatics()** method. These will be added to the table of the base object - the extension will actually edit the $db, $has_one, etc static variables on load.
The function should return a map where the keys are the names of the static variables to update: The function should return a map where the keys are the names of the static variables to update:
:::php :::php
class CustomMember extends DataObjectDecorator { class CustomMember extends DataExtension {
function extraStatics() { function extraStatics() {
return array( return array(
@ -67,7 +67,7 @@ The function should return a map where the keys are the names of the static vari
*NOTE* *NOTE*
If you want to add has_one or db items to a particular class, then that class **must** have that static variable If you want to add has_one or db items to a particular class, then that class **must** have that static variable
explicitly defined, even if it's just a blank array. For example, the decorator method above wouldn't work if you added explicitly defined, even if it's just a blank array. For example, the extension method above wouldn't work if you added
to a class that didn't have static $has_one explicitly declared on the object. This is because of PHP's crappy support to a class that didn't have static $has_one explicitly declared on the object. This is because of PHP's crappy support
for statics. for statics.
@ -75,7 +75,7 @@ for statics.
### Modifying CMS Fields ### Modifying CMS Fields
The member class demonstrates an extension that allows you to update the default CMS fields for an object in a The member class demonstrates an extension that allows you to update the default CMS fields for an object in a
decorator: extension:
:::php :::php
public function getCMSFields() { public function getCMSFields() {
@ -97,11 +97,11 @@ The $fields parameter is passed by reference, as it is an object.
### Custom database generation ### Custom database generation
Some decorators are designed to transparently add more sophisticated data-collection capabilities to your data object. Some extensions are designed to transparently add more sophisticated data-collection capabilities to your data object.
For example, `[api:Versioned]` adds version tracking and staging to any data object that it is applied to. To do this, For example, `[api:Versioned]` adds version tracking and staging to any data object that it is applied to. To do this,
you need to be able to create additional database tables and fields to keep your state stored in. you need to be able to create additional database tables and fields to keep your state stored in.
To do this, define an **augmentDatabase()** method on your decorator. This will be called when db/build is visited. To do this, define an **augmentDatabase()** method on your extension. This will be called when db/build is visited.
* You can query ``$this->owner`` for information about the data object, such as the fields it has * You can query ``$this->owner`` for information about the data object, such as the fields it has
* You can use **DB::requireTable($tableName, $fieldList, $indexList)** to set up your new tables. This function takes * You can use **DB::requireTable($tableName, $fieldList, $indexList)** to set up your new tables. This function takes
@ -125,18 +125,18 @@ be modified as needed by your method. Instead of a manipulation array, we have
### Additional methods ### Additional methods
The other thing you may want to do with a decorator is provide a method that can be called on the `[api:DataObject]` that is The other thing you may want to do with a extension is provide a method that can be called on the `[api:DataObject]` that is
being decorated. For instance, you may add a publish() method to every `[api:DataObject]` that is decorated with `[api:Versioned]`. being extended. For instance, you may add a publish() method to every `[api:DataObject]` that is extended with `[api:Versioned]`.
This is as simple as defining a method called publish() on your decorator. Bear in mind, however, that instead of This is as simple as defining a method called publish() on your extension. Bear in mind, however, that instead of
$this, you should be referring to $this->owner. $this, you should be referring to $this->owner.
* $this = The `[api:DataObjectDecorator]` object. * $this = The `[api:DataExtension]` object.
* $this->owner = The related `[api:DataObject]` object. * $this->owner = The related `[api:DataObject]` object.
If you want to add your own internal properties, you can add this to the `[api:DataObjectDecorator]`, and these will be referred If you want to add your own internal properties, you can add this to the `[api:DataExtension]`, and these will be referred
to as `$this->propertyName`. Every `[api:DataObject]` has an associated `[api:DataObjectDecorator]` instance for each class that it is to as `$this->propertyName`. Every `[api:DataObject]` has an associated `[api:DataExtension]` instance for each class that it is
decorated by. extended by.
:::php :::php
class Customer extends DataObject { class Customer extends DataObject {
@ -159,7 +159,7 @@ decorated by.
} }
class CustomerWorkflow extends DataObjectDecorator { class CustomerWorkflow extends DataExtension {
function IsMarkedForDeletion() { function IsMarkedForDeletion() {
return ($this->owner->Account()->IsMarkedForDeletion == 1) ? true : false; return ($this->owner->Account()->IsMarkedForDeletion == 1) ? true : false;
@ -169,4 +169,4 @@ decorated by.
## API Documentation ## API Documentation
`[api:DataObjectDecorator]` `[api:DataExtension]`

View File

@ -40,7 +40,7 @@ These calls retrieve a `[api:FieldSet]` for the area where you intend to work wi
### For the Frontend ### For the Frontend
Used for simple frontend forms without relation editing or `[api:TabSet] behaviour. Uses `scaffoldFormFields()` by Used for simple frontend forms without relation editing or `[api:TabSet] behaviour. Uses `scaffoldFormFields()` by
default. To customize, either overload this method in your subclass, or decorate it by `DataObjectDecorator->updateFormFields()`. default. To customize, either overload this method in your subclass, or extend it by `DataExtension->updateFormFields()`.
* Requirements: SilverStripe 2.3.* * Requirements: SilverStripe 2.3.*

View File

@ -8,7 +8,7 @@ Reference articles complement our auto-generated [API docs](http://api.silverstr
* [ComplexTableField](complextablefield): Manage records and their relations inside the CMS * [ComplexTableField](complextablefield): Manage records and their relations inside the CMS
* [Database Structure](database-structure): Conventions and best practices for database tables and fields * [Database Structure](database-structure): Conventions and best practices for database tables and fields
* [DataObject](dataobject): Base class for database records * [DataObject](dataobject): Base class for database records
* [DataObjectDecorator](dataobjectdecorator): A "mixin" system allowing to extend core classes * [DataExtension](dataextension): A "mixin" system allowing to extend core classes
* [DataObjectSet](dataobjectset): The base collection of database records in the ORM * [DataObjectSet](dataobjectset): The base collection of database records in the ORM
* [Director](director): Routes URLs and handles HTTP requests * [Director](director): Routes URLs and handles HTTP requests
* [Execution Pipeline](execution-pipeline): Detailed look on the way an HTTP request takes through the system * [Execution Pipeline](execution-pipeline): Detailed look on the way an HTTP request takes through the system

View File

@ -38,7 +38,7 @@ Returns the full *Member* Object for the current user, returns *null* if user is
## Subclassing ## Subclassing
<div class="warning" markdown="1"> <div class="warning" markdown="1">
This is the least desirable way of extending the `[api:Member]` class. It's better to use `[api:DataObjectDecorator]` This is the least desirable way of extending the `[api:Member]` class. It's better to use `[api:DataExtension]`
(see below). (see below).
</div> </div>
@ -86,22 +86,22 @@ For persons without login-capabilities (e.g. for an address-database), you shoul
with the Member-database. This enables us to have a different subclass of `[api:Member]` for an email-address with login-data, with the Member-database. This enables us to have a different subclass of `[api:Member]` for an email-address with login-data,
and another subclass for the same email-address in the address-database. and another subclass for the same email-address in the address-database.
## Member Role Decorator ## Member Role Extension
Using inheritance to add extra behaviour or data fields to a member is limiting, because you can only inherit from 1 Using inheritance to add extra behaviour or data fields to a member is limiting, because you can only inherit from 1
class. A better way is to use role decorators to add this behaviour. class. A better way is to use role extensions to add this behaviour.
:::php :::php
DataObject::add_extension('Member', 'ForumRole'); Object::add_extension('Member', 'ForumRole');
// OR // OR
Member::add_role('ForumRole'); Member::add_role('ForumRole');
A role decorator is simply a subclass of `[api:DataObjectDecorator]` that is designed to be used to add behaviour to `[api:Member]`. A role extension is simply a subclass of `[api:DataExtension]` that is designed to be used to add behaviour to `[api:Member]`.
The roles affect the entire class - all members will get the additional behaviour. However, if you want to restrict The roles affect the entire class - all members will get the additional behaviour. However, if you want to restrict
things, you should add appropriate `[api:Permission::checkMember()]` calls to the role's methods. things, you should add appropriate `[api:Permission::checkMember()]` calls to the role's methods.
:::php :::php
class ForumRole extends DataObjectDecorator { class ForumRole extends DataExtension {
/** /**
* Modify the field set to be displayed in the CMS detail pop-up * Modify the field set to be displayed in the CMS detail pop-up

View File

@ -37,7 +37,7 @@ Create a mysite/code/CustomSiteConfig.php file.
:::php :::php
<?php <?php
class CustomSiteConfig extends DataObjectDecorator { class CustomSiteConfig extends DataExtension {
function extraStatics() { function extraStatics() {
return array( return array(

View File

@ -35,7 +35,7 @@ Append the option and corresponding value to your URL in your browser's address
| URL Variable | | Values | | Description | | URL Variable | | Values | | Description |
| ------------ | | ------ | | ----------- | | ------------ | | ------ | | ----------- |
| debugmethods | | 1 | | Shows all methods available when an object is constructed (useful when extending classes or using object decorators) | | debugmethods | | 1 | | Shows all methods available when an object is constructed (useful when extending classes or using object extensions) |
| debugfailover | | 1 | | Shows failover methods from classes extended | | debugfailover | | 1 | | Shows failover methods from classes extended |
## Database ## Database

View File

@ -453,11 +453,11 @@ See `[api:SQLQuery]` for custom *INSERT*, *UPDATE*, *DELETE* queries.
## Decorating DataObjects ## Extending DataObjects
You can add properties and methods to existing `[api:DataObjects]`s like `[api:Member]` (a core class) without hacking core You can add properties and methods to existing `[api:DataObjects]`s like `[api:Member]` (a core class) without hacking core
code or subclassing. code or subclassing.
Please see `[api:DataObjectDecorator]` for a general description, and `[api:Hierarchy]` for our most Please see `[api:DataExtension]` for a general description, and `[api:Hierarchy]` for our most
popular examples. popular examples.

View File

@ -15,7 +15,7 @@ project, like a forum, an ecommerce package or a blog you can do it like this;
Try and keep your module as generic as possible - for example if you're making a forum module, your members section Try and keep your module as generic as possible - for example if you're making a forum module, your members section
shouldn't contain fields like 'Games You Play' or 'Your LiveJournal Name' - if people want to add these fields they can shouldn't contain fields like 'Games You Play' or 'Your LiveJournal Name' - if people want to add these fields they can
sub-class your class, or decorate the fields on to it. sub-class your class, or extend the fields on to it.
If you're using Requirements to include generic support files for your project like CSS or Javascript, and want to If you're using Requirements to include generic support files for your project like CSS or Javascript, and want to
override these files to be more specific in your project, the following code is an example of how to do so using the override these files to be more specific in your project, the following code is an example of how to do so using the
@ -54,5 +54,5 @@ adherence to conventions, writing documentation, and releasing updates. See [con
* [Modules](modules) * [Modules](modules)
* [Module Release Process](module-release-process) * [Module Release Process](module-release-process)
* [Debugging methods](/topics/debugging) * [Debugging methods](/topics/debugging)
* [URL Variable Tools](/reference/urlvariabletools) - Lists a number of “page options” , “rendering tools” or “special * [URL Variable Tools](/reference/urlvariabletools) - Lists a number of <EFBFBD><EFBFBD><EFBFBD>page options<6E><73><EFBFBD> , <20><><EFBFBD>rendering tools<6C><73><EFBFBD> or <20><><EFBFBD>special
URL variables that you can use to debug your sapphire applications URL variables<EFBFBD><EFBFBD><EFBFBD> that you can use to debug your sapphire applications

View File

@ -13,7 +13,7 @@ Object::useCustomClass(), and the like. So, by unpacking a module into site's m
* You can create subclasses of base classes such as SiteTree to extend behaviour. * You can create subclasses of base classes such as SiteTree to extend behaviour.
* You can use Object::useCustomClass() to replace a built in class with a class of your own. * You can use Object::useCustomClass() to replace a built in class with a class of your own.
* You can use [a decorator](api:DataObjectDecorator) to extend or alter the behaviour of a built-in class without replacing * You can use [an extension](api:DataExtension) to extend or alter the behaviour of a built-in class without replacing
it. it.
* You can provide additional director rules to define your own controller for particular URLs. * You can provide additional director rules to define your own controller for particular URLs.

View File

@ -385,8 +385,8 @@ class Folder extends File {
/** /**
* Return the FieldSet used to edit this folder in the CMS. * Return the FieldSet used to edit this folder in the CMS.
* You can modify this fieldset by subclassing folder, or by creating a {@link DataObjectDecorator} * You can modify this fieldset by subclassing folder, or by creating a {@link DataExtension}
* and implemeting updateCMSFields(FieldSet $fields) on that decorator. * and implemeting updateCMSFields(FieldSet $fields) on that extension.
*/ */
function getCMSFields() { function getCMSFields() {
$fileList = new AssetTableField( $fileList = new AssetTableField(

231
model/DataExtension.php Executable file
View File

@ -0,0 +1,231 @@
<?php
/**
* An extension that adds additional functionality to a {@link DataObject}.
*
* @package sapphire
* @subpackage model
*/
abstract class DataExtension extends Extension {
/**
* Statics on a {@link DataObject} subclass
* which can be extended by an extension. This list is
* limited for security and performance reasons.
*
* Keys are the static names, and the values are whether or not the value is an array that should
* be merged.
*
* @var array
*/
protected static $extendable_statics = array(
'db' => true,
'has_one' => true,
'belongs_to' => true,
'indexes' => true,
'defaults' => true,
'has_many' => true,
'many_many' => true,
'belongs_many_many' => true,
'many_many_extraFields' => true,
'searchable_fields' => true,
'api_access' => false,
);
private static $extra_statics_loaded = array();
/**
* Load the extra static definitions for the given extension
* class name, called by {@link Object::add_extension()}
*
* @param string $class Class name of the owner class (or owner base class)
* @param string $extension Class name of the extension class
*/
public static function load_extra_statics($class, $extension) {
if(!empty(self::$extra_statics_loaded[$class][$extension])) return;
self::$extra_statics_loaded[$class][$extension] = true;
if(preg_match('/^([^(]*)/', $extension, $matches)) {
$extensionClass = $matches[1];
} else {
user_error("Bad extenion '$extension' - can't find classname", E_USER_WARNING);
return;
}
// @deprecated 2.4 - use extraStatics() now, not extraDBFields()
if(method_exists($extensionClass, 'extraDBFields')) {
user_error('DataExtension::extraDBFields() is deprecated. Please use extraStatics() instead.', E_USER_NOTICE);
$extraStaticsMethod = 'extraDBFields';
} else {
$extraStaticsMethod = 'extraStatics';
}
// If the extension has been manually applied to a subclass, we should ignore that.
if(Object::has_extension(get_parent_class($class), $extensionClass)) return;
$statics = call_user_func(array($extensionClass, $extraStaticsMethod), $class, $extension);
if($statics) {
foreach($statics as $name => $newVal) {
if(isset(self::$extendable_statics[$name])) {
// Array to be merged
if(self::$extendable_statics[$name]) {
$origVal = Object::uninherited_static($class, $name);
// Can't use add_static_var() here as it would merge the array rather than replacing
Object::set_static($class, $name, array_merge((array)$origVal, $newVal));
// Value to be overwritten
} else {
Object::set_static($class, $name, $newVal);
}
}
}
DataObject::$cache_has_own_table[$class] = null;
DataObject::$cache_has_own_table_field[$class] = null;
}
}
/**
* Edit the given query object to support queries for this extension
*
* @param SQLQuery $query Query to augment.
*/
function augmentSQL(SQLQuery &$query) {
}
/**
* Update the database schema as required by this extension.
*/
function augmentDatabase() {
}
/**
* Augment a write-record request.
*
* @param SQLQuery $manipulation Query to augment.
*/
function augmentWrite(&$manipulation) {
}
function onBeforeWrite() {
}
function onAfterWrite() {
}
function onBeforeDelete() {
}
function onAfterDelete() {
}
function requireDefaultRecords() {
}
function populateDefaults() {
}
function can($member) {
}
function canEdit($member) {
}
function canDelete($member) {
}
function canCreate($member) {
}
/**
* Define extra database fields
*
* Return a map where the keys are db, has_one, etc, and the values are
* additional fields/relations to be defined.
*
* @return array Returns a map where the keys are db, has_one, etc, and
* the values are additional fields/relations to be defined.
*/
function extraStatics() {
return array();
}
/**
* This function is used to provide modifications to the form in the CMS
* by the extension. By default, no changes are made. {@link DataObject->getCMSFields()}.
*
* Please consider using {@link updateFormFields()} to globally add
* formfields to the record. The method {@link updateCMSFields()}
* should just be used to add or modify tabs, or fields which
* are specific to the CMS-context.
*
* Caution: Use {@link FieldSet->addFieldToTab()} to add fields.
*
* @param FieldSet $fields FieldSet with a contained TabSet
*/
function updateCMSFields(FieldSet &$fields) {
}
/**
* This function is used to provide modifications to the form used
* for front end forms. {@link DataObject->getFrontEndFields()}
*
* Caution: Use {@link FieldSet->push()} to add fields.
*
* @param FieldSet $fields FieldSet without TabSet nesting
*/
function updateFrontEndFields(FieldSet &$fields) {
}
/**
* This is used to provide modifications to the form actions
* used in the CMS. {@link DataObject->getCMSActions()}.
*
* @param FieldSet $actions FieldSet
*/
function updateCMSActions(FieldSet &$actions) {
}
/**
* this function is used to provide modifications to the summary fields in CMS
* by the extension
* By default, the summaryField() of its owner will merge more fields defined in the extension's
* $extra_fields['summary_fields']
*/
function updateSummaryFields(&$fields){
$extra_fields = $this->extraStatics();
if(isset($extra_fields['summary_fields'])){
$summary_fields = $extra_fields['summary_fields'];
// if summary_fields were passed in numeric array,
// convert to an associative array
if($summary_fields && array_key_exists(0, $summary_fields)) {
$summary_fields = array_combine(array_values($summary_fields), array_values($summary_fields));
}
if($summary_fields) $fields = array_merge($fields, $summary_fields);
}
}
/**
* this function is used to provide modifications to the fields labels in CMS
* by the extension
* By default, the fieldLabels() of its owner will merge more fields defined in the extension's
* $extra_fields['field_labels']
*/
function updateFieldLabels(&$lables){
$extra_fields = $this->extraStatics();
if(isset($extra_fields['field_labels'])){
$field_labels = $extra_fields['field_labels'];
if($field_labels) $lables = array_merge($lables, $field_labels);
}
}
/**
* Clear any internal caches.
*/
function flushCache() {
}
}
?>

View File

@ -2,9 +2,9 @@
/** /**
* A single database record & abstract class for the data-access-model. * A single database record & abstract class for the data-access-model.
* *
* <h2>Extensions and Decorators</h2> * <h2>Extensions</h2>
* *
* See {@link Extension} and {@link DataObjectDecorator}. * See {@link Extension} and {@link DataExtension}.
* *
* <h2>Permission Control</h2> * <h2>Permission Control</h2>
* *
@ -890,7 +890,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* *
* This called after {@link $this->validate()}, so you can be sure that your data is valid. * This called after {@link $this->validate()}, so you can be sure that your data is valid.
* *
* @uses DataObjectDecorator->onBeforeWrite() * @uses DataExtension->onBeforeWrite()
*/ */
protected function onBeforeWrite() { protected function onBeforeWrite() {
$this->brokenOnWrite = false; $this->brokenOnWrite = false;
@ -905,7 +905,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* $this->changed will have a record * $this->changed will have a record
* database. Don't forget to call parent::onAfterWrite(), though! * database. Don't forget to call parent::onAfterWrite(), though!
* *
* @uses DataObjectDecorator->onAfterWrite() * @uses DataExtension->onAfterWrite()
*/ */
protected function onAfterWrite() { protected function onAfterWrite() {
$dummy = null; $dummy = null;
@ -917,7 +917,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* You can overload this to clean up or otherwise process data before delete this * You can overload this to clean up or otherwise process data before delete this
* record. Don't forget to call parent::onBeforeDelete(), though! * record. Don't forget to call parent::onBeforeDelete(), though!
* *
* @uses DataObjectDecorator->onBeforeDelete() * @uses DataExtension->onBeforeDelete()
*/ */
protected function onBeforeDelete() { protected function onBeforeDelete() {
$this->brokenOnDelete = false; $this->brokenOnDelete = false;
@ -935,7 +935,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* Will traverse the defaults of the current class and all its parent classes. * Will traverse the defaults of the current class and all its parent classes.
* Called by the constructor when creating new records. * Called by the constructor when creating new records.
* *
* @uses DataObjectDecorator->populateDefaults() * @uses DataExtension->populateDefaults()
*/ */
public function populateDefaults() { public function populateDefaults() {
$classes = array_reverse(ClassInfo::ancestry($this)); $classes = array_reverse(ClassInfo::ancestry($this));
@ -976,7 +976,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* - Extensions such as Versioned will ammend the database-write to ensure that a version is saved. * - Extensions such as Versioned will ammend the database-write to ensure that a version is saved.
* - Calls to {@link DataObjectLog} can be used to see everything that's been changed. * - Calls to {@link DataObjectLog} can be used to see everything that's been changed.
* *
* @uses DataObjectDecorator->augmentWrite() * @uses DataExtension->augmentWrite()
* *
* @param boolean $showDebug Show debugging information * @param boolean $showDebug Show debugging information
* @param boolean $forceInsert Run INSERT command rather than UPDATE, even if record already exists * @param boolean $forceInsert Run INSERT command rather than UPDATE, even if record already exists
@ -1183,7 +1183,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* Delete this data object. * Delete this data object.
* $this->onBeforeDelete() gets called. * $this->onBeforeDelete() gets called.
* Note that in Versioned objects, both Stage and Live will be deleted. * Note that in Versioned objects, both Stage and Live will be deleted.
* @uses DataObjectDecorator->augmentSQL() * @uses DataExtension->augmentSQL()
*/ */
public function delete() { public function delete() {
$this->brokenOnDelete = true; $this->brokenOnDelete = true;
@ -1983,7 +1983,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* which returns a {@link FieldSet} suitable for a {@link Form} object. * which returns a {@link FieldSet} suitable for a {@link Form} object.
* If not overloaded, we're using {@link scaffoldFormFields()} to automatically * If not overloaded, we're using {@link scaffoldFormFields()} to automatically
* generate this set. To customize, overload this method in a subclass * generate this set. To customize, overload this method in a subclass
* or decorate onto it by using {@link DataObjectDecorator->updateCMSFields()}. * or extended onto it by using {@link DataExtension->updateCMSFields()}.
* *
* <code> * <code>
* klass MyCustomClass extends DataObject { * klass MyCustomClass extends DataObject {
@ -2019,7 +2019,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
/** /**
* need to be overload by solid dataobject, so that the customised actions of that dataobject, * need to be overload by solid dataobject, so that the customised actions of that dataobject,
* including that dataobject's decorator customised actions could be added to the EditForm. * including that dataobject's extensions customised actions could be added to the EditForm.
* *
* @return an Empty FieldSet(); need to be overload by solid subclass * @return an Empty FieldSet(); need to be overload by solid subclass
*/ */
@ -2034,7 +2034,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* Used for simple frontend forms without relation editing * Used for simple frontend forms without relation editing
* or {@link TabSet} behaviour. Uses {@link scaffoldFormFields()} * or {@link TabSet} behaviour. Uses {@link scaffoldFormFields()}
* by default. To customize, either overload this method in your * by default. To customize, either overload this method in your
* subclass, or decorate it by {@link DataObjectDecorator->updateFrontEndFields()}. * subclass, or extend it by {@link DataExtension->updateFrontEndFields()}.
* *
* @todo Decide on naming for "website|frontend|site|page" and stick with it in the API * @todo Decide on naming for "website|frontend|site|page" and stick with it in the API
* *
@ -2277,7 +2277,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if($field == "LastEdited" && get_parent_class($this) == "DataObject") return "SS_Datetime"; if($field == "LastEdited" && get_parent_class($this) == "DataObject") return "SS_Datetime";
if($field == "Created" && get_parent_class($this) == "DataObject") return "SS_Datetime"; if($field == "Created" && get_parent_class($this) == "DataObject") return "SS_Datetime";
// Add fields from Versioned decorator // Add fields from Versioned extension
if($field == 'Version' && $this->hasExtension('Versioned')) { if($field == 'Version' && $this->hasExtension('Versioned')) {
return 'Int'; return 'Int';
} }
@ -2408,11 +2408,11 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
} }
/** /**
* Process tri-state responses from permission-alterting decorators. The decorators are * Process tri-state responses from permission-alterting extensions. The extensions are
* expected to return one of three values: * expected to return one of three values:
* *
* - false: Disallow this permission, regardless of what other decorators say * - false: Disallow this permission, regardless of what other extensions say
* - true: Allow this permission, as long as no other decorators return false * - true: Allow this permission, as long as no other extensions return false
* - NULL: Don't affect the outcome * - NULL: Don't affect the outcome
* *
* This method itself returns a tri-state value, and is designed to be used like this: * This method itself returns a tri-state value, and is designed to be used like this:
@ -2756,7 +2756,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
/** /**
* Like {@link buildSQL}, but applies the extension modifications. * Like {@link buildSQL}, but applies the extension modifications.
* *
* @uses DataObjectDecorator->augmentSQL() * @uses DataExtension->augmentSQL()
* *
* @param string $filter A filter to be inserted into the WHERE clause. * @param string $filter A filter to be inserted into the WHERE clause.
* @param string|array $sort A sort expression to be inserted into the ORDER BY clause. If omitted, self::$default_sort will be used. * @param string|array $sort A sort expression to be inserted into the ORDER BY clause. If omitted, self::$default_sort will be used.
@ -2936,7 +2936,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
/** /**
* Does the hard work for get_one() * Does the hard work for get_one()
* *
* @uses DataObjectDecorator->augmentSQL() * @uses DataExtension->augmentSQL()
* *
* @param string $filter A filter to be inserted into the WHERE clause * @param string $filter A filter to be inserted into the WHERE clause
* @param string $orderby A sort expression to be inserted into the ORDER BY clause. * @param string $orderby A sort expression to be inserted into the ORDER BY clause.
@ -3042,7 +3042,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
/** /**
* Check the database schema and update it as necessary. * Check the database schema and update it as necessary.
* *
* @uses DataObjectDecorator->augmentDatabase() * @uses DataExtension->augmentDatabase()
*/ */
public function requireTable() { public function requireTable() {
// Only build the table if we've actually got fields // Only build the table if we've actually got fields
@ -3091,7 +3091,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* this to add default records when the database is built, but make sure you * this to add default records when the database is built, but make sure you
* call parent::requireDefaultRecords(). * call parent::requireDefaultRecords().
* *
* @uses DataObjectDecorator->requireDefaultRecords() * @uses DataExtension->requireDefaultRecords()
*/ */
public function requireDefaultRecords() { public function requireDefaultRecords() {
$defaultRecords = $this->stat('default_records'); $defaultRecords = $this->stat('default_records');
@ -3172,8 +3172,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if(!$fields) $fields = array_keys($this->summaryFields()); if(!$fields) $fields = array_keys($this->summaryFields());
// we need to make sure the format is unified before // we need to make sure the format is unified before
// augmenting fields, so decorators can apply consistent checks // augmenting fields, so extensions can apply consistent checks
// but also after augmenting fields, because the decorator // but also after augmenting fields, because the extension
// might use the shorthand notation as well // might use the shorthand notation as well
// rewrite array, if it is using shorthand syntax // rewrite array, if it is using shorthand syntax
@ -3210,7 +3210,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$fields = $rewrite; $fields = $rewrite;
// apply DataObjectDecorators if present // apply DataExtensions if present
$this->extend('updateSearchableFields', $fields); $this->extend('updateSearchableFields', $fields);
return $fields; return $fields;
@ -3349,7 +3349,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
/** /**
* Sets a 'context object' that can be used to provide hints about how to process a particular get / get_one request. * Sets a 'context object' that can be used to provide hints about how to process a particular get / get_one request.
* In particular, DataObjectDecorators can use this to amend queries more effectively. * In particular, DataExtensions can use this to amend queries more effectively.
* Care must be taken to unset the context object after you're done with it, otherwise you will have a stale context, * Care must be taken to unset the context object after you're done with it, otherwise you will have a stale context,
* which could cause horrible bugs. * which could cause horrible bugs.
*/ */

View File

@ -1,235 +1,17 @@
<?php <?php
/** /**
* Plug-ins for additional functionality in your DataObjects. * @package sapphire
*
* Note: DataObjectDecorators are not actually Decorators in the GoF Design Patterns sense of the
* word. A better name for this class would be DataExtension. However, in the interests of
* backward compatibility we haven't renamed the class.
*
* @package sapphire
* @subpackage model * @subpackage model
* @deprecated 3.0 Use {@link DataExtension}.
*/ */
abstract class DataObjectDecorator extends Extension { abstract class DataObjectDecorator extends DataExtension {
/** public function __construct() {
* Statics on a {@link DataObject} subclass user_error(
* which can be decorated onto. This list is 'DataObjectDecorator is deprecated, please use DataExtension instead.',
* limited for security and performance reasons. E_USER_NOTICE
* );
* Keys are the static names, and the values are whether or not the value is an array that should parent::__construct();
* be merged.
*
* @var array
*/
protected static $decoratable_statics = array(
'db' => true,
'has_one' => true,
'belongs_to' => true,
'indexes' => true,
'defaults' => true,
'has_many' => true,
'many_many' => true,
'belongs_many_many' => true,
'many_many_extraFields' => true,
'searchable_fields' => true,
'api_access' => false,
);
private static $extra_statics_loaded = array();
/**
* Load the extra static definitions for the given extension
* class name, called by {@link Object::add_extension()}
*
* @param string $class Class name of the owner class (or owner base class)
* @param string $extension Class name of the extension class
*/
public static function load_extra_statics($class, $extension) {
if(!empty(self::$extra_statics_loaded[$class][$extension])) return;
self::$extra_statics_loaded[$class][$extension] = true;
if(preg_match('/^([^(]*)/', $extension, $matches)) {
$extensionClass = $matches[1];
} else {
user_error("Bad extenion '$extension' - can't find classname", E_USER_WARNING);
return;
}
// @deprecated 2.4 - use extraStatics() now, not extraDBFields()
if(method_exists($extensionClass, 'extraDBFields')) {
user_error('DataObjectDecorator::extraDBFields() is deprecated. Please use extraStatics() instead.', E_USER_NOTICE);
$extraStaticsMethod = 'extraDBFields';
} else {
$extraStaticsMethod = 'extraStatics';
}
// If the extension has been manually applied to a subclass, we should ignore that.
if(Object::has_extension(get_parent_class($class), $extensionClass)) return;
$statics = call_user_func(array($extensionClass, $extraStaticsMethod), $class, $extension);
if($statics) {
foreach($statics as $name => $newVal) {
if(isset(self::$decoratable_statics[$name])) {
// Array to be merged
if(self::$decoratable_statics[$name]) {
$origVal = Object::uninherited_static($class, $name);
// Can't use add_static_var() here as it would merge the array rather than replacing
Object::set_static($class, $name, array_merge((array)$origVal, $newVal));
// Value to be overwritten
} else {
Object::set_static($class, $name, $newVal);
}
}
}
DataObject::$cache_has_own_table[$class] = null;
DataObject::$cache_has_own_table_field[$class] = null;
}
}
/**
* Edit the given query object to support queries for this extension
*
* @param SQLQuery $query Query to augment.
*/
function augmentSQL(SQLQuery &$query) {
}
/**
* Update the database schema as required by this extension.
*/
function augmentDatabase() {
}
/**
* Augment a write-record request.
*
* @param SQLQuery $manipulation Query to augment.
*/
function augmentWrite(&$manipulation) {
}
function onBeforeWrite() {
}
function onAfterWrite() {
}
function onBeforeDelete() {
}
function onAfterDelete() {
}
function requireDefaultRecords() {
}
function populateDefaults() {
}
function can($member) {
}
function canEdit($member) {
}
function canDelete($member) {
}
function canCreate($member) {
}
/**
* Define extra database fields
*
* Return a map where the keys are db, has_one, etc, and the values are
* additional fields/relations to be defined.
*
* @return array Returns a map where the keys are db, has_one, etc, and
* the values are additional fields/relations to be defined.
*/
function extraStatics() {
return array();
}
/**
* This function is used to provide modifications to the form in the CMS
* by the decorator. By default, no changes are made. {@link DataObject->getCMSFields()}.
*
* Please consider using {@link updateFormFields()} to globally add
* formfields to the record. The method {@link updateCMSFields()}
* should just be used to add or modify tabs, or fields which
* are specific to the CMS-context.
*
* Caution: Use {@link FieldSet->addFieldToTab()} to add fields.
*
* @param FieldSet $fields FieldSet with a contained TabSet
*/
function updateCMSFields(FieldSet &$fields) {
}
/**
* This function is used to provide modifications to the form used
* for front end forms. {@link DataObject->getFrontEndFields()}
*
* Caution: Use {@link FieldSet->push()} to add fields.
*
* @param FieldSet $fields FieldSet without TabSet nesting
*/
function updateFrontEndFields(FieldSet &$fields) {
}
/**
* This is used to provide modifications to the form actions
* used in the CMS. {@link DataObject->getCMSActions()}.
*
* @param FieldSet $actions FieldSet
*/
function updateCMSActions(FieldSet &$actions) {
}
/**
* this function is used to provide modifications to the summary fields in CMS
* by the decorator
* By default, the summaryField() of its owner will merge more fields defined in the decorator's
* $extra_fields['summary_fields']
*/
function updateSummaryFields(&$fields){
$extra_fields = $this->extraStatics();
if(isset($extra_fields['summary_fields'])){
$summary_fields = $extra_fields['summary_fields'];
// if summary_fields were passed in numeric array,
// convert to an associative array
if($summary_fields && array_key_exists(0, $summary_fields)) {
$summary_fields = array_combine(array_values($summary_fields), array_values($summary_fields));
}
if($summary_fields) $fields = array_merge($fields, $summary_fields);
}
}
/**
* this function is used to provide modifications to the fields labels in CMS
* by the decorator
* By default, the fieldLabels() of its owner will merge more fields defined in the decorator's
* $extra_fields['field_labels']
*/
function updateFieldLabels(&$lables){
$extra_fields = $this->extraStatics();
if(isset($extra_fields['field_labels'])){
$field_labels = $extra_fields['field_labels'];
if($field_labels) $lables = array_merge($lables, $field_labels);
}
}
/**
* Clear any internal caches.
*/
function flushCache() {
} }
} }
?>

View File

@ -1,11 +1,11 @@
<?php <?php
/** /**
* DataObjects that use the Hierachy decorator can be be organised as a hierachy, with children and parents. * DataObjects that use the Hierachy extension can be be organised as a hierachy, with children and parents.
* The most obvious example of this is SiteTree. * The most obvious example of this is SiteTree.
* @package sapphire * @package sapphire
* @subpackage model * @subpackage model
*/ */
class Hierarchy extends DataObjectDecorator { class Hierarchy extends DataExtension {
protected $markedNodes; protected $markedNodes;

View File

@ -1,11 +1,11 @@
<?php <?php
/** /**
* The Versioned decorator allows your DataObjects to have several versions, allowing * The Versioned extension allows your DataObjects to have several versions, allowing
* you to rollback changes and view history. An example of this is the pages used in the CMS. * you to rollback changes and view history. An example of this is the pages used in the CMS.
* @package sapphire * @package sapphire
* @subpackage model * @subpackage model
*/ */
class Versioned extends DataObjectDecorator { class Versioned extends DataExtension {
/** /**
* An array of possible stages. * An array of possible stages.
* @var array * @var array
@ -42,7 +42,7 @@ class Versioned extends DataObjectDecorator {
/** /**
* Additional database columns for the new * Additional database columns for the new
* "_versions" table. Used in {@link augmentDatabase()} * "_versions" table. Used in {@link augmentDatabase()}
* and all Versioned calls decorating or creating * and all Versioned calls extending or creating
* SELECT statements. * SELECT statements.
* *
* @var array $db_for_versions_table * @var array $db_for_versions_table

View File

@ -11,7 +11,7 @@
* @package sapphire * @package sapphire
* @subpackage search * @subpackage search
*/ */
class FulltextSearchable extends DataObjectDecorator { class FulltextSearchable extends DataExtension {
/** /**
* @var String Comma-separated list of database column names * @var String Comma-separated list of database column names

View File

@ -359,7 +359,7 @@ class Group extends DataObject {
public function canEdit($member = null) { public function canEdit($member = null) {
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser(); if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser();
// decorated access checks // extended access checks
$results = $this->extend('canEdit', $member); $results = $this->extend('canEdit', $member);
if($results && is_array($results)) if(!min($results)) return false; if($results && is_array($results)) if(!min($results)) return false;
@ -389,7 +389,7 @@ class Group extends DataObject {
public function canView($member = null) { public function canView($member = null) {
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser(); if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser();
// decorated access checks // extended access checks
$results = $this->extend('canView', $member); $results = $this->extend('canView', $member);
if($results && is_array($results)) if(!min($results)) return false; if($results && is_array($results)) if(!min($results)) return false;
@ -402,7 +402,7 @@ class Group extends DataObject {
public function canDelete($member = null) { public function canDelete($member = null) {
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser(); if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser();
// decorated access checks // extended access checks
$results = $this->extend('canDelete', $member); $results = $this->extend('canDelete', $member);
if($results && is_array($results)) if(!min($results)) return false; if($results && is_array($results)) if(!min($results)) return false;

View File

@ -1264,7 +1264,7 @@ class Member extends DataObject {
function canView($member = null) { function canView($member = null) {
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser(); if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser();
// decorated access checks // extended access checks
$results = $this->extend('canView', $member); $results = $this->extend('canView', $member);
if($results && is_array($results)) { if($results && is_array($results)) {
if(!min($results)) return false; if(!min($results)) return false;
@ -1291,7 +1291,7 @@ class Member extends DataObject {
function canEdit($member = null) { function canEdit($member = null) {
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser(); if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser();
// decorated access checks // extended access checks
$results = $this->extend('canEdit', $member); $results = $this->extend('canEdit', $member);
if($results && is_array($results)) { if($results && is_array($results)) {
if(!min($results)) return false; if(!min($results)) return false;
@ -1318,7 +1318,7 @@ class Member extends DataObject {
function canDelete($member = null) { function canDelete($member = null) {
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser(); if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser();
// decorated access checks // extended access checks
$results = $this->extend('canDelete', $member); $results = $this->extend('canDelete', $member);
if($results && is_array($results)) { if($results && is_array($results)) {
if(!min($results)) return false; if(!min($results)) return false;

View File

@ -5,7 +5,7 @@
* *
* @todo tests for addStaticVars() * @todo tests for addStaticVars()
* @todo tests for setting statics which are not defined on the object as built-in PHP statics * @todo tests for setting statics which are not defined on the object as built-in PHP statics
* @todo tests for setting statics through decorators (#2387) * @todo tests for setting statics through extensions (#2387)
*/ */
class ObjectTest extends SapphireTest { class ObjectTest extends SapphireTest {

View File

@ -38,8 +38,8 @@ class FormScaffolderTest extends SapphireTest {
$article1 = $this->objFromFixture('FormScaffolderTest_Article', 'article1'); $article1 = $this->objFromFixture('FormScaffolderTest_Article', 'article1');
$fields = $article1->getCMSFields(); $fields = $article1->getCMSFields();
$this->assertNotNull( $this->assertNotNull(
$fields->dataFieldByName('AddedDecoratorField'), $fields->dataFieldByName('AddedExtensionField'),
'getCMSFields() includes decorated fields' 'getCMSFields() includes extended fields'
); );
} }
@ -102,16 +102,16 @@ class FormScaffolderTest_Tag extends DataObject implements TestOnly {
'Articles' => 'FormScaffolderTest_Article' 'Articles' => 'FormScaffolderTest_Article'
); );
} }
class FormScaffolderTest_ArticleDecorator extends DataObjectDecorator implements TestOnly { class FormScaffolderTest_ArticleExtension extends DataExtension implements TestOnly {
static $db = array( static $db = array(
'DecoratedField' => 'Varchar' 'ExtendedField' => 'Varchar'
); );
function updateCMSFields(&$fields) { function updateCMSFields(&$fields) {
$fields->addFieldToTab('Root.Main', $fields->addFieldToTab('Root.Main',
new TextField('AddedDecoratorField') new TextField('AddedExtensionField')
); );
} }
} }
DataObject::add_extension('FormScaffolderTest_Article', 'FormScaffolderTest_ArticleDecorator'); DataObject::add_extension('FormScaffolderTest_Article', 'FormScaffolderTest_ArticleExtension');
?> ?>

View File

@ -1,5 +1,5 @@
<?php <?php
class i18nTestModuleDecorator extends DataObjectDecorator { class i18nTestModuleExtension extends DataExtension {
function extraStatics() { function extraStatics() {
return array( return array(
'db' => array( 'db' => array(

View File

@ -1,26 +1,26 @@
<?php <?php
class DataObjectDecoratorTest extends SapphireTest { class DataExtensionTest extends SapphireTest {
static $fixture_file = 'DataObjectDecoratorTest.yml'; static $fixture_file = 'DataExtensionTest.yml';
protected $extraDataObjects = array( protected $extraDataObjects = array(
'DataObjectDecoratorTest_Member', 'DataExtensionTest_Member',
'DataObjectDecoratorTest_Player', 'DataExtensionTest_Player',
'DataObjectDecoratorTest_RelatedObject', 'DataExtensionTest_RelatedObject',
'DataObjectDecoratorTest_MyObject', 'DataExtensionTest_MyObject',
); );
protected $requiredExtensions = array( protected $requiredExtensions = array(
'DataObject' => array( 'DataObjectDecoratorTest_AppliedToDO' ), 'DataObject' => array( 'DataExtensionTest_AppliedToDO' ),
); );
function testOneToManyAssociationWithDecorator() { function testOneToManyAssociationWithExtension() {
// Fails in RestfulServerTest // Fails in RestfulServerTest
// Error: Object::__call() Method 'RelatedObjects' not found in class 'RestfulServerTest_Comment' // Error: Object::__call() Method 'RelatedObjects' not found in class 'RestfulServerTest_Comment'
$contact = new DataObjectDecoratorTest_Member(); $contact = new DataExtensionTest_Member();
$contact->Website = "http://www.example.com"; $contact->Website = "http://www.example.com";
$object = new DataObjectDecoratorTest_RelatedObject(); $object = new DataExtensionTest_RelatedObject();
$object->FieldOne = "Lorem ipsum dolor"; $object->FieldOne = "Lorem ipsum dolor";
$object->FieldTwo = "Random notes"; $object->FieldTwo = "Random notes";
@ -37,28 +37,28 @@ class DataObjectDecoratorTest extends SapphireTest {
unset($contact); unset($contact);
unset($object); unset($object);
$contact = DataObject::get_one("DataObjectDecoratorTest_Member", "\"Website\"='http://www.example.com'"); $contact = DataObject::get_one("DataExtensionTest_Member", "\"Website\"='http://www.example.com'");
$object = DataObject::get_one('DataObjectDecoratorTest_RelatedObject', "\"ContactID\" = {$contactID}"); $object = DataObject::get_one('DataExtensionTest_RelatedObject', "\"ContactID\" = {$contactID}");
$this->assertNotNull($object, 'Related object not null'); $this->assertNotNull($object, 'Related object not null');
$this->assertType('DataObjectDecoratorTest_Member', $object->Contact(), 'Related contact is a member dataobject'); $this->assertType('DataExtensionTest_Member', $object->Contact(), 'Related contact is a member dataobject');
$this->assertType('DataObjectDecoratorTest_Member', $object->getComponent('Contact'), 'getComponent does the same thing as Contact()'); $this->assertType('DataExtensionTest_Member', $object->getComponent('Contact'), 'getComponent does the same thing as Contact()');
$this->assertType('DataObjectDecoratorTest_RelatedObject', $contact->RelatedObjects()->First()); $this->assertType('DataExtensionTest_RelatedObject', $contact->RelatedObjects()->First());
$this->assertEquals("Lorem ipsum dolor", $contact->RelatedObjects()->First()->FieldOne); $this->assertEquals("Lorem ipsum dolor", $contact->RelatedObjects()->First()->FieldOne);
$this->assertEquals("Random notes", $contact->RelatedObjects()->First()->FieldTwo); $this->assertEquals("Random notes", $contact->RelatedObjects()->First()->FieldTwo);
$contact->delete(); $contact->delete();
} }
function testManyManyAssociationWithDecorator() { function testManyManyAssociationWithExtension() {
$parent = new DataObjectDecoratorTest_MyObject(); $parent = new DataExtensionTest_MyObject();
$parent->Title = 'My Title'; $parent->Title = 'My Title';
$parent->write(); $parent->write();
$this->assertEquals(0, $parent->Faves()->Count()); $this->assertEquals(0, $parent->Faves()->Count());
$obj1 = $this->objFromFixture('DataObjectDecoratorTest_RelatedObject', 'obj1'); $obj1 = $this->objFromFixture('DataExtensionTest_RelatedObject', 'obj1');
$obj2 = $this->objFromFixture('DataObjectDecoratorTest_RelatedObject', 'obj2'); $obj2 = $this->objFromFixture('DataExtensionTest_RelatedObject', 'obj2');
$parent->Faves()->add($obj1->ID); $parent->Faves()->add($obj1->ID);
$this->assertEquals(1, $parent->Faves()->Count()); $this->assertEquals(1, $parent->Faves()->Count());
@ -71,17 +71,17 @@ class DataObjectDecoratorTest extends SapphireTest {
} }
/** /**
* Test {@link Object::add_extension()} has loaded DataObjectDecorator statics correctly. * Test {@link Object::add_extension()} has loaded DataExtension statics correctly.
*/ */
function testAddExtensionLoadsStatics() { function testAddExtensionLoadsStatics() {
// Object::add_extension() will load DOD statics directly, so let's try adding a decorator on the fly // Object::add_extension() will load DOD statics directly, so let's try adding a extension on the fly
Object::add_extension('DataObjectDecoratorTest_Player', 'DataObjectDecoratorTest_PlayerDecorator'); Object::add_extension('DataExtensionTest_Player', 'DataExtensionTest_PlayerExtension');
// Now that we've just added the decorator, we need to rebuild the database // Now that we've just added the extension, we need to rebuild the database
$this->resetDBSchema(true); $this->resetDBSchema(true);
// Create a test record with decorated fields, writing to the DB // Create a test record with extended fields, writing to the DB
$player = new DataObjectDecoratorTest_Player(); $player = new DataExtensionTest_Player();
$player->setField('Name', 'Joe'); $player->setField('Name', 'Joe');
$player->setField('DateBirth', '1990-5-10'); $player->setField('DateBirth', '1990-5-10');
$player->Address = '123 somewhere street'; $player->Address = '123 somewhere street';
@ -89,66 +89,66 @@ class DataObjectDecoratorTest extends SapphireTest {
unset($player); unset($player);
// Pull the record out of the DB and examine the decorated fields // Pull the record out of the DB and examine the extended fields
$player = DataObject::get_one('DataObjectDecoratorTest_Player', "\"Name\" = 'Joe'"); $player = DataObject::get_one('DataExtensionTest_Player', "\"Name\" = 'Joe'");
$this->assertEquals($player->DateBirth, '1990-05-10'); $this->assertEquals($player->DateBirth, '1990-05-10');
$this->assertEquals($player->Address, '123 somewhere street'); $this->assertEquals($player->Address, '123 somewhere street');
$this->assertEquals($player->Status, 'Goalie'); $this->assertEquals($player->Status, 'Goalie');
} }
/** /**
* Test that DataObject::$api_access can be set to true via a decorator * Test that DataObject::$api_access can be set to true via a extension
*/ */
function testApiAccessCanBeDecorated() { function testApiAccessCanBeExtended() {
$this->assertTrue(Object::get_static('DataObjectDecoratorTest_Member', 'api_access')); $this->assertTrue(Object::get_static('DataExtensionTest_Member', 'api_access'));
} }
function testPermissionDecoration() { function testPermissionExtension() {
// testing behaviour in isolation, too many sideeffects and other checks // testing behaviour in isolation, too many sideeffects and other checks
// in SiteTree->can*() methods to test one single feature reliably with them // in SiteTree->can*() methods to test one single feature reliably with them
$obj = $this->objFromFixture('DataObjectDecoratorTest_MyObject', 'object1'); $obj = $this->objFromFixture('DataExtensionTest_MyObject', 'object1');
$websiteuser = $this->objFromFixture('Member', 'websiteuser'); $websiteuser = $this->objFromFixture('Member', 'websiteuser');
$admin = $this->objFromFixture('Member', 'admin'); $admin = $this->objFromFixture('Member', 'admin');
$this->assertFalse( $this->assertFalse(
$obj->canOne($websiteuser), $obj->canOne($websiteuser),
'Both decorators return true, but original method returns false' 'Both extensions return true, but original method returns false'
); );
$this->assertFalse( $this->assertFalse(
$obj->canTwo($websiteuser), $obj->canTwo($websiteuser),
'One decorator returns false, original returns true, but decorator takes precedence' 'One extension returns false, original returns true, but extension takes precedence'
); );
$this->assertTrue( $this->assertTrue(
$obj->canThree($admin), $obj->canThree($admin),
'Undefined decorator methods returning NULL dont influence the original method' 'Undefined extension methods returning NULL dont influence the original method'
); );
} }
function testPopulateDefaults() { function testPopulateDefaults() {
$obj = new DataObjectDecoratorTest_Member(); $obj = new DataExtensionTest_Member();
$this->assertEquals( $this->assertEquals(
$obj->Phone, $obj->Phone,
'123', '123',
'Defaults can be populated through decorator' 'Defaults can be populated through extension'
); );
} }
/** /**
* Test that DataObject::dbObject() works for fields applied by a decorator * Test that DataObject::dbObject() works for fields applied by a extension
*/ */
function testDbObjectOnDecoratedFields() { function testDbObjectOnExtendedFields() {
$member = $this->objFromFixture('DataObjectDecoratorTest_Member', 'member1'); $member = $this->objFromFixture('DataExtensionTest_Member', 'member1');
$this->assertNotNull($member->dbObject('Website')); $this->assertNotNull($member->dbObject('Website'));
$this->assertType('Varchar', $member->dbObject('Website')); $this->assertType('Varchar', $member->dbObject('Website'));
} }
function testDecoratorCanBeAppliedToDataObject() { function testExtensionCanBeAppliedToDataObject() {
$do = new DataObject(); $do = new DataObject();
$mo = new DataObjectDecoratorTest_MyObject(); $mo = new DataExtensionTest_MyObject();
$this->assertTrue($do->hasMethod('testMethodApplied')); $this->assertTrue($do->hasMethod('testMethodApplied'));
$this->assertTrue($mo->hasMethod('testMethodApplied')); $this->assertTrue($mo->hasMethod('testMethodApplied'));
@ -158,7 +158,7 @@ class DataObjectDecoratorTest extends SapphireTest {
} }
} }
class DataObjectDecoratorTest_Member extends DataObject implements TestOnly { class DataExtensionTest_Member extends DataObject implements TestOnly {
static $db = array( static $db = array(
"Name" => "Varchar", "Name" => "Varchar",
@ -167,7 +167,7 @@ class DataObjectDecoratorTest_Member extends DataObject implements TestOnly {
} }
class DataObjectDecoratorTest_Player extends DataObject implements TestOnly { class DataExtensionTest_Player extends DataObject implements TestOnly {
static $db = array( static $db = array(
'Name' => 'Varchar' 'Name' => 'Varchar'
@ -175,12 +175,12 @@ class DataObjectDecoratorTest_Player extends DataObject implements TestOnly {
} }
class DataObjectDecoratorTest_PlayerDecorator extends DataObjectDecorator implements TestOnly { class DataExtensionTest_PlayerExtension extends DataExtension implements TestOnly {
function extraStatics($class) { function extraStatics($class) {
// Only add these extensions if the $class is set to DataObjectDecoratorTest_Player, to // Only add these extensions if the $class is set to DataExtensionTest_Player, to
// test that the argument works. // test that the argument works.
if($class == 'DataObjectDecoratorTest_Player') { if($class == 'DataExtensionTest_Player') {
return array( return array(
'db' => array( 'db' => array(
'Address' => 'Text', 'Address' => 'Text',
@ -196,7 +196,7 @@ class DataObjectDecoratorTest_PlayerDecorator extends DataObjectDecorator implem
} }
class DataObjectDecoratorTest_ContactRole extends DataObjectDecorator implements TestOnly { class DataExtensionTest_ContactRole extends DataExtension implements TestOnly {
function extraStatics() { function extraStatics() {
return array( return array(
@ -205,7 +205,7 @@ class DataObjectDecoratorTest_ContactRole extends DataObjectDecorator implements
'Phone' => 'Varchar(255)', 'Phone' => 'Varchar(255)',
), ),
'has_many' => array( 'has_many' => array(
'RelatedObjects' => 'DataObjectDecoratorTest_RelatedObject' 'RelatedObjects' => 'DataExtensionTest_RelatedObject'
), ),
'defaults' => array( 'defaults' => array(
'Phone' => '123' 'Phone' => '123'
@ -216,7 +216,7 @@ class DataObjectDecoratorTest_ContactRole extends DataObjectDecorator implements
} }
class DataObjectDecoratorTest_RelatedObject extends DataObject implements TestOnly { class DataExtensionTest_RelatedObject extends DataObject implements TestOnly {
static $db = array( static $db = array(
"FieldOne" => "Varchar", "FieldOne" => "Varchar",
@ -224,21 +224,21 @@ class DataObjectDecoratorTest_RelatedObject extends DataObject implements TestOn
); );
static $has_one = array( static $has_one = array(
"Contact" => "DataObjectDecoratorTest_Member" "Contact" => "DataExtensionTest_Member"
); );
} }
DataObject::add_extension('DataObjectDecoratorTest_Member', 'DataObjectDecoratorTest_ContactRole'); DataObject::add_extension('DataExtensionTest_Member', 'DataExtensionTest_ContactRole');
class DataObjectDecoratorTest_MyObject extends DataObject implements TestOnly { class DataExtensionTest_MyObject extends DataObject implements TestOnly {
static $db = array( static $db = array(
'Title' => 'Varchar', 'Title' => 'Varchar',
); );
function canOne($member = null) { function canOne($member = null) {
// decorated access checks // extended access checks
$results = $this->extend('canOne', $member); $results = $this->extend('canOne', $member);
if($results && is_array($results)) if(!min($results)) return false; if($results && is_array($results)) if(!min($results)) return false;
@ -246,7 +246,7 @@ class DataObjectDecoratorTest_MyObject extends DataObject implements TestOnly {
} }
function canTwo($member = null) { function canTwo($member = null) {
// decorated access checks // extended access checks
$results = $this->extend('canTwo', $member); $results = $this->extend('canTwo', $member);
if($results && is_array($results)) if(!min($results)) return false; if($results && is_array($results)) if(!min($results)) return false;
@ -254,7 +254,7 @@ class DataObjectDecoratorTest_MyObject extends DataObject implements TestOnly {
} }
function canThree($member = null) { function canThree($member = null) {
// decorated access checks // extended access checks
$results = $this->extend('canThree', $member); $results = $this->extend('canThree', $member);
if($results && is_array($results)) if(!min($results)) return false; if($results && is_array($results)) if(!min($results)) return false;
@ -262,7 +262,7 @@ class DataObjectDecoratorTest_MyObject extends DataObject implements TestOnly {
} }
} }
class DataObjectDecoratorTest_Ext1 extends DataObjectDecorator implements TestOnly { class DataExtensionTest_Ext1 extends DataExtension implements TestOnly {
function canOne($member = null) { function canOne($member = null) {
return true; return true;
@ -277,7 +277,7 @@ class DataObjectDecoratorTest_Ext1 extends DataObjectDecorator implements TestOn
} }
class DataObjectDecoratorTest_Ext2 extends DataObjectDecorator implements TestOnly { class DataExtensionTest_Ext2 extends DataExtension implements TestOnly {
function canOne($member = null) { function canOne($member = null) {
return true; return true;
@ -292,23 +292,23 @@ class DataObjectDecoratorTest_Ext2 extends DataObjectDecorator implements TestOn
} }
class DataObjectDecoratorTest_Faves extends DataObjectDecorator implements TestOnly { class DataExtensionTest_Faves extends DataExtension implements TestOnly {
public function extraStatics() { public function extraStatics() {
return array( return array(
'many_many' => array( 'many_many' => array(
'Faves' => 'DataObjectDecoratorTest_RelatedObject' 'Faves' => 'DataExtensionTest_RelatedObject'
) )
); );
} }
} }
class DataObjectDecoratorTest_AppliedToDO extends DataObjectDecorator implements TestOnly { class DataExtensionTest_AppliedToDO extends DataExtension implements TestOnly {
public function testMethodApplied() { public function testMethodApplied() {
return "hello world"; return "hello world";
} }
} }
DataObject::add_extension('DataObjectDecoratorTest_MyObject', 'DataObjectDecoratorTest_Ext1'); DataObject::add_extension('DataExtensionTest_MyObject', 'DataExtensionTest_Ext1');
DataObject::add_extension('DataObjectDecoratorTest_MyObject', 'DataObjectDecoratorTest_Ext2'); DataObject::add_extension('DataExtensionTest_MyObject', 'DataExtensionTest_Ext2');
DataObject::add_extension('DataObjectDecoratorTest_MyObject', 'DataObjectDecoratorTest_Faves'); DataObject::add_extension('DataExtensionTest_MyObject', 'DataExtensionTest_Faves');
?> ?>

View File

@ -1,4 +1,4 @@
DataObjectDecoratorTest_RelatedObject: DataExtensionTest_RelatedObject:
obj1: obj1:
FieldOne: Obj1 FieldOne: Obj1
obj2: obj2:
@ -15,10 +15,10 @@ Member:
Groups: =>Group.admingroup Groups: =>Group.admingroup
websiteuser: websiteuser:
Email: websiteuser@test.com Email: websiteuser@test.com
DataObjectDecoratorTest_Member: DataExtensionTest_Member:
member1: member1:
Name: Sam Name: Sam
Website: http://www.example.org Website: http://www.example.org
DataObjectDecoratorTest_MyObject: DataExtensionTest_MyObject:
object1: object1:
Title: Object 1 Title: Object 1

View File

@ -22,11 +22,11 @@ DataObjectTest_SubTeam:
subteam1: subteam1:
Title: Subteam 1 Title: Subteam 1
SubclassDatabaseField: Subclassed 1 SubclassDatabaseField: Subclassed 1
DecoratedDatabaseField: Decorated 1 ExtendedDatabaseField: Extended 1
subteam2_with_player_relation: subteam2_with_player_relation:
Title: Subteam 2 Title: Subteam 2
SubclassDatabaseField: Subclassed 2 SubclassDatabaseField: Subclassed 2
DecoratedHasOneRelationship: =>DataObjectTest_Player.player1 ExtendeHasOneRelationship: =>DataObjectTest_Player.player1
subteam3_with_empty_fields: subteam3_with_empty_fields:
Title: Subteam 3 Title: Subteam 3

View File

@ -455,9 +455,9 @@ class DataObjectTest extends SapphireTest {
//$this->assertFalse($teamInstance->hasField('SubclassDatabaseField'), 'hasField() doesnt find subclass fields in parentclass instances'); //$this->assertFalse($teamInstance->hasField('SubclassDatabaseField'), 'hasField() doesnt find subclass fields in parentclass instances');
$this->assertTrue($teamInstance->hasField('DynamicField'), 'hasField() finds dynamic getters in instances'); $this->assertTrue($teamInstance->hasField('DynamicField'), 'hasField() finds dynamic getters in instances');
$this->assertTrue($teamInstance->hasField('HasOneRelationshipID'), 'hasField() finds foreign keys in instances'); $this->assertTrue($teamInstance->hasField('HasOneRelationshipID'), 'hasField() finds foreign keys in instances');
$this->assertTrue($teamInstance->hasField('DecoratedDatabaseField'), 'hasField() finds decorated fields in instances'); $this->assertTrue($teamInstance->hasField('ExtendedDatabaseField'), 'hasField() finds extended fields in instances');
$this->assertTrue($teamInstance->hasField('DecoratedHasOneRelationshipID'), 'hasField() finds decorated foreign keys in instances'); $this->assertTrue($teamInstance->hasField('ExtendedHasOneRelationshipID'), 'hasField() finds extended foreign keys in instances');
//$this->assertTrue($teamInstance->hasField('DecoratedDynamicField'), 'hasField() includes decorated dynamic getters in instances'); //$this->assertTrue($teamInstance->hasField('ExtendedDynamicField'), 'hasField() includes extended dynamic getters in instances');
/* hasField() subclass checks */ /* hasField() subclass checks */
$this->assertTrue($subteamInstance->hasField('ID'), 'hasField() finds built-in fields in subclass instances'); $this->assertTrue($subteamInstance->hasField('ID'), 'hasField() finds built-in fields in subclass instances');
@ -466,8 +466,8 @@ class DataObjectTest extends SapphireTest {
$this->assertTrue($subteamInstance->hasField('SubclassDatabaseField'), 'hasField() finds custom fields in subclass instances'); $this->assertTrue($subteamInstance->hasField('SubclassDatabaseField'), 'hasField() finds custom fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('DynamicField'), 'hasField() finds dynamic getters in subclass instances'); $this->assertTrue($subteamInstance->hasField('DynamicField'), 'hasField() finds dynamic getters in subclass instances');
$this->assertTrue($subteamInstance->hasField('HasOneRelationshipID'), 'hasField() finds foreign keys in subclass instances'); $this->assertTrue($subteamInstance->hasField('HasOneRelationshipID'), 'hasField() finds foreign keys in subclass instances');
$this->assertTrue($subteamInstance->hasField('DecoratedDatabaseField'), 'hasField() finds decorated fields in subclass instances'); $this->assertTrue($subteamInstance->hasField('ExtendedDatabaseField'), 'hasField() finds extended fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('DecoratedHasOneRelationshipID'), 'hasField() finds decorated foreign keys in subclass instances'); $this->assertTrue($subteamInstance->hasField('ExtendedHasOneRelationshipID'), 'hasField() finds extended foreign keys in subclass instances');
/* hasDatabaseField() singleton checks */ /* hasDatabaseField() singleton checks */
//$this->assertTrue($teamSingleton->hasDatabaseField('ID'), 'hasDatabaseField() finds built-in fields in singletons'); //$this->assertTrue($teamSingleton->hasDatabaseField('ID'), 'hasDatabaseField() finds built-in fields in singletons');
@ -481,9 +481,9 @@ class DataObjectTest extends SapphireTest {
$this->assertFalse($teamInstance->hasDatabaseField('SubclassDatabaseField'), 'hasDatabaseField() doesnt find subclass fields in parentclass instances'); $this->assertFalse($teamInstance->hasDatabaseField('SubclassDatabaseField'), 'hasDatabaseField() doesnt find subclass fields in parentclass instances');
//$this->assertFalse($teamInstance->hasDatabaseField('DynamicField'), 'hasDatabaseField() doesnt dynamic getters in instances'); //$this->assertFalse($teamInstance->hasDatabaseField('DynamicField'), 'hasDatabaseField() doesnt dynamic getters in instances');
$this->assertTrue($teamInstance->hasDatabaseField('HasOneRelationshipID'), 'hasDatabaseField() finds foreign keys in instances'); $this->assertTrue($teamInstance->hasDatabaseField('HasOneRelationshipID'), 'hasDatabaseField() finds foreign keys in instances');
$this->assertTrue($teamInstance->hasDatabaseField('DecoratedDatabaseField'), 'hasDatabaseField() finds decorated fields in instances'); $this->assertTrue($teamInstance->hasDatabaseField('ExtendedDatabaseField'), 'hasDatabaseField() finds extended fields in instances');
$this->assertTrue($teamInstance->hasDatabaseField('DecoratedHasOneRelationshipID'), 'hasDatabaseField() finds decorated foreign keys in instances'); $this->assertTrue($teamInstance->hasDatabaseField('ExtendedHasOneRelationshipID'), 'hasDatabaseField() finds extended foreign keys in instances');
$this->assertFalse($teamInstance->hasDatabaseField('DecoratedDynamicField'), 'hasDatabaseField() doesnt include decorated dynamic getters in instances'); $this->assertFalse($teamInstance->hasDatabaseField('ExtendedDynamicField'), 'hasDatabaseField() doesnt include extended dynamic getters in instances');
/* hasDatabaseField() subclass checks */ /* hasDatabaseField() subclass checks */
$this->assertTrue($subteamInstance->hasField('DatabaseField'), 'hasField() finds custom fields in subclass instances'); $this->assertTrue($subteamInstance->hasField('DatabaseField'), 'hasField() finds custom fields in subclass instances');
@ -507,12 +507,12 @@ class DataObjectTest extends SapphireTest {
//'LastEdited', //'LastEdited',
'Title', 'Title',
'DatabaseField', 'DatabaseField',
'DecoratedDatabaseField', 'ExtendedDatabaseField',
'CaptainID', 'CaptainID',
'HasOneRelationshipID', 'HasOneRelationshipID',
'DecoratedHasOneRelationshipID' 'ExtendedHasOneRelationshipID'
), ),
'inheritedDatabaseFields() contains all fields defined on instance, including base fields, decorated fields and foreign keys' 'inheritedDatabaseFields() contains all fields defined on instance, including base fields, extended fields and foreign keys'
); );
$this->assertEquals( $this->assertEquals(
@ -524,12 +524,12 @@ class DataObjectTest extends SapphireTest {
'LastEdited', 'LastEdited',
'Title', 'Title',
'DatabaseField', 'DatabaseField',
'DecoratedDatabaseField', 'ExtendedDatabaseField',
'CaptainID', 'CaptainID',
'HasOneRelationshipID', 'HasOneRelationshipID',
'DecoratedHasOneRelationshipID' 'ExtendedHasOneRelationshipID'
), ),
'databaseFields() contains only fields defined on instance, including base fields, decorated fields and foreign keys' 'databaseFields() contains only fields defined on instance, including base fields, extended fields and foreign keys'
); );
$this->assertEquals( $this->assertEquals(
@ -542,12 +542,12 @@ class DataObjectTest extends SapphireTest {
'SubclassDatabaseField', 'SubclassDatabaseField',
'Title', 'Title',
'DatabaseField', 'DatabaseField',
'DecoratedDatabaseField', 'ExtendedDatabaseField',
'CaptainID', 'CaptainID',
'HasOneRelationshipID', 'HasOneRelationshipID',
'DecoratedHasOneRelationshipID', 'ExtendedHasOneRelationshipID',
), ),
'inheritedDatabaseFields() on subclass contains all fields defined on instance, including base fields, decorated fields and foreign keys' 'inheritedDatabaseFields() on subclass contains all fields defined on instance, including base fields, extended fields and foreign keys'
); );
$this->assertEquals( $this->assertEquals(
@ -803,8 +803,8 @@ class DataObjectTest extends SapphireTest {
"hasOwnDatabaseField() doesn't detect non-existend fields" "hasOwnDatabaseField() doesn't detect non-existend fields"
); );
$this->assertTrue( $this->assertTrue(
$team->hasDatabaseField('DecoratedDatabaseField'), $team->hasDatabaseField('ExtendedDatabaseField'),
"hasOwnDatabaseField() works with decorated fields" "hasOwnDatabaseField() works with extended fields"
); );
$this->assertFalse( $this->assertFalse(
$team->hasDatabaseField('SubclassDatabaseField'), $team->hasDatabaseField('SubclassDatabaseField'),
@ -1087,21 +1087,21 @@ class DataObjectTest_FieldlessSubTable extends DataObjectTest_Team implements Te
} }
class DataObjectTest_Team_Decorator extends DataObjectDecorator implements TestOnly { class DataObjectTest_Team_Extension extends DataExtension implements TestOnly {
function extraStatics() { function extraStatics() {
return array( return array(
'db' => array( 'db' => array(
'DecoratedDatabaseField' => 'Varchar' 'ExtendedDatabaseField' => 'Varchar'
), ),
'has_one' => array( 'has_one' => array(
'DecoratedHasOneRelationship' => 'DataObjectTest_Player' 'ExtendedHasOneRelationship' => 'DataObjectTest_Player'
) )
); );
} }
function getDecoratedDynamicField() { function getExtendedDynamicField() {
return "decorated dynamic field"; return "extended dynamic field";
} }
} }
@ -1159,6 +1159,6 @@ class DataObjectTest_TeamComment extends DataObject {
); );
} }
DataObject::add_extension('DataObjectTest_Team', 'DataObjectTest_Team_Decorator'); DataObject::add_extension('DataObjectTest_Team', 'DataObjectTest_Team_Extension');
?> ?>

View File

@ -23,11 +23,11 @@ DataObjectTest_SubTeam:
subteam1: subteam1:
Title: Subteam 1 Title: Subteam 1
SubclassDatabaseField: Subclassed 1 SubclassDatabaseField: Subclassed 1
DecoratedDatabaseField: Decorated 1 ExtendedDatabaseField: Extended 1
subteam2_with_player_relation: subteam2_with_player_relation:
Title: Subteam 2 Title: Subteam 2
SubclassDatabaseField: Subclassed 2 SubclassDatabaseField: Subclassed 2
DecoratedHasOneRelationship: =>DataObjectTest_Player.player1 ExtendedHasOneRelationship: =>DataObjectTest_Player.player1
subteam3_with_empty_fields: subteam3_with_empty_fields:
Title: Subteam 3 Title: Subteam 3

View File

@ -457,7 +457,7 @@ class MemberTest extends FunctionalTest {
$this->session()->inst_set('loggedInAs', null); $this->session()->inst_set('loggedInAs', null);
} }
public function testDecoratedCan() { public function testExtendedCan() {
$extensions = $this->removeExtensions(Object::get_extensions('Member')); $extensions = $this->removeExtensions(Object::get_extensions('Member'));
$member = $this->objFromFixture('Member', 'test'); $member = $this->objFromFixture('Member', 'test');
@ -466,7 +466,7 @@ class MemberTest extends FunctionalTest {
$this->assertFalse($member->canDelete()); $this->assertFalse($member->canDelete());
$this->assertFalse($member->canEdit()); $this->assertFalse($member->canEdit());
/* Apply a decorator that allows viewing in any case (most likely the case for member profiles) */ /* Apply a extension that allows viewing in any case (most likely the case for member profiles) */
Object::add_extension('Member', 'MemberTest_ViewingAllowedExtension'); Object::add_extension('Member', 'MemberTest_ViewingAllowedExtension');
$member2 = $this->objFromFixture('Member', 'staffmember'); $member2 = $this->objFromFixture('Member', 'staffmember');
@ -474,7 +474,7 @@ class MemberTest extends FunctionalTest {
$this->assertFalse($member2->canDelete()); $this->assertFalse($member2->canDelete());
$this->assertFalse($member2->canEdit()); $this->assertFalse($member2->canEdit());
/* Apply a decorator that denies viewing of the Member */ /* Apply a extension that denies viewing of the Member */
Object::remove_extension('Member', 'MemberTest_ViewingAllowedExtension'); Object::remove_extension('Member', 'MemberTest_ViewingAllowedExtension');
Object::add_extension('Member', 'MemberTest_ViewingDeniedExtension'); Object::add_extension('Member', 'MemberTest_ViewingDeniedExtension');
$member3 = $this->objFromFixture('Member', 'managementmember'); $member3 = $this->objFromFixture('Member', 'managementmember');
@ -483,7 +483,7 @@ class MemberTest extends FunctionalTest {
$this->assertFalse($member3->canDelete()); $this->assertFalse($member3->canDelete());
$this->assertFalse($member3->canEdit()); $this->assertFalse($member3->canEdit());
/* Apply a decorator that allows viewing and editing but denies deletion */ /* Apply a extension that allows viewing and editing but denies deletion */
Object::remove_extension('Member', 'MemberTest_ViewingDeniedExtension'); Object::remove_extension('Member', 'MemberTest_ViewingDeniedExtension');
Object::add_extension('Member', 'MemberTest_EditingAllowedDeletingDeniedExtension'); Object::add_extension('Member', 'MemberTest_EditingAllowedDeletingDeniedExtension');
$member4 = $this->objFromFixture('Member', 'accountingmember'); $member4 = $this->objFromFixture('Member', 'accountingmember');
@ -589,21 +589,21 @@ class MemberTest extends FunctionalTest {
} }
} }
class MemberTest_ViewingAllowedExtension extends DataObjectDecorator implements TestOnly { class MemberTest_ViewingAllowedExtension extends DataExtension implements TestOnly {
public function canView() { public function canView() {
return true; return true;
} }
} }
class MemberTest_ViewingDeniedExtension extends DataObjectDecorator implements TestOnly { class MemberTest_ViewingDeniedExtension extends DataExtension implements TestOnly {
public function canView() { public function canView() {
return false; return false;
} }
} }
class MemberTest_EditingAllowedDeletingDeniedExtension extends DataObjectDecorator implements TestOnly { class MemberTest_EditingAllowedDeletingDeniedExtension extends DataExtension implements TestOnly {
public function canView() { public function canView() {
return true; return true;

View File

@ -2924,7 +2924,7 @@ class SSTemplateParser extends Parser {
* 'Arguments' an array of the Argument match rule result arrays * 'Arguments' an array of the Argument match rule result arrays
* *
* Once a block has successfully been matched against, it will then look for the actual handler, which should * Once a block has successfully been matched against, it will then look for the actual handler, which should
* be on this class (either defined or decorated on) as ClosedBlock_Handler_Name(&$res), where Name is the * be on this class (either defined or extended on) as ClosedBlock_Handler_Name(&$res), where Name is the
* tag name, first letter captialized (i.e Control, Loop, With, etc). * tag name, first letter captialized (i.e Control, Loop, With, etc).
* *
* This function will be called with the match rule result array as it's first argument. It should return * This function will be called with the match rule result array as it's first argument. It should return

View File

@ -601,7 +601,7 @@ class SSTemplateParser extends Parser {
* 'Arguments' an array of the Argument match rule result arrays * 'Arguments' an array of the Argument match rule result arrays
* *
* Once a block has successfully been matched against, it will then look for the actual handler, which should * Once a block has successfully been matched against, it will then look for the actual handler, which should
* be on this class (either defined or decorated on) as ClosedBlock_Handler_Name(&$res), where Name is the * be on this class (either defined or extended on) as ClosedBlock_Handler_Name(&$res), where Name is the
* tag name, first letter captialized (i.e Control, Loop, With, etc). * tag name, first letter captialized (i.e Control, Loop, With, etc).
* *
* This function will be called with the match rule result array as it's first argument. It should return * This function will be called with the match rule result array as it's first argument. It should return