Completed
Push — master ( b0e0e9...f4bf0c )
by Will
08:32
created

VersionedTest::testArchiveDraft()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 14
nc 1
nop 0
dl 0
loc 21
rs 9.3142
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\ORM\Tests;
4
5
use SilverStripe\Control\HTTPResponse_Exception;
6
use SilverStripe\ORM\DataObjectSchema;
7
use SilverStripe\ORM\DB;
8
use SilverStripe\ORM\Versioning\Versioned;
9
use SilverStripe\ORM\DataObject;
10
use SilverStripe\ORM\FieldType\DBDatetime;
11
use SilverStripe\Core\Convert;
12
use SilverStripe\Core\ClassInfo;
13
use SilverStripe\Core\Config\Config;
14
use SilverStripe\Core\Injector\Injector;
15
use SilverStripe\Dev\SapphireTest;
16
use SilverStripe\Control\Director;
17
use SilverStripe\Control\Session;
18
use DateTime;
19
20
class VersionedTest extends SapphireTest
21
{
22
23
    protected static $fixture_file = 'VersionedTest.yml';
24
25
    public static $extra_data_objects = [
26
        VersionedTest\TestObject::class,
27
        VersionedTest\Subclass::class,
28
        VersionedTest\AnotherSubclass::class,
29
        VersionedTest\RelatedWithoutversion::class,
30
        VersionedTest\SingleStage::class,
31
        VersionedTest\WithIndexes::class,
32
        VersionedTest\PublicStage::class,
33
        VersionedTest\PublicViaExtension::class,
34
        VersionedTest\CustomTable::class,
35
    ];
36
37
    protected function getExtraDataObjects()
38
    {
39
        return static::$extra_data_objects;
40
    }
41
42
    public function testUniqueIndexes()
43
    {
44
        $tableExpectations = array(
45
            'VersionedTest_WithIndexes' =>
46
                array('value' => true, 'message' => 'Unique indexes are unique in main table'),
47
            'VersionedTest_WithIndexes_Versions' =>
48
                array('value' => false, 'message' => 'Unique indexes are no longer unique in _Versions table'),
49
            'VersionedTest_WithIndexes_Live' =>
50
                array('value' => true, 'message' => 'Unique indexes are unique in _Live table'),
51
        );
52
53
        // Test each table's performance
54
        foreach ($tableExpectations as $tableName => $expectation) {
55
            $indexes = DB::get_schema()->indexList($tableName);
56
57
            // Check for presence of all unique indexes
58
            $indexColumns = array_map(
59
                function ($index) {
60
                    return $index['value'];
61
                },
62
                $indexes
63
            );
64
            sort($indexColumns);
65
            $expectedColumns = array('"UniqA"', '"UniqS"');
66
            $this->assertEquals(
67
                array_values($expectedColumns),
68
                array_values(array_intersect($indexColumns, $expectedColumns)),
69
                "$tableName has both indexes"
70
            );
71
72
            // Check unique -> non-unique conversion
73
            foreach ($indexes as $indexKey => $indexSpec) {
74
                if (in_array($indexSpec['value'], $expectedColumns)) {
75
                    $isUnique = $indexSpec['type'] === 'unique';
76
                    $this->assertEquals($isUnique, $expectation['value'], $expectation['message']);
77
                }
78
            }
79
        }
80
    }
81
82
    public function testDeletingOrphanedVersions()
83
    {
84
        $obj = new VersionedTest\Subclass();
85
        $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...
86
        $obj->write();
87
        $obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
88
89
        $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...
90
        $obj->write();
91
        $obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
92
93
        $versions = DB::query(
94
            "SELECT COUNT(*) FROM \"VersionedTest_Subclass_Versions\""
95
            . " WHERE \"RecordID\" = '$obj->ID'"
96
        )->value();
97
98
        $this->assertGreaterThan(0, $versions, 'At least 1 version exists in the history of the page');
99
100
        // Force orphaning of all versions created earlier, only on parent record.
101
        // The child versiones table should still have the correct relationship
102
        DB::query("DELETE FROM \"VersionedTest_DataObject_Versions\" WHERE \"RecordID\" = $obj->ID");
103
104
        // insert a record with no primary key (ID)
105
        DB::query("INSERT INTO \"VersionedTest_DataObject_Versions\" (\"RecordID\") VALUES ($obj->ID)");
106
107
        // run the script which should clean that up
108
        $obj->augmentDatabase();
109
110
        $versions = DB::query(
111
            "SELECT COUNT(*) FROM \"VersionedTest_Subclass_Versions\""
112
            . " WHERE \"RecordID\" = '$obj->ID'"
113
        )->value();
114
        $this->assertEquals(0, $versions, 'Orphaned versions on child tables are removed');
115
116
        // test that it doesn't delete records that we need
117
        $obj->write();
118
        $obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
119
120
        $count = DB::query(
121
            "SELECT COUNT(*) FROM \"VersionedTest_Subclass_Versions\""
122
            . " WHERE \"RecordID\" = '$obj->ID'"
123
        )->value();
124
        $obj->augmentDatabase();
125
126
        $count2 = DB::query(
127
            "SELECT COUNT(*) FROM \"VersionedTest_Subclass_Versions\""
128
            . " WHERE \"RecordID\" = '$obj->ID'"
129
        )->value();
130
131
        $this->assertEquals($count, $count2);
132
    }
133
134
    public function testCustomTable()
135
    {
136
        $obj = new VersionedTest\CustomTable();
137
        $obj->Title = 'my object';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...sionedTest\CustomTable>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
138
        $obj->write();
139
        $id = $obj->ID;
140
        $obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
141
        $obj->Title = 'new title';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...sionedTest\CustomTable>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
142
        $obj->write();
143
144
        $liveRecord = Versioned::get_by_stage(VersionedTest\CustomTable::class, Versioned::LIVE)->byID($id);
145
        $draftRecord = Versioned::get_by_stage(VersionedTest\CustomTable::class, Versioned::DRAFT)->byID($id);
146
147
        $this->assertEquals('my object', $liveRecord->Title);
148
        $this->assertEquals('new title', $draftRecord->Title);
149
    }
150
151
    /**
152
     * Test that publishing from invalid stage will throw exception
153
     */
154
    public function testInvalidPublish()
155
    {
156
        $obj = new VersionedTest\Subclass();
157
        $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...
158
        $obj->write();
159
        $class = VersionedTest\TestObject::class;
160
        $this->setExpectedException(
161
            'InvalidArgumentException',
162
            "Can't find {$class}#{$obj->ID} in stage Live"
163
        );
164
165
        // Fail publishing from live to stage
166
        $obj->copyVersionToStage(Versioned::LIVE, Versioned::DRAFT);
167
    }
168
169
    public function testDuplicate()
170
    {
171
        $obj1 = new VersionedTest\Subclass();
172
        $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...
173
        $obj1->write(); // version 1
174
        $obj1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
175
        $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...
176
        $obj1->write(); // version 2
177
178
        // Make duplicate
179
        $obj2 = $obj1->duplicate();
180
181
        // Check records differ
182
        $this->assertNotEquals($obj1->ID, $obj2->ID);
183
        $this->assertEquals(2, $obj1->Version);
184
        $this->assertEquals(1, $obj2->Version);
185
    }
186
187
    public function testForceChangeUpdatesVersion()
188
    {
189
        $obj = new VersionedTest\TestObject();
190
        $obj->Name = "test";
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
191
        $obj->write();
192
193
        $oldVersion = $obj->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
194
        $obj->forceChange();
195
        $obj->write();
196
197
        $this->assertTrue(
198
            ($obj->Version > $oldVersion),
199
            "A object Version is increased when just calling forceChange() without any other changes"
200
        );
201
    }
202
203
    /**
204
     * Test Versioned::get_including_deleted()
205
     */
206
    public function testGetIncludingDeleted()
207
    {
208
        // Get all ids of pages
209
        $allPageIDs = DataObject::get(
210
            VersionedTest\TestObject::class,
211
            "\"ParentID\" = 0",
212
            "\"VersionedTest_DataObject\".\"ID\" ASC"
213
        )->column('ID');
214
215
        // Modify a page, ensuring that the Version ID and Record ID will differ,
216
        // and then subsequently delete it
217
        $targetPage = $this->objFromFixture(VersionedTest\TestObject::class, 'page3');
218
        $targetPage->Content = 'To be deleted';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
219
        $targetPage->write();
220
        $targetPage->delete();
221
222
        // Get all items, ignoring deleted
223
        $remainingPages = DataObject::get(
224
            VersionedTest\TestObject::class,
225
            "\"ParentID\" = 0",
226
            "\"VersionedTest_DataObject\".\"ID\" ASC"
227
        );
228
        // Check that page 3 has gone
229
        $this->assertNotNull($remainingPages);
230
        $this->assertEquals(array("Page 1", "Page 2", "Subclass Page 1"), $remainingPages->column('Title'));
231
232
        // Get all including deleted
233
        $allPages = Versioned::get_including_deleted(
234
            VersionedTest\TestObject::class,
235
            "\"ParentID\" = 0",
236
            "\"VersionedTest_DataObject\".\"ID\" ASC"
237
        );
238
        // Check that page 3 is still there
239
        $this->assertEquals(array("Page 1", "Page 2", "Page 3", "Subclass Page 1"), $allPages->column('Title'));
240
241
        // Check that the returned pages have the correct IDs
242
        $this->assertEquals($allPageIDs, $allPages->column('ID'));
243
244
        // Check that this still works if we switch to reading the other stage
245
        Versioned::set_stage(Versioned::LIVE);
246
        $allPages = Versioned::get_including_deleted(
247
            VersionedTest\TestObject::class,
248
            "\"ParentID\" = 0",
249
            "\"VersionedTest_DataObject\".\"ID\" ASC"
250
        );
251
        $this->assertEquals(array("Page 1", "Page 2", "Page 3", "Subclass Page 1"), $allPages->column('Title'));
252
253
        // Check that the returned pages still have the correct IDs
254
        $this->assertEquals($allPageIDs, $allPages->column('ID'));
255
    }
256
257
    public function testVersionedFieldsAdded()
258
    {
259
        $obj = new VersionedTest\TestObject();
260
        // Check that the Version column is added as a full-fledged column
261
        $this->assertInstanceOf('SilverStripe\\ORM\\FieldType\\DBInt', $obj->dbObject('Version'));
262
263
        $obj2 = new VersionedTest\Subclass();
264
        // Check that the Version column is added as a full-fledged column
265
        $this->assertInstanceOf('SilverStripe\\ORM\\FieldType\\DBInt', $obj2->dbObject('Version'));
266
    }
267
268
    public function testVersionedFieldsNotInCMS()
269
    {
270
        $obj = new VersionedTest\TestObject();
271
272
        // the version field in cms causes issues with Versioned::augmentWrite()
273
        $this->assertNull($obj->getCMSFields()->dataFieldByName('Version'));
274
    }
275
276
    public function testPublishCreateNewVersion()
277
    {
278
        /** @var VersionedTest\TestObject $page1 */
279
        $page1 = $this->objFromFixture(VersionedTest\TestObject::class, 'page1');
280
        $page1->Content = 'orig';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
281
        $page1->write();
282
        $firstVersion = $page1->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
283
        $page1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE, false);
284
        $this->assertEquals(
285
            $firstVersion,
286
            $page1->Version,
287
            'publish() with $createNewVersion=FALSE does not create a new version'
288
        );
289
290
        $page1->Content = 'changed';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
291
        $page1->write();
292
        $secondVersion = $page1->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
293
        $this->assertTrue($firstVersion < $secondVersion, 'write creates new version');
294
295
        $page1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE, true);
