Completed
Push — 3.7 ( 81b2d8...ef0909 )
by
unknown
09:42
created

VersionedTest::testGetIncludingDeleted()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 39
rs 9.296
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @package framework
5
 * @subpackage tests
6
 */
7
class VersionedTest extends SapphireTest {
8
9
	protected static $fixture_file = 'VersionedTest.yml';
10
11
	protected $extraDataObjects = array(
12
		'VersionedTest_DataObject',
13
		'VersionedTest_Subclass',
14
		'VersionedTest_AnotherSubclass',
15
		'VersionedTest_RelatedWithoutVersion',
16
		'VersionedTest_SingleStage',
17
		'VersionedTest_WithIndexes',
18
		'VersionedTest_PublicStage',
19
		'VersionedTest_PublicViaExtension',
20
	);
21
22
	protected $requiredExtensions = array(
23
		"VersionedTest_DataObject" => array('Versioned'),
24
		"VersionedTest_WithIndexes" => array('Versioned'),
25
	);
26
27
	public function testUniqueIndexes() {
28
		$tableExpectations = array(
29
			'VersionedTest_WithIndexes' =>
30
				array('value' => true, 'message' => 'Unique indexes are unique in main table'),
31
			'VersionedTest_WithIndexes_versions' =>
32
				array('value' => false, 'message' => 'Unique indexes are no longer unique in _versions table'),
33
			'VersionedTest_WithIndexes_Live' =>
34
				array('value' => false, 'message' => 'Unique indexes are no longer unique in _Live table'),
35
		);
36
37
		// Test each table's performance
38
		foreach ($tableExpectations as $tableName => $expectation) {
39
			$indexes = DB::get_schema()->indexList($tableName);
40
41
			// Check for presence of all unique indexes
42
			$indexColumns = array_map(function($index) {
43
				return $index['value'];
44
			}, $indexes);
45
			sort($indexColumns);
46
			$expectedColumns = array('"UniqA"', '"UniqS"');
47
			$this->assertEquals(
48
					array_values($expectedColumns),
49
					array_values(array_intersect($indexColumns, $expectedColumns)),
50
					"$tableName has both indexes");
51
52
			// Check unique -> non-unique conversion
53
			foreach ($indexes as $indexKey => $indexSpec) {
54
				if (in_array($indexSpec['value'], $expectedColumns)) {
55
					$isUnique = $indexSpec['type'] === 'unique';
56
					$this->assertEquals($isUnique, $expectation['value'], $expectation['message']);
57
}
58
			}
59
		}
60
	}
61
62
	public function testDeletingOrphanedVersions() {
63
		$obj = new VersionedTest_Subclass();
64
		$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...
65
		$obj->write();
66
		$obj->publish('Stage', 'Live');
67
68
		$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...
69
		$obj->write();
70
		$obj->publish('Stage', 'Live');
71
72
		$versions = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\""
73
			. " WHERE \"RecordID\" = '$obj->ID'")->value();
74
75
		$this->assertGreaterThan(0, $versions, 'At least 1 version exists in the history of the page');
76
77
		// Force orphaning of all versions created earlier, only on parent record.
78
		// The child versiones table should still have the correct relationship
79
		DB::query("DELETE FROM \"VersionedTest_DataObject_versions\" WHERE \"RecordID\" = $obj->ID");
80
81
		// insert a record with no primary key (ID)
82
		DB::query("INSERT INTO \"VersionedTest_DataObject_versions\" (\"RecordID\") VALUES ($obj->ID)");
83
84
		// run the script which should clean that up
85
		$obj->augmentDatabase();
86
87
		$versions = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\""
88
			. " WHERE \"RecordID\" = '$obj->ID'")->value();
89
		$this->assertEquals(0, $versions, 'Orphaned versions on child tables are removed');
90
91
		// test that it doesn't delete records that we need
92
		$obj->write();
93
		$obj->publish('Stage', 'Live');
94
95
		$count = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\""
96
			. " WHERE \"RecordID\" = '$obj->ID'")->value();
97
		$obj->augmentDatabase();
98
99
		$count2 = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\""
100
			. " WHERE \"RecordID\" = '$obj->ID'")->value();
101
102
		$this->assertEquals($count, $count2);
103
	}
104
105
	public function testDuplicate() {
106
		$obj1 = new VersionedTest_Subclass();
107
		$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...
108
		$obj1->write(); // version 1
109
		$obj1->publish('Stage', 'Live');
110
		$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...
111
		$obj1->write(); // version 2
112
113
		// Make duplicate
114
		$obj2 = $obj1->duplicate();
115
116
		// Check records differ
117
		$this->assertNotEquals($obj1->ID, $obj2->ID);
118
		$this->assertEquals(2, $obj1->Version);
119
		$this->assertEquals(1, $obj2->Version);
120
	}
121
122
	public function testForceChangeUpdatesVersion() {
123
		$obj = new VersionedTest_DataObject();
124
		$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...
125
		$obj->write();
126
127
		$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...
128
		$obj->forceChange();
129
		$obj->write();
130
131
		$this->assertTrue(
132
			($obj->Version > $oldVersion),
133
			"A object Version is increased when just calling forceChange() without any other changes"
134
		);
135
	}
136
137
	/**
138
	 * Test Versioned::get_including_deleted()
139
	 */
