Completed
Push — master ( 1e7030...a596bd )
by Loz
11:34
created

VersionedTest::testLazyLoadFieldsRetrieval()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 11
c 1
b 1
f 0
nc 1
nop 0
dl 0
loc 20
rs 9.4285
1
<?php
2
3
use SilverStripe\Model\FieldType\DBDatetime;
4
5
/**
6
 * @package framework
7
 * @subpackage tests
8
 */
9
class VersionedTest extends SapphireTest {
10
11
	protected static $fixture_file = 'VersionedTest.yml';
12
13
	protected $extraDataObjects = array(
14
		'VersionedTest_DataObject',
15
		'VersionedTest_Subclass',
16
		'VersionedTest_AnotherSubclass',
17
		'VersionedTest_RelatedWithoutVersion',
18
		'VersionedTest_SingleStage',
19
		'VersionedTest_WithIndexes',
20
		'VersionedTest_PublicStage',
21
		'VersionedTest_PublicViaExtension',
22
	);
23
24
	protected $requiredExtensions = array(
25
		"VersionedTest_DataObject" => array('Versioned'),
26
		"VersionedTest_WithIndexes" => array('Versioned'),
27
	);
28
29
	public function testUniqueIndexes() {
30
		$tableExpectations = array(
31
			'VersionedTest_WithIndexes' =>
32
				array('value' => true, 'message' => 'Unique indexes are unique in main table'),
33
			'VersionedTest_WithIndexes_versions' =>
34
				array('value' => false, 'message' => 'Unique indexes are no longer unique in _versions table'),
35
			'VersionedTest_WithIndexes_Live' =>
36
				array('value' => false, 'message' => 'Unique indexes are no longer unique in _Live table'),
37
		);
38
39
		// Test each table's performance
40
		foreach ($tableExpectations as $tableName => $expectation) {
41
			$indexes = DB::get_schema()->indexList($tableName);
42
43
			// Check for presence of all unique indexes
44
			$indexColumns = array_map(function($index) {
45
				return $index['value'];
46
			}, $indexes);
47
			sort($indexColumns);
48
			$expectedColumns = array('"UniqA"', '"UniqS"');
49
			$this->assertEquals(
50
					array_values($expectedColumns),
51
					array_values(array_intersect($indexColumns, $expectedColumns)),
52
					"$tableName has both indexes");
53
54
			// Check unique -> non-unique conversion
55
			foreach ($indexes as $indexKey => $indexSpec) {
56
				if (in_array($indexSpec['value'], $expectedColumns)) {
57
					$isUnique = $indexSpec['type'] === 'unique';
58
					$this->assertEquals($isUnique, $expectation['value'], $expectation['message']);
59
}
60
			}
61
		}
62
	}
63
64
	public function testDeletingOrphanedVersions() {
65
		$obj = new VersionedTest_Subclass();
66
		$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...
67
		$obj->write();
68
		$obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
69
70
		$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...
71
		$obj->write();
72
		$obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
73
74
		$versions = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\""
75
			. " WHERE \"RecordID\" = '$obj->ID'")->value();
76
77
		$this->assertGreaterThan(0, $versions, 'At least 1 version exists in the history of the page');
78
79
		// Force orphaning of all versions created earlier, only on parent record.
80
		// The child versiones table should still have the correct relationship
81
		DB::query("DELETE FROM \"VersionedTest_DataObject_versions\" WHERE \"RecordID\" = $obj->ID");
82
83
		// insert a record with no primary key (ID)
84
		DB::query("INSERT INTO \"VersionedTest_DataObject_versions\" (\"RecordID\") VALUES ($obj->ID)");
85
86
		// run the script which should clean that up
87
		$obj->augmentDatabase();
88
89
		$versions = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\""
90
			. " WHERE \"RecordID\" = '$obj->ID'")->value();
91
		$this->assertEquals(0, $versions, 'Orphaned versions on child tables are removed');
92
93
		// test that it doesn't delete records that we need
94
		$obj->write();
95
		$obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
96
97
		$count = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\""
98
			. " WHERE \"RecordID\" = '$obj->ID'")->value();
99
		$obj->augmentDatabase();
100
101
		$count2 = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\""
102
			. " WHERE \"RecordID\" = '$obj->ID'")->value();
103
104
		$this->assertEquals($count, $count2);
105
	}
106
107
	/**
108
	 * Test that publishing from invalid stage will throw exception
109
	 */
110
	public function testInvalidPublish() {
111
		$obj = new VersionedTest_Subclass();
112
		$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...
113
		$obj->write();
114
		$this->setExpectedException(
115
			'InvalidArgumentException',
116
			"Can't find VersionedTest_DataObject#{$obj->ID} in stage Live"
117
		);
118
119
		// Fail publishing from live to stage
120
		$obj->copyVersionToStage(Versioned::LIVE, Versioned::DRAFT);
121
	}
122
123
	public function testDuplicate() {
124
		$obj1 = new VersionedTest_Subclass();
125
		$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...
126
		$obj1->write(); // version 1
127
		$obj1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
128
		$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...
129
		$obj1->write(); // version 2
130
131
		// Make duplicate
132
		$obj2 = $obj1->duplicate();
133
134
		// Check records differ
135
		$this->assertNotEquals($obj1->ID, $obj2->ID);
136
		$this->assertEquals(2, $obj1->Version);
137
		$this->assertEquals(1, $obj2->Version);
138
	}
139
140
	public function testForceChangeUpdatesVersion() {
141
		$obj = new VersionedTest_DataObject();
142
		$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...
143
		$obj->write();
144
145
		$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...
146
		$obj->forceChange();
147
		$obj->write();
148
149
		$this->assertTrue(
150
			($obj->Version > $oldVersion),
151
			"A object Version is increased when just calling forceChange() without any other changes"
152
		);
153
	}
154
155
	/**
156
	 * Test Versioned::get_including_deleted()
157
	 */