296
        $thirdVersion = Versioned::get_latest_version(VersionedTest\TestObject::class, $page1->ID)->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
297
        $liveVersion = Versioned::get_versionnumber_by_stage(VersionedTest\TestObject::class, 'Live', $page1->ID);
298
        $stageVersion = Versioned::get_versionnumber_by_stage(VersionedTest\TestObject::class, 'Stage', $page1->ID);
299
        $this->assertTrue(
300
            $secondVersion < $thirdVersion,
301
            'publish() with $createNewVersion=TRUE creates a new version'
302
        );
303
        $this->assertEquals(
304
            $liveVersion,
305
            $thirdVersion,
306
            'publish() with $createNewVersion=TRUE publishes to live'
307
        );
308
        $this->assertEquals(
309
            $stageVersion,
310
            $thirdVersion,
311
            'publish() with $createNewVersion=TRUE also updates draft'
312
        );
313
    }
314
315
    public function testRollbackTo()
316
    {
317
        $page1 = $this->objFromFixture(VersionedTest\AnotherSubclass::class, 'subclass1');
318
        $page1->Content = 'orig';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

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

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

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

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

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

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
327
328
        $page1->doRollbackTo($origVersion);
329
        $page1 = Versioned::get_one_by_stage(
330
            VersionedTest\TestObject::class,
331
            'Stage',
332
            array(
333
            '"VersionedTest_DataObject"."ID" = ?' => $page1->ID
334
            )
335
        );
336
337
        $this->assertTrue($page1->Version == $changedVersion + 1, 'Create a new higher version number');
338
        $this->assertEquals('orig', $page1->Content, 'Copies the content from the old version');
339
340
        // check db entries
341
        $version = DB::prepared_query(
342
            "SELECT MAX(\"Version\") FROM \"VersionedTest_DataObject_Versions\" WHERE \"RecordID\" = ?",
343
            array($page1->ID)
344
        )->value();
345
        $this->assertEquals($page1->Version, $version, 'Correct entry in VersionedTest_DataObject_Versions');
346
347
        $version = DB::prepared_query(
348
            "SELECT MAX(\"Version\") FROM \"VersionedTest_AnotherSubclass_Versions\" WHERE \"RecordID\" = ?",
349
            array($page1->ID)
350
        )->value();
351
        $this->assertEquals($page1->Version, $version, 'Correct entry in VersionedTest_AnotherSubclass_Versions');
352
    }