140
	public function testGetIncludingDeleted() {
141
		// Get all ids of pages
142
		$allPageIDs = DataObject::get(
143
			'VersionedTest_DataObject',
144
			"\"ParentID\" = 0", "\"VersionedTest_DataObject\".\"ID\" ASC"
145
		)->column('ID');
146
147
		// Modify a page, ensuring that the Version ID and Record ID will differ,
148
		// and then subsequently delete it
149
		$targetPage = $this->objFromFixture('VersionedTest_DataObject', 'page3');
150
		$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...
151
		$targetPage->write();
152
		$targetPage->delete();
153
154
		// Get all items, ignoring deleted
155
		$remainingPages = DataObject::get("VersionedTest_DataObject", "\"ParentID\" = 0",
156
			"\"VersionedTest_DataObject\".\"ID\" ASC");
157
		// Check that page 3 has gone
158
		$this->assertNotNull($remainingPages);
159
		$this->assertEquals(array("Page 1", "Page 2", "Subclass Page 1"), $remainingPages->column('Title'));
160
161
		// Get all including deleted
162
		$allPages = Versioned::get_including_deleted("VersionedTest_DataObject", "\"ParentID\" = 0",
163
			"\"VersionedTest_DataObject\".\"ID\" ASC");
164
		// Check that page 3 is still there
165
		$this->assertEquals(array("Page 1", "Page 2", "Page 3", "Subclass Page 1"), $allPages->column('Title'));
166
167
		// Check that the returned pages have the correct IDs
168
		$this->assertEquals($allPageIDs, $allPages->column('ID'));
169
170
		// Check that this still works if we switch to reading the other stage
171
		Versioned::reading_stage("Live");
172
		$allPages = Versioned::get_including_deleted("VersionedTest_DataObject", "\"ParentID\" = 0",
173
			"\"VersionedTest_DataObject\".\"ID\" ASC");
174
		$this->assertEquals(array("Page 1", "Page 2", "Page 3", "Subclass Page 1"), $allPages->column('Title'));
175
176
		// Check that the returned pages still have the correct IDs
177
		$this->assertEquals($allPageIDs, $allPages->column('ID'));
178
	}
179
180
	public function testVersionedFieldsAdded() {
181
		$obj = new VersionedTest_DataObject();
182
		// Check that the Version column is added as a full-fledged column
183
		$this->assertInstanceOf('DBInt', $obj->dbObject('Version'));
184
185
		$obj2 = new VersionedTest_Subclass();
186
		// Check that the Version column is added as a full-fledged column
187
		$this->assertInstanceOf('DBInt', $obj2->dbObject('Version'));
188
	}
189
190
	public function testVersionedFieldsNotInCMS() {
191
		$obj = new VersionedTest_DataObject();
192
193
		// the version field in cms causes issues with Versioned::augmentWrite()
194
		$this->assertNull($obj->getCMSFields()->dataFieldByName('Version'));
195
	}
196
197
	public function testPublishCreateNewVersion() {
198
		$page1 = $this->objFromFixture('VersionedTest_DataObject', 'page1');
199
		$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...
200
		$page1->write();
201
		$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...
202
		$page1->publish('Stage', 'Live', false);
203
		$this->assertEquals(
204
			$firstVersion,
205
			$page1->Version,
206
			'publish() with $createNewVersion=FALSE does not create a new version'
207
		);
208
209
		$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...
210
		$page1->write();
211
		$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...
212
		$this->assertTrue($firstVersion < $secondVersion, 'write creates new version');
213
214
		$page1->publish('Stage', 'Live', true);
215
		$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...
216
		$liveVersion = Versioned::get_versionnumber_by_stage('VersionedTest_DataObject', 'Live', $page1->ID);
217
		$stageVersion = Versioned::get_versionnumber_by_stage('VersionedTest_DataObject', 'Stage', $page1->ID);
218
		$this->assertTrue(
219
			$secondVersion < $thirdVersion,
220
			'publish() with $createNewVersion=TRUE creates a new version'
221
		);
222
		$this->assertEquals(
223
			$liveVersion,
224
			$thirdVersion,
225
			'publish() with $createNewVersion=TRUE publishes to live'
226
		);
227
		$this->assertEquals(
228
			$stageVersion,
229
			$secondVersion,
230
			'publish() with $createNewVersion=TRUE does not affect stage'
231
		);
232
	}
233
234
	public function testRollbackTo() {
235
		$page1 = $this->objFromFixture('VersionedTest_AnotherSubclass', 'subclass1');
236
		$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...
237
		$page1->write();
238
		$page1->publish('Stage', 'Live');
239
		$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...
240
241
		$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...
242
		$page1->write();
243
		$page1->publish('Stage', 'Live');
244
		$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...
245
246
		$page1->doRollbackTo($origVersion);
247
		$page1 = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', array(
248
			'"VersionedTest_DataObject"."ID" = ?' => $page1->ID
249
		));
250
251
		$this->assertTrue($page1->Version == $changedVersion + 1, 'Create a new higher version number');
252
		$this->assertEquals('orig', $page1->Content, 'Copies the content from the old version');
253
254
		// check db entries
255
		$version = DB::prepared_query("SELECT MAX(\"Version\") FROM \"VersionedTest_DataObject_versions\" WHERE \"RecordID\" = ?",
256
			array($page1->ID)
257
		)->value();
258
		$this->assertEquals($page1->Version, $version, 'Correct entry in VersionedTest_DataObject_versions');
259
260
		$version = DB::prepared_query("SELECT MAX(\"Version\") FROM \"VersionedTest_AnotherSubclass_versions\" WHERE \"RecordID\" = ?",
261
			array($page1->ID)
262
		)->value();
