Damian Mooyman 2017-08-22 16:43:02 +12:00
parent ecc619248b
commit 6b1dfddcb8
No known key found for this signature in database
GPG Key ID: 78B823A10DE27D1A
1 changed files with 175 additions and 136 deletions

View File

@ -21,15 +21,15 @@ also track versioned history.
```php
use SilverStripe\Versioned\Versioned;
use SilverStripe\ORM\DataObject;
use SilverStripe\Versioned\Versioned;
use SilverStripe\ORM\DataObject;
class MyStagedModel extends DataObject
{
private static $extensions = [
Versioned::class
];
}
class MyStagedModel extends DataObject
{
private static $extensions = [
Versioned::class
];
}
```
Alternatively, staging can be disabled, so that only versioned changes are tracked for your model. This
@ -37,14 +37,14 @@ can be specified by setting the constructor argument to "Versioned"
```php
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObject;
class VersionedModel extends DataObject
{
private static $extensions = [
"SilverStripe\\ORM\\Versioning\\Versioned('Versioned')"
];
}
class VersionedModel extends DataObject
{
private static $extensions = [
"SilverStripe\\ORM\\Versioning\\Versioned('Versioned')"
];
}
```
<div class="notice" markdown="1">
@ -57,6 +57,49 @@ Versioning only works if you are adding the extension to the base class. That is
of `DataObject`. Adding this extension to children of the base class will have unpredictable behaviour.
</div>
## Versioned gridfield extension
By default the versioned module includes a `VersionedGridfieldDetailForm` that can extend gridfield
with versioning support for models.
You can enable this on a per-model basis using the following code:
```php
use SilverStripe\ORM\DataObject;
use SilverStripe\Versioned\Versioned;
class MyBanner extends DataObject {
private static $extensions = [
Versioned::class,
];
private static $versioned_gridfield_extensions = true;
}
```
This can be manually enabled for a single gridfield, alternatively, by setting the following option on the
GridFieldDetailForm component.
```php
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
use SilverStripe\Forms\GridField\GridFieldDetailForm;
use SilverStripe\Versioned\VersionedGridFieldItemRequest;
class Page extends SiteTree
{
public function getCMSFields()
{
$fields = parent::getCMSFields();
$config = GridFieldConfig_RelationEditor::create();
$config
->getComponentByType(GridFieldDetailForm::class)
->setItemRequestClass(VersionedGridFieldItemRequest::class);
$gridField = GridField::create('Items', 'Items', $this->Items(), $config);
$fields->addFieldToTab('Root.Items', $gridField);
return $fields;
}
}
```
## Database Structure
Depending on whether staging is enabled, one or more new tables will be created for your records. `<class>_versions`
@ -87,13 +130,13 @@ By default, all records are retrieved from the "Draft" stage (so the `MyRecord`
explicitly request a certain stage through various getters on the `Versioned` class.
```php
// Fetching multiple records
$stageRecords = Versioned::get_by_stage('MyRecord', Versioned::DRAFT);
$liveRecords = Versioned::get_by_stage('MyRecord', Versioned::LIVE);
// Fetching multiple records
$stageRecords = Versioned::get_by_stage('MyRecord', Versioned::DRAFT);
$liveRecords = Versioned::get_by_stage('MyRecord', Versioned::LIVE);
// Fetching a single record
$stageRecord = Versioned::get_by_stage('MyRecord', Versioned::DRAFT)->byID(99);
$liveRecord = Versioned::get_by_stage('MyRecord', Versioned::LIVE)->byID(99);
// Fetching a single record
$stageRecord = Versioned::get_by_stage('MyRecord', Versioned::DRAFT)->byID(99);
$liveRecord = Versioned::get_by_stage('MyRecord', Versioned::LIVE)->byID(99);
```
### Historical Versions
@ -102,7 +145,7 @@ The above commands will just retrieve the latest version of its respective stage
in the `<class>_versions` tables.
```php
$historicalRecord = Versioned::get_version('MyRecord', <record-id>, <version-id>);
$historicalRecord = Versioned::get_version('MyRecord', <record-id>, <version-id>);
```
<div class="alert" markdown="1">
@ -115,9 +158,9 @@ objects, which expose the same database information as a `DataObject`, but also
a record was published.
```php
$record = MyRecord::get()->byID(99); // stage doesn't matter here
$versions = $record->allVersions();
echo $versions->First()->Version; // instance of Versioned_Version
$record = MyRecord::get()->byID(99); // stage doesn't matter here
$versions = $record->allVersions();
echo $versions->First()->Version; // instance of Versioned_Version
```
### Writing Versions and Changing Stages
@ -141,21 +184,21 @@ done via one of several ways:
See "DataObject ownership" for reference on dependant objects.
```php
$record = Versioned::get_by_stage('MyRecord', Versioned::DRAFT)->byID(99);
$record->MyField = 'changed';
// will update `MyRecord` table (assuming Versioned::current_stage() == 'Stage'),
// and write a row to `MyRecord_versions`.
$record->write();
// will copy the saved record information to the `MyRecord_Live` table
$record->publishRecursive();
$record = Versioned::get_by_stage('MyRecord', Versioned::DRAFT)->byID(99);
$record->MyField = 'changed';
// will update `MyRecord` table (assuming Versioned::current_stage() == 'Stage'),
// and write a row to `MyRecord_versions`.
$record->write();
// will copy the saved record information to the `MyRecord_Live` table
$record->publishRecursive();
```
Similarly, an "unpublish" operation does the reverse, and removes a record from a specific stage.
```php
$record = MyRecord::get()->byID(99); // stage doesn't matter here
// will remove the row from the `MyRecord_Live` table
$record->deleteFromStage(Versioned::LIVE);
$record = MyRecord::get()->byID(99); // stage doesn't matter here
// will remove the row from the `MyRecord_Live` table
$record->deleteFromStage(Versioned::LIVE);
```
### Forcing the Current Stage
@ -164,11 +207,11 @@ The current stage is stored as global state on the object. It is usually modifie
is initialized. But it can also be set and reset temporarily to force a specific operation to run on a certain stage.
```php
$origMode = Versioned::get_reading_mode(); // save current mode
$obj = MyRecord::getComplexObjectRetrieval(); // returns 'Live' records
Versioned::set_reading_mode(Versioned::DRAFT); // temporarily overwrite mode
$obj = MyRecord::getComplexObjectRetrieval(); // returns 'Stage' records
Versioned::set_reading_mode($origMode); // reset current mode
$origMode = Versioned::get_reading_mode(); // save current mode
$obj = MyRecord::getComplexObjectRetrieval(); // returns 'Live' records
Versioned::set_reading_mode(Versioned::DRAFT); // temporarily overwrite mode
$obj = MyRecord::getComplexObjectRetrieval(); // returns 'Stage' records
Versioned::set_reading_mode($origMode); // reset current mode
```
### DataObject ownership
@ -191,33 +234,32 @@ without requiring any custom code.
```php
use SilverStripe\Versioned\Versioned;
use SilverStripe\Assets\Image;
use Page;
class MyPage extends Page
{
private static $has_many = [
'Banners' => Banner::class
];
private static $owns = [
'Banners'
];
}
class Banner extends Page
{
private static $extensions = [
Versioned::class
];
private static $has_one = [
'Parent' => MyPage::class,
'Image' => Image::class,
];
private static $owns = [
'Image'
];
}
use SilverStripe\Versioned\Versioned;
use SilverStripe\Assets\Image;
use Page;
class MyPage extends Page
{
private static $has_many = [
'Banners' => Banner::class
];
private static $owns = [
'Banners'
];
}
class Banner extends Page
{
private static $extensions = [
Versioned::class
];
private static $has_one = [
'Parent' => MyPage::class,
'Image' => Image::class,
];
private static $owns = [
'Image'
];
}
```
Note that ownership cannot be used with polymorphic relations. E.g. has_one to non-type specific `DataObject`.
@ -236,36 +278,35 @@ that can be used to traverse between each, and then by ensuring you configure bo
E.g.
```php
use SilverStripe\Versioned\Versioned;
use SilverStripe\ORM\DataObject;
use SilverStripe\Versioned\Versioned;
use SilverStripe\ORM\DataObject;
class MyParent extends DataObject
class MyParent extends DataObject
{
private static $extensions = [
Versioned::class
];
private static $owns = [
'ChildObjects'
];
public function ChildObjects()
{
private static $extensions = [
Versioned::class
];
private static $owns = [
'ChildObjects'
];
public function ChildObjects()
{
return MyChild::get();
}
return MyChild::get();
}
class MyChild extends DataObject
}
class MyChild extends DataObject
{
private static $extensions = [
Versioned::class
];
private static $owned_by = [
'Parent'
];
public function Parent()
{
private static $extensions = [
Versioned::class
];
private static $owned_by = [
'Parent'
];
public function Parent()
{
return MyParent::get()->first();
}
return MyParent::get()->first();
}
}
```
#### DataObject Ownership in HTML Content
@ -286,7 +327,7 @@ smaller modifications of the generated `DataList` objects.
Example: Get the first 10 live records, filtered by creation date:
```php
$records = Versioned::get_by_stage('MyRecord', Versioned::LIVE)->limit(10)->sort('Created', 'ASC');
$records = Versioned::get_by_stage('MyRecord', Versioned::LIVE)->limit(10)->sort('Created', 'ASC');
```
### Permissions
@ -310,30 +351,29 @@ Versioned object visibility can be customised in one of the following ways by ed
E.g.
```php
use SilverStripe\Versioned\Versioned;
use SilverStripe\Security\Permission;
use SilverStripe\ORM\DataObject;
use SilverStripe\Versioned\Versioned;
use SilverStripe\Security\Permission;
use SilverStripe\ORM\DataObject;
class MyObject extends DataObject
{
private static $extensions = [
Versioned::class,
];
public function canViewVersioned($member = null)
{
// Check if site is live
$mode = $this->getSourceQueryParam("Versioned.mode");
$stage = $this->getSourceQueryParam("Versioned.stage");
if ($mode === 'Stage' && $stage === 'Live') {
return true;
}
// Only admins can view non-live objects
return Permission::checkMember($member, 'ADMIN');
class MyObject extends DataObject
{
private static $extensions = [
Versioned::class,
];
public function canViewVersioned($member = null)
{
// Check if site is live
$mode = $this->getSourceQueryParam("Versioned.mode");
$stage = $this->getSourceQueryParam("Versioned.stage");
if ($mode === 'Stage' && $stage === 'Live') {
return true;
}
// Only admins can view non-live objects
return Permission::checkMember($member, 'ADMIN');
}
}
```
If you want to control permissions of an object in an extension, you can also use
@ -348,16 +388,16 @@ only be invoked if the object is in a non-published state.
E.g.
```php
use SilverStripe\Security\Permission;
use SilverStripe\ORM\DataExtension;
use SilverStripe\Security\Permission;
use SilverStripe\ORM\DataExtension;
class MyObjectExtension extends DataExtension
{
public function canViewNonLive($member = null)
{
return Permission::check($member, 'DRAFT_STATUS');
}
class MyObjectExtension extends DataExtension
{
public function canViewNonLive($member = null)
{
return Permission::check($member, 'DRAFT_STATUS');
}
}
```
If none of the above checks are overridden, visibility will be determined by the
@ -366,17 +406,16 @@ permissions in the `TargetObject.non_live_permissions` config.
E.g.
```php
use SilverStripe\Versioned\Versioned;
use SilverStripe\ORM\DataObject;
class MyObject extends DataObject
{
private static $extensions = [
Versioned::class,
];
private static $non_live_permissions = ['ADMIN'];
}
use SilverStripe\Versioned\Versioned;
use SilverStripe\ORM\DataObject;
class MyObject extends DataObject
{
private static $extensions = [
Versioned::class,
];
private static $non_live_permissions = ['ADMIN'];
}
```
Versioned applies no additional permissions to `canEdit` or `canCreate`, and such
@ -395,11 +434,11 @@ to force a specific stage, we recommend the `Controller->init()` method for this
**mysite/code/MyController.php**
```php
public function init()
{
parent::init();
Versioned::set_stage(Versioned::DRAFT);
}
public function init()
{
parent::init();
Versioned::set_stage(Versioned::DRAFT);
}
```
### Controllers