353
354
    public function testDeleteFromStage()
355
    {
356
        $page1 = $this->objFromFixture(VersionedTest\TestObject::class, 'page1');
357
        $pageID = $page1->ID;
358
359
        $page1->Content = 'orig';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
360
        $page1->write();
361
        $page1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
362
363
        $this->assertEquals(
364
            1,
365
            DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value()
366
        );
367
        $this->assertEquals(
368
            1,
369
            DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value()
370
        );
371
372
        $page1->deleteFromStage('Live');
373
374
        // Confirm that deleteFromStage() doesn't manipulate the original record
375
        $this->assertEquals($pageID, $page1->ID);
376
377
        $this->assertEquals(
378
            1,
379
            DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value()
380
        );
381
        $this->assertEquals(
382
            0,
383
            DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value()
384
        );
385
386
        $page1->delete();
387
388
        $this->assertEquals(0, $page1->ID);
389
        $this->assertEquals(
390
            0,
391
            DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value()
392
        );
393
        $this->assertEquals(
394
            0,
395
            DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value()
396
        );
397
    }
398
399
    public function testWritingNewToStage()
400
    {
401
        $origReadingMode = Versioned::get_reading_mode();
402
403
        Versioned::set_stage(Versioned::DRAFT);
404
        $page = new VersionedTest\TestObject();
405
        $page->Title = "testWritingNewToStage";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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
        $page->URLSegment = "testWritingNewToStage";
0 ignored issues
show
Documentation introduced by
The property URLSegment does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
407
        $page->write();
408
409
        $live = Versioned::get_by_stage(
410
            VersionedTest\TestObject::class,
411
            'Live',
412
            array(
413
            '"VersionedTest_DataObject_Live"."ID"' => $page->ID
414
            )
415
        );
416
        $this->assertEquals(0, $live->count());
417
418
        $stage = Versioned::get_by_stage(
419
            VersionedTest\TestObject::class,
420
            'Stage',
421
            array(
422
            '"VersionedTest_DataObject"."ID"' => $page->ID
423
            )
424
        );
425
        $this->assertEquals(1, $stage->count());
426
        $this->assertEquals($stage->First()->Title, 'testWritingNewToStage');
427
428
        Versioned::set_reading_mode($origReadingMode);
429
    }
430
431
    /**
432
     * Writing a page to live should update both draft and live tables
433
     */
434
    public function testWritingNewToLive()
435
    {
436
        $origReadingMode = Versioned::get_reading_mode();
437
438
        Versioned::set_stage(Versioned::LIVE);
439
        $page = new VersionedTest\TestObject();
440
        $page->Title = "testWritingNewToLive";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
441
        $page->URLSegment = "testWritingNewToLive";
0 ignored issues
show
Documentation introduced by
The property URLSegment does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
442
        $page->write();
443
444
        $live = Versioned::get_by_stage(
445
            VersionedTest\TestObject::class,
446
            'Live',
447
            array(
448
            '"VersionedTest_DataObject_Live"."ID"' => $page->ID
449
            )
450
        );
451
        $this->assertEquals(1, $live->count());
452
        $liveRecord = $live->First();
453
        $this->assertEquals($liveRecord->Title, 'testWritingNewToLive');
454
455
        $stage = Versioned::get_by_stage(
456
            VersionedTest\TestObject::class,
457
            'Stage',
458
            array(
459
            '"VersionedTest_DataObject"."ID"' => $page->ID
460
            )
461
        );
462
        $this->assertEquals(1, $stage->count());
463
        $stageRecord = $stage->first();
464
        $this->assertEquals($stageRecord->Title, 'testWritingNewToLive');
465
466
        // Both records have the same version
467
        $this->assertEquals($liveRecord->Version, $stageRecord->Version);
468
469
        Versioned::set_reading_mode($origReadingMode);
470
    }
471
472
    /**
473
     * Tests DataObject::hasOwnTableDatabaseField
474
     */
475
    public function testHasOwnTableDatabaseFieldWithVersioned()
476
    {
477
        $schema = DataObject::getSchema();
478
479
        $this->assertNull(
480
            $schema->fieldSpec(DataObject::class, 'Version', DataObjectSchema::UNINHERITED),
481
            'Plain models have no version field.'
482
        );
483
        $this->assertEquals(
484
            'Int',
485
            $schema->fieldSpec(VersionedTest\TestObject::class, 'Version', DataObjectSchema::UNINHERITED),
486
            'The versioned ext adds an Int version field.'
487
        );
488
        $this->assertNull(
489
            $schema->fieldSpec(VersionedTest\Subclass::class, 'Version', DataObjectSchema::UNINHERITED),
490
            'Sub-classes of a versioned model don\'t have a Version field.'
491
        );
492
        $this->assertNull(
493
            $schema->fieldSpec(VersionedTest\AnotherSubclass::class, 'Version', DataObjectSchema::UNINHERITED),
494
            'Sub-classes of a versioned model don\'t have a Version field.'
495
        );
496
        $this->assertEquals(
497
            'Varchar(255)',
498
            $schema->fieldSpec(VersionedTest\UnversionedWithField::class, 'Version', DataObjectSchema::UNINHERITED),
499
            'Models w/o Versioned can have their own Version field.'
500
        );
501
    }
502
503
    /**
504
     * Test that SQLSelect::queriedTables() applies the version-suffixes properly.
505
     */
506
    public function testQueriedTables()
507
    {
508
        Versioned::set_stage(Versioned::LIVE);
509
510
        $this->assertEquals(
511
            array(
512
            'VersionedTest_DataObject_Live',
513
            'VersionedTest_Subclass_Live',
514
            ),
515
            DataObject::get(VersionedTest\Subclass::class)->dataQuery()->query()->queriedTables()
516
        );
517
    }
518
519
    /**
520
     * Virtual "sleep" that doesn't actually slow execution, only advances DBDateTime::now()
521
     *
522
     * @param int $minutes
523
     */
524
    protected function sleep($minutes)
525
    {
526
        $now = DBDatetime::now();
527
        $date = DateTime::createFromFormat('Y-m-d H:i:s', $now->getValue());
528
        $date->modify("+{$minutes} minutes");
529
        DBDatetime::set_mock_now($date->format('Y-m-d H:i:s'));
530
    }
531
532
    /**
533
     * Tests records selected by specific version
534
     */
535
    public function testGetVersion()
