Completed
Push — php7-fix ( 538bb9...015411 )
by Sam
07:21
created

VersionedTest::testWritingNewToLive()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 28
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 19
nc 1
nop 0
dl 0
loc 28
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
use SilverStripe\Dev\Debug;
4
use SilverStripe\ORM\DataObjectSchema;
5
use SilverStripe\ORM\DB;
6
use SilverStripe\ORM\HasManyList;
7
use SilverStripe\ORM\ManyManyList;
8
use SilverStripe\ORM\Versioning\Versioned;
9
use SilverStripe\ORM\DataObject;
10
use SilverStripe\ORM\FieldType\DBDatetime;
11
use SilverStripe\ORM\DataExtension;
12
use SilverStripe\Core\Convert;
13
use SilverStripe\Core\ClassInfo;
14
use SilverStripe\Core\Config\Config;
15
use SilverStripe\Core\Injector\Injector;
16
use SilverStripe\Dev\SapphireTest;
17
use SilverStripe\Dev\TestOnly;
18
use SilverStripe\Control\Director;
19
use SilverStripe\Control\Session;
20
21
22
23
24
25
26
/**
27
 * @package framework
28
 * @subpackage tests
29
 */
30
class VersionedTest extends SapphireTest {
31
32
	protected static $fixture_file = 'VersionedTest.yml';
33
34
	protected $extraDataObjects = array(
35
		'VersionedTest_DataObject',
36
		'VersionedTest_Subclass',
37
		'VersionedTest_AnotherSubclass',
38
		'VersionedTest_RelatedWithoutVersion',
39
		'VersionedTest_SingleStage',
40
		'VersionedTest_WithIndexes',
41
		'VersionedTest_PublicStage',
42
		'VersionedTest_PublicViaExtension',
43
		'VersionedTest_CustomTable',
44
	);
45
46
	protected $requiredExtensions = array(
47
		"VersionedTest_DataObject" => array('SilverStripe\\ORM\\Versioning\\Versioned'),
48
		"VersionedTest_WithIndexes" => array('SilverStripe\\ORM\\Versioning\\Versioned'),
49
	);
50
51
	public function testUniqueIndexes() {
52
		$tableExpectations = array(
53
			'VersionedTest_WithIndexes' =>
54
				array('value' => true, 'message' => 'Unique indexes are unique in main table'),
55
			'VersionedTest_WithIndexes_Versions' =>
56
				array('value' => false, 'message' => 'Unique indexes are no longer unique in _Versions table'),
57
			'VersionedTest_WithIndexes_Live' =>
58
				array('value' => true, 'message' => 'Unique indexes are unique in _Live table'),
59
		);
60
61
		// Test each table's performance
62
		foreach ($tableExpectations as $tableName => $expectation) {
63
			$indexes = DB::get_schema()->indexList($tableName);
64
65
			// Check for presence of all unique indexes
66
			$indexColumns = array_map(function($index) {
67
				return $index['value'];
68
			}, $indexes);
69
			sort($indexColumns);
70
			$expectedColumns = array('"UniqA"', '"UniqS"');
71
			$this->assertEquals(
72
					array_values($expectedColumns),
73
					array_values(array_intersect($indexColumns, $expectedColumns)),
74
					"$tableName has both indexes");
75
76
			// Check unique -> non-unique conversion
77
			foreach ($indexes as $indexKey => $indexSpec) {
78
				if (in_array($indexSpec['value'], $expectedColumns)) {
79
					$isUnique = $indexSpec['type'] === 'unique';
80
					$this->assertEquals($isUnique, $expectation['value'], $expectation['message']);
81
				}
82
			}
83
		}
84
	}
85
86
	public function testDeletingOrphanedVersions() {
87
		$obj = new VersionedTest_Subclass();
88
		$obj->ExtraField = 'Foo'; // ensure that child version table gets written
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
89
		$obj->write();
90
		$obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
91
92
		$obj->ExtraField = 'Bar'; // ensure that child version table gets written
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
93
		$obj->write();
94
		$obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
95
96
		$versions = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_Versions\""
97
			. " WHERE \"RecordID\" = '$obj->ID'")->value();
98
99
		$this->assertGreaterThan(0, $versions, 'At least 1 version exists in the history of the page');
100
101
		// Force orphaning of all versions created earlier, only on parent record.
102
		// The child versiones table should still have the correct relationship
103
		DB::query("DELETE FROM \"VersionedTest_DataObject_Versions\" WHERE \"RecordID\" = $obj->ID");
104
105
		// insert a record with no primary key (ID)
106
		DB::query("INSERT INTO \"VersionedTest_DataObject_Versions\" (\"RecordID\") VALUES ($obj->ID)");
107
108
		// run the script which should clean that up
109
		$obj->augmentDatabase();
110
111
		$versions = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_Versions\""
112
			. " WHERE \"RecordID\" = '$obj->ID'")->value();
113
		$this->assertEquals(0, $versions, 'Orphaned versions on child tables are removed');
114
115
		// test that it doesn't delete records that we need
116
		$obj->write();
117
		$obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
118
119
		$count = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_Versions\""
120
			. " WHERE \"RecordID\" = '$obj->ID'")->value();
121
		$obj->augmentDatabase();
122
123
		$count2 = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_Versions\""
124
			. " WHERE \"RecordID\" = '$obj->ID'")->value();
125
126
		$this->assertEquals($count, $count2);
127
	}
128
129
	public function testCustomTable() {
130
		$obj = new VersionedTest_CustomTable();
131
		$obj->Title = 'my object';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_CustomTable>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
132
		$obj->write();
133
		$id = $obj->ID;
134
		$obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
135
		$obj->Title = 'new title';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_CustomTable>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
136
		$obj->write();
137
138
		$liveRecord = Versioned::get_by_stage('VersionedTest_CustomTable', Versioned::LIVE)->byID($id);
139
		$draftRecord = Versioned::get_by_stage('VersionedTest_CustomTable', Versioned::DRAFT)->byID($id);
140
141
		$this->assertEquals('my object', $liveRecord->Title);
142
		$this->assertEquals('new title', $draftRecord->Title);
143
	}
144
145
	/**
146
	 * Test that publishing from invalid stage will throw exception
147
	 */
148
	public function testInvalidPublish() {
149
		$obj = new VersionedTest_Subclass();
150
		$obj->ExtraField = 'Foo'; // ensure that child version table gets written
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
151
		$obj->write();
152
		$this->setExpectedException(
153
			'InvalidArgumentException',
154
			"Can't find VersionedTest_DataObject#{$obj->ID} in stage Live"
155
		);
156
157
		// Fail publishing from live to stage
158
		$obj->copyVersionToStage(Versioned::LIVE, Versioned::DRAFT);
159
	}
160
161
	public function testDuplicate() {
162
		$obj1 = new VersionedTest_Subclass();
163
		$obj1->ExtraField = 'Foo';
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
164
		$obj1->write(); // version 1
165
		$obj1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
166
		$obj1->ExtraField = 'Foo2';
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
167
		$obj1->write(); // version 2
168
169
		// Make duplicate
170
		$obj2 = $obj1->duplicate();
171
172
		// Check records differ
173
		$this->assertNotEquals($obj1->ID, $obj2->ID);
174
		$this->assertEquals(2, $obj1->Version);
175
		$this->assertEquals(1, $obj2->Version);
176
	}
