mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #6066 from sminnee/campaignadmin-error-check
FIX: Graceful degradation if obsolete classnames in ChangeSetItem (fixes #6065)
This commit is contained in:
commit
f6f3fe561d
10
ORM/UnexpectedDataException.php
Normal file
10
ORM/UnexpectedDataException.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\ORM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throw this exception whenever unexpected data is found.
|
||||||
|
*/
|
||||||
|
class UnexpectedDataException extends \Exception
|
||||||
|
{
|
||||||
|
}
|
@ -11,6 +11,7 @@ use SilverStripe\ORM\HasManyList;
|
|||||||
use SilverStripe\ORM\ValidationException;
|
use SilverStripe\ORM\ValidationException;
|
||||||
use SilverStripe\ORM\DB;
|
use SilverStripe\ORM\DB;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
use SilverStripe\ORM\UnexpectedDataException;
|
||||||
use SilverStripe\Security\Member;
|
use SilverStripe\Security\Member;
|
||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
use BadMethodCallException;
|
use BadMethodCallException;
|
||||||
@ -202,15 +203,20 @@ class ChangeSet extends DataObject {
|
|||||||
$explicit[$explicitKey] = true;
|
$explicit[$explicitKey] = true;
|
||||||
|
|
||||||
foreach ($item->findReferenced() as $referee) {
|
foreach ($item->findReferenced() as $referee) {
|
||||||
/** @var DataObject $referee */
|
try {
|
||||||
$key = $this->implicitKey($referee);
|
/** @var DataObject $referee */
|
||||||
|
$key = $this->implicitKey($referee);
|
||||||
|
|
||||||
$referenced[$key] = [
|
$referenced[$key] = [
|
||||||
'ObjectID' => $referee->ID,
|
'ObjectID' => $referee->ID,
|
||||||
'ObjectClass' => $referee->baseClass(),
|
'ObjectClass' => $referee->baseClass(),
|
||||||
];
|
];
|
||||||
|
|
||||||
$references[$key][] = $item->ID;
|
$references[$key][] = $item->ID;
|
||||||
|
|
||||||
|
// Skip any bad records
|
||||||
|
} catch(UnexpectedDataException $e) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ use SilverStripe\ORM\DataList;
|
|||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
use SilverStripe\ORM\ManyManyList;
|
use SilverStripe\ORM\ManyManyList;
|
||||||
use SilverStripe\ORM\SS_List;
|
use SilverStripe\ORM\SS_List;
|
||||||
|
use SilverStripe\ORM\UnexpectedDataException;
|
||||||
use SilverStripe\Security\Member;
|
use SilverStripe\Security\Member;
|
||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
use BadMethodCallException;
|
use BadMethodCallException;
|
||||||
@ -112,6 +113,10 @@ class ChangeSetItem extends DataObject implements Thumbnail {
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getChangeType() {
|
public function getChangeType() {
|
||||||
|
if(!class_exists($this->ObjectClass)) {
|
||||||
|
throw new UnexpectedDataException("Invalid Class '{$this->ObjectClass}' in ChangeSetItem #{$this->ID}");
|
||||||
|
}
|
||||||
|
|
||||||
// Get change versions
|
// Get change versions
|
||||||
if($this->VersionBefore || $this->VersionAfter) {
|
if($this->VersionBefore || $this->VersionAfter) {
|
||||||
$draftVersion = $this->VersionAfter; // After publishing draft was written to stage
|
$draftVersion = $this->VersionAfter; // After publishing draft was written to stage
|
||||||
@ -144,6 +149,10 @@ class ChangeSetItem extends DataObject implements Thumbnail {
|
|||||||
* @return Versioned|DataObject
|
* @return Versioned|DataObject
|
||||||
*/
|
*/
|
||||||
protected function getObjectInStage($stage) {
|
protected function getObjectInStage($stage) {
|
||||||
|
if(!class_exists($this->ObjectClass)) {
|
||||||
|
throw new UnexpectedDataException("Invalid Class '{$this->ObjectClass}' in ChangeSetItem #{$this->ID}");
|
||||||
|
}
|
||||||
|
|
||||||
return Versioned::get_by_stage($this->ObjectClass, $stage)->byID($this->ObjectID);
|
return Versioned::get_by_stage($this->ObjectClass, $stage)->byID($this->ObjectID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,6 +162,10 @@ class ChangeSetItem extends DataObject implements Thumbnail {
|
|||||||
* @return Versioned|DataObject
|
* @return Versioned|DataObject
|
||||||
*/
|
*/
|
||||||
protected function getObjectLatestVersion() {
|
protected function getObjectLatestVersion() {
|
||||||
|
if(!class_exists($this->ObjectClass)) {
|
||||||
|
throw new UnexpectedDataException("Invalid Class '{$this->ObjectClass}' in ChangeSetItem #{$this->ID}");
|
||||||
|
}
|
||||||
|
|
||||||
return Versioned::get_latest_version($this->ObjectClass, $this->ObjectID);
|
return Versioned::get_latest_version($this->ObjectClass, $this->ObjectID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,6 +193,10 @@ class ChangeSetItem extends DataObject implements Thumbnail {
|
|||||||
* Note: Unlike Versioned::doPublish() and Versioned::doUnpublish, this action is not recursive.
|
* Note: Unlike Versioned::doPublish() and Versioned::doUnpublish, this action is not recursive.
|
||||||
*/
|
*/
|
||||||
public function publish() {
|
public function publish() {
|
||||||
|
if(!class_exists($this->ObjectClass)) {
|
||||||
|
throw new UnexpectedDataException("Invalid Class '{$this->ObjectClass}' in ChangeSetItem #{$this->ID}");
|
||||||
|
}
|
||||||
|
|
||||||
// Logical checks prior to publish
|
// Logical checks prior to publish
|
||||||
if(!$this->canPublish()) {
|
if(!$this->canPublish()) {
|
||||||
throw new Exception("The current member does not have permission to publish this ChangeSetItem.");
|
throw new Exception("The current member does not have permission to publish this ChangeSetItem.");
|
||||||
|
@ -14,6 +14,7 @@ use SilverStripe\ORM\SS_List;
|
|||||||
use SilverStripe\ORM\Versioning\ChangeSet;
|
use SilverStripe\ORM\Versioning\ChangeSet;
|
||||||
use SilverStripe\ORM\Versioning\ChangeSetItem;
|
use SilverStripe\ORM\Versioning\ChangeSetItem;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
use SilverStripe\ORM\UnexpectedDataException;
|
||||||
use SilverStripe\Security\SecurityToken;
|
use SilverStripe\Security\SecurityToken;
|
||||||
use SilverStripe\Security\PermissionProvider;
|
use SilverStripe\Security\PermissionProvider;
|
||||||
use LogicException;
|
use LogicException;
|
||||||
@ -256,9 +257,6 @@ JSON;
|
|||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function getChangeSetResource(ChangeSet $changeSet) {
|
protected function getChangeSetResource(ChangeSet $changeSet) {
|
||||||
// Before presenting the changeset to the client,
|
|
||||||
// synchronise it with new changes.
|
|
||||||
$changeSet->sync();
|
|
||||||
$hal = [
|
$hal = [
|
||||||
'_links' => [
|
'_links' => [
|
||||||
'self' => [
|
'self' => [
|
||||||
@ -267,24 +265,37 @@ JSON;
|
|||||||
],
|
],
|
||||||
'ID' => $changeSet->ID,
|
'ID' => $changeSet->ID,
|
||||||
'Name' => $changeSet->Name,
|
'Name' => $changeSet->Name,
|
||||||
'Description' => $changeSet->getDescription(),
|
|
||||||
'Created' => $changeSet->Created,
|
'Created' => $changeSet->Created,
|
||||||
'LastEdited' => $changeSet->LastEdited,
|
'LastEdited' => $changeSet->LastEdited,
|
||||||
'State' => $changeSet->State,
|
'State' => $changeSet->State,
|
||||||
'canEdit' => $changeSet->canEdit(),
|
'canEdit' => $changeSet->canEdit(),
|
||||||
'canPublish' => $changeSet->canPublish(),
|
'canPublish' => false,
|
||||||
'_embedded' => ['items' => []]
|
'_embedded' => ['items' => []]
|
||||||
];
|
];
|
||||||
foreach($changeSet->Changes() as $changeSetItem) {
|
|
||||||
if(!$changeSetItem) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var ChangesetItem $changeSetItem */
|
// Before presenting the changeset to the client,
|
||||||
$resource = $this->getChangeSetItemResource($changeSetItem);
|
// synchronise it with new changes.
|
||||||
$hal['_embedded']['items'][] = $resource;
|
try {
|
||||||
|
$changeSet->sync();
|
||||||
|
$hal['Description'] = $changeSet->getDescription();
|
||||||
|
$hal['canPublish'] = $changeSet->canPublish();
|
||||||
|
|
||||||
|
foreach($changeSet->Changes() as $changeSetItem) {
|
||||||
|
if(!$changeSetItem) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var ChangesetItem $changeSetItem */
|
||||||
|
$resource = $this->getChangeSetItemResource($changeSetItem);
|
||||||
|
$hal['_embedded']['items'][] = $resource;
|
||||||
|
}
|
||||||
|
$hal['ChangesCount'] = count($hal['_embedded']['items']);
|
||||||
|
|
||||||
|
// An unexpected data exception means that the database is corrupt
|
||||||
|
} catch(UnexpectedDataException $e) {
|
||||||
|
$hal['Description'] = 'Corrupt database! ' . $e->getMessage();
|
||||||
|
$hal['ChangesCount'] = '-';
|
||||||
}
|
}
|
||||||
$hal['ChangesCount'] = count($hal['_embedded']['items']);
|
|
||||||
return $hal;
|
return $hal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
40
admin/tests/CampaignAdminTest.php
Normal file
40
admin/tests/CampaignAdminTest.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
use SilverStripe\Admin\CampaignAdmin;
|
||||||
|
use SilverStripe\ORM\Versioning\ChangeSet;
|
||||||
|
use SilverStripe\ORM\UnexpectedDataException;
|
||||||
|
|
||||||
|
class CampaignAdminTest extends SapphireTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call a protected method on an object via reflection
|
||||||
|
*
|
||||||
|
* @param object $object The object to call the method on
|
||||||
|
* @param string $method The name of the method
|
||||||
|
* @param array $args The arguments to pass to the method
|
||||||
|
*/
|
||||||
|
function callProtectedMethod($object, $method, $args = []) {
|
||||||
|
$class = new ReflectionClass(get_class($object));
|
||||||
|
$methodObj = $class->getMethod($method);
|
||||||
|
$methodObj->setAccessible(true);
|
||||||
|
return $methodObj->invokeArgs($object, $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testInvalidDataHandling() {
|
||||||
|
$changeset = new CampaignAdminTest_InvalidChangeSet();
|
||||||
|
$admin = new CampaignAdmin();
|
||||||
|
|
||||||
|
$result = $this->callProtectedMethod($admin, 'getChangeSetResource', [$changeset] );
|
||||||
|
$this->assertEquals('Corrupt database! bad data' , $result['Description']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CampaignAdminTest_InvalidChangeSet extends ChangeSet
|
||||||
|
{
|
||||||
|
function sync()
|
||||||
|
{
|
||||||
|
throw new UnexpectedDataException("bad data");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user