536
    {
537
        // Create a few initial versions to ensure this version
538
        // doesn't clash with child versions
539
        $this->sleep(1);
540
        /** @var VersionedTest\TestObject $page2 */
541
        $page2 = $this->objFromFixture(VersionedTest\TestObject::class, 'page2');
542
        $page2->Title = 'dummy1';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
543
        $page2->write();
544
        $this->sleep(1);
545
        $page2->Title = 'dummy2';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
546
        $page2->write();
547
        $this->sleep(1);
548
        $page2->Title = 'Page 2 - v1';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
549
        $page2->write();
550
        $version1Date = $page2->LastEdited;
551
        $version1 = $page2->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
552
553
        // Create another version where this object and some
554
        // child records have been modified
555
        $this->sleep(1);
556
        /** @var VersionedTest\TestObject $page2a */
557
        $page2a = $this->objFromFixture(VersionedTest\TestObject::class, 'page2a');
558
        $page2a->Title = 'Page 2a - v2';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
559
        $page2a->write();
560
        $this->sleep(1);
561
        $page2->Title = 'Page 2 - v2';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
562
        $page2->write();
563
        $version2Date = $page2->LastEdited;
564
        $version2 = $page2->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
565
        $this->assertGreaterThan($version1, $version2);
566
        $this->assertDOSEquals(
567
            [
568
                ['Title' => 'Page 2a - v2'],
569
                ['Title' => 'Page 2b'],
570
            ],
571
            $page2->Children()
572
        );
573
574
        // test selecting v1
575
        /** @var VersionedTest\TestObject $page2v1 */
576
        $page2v1 = Versioned::get_version(VersionedTest\TestObject::class, $page2->ID, $version1);
577
        $this->assertEquals('Page 2 - v1', $page2v1->Title);
578
579
        // When selecting v1, related records should by filtered by
580
        // the modified date of that version
581
        $archiveParms = [
582
            'Versioned.mode' => 'archive',
583
            'Versioned.date' => $version1Date
584
        ];
585
        $this->assertEquals($archiveParms, $page2v1->getInheritableQueryParams());
586
        $this->assertArraySubset($archiveParms, $page2v1->Children()->getQueryParams());
587
        $this->assertDOSEquals(
588
            [
589
                ['Title' => 'Page 2a'],
590
                ['Title' => 'Page 2b'],
591
            ],
592
            $page2v1->Children()
593
        );
594
595
        // When selecting v2, we get the same as on stage
596
        /** @var VersionedTest\TestObject $page2v2 */
597
        $page2v2 = Versioned::get_version(VersionedTest\TestObject::class, $page2->ID, $version2);
598
        $this->assertEquals('Page 2 - v2', $page2v2->Title);
599
600
        // When selecting v2, related records should by filtered by
601
        // the modified date of that version
602
        $archiveParms = [
603
            'Versioned.mode' => 'archive',
604
            'Versioned.date' => $version2Date
605
        ];
606
        $this->assertEquals($archiveParms, $page2v2->getInheritableQueryParams());
607
        $this->assertArraySubset($archiveParms, $page2v2->Children()->getQueryParams());
608
        $this->assertDOSEquals(
609
            [
610
                ['Title' => 'Page 2a - v2'],
611
                ['Title' => 'Page 2b'],
612
            ],
613
            $page2v2->Children()
614
        );
615
    }
616
617
    public function testGetVersionWhenClassnameChanged()
618
    {
619
        $obj = new VersionedTest\TestObject;
620
        $obj->Name = "test";
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
621
        $obj->write();
622
        $obj->Name = "test2";
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
623
        $obj->ClassName = VersionedTest\Subclass::class;
624
        $obj->write();
625
        $subclassVersion = $obj->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
626
627
        $obj->Name = "test3";
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
628
        $obj->ClassName = VersionedTest\TestObject::class;
629
        $obj->write();
630
631
        // We should be able to pass the subclass and still get the correct class back
632
        $obj2 = Versioned::get_version(VersionedTest\Subclass::class, $obj->ID, $subclassVersion);
633
        $this->assertInstanceOf(VersionedTest\Subclass::class, $obj2);
634
        $this->assertEquals("test2", $obj2->Name);
635
636
        $obj3 = Versioned::get_latest_version(VersionedTest\Subclass::class, $obj->ID);
637
        $this->assertEquals("test3", $obj3->Name);
638
        $this->assertInstanceOf(VersionedTest\TestObject::class, $obj3);
639
    }
640
641
    public function testArchiveVersion()
642
    {
643
        // In 2005 this file was created
644
        DBDatetime::set_mock_now('2005-01-01 00:00:00');
645
        $testPage = new VersionedTest\Subclass();
646
        $testPage->Title = 'Archived page';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...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...
647
        $testPage->Content = 'This is the content from 2005';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\...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...
648
        $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...
649
        $testPage->write();
650
651
        // In 2007 we updated it
652
        DBDatetime::set_mock_now('2007-01-01 00:00:00');
653
        $testPage->Content = "It's 2007 already!";
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\...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...
654
        $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...
655
        $testPage->write();
656
657
        // In 2009 we updated it again
658
        DBDatetime::set_mock_now('2009-01-01 00:00:00');
659
        $testPage->Content = "I'm enjoying 2009";
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\...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...
660
        $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...
661
        $testPage->write();
662
663
        // End mock, back to the present day:)
664
        DBDatetime::clear_mock_now();
665
666
        // Test 1 - 2006 Content
667
        singleton(VersionedTest\Subclass::class)->flushCache(true);
668
        Versioned::set_reading_mode('Archive.2006-01-01 00:00:00');
669
        $testPage2006 = DataObject::get(VersionedTest\Subclass::class)->filter(array('Title' => 'Archived page'))->first();
670
        $this->assertInstanceOf(VersionedTest\Subclass::class, $testPage2006);
671
        $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...
672
        $this->assertEquals("This is the content from 2005", $testPage2006->Content);
673
674
        // Test 2 - 2008 Content
675
        singleton(VersionedTest\Subclass::class)->flushCache(true);
676
        Versioned::set_reading_mode('Archive.2008-01-01 00:00:00');
677
        $testPage2008 = DataObject::get(VersionedTest\Subclass::class)->filter(array('Title' => 'Archived page'))->first();
678
        $this->assertInstanceOf(VersionedTest\Subclass::class, $testPage2008);
679
        $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...
680
        $this->assertEquals("It's 2007 already!", $testPage2008->Content);
681
682
        // Test 3 - Today
683
        singleton(VersionedTest\Subclass::class)->flushCache(true);
684
        Versioned::set_reading_mode('Stage.Stage');
685
        $testPageCurrent = DataObject::get(VersionedTest\Subclass::class)->filter(array('Title' => 'Archived page'))
686
            ->first();
687
        $this->assertInstanceOf(VersionedTest\Subclass::class, $testPageCurrent);
688
        $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...
689
        $this->assertEquals("I'm enjoying 2009", $testPageCurrent->Content);
690
    }
691
692
    /**
693
     * Test that archive works on live stage
694
     */
695
    public function testArchiveLive()
696
    {
697
        Versioned::set_stage(Versioned::LIVE);
698
        $this->logInWithPermission('ADMIN');
699
        $record = new VersionedTest\TestObject();
700
        $record->Name = 'test object';
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
701
        // Writing in live mode should write to draft as well
702
        $record->write();
703
        $recordID = $record->ID;
704
        $this->assertTrue($record->isPublished());
705
        $this->assertTrue($record->isOnDraft());
706
707
        // Delete in live
708
        /** @var VersionedTest\TestObject $recordLive */
709
        $recordLive = VersionedTest\TestObject::get()->byID($recordID);
710
        $recordLive->doArchive();
711
        $this->assertFalse($recordLive->isPublished());
712
        $this->assertFalse($recordLive->isOnDraft());
713
    }