158
	public function testGetIncludingDeleted() {
159
		// Get all ids of pages
160
		$allPageIDs = DataObject::get(
161
			'VersionedTest_DataObject',
162
			"\"ParentID\" = 0", "\"VersionedTest_DataObject\".\"ID\" ASC"
163
		)->column('ID');
164
165
		// Modify a page, ensuring that the Version ID and Record ID will differ,
166
		// and then subsequently delete it
167
		$targetPage = $this->objFromFixture('VersionedTest_DataObject', 'page3');
168
		$targetPage->Content = 'To be deleted';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<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...
169
		$targetPage->write();
170
		$targetPage->delete();
171
172
		// Get all items, ignoring deleted
173
		$remainingPages = DataObject::get("VersionedTest_DataObject", "\"ParentID\" = 0",
174
			"\"VersionedTest_DataObject\".\"ID\" ASC");
175
		// Check that page 3 has gone
176
		$this->assertNotNull($remainingPages);
177
		$this->assertEquals(array("Page 1", "Page 2", "Subclass Page 1"), $remainingPages->column('Title'));
178
179
		// Get all including deleted
180
		$allPages = Versioned::get_including_deleted("VersionedTest_DataObject", "\"ParentID\" = 0",
181
			"\"VersionedTest_DataObject\".\"ID\" ASC");
182
		// Check that page 3 is still there
183
		$this->assertEquals(array("Page 1", "Page 2", "Page 3", "Subclass Page 1"), $allPages->column('Title'));
184
185
		// Check that the returned pages have the correct IDs
186
		$this->assertEquals($allPageIDs, $allPages->column('ID'));
187
188
		// Check that this still works if we switch to reading the other stage
189
		Versioned::set_stage(Versioned::LIVE);
190
		$allPages = Versioned::get_including_deleted("VersionedTest_DataObject", "\"ParentID\" = 0",
191
			"\"VersionedTest_DataObject\".\"ID\" ASC");
192
		$this->assertEquals(array("Page 1", "Page 2", "Page 3", "Subclass Page 1"), $allPages->column('Title'));
193
194
		// Check that the returned pages still have the correct IDs
195
		$this->assertEquals($allPageIDs, $allPages->column('ID'));
196
	}
197
198
	public function testVersionedFieldsAdded() {
199
		$obj = new VersionedTest_DataObject();
200
		// Check that the Version column is added as a full-fledged column
201
		$this->assertInstanceOf('SilverStripe\\Model\\FieldType\\DBInt', $obj->dbObject('Version'));
202
203
		$obj2 = new VersionedTest_Subclass();
204
		// Check that the Version column is added as a full-fledged column
205
		$this->assertInstanceOf('SilverStripe\\Model\\FieldType\\DBInt', $obj2->dbObject('Version'));
206
	}
207
208
	public function testVersionedFieldsNotInCMS() {
209
		$obj = new VersionedTest_DataObject();
210
211
		// the version field in cms causes issues with Versioned::augmentWrite()
212
		$this->assertNull($obj->getCMSFields()->dataFieldByName('Version'));
213
	}
214
215
	public function testPublishCreateNewVersion() {
216
		$page1 = $this->objFromFixture('VersionedTest_DataObject', 'page1');
217
		$page1->Content = 'orig';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<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...
218
		$page1->write();
219
		$firstVersion = $page1->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<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...
220
		$page1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE, false);
221
		$this->assertEquals(
222
			$firstVersion,
223
			$page1->Version,
224
			'publish() with $createNewVersion=FALSE does not create a new version'
225
		);
226
227
		$page1->Content = 'changed';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<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...
228
		$page1->write();
229
		$secondVersion = $page1->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<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...
230
		$this->assertTrue($firstVersion < $secondVersion, 'write creates new version');
231
232
		$page1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE, true);
233
		$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<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...
234
		$liveVersion = Versioned::get_versionnumber_by_stage('VersionedTest_DataObject', 'Live', $page1->ID);
235
		$stageVersion = Versioned::get_versionnumber_by_stage('VersionedTest_DataObject', 'Stage', $page1->ID);
236
		$this->assertTrue(
237
			$secondVersion < $thirdVersion,
238
			'publish() with $createNewVersion=TRUE creates a new version'
239
		);
240
		$this->assertEquals(
241
			$liveVersion,
242
			$thirdVersion,
243
			'publish() with $createNewVersion=TRUE publishes to live'
244
		);
245
		$this->assertEquals(
246
			$stageVersion,
247
			$secondVersion,
248
			'publish() with $createNewVersion=TRUE does not affect stage'
249
		);
250
	}
251
252
	public function testRollbackTo() {
253
		$page1 = $this->objFromFixture('VersionedTest_AnotherSubclass', 'subclass1');
254
		$page1->Content = 'orig';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<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...
255
		$page1->write();
256
		$page1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
257
		$origVersion = $page1->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<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...
258
259
		$page1->Content = 'changed';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<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...
260
		$page1->write();
261
		$page1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
262
		$changedVersion = $page1->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<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...
263
264
		$page1->doRollbackTo($origVersion);
265
		$page1 = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', array(
266
			'"VersionedTest_DataObject"."ID" = ?' => $page1->ID
267
		));
268
269
		$this->assertTrue($page1->Version == $changedVersion + 1, 'Create a new higher version number');
270
		$this->assertEquals('orig', $page1->Content, 'Copies the content from the old version');
271
272
		// check db entries
273
		$version = DB::prepared_query("SELECT MAX(\"Version\") FROM \"VersionedTest_DataObject_versions\" WHERE \"RecordID\" = ?",
274
			array($page1->ID)
275
		)->value();
276
		$this->assertEquals($page1->Version, $version, 'Correct entry in VersionedTest_DataObject_versions');
277
278
		$version = DB::prepared_query("SELECT MAX(\"Version\") FROM \"VersionedTest_AnotherSubclass_versions\" WHERE \"RecordID\" = ?",
279
			array($page1->ID)
280
		)->value();
281
		$this->assertEquals($page1->Version, $version, 'Correct entry in VersionedTest_AnotherSubclass_versions');