263
		$this->assertEquals($page1->Version, $version, 'Correct entry in VersionedTest_AnotherSubclass_versions');
264
	}
265
266
	public function testDeleteFromStage() {
267
		$page1 = $this->objFromFixture('VersionedTest_DataObject', 'page1');
268
		$pageID = $page1->ID;
269
270
		$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...
271
		$page1->write();
272
		$page1->publish('Stage', 'Live');
273
274
		$this->assertEquals(1,
275
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value());
276
		$this->assertEquals(1,
277
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value());
278
279
		$page1->deleteFromStage('Live');
280
281
		// Confirm that deleteFromStage() doesn't manipulate the original record
282
		$this->assertEquals($pageID, $page1->ID);
283
284
		$this->assertEquals(1,
285
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value());
286
		$this->assertEquals(0,
287
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value());
288
289
		$page1->delete();
290
291
		$this->assertEquals(0, $page1->ID);
292
		$this->assertEquals(0,
293
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value());
294
		$this->assertEquals(0,
295
			DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value());
296
	}
297
298
	public function testWritingNewToStage() {
299
		$origMode = Versioned::get_reading_mode();
300
301
		Versioned::reading_stage("Stage");
302
		$page = new VersionedTest_DataObject();
303
		$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...
304
		$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...
305
		$page->write();
306
307
		$live = Versioned::get_by_stage('VersionedTest_DataObject', 'Live', array(
308
			'"VersionedTest_DataObject_Live"."ID"' => $page->ID
309
		));
310
		$this->assertEquals(0, $live->count());
311
312
		$stage = Versioned::get_by_stage('VersionedTest_DataObject', 'Stage',array(
313
			'"VersionedTest_DataObject"."ID"' => $page->ID
314
		));
315
		$this->assertEquals(1, $stage->count());
316
		$this->assertEquals($stage->First()->Title, 'testWritingNewToStage');
317
318
		Versioned::set_reading_mode($origMode);
319
	}
320
321
	/**
322
	 * This tests for the situation described in the ticket #5596.
323
	 * Writing new Page to live first creates a row in VersionedTest_DataObject table (to get the new ID),
324
	 * then "changes it's mind" in Versioned and writes VersionedTest_DataObject_Live. It does not remove
325
	 * the VersionedTest_DataObject record though.
326
	 */
327
	public function testWritingNewToLive() {
328
		$origMode = Versioned::get_reading_mode();
329
330
		Versioned::reading_stage("Live");
331
		$page = new VersionedTest_DataObject();
332
		$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...
333
		$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...
334
		$page->write();
335
336
		$live = Versioned::get_by_stage('VersionedTest_DataObject', 'Live',array(
337
			'"VersionedTest_DataObject_Live"."ID"' => $page->ID
338
		));
339
		$this->assertEquals(1, $live->count());
340
		$this->assertEquals($live->First()->Title, 'testWritingNewToLive');
341
342
		$stage = Versioned::get_by_stage('VersionedTest_DataObject', 'Stage',array(
343
			'"VersionedTest_DataObject"."ID"' => $page->ID
344
		));
345
		$this->assertEquals(0, $stage->count());
346
347
		Versioned::set_reading_mode($origMode);
348
	}
349
350
	/**
351
	 * Tests DataObject::hasOwnTableDatabaseField
352
	 */
353
	public function testHasOwnTableDatabaseFieldWithVersioned() {
354
		$noversion    = new DataObject();
355
		$versioned    = new VersionedTest_DataObject();
356
		$versionedSub = new VersionedTest_Subclass();
357
		$versionedAno = new VersionedTest_AnotherSubclass();
358
		$versionField = new VersionedTest_UnversionedWithField();
359
360
		$this->assertFalse(
361
			(bool) $noversion->hasOwnTableDatabaseField('Version'),
362
			'Plain models have no version field.'
363
		);
364
		$this->assertEquals(
365
			'Int', $versioned->hasOwnTableDatabaseField('Version'),
366
			'The versioned ext adds an Int version field.'
367
		);
368
		$this->assertEquals(
369
			null,
370
			$versionedSub->hasOwnTableDatabaseField('Version'),
371
			'Sub-classes of a versioned model don\'t have a Version field.'
372
		);
373
		$this->assertEquals(
374
			null,
375
			$versionedAno->hasOwnTableDatabaseField('Version'),
376
			'Sub-classes of a versioned model don\'t have a Version field.'
377
		);
378
		$this->assertEquals(
379
			'Varchar', $versionField->hasOwnTableDatabaseField('Version'),
380
			'Models w/o Versioned can have their own Version field.'
381
		);
382
	}
383
384
	/**
385
	 * Test that SQLQuery::queriedTables() applies the version-suffixes properly.
386
	 */
387
	public function testQueriedTables() {
388
		Versioned::reading_stage('Live');
389
390
		$this->assertEquals(array(
391
			'VersionedTest_DataObject_Live',
392
			'VersionedTest_Subclass_Live',
393
		), DataObject::get('VersionedTest_Subclass')->dataQuery()->query()->queriedTables());
394
	}