714
715
    /**
716
     * Test archive works on draft
717
     */
718
    public function testArchiveDraft()
719
    {
720
        Versioned::set_stage(Versioned::DRAFT);
721
        $this->logInWithPermission('ADMIN');
722
        $record = new VersionedTest\TestObject();
723
        $record->Name = 'test object';
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
724
725
        // Writing in draft mode requires publishing to effect on live
726
        $record->write();
727
        $record->publishRecursive();
728
        $recordID = $record->ID;
729
        $this->assertTrue($record->isPublished());
730
        $this->assertTrue($record->isOnDraft());
731
732
        // Delete in draft
733
        /** @var VersionedTest\TestObject $recordDraft */
734
        $recordDraft = VersionedTest\TestObject::get()->byID($recordID);
735
        $recordDraft->doArchive();
736
        $this->assertFalse($recordDraft->isPublished());
737
        $this->assertFalse($recordDraft->isOnDraft());
738
    }
739
740
    public function testAllVersions()
741
    {
742
        // In 2005 this file was created
743
        DBDatetime::set_mock_now('2005-01-01 00:00:00');
744
        $testPage = new VersionedTest\Subclass();
745
        $testPage->Title = 'Archived page';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...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...
746
        $testPage->Content = 'This is the content from 2005';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\...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...
747
        $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...
748
        $testPage->write();
749
750
        // In 2007 we updated it
751
        DBDatetime::set_mock_now('2007-01-01 00:00:00');
752
        $testPage->Content = "It's 2007 already!";
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\...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...
753
        $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...
754
        $testPage->write();
755
756
        // Check both versions are returned
757
        $versions = Versioned::get_all_versions(VersionedTest\Subclass::class, $testPage->ID);
758
        $content = array();
759
        $extraFields = array();
760
        foreach ($versions as $version) {
761
            $content[] = $version->Content;
762
            $extraFields[] = $version->ExtraField;
763
        }
764
765
        $this->assertEquals($versions->Count(), 2, 'All versions returned');
766
        $this->assertEquals(
767
            $content,
768
            array('This is the content from 2005', "It's 2007 already!"),
769
            'Version fields returned'
770
        );
771
        $this->assertEquals($extraFields, array('2005', '2007'), 'Version fields returned');
772
773
        // In 2009 we updated it again
774
        DBDatetime::set_mock_now('2009-01-01 00:00:00');
775
        $testPage->Content = "I'm enjoying 2009";
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\...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...
776
        $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...
777
        $testPage->write();
778
779
        // End mock, back to the present day:)
780
        DBDatetime::clear_mock_now();
781
782
        $versions = Versioned::get_all_versions(VersionedTest\Subclass::class, $testPage->ID);
783
        $content = array();
784
        $extraFields = array();
785
        foreach ($versions as $version) {
786
            $content[] = $version->Content;
787
            $extraFields[] = $version->ExtraField;
788
        }
789
790
        $this->assertEquals($versions->Count(), 3, 'Additional all versions returned');
791
        $this->assertEquals(
792
            $content,
793
            array('This is the content from 2005', "It's 2007 already!", "I'm enjoying 2009"),
794
            'Additional version fields returned'
795
        );
796
        $this->assertEquals($extraFields, array('2005', '2007', '2009'), 'Additional version fields returned');
797
    }
798
799
    public function testArchiveRelatedDataWithoutVersioned()
800
    {
801
        DBDatetime::set_mock_now('2009-01-01 00:00:00');
802
803
        $relatedData = new VersionedTest\RelatedWithoutversion();
804
        $relatedData->Name = 'Related Data';
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<SilverStripe\ORM\...\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...
805
        $relatedDataId = $relatedData->write();
806
807
        $testData = new VersionedTest\TestObject();
808
        $testData->Title = 'Test';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
809
        $testData->Content = 'Before Content';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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
        $testData->Related()->add($relatedData);
811
        $id = $testData->write();
812
813
        DBDatetime::set_mock_now('2010-01-01 00:00:00');
814
        $testData->Content = 'After Content';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
815
        $testData->write();
816
817
        Versioned::reading_archived_date('2009-01-01 19:00:00');
818
819
        $fetchedData = VersionedTest\TestObject::get()->byId($id);
820
        $this->assertEquals('Before Content', $fetchedData->Content, 'We see the correct content of the older version');
821
822
        $relatedData = VersionedTest\RelatedWithoutversion::get()->byId($relatedDataId);
823
        $this->assertEquals(
824
            1,
825
            $relatedData->Related()->count(),
826
            'We have a relation, with no version table, querying it still works'
827
        );
828
    }
829
830
    public function testVersionedWithSingleStage()
831
    {
832
        $tables = DB::table_list();
833
        $this->assertContains(
834
            'versionedtest_singlestage',
835
            array_keys($tables),
836
            'Contains base table'
837
        );
838
        $this->assertContains(
839
            'versionedtest_singlestage_versions',
840
            array_keys($tables),
841
            'Contains versions table'
842
        );
843
        $this->assertNotContains(
844
            'versionedtest_singlestage_live',
845
            array_keys($tables),
846
            'Does not contain separate table with _Live suffix'
847
        );
848
        $this->assertNotContains(
849
            'versionedtest_singlestage_stage',
850
            array_keys($tables),
851
            'Does not contain separate table with _Stage suffix'
852
        );
853
854
        Versioned::set_stage(Versioned::DRAFT);
855
        $obj = new VersionedTest\SingleStage(array('Name' => 'MyObj'));
856
        $obj->write();
857
        $this->assertNotNull(
858
            VersionedTest\SingleStage::get()->byID($obj->ID),
859
            'Writes to and reads from default stage if its set explicitly'
860
        );
861
862
        Versioned::set_stage(Versioned::LIVE);
863
        $obj = new VersionedTest\SingleStage(array('Name' => 'MyObj'));
864
        $obj->write();
865
        $this->assertNotNull(
866
            VersionedTest\SingleStage::get()->byID($obj->ID),
867
            'Writes to and reads from default stage even if a non-matching stage is set'
868
        );
869
    }
870
871
    /**
872
     * Test that publishing processes respects lazy loaded fields
873
     */
874
    public function testLazyLoadFields()
