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

ChangeSetItem::getObjectLatestVersion()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 7
Ratio 100 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 0
dl 7
loc 7
rs 9.4285
1
<?php
2
3
namespace SilverStripe\ORM\Versioning;
4
5
use SilverStripe\Admin\CMSPreviewable;
6
use SilverStripe\Assets\Thumbnail;
7
use SilverStripe\Control\Controller;
8
use SilverStripe\ORM\ArrayList;
9
use SilverStripe\ORM\DataList;
10
use SilverStripe\ORM\DataObject;
11
use SilverStripe\ORM\ManyManyList;
12
use SilverStripe\ORM\SS_List;
13
use SilverStripe\ORM\UnexpectedDataException;
14
use SilverStripe\Security\Member;
15
use SilverStripe\Security\Permission;
16
use BadMethodCallException;
17
use Exception;
18
19
/**
20
 * A single line in a changeset
21
 *
22
 * @property string $Added
23
 * @property string $ObjectClass The _base_ data class for the referenced DataObject
24
 * @property int $ObjectID The numeric ID for the referenced object
25
 * @method ManyManyList ReferencedBy() List of explicit items that require this change
26
 * @method ManyManyList References() List of implicit items required by this change
27
 * @method ChangeSet ChangeSet()
28
 */
29
class ChangeSetItem extends DataObject implements Thumbnail {
30
31
	const EXPLICITLY = 'explicitly';
32
33
	const IMPLICITLY = 'implicitly';
34
35
	/** Represents an object deleted */
36
	const CHANGE_DELETED = 'deleted';
37
38
	/** Represents an object which was modified */
39
	const CHANGE_MODIFIED = 'modified';
40
41
	/** Represents an object added */
42
	const CHANGE_CREATED = 'created';
43
44
	/** Represents an object which hasn't been changed directly, but owns a modified many_many relationship. */
45
	//const CHANGE_MANYMANY = 'manymany';
46
47
	private static $table_name = 'ChangeSetItem';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
48
49
	/**
50
	 * Represents that an object has not yet been changed, but
51
	 * should be included in this changeset as soon as any changes exist
52
	 */
53
	const CHANGE_NONE = 'none';
54
55
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
56
		'VersionBefore' => 'Int',
57
		'VersionAfter'  => 'Int',
58
		'Added'         => "Enum('explicitly, implicitly', 'implicitly')"
59
	);
60
61
	private static $has_one = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
62
		'ChangeSet' => 'SilverStripe\ORM\Versioning\ChangeSet',
63
		'Object'    => 'SilverStripe\ORM\DataObject',
64
	);
65
66
	private static $many_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
67
		'ReferencedBy' => 'SilverStripe\ORM\Versioning\ChangeSetItem'
68
	);
69
70
	private static $belongs_many_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
71
		'References' => 'ChangeSetItem.ReferencedBy'
72
	);
73
74
	private static $indexes = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
75
		'ObjectUniquePerChangeSet' => array(
76
			'type' => 'unique',
77
			'value' => '"ObjectID", "ObjectClass", "ChangeSetID"'
78
		)
79
	);
80
81
	public function onBeforeWrite() {
82
		// Make sure ObjectClass refers to the base data class in the case of old or wrong code
83
		$this->ObjectClass = $this->getSchema()->baseDataClass($this->ObjectClass);
84
		parent::onBeforeWrite();
85
	}
86
87
	public function getTitle() {
88
		// Get title of modified object
89
		$object = $this->getObjectLatestVersion();
90
		if($object) {
91
			return $object->getTitle();
92
		}
93
		return $this->i18n_singular_name() . ' #' . $this->ID;
94
	}
95
96
	/**
97
	 * Get a thumbnail for this object
98
	 *
99
	 * @param int $width Preferred width of the thumbnail
100
	 * @param int $height Preferred height of the thumbnail
101
	 * @return string URL to the thumbnail, if available
102
	 */
103
	public function ThumbnailURL($width, $height) {
104
		$object = $this->getObjectLatestVersion();
105
		if($object instanceof Thumbnail) {
106
			return $object->ThumbnailURL($width, $height);
107
		}
108
		return null;
109
	}
110
111
	/**
112
	 * Get the type of change: none, created, deleted, modified, manymany
113
	 * @return string
114
	 * @throws UnexpectedDataException
115
	 */