282
	}
283
284
	public function testDeleteFromStage() {
285
		$page1 = $this->objFromFixture('VersionedTest_DataObject', 'page1');
286
		$pageID = $page1->ID;
287
288
		$page1->Content = 'orig';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<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...
289
		$page1->write();
290
		$page1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
291
292
		$this->assertEquals(1,
293
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value());
294
		$this->assertEquals(1,
295
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value());
296
297
		$page1->deleteFromStage('Live');
298
299
		// Confirm that deleteFromStage() doesn't manipulate the original record
300
		$this->assertEquals($pageID, $page1->ID);
301
302
		$this->assertEquals(1,
303
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value());
304
		$this->assertEquals(0,
305
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value());
306
307
		$page1->delete();
308
309
		$this->assertEquals(0, $page1->ID);
310
		$this->assertEquals(0,
311
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value());
312
		$this->assertEquals(0,
313
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value());
314
	}
315
316
	public function testWritingNewToStage() {
317
		$origStage = Versioned::get_stage();
318
319
		Versioned::set_stage(Versioned::DRAFT);
320
		$page = new VersionedTest_DataObject();
321
		$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...
322
		$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...
323
		$page->write();
324
325
		$live = Versioned::get_by_stage('VersionedTest_DataObject', 'Live', array(
326
			'"VersionedTest_DataObject_Live"."ID"' => $page->ID
327
		));
328
		$this->assertEquals(0, $live->count());
329
330
		$stage = Versioned::get_by_stage('VersionedTest_DataObject', 'Stage',array(
331
			'"VersionedTest_DataObject"."ID"' => $page->ID
332
		));
333
		$this->assertEquals(1, $stage->count());
334
		$this->assertEquals($stage->First()->Title, 'testWritingNewToStage');
335
336
		Versioned::set_stage($origStage);
337
	}
338
339
	/**
340
	 * This tests for the situation described in the ticket #5596.
341
	 * Writing new Page to live first creates a row in VersionedTest_DataObject table (to get the new ID),
342
	 * then "changes it's mind" in Versioned and writes VersionedTest_DataObject_Live. It does not remove
343
	 * the VersionedTest_DataObject record though.
344
	 */
345
	public function testWritingNewToLive() {
346
		$origStage = Versioned::get_stage();
347
348
		Versioned::set_stage(Versioned::LIVE);
349
		$page = new VersionedTest_DataObject();
350
		$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...
351
		$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...
352
		$page->write();
353
354
		$live = Versioned::get_by_stage('VersionedTest_DataObject', 'Live',array(
355
			'"VersionedTest_DataObject_Live"."ID"' => $page->ID
356
		));
357
		$this->assertEquals(1, $live->count());
358
		$this->assertEquals($live->First()->Title, 'testWritingNewToLive');
359
360
		$stage = Versioned::get_by_stage('VersionedTest_DataObject', 'Stage',array(
361
			'"VersionedTest_DataObject"."ID"' => $page->ID
362
		));
363
		$this->assertEquals(0, $stage->count());
364
365
		Versioned::set_stage($origStage);
366
	}
367
368
	/**
369
	 * Tests DataObject::hasOwnTableDatabaseField
370
	 */
371
	public function testHasOwnTableDatabaseFieldWithVersioned() {
372
		$noversion    = new DataObject();
373
		$versioned    = new VersionedTest_DataObject();
374
		$versionedSub = new VersionedTest_Subclass();
375
		$versionedAno = new VersionedTest_AnotherSubclass();
376
		$versionField = new VersionedTest_UnversionedWithField();
377
378
		$this->assertFalse(
379
			(bool) $noversion->hasOwnTableDatabaseField('Version'),
380
			'Plain models have no version field.'
381
		);
382
		$this->assertEquals(
383
			'Int', $versioned->hasOwnTableDatabaseField('Version'),
384
			'The versioned ext adds an Int version field.'
385
		);
386
		$this->assertEquals(
387
			null,
388
			$versionedSub->hasOwnTableDatabaseField('Version'),
389
			'Sub-classes of a versioned model don\'t have a Version field.'
390
		);
391
		$this->assertEquals(
392
			null,
393
			$versionedAno->hasOwnTableDatabaseField('Version'),
394
			'Sub-classes of a versioned model don\'t have a Version field.'
395
		);
396
		$this->assertEquals(
397
			'Varchar', $versionField->hasOwnTableDatabaseField('Version'),
398
			'Models w/o Versioned can have their own Version field.'
399
		);
400
	}
401
402
	/**
403
	 * Test that SQLSelect::queriedTables() applies the version-suffixes properly.
404
	 */
405
	public function testQueriedTables() {
406
		Versioned::set_stage(Versioned::LIVE);
407
408
		$this->assertEquals(array(
409
			'VersionedTest_DataObject_Live',
410
			'VersionedTest_Subclass_Live',
411
		), DataObject::get('VersionedTest_Subclass')->dataQuery()->query()->queriedTables());
412
	}
413
414
	/**
415
	 * Virtual "sleep" that doesn't actually slow execution, only advances SS_DateTime::now()
416
	 *
417
	 * @param int $minutes
418
	 */
419
	protected function sleep($minutes) {
420
		$now = SS_Datetime::now();
421
		$date = DateTime::createFromFormat('Y-m-d H:i:s', $now->getValue());
422
		$date->modify("+{$minutes} minutes");
423
		SS_Datetime::set_mock_now($date->format('Y-m-d H:i:s'));
424
	}
425
426
	/**
427
	 * Tests records selected by specific version
428
	 */