395
396
	public function testGetVersionWhenClassnameChanged() {
397
		$obj = new VersionedTest_DataObject;
398
		$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...
399
		$obj->write();
400
		$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...
401
		$obj->ClassName = "VersionedTest_Subclass";
402
		$obj->write();
403
		$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...
404
405
		$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...
406
		$obj->ClassName = "VersionedTest_DataObject";
407
		$obj->write();
408
409
		// We should be able to pass the subclass and still get the correct class back
410
		$obj2 = Versioned::get_version("VersionedTest_Subclass", $obj->ID, $subclassVersion);
411
		$this->assertInstanceOf("VersionedTest_Subclass", $obj2);
412
		$this->assertEquals("test2", $obj2->Name);
413
414
		$obj3 = Versioned::get_latest_version("VersionedTest_Subclass", $obj->ID);
415
		$this->assertEquals("test3", $obj3->Name);
416
		$this->assertInstanceOf("VersionedTest_DataObject", $obj3);
417
418
	}
419
420
	public function testArchiveVersion() {
421
422
		// In 2005 this file was created
423
		SS_Datetime::set_mock_now('2005-01-01 00:00:00');
424
		$testPage = new VersionedTest_Subclass();
425
		$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...
426
		$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...
427
		$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...
428
		$testPage->write();
429
430
		// In 2007 we updated it
431
		SS_Datetime::set_mock_now('2007-01-01 00:00:00');
432
		$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...
433
		$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...
434
		$testPage->write();
435
436
		// In 2009 we updated it again
437
		SS_Datetime::set_mock_now('2009-01-01 00:00:00');
438
		$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...
439
		$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...
440
		$testPage->write();
441
442
		// End mock, back to the present day:)
443
		SS_Datetime::clear_mock_now();
444
445
		// Test 1 - 2006 Content
446
		singleton('VersionedTest_Subclass')->flushCache(true);
447
		Versioned::set_reading_mode('Archive.2006-01-01 00:00:00');
448
		$testPage2006 = DataObject::get('VersionedTest_Subclass')->filter(array('Title' => 'Archived page'))->first();
449
		$this->assertInstanceOf("VersionedTest_Subclass", $testPage2006);
450
		$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...
451
		$this->assertEquals("This is the content from 2005", $testPage2006->Content);
452
453
		// Test 2 - 2008 Content
454
		singleton('VersionedTest_Subclass')->flushCache(true);
455
		Versioned::set_reading_mode('Archive.2008-01-01 00:00:00');
456
		$testPage2008 = DataObject::get('VersionedTest_Subclass')->filter(array('Title' => 'Archived page'))->first();
457
		$this->assertInstanceOf("VersionedTest_Subclass", $testPage2008);
458
		$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...
459
		$this->assertEquals("It's 2007 already!", $testPage2008->Content);
460
461
		// Test 3 - Today
462
		singleton('VersionedTest_Subclass')->flushCache(true);
463
		Versioned::set_reading_mode('Stage.Stage');
464
		$testPageCurrent = DataObject::get('VersionedTest_Subclass')->filter(array('Title' => 'Archived page'))
465
			->first();
466
		$this->assertInstanceOf("VersionedTest_Subclass", $testPageCurrent);
467
		$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...
468
		$this->assertEquals("I'm enjoying 2009", $testPageCurrent->Content);
469
	}
470
471
	public function testAllVersions()
472
	{
473
		// In 2005 this file was created
474
		SS_Datetime::set_mock_now('2005-01-01 00:00:00');
475
		$testPage = new VersionedTest_Subclass();
476
		$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...
477
		$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...
478
		$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...
479
		$testPage->write();
480
481
		// In 2007 we updated it
482
		SS_Datetime::set_mock_now('2007-01-01 00:00:00');
483
		$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...
484
		$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...
485
		$testPage->write();
486
487
		// Check both versions are returned
488
		$versions = Versioned::get_all_versions('VersionedTest_Subclass', $testPage->ID);
489
		$content = array();
490
		$extraFields = array();
491
		foreach($versions as $version)
492
		{
493
			$content[] = $version->Content;
494
			$extraFields[] = $version->ExtraField;
495
		}
496
497
		$this->assertEquals($versions->Count(), 2, 'All versions returned');
498
		$this->assertEquals($content, array('This is the content from 2005', "It's 2007 already!"),
499
			'Version fields returned');
500
		$this->assertEquals($extraFields, array('2005', '2007'), 'Version fields returned');
501
502
		// In 2009 we updated it again
503
		SS_Datetime::set_mock_now('2009-01-01 00:00:00');
504
		$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...
505
		$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...
506
		$testPage->write();
507
508
		// End mock, back to the present day:)
509
		SS_Datetime::clear_mock_now();
510
511
		$versions = Versioned::get_all_versions('VersionedTest_Subclass', $testPage->ID);
512
		$content = array();
513
		$extraFields = array();
514
		foreach($versions as $version)
515
		{
516
			$content[] = $version->Content;
517
			$extraFields[] = $version->ExtraField;
518
		}
519
520
		$this->assertEquals($versions->Count(), 3, 'Additional all versions returned');
521
		$this->assertEquals($content,
522
			array('This is the content from 2005', "It's 2007 already!", "I'm enjoying 2009"),
523
			'Additional version fields returned');
524
		$this->assertEquals($extraFields, array('2005', '2007', '2009'), 'Additional version fields returned');
525
	}
