mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge branch '4.0' into 4.1
This commit is contained in:
commit
c04ff8c55a
@ -115,7 +115,7 @@ Links to internal `File` database records work exactly the same, but with the `[
|
||||
### Images
|
||||
|
||||
Images inserted through the "Insert Media" form (WYSIWYG editor) need to retain a relationship with
|
||||
the underlying `[Image](api:SilverStripe\Assets\Image)` database record. The `[image]` shortcode saves this database reference
|
||||
the underlying [Image](api:SilverStripe\Assets\Image) database record. The `[image]` shortcode saves this database reference
|
||||
instead of hard-linking to the filesystem path of a given image.
|
||||
|
||||
```html
|
||||
|
@ -337,8 +337,9 @@ class DatabaseAdmin extends Controller
|
||||
$updateQueries = [sprintf($updateQuery, '')];
|
||||
|
||||
// Remap versioned table ClassName values as well
|
||||
/** @var Versioned|DataObject $class */
|
||||
$class = DataObject::singleton($newClassName);
|
||||
if ($class->has_extension(Versioned::class)) {
|
||||
if ($class->hasExtension(Versioned::class)) {
|
||||
if ($class->hasStages()) {
|
||||
$updateQueries[] = sprintf($updateQuery, '_Live');
|
||||
}
|
||||
|
@ -208,12 +208,14 @@ class Hierarchy extends DataExtension
|
||||
*/
|
||||
public function AllChildrenIncludingDeleted()
|
||||
{
|
||||
$stageChildren = $this->owner->stageChildren(true);
|
||||
/** @var DataObject|Hierarchy|Versioned $owner */
|
||||
$owner = $this->owner;
|
||||
$stageChildren = $owner->stageChildren(true);
|
||||
|
||||
// Add live site content that doesn't exist on the stage site, if required.
|
||||
if ($this->owner->hasExtension(Versioned::class)) {
|
||||
if ($owner->hasExtension(Versioned::class) && $owner->hasStages()) {
|
||||
// Next, go through the live children. Only some of these will be listed
|
||||
$liveChildren = $this->owner->liveChildren(true, true);
|
||||
$liveChildren = $owner->liveChildren(true, true);
|
||||
if ($liveChildren) {
|
||||
$merged = new ArrayList();
|
||||
$merged->merge($stageChildren);
|
||||
@ -221,7 +223,7 @@ class Hierarchy extends DataExtension
|
||||
$stageChildren = $merged;
|
||||
}
|
||||
}
|
||||
$this->owner->extend("augmentAllChildrenIncludingDeleted", $stageChildren);
|
||||
$owner->extend("augmentAllChildrenIncludingDeleted", $stageChildren);
|
||||
return $stageChildren;
|
||||
}
|
||||
|
||||
@ -233,15 +235,19 @@ class Hierarchy extends DataExtension
|
||||
*/
|
||||
public function AllHistoricalChildren()
|
||||
{
|
||||
if (!$this->owner->hasExtension(Versioned::class)) {
|
||||
throw new Exception('Hierarchy->AllHistoricalChildren() only works with Versioned extension applied');
|
||||
/** @var DataObject|Versioned|Hierarchy $owner */
|
||||
$owner = $this->owner;
|
||||
if (!$owner->hasExtension(Versioned::class) || !$owner->hasStages()) {
|
||||
throw new Exception(
|
||||
'Hierarchy->AllHistoricalChildren() only works with Versioned extension applied with staging'
|
||||
);
|
||||
}
|
||||
|
||||
$baseTable = $this->owner->baseTable();
|
||||
$parentIDColumn = $this->owner->getSchema()->sqlColumnForField($this->owner, 'ParentID');
|
||||
$baseTable = $owner->baseTable();
|
||||
$parentIDColumn = $owner->getSchema()->sqlColumnForField($owner, 'ParentID');
|
||||
return Versioned::get_including_deleted(
|
||||
$this->owner->baseClass(),
|
||||
[ $parentIDColumn => $this->owner->ID ],
|
||||
$owner->baseClass(),
|
||||
[ $parentIDColumn => $owner->ID ],
|
||||
"\"{$baseTable}\".\"ID\" ASC"
|
||||
);
|
||||
}
|
||||
@ -337,15 +343,17 @@ class Hierarchy extends DataExtension
|
||||
*/
|
||||
public function liveChildren($showAll = false, $onlyDeletedFromStage = false)
|
||||
{
|
||||
if (!$this->owner->hasExtension(Versioned::class)) {
|
||||
throw new Exception('Hierarchy->liveChildren() only works with Versioned extension applied');
|
||||
/** @var Versioned|DataObject|Hierarchy $owner */
|
||||
$owner = $this->owner;
|
||||
if (!$owner->hasExtension(Versioned::class) || !$owner->hasStages()) {
|
||||
throw new Exception('Hierarchy->liveChildren() only works with Versioned extension applied with staging');
|
||||
}
|
||||
|
||||
$hideFromHierarchy = $this->owner->config()->hide_from_hierarchy;
|
||||
$hideFromCMSTree = $this->owner->config()->hide_from_cms_tree;
|
||||
$children = DataObject::get($this->owner->baseClass())
|
||||
->filter('ParentID', (int)$this->owner->ID)
|
||||
->exclude('ID', (int)$this->owner->ID)
|
||||
$hideFromHierarchy = $owner->config()->hide_from_hierarchy;
|
||||
$hideFromCMSTree = $owner->config()->hide_from_cms_tree;
|
||||
$children = DataObject::get($owner->baseClass())
|
||||
->filter('ParentID', (int)$owner->ID)
|
||||
->exclude('ID', (int)$owner->ID)
|
||||
->setDataQueryParam(array(
|
||||
'Versioned.mode' => $onlyDeletedFromStage ? 'stage_unique' : 'stage',
|
||||
'Versioned.stage' => 'Live'
|
||||
@ -356,7 +364,7 @@ class Hierarchy extends DataExtension
|
||||
if ($hideFromCMSTree && $this->showingCMSTree()) {
|
||||
$children = $children->exclude('ClassName', $hideFromCMSTree);
|
||||
}
|
||||
if (!$showAll && DataObject::getSchema()->fieldSpec($this->owner, 'ShowInMenus')) {
|
||||
if (!$showAll && DataObject::getSchema()->fieldSpec($owner, 'ShowInMenus')) {
|
||||
$children = $children->filter('ShowInMenus', 1);
|
||||
}
|
||||
|
||||
|
@ -665,8 +665,9 @@ class InheritedPermissions implements PermissionChecker, MemberCacheFlusher
|
||||
if (!class_exists(Versioned::class)) {
|
||||
return false;
|
||||
}
|
||||
/** @var Versioned|DataObject $singleton */
|
||||
$singleton = DataObject::singleton($this->getBaseClass());
|
||||
return $singleton->hasExtension(Versioned::class);
|
||||
return $singleton->hasExtension(Versioned::class) && $singleton->hasStages();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,6 +8,7 @@ use SilverStripe\Core\Extension;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\FormAction;
|
||||
use SilverStripe\Forms\LiteralField;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
|
||||
/**
|
||||
@ -40,8 +41,9 @@ class ControllerExtension extends Extension
|
||||
if (empty($context['Record'])) {
|
||||
return;
|
||||
}
|
||||
/** @var Versioned|DataObject $record */
|
||||
$record = $context['Record'];
|
||||
if ($record->hasExtension(Versioned::class)) {
|
||||
if ($record->hasExtension(Versioned::class) && $record->hasStages()) {
|
||||
$actions->push(new FormAction('publish', 'Publish'));
|
||||
}
|
||||
}
|
||||
@ -60,8 +62,9 @@ class ControllerExtension extends Extension
|
||||
if (empty($context['Record'])) {
|
||||
return;
|
||||
}
|
||||
/** @var Versioned|DataObject $record */
|
||||
$record = $context['Record'];
|
||||
if ($record->hasExtension(Versioned::class)) {
|
||||
if ($record->hasExtension(Versioned::class) && $record->hasStages()) {
|
||||
$link = $controller->Link('preview');
|
||||
$fields->unshift(
|
||||
new LiteralField(
|
||||
|
@ -10,6 +10,7 @@ use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\PermissionChecker;
|
||||
use SilverStripe\Security\Tests\InheritedPermissionsTest\TestPermissionNode;
|
||||
use SilverStripe\Security\Tests\InheritedPermissionsTest\TestDefaultPermissionChecker;
|
||||
use SilverStripe\Security\Tests\InheritedPermissionsTest\UnstagedNode;
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use ReflectionClass;
|
||||
@ -20,6 +21,7 @@ class InheritedPermissionsTest extends SapphireTest
|
||||
|
||||
protected static $extra_dataobjects = [
|
||||
TestPermissionNode::class,
|
||||
UnstagedNode::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@ -29,18 +31,38 @@ class InheritedPermissionsTest extends SapphireTest
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->rootPermissions = new TestDefaultPermissionChecker();
|
||||
|
||||
// Register root permissions
|
||||
$permission = InheritedPermissions::create(TestPermissionNode::class)
|
||||
$permission1 = InheritedPermissions::create(TestPermissionNode::class)
|
||||
->setGlobalEditPermissions(['TEST_NODE_ACCESS'])
|
||||
->setDefaultPermissions($this->rootPermissions = new TestDefaultPermissionChecker());
|
||||
->setDefaultPermissions($this->rootPermissions);
|
||||
Injector::inst()->registerService(
|
||||
$permission,
|
||||
$permission1,
|
||||
PermissionChecker::class . '.testpermissions'
|
||||
);
|
||||
|
||||
// Reset root permission
|
||||
$permission2 = InheritedPermissions::create(UnstagedNode::class)
|
||||
->setGlobalEditPermissions(['TEST_NODE_ACCESS'])
|
||||
->setDefaultPermissions($this->rootPermissions);
|
||||
Injector::inst()->registerService(
|
||||
$permission2,
|
||||
PermissionChecker::class . '.unstagedpermissions'
|
||||
);
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$permission1->clearCache();
|
||||
$permission2->clearCache();
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
Injector::inst()->unregisterNamedObject(PermissionChecker::class . '.testpermissions');
|
||||
Injector::inst()->unregisterNamedObject(PermissionChecker::class . '.unstagedpermissions');
|
||||
$this->rootPermissions = null;
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testEditPermissions()
|
||||
@ -162,6 +184,49 @@ class InheritedPermissionsTest extends SapphireTest
|
||||
$this->assertFalse($history->canView($editor));
|
||||
}
|
||||
|
||||
public function testUnstagedViewPermissions()
|
||||
{
|
||||
$history = $this->objFromFixture(UnstagedNode::class, 'history');
|
||||
$contact = $this->objFromFixture(UnstagedNode::class, 'contact');
|
||||
$contactForm = $this->objFromFixture(UnstagedNode::class, 'contact-form');
|
||||
$secret = $this->objFromFixture(UnstagedNode::class, 'secret');
|
||||
$secretNested = $this->objFromFixture(UnstagedNode::class, 'secret-nested');
|
||||
$protected = $this->objFromFixture(UnstagedNode::class, 'protected');
|
||||
$protectedChild = $this->objFromFixture(UnstagedNode::class, 'protected-child');
|
||||
$editor = $this->objFromFixture(Member::class, 'editor');
|
||||
|
||||
// Not logged in user can only access Inherit or Anyone pages
|
||||
Member::actAs(
|
||||
null,
|
||||
function () use ($protectedChild, $secretNested, $protected, $secret, $history, $contact, $contactForm) {
|
||||
$this->assertTrue($history->canView());
|
||||
$this->assertTrue($contact->canView());
|
||||
$this->assertTrue($contactForm->canView());
|
||||
// Protected
|
||||
$this->assertFalse($secret->canView());
|
||||
$this->assertFalse($secretNested->canView());
|
||||
$this->assertFalse($protected->canView());
|
||||
$this->assertFalse($protectedChild->canView());
|
||||
}
|
||||
);
|
||||
|
||||
// Editor can view pages restricted to logged in users
|
||||
$this->assertTrue($secret->canView($editor));
|
||||
$this->assertTrue($secretNested->canView($editor));
|
||||
|
||||
// Cannot read admin-only pages
|
||||
$this->assertFalse($protected->canView($editor));
|
||||
$this->assertFalse($protectedChild->canView($editor));
|
||||
|
||||
// Check root permissions
|
||||
$this->assertTrue($history->canView($editor));
|
||||
|
||||
UnstagedNode::getInheritedPermissions()->clearCache();
|
||||
$this->rootPermissions->setCanView(false);
|
||||
|
||||
$this->assertFalse($history->canView($editor));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that draft permissions deny unrestricted live permissions
|
||||
*/
|
||||
@ -316,7 +381,7 @@ class InheritedPermissionsTest extends SapphireTest
|
||||
/* @var CacheInterface $cache */
|
||||
$cache = Injector::inst()->create(CacheInterface::class . '.InheritedPermissions');
|
||||
$cache->clear();
|
||||
|
||||
|
||||
$permissionChecker = new InheritedPermissions(TestPermissionNode::class, $cache);
|
||||
$member1 = $this->objFromFixture(Member::class, 'editor');
|
||||
$member2 = $this->objFromFixture(Member::class, 'admin');
|
||||
|
@ -100,3 +100,70 @@ SilverStripe\Security\Tests\InheritedPermissionsTest\TestPermissionNode:
|
||||
Title: Child
|
||||
CanViewType: Inherit
|
||||
Parent: =>SilverStripe\Security\Tests\InheritedPermissionsTest\TestPermissionNode.protected
|
||||
|
||||
SilverStripe\Security\Tests\InheritedPermissionsTest\UnstagedNode:
|
||||
about:
|
||||
Title: About Us
|
||||
CanEditType: OnlyTheseUsers
|
||||
EditorGroups: =>SilverStripe\Security\Group.admins
|
||||
about-staff:
|
||||
Title: Staff
|
||||
CanEditType: Inherit
|
||||
Parent: =>SilverStripe\Security\Tests\InheritedPermissionsTest\UnstagedNode.about
|
||||
about-staff-ceo:
|
||||
Title: CEO
|
||||
Parent: =>SilverStripe\Security\Tests\InheritedPermissionsTest\UnstagedNode.about-staff
|
||||
about-staffduplicate:
|
||||
Title: Staff
|
||||
Parent: =>SilverStripe\Security\Tests\InheritedPermissionsTest\UnstagedNode.about
|
||||
products:
|
||||
Title: Products
|
||||
CanEditType: OnlyTheseUsers
|
||||
EditorGroups: =>SilverStripe\Security\Group.editors
|
||||
products-product1:
|
||||
Title: 1.1 Test Product
|
||||
Parent: =>SilverStripe\Security\Tests\InheritedPermissionsTest\UnstagedNode.products
|
||||
CanEditType: Inherit
|
||||
products-product2:
|
||||
Title: Another Product
|
||||
Parent: =>SilverStripe\Security\Tests\InheritedPermissionsTest\UnstagedNode.products
|
||||
CanEditType: Inherit
|
||||
products-product3:
|
||||
Title: Another Product
|
||||
Parent: =>SilverStripe\Security\Tests\InheritedPermissionsTest\UnstagedNode.products
|
||||
CanEditType: Inherit
|
||||
products-product4:
|
||||
Title: Another Product
|
||||
Parent: =>SilverStripe\Security\Tests\InheritedPermissionsTest\UnstagedNode.products
|
||||
CanEditType: OnlyTheseUsers
|
||||
EditorGroups: =>SilverStripe\Security\Group.admins
|
||||
history:
|
||||
Title: History
|
||||
CanViewType: Inherit
|
||||
CanEditType: Inherit
|
||||
history-gallery:
|
||||
Title: Gallery
|
||||
CanViewType: Inherit
|
||||
Parent: =>SilverStripe\Security\Tests\InheritedPermissionsTest\UnstagedNode.history
|
||||
contact:
|
||||
Title: Contact Us
|
||||
CanViewType: Anyone
|
||||
contact-form:
|
||||
Title: Send us a message
|
||||
CanViewType: Inherit
|
||||
Parent: =>SilverStripe\Security\Tests\InheritedPermissionsTest\UnstagedNode.contact
|
||||
secret:
|
||||
Title: Secret
|
||||
CanViewType: LoggedInUsers
|
||||
secret-nested:
|
||||
Title: Nested
|
||||
CanViewType: Inherit
|
||||
Parent: =>SilverStripe\Security\Tests\InheritedPermissionsTest\UnstagedNode.secret
|
||||
protected:
|
||||
Title: Protected
|
||||
CanViewType: OnlyTheseUsers
|
||||
ViewerGroups: =>SilverStripe\Security\Group.admins
|
||||
protected-child:
|
||||
Title: Child
|
||||
CanViewType: Inherit
|
||||
Parent: =>SilverStripe\Security\Tests\InheritedPermissionsTest\UnstagedNode.protected
|
||||
|
@ -7,7 +7,6 @@ use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Security\InheritedPermissions;
|
||||
use SilverStripe\Security\InheritedPermissionsExtension;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\PermissionChecker;
|
||||
use SilverStripe\Security\Security;
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
|
68
tests/php/Security/InheritedPermissionsTest/UnstagedNode.php
Normal file
68
tests/php/Security/InheritedPermissionsTest/UnstagedNode.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Security\Tests\InheritedPermissionsTest;
|
||||
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Security\InheritedPermissions;
|
||||
use SilverStripe\Security\InheritedPermissionsExtension;
|
||||
use SilverStripe\Security\PermissionChecker;
|
||||
use SilverStripe\Security\Security;
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
|
||||
/**
|
||||
* @method UnstagedNode Parent()
|
||||
* @mixin Versioned
|
||||
* @mixin InheritedPermissionsExtension
|
||||
*/
|
||||
class UnstagedNode extends DataObject implements TestOnly
|
||||
{
|
||||
private static $db = [
|
||||
"Title" => "Varchar(255)",
|
||||
];
|
||||
|
||||
private static $has_one = [
|
||||
"Parent" => self::class,
|
||||
];
|
||||
|
||||
private static $table_name = 'InheritedPermissionsTest_UnstagedNode';
|
||||
|
||||
private static $extensions = [
|
||||
Versioned::class . '.versioned',
|
||||
InheritedPermissionsExtension::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @return InheritedPermissions
|
||||
*/
|
||||
public static function getInheritedPermissions()
|
||||
{
|
||||
/** @var InheritedPermissions $permissions */
|
||||
return Injector::inst()->get(PermissionChecker::class . '.unstagedpermissions');
|
||||
}
|
||||
|
||||
public function canEdit($member = null)
|
||||
{
|
||||
if (!$member) {
|
||||
$member = Security::getCurrentUser();
|
||||
}
|
||||
return static::getInheritedPermissions()->canEdit($this->ID, $member);
|
||||
}
|
||||
|
||||
public function canView($member = null)
|
||||
{
|
||||
if (!$member) {
|
||||
$member = Security::getCurrentUser();
|
||||
}
|
||||
return static::getInheritedPermissions()->canView($this->ID, $member);
|
||||
}
|
||||
|
||||
public function canDelete($member = null)
|
||||
{
|
||||
if (!$member) {
|
||||
$member = Security::getCurrentUser();
|
||||
}
|
||||
return static::getInheritedPermissions()->canDelete($this->ID, $member);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user