875
    {
876
        $originalMode = Versioned::get_reading_mode();
877
878
        // Generate staging record and retrieve it from stage in live mode
879
        Versioned::set_stage(Versioned::DRAFT);
880
        $obj = new VersionedTest\Subclass();
881
        $obj->Name = 'bob';
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<SilverStripe\ORM\...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...
882
        $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...
883
        $obj->write();
884
        $objID = $obj->ID;
885
        $filter = sprintf('"VersionedTest_DataObject"."ID" = \'%d\'', Convert::raw2sql($objID));
886
        Versioned::set_stage(Versioned::LIVE);
887
888
        // Check fields are unloaded prior to access
889
        $objLazy = Versioned::get_one_by_stage(VersionedTest\TestObject::class, 'Stage', $filter, false);
890
        $lazyFields = $objLazy->getQueriedDatabaseFields();
891
        $this->assertTrue(isset($lazyFields['ExtraField_Lazy']));
892
        $this->assertEquals(VersionedTest\Subclass::class, $lazyFields['ExtraField_Lazy']);
893
894
        // Check lazy loading works when viewing a Stage object in Live mode
895
        $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...
896
897
        // Test that writeToStage respects lazy loaded fields
898
        $objLazy = Versioned::get_one_by_stage(VersionedTest\TestObject::class, 'Stage', $filter, false);
899
        $objLazy->writeToStage('Live');
900
        $objLive = Versioned::get_one_by_stage(VersionedTest\TestObject::class, 'Live', $filter, false);
901
        $liveLazyFields = $objLive->getQueriedDatabaseFields();
902
903
        // Check fields are unloaded prior to access
904
        $this->assertTrue(isset($liveLazyFields['ExtraField_Lazy']));
905
        $this->assertEquals(VersionedTest\Subclass::class, $liveLazyFields['ExtraField_Lazy']);
906
907
        // Check that live record has original value
908
        $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...
909
910
        Versioned::set_reading_mode($originalMode);
911
    }
912
913
    public function testLazyLoadFieldsRetrieval()
914
    {
915
        // Set reading mode to Stage
916
        Versioned::set_stage(Versioned::DRAFT);
917
918
        // Create object only in reading stage
919
        $original = new VersionedTest\Subclass();
920
        $original->ExtraField = 'Foo';
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

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

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

Loading history...
921
        $original->write();
922
923
        // Query for object using base class
924
        $query = VersionedTest\TestObject::get()->filter('ID', $original->ID);
925
926
        // Set reading mode to Live
927
        Versioned::set_stage(Versioned::LIVE);
928
929
        $fetched = $query->first();
930
        $this->assertTrue($fetched instanceof VersionedTest\Subclass);
931
        $this->assertEquals($original->ID, $fetched->ID); // Eager loaded
932
        $this->assertEquals($original->ExtraField, $fetched->ExtraField); // Lazy loaded
0 ignored issues
show
Bug introduced by
The property ExtraField does not seem to exist. Did you mean many_many_extraFields?

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

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

Loading history...
933
    }
934
935
    /**
936
     * Tests that reading mode persists between requests
937
     */
938
    public function testReadingPersistent()
0 ignored issues
show
Coding Style introduced by
testReadingPersistent uses the super-global variable $_GET which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
939
    {
940
        $session = Injector::inst()->create('SilverStripe\\Control\\Session', array());
941
        $adminID = $this->logInWithPermission('ADMIN');
942
        $session->inst_set('loggedInAs', $adminID);
943
944
        // Set to stage
945
        Director::test('/?stage=Stage', null, $session);
946
        $this->assertEquals(
947
            'Stage.Stage',
948
            $session->inst_get('readingMode'),
949
            'Check querystring changes reading mode to Stage'
950
        );
951
        Director::test('/', null, $session);
952
        $this->assertEquals(
953
            'Stage.Stage',
954
            $session->inst_get('readingMode'),
955
            'Check that subsequent requests in the same session remain in Stage mode'
956
        );
957
958
        // Test live persists
959
        Director::test('/?stage=Live', null, $session);
960
        $this->assertEquals(
961
            'Stage.Live',
962
            $session->inst_get('readingMode'),
963
            'Check querystring changes reading mode to Live'
964
        );
965
        Director::test('/', null, $session);
966
        $this->assertEquals(
967
            'Stage.Live',
968
            $session->inst_get('readingMode'),
969
            'Check that subsequent requests in the same session remain in Live mode'
970
        );
971
972
        // Test that session doesn't redundantly store the default stage if it doesn't need to
973
        $session2 = Injector::inst()->create('SilverStripe\\Control\\Session', array());
974
        $session2->inst_set('loggedInAs', $adminID);
975
        Director::test('/', null, $session2);
976
        $this->assertArrayNotHasKey('readingMode', $session2->inst_changedData());
977
        Director::test('/?stage=Live', null, $session2);
978
        $this->assertArrayNotHasKey('readingMode', $session2->inst_changedData());
979
980
        // Test choose_site_stage
981
        unset($_GET['stage']);
982
        unset($_GET['archiveDate']);
983
        Session::set('readingMode', 'Stage.Stage');
984
        Versioned::choose_site_stage();
985
        $this->assertEquals('Stage.Stage', Versioned::get_reading_mode());
986
        Session::set('readingMode', 'Archive.2014-01-01');
987
        Versioned::choose_site_stage();
988
        $this->assertEquals('Archive.2014-01-01', Versioned::get_reading_mode());
989
        Session::clear('readingMode');
990
        Versioned::choose_site_stage();
991
        $this->assertEquals('Stage.Live', Versioned::get_reading_mode());
992
    }
993
994
    /**
995
     * Test that stage parameter is blocked by non-administrative users
996
     */
997
    public function testReadingModeSecurity()
998
    {
999
        $this->setExpectedException(HTTPResponse_Exception::class);
1000
        $session = Injector::inst()->create(Session::class, array());
1001
        Director::test('/?stage=Stage', null, $session);
1002
    }
1003
1004
    /**
1005
     * Ensures that the latest version of a record is the expected value
1006
     *
1007
     * @param DataObject $record
1008
     * @param int        $version
1009
     */
1010
    protected function assertRecordHasLatestVersion($record, $version)
1011
    {
1012
        $schema = DataObject::getSchema();
1013
        foreach (ClassInfo::ancestry(get_class($record), true) as $class) {
1014
            $table = $schema->tableName($class);
1015
            $versionForClass = DB::prepared_query(
1016
                $sql = "SELECT MAX(\"Version\") FROM \"{$table}_Versions\" WHERE \"RecordID\" = ?",
1017
                array($record->ID)
1018
            )->value();
1019
            $this->assertEquals($version, $versionForClass, "That the table $table has the latest version $version");
1020
        }
1021
    }
1022
1023
    /**
1024
     * Tests that multi-table dataobjects are correctly versioned
1025
     */
1026
    public function testWriteToStage()