526
527
	public function testArchiveRelatedDataWithoutVersioned() {
528
		SS_Datetime::set_mock_now('2009-01-01 00:00:00');
529
530
		$relatedData = new VersionedTest_RelatedWithoutVersion();
531
		$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...
532
		$relatedDataId = $relatedData->write();
533
534
		$testData = new VersionedTest_DataObject();
535
		$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...
536
		$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...
537
		$testData->Related()->add($relatedData);
538
		$id = $testData->write();
539
540
		SS_Datetime::set_mock_now('2010-01-01 00:00:00');
541
		$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...
542
		$testData->write();
543
544
		Versioned::reading_archived_date('2009-01-01 19:00:00');
545
546
		$fetchedData = VersionedTest_DataObject::get()->byId($id);
547
		$this->assertEquals('Before Content', $fetchedData->Content, 'We see the correct content of the older version');
548
549
		$relatedData = VersionedTest_RelatedWithoutVersion::get()->byId($relatedDataId);
550
		$this->assertEquals(
551
			1,
552
			$relatedData->Related()->count(),
553
			'We have a relation, with no version table, querying it still works'
554
		);
555
	}
556
557
	public function testVersionedWithSingleStage() {
558
		$tables = DB::table_list();
559
		$this->assertContains(
560
			'versionedtest_singlestage',
561
			array_keys($tables),
562
			'Contains base table'
563
		);
564
		$this->assertContains(
565
			'versionedtest_singlestage_versions',
566
			array_keys($tables),
567
			'Contains versions table'
568
		);
569
		$this->assertNotContains(
570
			'versionedtest_singlestage_live',
571
			array_keys($tables),
572
			'Does not contain separate table with _Live suffix'
573
		);
574
		$this->assertNotContains(
575
			'versionedtest_singlestage_stage',
576
			array_keys($tables),
577
			'Does not contain separate table with _Stage suffix'
578
		);
579
580
		Versioned::reading_stage("Stage");
581
		$obj = new VersionedTest_SingleStage(array('Name' => 'MyObj'));
582
		$obj->write();
583
		$this->assertNotNull(
584
			VersionedTest_SingleStage::get()->byID($obj->ID),
585
			'Writes to and reads from default stage if its set explicitly'
586
		);
587
588
		Versioned::reading_stage("Live");
589
		$obj = new VersionedTest_SingleStage(array('Name' => 'MyObj'));
590
		$obj->write();
591
		$this->assertNotNull(
592
			VersionedTest_SingleStage::get()->byID($obj->ID),
593
			'Writes to and reads from default stage even if a non-matching stage is set'
594
		);
595
	}
596
597
	/**
598
	 * Test that publishing processes respects lazy loaded fields
599
	 */
600
	public function testLazyLoadFields() {
601
		$originalMode = Versioned::get_reading_mode();
602
603
		// Generate staging record and retrieve it from stage in live mode
604
		Versioned::reading_stage('Stage');
605
		$obj = new VersionedTest_Subclass();
606
		$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...
607
		$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...
608
		$obj->write();
609
		$objID = $obj->ID;
610
		$filter = sprintf('"VersionedTest_DataObject"."ID" = \'%d\'', Convert::raw2sql($objID));
611
		Versioned::reading_stage('Live');
612
613
		// Check fields are unloaded prior to access
614
		$objLazy = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', $filter, false);
615
		$lazyFields = $objLazy->getQueriedDatabaseFields();
616
		$this->assertTrue(isset($lazyFields['ExtraField_Lazy']));
617
		$this->assertEquals('VersionedTest_Subclass', $lazyFields['ExtraField_Lazy']);
618
619
		// Check lazy loading works when viewing a Stage object in Live mode
620
		$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...
621
622
		// Test that writeToStage respects lazy loaded fields
623
		$objLazy = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', $filter, false);
624
		$objLazy->writeToStage('Live');
625
		$objLive = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Live', $filter, false);
626
		$liveLazyFields = $objLive->getQueriedDatabaseFields();
627
628
		// Check fields are unloaded prior to access
629
		$this->assertTrue(isset($liveLazyFields['ExtraField_Lazy']));
630
		$this->assertEquals('VersionedTest_Subclass', $liveLazyFields['ExtraField_Lazy']);
631
632
		// Check that live record has original value
633
		$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...
634
635
		Versioned::set_reading_mode($originalMode);
636
	}
637
638
	public function testReadingNotPersistentWhenUseSessionFalse()
639
    {
640
        Config::inst()->update('Versioned', 'use_session', false);
641
642
        $session = new Session(array());
643
        $adminID = $this->logInWithPermission('ADMIN');
644
        $session->inst_set('loggedInAs', $adminID);
645
646
        Director::test('/?stage=Stage', null, $session);
647
        $this->assertNull(
648
            $session->inst_get('readingMode'),
649
            'Check querystring does not change reading mode'
650
        );
651
652
        Director::test('/', null, $session);
653
        $this->assertNull(
654
            $session->inst_get('readingMode'),
655
            'Check that subsequent requests in the same session do not have a changed reading mode'
656
        );
657
    }
658
659
    /**
660
     * Tests that reading mode persists between requests
661
     */
662
    public function testReadingPersistentWhenUseSessionTrue()
