Completed
Push — master ( ae9e05...431714 )
by Damian
11s
created

CMSPageHistoryController::ShowVersionForm()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
namespace SilverStripe\CMS\Controllers;
4
5
use SilverStripe\CMS\Model\SiteTree;
6
use SilverStripe\Control\Controller;
7
use SilverStripe\Control\HTTPRequest;
8
use SilverStripe\Control\HTTPResponse;
9
use SilverStripe\Forms\CheckboxField;
10
use SilverStripe\Forms\FieldList;
11
use SilverStripe\Forms\Form;
12
use SilverStripe\Forms\FormAction;
13
use SilverStripe\Forms\HiddenField;
14
use SilverStripe\Forms\LiteralField;
15
use SilverStripe\ORM\FieldType\DBField;
16
use SilverStripe\ORM\FieldType\DBHTMLText;
17
use SilverStripe\ORM\Versioning\Versioned;
18
use SilverStripe\Security\Security;
19
use SilverStripe\View\ViewableData;
20
21
class CMSPageHistoryController extends CMSMain {
22
23
	private static $url_segment = 'pages/history';
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...
24
25
	private static $url_rule = '/$Action/$ID/$VersionID/$OtherVersionID';
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...
26
27
	private static $url_priority = 42;
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...
28
29
	private static $menu_title = 'History';
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...
30
31
	private static $required_permission_codes = 'CMS_ACCESS_CMSMain';
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...
32
33
	private static $allowed_actions = 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...
34
		'VersionsForm',
35
		'CompareVersionsForm',
36
		'show',
37
		'compare'
38
	);
39
40
	private static $url_handlers = 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...
41
		'$Action/$ID/$VersionID/$OtherVersionID' => 'handleAction'
42
	);
43
44
	public function getResponseNegotiator() {
45
		$negotiator = parent::getResponseNegotiator();
46
		$controller = $this;
47
		$negotiator->setCallback('CurrentForm', function() use(&$controller) {
48
			$form = $controller->ShowVersionForm($controller->getRequest()->param('VersionID'));
49
			if($form) return $form->forTemplate();
50
			else return $controller->renderWith($controller->getTemplatesWithSuffix('_Content'));
51
		});
52
		$negotiator->setCallback('default', function() use(&$controller) {
53
			return $controller->renderWith($controller->getViewer('show'));
54
		});
55
		return $negotiator;
56
	}
57
58
	/**
59
	 * @param HTTPRequest $request
60
	 * @return array
61
	 */
62 View Code Duplication
	public function show($request) {
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...
63
		$form = $this->ShowVersionForm($request->param('VersionID'));
64
65
		$negotiator = $this->getResponseNegotiator();
66
		$controller = $this;
67
		$negotiator->setCallback('CurrentForm', function() use(&$controller, &$form) {
68
			return $form ? $form->forTemplate() : $controller->renderWith($controller->getTemplatesWithSuffix('_Content'));
69
		});
70
		$negotiator->setCallback('default', function() use(&$controller, &$form) {
71
			return $controller->customise(array('EditForm' => $form))->renderWith($controller->getViewer('show'));
72
		});
73
74
		return $negotiator->respond($request);
75
	}
76
77
	/**
78
	 * @param HTTPRequest $request
79
	 * @return array
80
	 */
81 View Code Duplication
	public function compare($request) {
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...
82
		$form = $this->CompareVersionsForm(
83
			$request->param('VersionID'),
84
			$request->param('OtherVersionID')
85
		);
86
87
		$negotiator = $this->getResponseNegotiator();
88
		$controller = $this;
89
		$negotiator->setCallback('CurrentForm', function() use(&$controller, &$form) {
90
			return $form ? $form->forTemplate() : $controller->renderWith($controller->getTemplatesWithSuffix('_Content'));
91
		});
92
		$negotiator->setCallback('default', function() use(&$controller, &$form) {
93
			return $controller->customise(array('EditForm' => $form))->renderWith($controller->getViewer('show'));
94
		});
95
96
		return $negotiator->respond($request);
97
	}
98
99
	public function getSilverStripeNavigator() {
100
		$record = $this->getRecord($this->currentPageID(), $this->getRequest()->param('VersionID'));
101
		if($record) {
102
			$navigator = new SilverStripeNavigator($record);
103
			return $navigator->renderWith($this->getTemplatesWithSuffix('_SilverStripeNavigator'));
104
		} else {
105
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type of the parent method SilverStripe\Admin\LeftA...etSilverStripeNavigator of type SilverStripe\ORM\FieldType\DBHTMLText.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
106
		}
107
	}