1027
    {
1028
        // Test subclass with versioned extension directly added
1029
        $record = VersionedTest\Subclass::create();
1030
        $record->Title = "Test A";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...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...
1031
        $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...
1032
        $record->writeToStage("Stage");
1033
        $this->assertRecordHasLatestVersion($record, 1);
1034
        $record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
1035
        $this->assertRecordHasLatestVersion($record, 1);
1036
        $record->Title = "Test A2";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...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...
1037
        $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...
1038
        $record->writeToStage("Stage");
1039
        $this->assertRecordHasLatestVersion($record, 2);
1040
1041
        // Test subclass without changes to base class
1042
        $record = VersionedTest\Subclass::create();
1043
        $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...
1044
        $record->writeToStage("Stage");
1045
        $this->assertRecordHasLatestVersion($record, 1);
1046
        $record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
1047
        $this->assertRecordHasLatestVersion($record, 1);
1048
        $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...
1049
        $record->writeToStage("Stage");
1050
        $this->assertRecordHasLatestVersion($record, 2);
1051
1052
        // Test subclass without changes to sub class
1053
        $record = VersionedTest\Subclass::create();
1054
        $record->Title = "Test C";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...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...
1055
        $record->writeToStage("Stage");
1056
        $this->assertRecordHasLatestVersion($record, 1);
1057
        $record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
1058
        $this->assertRecordHasLatestVersion($record, 1);
1059
        $record->Title = "Test C2";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...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...
1060
        $record->writeToStage("Stage");
1061
        $this->assertRecordHasLatestVersion($record, 2);
1062
1063
        // Test subclass with versioned extension only added to the base clases
1064
        $record = VersionedTest\AnotherSubclass::create();
1065
        $record->Title = "Test A";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...edTest\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...
1066
        $record->AnotherField = "Test A";
0 ignored issues
show
Documentation introduced by
The property AnotherField does not exist on object<SilverStripe\ORM\...edTest\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...
1067
        $record->writeToStage("Stage");
1068
        $this->assertRecordHasLatestVersion($record, 1);
1069
        $record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
1070
        $this->assertRecordHasLatestVersion($record, 1);
1071
        $record->Title = "Test A2";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...edTest\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...
1072
        $record->AnotherField = "Test A2";
0 ignored issues
show
Documentation introduced by
The property AnotherField does not exist on object<SilverStripe\ORM\...edTest\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...
1073
        $record->writeToStage("Stage");
1074
        $this->assertRecordHasLatestVersion($record, 2);
1075
1076
1077
        // Test subclass without changes to base class
1078
        $record = VersionedTest\AnotherSubclass::create();
1079
        $record->AnotherField = "Test B";
0 ignored issues
show
Documentation introduced by
The property AnotherField does not exist on object<SilverStripe\ORM\...edTest\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...
1080
        $record->writeToStage("Stage");
1081
        $this->assertRecordHasLatestVersion($record, 1);
1082
        $record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
1083
        $this->assertRecordHasLatestVersion($record, 1);
1084
        $record->AnotherField = "Test B2";
0 ignored issues
show
Documentation introduced by
The property AnotherField does not exist on object<SilverStripe\ORM\...edTest\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...
1085
        $record->writeToStage("Stage");
1086
        $this->assertRecordHasLatestVersion($record, 2);
1087
1088
        // Test subclass without changes to sub class
1089
        $record = VersionedTest\AnotherSubclass::create();
1090
        $record->Title = "Test C";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...edTest\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...
1091
        $record->writeToStage("Stage");
1092
        $this->assertRecordHasLatestVersion($record, 1);
1093
        $record->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
1094
        $this->assertRecordHasLatestVersion($record, 1);
1095
        $record->Title = "Test C2";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...edTest\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...
1096
        $record->writeToStage("Stage");
1097
        $this->assertRecordHasLatestVersion($record, 2);
1098
    }
1099
1100
    public function testVersionedHandlesRenamedDataObjectFields()
1101
    {
1102
        Config::inst()->remove(VersionedTest\RelatedWithoutversion::class, 'db', 'Name', 'Varchar');
1103
1104
        Config::inst()->update(
1105
            VersionedTest\RelatedWithoutversion::class,
1106
            'db',
1107
            array(
1108
            "NewField" => "Varchar",
1109
            )
1110
        );
1111
1112
        VersionedTest\RelatedWithoutversion::add_extension(Versioned::class);
1113
        $this->resetDBSchema(true);
1114
        $testData = new VersionedTest\RelatedWithoutversion();
1115
        $testData->NewField = 'Test';
0 ignored issues
show
Documentation introduced by
The property NewField does not exist on object<SilverStripe\ORM\...\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...
1116
        $testData->write();
1117
    }
1118
1119
    public function testCanView()
1120
    {
1121
        $public1ID = $this->idFromFixture(VersionedTest\PublicStage::class, 'public1');
1122
        $public2ID = $this->idFromFixture(VersionedTest\PublicViaExtension::class, 'public2');
1123
        $privateID = $this->idFromFixture(VersionedTest\TestObject::class, 'page1');
1124
        $singleID = $this->idFromFixture(VersionedTest\SingleStage::class, 'single');
1125
1126
        // Test that all (and only) public pages are viewable in stage mode
1127
        Session::clear("loggedInAs");
1128
        Versioned::set_stage(Versioned::DRAFT);
1129
        $public1 = Versioned::get_one_by_stage(VersionedTest\PublicStage::class, 'Stage', array('"ID"' => $public1ID));
1130
        $public2 = Versioned::get_one_by_stage(VersionedTest\PublicViaExtension::class, 'Stage', array('"ID"' => $public2ID));
1131
        $private = Versioned::get_one_by_stage(VersionedTest\TestObject::class, 'Stage', array('"ID"' => $privateID));
1132
        // Also test an object that has just a single-stage (eg. is only versioned)
1133
        $single = Versioned::get_one_by_stage(VersionedTest\SingleStage::class, 'Stage', array('"ID"' => $singleID));
1134
1135
1136
        $this->assertTrue($public1->canView());
1137
        $this->assertTrue($public2->canView());
1138
        $this->assertFalse($private->canView());
1139
        $this->assertFalse($single->canView());
1140
1141
        // Adjusting the current stage should not allow objects loaded in stage to be viewable
1142
        Versioned::set_stage(Versioned::LIVE);
1143
        $this->assertTrue($public1->canView());
1144
        $this->assertTrue($public2->canView());
1145
        $this->assertFalse($private->canView());
1146
        $this->assertFalse($single->canView());
1147
1148
        // Writing the private page to live should be fine though
1149
        $private->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
1150
        $privateLive = Versioned::get_one_by_stage(VersionedTest\TestObject::class, 'Live', array('"ID"' => $privateID));
1151
        $this->assertTrue($private->canView());
1152
        $this->assertTrue($privateLive->canView());
1153
1154
        // But if the private version becomes different to the live version, it's once again disallowed
1155
        Versioned::set_stage(Versioned::DRAFT);
1156
        $private->Title = 'Secret Title';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
1157
        $private->write();
1158
        $this->assertFalse($private->canView());
1159
        $this->assertTrue($privateLive->canView());
1160
1161
        // And likewise, viewing a live page (when mode is draft) should be ok
1162
        Versioned::set_stage(Versioned::DRAFT);
1163
        $this->assertFalse($private->canView());
1164
        $this->assertTrue($privateLive->canView());
1165
1166
        // Logging in as admin should allow all permissions
1167
        $this->logInWithPermission('ADMIN');
1168
        Versioned::set_stage(Versioned::DRAFT);
1169
        $this->assertTrue($public1->canView());
1170
        $this->assertTrue($public2->canView());
1171
        $this->assertTrue($private->canView());
1172
        $this->assertTrue($single->canView());
1173
    }