116
	public function getChangeType() {
117
		if(!class_exists($this->ObjectClass)) {
118
			throw new UnexpectedDataException("Invalid Class '{$this->ObjectClass}' in ChangeSetItem #{$this->ID}");
119
		}
120
121
		// Get change versions
122
		if($this->VersionBefore || $this->VersionAfter) {
123
			$draftVersion = $this->VersionAfter; // After publishing draft was written to stage
0 ignored issues
show
Documentation introduced by
The property VersionAfter does not exist on object<SilverStripe\ORM\Versioning\ChangeSetItem>. 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...
124
			$liveVersion = $this->VersionBefore; // The live version before the publish
0 ignored issues
show
Documentation introduced by
The property VersionBefore does not exist on object<SilverStripe\ORM\Versioning\ChangeSetItem>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
125
		} else {
126
			$draftVersion = Versioned::get_versionnumber_by_stage(
127
				$this->ObjectClass, Versioned::DRAFT, $this->ObjectID, false
128
			);
129
			$liveVersion = Versioned::get_versionnumber_by_stage(
130
				$this->ObjectClass, Versioned::LIVE, $this->ObjectID, false
131
			);
132
		}
133
134
		// Version comparisons
135
		if ($draftVersion == $liveVersion) {
136
			return self::CHANGE_NONE;
137
		} elseif (!$liveVersion) {
138
			return self::CHANGE_CREATED;
139
		} elseif (!$draftVersion) {
140
			return self::CHANGE_DELETED;
141
		} else {
142
			return self::CHANGE_MODIFIED;
143
		}
144
	}
145
146
	/**
147
	 * Find version of this object in the given stage
148
	 *
149
	 * @param string $stage
150
	 * @return DataObject|Versioned
151
	 * @throws UnexpectedDataException
152
	 */
153 View Code Duplication
	protected function getObjectInStage($stage) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
154
		if(!class_exists($this->ObjectClass)) {
155
			throw new UnexpectedDataException("Invalid Class '{$this->ObjectClass}' in ChangeSetItem #{$this->ID}");
156
		}
157
158
		return Versioned::get_by_stage($this->ObjectClass, $stage)->byID($this->ObjectID);
159
	}
160
161
	/**
162
	 * Find latest version of this object
163
	 * @return DataObject|Versioned
164
	 * @throws UnexpectedDataException
165
	 */
166 View Code Duplication
	protected function getObjectLatestVersion() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
167
		if(!class_exists($this->ObjectClass)) {
168
			throw new UnexpectedDataException("Invalid Class '{$this->ObjectClass}' in ChangeSetItem #{$this->ID}");
169
		}
170
171
		return Versioned::get_latest_version($this->ObjectClass, $this->ObjectID);
172
	}
173
174
	/**
175
	 * Get all implicit objects for this change
176
	 *
177
	 * @return SS_List
178
	 */
179
	public function findReferenced() {
180
		if($this->getChangeType() === ChangeSetItem::CHANGE_DELETED) {
181
			// If deleted from stage, need to look at live record
182
			$record = $this->getObjectInStage(Versioned::LIVE);
183
			if ($record) {
184
				return $record->findOwners(false);
185
			}
186
		} else {
187
			// If changed on stage, look at owned objects there
188
			$record = $this->getObjectInStage(Versioned::DRAFT);
189
			if ($record) {
190
				return $record->findOwned()->filterByCallback(function ($owned) {
191
					/** @var Versioned|DataObject $owned */
192
					return $owned->stagesDiffer(Versioned::DRAFT, Versioned::LIVE);
0 ignored issues
show
Bug introduced by
The method stagesDiffer does only exist in SilverStripe\ORM\Versioning\Versioned, but not in SilverStripe\ORM\DataObject.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
193
				});
194
			}
195
		}
196
		// Empty set
197
		return new ArrayList();
198
	}
199
200
	/**
201
	 * Publish this item, then close it.
202
	 *
203
	 * Note: Unlike Versioned::doPublish() and Versioned::doUnpublish, this action is not recursive.
204
	 */