108
109
	/**
110
	 * Returns the read only version of the edit form. Detaches all {@link FormAction}
111
	 * instances attached since only action relates to revert.
112
	 *
113
	 * Permission checking is done at the {@link CMSMain::getEditForm()} level.
114
	 *
115
	 * @param int $id ID of the record to show
116
	 * @param array $fields optional
117
	 * @param int $versionID
118
	 * @param int $compareID Compare mode
119
	 *
120
	 * @return Form
121
	 */
122
	public function getEditForm($id = null, $fields = null, $versionID = null, $compareID = null) {
123
		if(!$id) $id = $this->currentPageID();
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
124
125
		$record = $this->getRecord($id, $versionID);
126
		$versionID = ($record) ? $record->Version : $versionID;
0 ignored issues
show
Bug introduced by
The property Version does not seem to exist. Did you mean versioning?

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...
127
128
		$form = parent::getEditForm($record, ($record) ? $record->getCMSFields() : null);
0 ignored issues
show
Documentation introduced by
$record is of type object<SilverStripe\CMS\Model\SiteTree>, but the function expects a integer|null.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
129
		// Respect permission failures from parent implementation
130
		if(!($form instanceof Form)) return $form;
131
132
		// TODO: move to the SilverStripeNavigator structure so the new preview can pick it up.
133
		//$nav = new SilverStripeNavigatorItem_ArchiveLink($record);
134
135
		$form->setActions(new FieldList(
136
			$revert = FormAction::create('doRollback', _t('CMSPageHistoryController.REVERTTOTHISVERSION', 'Revert to this version'))->setUseButtonTag(true)
137
		));
138
139
		$fields = $form->Fields();
140
		$fields->removeByName("Status");
141
		$fields->push(new HiddenField("ID"));
142
		$fields->push(new HiddenField("Version"));
143
144
		$fields = $fields->makeReadonly();
145
146
		if($compareID) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $compareID of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
147
			$link = Controller::join_links(
148
				$this->Link('show'),
149
				$id
150
			);
151
152
			$view = _t('CMSPageHistoryController.VIEW',"view");
153
154
			$message = _t(
155
				'CMSPageHistoryController.COMPARINGVERSION',
156
				"Comparing versions {version1} and {version2}.",
157
				array(
0 ignored issues
show
Documentation introduced by
array('version1' => spri...k, $compareID), $view)) is of type array<string,string,{"ve...","version2":"string"}>, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
158
					'version1' => sprintf('%s (<a href="%s">%s</a>)', $versionID, Controller::join_links($link, $versionID), $view),
159
					'version2' => sprintf('%s (<a href="%s">%s</a>)', $compareID, Controller::join_links($link, $compareID), $view)
160
				)
161
			);
162
163
			$revert->setReadonly(true);
164
		} else {
165
			if($record->isLatestVersion()) {
0 ignored issues
show
Documentation Bug introduced by
The method isLatestVersion does not exist on object<SilverStripe\CMS\Model\SiteTree>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
166
				$message = _t('CMSPageHistoryController.VIEWINGLATEST', 'Currently viewing the latest version.');
167
			} else {
168
				$message = _t(
169
					'CMSPageHistoryController.VIEWINGVERSION',
170
					"Currently viewing version {version}.",
171
					array('version' => $versionID)
0 ignored issues
show
Documentation introduced by
array('version' => $versionID) is of type array<string,?,{"version":"?"}>, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
172
				);
173
			}
174
		}
175
176
		$fields->addFieldToTab('Root.Main',
177
			new LiteralField('CurrentlyViewingMessage', $this->customise(array(
178
				'Content' => DBField::create_field('HTMLFragment', $message),
179
				'Classes' => 'notice'
180
			))->renderWith($this->getTemplatesWithSuffix('_notice'))),
181
			"Title"
182
		);
183
184
		$form->setFields($fields->makeReadonly());
185
		$form->loadDataFrom(array(
186
			"ID" => $id,
187
			"Version" => $versionID,
188
		));
189
190
		if(($record && $record->isLatestVersion())) {
0 ignored issues
show
Documentation Bug introduced by
The method isLatestVersion does not exist on object<SilverStripe\CMS\Model\SiteTree>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
191
			$revert->setReadonly(true);
192
		}
193
194
		$form->removeExtraClass('cms-content');
195
196
		return $form;
197
	}
198
199
200
	/**
201
	 * Version select form. Main interface between selecting versions to view
202
	 * and comparing multiple versions.
203
	 *
204
	 * Because we can reload the page directly to a compare view (history/compare/1/2/3)
205
	 * this form has to adapt to those parameters as well.
206
	 *
207
	 * @return Form
208
	 */