0 ignored issues
show
Coding Style introduced by
testReadingPersistentWhenUseSessionTrue 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...
663
    {
664
        Config::inst()->update('Versioned', 'use_session', true);
665
666
        $session = new Session(array());
667
        $adminID = $this->logInWithPermission('ADMIN');
668
        $session->inst_set('loggedInAs', $adminID);
669
        // Set to stage
670
        Director::test('/?stage=Stage', null, $session);
671
        $this->assertEquals(
672
            'Stage.Stage',
673
            $session->inst_get('readingMode'),
674
            'Check querystring changes reading mode to Stage'
675
        );
676
        Director::test('/', null, $session);
677
        $this->assertEquals(
678
            'Stage.Stage',
679
            $session->inst_get('readingMode'),
680
            'Check that subsequent requests in the same session remain in Stage mode'
681
        );
682
        // Default stage stored anyway (in case default changes)
683
        Director::test('/?stage=Live', null, $session);
684
        $this->assertEquals(
685
            'Stage.Live',
686
            $session->inst_get('readingMode'),
687
            'Check querystring changes reading mode to Live'
688
        );
689
        Director::test('/', null, $session);
690
        $this->assertEquals(
691
            'Stage.Live',
692
            $session->inst_get('readingMode'),
693
            'Check that subsequent requests in the same session remain in Live mode'
694
        );
695
        // Test that session doesn't redundantly modify session stage without querystring args
696
        $session2 = new Session(array());
697
        $session2->inst_set('loggedInAs', $adminID);
698
        Director::test('/', null, $session2);
699
        $this->assertArrayNotHasKey('readingMode', $session2->inst_changedData());
700
        Director::test('/?stage=Live', null, $session2);
701
        $this->assertArrayHasKey('readingMode', $session2->inst_changedData());
702
        // Test choose_site_stage
703
        unset($_GET['stage']);
704
        unset($_GET['archiveDate']);
705
        $request = new SS_HTTPRequest('GET', '/');
706
        Session::clear_all();
707
        Session::set('readingMode', 'Stage.Stage');
708
        Versioned::choose_site_stage($request);
709
        $this->assertEquals('Stage.Stage', Versioned::get_reading_mode());
710
        Session::set('readingMode', 'Archive.2014-01-01');
711
        Versioned::choose_site_stage($request);
712
        $this->assertEquals('Archive.2014-01-01', Versioned::get_reading_mode());
713
        Session::clear('readingMode');
714
        Versioned::choose_site_stage($request);
715
        $this->assertEquals('Stage.Live', Versioned::get_reading_mode());
716
        // Ensure stage is reset to Live when logging out
717
        Session::set('readingMode', 'Stage.Stage');
718
        Versioned::choose_site_stage($request);
719
        Session::clear_all();
720
        Versioned::choose_site_stage($request);
721
        $this->assertSame('Stage.Live', Versioned::get_reading_mode());
722
    }
723
724
	/**
725
	 * Test that stage parameter is blocked by non-administrative users
726
	 */
727
	public function testReadingModeSecurity() {
728
		$this->setExpectedException('SS_HTTPResponse_Exception');
729
		$session = Injector::inst()->create('Session', array());
730
		$result = Director::test('/?stage=Stage', null, $session);
731
	}
732
733
	/**
734
	 * Ensures that the latest version of a record is the expected value
735
	 *
736
	 * @param DataObject $record
737
	 * @param int $version
738
	 */
739
	protected function assertRecordHasLatestVersion($record, $version) {
740
		foreach(ClassInfo::ancestry(get_class($record), true) as $table) {
741
			$versionForClass = DB::prepared_query(
742
				$sql = "SELECT MAX(\"Version\") FROM \"{$table}_versions\" WHERE \"RecordID\" = ?",
743
				array($record->ID)
744
			)->value();
745
			$this->assertEquals($version, $versionForClass, "That the table $table has the latest version $version");
746
		}
747
	}
748
749
	/**
750
	 * Tests that multi-table dataobjects are correctly versioned
751
	 */
752
	public function testWriteToStage() {
753
		// Test subclass with versioned extension directly added
754
		$record = VersionedTest_Subclass::create();
755
		$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...
756
		$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...
757
		$record->writeToStage("Stage");
758
		$this->assertRecordHasLatestVersion($record, 1);
759
		$record->publish("Stage", "Live");
760
		$this->assertRecordHasLatestVersion($record, 1);
761
		$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...
762
		$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...
763
		$record->writeToStage("Stage");
764
		$this->assertRecordHasLatestVersion($record, 2);
765
766
		// Test subclass without changes to base class
767
		$record = VersionedTest_Subclass::create();
768
		$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...
769
		$record->writeToStage("Stage");
770
		$this->assertRecordHasLatestVersion($record, 1);
771
		$record->publish("Stage", "Live");
772
		$this->assertRecordHasLatestVersion($record, 1);
773
		$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...
774
		$record->writeToStage("Stage");
775
		$this->assertRecordHasLatestVersion($record, 2);
776
777
		// Test subclass without changes to sub class
778
		$record = VersionedTest_Subclass::create();
779
		$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...
780
		$record->writeToStage("Stage");
781
		$this->assertRecordHasLatestVersion($record, 1);
782
		$record->publish("Stage", "Live");
783
		$this->assertRecordHasLatestVersion($record, 1);
784
		$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...
785
		$record->writeToStage("Stage");
786
		$this->assertRecordHasLatestVersion($record, 2);
787
788
		// Test subclass with versioned extension only added to the base clases
789
		$record = VersionedTest_AnotherSubclass::create();
790
		$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...
791
		$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...
792
		$record->writeToStage("Stage");
793
		$this->assertRecordHasLatestVersion($record, 1);
794
		$record->publish("Stage", "Live");
795
		$this->assertRecordHasLatestVersion($record, 1);
796
		$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...
797
		$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...
798
		$record->writeToStage("Stage");
799
		$this->assertRecordHasLatestVersion($record, 2);
800
801
802
		// Test subclass without changes to base class
803
		$record = VersionedTest_AnotherSubclass::create();
804
		$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...
805
		$record->writeToStage("Stage");
806
		$this->assertRecordHasLatestVersion($record, 1);
807
		$record->publish("Stage", "Live");
808
		$this->assertRecordHasLatestVersion($record, 1);
809
		$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...
810
		$record->writeToStage("Stage");
811
		$this->assertRecordHasLatestVersion($record, 2);
812
813
		// Test subclass without changes to sub class
814
		$record = VersionedTest_AnotherSubclass::create();
815
		$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...
816
		$record->writeToStage("Stage");
817
		$this->assertRecordHasLatestVersion($record, 1);
818
		$record->publish("Stage", "Live");
819
		$this->assertRecordHasLatestVersion($record, 1);
820
		$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...
821
		$record->writeToStage("Stage");
822
		$this->assertRecordHasLatestVersion($record, 2);
823
	}