177
178
	public function testForceChangeUpdatesVersion() {
179
		$obj = new VersionedTest_DataObject();
180
		$obj->Name = "test";
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
181
		$obj->write();
182
183
		$oldVersion = $obj->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
184
		$obj->forceChange();
185
		$obj->write();
186
187
		$this->assertTrue(
188
			($obj->Version > $oldVersion),
189
			"A object Version is increased when just calling forceChange() without any other changes"
190
		);
191
	}
192
193
	/**
194
	 * Test Versioned::get_including_deleted()
195
	 */
196
	public function testGetIncludingDeleted() {
197
		// Get all ids of pages
198
		$allPageIDs = DataObject::get(
199
			'VersionedTest_DataObject',
200
			"\"ParentID\" = 0", "\"VersionedTest_DataObject\".\"ID\" ASC"
201
		)->column('ID');
202
203
		// Modify a page, ensuring that the Version ID and Record ID will differ,
204
		// and then subsequently delete it
205
		$targetPage = $this->objFromFixture('VersionedTest_DataObject', 'page3');
206
		$targetPage->Content = 'To be deleted';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
207
		$targetPage->write();
208
		$targetPage->delete();
209
210
		// Get all items, ignoring deleted
211
		$remainingPages = DataObject::get("VersionedTest_DataObject", "\"ParentID\" = 0",
212
			"\"VersionedTest_DataObject\".\"ID\" ASC");
213
		// Check that page 3 has gone
214
		$this->assertNotNull($remainingPages);
215
		$this->assertEquals(array("Page 1", "Page 2", "Subclass Page 1"), $remainingPages->column('Title'));
216
217
		// Get all including deleted
218
		$allPages = Versioned::get_including_deleted("VersionedTest_DataObject", "\"ParentID\" = 0",
219
			"\"VersionedTest_DataObject\".\"ID\" ASC");
220
		// Check that page 3 is still there
221
		$this->assertEquals(array("Page 1", "Page 2", "Page 3", "Subclass Page 1"), $allPages->column('Title'));
222
223
		// Check that the returned pages have the correct IDs
224
		$this->assertEquals($allPageIDs, $allPages->column('ID'));
225
226
		// Check that this still works if we switch to reading the other stage
227
		Versioned::set_stage(Versioned::LIVE);
228
		$allPages = Versioned::get_including_deleted("VersionedTest_DataObject", "\"ParentID\" = 0",
229
			"\"VersionedTest_DataObject\".\"ID\" ASC");
230
		$this->assertEquals(array("Page 1", "Page 2", "Page 3", "Subclass Page 1"), $allPages->column('Title'));
231
232
		// Check that the returned pages still have the correct IDs
233
		$this->assertEquals($allPageIDs, $allPages->column('ID'));
234
	}
235
236
	public function testVersionedFieldsAdded() {
237
		$obj = new VersionedTest_DataObject();
238
		// Check that the Version column is added as a full-fledged column
239
		$this->assertInstanceOf('SilverStripe\\ORM\\FieldType\\DBInt', $obj->dbObject('Version'));
240
241
		$obj2 = new VersionedTest_Subclass();
242
		// Check that the Version column is added as a full-fledged column
243
		$this->assertInstanceOf('SilverStripe\\ORM\\FieldType\\DBInt', $obj2->dbObject('Version'));
244
	}
245
246
	public function testVersionedFieldsNotInCMS() {
247
		$obj = new VersionedTest_DataObject();
248
249
		// the version field in cms causes issues with Versioned::augmentWrite()
250
		$this->assertNull($obj->getCMSFields()->dataFieldByName('Version'));
251
	}
252
253
	public function testPublishCreateNewVersion() {
254
		/** @var VersionedTest_DataObject $page1 */
255
		$page1 = $this->objFromFixture('VersionedTest_DataObject', 'page1');
256
		$page1->Content = 'orig';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
257
		$page1->write();
258
		$firstVersion = $page1->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
259
		$page1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE, false);
260
		$this->assertEquals(
261
			$firstVersion,
262
			$page1->Version,
263
			'publish() with $createNewVersion=FALSE does not create a new version'
264
		);
265
266
		$page1->Content = 'changed';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
267
		$page1->write();
268
		$secondVersion = $page1->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
269
		$this->assertTrue($firstVersion < $secondVersion, 'write creates new version');
270
271
		$page1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE, true);
272
		$thirdVersion = Versioned::get_latest_version('VersionedTest_DataObject', $page1->ID)->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
273
		$liveVersion = Versioned::get_versionnumber_by_stage('VersionedTest_DataObject', 'Live', $page1->ID);
274
		$stageVersion = Versioned::get_versionnumber_by_stage('VersionedTest_DataObject', 'Stage', $page1->ID);
275
		$this->assertTrue(
276
			$secondVersion < $thirdVersion,
277
			'publish() with $createNewVersion=TRUE creates a new version'
278
		);
279
		$this->assertEquals(
280
			$liveVersion,
281
			$thirdVersion,
282
			'publish() with $createNewVersion=TRUE publishes to live'
283
		);
284
		$this->assertEquals(
285
			$stageVersion,
286
			$thirdVersion,
287
			'publish() with $createNewVersion=TRUE also updates draft'
288
		);
289
	}
290
291
	public function testRollbackTo() {
292
		$page1 = $this->objFromFixture('VersionedTest_AnotherSubclass', 'subclass1');
293
		$page1->Content = 'orig';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
294
		$page1->write();
295
		$page1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
296
		$origVersion = $page1->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
297
298
		$page1->Content = 'changed';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
299
		$page1->write();
300
		$page1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
301
		$changedVersion = $page1->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
302
303
		$page1->doRollbackTo($origVersion);
304
		$page1 = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', array(
0 ignored issues
show
Documentation introduced by
array('"VersionedTest_Da...ID" = ?' => $page1->ID) is of type array<string,integer,{"\...\"ID\" = ?":"integer"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
305
			'"VersionedTest_DataObject"."ID" = ?' => $page1->ID
306
		));
307
308
		$this->assertTrue($page1->Version == $changedVersion + 1, 'Create a new higher version number');
309
		$this->assertEquals('orig', $page1->Content, 'Copies the content from the old version');
310
311
		// check db entries
312
		$version = DB::prepared_query("SELECT MAX(\"Version\") FROM \"VersionedTest_DataObject_Versions\" WHERE \"RecordID\" = ?",
313
			array($page1->ID)
314
		)->value();
315
		$this->assertEquals($page1->Version, $version, 'Correct entry in VersionedTest_DataObject_Versions');
316
317
		$version = DB::prepared_query("SELECT MAX(\"Version\") FROM \"VersionedTest_AnotherSubclass_Versions\" WHERE \"RecordID\" = ?",
318
			array($page1->ID)
319
		)->value();
320
		$this->assertEquals($page1->Version, $version, 'Correct entry in VersionedTest_AnotherSubclass_Versions');
321
	}
322
323
	public function testDeleteFromStage() {
324
		$page1 = $this->objFromFixture('VersionedTest_DataObject', 'page1');
325
		$pageID = $page1->ID;
326
327
		$page1->Content = 'orig';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
328
		$page1->write();
329
		$page1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
330
331
		$this->assertEquals(1,
332
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value());
333
		$this->assertEquals(1,
334
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value());
335
336
		$page1->deleteFromStage('Live');
337
338
		// Confirm that deleteFromStage() doesn't manipulate the original record
339
		$this->assertEquals($pageID, $page1->ID);
340
341
		$this->assertEquals(1,
342
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value());
343
		$this->assertEquals(0,
344
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value());
345
346
		$page1->delete();
347
348
		$this->assertEquals(0, $page1->ID);
349
		$this->assertEquals(0,
350
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value());
351
		$this->assertEquals(0,
352
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value());
353
	}