209
	public function VersionsForm() {
210
		$id = $this->currentPageID();
211
		$page = $this->getRecord($id);
212
		$versionsHtml = '';
213
214
		$action = $this->getRequest()->param('Action');
215
		$versionID = $this->getRequest()->param('VersionID');
216
		$otherVersionID = $this->getRequest()->param('OtherVersionID');
217
218
		$showUnpublishedChecked = 0;
219
		$compareModeChecked = ($action == "compare");
220
221
		if($page) {
222
			$versions = $page->allVersions();
0 ignored issues
show
Documentation Bug introduced by
The method allVersions does not exist on object<SilverStripe\CMS\Model\SiteTree>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
223
			$versionID = (!$versionID) ? $page->Version : $versionID;
0 ignored issues
show
Bug introduced by
The property Version does not seem to exist. Did you mean versioning?

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...
224
225
			if($versions) {
226
				foreach($versions as $k => $version) {
227
					$active = false;
228
229
					if($version->Version == $versionID || $version->Version == $otherVersionID) {
230
						$active = true;
231
232
						if(!$version->WasPublished) $showUnpublishedChecked = 1;
233
					}
234
235
					$version->Active = ($active);
236
				}
237
			}
238
239
			$vd = new ViewableData();
240
241
			$versionsHtml = $vd->customise(array(
242
				'Versions' => $versions
243
			))->renderWith($this->getTemplatesWithSuffix('_versions'));
244
		}
245
246
		$fields = new FieldList(
247
			new CheckboxField(
248
				'ShowUnpublished',
249
				_t('CMSPageHistoryController.SHOWUNPUBLISHED','Show unpublished versions'),
250
				$showUnpublishedChecked
251
			),
252
			new CheckboxField(
253
				'CompareMode',
254
				_t('CMSPageHistoryController.COMPAREMODE', 'Compare mode (select two)'),
255
				$compareModeChecked
256
			),
257
			new LiteralField('VersionsHtml', $versionsHtml),
0 ignored issues
show
Bug introduced by
It seems like $versionsHtml defined by $vd->customise(array('Ve...ithSuffix('_versions')) on line 241 can also be of type object<SilverStripe\ORM\FieldType\DBHTMLText>; however, SilverStripe\Forms\LiteralField::__construct() does only seem to accept string|object<SilverStripe\Forms\FormField>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
258
			$hiddenID = new HiddenField('ID', false, "")
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a null|string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
259
		);
260
261
		$actions = new FieldList(
262
			new FormAction(
263
				'doCompare', _t('CMSPageHistoryController.COMPAREVERSIONS','Compare Versions')
264
			),
265
			new FormAction(
266
				'doShowVersion', _t('CMSPageHistoryController.SHOWVERSION','Show Version')
267
			)
268
		);
269
270
		// Use <button> to allow full jQuery UI styling
271
		foreach($actions->dataFields() as $action) {
272
			/** @var FormAction $action */
273
			$action->setUseButtonTag(true);
274
		}
275
276
		$form = Form::create(
277
			$this,
278
			'VersionsForm',
279
			$fields,
280
			$actions
281
		)->setHTMLID('Form_VersionsForm');
282
		$form->loadDataFrom($this->getRequest()->requestVars());
283
		$hiddenID->setValue($id);
284
		$form->unsetValidator();
285
286
		$form
287
			->addExtraClass('cms-versions-form') // placeholder, necessary for $.metadata() to work
288
			->setAttribute('data-link-tmpl-compare', Controller::join_links($this->Link('compare'), '%s', '%s', '%s'))
289
			->setAttribute('data-link-tmpl-show', Controller::join_links($this->Link('show'), '%s', '%s'));
290
291
		return $form;
292
	}
293
294
	/**
295
	 * Process the {@link VersionsForm} compare function between two pages.
296
	 *
297
	 * @param array $data
298
	 * @param Form $form
299
	 * @return HTTPResponse|DBHTMLText
300
	 */
301
	public function doCompare($data, $form) {
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed.

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

Loading history...
302
		$versions = $data['Versions'];
303
		if(count($versions) < 2) {
304
			return null;
305
		}
306
307
		$version1 = array_shift($versions);
308
		$version2 = array_shift($versions);
309
310
		$form = $this->CompareVersionsForm($version1, $version2);
311
312
		// javascript solution, render into template
313 View Code Duplication
		if($this->getRequest()->isAjax()) {
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...
314
			return $this->customise(array(
315
				"EditForm" => $form
316
			))->renderWith(array(
317
				static::class . '_EditForm',
318
				'LeftAndMain_Content'
319
			));
320
		}
321
322
		// non javascript, redirect the user to the page
323
		return $this->redirect(Controller::join_links(
324
			$this->Link('compare'),
325
			$version1,
326
			$version2
327
		));
328
	}
329
330
	/**
331
	 * Process the {@link VersionsForm} show version function. Only requires
332
	 * one page to be selected.
333
	 *
334
	 * @param array
335
	 * @param Form
336
	 *
337
	 * @return DBHTMLText|HTTPResponse
338
	 */
339
	public function doShowVersion($data, $form) {
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed.

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

Loading history...
340
		$versionID = null;
341
342
		if(isset($data['Versions']) && is_array($data['Versions'])) {
343
			$versionID  = array_shift($data['Versions']);
344
		}
345
346
		if(!$versionID) {
347
			return null;
348
		}
349
350
		$request = $this->getRequest();
351 View Code Duplication
		if($request->isAjax()) {
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...
352
			return $this->customise(array(
353
				"EditForm" => $this->ShowVersionForm($versionID)
354
			))->renderWith(array(
355
				static::class . '_EditForm',
356
				'LeftAndMain_Content'
357
			));
358
		}
359
360
		// non javascript, redirect the user to the page
361
		return $this->redirect(Controller::join_links(
362
			$this->Link('version'),
363
			$versionID
364
		));
365
	}
366
367
	/**
368
	 * @param int|null $versionID
369
	 * @return Form
370
	 */
371
	public function ShowVersionForm($versionID = null) {
372
		if(!$versionID) return null;
0 ignored issues
show
Bug Best Practice introduced by
The expression $versionID of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
373
374
		$id = $this->currentPageID();
375
		$form = $this->getEditForm($id, null, $versionID);
376
377
		return $form;
378
	}
379
380
	/**
381
	 * @param int $versionID
382
	 * @param int $otherVersionID
383
	 * @return mixed
384
	 */
385
	public function CompareVersionsForm($versionID, $otherVersionID) {
386
		if($versionID > $otherVersionID) {
387
			$toVersion = $versionID;
388
			$fromVersion = $otherVersionID;
389
		} else {
390
			$toVersion = $otherVersionID;
391
			$fromVersion = $versionID;
392
		}
393
394
		if(!$toVersion || !$fromVersion) {
395
			return null;
396
		}
397
398
		$id = $this->currentPageID();
399
		/** @var SiteTree $page */
400
		$page = SiteTree::get()->byID($id);
401
402
		$record = null;
403
 		if($page && $page->exists()) {
404
			if(!$page->canView()) {
405
				return Security::permissionFailure($this);
406
			}
407
408
			$record = $page->compareVersions($fromVersion, $toVersion);
0 ignored issues
show
Documentation Bug introduced by
The method compareVersions does not exist on object<SilverStripe\CMS\Model\SiteTree>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
409
		}
410
411
		$fromVersionRecord = Versioned::get_version('SilverStripe\\CMS\\Model\\SiteTree', $id, $fromVersion);
412
		$toVersionRecord = Versioned::get_version('SilverStripe\\CMS\\Model\\SiteTree', $id, $toVersion);
413
414
		if(!$fromVersionRecord) {
415
			user_error("Can't find version $fromVersion of page $id", E_USER_ERROR);
416
		}
417
418
		if(!$toVersionRecord) {
419
			user_error("Can't find version $toVersion of page $id", E_USER_ERROR);
420
		}
421
422
		if(!$record) {
423
			return null;
424
		}
425
		$form = $this->getEditForm($id, null, null, true);
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a integer|null.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
426
		$form->setActions(new FieldList());
427
		$form->addExtraClass('compare');
428
429
		// Comparison views shouldn't be editable.
430
		// Its important to convert fields *before* loading data,
431
		// as the comparison output is HTML and not valid values for the various field types
432
		$readonlyFields = $form->Fields()->makeReadonly();
433
		$form->setFields($readonlyFields);
434
435
		$form->loadDataFrom($record);
436
		$form->loadDataFrom(array(
437
			"ID" => $id,
438
			"Version" => $fromVersion,
439
		));
440
441
		foreach($form->Fields()->dataFields() as $field) {
442
			$field->dontEscape = true;
443
		}
444
445
		return $form;
446
	}
447
448
	public function Breadcrumbs($unlinked = false) {
449
		$crumbs = parent::Breadcrumbs($unlinked);
450
		$crumbs[0]->Title = _t('CMSPagesController.MENUTITLE');
451
		return $crumbs;
452
	}
453
454
}
455