824
825
	public function testVersionedHandlesRenamedDataObjectFields(){
826
		Config::inst()->remove('VersionedTest_RelatedWithoutVersion','db','Name','Varchar');
827
828
		Config::inst()->update('VersionedTest_RelatedWithoutVersion','db',array(
829
			"NewField" => "Varchar",
830
		));
831
832
		VersionedTest_RelatedWithoutVersion::add_extension("Versioned('Stage', 'Live')");
833
		$this->resetDBSchema(true);
834
		$testData = new VersionedTest_RelatedWithoutVersion();
835
		$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...
836
		$testData->write();
837
	}
838
839
	public function testCanView() {
840
		$public1ID = $this->idFromFixture('VersionedTest_PublicStage', 'public1');
841
		$public2ID = $this->idFromFixture('VersionedTest_PublicViaExtension', 'public2');
842
		$privateID = $this->idFromFixture('VersionedTest_DataObject', 'page1');
843
		$singleID = $this->idFromFixture('VersionedTest_SingleStage', 'single');
844
845
		// Test that all (and only) public pages are viewable in stage mode
846
		Session::clear("loggedInAs");
847
		Versioned::reading_stage('Stage');
848
		$public1 = Versioned::get_one_by_stage('VersionedTest_PublicStage', 'Stage', array('"ID"' => $public1ID));
849
		$public2 = Versioned::get_one_by_stage('VersionedTest_PublicViaExtension', 'Stage', array('"ID"' => $public2ID));
850
		$private = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', array('"ID"' => $privateID));
851
		// Also test an object that has just a single-stage (eg. is only versioned)
852
		$single = Versioned::get_one_by_stage('VersionedTest_SingleStage', 'Stage', array('"ID"' => $singleID));
853
854
855
		$this->assertTrue($public1->canView());
856
		$this->assertTrue($public2->canView());
857
		$this->assertFalse($private->canView());
858
		$this->assertFalse($single->canView());
859
860
		// Adjusting the current stage should not allow objects loaded in stage to be viewable
861
		Versioned::reading_stage('Live');
862
		$this->assertTrue($public1->canView());
863
		$this->assertTrue($public2->canView());
864
		$this->assertFalse($private->canView());
865
		$this->assertFalse($single->canView());
866
867
		// Writing the private page to live should be fine though
868
		$private->publish("Stage", "Live");
869
		$privateLive = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Live', array('"ID"' => $privateID));
870
		$this->assertTrue($private->canView());
871
		$this->assertTrue($privateLive->canView());
872
873
		// But if the private version becomes different to the live version, it's once again disallowed
874
		Versioned::reading_stage('Stage');
875
		$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...
876
		$private->write();
877
		$this->assertFalse($private->canView());
878
		$this->assertTrue($privateLive->canView());
879
880
		// And likewise, viewing a live page (when mode is draft) should be ok
881
		Versioned::reading_stage('Stage');
882
		$this->assertFalse($private->canView());
883
		$this->assertTrue($privateLive->canView());
884
885
		// Logging in as admin should allow all permissions
886
		$this->logInWithPermission('ADMIN');
887
		Versioned::reading_stage('Stage');
888
		$this->assertTrue($public1->canView());
889
		$this->assertTrue($public2->canView());
890
		$this->assertTrue($private->canView());
891
		$this->assertTrue($single->canView());
892
	}
893
894
895
	public function testCanViewStage() {
896
		$public = $this->objFromFixture('VersionedTest_PublicStage', 'public1');
897
		$private = $this->objFromFixture('VersionedTest_DataObject', 'page1');
898
		Session::clear("loggedInAs");
899
		Versioned::reading_stage('Stage');
900
901
		// Test that all (and only) public pages are viewable in stage mode
902
		// Unpublished records are not viewable in live regardless of permissions
903
		$this->assertTrue($public->canViewStage('Stage'));
904
		$this->assertFalse($private->canViewStage('Stage'));
905
		$this->assertFalse($public->canViewStage('Live'));
906
		$this->assertFalse($private->canViewStage('Live'));
907
908
		// Writing records to live should make both stage and live modes viewable
909
		$private->publish("Stage", "Live");
910
		$public->publish("Stage", "Live");
911
		$this->assertTrue($public->canViewStage('Stage'));
912
		$this->assertTrue($private->canViewStage('Stage'));
913
		$this->assertTrue($public->canViewStage('Live'));
914
		$this->assertTrue($private->canViewStage('Live'));
915
916
		// If the draft mode changes, the live mode remains public, although the updated
917
		// draft mode is secured for non-public records.
918
		$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...
919
		$private->write();
920
		$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...
921
		$public->write();
922
		$this->assertTrue($public->canViewStage('Stage'));
923
		$this->assertFalse($private->canViewStage('Stage'));
924
		$this->assertTrue($public->canViewStage('Live'));
925
		$this->assertTrue($private->canViewStage('Live'));
926
	}