429
	public function testGetVersion() {
430
		// Create a few initial versions to ensure this version
431
		// doesn't clash with child versions
432
		$this->sleep(1);
433
		/** @var VersionedTest_DataObject $page2 */
434
		$page2 = $this->objFromFixture('VersionedTest_DataObject', 'page2');
435
		$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...
436
		$page2->write();
437
		$this->sleep(1);
438
		$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...
439
		$page2->write();
440
		$this->sleep(1);
441
		$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...
442
		$page2->write();
443
		$version1Date = $page2->LastEdited;
444
		$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...
445
446
		// Create another version where this object and some
447
		// child records have been modified
448
		$this->sleep(1);
449
		/** @var VersionedTest_DataObject $page2a */
450
		$page2a = $this->objFromFixture('VersionedTest_DataObject', 'page2a');
451
		$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...
452
		$page2a->write();
453
		$this->sleep(1);
454
		$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...
455
		$page2->write();
456
		$version2Date = $page2->LastEdited;
457
		$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...
458
		$this->assertGreaterThan($version1, $version2);
459
		$this->assertDOSEquals(
460
			[
461
				['Title' => 'Page 2a - v2'],
462
				['Title' => 'Page 2b'],
463
			],
464
			$page2->Children()
465
		);
466
467
		// test selecting v1
468
		/** @var VersionedTest_DataObject $page2v1 */
469
		$page2v1 = Versioned::get_version('VersionedTest_DataObject', $page2->ID, $version1);
470
		$this->assertEquals('Page 2 - v1', $page2v1->Title);
471
472
		// When selecting v1, related records should by filtered by
473
		// the modified date of that version
474
		$archiveParms = [
475
			'Versioned.mode' => 'archive',
476
			'Versioned.date' => $version1Date
477
		];
478
		$this->assertEquals($archiveParms, $page2v1->getInheritableQueryParams());
479
		$this->assertArraySubset($archiveParms, $page2v1->Children()->getQueryParams());
480
		$this->assertDOSEquals(
481
			[
482
				['Title' => 'Page 2a'],
483
				['Title' => 'Page 2b'],
484
			],
485
			$page2v1->Children()
486
		);
487
488
		// When selecting v2, we get the same as on stage
489
		/** @var VersionedTest_DataObject $page2v2 */
490
		$page2v2 = Versioned::get_version('VersionedTest_DataObject', $page2->ID, $version2);
491
		$this->assertEquals('Page 2 - v2', $page2v2->Title);
492
493
		// When selecting v2, related records should by filtered by
494
		// the modified date of that version
495
		$archiveParms = [
496
			'Versioned.mode' => 'archive',
497
			'Versioned.date' => $version2Date
498
		];
499
		$this->assertEquals($archiveParms, $page2v2->getInheritableQueryParams());
500
		$this->assertArraySubset($archiveParms, $page2v2->Children()->getQueryParams());
501
		$this->assertDOSEquals(
502
			[
503
				['Title' => 'Page 2a - v2'],
504
				['Title' => 'Page 2b'],
505
			],
506
			$page2v2->Children()
507
		);
508
	}
509
510
	public function testGetVersionWhenClassnameChanged() {
511
		$obj = new VersionedTest_DataObject;
512
		$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...
513
		$obj->write();
514
		$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...
515
		$obj->ClassName = "VersionedTest_Subclass";
516
		$obj->write();
517
		$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...
518
519
		$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...
520
		$obj->ClassName = "VersionedTest_DataObject";
521
		$obj->write();
522
523
		// We should be able to pass the subclass and still get the correct class back
524
		$obj2 = Versioned::get_version("VersionedTest_Subclass", $obj->ID, $subclassVersion);
525
		$this->assertInstanceOf("VersionedTest_Subclass", $obj2);
526
		$this->assertEquals("test2", $obj2->Name);
527
528
		$obj3 = Versioned::get_latest_version("VersionedTest_Subclass", $obj->ID);
529
		$this->assertEquals("test3", $obj3->Name);
530
		$this->assertInstanceOf("VersionedTest_DataObject", $obj3);
531
532
	}
533
534
	public function testArchiveVersion() {
535
536
		// In 2005 this file was created
537
		DBDatetime::set_mock_now('2005-01-01 00:00:00');
538
		$testPage = new VersionedTest_Subclass();
539
		$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...
540
		$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...
541
		$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...
542
		$testPage->write();
543
544
		// In 2007 we updated it
545
		DBDatetime::set_mock_now('2007-01-01 00:00:00');
546
		$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...
547
		$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...
548
		$testPage->write();
549
550
		// In 2009 we updated it again
551
		DBDatetime::set_mock_now('2009-01-01 00:00:00');
552
		$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...
553
		$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...
554
		$testPage->write();
555
556
		// End mock, back to the present day:)
557
		DBDatetime::clear_mock_now();
558
559
		// Test 1 - 2006 Content
560
		singleton('VersionedTest_Subclass')->flushCache(true);
561
		Versioned::set_reading_mode('Archive.2006-01-01 00:00:00');
562
		$testPage2006 = DataObject::get('VersionedTest_Subclass')->filter(array('Title' => 'Archived page'))->first();
563
		$this->assertInstanceOf("VersionedTest_Subclass", $testPage2006);
564
		$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...
565
		$this->assertEquals("This is the content from 2005", $testPage2006->Content);
566
567
		// Test 2 - 2008 Content
568
		singleton('VersionedTest_Subclass')->flushCache(true);
569
		Versioned::set_reading_mode('Archive.2008-01-01 00:00:00');
570
		$testPage2008 = DataObject::get('VersionedTest_Subclass')->filter(array('Title' => 'Archived page'))->first();
571
		$this->assertInstanceOf("VersionedTest_Subclass", $testPage2008);
572
		$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...
573
		$this->assertEquals("It's 2007 already!", $testPage2008->Content);
574
575
		// Test 3 - Today
576
		singleton('VersionedTest_Subclass')->flushCache(true);
577
		Versioned::set_reading_mode('Stage.Stage');
578
		$testPageCurrent = DataObject::get('VersionedTest_Subclass')->filter(array('Title' => 'Archived page'))
579
			->first();
580
		$this->assertInstanceOf("VersionedTest_Subclass", $testPageCurrent);
581
		$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...
582
		$this->assertEquals("I'm enjoying 2009", $testPageCurrent->Content);
583
	}