205
	public function publish() {
206
		if(!class_exists($this->ObjectClass)) {
207
			throw new UnexpectedDataException("Invalid Class '{$this->ObjectClass}' in ChangeSetItem #{$this->ID}");
208
		}
209
210
		// Logical checks prior to publish
211
		if(!$this->canPublish()) {
212
			throw new Exception("The current member does not have permission to publish this ChangeSetItem.");
213
		}
214
		if($this->VersionBefore || $this->VersionAfter) {
215
			throw new BadMethodCallException("This ChangeSetItem has already been published");
216
		}
217
218
		// Record state changed
219
		$this->VersionAfter = Versioned::get_versionnumber_by_stage(
0 ignored issues
show
Documentation introduced by
The property VersionAfter does not exist on object<SilverStripe\ORM\Versioning\ChangeSetItem>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
220
			$this->ObjectClass, Versioned::DRAFT, $this->ObjectID, false
221
		);
222
		$this->VersionBefore = Versioned::get_versionnumber_by_stage(
0 ignored issues
show
Documentation introduced by
The property VersionBefore does not exist on object<SilverStripe\ORM\Versioning\ChangeSetItem>. 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...
223
			$this->ObjectClass, Versioned::LIVE, $this->ObjectID, false
224
		);
225
226
		switch($this->getChangeType()) {
227
			case static::CHANGE_NONE: {
228
				break;
229
			}
230
			case static::CHANGE_DELETED: {
231
				// Non-recursive delete
232
				$object = $this->getObjectInStage(Versioned::LIVE);
233
				$object->deleteFromStage(Versioned::LIVE);
234
				break;
235
			}
236
			case static::CHANGE_MODIFIED:
237
			case static::CHANGE_CREATED: {
238
				// Non-recursive publish
239
				$object = $this->getObjectInStage(Versioned::DRAFT);
240
				$object->publishSingle();
241
				break;
242
			}
243
		}
244
245
		$this->write();
246
	}
247
248
	/**
249
	 * Once this item (and all owned objects) are published, unlink
250
	 * all disowned objects
251
	 */
252
	public function unlinkDisownedObjects() {
253
		$object = $this->getObjectInStage(Versioned::DRAFT);
254
		if ($object) {
255
			$object->unlinkDisownedObjects(Versioned::DRAFT, Versioned::LIVE);
256
		}
257
	}
258
259
	/** Reverts this item, then close it. **/
260
	public function revert() {
261
		user_error('Not implemented', E_USER_ERROR);
262
	}
263
264
	public function canView($member = null) {
265
		return $this->can(__FUNCTION__, $member);
266
	}
267
268
	public function canEdit($member = null) {
269
		return $this->can(__FUNCTION__, $member);
270
	}
271
272
	public function canCreate($member = null, $context = array()) {
273
		return $this->can(__FUNCTION__, $member, $context);
274
	}
275
276
	public function canDelete($member = null) {
277
		return $this->can(__FUNCTION__, $member);
278
	}
279
280
	/**
281
	 * Check if the BeforeVersion of this changeset can be restored to draft
282
	 *
283
	 * @param Member $member
284
	 * @return bool
285
	 */
286
	public function canRevert($member) {
287
		// Just get the best version as this object may not even exist on either stage anymore.
288
		/** @var Versioned|DataObject $object */
289
		$object = $this->getObjectLatestVersion();
290
		if(!$object) {
291
			return false;
292
		}
293
294
		// Check change type
295
		switch($this->getChangeType()) {
296
			case static::CHANGE_CREATED: {
297
				// Revert creation by deleting from stage
298
				return $object->canDelete($member);
299
			}
300
			default: {
301
				// All other actions are typically editing draft stage
302
				return $object->canEdit($member);
303
			}
304
		}
305
	}
306
307
	/**
308
	 * Check if this ChangeSetItem can be published
309
	 *
310
	 * @param Member $member
311
	 * @return bool
312
	 */