354
355
	public function testWritingNewToStage() {
356
		$origReadingMode = Versioned::get_reading_mode();
357
358
		Versioned::set_stage(Versioned::DRAFT);
359
		$page = new VersionedTest_DataObject();
360
		$page->Title = "testWritingNewToStage";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
361
		$page->URLSegment = "testWritingNewToStage";
0 ignored issues
show
Documentation introduced by
The property URLSegment does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
362
		$page->write();
363
364
		$live = Versioned::get_by_stage('VersionedTest_DataObject', 'Live', array(
0 ignored issues
show
Documentation introduced by
array('"VersionedTest_Da...ve"."ID"' => $page->ID) is of type array<string,integer,{"\...e\".\"ID\"":"integer"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
365
			'"VersionedTest_DataObject_Live"."ID"' => $page->ID
366
		));
367
		$this->assertEquals(0, $live->count());
368
369
		$stage = Versioned::get_by_stage('VersionedTest_DataObject', 'Stage',array(
0 ignored issues
show
Documentation introduced by
array('"VersionedTest_Da...ct"."ID"' => $page->ID) is of type array<string,integer,{"\...t\".\"ID\"":"integer"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
370
			'"VersionedTest_DataObject"."ID"' => $page->ID
371
		));
372
		$this->assertEquals(1, $stage->count());
373
		$this->assertEquals($stage->First()->Title, 'testWritingNewToStage');
374
375
		Versioned::set_reading_mode($origReadingMode);
376
	}
377
378
	/**
379
	 * Writing a page to live should update both draft and live tables
380
	 */
381
	public function testWritingNewToLive() {
382
		$origReadingMode = Versioned::get_reading_mode();
383
384
		Versioned::set_stage(Versioned::LIVE);
385
		$page = new VersionedTest_DataObject();
386
		$page->Title = "testWritingNewToLive";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
387
		$page->URLSegment = "testWritingNewToLive";
0 ignored issues
show
Documentation introduced by
The property URLSegment does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
388
		$page->write();
389
390
		$live = Versioned::get_by_stage('VersionedTest_DataObject', 'Live',array(
0 ignored issues
show
Documentation introduced by
array('"VersionedTest_Da...ve"."ID"' => $page->ID) is of type array<string,integer,{"\...e\".\"ID\"":"integer"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
391
			'"VersionedTest_DataObject_Live"."ID"' => $page->ID
392
		));
393
		$this->assertEquals(1, $live->count());
394
		$liveRecord = $live->First();
395
		$this->assertEquals($liveRecord->Title, 'testWritingNewToLive');
396
397
		$stage = Versioned::get_by_stage('VersionedTest_DataObject', 'Stage',array(
0 ignored issues
show
Documentation introduced by
array('"VersionedTest_Da...ct"."ID"' => $page->ID) is of type array<string,integer,{"\...t\".\"ID\"":"integer"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
398
			'"VersionedTest_DataObject"."ID"' => $page->ID
399
		));
400
		$this->assertEquals(1, $stage->count());
401
		$stageRecord = $stage->first();
402
		$this->assertEquals($stageRecord->Title, 'testWritingNewToLive');
403
404
		// Both records have the same version
405
		$this->assertEquals($liveRecord->Version, $stageRecord->Version);
406
407
		Versioned::set_reading_mode($origReadingMode);
408
	}
409
410
	/**
411
	 * Tests DataObject::hasOwnTableDatabaseField
412
	 */
413
	public function testHasOwnTableDatabaseFieldWithVersioned() {
414
		$schema = DataObject::getSchema();
415
416
		$this->assertNull(
417
			$schema->fieldSpec(DataObject::class, 'Version', DataObjectSchema::UNINHERITED),
418
			'Plain models have no version field.'
419
		);
420
		$this->assertEquals(
421
			'Int',
422
			$schema->fieldSpec(VersionedTest_DataObject::class, 'Version', DataObjectSchema::UNINHERITED),
423
			'The versioned ext adds an Int version field.'
424
		);
425
		$this->assertNull(
426
			$schema->fieldSpec(VersionedTest_Subclass::class, 'Version', DataObjectSchema::UNINHERITED),
427
			'Sub-classes of a versioned model don\'t have a Version field.'
428
		);
429
		$this->assertNull(
430
			$schema->fieldSpec(VersionedTest_AnotherSubclass::class, 'Version', DataObjectSchema::UNINHERITED),
431
			'Sub-classes of a versioned model don\'t have a Version field.'
432
		);
433
		$this->assertEquals(
434
			'Varchar(255)',
435
			$schema->fieldSpec(VersionedTest_UnversionedWithField::class, 'Version', DataObjectSchema::UNINHERITED),
436
			'Models w/o Versioned can have their own Version field.'
437
		);
438
	}
439
440
	/**
441
	 * Test that SQLSelect::queriedTables() applies the version-suffixes properly.
442
	 */
443
	public function testQueriedTables() {
444
		Versioned::set_stage(Versioned::LIVE);
445
446
		$this->assertEquals(array(
447
			'VersionedTest_DataObject_Live',
448
			'VersionedTest_Subclass_Live',
449
		), DataObject::get('VersionedTest_Subclass')->dataQuery()->query()->queriedTables());
450
	}
451
452
	/**
453
	 * Virtual "sleep" that doesn't actually slow execution, only advances DBDateTime::now()
454
	 *
455
	 * @param int $minutes
456
	 */
457 View Code Duplication
	protected function sleep($minutes) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
458
		$now = DBDatetime::now();
459
		$date = DateTime::createFromFormat('Y-m-d H:i:s', $now->getValue());
460
		$date->modify("+{$minutes} minutes");
461
		DBDatetime::set_mock_now($date->format('Y-m-d H:i:s'));
462
	}
463
464
	/**
465
	 * Tests records selected by specific version
466
	 */
467
	public function testGetVersion() {
468
		// Create a few initial versions to ensure this version
469
		// doesn't clash with child versions
470
		$this->sleep(1);
471
		/** @var VersionedTest_DataObject $page2 */
472
		$page2 = $this->objFromFixture('VersionedTest_DataObject', 'page2');
473
		$page2->Title = 'dummy1';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
474
		$page2->write();
475
		$this->sleep(1);
476
		$page2->Title = 'dummy2';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
477
		$page2->write();
478
		$this->sleep(1);
479
		$page2->Title = 'Page 2 - v1';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
480
		$page2->write();
481
		$version1Date = $page2->LastEdited;
482
		$version1 = $page2->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
483
484
		// Create another version where this object and some
485
		// child records have been modified
486
		$this->sleep(1);
487
		/** @var VersionedTest_DataObject $page2a */
488
		$page2a = $this->objFromFixture('VersionedTest_DataObject', 'page2a');
489
		$page2a->Title = 'Page 2a - v2';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
490
		$page2a->write();
491
		$this->sleep(1);
492
		$page2->Title = 'Page 2 - v2';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
493
		$page2->write();
494
		$version2Date = $page2->LastEdited;
495
		$version2 = $page2->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
496
		$this->assertGreaterThan($version1, $version2);
497
		$this->assertDOSEquals(
498
			[
499
				['Title' => 'Page 2a - v2'],
500
				['Title' => 'Page 2b'],
501
			],
502
			$page2->Children()
503
		);
504
505
		// test selecting v1
506
		/** @var VersionedTest_DataObject $page2v1 */
507
		$page2v1 = Versioned::get_version('VersionedTest_DataObject', $page2->ID, $version1);
508
		$this->assertEquals('Page 2 - v1', $page2v1->Title);
509
510
		// When selecting v1, related records should by filtered by
511
		// the modified date of that version
512
		$archiveParms = [
513
			'Versioned.mode' => 'archive',
514
			'Versioned.date' => $version1Date
515
		];
516
		$this->assertEquals($archiveParms, $page2v1->getInheritableQueryParams());
517
		$this->assertArraySubset($archiveParms, $page2v1->Children()->getQueryParams());
518
		$this->assertDOSEquals(
519
			[
520
				['Title' => 'Page 2a'],
521
				['Title' => 'Page 2b'],
522
			],
523
			$page2v1->Children()
524
		);
525
526
		// When selecting v2, we get the same as on stage
527
		/** @var VersionedTest_DataObject $page2v2 */
528
		$page2v2 = Versioned::get_version('VersionedTest_DataObject', $page2->ID, $version2);
529
		$this->assertEquals('Page 2 - v2', $page2v2->Title);
530
531
		// When selecting v2, related records should by filtered by
532
		// the modified date of that version
533
		$archiveParms = [
534
			'Versioned.mode' => 'archive',
535
			'Versioned.date' => $version2Date
536
		];
537
		$this->assertEquals($archiveParms, $page2v2->getInheritableQueryParams());
538
		$this->assertArraySubset($archiveParms, $page2v2->Children()->getQueryParams());
539
		$this->assertDOSEquals(
540
			[
541
				['Title' => 'Page 2a - v2'],
542
				['Title' => 'Page 2b'],
543
			],
544
			$page2v2->Children()
545
		);
546
	}
547
548
	public function testGetVersionWhenClassnameChanged() {
549
		$obj = new VersionedTest_DataObject;
550
		$obj->Name = "test";
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
551
		$obj->write();
552
		$obj->Name = "test2";
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
553
		$obj->ClassName = "VersionedTest_Subclass";
554
		$obj->write();
555
		$subclassVersion = $obj->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
556
557
		$obj->Name = "test3";
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
558
		$obj->ClassName = "VersionedTest_DataObject";
559
		$obj->write();
560
561
		// We should be able to pass the subclass and still get the correct class back
562
		$obj2 = Versioned::get_version("VersionedTest_Subclass", $obj->ID, $subclassVersion);
563
		$this->assertInstanceOf("VersionedTest_Subclass", $obj2);
564
		$this->assertEquals("test2", $obj2->Name);
565
566
		$obj3 = Versioned::get_latest_version("VersionedTest_Subclass", $obj->ID);
567
		$this->assertEquals("test3", $obj3->Name);
568
		$this->assertInstanceOf("VersionedTest_DataObject", $obj3);
569
570
	}
571
572
	public function testArchiveVersion() {
573
574
		// In 2005 this file was created
575
		DBDatetime::set_mock_now('2005-01-01 00:00:00');
576
		$testPage = new VersionedTest_Subclass();
577
		$testPage->Title = 'Archived page';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
578
		$testPage->Content = 'This is the content from 2005';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
579
		$testPage->ExtraField = '2005';
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
580
		$testPage->write();
581
582
		// In 2007 we updated it
583
		DBDatetime::set_mock_now('2007-01-01 00:00:00');
584
		$testPage->Content = "It's 2007 already!";
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
585
		$testPage->ExtraField = '2007';
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
586
		$testPage->write();
587
588
		// In 2009 we updated it again
589
		DBDatetime::set_mock_now('2009-01-01 00:00:00');
590
		$testPage->Content = "I'm enjoying 2009";
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
591
		$testPage->ExtraField = '2009';
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
592
		$testPage->write();
593
594
		// End mock, back to the present day:)
595
		DBDatetime::clear_mock_now();
596
597
		// Test 1 - 2006 Content
598
		singleton('VersionedTest_Subclass')->flushCache(true);
599
		Versioned::set_reading_mode('Archive.2006-01-01 00:00:00');
600
		$testPage2006 = DataObject::get('VersionedTest_Subclass')->filter(array('Title' => 'Archived page'))->first();
601
		$this->assertInstanceOf("VersionedTest_Subclass", $testPage2006);
602
		$this->assertEquals("2005", $testPage2006->ExtraField);
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
603
		$this->assertEquals("This is the content from 2005", $testPage2006->Content);
604
605
		// Test 2 - 2008 Content
606
		singleton('VersionedTest_Subclass')->flushCache(true);
607
		Versioned::set_reading_mode('Archive.2008-01-01 00:00:00');
608
		$testPage2008 = DataObject::get('VersionedTest_Subclass')->filter(array('Title' => 'Archived page'))->first();
609
		$this->assertInstanceOf("VersionedTest_Subclass", $testPage2008);
610
		$this->assertEquals("2007", $testPage2008->ExtraField);
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
611
		$this->assertEquals("It's 2007 already!", $testPage2008->Content);
612
613
		// Test 3 - Today
614
		singleton('VersionedTest_Subclass')->flushCache(true);
615
		Versioned::set_reading_mode('Stage.Stage');
616
		$testPageCurrent = DataObject::get('VersionedTest_Subclass')->filter(array('Title' => 'Archived page'))
617
			->first();
618
		$this->assertInstanceOf("VersionedTest_Subclass", $testPageCurrent);
619
		$this->assertEquals("2009", $testPageCurrent->ExtraField);
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
620
		$this->assertEquals("I'm enjoying 2009", $testPageCurrent->Content);
621
	}
622
623
	public function testAllVersions()
624
	{
625
		// In 2005 this file was created
626
		DBDatetime::set_mock_now('2005-01-01 00:00:00');
627
		$testPage = new VersionedTest_Subclass();
628
		$testPage->Title = 'Archived page';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
629
		$testPage->Content = 'This is the content from 2005';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
630
		$testPage->ExtraField = '2005';
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
631
		$testPage->write();
632
633
		// In 2007 we updated it
634
		DBDatetime::set_mock_now('2007-01-01 00:00:00');
635
		$testPage->Content = "It's 2007 already!";
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
636
		$testPage->ExtraField = '2007';
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
637
		$testPage->write();
638
639
		// Check both versions are returned
640
		$versions = Versioned::get_all_versions('VersionedTest_Subclass', $testPage->ID);
641
		$content = array();
642
		$extraFields = array();
643
		foreach($versions as $version)
644
		{
645
			$content[] = $version->Content;
646
			$extraFields[] = $version->ExtraField;
647
		}
648
649
		$this->assertEquals($versions->Count(), 2, 'All versions returned');
650
		$this->assertEquals($content, array('This is the content from 2005', "It's 2007 already!"),
651
			'Version fields returned');
652
		$this->assertEquals($extraFields, array('2005', '2007'), 'Version fields returned');
653
654
		// In 2009 we updated it again
655
		DBDatetime::set_mock_now('2009-01-01 00:00:00');
656
		$testPage->Content = "I'm enjoying 2009";
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
657
		$testPage->ExtraField = '2009';
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
658
		$testPage->write();
659
660
		// End mock, back to the present day:)
661
		DBDatetime::clear_mock_now();
662
663
		$versions = Versioned::get_all_versions('VersionedTest_Subclass', $testPage->ID);
664
		$content = array();
665
		$extraFields = array();
666
		foreach($versions as $version)
667
		{
668
			$content[] = $version->Content;
669
			$extraFields[] = $version->ExtraField;
670
		}
671
672
		$this->assertEquals($versions->Count(), 3, 'Additional all versions returned');
673
		$this->assertEquals($content,
674
			array('This is the content from 2005', "It's 2007 already!", "I'm enjoying 2009"),
675
			'Additional version fields returned');
676
		$this->assertEquals($extraFields, array('2005', '2007', '2009'), 'Additional version fields returned');
677
	}
678
679
	public function testArchiveRelatedDataWithoutVersioned() {
680
		DBDatetime::set_mock_now('2009-01-01 00:00:00');
681
682
		$relatedData = new VersionedTest_RelatedWithoutVersion();
683
		$relatedData->Name = 'Related Data';
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<VersionedTest_RelatedWithoutVersion>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
684
		$relatedDataId = $relatedData->write();
685
686
		$testData = new VersionedTest_DataObject();
687
		$testData->Title = 'Test';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
688
		$testData->Content = 'Before Content';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
689
		$testData->Related()->add($relatedData);
690
		$id = $testData->write();
691
692
		DBDatetime::set_mock_now('2010-01-01 00:00:00');
693
		$testData->Content = 'After Content';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
694
		$testData->write();
695
696
		Versioned::reading_archived_date('2009-01-01 19:00:00');
697
698
		$fetchedData = VersionedTest_DataObject::get()->byId($id);
699
		$this->assertEquals('Before Content', $fetchedData->Content, 'We see the correct content of the older version');
700
701
		$relatedData = VersionedTest_RelatedWithoutVersion::get()->byId($relatedDataId);
702
		$this->assertEquals(
703
			1,
704
			$relatedData->Related()->count(),
705
			'We have a relation, with no version table, querying it still works'
706
		);
707
	}
708
709
	public function testVersionedWithSingleStage() {
710
		$tables = DB::table_list();
711
		$this->assertContains(
712
			'versionedtest_singlestage',
713
			array_keys($tables),
714
			'Contains base table'
715
		);
716
		$this->assertContains(
717
			'versionedtest_singlestage_versions',
718
			array_keys($tables),
719
			'Contains versions table'
720
		);
721
		$this->assertNotContains(
722
			'versionedtest_singlestage_live',
723
			array_keys($tables),
724
			'Does not contain separate table with _Live suffix'
725
		);
726
		$this->assertNotContains(
727
			'versionedtest_singlestage_stage',
728
			array_keys($tables),
729
			'Does not contain separate table with _Stage suffix'
730
		);
731
732
		Versioned::set_stage(Versioned::DRAFT);
733
		$obj = new VersionedTest_SingleStage(array('Name' => 'MyObj'));
734
		$obj->write();
735
		$this->assertNotNull(
736
			VersionedTest_SingleStage::get()->byID($obj->ID),
737
			'Writes to and reads from default stage if its set explicitly'
738
		);
739
740
		Versioned::set_stage(Versioned::LIVE);
741
		$obj = new VersionedTest_SingleStage(array('Name' => 'MyObj'));
742
		$obj->write();
743
		$this->assertNotNull(
744
			VersionedTest_SingleStage::get()->byID($obj->ID),
745
			'Writes to and reads from default stage even if a non-matching stage is set'
746
		);
747
	}
748
749
	/**
750
	 * Test that publishing processes respects lazy loaded fields
751
	 */
752
	public function testLazyLoadFields() {
753
		$originalMode = Versioned::get_reading_mode();
754
755
		// Generate staging record and retrieve it from stage in live mode
756
		Versioned::set_stage(Versioned::DRAFT);
757
		$obj = new VersionedTest_Subclass();
758
		$obj->Name = 'bob';
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
759
		$obj->ExtraField = 'Field Value';
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
760
		$obj->write();
761
		$objID = $obj->ID;
762
		$filter = sprintf('"VersionedTest_DataObject"."ID" = \'%d\'', Convert::raw2sql($objID));
763
		Versioned::set_stage(Versioned::LIVE);
764
765
		// Check fields are unloaded prior to access
766
		$objLazy = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', $filter, false);
767
		$lazyFields = $objLazy->getQueriedDatabaseFields();
768
		$this->assertTrue(isset($lazyFields['ExtraField_Lazy']));
769
		$this->assertEquals('VersionedTest_Subclass', $lazyFields['ExtraField_Lazy']);
770
771
		// Check lazy loading works when viewing a Stage object in Live mode
772
		$this->assertEquals('Field Value', $objLazy->ExtraField);
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
773
774
		// Test that writeToStage respects lazy loaded fields
775
		$objLazy = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', $filter, false);
776
		$objLazy->writeToStage('Live');
777
		$objLive = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Live', $filter, false);
778
		$liveLazyFields = $objLive->getQueriedDatabaseFields();
779
780
		// Check fields are unloaded prior to access
781
		$this->assertTrue(isset($liveLazyFields['ExtraField_Lazy']));
782
		$this->assertEquals('VersionedTest_Subclass', $liveLazyFields['ExtraField_Lazy']);
783
784
		// Check that live record has original value
785
		$this->assertEquals('Field Value', $objLive->ExtraField);
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
786
787
		Versioned::set_reading_mode($originalMode);
788
	}
789
790
	public function testLazyLoadFieldsRetrieval() {
791
		// Set reading mode to Stage
792
		Versioned::set_stage(Versioned::DRAFT);
793
794
		// Create object only in reading stage
795
		$original = new VersionedTest_Subclass();
796
		$original->ExtraField = 'Foo';
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
797
		$original->write();
798
799
		// Query for object using base class
800
		$query = VersionedTest_DataObject::get()->filter('ID', $original->ID);
801
802
		// Set reading mode to Live
803
		Versioned::set_stage(Versioned::LIVE);
804
805
		$fetched = $query->first();
806
		$this->assertTrue($fetched instanceof VersionedTest_Subclass);
807
		$this->assertEquals($original->ID, $fetched->ID); // Eager loaded
808
		$this->assertEquals($original->ExtraField, $fetched->ExtraField); // Lazy loaded
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
809
	}
810
811
	/**
812
	 * Tests that reading mode persists between requests
813
	 */
814
	public function testReadingPersistent() {
815
		$session = Injector::inst()->create('SilverStripe\\Control\\Session', array());
816
		$adminID = $this->logInWithPermission('ADMIN');
817
		$session->inst_set('loggedInAs', $adminID);
818
819
		// Set to stage
820
		Director::test('/?stage=Stage', null, $session);
821
		$this->assertEquals(
822
				'Stage.Stage',
823
				$session->inst_get('readingMode'),
824
				'Check querystring changes reading mode to Stage'
825
		);
826
		Director::test('/', null, $session);
827
		$this->assertEquals(
828
				'Stage.Stage',
829
				$session->inst_get('readingMode'),
830
				'Check that subsequent requests in the same session remain in Stage mode'
831
		);
832
833
		// Test live persists
834
		Director::test('/?stage=Live', null, $session);
835
		$this->assertEquals(
836
				'Stage.Live',
837
				$session->inst_get('readingMode'),
838
				'Check querystring changes reading mode to Live'
839
		);
840
		Director::test('/', null, $session);
841
		$this->assertEquals(
842
				'Stage.Live',
843
				$session->inst_get('readingMode'),
844
				'Check that subsequent requests in the same session remain in Live mode'
845
		);
846
847
		// Test that session doesn't redundantly store the default stage if it doesn't need to
848
		$session2 = Injector::inst()->create('SilverStripe\\Control\\Session', array());
849
		$session2->inst_set('loggedInAs', $adminID);
850
		Director::test('/', null, $session2);
851
		$this->assertArrayNotHasKey('readingMode', $session2->inst_changedData());
852
		Director::test('/?stage=Live', null, $session2);
853
		$this->assertArrayNotHasKey('readingMode', $session2->inst_changedData());
854
855
		// Test choose_site_stage
856
		unset($_GET['stage']);
857
		unset($_GET['archiveDate']);
858
		Session::set('readingMode', 'Stage.Stage');
859
		Versioned::choose_site_stage();
860
		$this->assertEquals('Stage.Stage', Versioned::get_reading_mode());
861
		Session::set('readingMode', 'Archive.2014-01-01');
862
		Versioned::choose_site_stage();
863
		$this->assertEquals('Archive.2014-01-01', Versioned::get_reading_mode());
864
		Session::clear('readingMode');
865
		Versioned::choose_site_stage();
866
		$this->assertEquals('Stage.Live', Versioned::get_reading_mode());
867
	}
868
869
	/**
870
	 * Test that stage parameter is blocked by non-administrative users
871
	 */
872
	public function testReadingModeSecurity() {
873
		$this->setExpectedException('SilverStripe\\Control\\HTTPResponse_Exception');
874
		$session = Injector::inst()->create('SilverStripe\\Control\\Session', array());
875
		$result = Director::test('/?stage=Stage', null, $session);
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
876
	}
877
878
	/**
879
	 * Ensures that the latest version of a record is the expected value
880
	 *
881
	 * @param DataObject $record
882
	 * @param int $version
883
	 */
884
	protected function assertRecordHasLatestVersion($record, $version) {
885
		foreach(ClassInfo::ancestry(get_class($record), true) as $table) {
886
			$versionForClass = DB::prepared_query(
887
				$sql = "SELECT MAX(\"Version\") FROM \"{$table}_Versions\" WHERE \"RecordID\" = ?",
888
				array($record->ID)
889
			)->value();
890
			$this->assertEquals($version, $versionForClass, "That the table $table has the latest version $version");
891
		}
892
	}
893
894
	/**
895
	 * Tests that multi-table dataobjects are correctly versioned
896
	 */
897
	public function testWriteToStage() {
898
		// Test subclass with versioned extension directly added
899
		$record = VersionedTest_Subclass::create();
900
		$record->Title = "Test A";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
901
		$record->ExtraField = "Test A";
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
902
		$record->writeToStage("Stage");
903
		$this->assertRecordHasLatestVersion($record, 1);
904
		$record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
905
		$this->assertRecordHasLatestVersion($record, 1);
906
		$record->Title = "Test A2";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
907
		$record->ExtraField = "Test A2";
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
908
		$record->writeToStage("Stage");
909
		$this->assertRecordHasLatestVersion($record, 2);
910
911
		// Test subclass without changes to base class
912
		$record = VersionedTest_Subclass::create();
913
		$record->ExtraField = "Test B";
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
914
		$record->writeToStage("Stage");
915
		$this->assertRecordHasLatestVersion($record, 1);
916
		$record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
917
		$this->assertRecordHasLatestVersion($record, 1);
918
		$record->ExtraField = "Test B2";
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
919
		$record->writeToStage("Stage");
920
		$this->assertRecordHasLatestVersion($record, 2);
921
922
		// Test subclass without changes to sub class
923
		$record = VersionedTest_Subclass::create();
924
		$record->Title = "Test C";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
925
		$record->writeToStage("Stage");
926
		$this->assertRecordHasLatestVersion($record, 1);
927
		$record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
928
		$this->assertRecordHasLatestVersion($record, 1);
929
		$record->Title = "Test C2";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
930
		$record->writeToStage("Stage");
931
		$this->assertRecordHasLatestVersion($record, 2);
932
933
		// Test subclass with versioned extension only added to the base clases
934
		$record = VersionedTest_AnotherSubclass::create();
935
		$record->Title = "Test A";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_AnotherSubclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
936
		$record->AnotherField = "Test A";
0 ignored issues
show
Documentation introduced by
The property AnotherField does not exist on object<VersionedTest_AnotherSubclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
937
		$record->writeToStage("Stage");
938
		$this->assertRecordHasLatestVersion($record, 1);
939
		$record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
940
		$this->assertRecordHasLatestVersion($record, 1);
941
		$record->Title = "Test A2";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_AnotherSubclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
942
		$record->AnotherField = "Test A2";
0 ignored issues
show
Documentation introduced by
The property AnotherField does not exist on object<VersionedTest_AnotherSubclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
943
		$record->writeToStage("Stage");
944
		$this->assertRecordHasLatestVersion($record, 2);
945
946
947
		// Test subclass without changes to base class
948
		$record = VersionedTest_AnotherSubclass::create();
949
		$record->AnotherField = "Test B";
0 ignored issues
show
Documentation introduced by
The property AnotherField does not exist on object<VersionedTest_AnotherSubclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
950
		$record->writeToStage("Stage");
951
		$this->assertRecordHasLatestVersion($record, 1);
952
		$record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
953
		$this->assertRecordHasLatestVersion($record, 1);
954
		$record->AnotherField = "Test B2";
0 ignored issues
show
Documentation introduced by
The property AnotherField does not exist on object<VersionedTest_AnotherSubclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
955
		$record->writeToStage("Stage");
956
		$this->assertRecordHasLatestVersion($record, 2);
957
958
		// Test subclass without changes to sub class
959
		$record = VersionedTest_AnotherSubclass::create();
960
		$record->Title = "Test C";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_AnotherSubclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
961
		$record->writeToStage("Stage");
962
		$this->assertRecordHasLatestVersion($record, 1);
963
		$record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
964
		$this->assertRecordHasLatestVersion($record, 1);
965
		$record->Title = "Test C2";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_AnotherSubclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
966
		$record->writeToStage("Stage");
967
		$this->assertRecordHasLatestVersion($record, 2);
968
	}
969
970
	public function testVersionedHandlesRenamedDataObjectFields(){
971
		Config::inst()->remove('VersionedTest_RelatedWithoutVersion','db','Name','Varchar');
972
973
		Config::inst()->update('VersionedTest_RelatedWithoutVersion','db',array(
974
			"NewField" => "Varchar",
975
		));
976
977
		VersionedTest_RelatedWithoutVersion::add_extension("SilverStripe\\ORM\\Versioning\\Versioned");
978
		$this->resetDBSchema(true);
979
		$testData = new VersionedTest_RelatedWithoutVersion();
980
		$testData->NewField = 'Test';
0 ignored issues
show
Documentation introduced by
The property NewField does not exist on object<VersionedTest_RelatedWithoutVersion>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
981
		$testData->write();
982
	}
983
984
	public function testCanView() {
985
		$public1ID = $this->idFromFixture('VersionedTest_PublicStage', 'public1');
986
		$public2ID = $this->idFromFixture('VersionedTest_PublicViaExtension', 'public2');
987
		$privateID = $this->idFromFixture('VersionedTest_DataObject', 'page1');
988
		$singleID = $this->idFromFixture('VersionedTest_SingleStage', 'single');
989
990
		// Test that all (and only) public pages are viewable in stage mode
991
		Session::clear("loggedInAs");
992
		Versioned::set_stage(Versioned::DRAFT);
993
		$public1 = Versioned::get_one_by_stage('VersionedTest_PublicStage', 'Stage', array('"ID"' => $public1ID));
0 ignored issues
show
Documentation introduced by
array('"ID"' => $public1ID) is of type array<string,integer,{"\"ID\"":"integer"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
994
		$public2 = Versioned::get_one_by_stage('VersionedTest_PublicViaExtension', 'Stage', array('"ID"' => $public2ID));
0 ignored issues
show
Documentation introduced by
array('"ID"' => $public2ID) is of type array<string,integer,{"\"ID\"":"integer"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
995
		$private = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', array('"ID"' => $privateID));
0 ignored issues
show
Documentation introduced by
array('"ID"' => $privateID) is of type array<string,integer,{"\"ID\"":"integer"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
996
		// Also test an object that has just a single-stage (eg. is only versioned)
997
		$single = Versioned::get_one_by_stage('VersionedTest_SingleStage', 'Stage', array('"ID"' => $singleID));
0 ignored issues
show
Documentation introduced by
array('"ID"' => $singleID) is of type array<string,integer,{"\"ID\"":"integer"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
998
999
1000
		$this->assertTrue($public1->canView());
1001
		$this->assertTrue($public2->canView());
1002
		$this->assertFalse($private->canView());
1003
		$this->assertFalse($single->canView());
1004
1005
		// Adjusting the current stage should not allow objects loaded in stage to be viewable
1006
		Versioned::set_stage(Versioned::LIVE);
1007
		$this->assertTrue($public1->canView());
1008
		$this->assertTrue($public2->canView());
1009
		$this->assertFalse($private->canView());
1010
		$this->assertFalse($single->canView());
1011
1012
		// Writing the private page to live should be fine though
1013
		$private->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
1014
		$privateLive = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Live', array('"ID"' => $privateID));
0 ignored issues
show
Documentation introduced by
array('"ID"' => $privateID) is of type array<string,integer,{"\"ID\"":"integer"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1015
		$this->assertTrue($private->canView());
1016
		$this->assertTrue($privateLive->canView());
1017
1018
		// But if the private version becomes different to the live version, it's once again disallowed
1019
		Versioned::set_stage(Versioned::DRAFT);
1020
		$private->Title = 'Secret Title';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1021
		$private->write();
1022
		$this->assertFalse($private->canView());
1023
		$this->assertTrue($privateLive->canView());
1024
1025
		// And likewise, viewing a live page (when mode is draft) should be ok
1026
		Versioned::set_stage(Versioned::DRAFT);
1027
		$this->assertFalse($private->canView());
1028
		$this->assertTrue($privateLive->canView());
1029
1030
		// Logging in as admin should allow all permissions
1031
		$this->logInWithPermission('ADMIN');
1032
		Versioned::set_stage(Versioned::DRAFT);
1033
		$this->assertTrue($public1->canView());
1034
		$this->assertTrue($public2->canView());
1035
		$this->assertTrue($private->canView());
1036
		$this->assertTrue($single->canView());
1037
	}
1038
1039
	public function testCanViewStage() {
1040
		$public = $this->objFromFixture('VersionedTest_PublicStage', 'public1');
1041
		$private = $this->objFromFixture('VersionedTest_DataObject', 'page1');
1042
		Session::clear("loggedInAs");
1043
		Versioned::set_stage(Versioned::DRAFT);
1044
1045
		// Test that all (and only) public pages are viewable in stage mode
1046
		// Unpublished records are not viewable in live regardless of permissions
1047
		$this->assertTrue($public->canViewStage('Stage'));
1048
		$this->assertFalse($private->canViewStage('Stage'));
1049
		$this->assertFalse($public->canViewStage('Live'));
1050
		$this->assertFalse($private->canViewStage('Live'));
1051
1052
		// Writing records to live should make both stage and live modes viewable
1053
		$private->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
1054
		$public->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
1055
		$this->assertTrue($public->canViewStage('Stage'));
1056
		$this->assertTrue($private->canViewStage('Stage'));
1057
		$this->assertTrue($public->canViewStage('Live'));
1058
		$this->assertTrue($private->canViewStage('Live'));
1059
1060
		// If the draft mode changes, the live mode remains public, although the updated
1061
		// draft mode is secured for non-public records.
1062
		$private->Title = 'Secret Title';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1063
		$private->write();
1064
		$public->Title = 'Public Title';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1065
		$public->write();
1066
		$this->assertTrue($public->canViewStage('Stage'));
1067
		$this->assertFalse($private->canViewStage('Stage'));
1068
		$this->assertTrue($public->canViewStage('Live'));
1069
		$this->assertTrue($private->canViewStage('Live'));
1070
	}
1071
1072
	/**
1073
	 * Values that are overwritten with null are saved to the _versions table correctly.
1074
	 */
1075
	public function testWriteNullValueToVersion()
1076
	{
1077
		$record = VersionedTest_Subclass::create();
1078
		$record->Title = "Test A";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1079
		$record->write();
1080
1081
		$version = Versioned::get_latest_version($record->ClassName, $record->ID);
1082
1083
		$this->assertEquals(1, $version->Version);
1084
		$this->assertEquals($record->Title, $version->Title);
1085
1086
		$record->Title = null;
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1087
		$record->write();
1088
1089
		$version = Versioned::get_latest_version($record->ClassName, $record->ID);
1090
1091
		$this->assertEquals(2, $version->Version);
1092
		$this->assertEquals($record->Title, $version->Title);
1093
	}
1094
1095
1096
1097
	public function testStageStates() {
1098
		// newly created page
1099
		$createdPage = new VersionedTest_DataObject();
1100
		$createdPage->write();
1101
		$this->assertTrue($createdPage->isOnDraft());
1102
		$this->assertFalse($createdPage->isPublished());
1103
		$this->assertTrue($createdPage->isOnDraftOnly());
1104
		$this->assertTrue($createdPage->isModifiedOnDraft());
1105
1106
		// published page
1107
		$publishedPage = new VersionedTest_DataObject();
1108
		$publishedPage->write();
1109
		$publishedPage->copyVersionToStage('Stage','Live');
1110
		$this->assertTrue($publishedPage->isOnDraft());
1111
		$this->assertTrue($publishedPage->isPublished());
1112
		$this->assertFalse($publishedPage->isOnDraftOnly());
1113
		$this->assertFalse($publishedPage->isOnLiveOnly());
1114
		$this->assertFalse($publishedPage->isModifiedOnDraft());
1115
1116
		// published page, deleted from stage
1117
		$deletedFromDraftPage = new VersionedTest_DataObject();
1118
		$deletedFromDraftPage->write();
1119
		$deletedFromDraftPage->copyVersionToStage('Stage','Live');
1120
		$deletedFromDraftPage->deleteFromStage('Stage');
1121
		$this->assertFalse($deletedFromDraftPage->isArchived());
1122
		$this->assertFalse($deletedFromDraftPage->isOnDraft());
1123
		$this->assertTrue($deletedFromDraftPage->isPublished());
1124
		$this->assertFalse($deletedFromDraftPage->isOnDraftOnly());
1125
		$this->assertTrue($deletedFromDraftPage->isOnLiveOnly());
1126
		$this->assertFalse($deletedFromDraftPage->isModifiedOnDraft());
1127
1128
		// published page, deleted from live
1129
		$deletedFromLivePage = new VersionedTest_DataObject();
1130
		$deletedFromLivePage->write();
1131
		$deletedFromLivePage->copyVersionToStage('Stage','Live');
1132
		$deletedFromLivePage->deleteFromStage('Live');
1133
		$this->assertFalse($deletedFromLivePage->isArchived());
1134
		$this->assertTrue($deletedFromLivePage->isOnDraft());
1135
		$this->assertFalse($deletedFromLivePage->isPublished());
1136
		$this->assertTrue($deletedFromLivePage->isOnDraftOnly());
1137
		$this->assertFalse($deletedFromLivePage->isOnLiveOnly());
1138
		$this->assertTrue($deletedFromLivePage->isModifiedOnDraft());
1139
1140
		// published page, deleted from both stages
1141
		$deletedFromAllStagesPage = new VersionedTest_DataObject();
1142
		$deletedFromAllStagesPage->write();
1143
		$deletedFromAllStagesPage->copyVersionToStage('Stage','Live');
1144
		$deletedFromAllStagesPage->doArchive();
1145
		$this->assertTrue($deletedFromAllStagesPage->isArchived());
1146
		$this->assertFalse($deletedFromAllStagesPage->isOnDraft());
1147
		$this->assertFalse($deletedFromAllStagesPage->isPublished());
1148
		$this->assertFalse($deletedFromAllStagesPage->isOnDraftOnly());
1149
		$this->assertFalse($deletedFromAllStagesPage->isOnLiveOnly());
1150
		$this->assertFalse($deletedFromAllStagesPage->isModifiedOnDraft());
1151
1152
		// published page, modified
1153
		$modifiedOnDraftPage = new VersionedTest_DataObject();
1154
		$modifiedOnDraftPage->write();
1155
		$modifiedOnDraftPage->copyVersionToStage('Stage','Live');
1156
		$modifiedOnDraftPage->Content = 'modified';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<VersionedTest_DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1157
		$modifiedOnDraftPage->write();
1158
		$this->assertFalse($modifiedOnDraftPage->isArchived());
1159
		$this->assertTrue($modifiedOnDraftPage->isOnDraft());
1160
		$this->assertTrue($modifiedOnDraftPage->isPublished());
1161
		$this->assertFalse($modifiedOnDraftPage->isOnDraftOnly());
1162
		$this->assertFalse($modifiedOnDraftPage->isOnLiveOnly());
1163
		$this->assertTrue($modifiedOnDraftPage->isModifiedOnDraft());
1164
	}
1165
}
1166
1167
1168
/**
1169
 * @method VersionedTest_DataObject Parent()
1170
 * @method HasManyList Children()
1171
 * @method ManyManyList Related()
1172
 *
1173
 * @package framework
1174
 * @subpackage tests
1175
 * @mixin Versioned
1176
 */
1177
class VersionedTest_DataObject extends DataObject implements TestOnly {
1178
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1179
		"Name" => "Varchar",
1180
		'Title' => 'Varchar',
1181
		'Content' => 'HTMLText',
1182
	);
1183
1184
	private static $extensions = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1185
		"SilverStripe\\ORM\\Versioning\\Versioned",
1186
	);
1187
1188
	private static $has_one = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1189
		'Parent' => 'VersionedTest_DataObject',
1190
	);
1191
1192
	private static $has_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1193
		'Children' => 'VersionedTest_DataObject',
1194
	);
1195
1196
	private static $many_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1197
		'Related' => 'VersionedTest_RelatedWithoutVersion',
1198
	);
1199
1200
1201 View Code Duplication
	public function canView($member = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1202
		$extended = $this->extendedCan(__FUNCTION__, $member);
1203
		if($extended !== null) {
1204
			return $extended;
1205
		}
1206
		return true;
1207
	}
1208
}
1209
1210
/**
1211
 * @mixin Versioned
1212
 */
1213
class VersionedTest_WithIndexes extends DataObject implements TestOnly {
1214
1215
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1216
		'UniqA' => 'Int',
1217
		'UniqS' => 'Int',
1218
	);
1219
	private static $extensions = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1220
		"SilverStripe\\ORM\\Versioning\\Versioned"
1221
	);
1222
	private static $indexes = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1223
		'UniqS_idx' => 'unique ("UniqS")',
1224
		'UniqA_idx' => array('type' => 'unique', 'name' => 'UniqA_idx', 'value' => '"UniqA"',),
1225
	);