584
585
	public function testAllVersions()
586
	{
587
		// In 2005 this file was created
588
		DBDatetime::set_mock_now('2005-01-01 00:00:00');
589
		$testPage = new VersionedTest_Subclass();
590
		$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...
591
		$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...
592
		$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...
593
		$testPage->write();
594
595
		// In 2007 we updated it
596
		DBDatetime::set_mock_now('2007-01-01 00:00:00');
597
		$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...
598
		$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...
599
		$testPage->write();
600
601
		// Check both versions are returned
602
		$versions = Versioned::get_all_versions('VersionedTest_Subclass', $testPage->ID);
603
		$content = array();
604
		$extraFields = array();
605
		foreach($versions as $version)
606
		{
607
			$content[] = $version->Content;
608
			$extraFields[] = $version->ExtraField;
609
		}
610
611
		$this->assertEquals($versions->Count(), 2, 'All versions returned');
612
		$this->assertEquals($content, array('This is the content from 2005', "It's 2007 already!"),
613
			'Version fields returned');
614
		$this->assertEquals($extraFields, array('2005', '2007'), 'Version fields returned');
615
616
		// In 2009 we updated it again
617
		DBDatetime::set_mock_now('2009-01-01 00:00:00');
618
		$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...
619
		$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...
620
		$testPage->write();
621
622
		// End mock, back to the present day:)
623
		DBDatetime::clear_mock_now();
624
625
		$versions = Versioned::get_all_versions('VersionedTest_Subclass', $testPage->ID);
626
		$content = array();
627
		$extraFields = array();
628
		foreach($versions as $version)
629
		{
630
			$content[] = $version->Content;
631
			$extraFields[] = $version->ExtraField;
632
		}
633
634
		$this->assertEquals($versions->Count(), 3, 'Additional all versions returned');
635
		$this->assertEquals($content,
636
			array('This is the content from 2005', "It's 2007 already!", "I'm enjoying 2009"),
637
			'Additional version fields returned');
638
		$this->assertEquals($extraFields, array('2005', '2007', '2009'), 'Additional version fields returned');
639
	}
640
641
	public function testArchiveRelatedDataWithoutVersioned() {
642
		DBDatetime::set_mock_now('2009-01-01 00:00:00');
643
644
		$relatedData = new VersionedTest_RelatedWithoutVersion();
645
		$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...
646
		$relatedDataId = $relatedData->write();
647
648
		$testData = new VersionedTest_DataObject();
649
		$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...
650
		$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...
651
		$testData->Related()->add($relatedData);
652
		$id = $testData->write();
653
654
		DBDatetime::set_mock_now('2010-01-01 00:00:00');
655
		$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...
656
		$testData->write();
657
658
		Versioned::reading_archived_date('2009-01-01 19:00:00');
659
660
		$fetchedData = VersionedTest_DataObject::get()->byId($id);
661
		$this->assertEquals('Before Content', $fetchedData->Content, 'We see the correct content of the older version');
662
663
		$relatedData = VersionedTest_RelatedWithoutVersion::get()->byId($relatedDataId);
664
		$this->assertEquals(
665
			1,
666
			$relatedData->Related()->count(),
667
			'We have a relation, with no version table, querying it still works'
668
		);
669
	}
670
671
	public function testVersionedWithSingleStage() {
672
		$tables = DB::table_list();
673
		$this->assertContains(
674
			'versionedtest_singlestage',
675
			array_keys($tables),
676
			'Contains base table'
677
		);
678
		$this->assertContains(
679
			'versionedtest_singlestage_versions',
680
			array_keys($tables),
681
			'Contains versions table'
682
		);
683
		$this->assertNotContains(
684
			'versionedtest_singlestage_live',
685
			array_keys($tables),
686
			'Does not contain separate table with _Live suffix'
687
		);
688
		$this->assertNotContains(
689
			'versionedtest_singlestage_stage',
690
			array_keys($tables),
691
			'Does not contain separate table with _Stage suffix'
692
		);
693
694
		Versioned::set_stage(Versioned::DRAFT);
695
		$obj = new VersionedTest_SingleStage(array('Name' => 'MyObj'));
696
		$obj->write();
697
		$this->assertNotNull(
698
			VersionedTest_SingleStage::get()->byID($obj->ID),
699
			'Writes to and reads from default stage if its set explicitly'
700
		);
701
702
		Versioned::set_stage(Versioned::LIVE);
703
		$obj = new VersionedTest_SingleStage(array('Name' => 'MyObj'));
704
		$obj->write();
705
		$this->assertNotNull(
706
			VersionedTest_SingleStage::get()->byID($obj->ID),
707
			'Writes to and reads from default stage even if a non-matching stage is set'
708
		);
709
	}
710
711
	/**
712
	 * Test that publishing processes respects lazy loaded fields
713
	 */
714
	public function testLazyLoadFields() {
715
		$originalMode = Versioned::get_reading_mode();
716
717
		// Generate staging record and retrieve it from stage in live mode
718
		Versioned::set_stage(Versioned::DRAFT);
719
		$obj = new VersionedTest_Subclass();
720
		$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...
721
		$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...
722
		$obj->write();
723
		$objID = $obj->ID;
724
		$filter = sprintf('"VersionedTest_DataObject"."ID" = \'%d\'', Convert::raw2sql($objID));
725
		Versioned::set_stage(Versioned::LIVE);
726
727
		// Check fields are unloaded prior to access
728
		$objLazy = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', $filter, false);
729
		$lazyFields = $objLazy->getQueriedDatabaseFields();
730
		$this->assertTrue(isset($lazyFields['ExtraField_Lazy']));
731
		$this->assertEquals('VersionedTest_Subclass', $lazyFields['ExtraField_Lazy']);
732
733
		// Check lazy loading works when viewing a Stage object in Live mode
734
		$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...
735
736
		// Test that writeToStage respects lazy loaded fields
737
		$objLazy = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', $filter, false);
738
		$objLazy->writeToStage('Live');
739
		$objLive = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Live', $filter, false);
740
		$liveLazyFields = $objLive->getQueriedDatabaseFields();
741
742
		// Check fields are unloaded prior to access
743
		$this->assertTrue(isset($liveLazyFields['ExtraField_Lazy']));
744
		$this->assertEquals('VersionedTest_Subclass', $liveLazyFields['ExtraField_Lazy']);
745
746
		// Check that live record has original value
747
		$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...
748
749
		Versioned::set_reading_mode($originalMode);
750
	}