1174
1175
    public function testCanViewStage()
1176
    {
1177
        $public = $this->objFromFixture(VersionedTest\PublicStage::class, 'public1');
1178
        $private = $this->objFromFixture(VersionedTest\TestObject::class, 'page1');
1179
        Session::clear("loggedInAs");
1180
        Versioned::set_stage(Versioned::DRAFT);
1181
1182
        // Test that all (and only) public pages are viewable in stage mode
1183
        // Unpublished records are not viewable in live regardless of permissions
1184
        $this->assertTrue($public->canViewStage('Stage'));
1185
        $this->assertFalse($private->canViewStage('Stage'));
1186
        $this->assertFalse($public->canViewStage('Live'));
1187
        $this->assertFalse($private->canViewStage('Live'));
1188
1189
        // Writing records to live should make both stage and live modes viewable
1190
        $private->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
1191
        $public->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
1192
        $this->assertTrue($public->canViewStage('Stage'));
1193
        $this->assertTrue($private->canViewStage('Stage'));
1194
        $this->assertTrue($public->canViewStage('Live'));
1195
        $this->assertTrue($private->canViewStage('Live'));
1196
1197
        // If the draft mode changes, the live mode remains public, although the updated
1198
        // draft mode is secured for non-public records.
1199
        $private->Title = 'Secret Title';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

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

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
1202
        $public->write();
1203
        $this->assertTrue($public->canViewStage('Stage'));
1204
        $this->assertFalse($private->canViewStage('Stage'));
1205
        $this->assertTrue($public->canViewStage('Live'));
1206
        $this->assertTrue($private->canViewStage('Live'));
1207
    }
1208
1209
    /**
1210
     * Values that are overwritten with null are saved to the _versions table correctly.
1211
     */
1212
    public function testWriteNullValueToVersion()
1213
    {
1214
        $record = VersionedTest\Subclass::create();
1215
        $record->Title = "Test A";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...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...
1216
        $record->write();
1217
1218
        $version = Versioned::get_latest_version($record->ClassName, $record->ID);
1219
1220
        $this->assertEquals(1, $version->Version);
1221
        $this->assertEquals($record->Title, $version->Title);
1222
1223
        $record->Title = null;
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\ORM\...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...
1224
        $record->write();
1225
1226
        $version = Versioned::get_latest_version($record->ClassName, $record->ID);
1227
1228
        $this->assertEquals(2, $version->Version);
1229
        $this->assertEquals($record->Title, $version->Title);
1230
    }
1231
1232
1233
1234
    public function testStageStates()
1235
    {
1236
        // newly created page
1237
        $createdPage = new VersionedTest\TestObject();
1238
        $createdPage->write();
1239
        $this->assertTrue($createdPage->isOnDraft());
1240
        $this->assertFalse($createdPage->isPublished());
1241
        $this->assertTrue($createdPage->isOnDraftOnly());
1242
        $this->assertTrue($createdPage->isModifiedOnDraft());
1243
1244
        // published page
1245
        $publishedPage = new VersionedTest\TestObject();
1246
        $publishedPage->write();
1247
        $publishedPage->copyVersionToStage('Stage', 'Live');
1248
        $this->assertTrue($publishedPage->isOnDraft());
1249
        $this->assertTrue($publishedPage->isPublished());
1250
        $this->assertFalse($publishedPage->isOnDraftOnly());
1251
        $this->assertFalse($publishedPage->isOnLiveOnly());
1252
        $this->assertFalse($publishedPage->isModifiedOnDraft());
1253
1254
        // published page, deleted from stage
1255
        $deletedFromDraftPage = new VersionedTest\TestObject();
1256
        $deletedFromDraftPage->write();
1257
        $deletedFromDraftPage->copyVersionToStage('Stage', 'Live');
1258
        $deletedFromDraftPage->deleteFromStage('Stage');
1259
        $this->assertFalse($deletedFromDraftPage->isArchived());
1260
        $this->assertFalse($deletedFromDraftPage->isOnDraft());
1261
        $this->assertTrue($deletedFromDraftPage->isPublished());
1262
        $this->assertFalse($deletedFromDraftPage->isOnDraftOnly());
1263
        $this->assertTrue($deletedFromDraftPage->isOnLiveOnly());
1264
        $this->assertFalse($deletedFromDraftPage->isModifiedOnDraft());
1265
1266
        // published page, deleted from live
1267
        $deletedFromLivePage = new VersionedTest\TestObject();
1268
        $deletedFromLivePage->write();
1269
        $deletedFromLivePage->copyVersionToStage('Stage', 'Live');
1270
        $deletedFromLivePage->deleteFromStage('Live');
1271
        $this->assertFalse($deletedFromLivePage->isArchived());
1272
        $this->assertTrue($deletedFromLivePage->isOnDraft());
1273
        $this->assertFalse($deletedFromLivePage->isPublished());
1274
        $this->assertTrue($deletedFromLivePage->isOnDraftOnly());
1275
        $this->assertFalse($deletedFromLivePage->isOnLiveOnly());
1276
        $this->assertTrue($deletedFromLivePage->isModifiedOnDraft());
1277
1278
        // published page, deleted from both stages
1279
        $deletedFromAllStagesPage = new VersionedTest\TestObject();
1280
        $deletedFromAllStagesPage->write();
1281
        $deletedFromAllStagesPage->copyVersionToStage('Stage', 'Live');
1282
        $deletedFromAllStagesPage->doArchive();
1283
        $this->assertTrue($deletedFromAllStagesPage->isArchived());
1284
        $this->assertFalse($deletedFromAllStagesPage->isOnDraft());
1285
        $this->assertFalse($deletedFromAllStagesPage->isPublished());
1286
        $this->assertFalse($deletedFromAllStagesPage->isOnDraftOnly());
1287
        $this->assertFalse($deletedFromAllStagesPage->isOnLiveOnly());
1288
        $this->assertFalse($deletedFromAllStagesPage->isModifiedOnDraft());
1289
1290
        // published page, modified
1291
        $modifiedOnDraftPage = new VersionedTest\TestObject();
1292
        $modifiedOnDraftPage->write();
1293
        $modifiedOnDraftPage->copyVersionToStage('Stage', 'Live');
1294
        $modifiedOnDraftPage->Content = 'modified';
0 ignored issues
show
Documentation introduced by
The property Content does not exist on object<SilverStripe\ORM\...rsionedTest\TestObject>. 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...
1295
        $modifiedOnDraftPage->write();
1296
        $this->assertFalse($modifiedOnDraftPage->isArchived());
1297
        $this->assertTrue($modifiedOnDraftPage->isOnDraft());
1298
        $this->assertTrue($modifiedOnDraftPage->isPublished());
1299
        $this->assertFalse($modifiedOnDraftPage->isOnDraftOnly());
1300
        $this->assertFalse($modifiedOnDraftPage->isOnLiveOnly());
1301
        $this->assertTrue($modifiedOnDraftPage->isModifiedOnDraft());
1302
    }
1303
}
1304