1226
1227
}
1228
1229
/**
1230
 * @package framework
1231
 * @subpackage tests
1232
 */
1233
class VersionedTest_RelatedWithoutVersion extends DataObject implements TestOnly {
1234
1235
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1236
		'Name' => 'Varchar'
1237
	);
1238
1239
	private static $belongs_many_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1240
		'Related' => 'VersionedTest_DataObject'
1241
	);
1242
1243
}
1244
1245
/**
1246
 * @package framework
1247
 * @subpackage tests
1248
 */
1249
class VersionedTest_Subclass extends VersionedTest_DataObject implements TestOnly {
1250
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1251
		"ExtraField" => "Varchar",
1252
	);
1253
}
1254
1255
/**
1256
 * @package framework
1257
 * @subpackage tests
1258
 */
1259
class VersionedTest_AnotherSubclass extends VersionedTest_DataObject implements TestOnly {
1260
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1261
		"AnotherField" => "Varchar"
1262
	);
1263
}
1264
1265
/**
1266
 * @package framework
1267
 * @subpackage tests
1268
 */
1269
class VersionedTest_UnversionedWithField extends DataObject implements TestOnly {
1270
	private static $db = array('Version' => 'Varchar(255)');
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1271
}
1272
1273
/**
1274
 * @mixin Versioned
1275
 */