751
752
	public function testLazyLoadFieldsRetrieval() {
753
		// Set reading mode to Stage
754
		Versioned::set_stage(Versioned::DRAFT);
755
756
		// Create object only in reading stage
757
		$original = new VersionedTest_Subclass();
758
		$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...
759
		$original->write();
760
761
		// Query for object using base class
762
		$query = VersionedTest_DataObject::get()->filter('ID', $original->ID);
763
764
		// Set reading mode to Live
765
		Versioned::set_stage(Versioned::LIVE);
766
767
		$fetched = $query->first();
768
		$this->assertTrue($fetched instanceof VersionedTest_Subclass);
769
		$this->assertEquals($original->ID, $fetched->ID); // Eager loaded
770
		$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...
771
	}
772
773
	/**
774
	 * Tests that reading mode persists between requests
775
	 */
776
	public function testReadingPersistent() {
0 ignored issues
show
Coding Style introduced by
testReadingPersistent uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
777
		$session = Injector::inst()->create('Session', array());
778
		$adminID = $this->logInWithPermission('ADMIN');
779
		$session->inst_set('loggedInAs', $adminID);
780
781
		// Set to stage
782
		Director::test('/?stage=Stage', null, $session);
783
		$this->assertEquals(
784
				'Stage.Stage',
785
				$session->inst_get('readingMode'),
786
				'Check querystring changes reading mode to Stage'
787
		);
788
		Director::test('/', null, $session);
789
		$this->assertEquals(
790
				'Stage.Stage',
791
				$session->inst_get('readingMode'),
792
				'Check that subsequent requests in the same session remain in Stage mode'
793
		);
794
795
		// Test live persists
796
		Director::test('/?stage=Live', null, $session);
797
		$this->assertEquals(
798
				'Stage.Live',
799
				$session->inst_get('readingMode'),
800
				'Check querystring changes reading mode to Live'
801
		);
802
		Director::test('/', null, $session);
803
		$this->assertEquals(
804
				'Stage.Live',
805
				$session->inst_get('readingMode'),
806
				'Check that subsequent requests in the same session remain in Live mode'
807
		);
808
809
		// Test that session doesn't redundantly store the default stage if it doesn't need to
810
		$session2 = Injector::inst()->create('Session', array());
811
		$session2->inst_set('loggedInAs', $adminID);
812
		Director::test('/', null, $session2);
813
		$this->assertArrayNotHasKey('readingMode', $session2->inst_changedData());
814
		Director::test('/?stage=Live', null, $session2);
815
		$this->assertArrayNotHasKey('readingMode', $session2->inst_changedData());
816
817
		// Test choose_site_stage
818
		unset($_GET['stage']);
819
		unset($_GET['archiveDate']);
820
		Session::set('readingMode', 'Stage.Stage');
821
		Versioned::choose_site_stage();
822
		$this->assertEquals('Stage.Stage', Versioned::get_reading_mode());
823
		Session::set('readingMode', 'Archive.2014-01-01');
824
		Versioned::choose_site_stage();
825
		$this->assertEquals('Archive.2014-01-01', Versioned::get_reading_mode());
826
		Session::clear('readingMode');
827
		Versioned::choose_site_stage();
828
		$this->assertEquals('Stage.Live', Versioned::get_reading_mode());
829
	}
830
831
	/**
832
	 * Test that stage parameter is blocked by non-administrative users
833
	 */
834
	public function testReadingModeSecurity() {
835
		$this->setExpectedException('SS_HTTPResponse_Exception');
836
		$session = Injector::inst()->create('Session', array());
837
		$result = Director::test('/?stage=Stage', null, $session);
838
	}
839
840
	/**
841
	 * Ensures that the latest version of a record is the expected value
842
	 *
843
	 * @param DataObject $record
844
	 * @param int $version
845
	 */
846
	protected function assertRecordHasLatestVersion($record, $version) {
847
		foreach(ClassInfo::ancestry(get_class($record), true) as $table) {
848
			$versionForClass = DB::prepared_query(
849
				$sql = "SELECT MAX(\"Version\") FROM \"{$table}_versions\" WHERE \"RecordID\" = ?",
850
				array($record->ID)
851
			)->value();
852
			$this->assertEquals($version, $versionForClass, "That the table $table has the latest version $version");
853
		}
854
	}
855
856
	/**
857
	 * Tests that multi-table dataobjects are correctly versioned
858
	 */