313
	public function canPublish($member = null) {
314
		// Check canMethod to invoke on object
315
		switch($this->getChangeType()) {
316
			case static::CHANGE_DELETED: {
317
				/** @var Versioned|DataObject $object */
318
				$object = Versioned::get_by_stage($this->ObjectClass, Versioned::LIVE)->byID($this->ObjectID);
319
				if ($object) {
320
					return $object->canUnpublish($member);
0 ignored issues
show
Bug introduced by
It seems like $member defined by parameter $member on line 313 can also be of type object<SilverStripe\Security\Member>; however, SilverStripe\ORM\Version...rsioned::canUnpublish() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
The method canUnpublish does only exist in SilverStripe\ORM\Versioning\Versioned, but not in SilverStripe\ORM\DataObject.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
321
				}
322
				break;
323
			}
324
			default: {
325
				/** @var Versioned|DataObject $object */
326
				$object = Versioned::get_by_stage($this->ObjectClass, Versioned::DRAFT)->byID($this->ObjectID);
327
				if($object) {
328
					return $object->canPublish($member);
0 ignored issues
show
Bug introduced by
The method canPublish does only exist in SilverStripe\ORM\Versioning\Versioned, but not in SilverStripe\ORM\DataObject.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
329
				}
330
				break;
331
			}
332
		}
333
334
		return false;
335
	}
336
337
	/**
338
	 * Default permissions for this ChangeSetItem
339
	 *
340
	 * @param string $perm
341
	 * @param Member $member
342
	 * @param array $context
343
	 * @return bool
344
	 */
345 View Code Duplication
	public function can($perm, $member = null, $context = array()) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
346
		if(!$member) {
347
			$member = Member::currentUser();
348
		}
349
350
		// Allow extensions to bypass default permissions, but only if
351
		// each change can be individually published.
352
		$extended = $this->extendedCan($perm, $member, $context);
0 ignored issues
show
Bug introduced by
It seems like $member can be null; however, extendedCan() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
353
		if($extended !== null) {
354
			return $extended;
355
		}
356
357
		// Default permissions
358
		return (bool)Permission::checkMember($member, ChangeSet::config()->required_permission);
359
	}
360
361
	/**
362
	 * Get the ChangeSetItems that reference a passed DataObject
363
	 *
364
	 * @param DataObject $object
365
	 * @return DataList
366
	 */
367
	public static function get_for_object($object) {
368
		return ChangeSetItem::get()->filter([
369
			'ObjectID' => $object->ID,
370
			'ObjectClass' => $object->baseClass(),
371
		]);
372
	}
373
374
	/**
375
	 * Get the ChangeSetItems that reference a passed DataObject
376
	 *
377
	 * @param int $objectID The ID of the object
378
	 * @param string $objectClass The class of the object (or any parent class)
379
	 * @return DataList
380
	 */
381
	public static function get_for_object_by_id($objectID, $objectClass) {
382
		return ChangeSetItem::get()->filter([
383
			'ObjectID' => $objectID,
384
			'ObjectClass' => static::getSchema()->baseDataClass($objectClass)
385
		]);
386
	}
387
388
	/**
389
	 * Gets the list of modes this record can be previewed in.
390
	 *
391
	 * {@link https://tools.ietf.org/html/draft-kelly-json-hal-07#section-5}
392
	 *
393
	 * @return array Map of links in acceptable HAL format
394
	 */
395
	public function getPreviewLinks() {
396
		$links = [];
397
398
		// Preview draft
399
		$stage = $this->getObjectInStage(Versioned::DRAFT);
400 View Code Duplication
		if($stage instanceof CMSPreviewable && $stage->canView() && ($link = $stage->PreviewLink())) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
401
			$links[Versioned::DRAFT] = [
402
				'href' => Controller::join_links($link, '?stage=' . Versioned::DRAFT),
403
				'type' => $stage->getMimeType(),
404
			];
405
		}
406
407
		// Preview live
408
		$live = $this->getObjectInStage(Versioned::LIVE);
409 View Code Duplication
		if($live instanceof CMSPreviewable && $live->canView() && ($link = $live->PreviewLink())) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
410
			$links[Versioned::LIVE] = [
411
				'href' => Controller::join_links($link, '?stage=' . Versioned::LIVE),
412
				'type' => $live->getMimeType(),
413
			];
414
		}
415
416
		return $links;
417
	}
418
419
	/**
420
	 * Get edit link for this item
421
	 *
422
	 * @return string
423
	 */
424
	public function CMSEditLink()
425
	{
426
		$link = $this->getObjectInStage(Versioned::DRAFT);
427
		if($link instanceof CMSPreviewable) {
428
			return $link->CMSEditLink();
429
		}
430
		return null;
431
	}
432
}
433