1276
class VersionedTest_SingleStage extends DataObject implements TestOnly {
1277
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1278
		'Name' => 'Varchar'
1279
	);
1280
1281
	private static $extensions = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1282
		'SilverStripe\ORM\Versioning\Versioned("Versioned")'
1283
	);
1284
}
1285
1286
/**
1287
 * Versioned dataobject with public stage mode
1288
 *
1289
 * @mixin Versioned
1290
 */
1291
class VersionedTest_PublicStage extends DataObject implements TestOnly {
1292
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1293
		'Title' => 'Varchar'
1294
	);
1295
1296
	private static $extensions = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1297
		"SilverStripe\\ORM\\Versioning\\Versioned"
1298
	);
1299
1300 View Code Duplication
	public function canView($member = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1301
		$extended = $this->extendedCan(__FUNCTION__, $member);
1302
		if($extended !== null) {
1303
			return $extended;
1304
		}
1305
		return true;
1306
	}
1307
1308
	public function canViewVersioned($member = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $member is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1309
		// All non-live modes are public
1310
		return true;
1311
	}
1312
}
1313
1314
/**
1315
 * Public access is provided via extension rather than overriding canViewVersioned
1316
 *
1317
 * @mixin Versioned
1318
 * @mixin VersionedTest_PublicExtension
1319
 */