859
	public function testWriteToStage() {
860
		// Test subclass with versioned extension directly added
861
		$record = VersionedTest_Subclass::create();
862
		$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...
863
		$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...
864
		$record->writeToStage("Stage");
865
		$this->assertRecordHasLatestVersion($record, 1);
866
		$record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
867
		$this->assertRecordHasLatestVersion($record, 1);
868
		$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...
869
		$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...
870
		$record->writeToStage("Stage");
871
		$this->assertRecordHasLatestVersion($record, 2);
872
873
		// Test subclass without changes to base class
874
		$record = VersionedTest_Subclass::create();
875
		$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...
876
		$record->writeToStage("Stage");
877
		$this->assertRecordHasLatestVersion($record, 1);
878
		$record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
879
		$this->assertRecordHasLatestVersion($record, 1);
880
		$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...
881
		$record->writeToStage("Stage");
882
		$this->assertRecordHasLatestVersion($record, 2);
883
884
		// Test subclass without changes to sub class
885
		$record = VersionedTest_Subclass::create();
886
		$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...
887
		$record->writeToStage("Stage");
888
		$this->assertRecordHasLatestVersion($record, 1);
889
		$record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
890
		$this->assertRecordHasLatestVersion($record, 1);
891
		$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...
892
		$record->writeToStage("Stage");
893
		$this->assertRecordHasLatestVersion($record, 2);
894
895
		// Test subclass with versioned extension only added to the base clases
896
		$record = VersionedTest_AnotherSubclass::create();
897
		$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...
898
		$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...
899
		$record->writeToStage("Stage");
900
		$this->assertRecordHasLatestVersion($record, 1);
901
		$record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
902
		$this->assertRecordHasLatestVersion($record, 1);
903
		$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...
904
		$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...
905
		$record->writeToStage("Stage");
906
		$this->assertRecordHasLatestVersion($record, 2);
907
908
909
		// Test subclass without changes to base class
910
		$record = VersionedTest_AnotherSubclass::create();
911
		$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...
912
		$record->writeToStage("Stage");
913
		$this->assertRecordHasLatestVersion($record, 1);
914
		$record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
915
		$this->assertRecordHasLatestVersion($record, 1);
916
		$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...
917
		$record->writeToStage("Stage");
918
		$this->assertRecordHasLatestVersion($record, 2);
919
920
		// Test subclass without changes to sub class
921
		$record = VersionedTest_AnotherSubclass::create();
922
		$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...
923
		$record->writeToStage("Stage");
924
		$this->assertRecordHasLatestVersion($record, 1);
925
		$record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
926
		$this->assertRecordHasLatestVersion($record, 1);
927
		$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...
928
		$record->writeToStage("Stage");
929
		$this->assertRecordHasLatestVersion($record, 2);
930
	}
931
932
	public function testVersionedHandlesRenamedDataObjectFields(){
933
		Config::inst()->remove('VersionedTest_RelatedWithoutVersion','db','Name','Varchar');
934
935
		Config::inst()->update('VersionedTest_RelatedWithoutVersion','db',array(
936
			"NewField" => "Varchar",
937
		));
938
939
		VersionedTest_RelatedWithoutVersion::add_extension("Versioned");
940
		$this->resetDBSchema(true);
941
		$testData = new VersionedTest_RelatedWithoutVersion();
942
		$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...
943
		$testData->write();
944
	}
945
946
	public function testCanView() {
947
		$public1ID = $this->idFromFixture('VersionedTest_PublicStage', 'public1');
948
		$public2ID = $this->idFromFixture('VersionedTest_PublicViaExtension', 'public2');
949
		$privateID = $this->idFromFixture('VersionedTest_DataObject', 'page1');
950
		$singleID = $this->idFromFixture('VersionedTest_SingleStage', 'single');
951
952
		// Test that all (and only) public pages are viewable in stage mode
953
		Session::clear("loggedInAs");
954
		Versioned::set_stage(Versioned::DRAFT);
955
		$public1 = Versioned::get_one_by_stage('VersionedTest_PublicStage', 'Stage', array('"ID"' => $public1ID));
956
		$public2 = Versioned::get_one_by_stage('VersionedTest_PublicViaExtension', 'Stage', array('"ID"' => $public2ID));
957
		$private = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', array('"ID"' => $privateID));
958
		// Also test an object that has just a single-stage (eg. is only versioned)
959
		$single = Versioned::get_one_by_stage('VersionedTest_SingleStage', 'Stage', array('"ID"' => $singleID));
960
961
962
		$this->assertTrue($public1->canView());
963
		$this->assertTrue($public2->canView());
964
		$this->assertFalse($private->canView());
965
		$this->assertFalse($single->canView());
966
967
		// Adjusting the current stage should not allow objects loaded in stage to be viewable
968
		Versioned::set_stage(Versioned::LIVE);
969
		$this->assertTrue($public1->canView());
970
		$this->assertTrue($public2->canView());
971
		$this->assertFalse($private->canView());
972
		$this->assertFalse($single->canView());
973
974
		// Writing the private page to live should be fine though
975
		$private->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
976
		$privateLive = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Live', array('"ID"' => $privateID));
977
		$this->assertTrue($private->canView());
978
		$this->assertTrue($privateLive->canView());
979
980
		// But if the private version becomes different to the live version, it's once again disallowed
981
		Versioned::set_stage(Versioned::DRAFT);
982
		$private->Title = 'Secret Title';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<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...
983
		$private->write();
984
		$this->assertFalse($private->canView());
985
		$this->assertTrue($privateLive->canView());
986
987
		// And likewise, viewing a live page (when mode is draft) should be ok
988
		Versioned::set_stage(Versioned::DRAFT);
989
		$this->assertFalse($private->canView());
990
		$this->assertTrue($privateLive->canView());
991
992
		// Logging in as admin should allow all permissions
993
		$this->logInWithPermission('ADMIN');
994
		Versioned::set_stage(Versioned::DRAFT);
995
		$this->assertTrue($public1->canView());
996
		$this->assertTrue($public2->canView());
997
		$this->assertTrue($private->canView());
998
		$this->assertTrue($single->canView());
999
	}
1000
1001
	public function testCanViewStage() {
1002
		$public = $this->objFromFixture('VersionedTest_PublicStage', 'public1');
1003
		$private = $this->objFromFixture('VersionedTest_DataObject', 'page1');
1004
		Session::clear("loggedInAs");
1005
		Versioned::set_stage(Versioned::DRAFT);
1006
1007
		// Test that all (and only) public pages are viewable in stage mode
1008
		// Unpublished records are not viewable in live regardless of permissions
1009
		$this->assertTrue($public->canViewStage('Stage'));
1010
		$this->assertFalse($private->canViewStage('Stage'));
1011
		$this->assertFalse($public->canViewStage('Live'));
1012
		$this->assertFalse($private->canViewStage('Live'));
1013
1014
		// Writing records to live should make both stage and live modes viewable
1015
		$private->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
1016
		$public->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
1017
		$this->assertTrue($public->canViewStage('Stage'));
1018
		$this->assertTrue($private->canViewStage('Stage'));
1019
		$this->assertTrue($public->canViewStage('Live'));
1020
		$this->assertTrue($private->canViewStage('Live'));
1021
1022
		// If the draft mode changes, the live mode remains public, although the updated
1023
		// draft mode is secured for non-public records.
1024
		$private->Title = 'Secret Title';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<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...
1025
		$private->write();
1026
		$public->Title = 'Public Title';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<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...
1027
		$public->write();
1028
		$this->assertTrue($public->canViewStage('Stage'));
1029
		$this->assertFalse($private->canViewStage('Stage'));
1030
		$this->assertTrue($public->canViewStage('Live'));
1031
		$this->assertTrue($private->canViewStage('Live'));
1032
	}
