Completed
Push — master ( 1a8caa...dd71c8 )
by Ingo
02:45
created

SiteTreeFileExtension::BackLinkTrackingCount()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 8
rs 9.4285
cc 2
eloc 6
nc 2
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
	public function updateCMSFields(FieldList $fields) {
23
		$fields->insertAfter(
24
			ReadonlyField::create(
25
				'BackLinkCount', 
26
				_t('AssetTableField.BACKLINKCOUNT', 'Used on:'), 
27
				$this->BackLinkTracking()->Count() . ' ' . _t('AssetTableField.PAGES', 'page(s)')
28
			)
29
				->addExtraClass('cms-description-toggle')
30
				->setDescription($this->BackLinkHTMLList()),
31
			'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...
32
		);
33
	}
34
35
	/**
36
	 * Generate an HTML list which provides links to where a file is used.
37
	 *
38
	 * @return string
39
	 */
40
	public function BackLinkHTMLList() {
41
		$html = '<em>' . _t(
42
			'SiteTreeFileExtension.BACKLINK_LIST_DESCRIPTION',
43
			'This list shows all pages where the file has been added through a WYSIWYG editor.'
44
		) . '</em>';
45
46
		$html .= '<ul>';
47
		foreach ($this->BackLinkTracking() as $backLink) {
48
			// Add the page link and CMS link
49
			$html .= sprintf(
50
				'<li><a href="%s" target="_blank">%s</a> &ndash; <a href="%s">%s</a></li>',
51
				Convert::raw2att($backLink->Link()),
52
				Convert::raw2xml($backLink->MenuTitle),
53
				Convert::raw2att($backLink->CMSEditLink()),
54
				_t('SiteTreeFileExtension.EDIT', 'Edit')
55
			);
56
		}
57
		$html .= '</ul>';
58
59
		return $html;
60
	}
61
62
	/**
63
	 * Extend through {@link updateBackLinkTracking()} in your own {@link Extension}.
64
	 *
65
	 * @return ManyManyList
66
	 */
67
	public function BackLinkTracking() {
68
		if(class_exists("Subsite")){
69
			$rememberSubsiteFilter = Subsite::$disable_subsite_filter;
70
			Subsite::disable_subsite_filter(true);
71
		}
72
73
		$links = $this->owner->getManyManyComponents('BackLinkTracking');
74
		$this->owner->extend('updateBackLinkTracking', $links);
75
		
76
		if(class_exists("Subsite")){
77
			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...
78
		}
79
		
80
		return $links;
81
	}
82
	
83
	/**
84
	 * @todo Unnecessary shortcut for AssetTableField, coupled with cms module.
85
	 * 
86
	 * @return integer
87
	 */
88
	public function BackLinkTrackingCount() {
89
		$pages = $this->owner->BackLinkTracking();
90
		if($pages) {
91
			return $pages->Count();
92
		} else {
93
			return 0;
94
		}
95
	}
96
	
97
	/**
98
	 * Updates link tracking in the current stage.
99
	 */
100
	public function onAfterDelete() {
101
		// Skip live stage
102
		if(\Versioned::current_stage() === \Versioned::get_live_stage()) {
103
			return;
104
		}
105
106
		// We query the explicit ID list, because BackLinkTracking will get modified after the stage
107
		// site does its thing
108
		$brokenPageIDs = $this->owner->BackLinkTracking()->column("ID");
109
		if($brokenPageIDs) {
110
			// This will syncLinkTracking on the same stage as this file
111
			$brokenPages = DataObject::get('SiteTree')->byIDs($brokenPageIDs);
112
			foreach($brokenPages as $brokenPage) {
113
				$brokenPage->write();
114
			}
115
		}
116
	}
117
118
	public function onAfterWrite() {
119
		// Update any database references in the current stage
120
		$this->updateLinks();
121
	}
122
123
	public function onAfterVersionedPublish() {
124
		// Ensure that ->updateLinks is invoked on the draft record
125
		// after ->doPublish() is invoked.
126
		$this->updateLinks();
127
	}
128
	
129
	/**
130
	 * Rewrite links to the $old file to now point to the $new file.
131
	 * 
132
	 * @uses SiteTree->rewriteFileID()
133
	 */
134
	public function updateLinks() {
135
		// Skip live stage
136
		if(\Versioned::current_stage() === \Versioned::get_live_stage()) {
137
			return;
138
		}
139
140
		if(class_exists('Subsite')) {
141
			Subsite::disable_subsite_filter(true);
142
		}
143
	
144
		$pages = $this->owner->BackLinkTracking();
145
		if($pages) {
146
			foreach($pages as $page) {
147
				$page->rewriteFileLinks();
148
			}
149
		}
150
		
151
		if(class_exists('Subsite')) {
152
			Subsite::disable_subsite_filter(false);
153
		}
154
	}
155
	
156
}
157