927
928
	/**
929
	 * Values that are overwritten with null are saved to the _versions table correctly.
930
	 */
931
	public function testWriteNullValueToVersion() {
932
		$record = VersionedTest_Subclass::create();
933
		$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...
934
		$record->write();
935
936
		$version = Versioned::get_latest_version($record->ClassName, $record->ID);
937
938
		$this->assertEquals(1, $version->Version);
939
		$this->assertEquals($record->Title, $version->Title);
940
941
		$record->Title = null;
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<VersionedTest_Subclass>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
942
		$record->write();
943
944
		$version = Versioned::get_latest_version($record->ClassName, $record->ID);
945
946
		$this->assertEquals(2, $version->Version);
947
		$this->assertEquals($record->Title, $version->Title);
948
	}
949
}
950
951
952
/**
953
 * @package framework
954
 * @subpackage tests
955
 */
956
class VersionedTest_DataObject extends DataObject implements TestOnly {
957
	private static $db = array(
958
		"Name" => "Varchar",
959
		'Title' => 'Varchar',
960
		'Content' => 'HTMLText'
961
	);
962
963
	private static $extensions = array(
964
		"Versioned('Stage', 'Live')"
965
	);
966
967
	private static $has_one = array(
968
		'Parent' => 'VersionedTest_DataObject'
969
	);
970
971
	private static $many_many = array(
972
		'Related' => 'VersionedTest_RelatedWithoutVersion'
973
	);
974
975
976
	public function canView($member = null) {
977
		$extended = $this->extendedCan(__FUNCTION__, $member);
978
		if($extended !== null) {
979
			return $extended;
980
		}
981
		return true;
982
	}
983
}
984
985
class VersionedTest_WithIndexes extends DataObject implements TestOnly {
986
987
	private static $db = array(
988
		'UniqA' => 'Int',
989
		'UniqS' => 'Int',
990
	);
991
	private static $extensions = array(
992
		"Versioned('Stage', 'Live')"
993
	);
994
	private static $indexes = array(
995
		'UniqS_idx' => 'unique ("UniqS")',
996
		'UniqA_idx' => array('type' => 'unique', 'name' => 'UniqA_idx', 'value' => '"UniqA"',),
997
	);
998
999
}
1000
1001
/**
1002
 * @package framework
1003
 * @subpackage tests
1004
 */
1005
class VersionedTest_RelatedWithoutVersion extends DataObject implements TestOnly {
1006
1007
	private static $db = array(
1008
		'Name' => 'Varchar'
1009
	);
1010
1011
	private static $belongs_many_many = array(
1012
		'Related' => 'VersionedTest_DataObject'
1013
	);
1014
1015
}
1016
1017
/**
1018
 * @package framework
1019
 * @subpackage tests
1020
 */
1021
class VersionedTest_Subclass extends VersionedTest_DataObject implements TestOnly {
1022
	private static $db = array(
1023
		"ExtraField" => "Varchar",
1024
	);
1025
}
1026
1027
/**
1028
 * @package framework
1029
 * @subpackage tests
1030
 */
1031
class VersionedTest_AnotherSubclass extends VersionedTest_DataObject implements TestOnly {
1032
	private static $db = array(
1033
		"AnotherField" => "Varchar"
1034
	);
1035
}
1036
1037
/**
1038
 * @package framework
1039
 * @subpackage tests
1040
 */
1041
class VersionedTest_UnversionedWithField extends DataObject implements TestOnly {
1042
	private static $db = array('Version' => 'Varchar(255)');
1043
}
1044
1045
class VersionedTest_SingleStage extends DataObject implements TestOnly {
1046
	private static $db = array(
1047
		'Name' => 'Varchar'
1048
	);
1049
1050
	private static $extensions = array(
1051
		'Versioned("Stage")'
1052
	);
1053
}
1054
1055
/**
1056
 * Versioned dataobject with public stage mode
1057
 */
1058
class VersionedTest_PublicStage extends DataObject implements TestOnly {
1059
	private static $db = array(
1060
		'Title' => 'Varchar'
1061
	);
1062
1063
	private static $extensions = array(
1064
		"Versioned('Stage', 'Live')"
1065
	);
1066
1067
	public function canView($member = null) {
1068
		$extended = $this->extendedCan(__FUNCTION__, $member);
1069
		if($extended !== null) {
1070
			return $extended;
1071
		}
1072
		return true;
1073
	}
1074
1075
	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...
1076
		// All non-live modes are public
1077
		return true;
1078
	}
1079
}
1080
1081
/**
1082
 * Public access is provided via extension rather than overriding canViewVersioned
1083
 */
1084
class VersionedTest_PublicViaExtension extends DataObject implements TestOnly {
1085
1086
	public function canView($member = null) {
1087
		$extended = $this->extendedCan(__FUNCTION__, $member);
1088
		if($extended !== null) {
1089
			return $extended;
1090
		}
1091
		return true;
1092
	}
1093
1094
	private static $db = array(
1095
		'Title' => 'Varchar'
1096
	);
1097
1098
	private static $extensions = array(
1099
		"Versioned('Stage', 'Live')",
1100
		"VersionedTest_PublicExtension"
1101
	);
1102
}
1103
1104
/**
1105
 * Alters stage mode of extended object to be public
1106
 */
1107
class VersionedTest_PublicExtension extends DataExtension implements TestOnly {
1108
	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...
1109
		return true;
1110
	}
1111
}
1112