Completed
Pull Request — master (#1423)
by Ingo
02:19
created

SiteTreeFileExtension::updateLinks()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 21
rs 8.7624
cc 6
eloc 11
nc 9
nop 0
1
<?php
2
3
/**
4
 * Extension applied to {@see File} object to track links to {@see SiteTree} records.
5
 *
6
 * {@see SiteTreeLinkTracking} for the extension applied to {@see SiteTree}
7
 *
8
 * Note that since both SiteTree and File are versioned, LinkTracking and ImageTracking will
9
 * only be enabled for the Stage record.
10
 *
11
 * @property File $owner
12
 *
13
 * @package cms
14
 * @subpackage model
15
 */
16
class SiteTreeFileExtension extends DataExtension {
17
18
	private static $belongs_many_many = array(
19
		'BackLinkTracking' => 'SiteTree.ImageTracking' // {@see SiteTreeLinkTracking}
20
	);
21
22
	/**
23
	 * Images tracked by pages are owned by those pages
24
	 *
25
	 * @config
26
	 * @var array
27
	 */
28
	private static $owned_by = array(
29
		'BackLinkTracking'
30
	);
31
32
	public function updateCMSFields(FieldList $fields) {
33
		$fields->insertAfter(
34
			ReadonlyField::create(
35
				'BackLinkCount',
36
				_t('AssetTableField.BACKLINKCOUNT', 'Used on:'),
37
				$this->BackLinkTracking()->Count() . ' ' . _t('AssetTableField.PAGES', 'page(s)')
38
			)
39
				->addExtraClass('cms-description-toggle')
40
				->setDescription($this->BackLinkHTMLList()),
41
			'LastEdited'
0 ignored issues
show
Documentation introduced by
'LastEdited' is of type string, but the function expects a object<FormField>.

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...
42
		);
43
	}
44
45
	/**
46
	 * Generate an HTML list which provides links to where a file is used.
47
	 *
48
	 * @return string
49
	 */
50
	public function BackLinkHTMLList() {
51
		$html = '<em>' . _t(
52
			'SiteTreeFileExtension.BACKLINK_LIST_DESCRIPTION',
53
			'This list shows all pages where the file has been added through a WYSIWYG editor.'
54
		) . '</em>';
55
56
		$html .= '<ul>';
57
		foreach ($this->BackLinkTracking() as $backLink) {
58
			// Add the page link and CMS link
59
			$html .= sprintf(
60
				'<li><a href="%s" target="_blank">%s</a> &ndash; <a href="%s">%s</a></li>',
61
				Convert::raw2att($backLink->Link()),
62
				Convert::raw2xml($backLink->MenuTitle),
63
				Convert::raw2att($backLink->CMSEditLink()),
64
				_t('SiteTreeFileExtension.EDIT', 'Edit')
65
			);
66
		}
67
		$html .= '</ul>';
68
69
		return $html;
70
	}
71
72
	/**
73
	 * Extend through {@link updateBackLinkTracking()} in your own {@link Extension}.
74
	 *
75
	 * @return ManyManyList
76
	 */
77
	public function BackLinkTracking() {
78
		if(class_exists("Subsite")){
79
			$rememberSubsiteFilter = Subsite::$disable_subsite_filter;
80
			Subsite::disable_subsite_filter(true);
81
		}
82
83
		$links = $this->owner->getManyManyComponents('BackLinkTracking');
84
		$this->owner->extend('updateBackLinkTracking', $links);
85
86
		if(class_exists("Subsite")){
87
			Subsite::disable_subsite_filter($rememberSubsiteFilter);
0 ignored issues
show
Bug introduced by
The variable $rememberSubsiteFilter does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
88
		}
89
90
		return $links;
91
	}
92
93
	/**
94
	 * @todo Unnecessary shortcut for AssetTableField, coupled with cms module.
95
	 *
96
	 * @return integer
97
	 */
98
	public function BackLinkTrackingCount() {
99
		$pages = $this->owner->BackLinkTracking();
100
		if($pages) {
101
			return $pages->Count();
102
		} else {
103
			return 0;
104
		}
105
	}
106
107
	/**
108
	 * Updates link tracking in the current stage.
109
	 */
110
	public function onAfterDelete() {
111
		// Skip live stage
112
		if(\Versioned::current_stage() === \Versioned::get_live_stage()) {
113
			return;
114
		}
115
116
		// We query the explicit ID list, because BackLinkTracking will get modified after the stage
117
		// site does its thing
118
		$brokenPageIDs = $this->owner->BackLinkTracking()->column("ID");
119
		if($brokenPageIDs) {
120
			// This will syncLinkTracking on the same stage as this file
121
			$brokenPages = DataObject::get('SiteTree')->byIDs($brokenPageIDs);
122
			foreach($brokenPages as $brokenPage) {
123
				$brokenPage->write();
124
			}
125
		}
126
	}
127
}
128