1033
}
1034
1035
1036
/**
1037
 * @method VersionedTest_DataObject Parent()
1038
 * @method HasManyList Children()
1039
 * @method ManyManyList Related()
1040
 *
1041
 * @package framework
1042
 * @subpackage tests
1043
 * @mixin Versioned
1044
 */
1045
class VersionedTest_DataObject extends DataObject implements TestOnly {
1046
	private static $db = array(
1047
		"Name" => "Varchar",
1048
		'Title' => 'Varchar',
1049
		'Content' => 'HTMLText',
1050
	);
1051
1052
	private static $extensions = array(
1053
		"Versioned",
1054
	);
1055
1056
	private static $has_one = array(
1057
		'Parent' => 'VersionedTest_DataObject',
1058
	);
1059
1060
	private static $has_many = array(
1061
		'Children' => 'VersionedTest_DataObject',
1062
	);
1063
1064
	private static $many_many = array(
1065
		'Related' => 'VersionedTest_RelatedWithoutVersion',
1066
	);
1067
1068
1069
	public function canView($member = null) {
1070
		$extended = $this->extendedCan(__FUNCTION__, $member);
1071
		if($extended !== null) {
1072
			return $extended;
1073
		}
1074
		return true;
1075
	}
1076
}
1077
1078
/**
1079
 * @mixin Versioned
1080
 */
1081
class VersionedTest_WithIndexes extends DataObject implements TestOnly {
1082
1083
	private static $db = array(
1084
		'UniqA' => 'Int',
1085
		'UniqS' => 'Int',
1086
	);
1087
	private static $extensions = array(
1088
		"Versioned"
1089
	);
1090
	private static $indexes = array(
1091
		'UniqS_idx' => 'unique ("UniqS")',
1092
		'UniqA_idx' => array('type' => 'unique', 'name' => 'UniqA_idx', 'value' => '"UniqA"',),
1093
	);
1094
1095
}
1096
1097
/**
1098
 * @package framework
1099
 * @subpackage tests
1100
 */
1101
class VersionedTest_RelatedWithoutVersion extends DataObject implements TestOnly {
1102
1103
	private static $db = array(
1104
		'Name' => 'Varchar'
1105
	);
1106
1107
	private static $belongs_many_many = array(
1108
		'Related' => 'VersionedTest_DataObject'
1109
	);
1110
1111
}
1112
1113
/**
1114
 * @package framework
1115
 * @subpackage tests
1116
 */
1117
class VersionedTest_Subclass extends VersionedTest_DataObject implements TestOnly {
1118
	private static $db = array(
1119
		"ExtraField" => "Varchar",
1120
	);
1121
}
1122
1123
/**
1124
 * @package framework
1125
 * @subpackage tests
1126
 */
1127
class VersionedTest_AnotherSubclass extends VersionedTest_DataObject implements TestOnly {
1128
	private static $db = array(
1129
		"AnotherField" => "Varchar"
1130
	);
1131
}
1132
1133
/**
1134
 * @package framework
1135
 * @subpackage tests
1136
 */
1137
class VersionedTest_UnversionedWithField extends DataObject implements TestOnly {
1138
	private static $db = array('Version' => 'Varchar(255)');
1139
}
1140
1141
/**
1142
 * @mixin Versioned
1143
 */
1144
class VersionedTest_SingleStage extends DataObject implements TestOnly {
1145
	private static $db = array(
1146
		'Name' => 'Varchar'
1147
	);
1148
1149
	private static $extensions = array(
1150
		'Versioned("Versioned")'
1151
	);
1152
}
1153
1154
/**
1155
 * Versioned dataobject with public stage mode
1156
 *
1157
 * @mixin Versioned
1158
 */
1159
class VersionedTest_PublicStage extends DataObject implements TestOnly {
1160
	private static $db = array(
1161
		'Title' => 'Varchar'
1162
	);
1163
1164
	private static $extensions = array(
1165
		"Versioned"
1166
	);
1167
1168
	public function canView($member = null) {
1169
		$extended = $this->extendedCan(__FUNCTION__, $member);
1170
		if($extended !== null) {
1171
			return $extended;
1172
		}
1173
		return true;
1174
	}
1175
1176
	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...
1177
		// All non-live modes are public
1178
		return true;
1179
	}
1180
}
1181
1182
/**
1183
 * Public access is provided via extension rather than overriding canViewVersioned
1184
 *
1185
 * @mixin Versioned
1186
 * @mixin VersionedTest_PublicExtension
1187
 */
1188
class VersionedTest_PublicViaExtension extends DataObject implements TestOnly {
1189
1190
	public function canView($member = null) {
1191
		$extended = $this->extendedCan(__FUNCTION__, $member);
1192
		if($extended !== null) {
1193
			return $extended;
1194
		}
1195
		return true;
1196
	}
1197
1198
	private static $db = array(
1199
		'Title' => 'Varchar'
1200
	);
1201
1202
	private static $extensions = array(
1203
		"Versioned",
1204
		"VersionedTest_PublicExtension"
1205
	);
1206
}
1207
1208
/**
1209
 * Alters stage mode of extended object to be public
1210
 */
1211
class VersionedTest_PublicExtension extends DataExtension implements TestOnly {
1212
	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...
1213
		return true;
1214
	}
1215
}
1216