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
if(!$member) return false;
// alternative decorated checks
// alternative extended checks
if($this->hasMethod('alternateAccessCheck')) {
$alternateAllowed = $this->alternateAccessCheck();
if($alternateAllowed === FALSE) return false;
@ -102,8 +102,8 @@ class LeftAndMain extends Controller {
}
/**
* @uses LeftAndMainDecorator->init()
* @uses LeftAndMainDecorator->accessedCMS()
* @uses LeftAndMainExtension->init()
* @uses LeftAndMainExtension->accessedCMS()
* @uses CMSMenu
*/
function init() {
@ -124,7 +124,7 @@ class LeftAndMain extends Controller {
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()
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

View File

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

View File

@ -1,7 +1,7 @@
<?php
/**
* 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
* {@link getOwner()}.
* Every object instance gets its own set of extension instances,
@ -19,8 +19,9 @@ abstract class Extension {
public static $allowed_actions = null;
/**
* The DataObject that owns this decorator.
* @var DataObject
* The object this extension is applied to.
*
* @var Object
*/
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 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,
@ -65,7 +66,7 @@ abstract class Extension {
}
/**
* Returns the owner of this decorator
* Returns the owner of this extension.
*
* @return Object
*/

View File

@ -277,7 +277,6 @@ abstract class Object {
}
if(!isset(self::$cached_statics[$class][$name]) || $uncached) {
//if($class == 'DataObjectDecoratorTest_MyObject') Debug::message("$class - $name");
$extra = $builtIn = $break = $replacedAt = false;
$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
* 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
* as a string, e.g. "Versioned" or "Translatable('Param')"
*/
@ -538,11 +537,11 @@ abstract class Object {
// load statics now for DataObject classes
if(is_subclass_of($class, 'DataObject')) {
if(is_subclass_of($extensionClass, 'DataObjectDecorator')) {
DataObjectDecorator::load_extra_statics($class, $extension);
if(is_subclass_of($extensionClass, 'DataExtension')) {
DataExtension::load_extra_statics($class, $extension);
}
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];
}
if(is_subclass_of($extensionClass, 'DataObjectDecorator')) {
DataObjectDecorator::load_extra_statics($class, $extension);
if(is_subclass_of($extensionClass, 'DataExtension')) {
DataExtension::load_extra_statics($class, $extension);
}
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 bool $includeArgumentString Include the argument string in the return array,
* 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.
*/
function get_extensions($class, $includeArgumentString = false) {
@ -950,7 +949,7 @@ abstract class Object {
* 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
* 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
* 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
* at constructor time, meaning if you use {@link add_extension()}
* 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.
* Caution: Don't use singleton(<class>)->hasExtension() as it will
* 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
* 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() {
return $this->extension_instances;

View File

@ -688,9 +688,9 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
$dbConn = DB::getConn();
$dbName = $dbConn->currentDatabase();
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
foreach(ClassInfo::subclassesFor('DataObjectDecorator') as $class) {
foreach(ClassInfo::subclassesFor('DataExtension') as $class) {
$toCall = array($class, 'on_db_reset');
if(is_callable($toCall)) call_user_func($toCall);
}
@ -709,9 +709,9 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
$dbadmin = new DatabaseAdmin();
$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
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');
if(is_callable($toCall)) call_user_func($toCall);
}

View File

@ -1,22 +1,22 @@
# DataObjectDecorator
# DataExtension
## 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
implementation. Have a look at `[api:Object->useCustomClass()]`.
## 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
// 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
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
`[api:ForumRole]` decorator to the `[api:Member]` object.
Sometimes you will want to add extension to classes that you didn't make. For example, you might want to add the
`[api:ForumRole]` extension to the `[api:Member]` object.
:::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
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:
:::php
class CustomMember extends DataObjectDecorator {
class CustomMember extends DataExtension {
function extraStatics() {
return array(
@ -67,7 +67,7 @@ The function should return a map where the keys are the names of the static vari
*NOTE*
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
for statics.
@ -75,7 +75,7 @@ for statics.
### Modifying CMS Fields
The member class demonstrates an extension that allows you to update the default CMS fields for an object in a
decorator:
extension:
:::php
public function getCMSFields() {
@ -97,11 +97,11 @@ The $fields parameter is passed by reference, as it is an object.
### 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,
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 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
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
being decorated. For instance, you may add a publish() method to every `[api:DataObject]` that is decorated with `[api:Versioned]`.
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 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 = The `[api:DataObjectDecorator]` object.
* $this = The `[api:DataExtension]` 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
to as `$this->propertyName`. Every `[api:DataObject]` has an associated `[api:DataObjectDecorator]` instance for each class that it is
decorated by.
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:DataExtension]` instance for each class that it is
extended by.
:::php
class Customer extends DataObject {
@ -159,7 +159,7 @@ decorated by.
}
class CustomerWorkflow extends DataObjectDecorator {
class CustomerWorkflow extends DataExtension {
function IsMarkedForDeletion() {
return ($this->owner->Account()->IsMarkedForDeletion == 1) ? true : false;
@ -169,4 +169,4 @@ decorated by.
## 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
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.*

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
* [Database Structure](database-structure): Conventions and best practices for database tables and fields
* [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
* [Director](director): Routes URLs and handles HTTP requests
* [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
<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).
</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,
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
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
DataObject::add_extension('Member', 'ForumRole');
Object::add_extension('Member', 'ForumRole');
// OR
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
things, you should add appropriate `[api:Permission::checkMember()]` calls to the role's methods.
:::php
class ForumRole extends DataObjectDecorator {
class ForumRole extends DataExtension {
/**
* 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
class CustomSiteConfig extends DataObjectDecorator {
class CustomSiteConfig extends DataExtension {
function extraStatics() {
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 |
| ------------ | | ------ | | ----------- |
| 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 |
## 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
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.

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
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
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)
* [Module Release Process](module-release-process)
* [Debugging methods](/topics/debugging)
* [URL Variable Tools](/reference/urlvariabletools) - Lists a number of “page options” , “rendering tools” or “special
URL variables that you can use to debug your sapphire applications
* [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<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 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.
* 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.
* You can modify this fieldset by subclassing folder, or by creating a {@link DataObjectDecorator}
* and implemeting updateCMSFields(FieldSet $fields) on that decorator.
* You can modify this fieldset by subclassing folder, or by creating a {@link DataExtension}
* and implemeting updateCMSFields(FieldSet $fields) on that extension.
*/
function getCMSFields() {
$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.
*
* <h2>Extensions and Decorators</h2>
* <h2>Extensions</h2>
*
* See {@link Extension} and {@link DataObjectDecorator}.
* See {@link Extension} and {@link DataExtension}.
*
* <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.
*
* @uses DataObjectDecorator->onBeforeWrite()
* @uses DataExtension->onBeforeWrite()
*/
protected function onBeforeWrite() {
$this->brokenOnWrite = false;
@ -905,7 +905,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* $this->changed will have a record
* database. Don't forget to call parent::onAfterWrite(), though!
*
* @uses DataObjectDecorator->onAfterWrite()
* @uses DataExtension->onAfterWrite()
*/
protected function onAfterWrite() {
$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
* record. Don't forget to call parent::onBeforeDelete(), though!
*
* @uses DataObjectDecorator->onBeforeDelete()
* @uses DataExtension->onBeforeDelete()
*/
protected function onBeforeDelete() {
$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.
* Called by the constructor when creating new records.
*
* @uses DataObjectDecorator->populateDefaults()
* @uses DataExtension->populateDefaults()
*/
public function populateDefaults() {
$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.
* - 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 $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.
* $this->onBeforeDelete() gets called.
* Note that in Versioned objects, both Stage and Live will be deleted.
* @uses DataObjectDecorator->augmentSQL()
* @uses DataExtension->augmentSQL()
*/
public function delete() {
$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.
* If not overloaded, we're using {@link scaffoldFormFields()} to automatically
* 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>
* 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,
* 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
*/
@ -2034,7 +2034,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* Used for simple frontend forms without relation editing
* or {@link TabSet} behaviour. Uses {@link scaffoldFormFields()}
* 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
*
@ -2277,7 +2277,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if($field == "LastEdited" && 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')) {
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:
*
* - false: Disallow this permission, regardless of what other decorators say
* - true: Allow this permission, as long as no other decorators return false
* - false: Disallow this permission, regardless of what other extensions say
* - true: Allow this permission, as long as no other extensions return false
* - NULL: Don't affect the outcome
*
* 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.
*
* @uses DataObjectDecorator->augmentSQL()
* @uses DataExtension->augmentSQL()
*
* @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.
@ -2936,7 +2936,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
/**
* 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 $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.
*
* @uses DataObjectDecorator->augmentDatabase()
* @uses DataExtension->augmentDatabase()
*/
public function requireTable() {
// 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
* call parent::requireDefaultRecords().
*
* @uses DataObjectDecorator->requireDefaultRecords()
* @uses DataExtension->requireDefaultRecords()
*/
public function requireDefaultRecords() {
$defaultRecords = $this->stat('default_records');
@ -3172,8 +3172,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if(!$fields) $fields = array_keys($this->summaryFields());
// we need to make sure the format is unified before
// augmenting fields, so decorators can apply consistent checks
// but also after augmenting fields, because the decorator
// augmenting fields, so extensions can apply consistent checks
// but also after augmenting fields, because the extension
// might use the shorthand notation as well
// rewrite array, if it is using shorthand syntax
@ -3210,7 +3210,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$fields = $rewrite;
// apply DataObjectDecorators if present
// apply DataExtensions if present
$this->extend('updateSearchableFields', $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.
* 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,
* which could cause horrible bugs.
*/

View File

@ -1,235 +1,17 @@
<?php
/**
* Plug-ins for additional functionality in your DataObjects.
*
* 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
* @package sapphire
* @subpackage model
* @deprecated 3.0 Use {@link DataExtension}.
*/
abstract class DataObjectDecorator extends Extension {
abstract class DataObjectDecorator extends DataExtension {
/**
* Statics on a {@link DataObject} subclass
* which can be decorated onto. 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 $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) {
public function __construct() {
user_error(
'DataObjectDecorator is deprecated, please use DataExtension instead.',
E_USER_NOTICE
);
parent::__construct();
}
/**
* 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
/**
* 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.
* @package sapphire
* @subpackage model
*/
class Hierarchy extends DataObjectDecorator {
class Hierarchy extends DataExtension {
protected $markedNodes;

View File

@ -1,11 +1,11 @@
<?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.
* @package sapphire
* @subpackage model
*/
class Versioned extends DataObjectDecorator {
class Versioned extends DataExtension {
/**
* An array of possible stages.
* @var array
@ -42,7 +42,7 @@ class Versioned extends DataObjectDecorator {
/**
* Additional database columns for the new
* "_versions" table. Used in {@link augmentDatabase()}
* and all Versioned calls decorating or creating
* and all Versioned calls extending or creating
* SELECT statements.
*
* @var array $db_for_versions_table

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@
*
* @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 through decorators (#2387)
* @todo tests for setting statics through extensions (#2387)
*/
class ObjectTest extends SapphireTest {

View File

@ -38,8 +38,8 @@ class FormScaffolderTest extends SapphireTest {
$article1 = $this->objFromFixture('FormScaffolderTest_Article', 'article1');
$fields = $article1->getCMSFields();
$this->assertNotNull(
$fields->dataFieldByName('AddedDecoratorField'),
'getCMSFields() includes decorated fields'
$fields->dataFieldByName('AddedExtensionField'),
'getCMSFields() includes extended fields'
);
}
@ -102,16 +102,16 @@ class FormScaffolderTest_Tag extends DataObject implements TestOnly {
'Articles' => 'FormScaffolderTest_Article'
);
}
class FormScaffolderTest_ArticleDecorator extends DataObjectDecorator implements TestOnly {
class FormScaffolderTest_ArticleExtension extends DataExtension implements TestOnly {
static $db = array(
'DecoratedField' => 'Varchar'
'ExtendedField' => 'Varchar'
);
function updateCMSFields(&$fields) {
$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
class i18nTestModuleDecorator extends DataObjectDecorator {
class i18nTestModuleExtension extends DataExtension {
function extraStatics() {
return array(
'db' => array(

View File

@ -1,26 +1,26 @@
<?php
class DataObjectDecoratorTest extends SapphireTest {
static $fixture_file = 'DataObjectDecoratorTest.yml';
class DataExtensionTest extends SapphireTest {
static $fixture_file = 'DataExtensionTest.yml';
protected $extraDataObjects = array(
'DataObjectDecoratorTest_Member',
'DataObjectDecoratorTest_Player',
'DataObjectDecoratorTest_RelatedObject',
'DataObjectDecoratorTest_MyObject',
'DataExtensionTest_Member',
'DataExtensionTest_Player',
'DataExtensionTest_RelatedObject',
'DataExtensionTest_MyObject',
);
protected $requiredExtensions = array(
'DataObject' => array( 'DataObjectDecoratorTest_AppliedToDO' ),
'DataObject' => array( 'DataExtensionTest_AppliedToDO' ),
);
function testOneToManyAssociationWithDecorator() {
function testOneToManyAssociationWithExtension() {
// Fails in RestfulServerTest
// 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";
$object = new DataObjectDecoratorTest_RelatedObject();
$object = new DataExtensionTest_RelatedObject();
$object->FieldOne = "Lorem ipsum dolor";
$object->FieldTwo = "Random notes";
@ -37,28 +37,28 @@ class DataObjectDecoratorTest extends SapphireTest {
unset($contact);
unset($object);
$contact = DataObject::get_one("DataObjectDecoratorTest_Member", "\"Website\"='http://www.example.com'");
$object = DataObject::get_one('DataObjectDecoratorTest_RelatedObject', "\"ContactID\" = {$contactID}");
$contact = DataObject::get_one("DataExtensionTest_Member", "\"Website\"='http://www.example.com'");
$object = DataObject::get_one('DataExtensionTest_RelatedObject', "\"ContactID\" = {$contactID}");
$this->assertNotNull($object, 'Related object not null');
$this->assertType('DataObjectDecoratorTest_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->Contact(), 'Related contact is a member dataobject');
$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("Random notes", $contact->RelatedObjects()->First()->FieldTwo);
$contact->delete();
}
function testManyManyAssociationWithDecorator() {
$parent = new DataObjectDecoratorTest_MyObject();
function testManyManyAssociationWithExtension() {
$parent = new DataExtensionTest_MyObject();
$parent->Title = 'My Title';
$parent->write();
$this->assertEquals(0, $parent->Faves()->Count());
$obj1 = $this->objFromFixture('DataObjectDecoratorTest_RelatedObject', 'obj1');
$obj2 = $this->objFromFixture('DataObjectDecoratorTest_RelatedObject', 'obj2');
$obj1 = $this->objFromFixture('DataExtensionTest_RelatedObject', 'obj1');
$obj2 = $this->objFromFixture('DataExtensionTest_RelatedObject', 'obj2');
$parent->Faves()->add($obj1->ID);
$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() {
// Object::add_extension() will load DOD statics directly, so let's try adding a decorator on the fly
Object::add_extension('DataObjectDecoratorTest_Player', 'DataObjectDecoratorTest_PlayerDecorator');
// Object::add_extension() will load DOD statics directly, so let's try adding a extension on the fly
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);
// Create a test record with decorated fields, writing to the DB
$player = new DataObjectDecoratorTest_Player();
// Create a test record with extended fields, writing to the DB
$player = new DataExtensionTest_Player();
$player->setField('Name', 'Joe');
$player->setField('DateBirth', '1990-5-10');
$player->Address = '123 somewhere street';
@ -89,66 +89,66 @@ class DataObjectDecoratorTest extends SapphireTest {
unset($player);
// Pull the record out of the DB and examine the decorated fields
$player = DataObject::get_one('DataObjectDecoratorTest_Player', "\"Name\" = 'Joe'");
// Pull the record out of the DB and examine the extended fields
$player = DataObject::get_one('DataExtensionTest_Player', "\"Name\" = 'Joe'");
$this->assertEquals($player->DateBirth, '1990-05-10');
$this->assertEquals($player->Address, '123 somewhere street');
$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() {
$this->assertTrue(Object::get_static('DataObjectDecoratorTest_Member', 'api_access'));
function testApiAccessCanBeExtended() {
$this->assertTrue(Object::get_static('DataExtensionTest_Member', 'api_access'));
}
function testPermissionDecoration() {
function testPermissionExtension() {
// testing behaviour in isolation, too many sideeffects and other checks
// 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');
$admin = $this->objFromFixture('Member', 'admin');
$this->assertFalse(
$obj->canOne($websiteuser),
'Both decorators return true, but original method returns false'
'Both extensions return true, but original method returns false'
);
$this->assertFalse(
$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(
$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() {
$obj = new DataObjectDecoratorTest_Member();
$obj = new DataExtensionTest_Member();
$this->assertEquals(
$obj->Phone,
'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() {
$member = $this->objFromFixture('DataObjectDecoratorTest_Member', 'member1');
function testDbObjectOnExtendedFields() {
$member = $this->objFromFixture('DataExtensionTest_Member', 'member1');
$this->assertNotNull($member->dbObject('Website'));
$this->assertType('Varchar', $member->dbObject('Website'));
}
function testDecoratorCanBeAppliedToDataObject() {
function testExtensionCanBeAppliedToDataObject() {
$do = new DataObject();
$mo = new DataObjectDecoratorTest_MyObject();
$mo = new DataExtensionTest_MyObject();
$this->assertTrue($do->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(
"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(
'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) {
// 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.
if($class == 'DataObjectDecoratorTest_Player') {
if($class == 'DataExtensionTest_Player') {
return array(
'db' => array(
'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() {
return array(
@ -205,7 +205,7 @@ class DataObjectDecoratorTest_ContactRole extends DataObjectDecorator implements
'Phone' => 'Varchar(255)',
),
'has_many' => array(
'RelatedObjects' => 'DataObjectDecoratorTest_RelatedObject'
'RelatedObjects' => 'DataExtensionTest_RelatedObject'
),
'defaults' => array(
'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(
"FieldOne" => "Varchar",
@ -224,21 +224,21 @@ class DataObjectDecoratorTest_RelatedObject extends DataObject implements TestOn
);
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(
'Title' => 'Varchar',
);
function canOne($member = null) {
// decorated access checks
// extended access checks
$results = $this->extend('canOne', $member);
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) {
// decorated access checks
// extended access checks
$results = $this->extend('canTwo', $member);
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) {
// decorated access checks
// extended access checks
$results = $this->extend('canThree', $member);
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) {
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) {
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() {
return 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() {
return "hello world";
}
}
DataObject::add_extension('DataObjectDecoratorTest_MyObject', 'DataObjectDecoratorTest_Ext1');
DataObject::add_extension('DataObjectDecoratorTest_MyObject', 'DataObjectDecoratorTest_Ext2');
DataObject::add_extension('DataObjectDecoratorTest_MyObject', 'DataObjectDecoratorTest_Faves');
DataObject::add_extension('DataExtensionTest_MyObject', 'DataExtensionTest_Ext1');
DataObject::add_extension('DataExtensionTest_MyObject', 'DataExtensionTest_Ext2');
DataObject::add_extension('DataExtensionTest_MyObject', 'DataExtensionTest_Faves');
?>

View File

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

View File

@ -22,11 +22,11 @@ DataObjectTest_SubTeam:
subteam1:
Title: Subteam 1
SubclassDatabaseField: Subclassed 1
DecoratedDatabaseField: Decorated 1
ExtendedDatabaseField: Extended 1
subteam2_with_player_relation:
Title: Subteam 2
SubclassDatabaseField: Subclassed 2
DecoratedHasOneRelationship: =>DataObjectTest_Player.player1
ExtendeHasOneRelationship: =>DataObjectTest_Player.player1
subteam3_with_empty_fields:
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->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('DecoratedDatabaseField'), 'hasField() finds decorated fields in instances');
$this->assertTrue($teamInstance->hasField('DecoratedHasOneRelationshipID'), 'hasField() finds decorated foreign keys in instances');
//$this->assertTrue($teamInstance->hasField('DecoratedDynamicField'), 'hasField() includes decorated dynamic getters in instances');
$this->assertTrue($teamInstance->hasField('ExtendedDatabaseField'), 'hasField() finds extended fields in instances');
$this->assertTrue($teamInstance->hasField('ExtendedHasOneRelationshipID'), 'hasField() finds extended foreign keys in instances');
//$this->assertTrue($teamInstance->hasField('ExtendedDynamicField'), 'hasField() includes extended dynamic getters in instances');
/* hasField() subclass checks */
$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('DynamicField'), 'hasField() finds dynamic getters 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('DecoratedHasOneRelationshipID'), 'hasField() finds decorated foreign keys in subclass instances');
$this->assertTrue($subteamInstance->hasField('ExtendedDatabaseField'), 'hasField() finds extended fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('ExtendedHasOneRelationshipID'), 'hasField() finds extended foreign keys in subclass instances');
/* hasDatabaseField() singleton checks */
//$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('DynamicField'), 'hasDatabaseField() doesnt dynamic getters 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('DecoratedHasOneRelationshipID'), 'hasDatabaseField() finds decorated foreign keys in instances');
$this->assertFalse($teamInstance->hasDatabaseField('DecoratedDynamicField'), 'hasDatabaseField() doesnt include decorated dynamic getters in instances');
$this->assertTrue($teamInstance->hasDatabaseField('ExtendedDatabaseField'), 'hasDatabaseField() finds extended fields in instances');
$this->assertTrue($teamInstance->hasDatabaseField('ExtendedHasOneRelationshipID'), 'hasDatabaseField() finds extended foreign keys in instances');
$this->assertFalse($teamInstance->hasDatabaseField('ExtendedDynamicField'), 'hasDatabaseField() doesnt include extended dynamic getters in instances');
/* hasDatabaseField() subclass checks */
$this->assertTrue($subteamInstance->hasField('DatabaseField'), 'hasField() finds custom fields in subclass instances');
@ -507,12 +507,12 @@ class DataObjectTest extends SapphireTest {
//'LastEdited',
'Title',
'DatabaseField',
'DecoratedDatabaseField',
'ExtendedDatabaseField',
'CaptainID',
'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(
@ -524,12 +524,12 @@ class DataObjectTest extends SapphireTest {
'LastEdited',
'Title',
'DatabaseField',
'DecoratedDatabaseField',
'ExtendedDatabaseField',
'CaptainID',
'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(
@ -542,12 +542,12 @@ class DataObjectTest extends SapphireTest {
'SubclassDatabaseField',
'Title',
'DatabaseField',
'DecoratedDatabaseField',
'ExtendedDatabaseField',
'CaptainID',
'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(
@ -803,8 +803,8 @@ class DataObjectTest extends SapphireTest {
"hasOwnDatabaseField() doesn't detect non-existend fields"
);
$this->assertTrue(
$team->hasDatabaseField('DecoratedDatabaseField'),
"hasOwnDatabaseField() works with decorated fields"
$team->hasDatabaseField('ExtendedDatabaseField'),
"hasOwnDatabaseField() works with extended fields"
);
$this->assertFalse(
$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() {
return array(
'db' => array(
'DecoratedDatabaseField' => 'Varchar'
'ExtendedDatabaseField' => 'Varchar'
),
'has_one' => array(
'DecoratedHasOneRelationship' => 'DataObjectTest_Player'
'ExtendedHasOneRelationship' => 'DataObjectTest_Player'
)
);
}
function getDecoratedDynamicField() {
return "decorated dynamic field";
function getExtendedDynamicField() {
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:
Title: Subteam 1
SubclassDatabaseField: Subclassed 1
DecoratedDatabaseField: Decorated 1
ExtendedDatabaseField: Extended 1
subteam2_with_player_relation:
Title: Subteam 2
SubclassDatabaseField: Subclassed 2
DecoratedHasOneRelationship: =>DataObjectTest_Player.player1
ExtendedHasOneRelationship: =>DataObjectTest_Player.player1
subteam3_with_empty_fields:
Title: Subteam 3

View File

@ -457,7 +457,7 @@ class MemberTest extends FunctionalTest {
$this->session()->inst_set('loggedInAs', null);
}
public function testDecoratedCan() {
public function testExtendedCan() {
$extensions = $this->removeExtensions(Object::get_extensions('Member'));
$member = $this->objFromFixture('Member', 'test');
@ -466,7 +466,7 @@ class MemberTest extends FunctionalTest {
$this->assertFalse($member->canDelete());
$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');
$member2 = $this->objFromFixture('Member', 'staffmember');
@ -474,7 +474,7 @@ class MemberTest extends FunctionalTest {
$this->assertFalse($member2->canDelete());
$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::add_extension('Member', 'MemberTest_ViewingDeniedExtension');
$member3 = $this->objFromFixture('Member', 'managementmember');
@ -483,7 +483,7 @@ class MemberTest extends FunctionalTest {
$this->assertFalse($member3->canDelete());
$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::add_extension('Member', 'MemberTest_EditingAllowedDeletingDeniedExtension');
$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() {
return true;
}
}
class MemberTest_ViewingDeniedExtension extends DataObjectDecorator implements TestOnly {
class MemberTest_ViewingDeniedExtension extends DataExtension implements TestOnly {
public function canView() {
return false;
}
}
class MemberTest_EditingAllowedDeletingDeniedExtension extends DataObjectDecorator implements TestOnly {
class MemberTest_EditingAllowedDeletingDeniedExtension extends DataExtension implements TestOnly {
public function canView() {
return true;

View File

@ -2924,7 +2924,7 @@ class SSTemplateParser extends Parser {
* '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
* 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).
*
* 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
*
* 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).
*
* This function will be called with the match rule result array as it's first argument. It should return