1320
class VersionedTest_PublicViaExtension extends DataObject implements TestOnly {
1321
1322 View Code Duplication
	public function canView($member = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1323
		$extended = $this->extendedCan(__FUNCTION__, $member);
1324
		if($extended !== null) {
1325
			return $extended;
1326
		}
1327
		return true;
1328
	}
1329
1330
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1331
		'Title' => 'Varchar'
1332
	);
1333
1334
	private static $extensions = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1335
		"SilverStripe\\ORM\\Versioning\\Versioned",
1336
		"VersionedTest_PublicExtension"
1337
	);
1338
}
1339
1340
/**
1341
 * Alters stage mode of extended object to be public
1342
 */
1343
class VersionedTest_PublicExtension extends DataExtension implements TestOnly {
1344
	public function canViewNonLive($member = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $member is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1345
		return true;
1346
	}
1347
}
1348
1349
/**
1350
 * @mixin Versioned
1351
 */
1352
class VersionedTest_CustomTable extends DataObject implements TestOnly {
1353
	private static $db = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1354
		'Title' => 'Varchar'
1355
	];
1356
1357
	private static $table_name = 'VTCustomTable';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1358
1359
	private static $extensions = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1360
		"SilverStripe\\ORM\\Versioning\\Versioned",
1361
	];
1362
}
1363