Passed
Pull Request — master (#20)
by Robbie
02:31
created
src/Extension/CwpControllerExtension.php 2 patches
Indentation   +190 added lines, -190 removed lines patch added patch discarded remove patch
@@ -18,194 +18,194 @@
 block discarded – undo
18 18
 class CwpControllerExtension extends Extension implements PermissionProvider
19 19
 {
20 20
 
21
-    /**
22
-     * Enables SSL redirections - disabling not recommended as it will prevent forcing SSL on admin panel.
23
-     *
24
-     * @config
25
-     * @var bool
26
-     */
27
-    private static $ssl_redirection_enabled = true;
28
-
29
-    /**
30
-     * Specify a domain to redirect the vulnerable areas to.
31
-     *
32
-     * If left as null, live instance will set this to <instance-id>.cwp.govt.nz via CWP_SECURE_DOMAIN in _config.php.
33
-     * This allows us to automatically protect vulnerable areas on live even if the frontend cert is not installed.
34
-     *
35
-     * Set to false to redirect to https protocol on current domain (e.g. if you have frontend cert).
36
-     *
37
-     * Set to a domain string (e.g. 'example.com') to force that domain.
38
-     *
39
-     * @config
40
-     * @var string
41
-     */
42
-    private static $ssl_redirection_force_domain = null;
43
-
44
-    /**
45
-     * Enables the BasicAuth protection on all test environments. Disable with caution - it will open up
46
-     * all your UAT and test environments to the world.
47
-     *
48
-     * @config
49
-     * @var bool
50
-     */
51
-    private static $test_basicauth_enabled = true;
52
-
53
-    /**
54
-     * Enables the BasicAuth protection on all live environments.
55
-     * Useful for securing sites prior to public launch.
56
-     *
57
-     * @config
58
-     * @var bool
59
-     */
60
-    private static $live_basicauth_enabled = false;
61
-
62
-    /**
63
-     * This executes the passed callback with subsite filter disabled,
64
-     * then enabled the filter again before returning the callback result
65
-     * (or throwing the exception the callback raised)
66
-     *
67
-     * @param  callback  $callback - The callback to execute
68
-     * @return mixed     The result of the callback
69
-     * @throws Exception Any exception the callback raised
70
-     */
71
-    protected function callWithSubsitesDisabled($callback)
72
-    {
73
-        $rv = null;
74
-
75
-        try {
76
-            if (class_exists(Subsite::class)) {
77
-                Subsite::disable_subsite_filter(true);
78
-            }
79
-
80
-            $rv = call_user_func($callback);
81
-        } catch (Exception $e) {
82
-            if (class_exists(Subsite::class)) {
83
-                Subsite::disable_subsite_filter(false);
84
-            }
85
-
86
-            throw $e;
87
-        }
88
-
89
-        if (class_exists(Subsite::class)) {
90
-            Subsite::disable_subsite_filter(false);
91
-        }
92
-
93
-        return $rv;
94
-    }
95
-
96
-    /**
97
-     * Trigger Basic Auth protection, except when there's a reason to bypass it
98
-     *  - The source IP address is in the comma-seperated string in the constant CWP_IP_BYPASS_BASICAUTH
99
-     *    (so Pingdom, etc, can access the site)
100
-     *  - There is an identifiable member, that member has the ACCESS_UAT_SERVER permission, and they're trying
101
-     *    to access a white-list of URLs (so people following a reset password link can reset their password)
102
-     */
103
-    protected function triggerBasicAuthProtection()
104
-    {
105
-        $allowWithoutAuth = false;
106
-
107
-        // Allow whitelisting IPs for bypassing the basic auth.
108
-        if (Environment::getEnv('CWP_IP_BYPASS_BASICAUTH')) {
109
-            $remote = $_SERVER['REMOTE_ADDR'];
110
-            $bypass = explode(',', Environment::getEnv('CWP_IP_BYPASS_BASICAUTH'));
111
-
112
-            if (in_array($remote, $bypass)) {
113
-                $allowWithoutAuth = true;
114
-            }
115
-        }
116
-
117
-        // First, see if we can get a member to act on, either from a changepassword token or the session
118
-        if (isset($_REQUEST['m']) && isset($_REQUEST['t'])) {
119
-            $member = Member::get()->filter('ID', (int) $_REQUEST['m'])->first();
120
-
121
-            if (!$member->validateAutoLoginToken($_REQUEST['t'])) {
122
-                $member = null;
123
-            }
124
-        } elseif ($this->owner->getRequest()->getSession()->get('AutoLoginHash')) {
125
-            $member = Member::member_from_autologinhash(
126
-                $this->owner->getRequest()->getSession()->get('AutoLoginHash')
127
-            );
128
-        } else {
129
-            $member = Security::getCurrentUser();
130
-        }
131
-
132
-        // Then, if they have the right permissions, check the allowed URLs
133
-        $existingMemberCanAccessUAT = $member && $this->callWithSubsitesDisabled(function () use ($member) {
134
-            return Permission::checkMember($member, 'ACCESS_UAT_SERVER');
135
-        });
136
-
137
-        if ($existingMemberCanAccessUAT) {
138
-            $allowed = array(
139
-                '/^Security\/changepassword/',
140
-                '/^Security\/ChangePasswordForm/'
141
-            );
142
-
143
-            $relativeURL = Director::makeRelative(Director::absoluteURL($_SERVER['REQUEST_URI']));
144
-
145
-            foreach ($allowed as $pattern) {
146
-                $allowWithoutAuth = $allowWithoutAuth || preg_match($pattern, $relativeURL);
147
-            }
148
-        }
149
-
150
-        // Finally if they weren't allowed to bypass Basic Auth, trigger it
151
-        if (!$allowWithoutAuth) {
152
-            $this->callWithSubsitesDisabled(function () {
153
-                BasicAuth::requireLogin(
154
-                    $this->owner->getRequest(),
155
-                    _t(__CLASS__ . '.LoginPrompt', "Please log in with your CMS credentials"),
156
-                    'ACCESS_UAT_SERVER',
157
-                    true
158
-                );
159
-            });
160
-        }
161
-    }
162
-
163
-    /**
164
-     * @return void
165
-     */
166
-    public function onBeforeInit()
167
-    {
168
-        // Grab global injectable service to allow testing.
169
-        $director = Injector::inst()->get(Director::class);
170
-
171
-        if (Config::inst()->get(__CLASS__, 'ssl_redirection_enabled')) {
172
-            // redirect some vulnerable areas to the secure domain
173
-            if (!$director::is_https()) {
174
-                $forceDomain = Config::inst()->get(__CLASS__, 'ssl_redirection_force_domain');
175
-
176
-                if ($forceDomain) {
177
-                    $director::forceSSL(['/^Security/', '/^api/'], $forceDomain);
178
-                } else {
179
-                    $director::forceSSL(['/^Security/', '/^api/']);
180
-                }
181
-            }
182
-        }
183
-
184
-        if (Config::inst()->get(__CLASS__, 'test_basicauth_enabled')) {
185
-            // Turn on Basic Auth in testing mode
186
-            if ($director::isTest()) {
187
-                $this->triggerBasicAuthProtection();
188
-            }
189
-        }
190
-
191
-        if (Config::inst()->get(__CLASS__, 'live_basicauth_enabled')) {
192
-            // Turn on Basic Auth in live mode
193
-            if ($director::isLive()) {
194
-                $this->triggerBasicAuthProtection();
195
-            }
196
-        }
197
-    }
198
-
199
-    /**
200
-     * @return array
201
-     */
202
-    public function providePermissions()
203
-    {
204
-        return [
205
-            'ACCESS_UAT_SERVER' => _t(
206
-                __CLASS__ . '.UatServerPermission',
207
-                'Allow users to use their accounts to access the UAT server'
208
-            )
209
-        ];
210
-    }
21
+	/**
22
+	 * Enables SSL redirections - disabling not recommended as it will prevent forcing SSL on admin panel.
23
+	 *
24
+	 * @config
25
+	 * @var bool
26
+	 */
27
+	private static $ssl_redirection_enabled = true;
28
+
29
+	/**
30
+	 * Specify a domain to redirect the vulnerable areas to.
31
+	 *
32
+	 * If left as null, live instance will set this to <instance-id>.cwp.govt.nz via CWP_SECURE_DOMAIN in _config.php.
33
+	 * This allows us to automatically protect vulnerable areas on live even if the frontend cert is not installed.
34
+	 *
35
+	 * Set to false to redirect to https protocol on current domain (e.g. if you have frontend cert).
36
+	 *
37
+	 * Set to a domain string (e.g. 'example.com') to force that domain.
38
+	 *
39
+	 * @config
40
+	 * @var string
41
+	 */
42
+	private static $ssl_redirection_force_domain = null;
43
+
44
+	/**
45
+	 * Enables the BasicAuth protection on all test environments. Disable with caution - it will open up
46
+	 * all your UAT and test environments to the world.
47
+	 *
48
+	 * @config
49
+	 * @var bool
50
+	 */
51
+	private static $test_basicauth_enabled = true;
52
+
53
+	/**
54
+	 * Enables the BasicAuth protection on all live environments.
55
+	 * Useful for securing sites prior to public launch.
56
+	 *
57
+	 * @config
58
+	 * @var bool
59
+	 */
60
+	private static $live_basicauth_enabled = false;
61
+
62
+	/**
63
+	 * This executes the passed callback with subsite filter disabled,
64
+	 * then enabled the filter again before returning the callback result
65
+	 * (or throwing the exception the callback raised)
66
+	 *
67
+	 * @param  callback  $callback - The callback to execute
68
+	 * @return mixed     The result of the callback
69
+	 * @throws Exception Any exception the callback raised
70
+	 */
71
+	protected function callWithSubsitesDisabled($callback)
72
+	{
73
+		$rv = null;
74
+
75
+		try {
76
+			if (class_exists(Subsite::class)) {
77
+				Subsite::disable_subsite_filter(true);
78
+			}
79
+
80
+			$rv = call_user_func($callback);
81
+		} catch (Exception $e) {
82
+			if (class_exists(Subsite::class)) {
83
+				Subsite::disable_subsite_filter(false);
84
+			}
85
+
86
+			throw $e;
87
+		}
88
+
89
+		if (class_exists(Subsite::class)) {
90
+			Subsite::disable_subsite_filter(false);
91
+		}
92
+
93
+		return $rv;
94
+	}
95
+
96
+	/**
97
+	 * Trigger Basic Auth protection, except when there's a reason to bypass it
98
+	 *  - The source IP address is in the comma-seperated string in the constant CWP_IP_BYPASS_BASICAUTH
99
+	 *    (so Pingdom, etc, can access the site)
100
+	 *  - There is an identifiable member, that member has the ACCESS_UAT_SERVER permission, and they're trying
101
+	 *    to access a white-list of URLs (so people following a reset password link can reset their password)
102
+	 */
103
+	protected function triggerBasicAuthProtection()
104
+	{
105
+		$allowWithoutAuth = false;
106
+
107
+		// Allow whitelisting IPs for bypassing the basic auth.
108
+		if (Environment::getEnv('CWP_IP_BYPASS_BASICAUTH')) {
109
+			$remote = $_SERVER['REMOTE_ADDR'];
110
+			$bypass = explode(',', Environment::getEnv('CWP_IP_BYPASS_BASICAUTH'));
111
+
112
+			if (in_array($remote, $bypass)) {
113
+				$allowWithoutAuth = true;
114
+			}
115
+		}
116
+
117
+		// First, see if we can get a member to act on, either from a changepassword token or the session
118
+		if (isset($_REQUEST['m']) && isset($_REQUEST['t'])) {
119
+			$member = Member::get()->filter('ID', (int) $_REQUEST['m'])->first();
120
+
121
+			if (!$member->validateAutoLoginToken($_REQUEST['t'])) {
122
+				$member = null;
123
+			}
124
+		} elseif ($this->owner->getRequest()->getSession()->get('AutoLoginHash')) {
125
+			$member = Member::member_from_autologinhash(
126
+				$this->owner->getRequest()->getSession()->get('AutoLoginHash')
127
+			);
128
+		} else {
129
+			$member = Security::getCurrentUser();
130
+		}
131
+
132
+		// Then, if they have the right permissions, check the allowed URLs
133
+		$existingMemberCanAccessUAT = $member && $this->callWithSubsitesDisabled(function () use ($member) {
134
+			return Permission::checkMember($member, 'ACCESS_UAT_SERVER');
135
+		});
136
+
137
+		if ($existingMemberCanAccessUAT) {
138
+			$allowed = array(
139
+				'/^Security\/changepassword/',
140
+				'/^Security\/ChangePasswordForm/'
141
+			);
142
+
143
+			$relativeURL = Director::makeRelative(Director::absoluteURL($_SERVER['REQUEST_URI']));
144
+
145
+			foreach ($allowed as $pattern) {
146
+				$allowWithoutAuth = $allowWithoutAuth || preg_match($pattern, $relativeURL);
147
+			}
148
+		}
149
+
150
+		// Finally if they weren't allowed to bypass Basic Auth, trigger it
151
+		if (!$allowWithoutAuth) {
152
+			$this->callWithSubsitesDisabled(function () {
153
+				BasicAuth::requireLogin(
154
+					$this->owner->getRequest(),
155
+					_t(__CLASS__ . '.LoginPrompt', "Please log in with your CMS credentials"),
156
+					'ACCESS_UAT_SERVER',
157
+					true
158
+				);
159
+			});
160
+		}
161
+	}
162
+
163
+	/**
164
+	 * @return void
165
+	 */
166
+	public function onBeforeInit()
167
+	{
168
+		// Grab global injectable service to allow testing.
169
+		$director = Injector::inst()->get(Director::class);
170
+
171
+		if (Config::inst()->get(__CLASS__, 'ssl_redirection_enabled')) {
172
+			// redirect some vulnerable areas to the secure domain
173
+			if (!$director::is_https()) {
174
+				$forceDomain = Config::inst()->get(__CLASS__, 'ssl_redirection_force_domain');
175
+
176
+				if ($forceDomain) {
177
+					$director::forceSSL(['/^Security/', '/^api/'], $forceDomain);
178
+				} else {
179
+					$director::forceSSL(['/^Security/', '/^api/']);
180
+				}
181
+			}
182
+		}
183
+
184
+		if (Config::inst()->get(__CLASS__, 'test_basicauth_enabled')) {
185
+			// Turn on Basic Auth in testing mode
186
+			if ($director::isTest()) {
187
+				$this->triggerBasicAuthProtection();
188
+			}
189
+		}
190
+
191
+		if (Config::inst()->get(__CLASS__, 'live_basicauth_enabled')) {
192
+			// Turn on Basic Auth in live mode
193
+			if ($director::isLive()) {
194
+				$this->triggerBasicAuthProtection();
195
+			}
196
+		}
197
+	}
198
+
199
+	/**
200
+	 * @return array
201
+	 */
202
+	public function providePermissions()
203
+	{
204
+		return [
205
+			'ACCESS_UAT_SERVER' => _t(
206
+				__CLASS__ . '.UatServerPermission',
207
+				'Allow users to use their accounts to access the UAT server'
208
+			)
209
+		];
210
+	}
211 211
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -116,7 +116,7 @@  discard block
 block discarded – undo
116 116
 
117 117
         // First, see if we can get a member to act on, either from a changepassword token or the session
118 118
         if (isset($_REQUEST['m']) && isset($_REQUEST['t'])) {
119
-            $member = Member::get()->filter('ID', (int) $_REQUEST['m'])->first();
119
+            $member = Member::get()->filter('ID', (int)$_REQUEST['m'])->first();
120 120
 
121 121
             if (!$member->validateAutoLoginToken($_REQUEST['t'])) {
122 122
                 $member = null;
@@ -130,7 +130,7 @@  discard block
 block discarded – undo
130 130
         }
131 131
 
132 132
         // Then, if they have the right permissions, check the allowed URLs
133
-        $existingMemberCanAccessUAT = $member && $this->callWithSubsitesDisabled(function () use ($member) {
133
+        $existingMemberCanAccessUAT = $member && $this->callWithSubsitesDisabled(function() use ($member) {
134 134
             return Permission::checkMember($member, 'ACCESS_UAT_SERVER');
135 135
         });
136 136
 
@@ -149,7 +149,7 @@  discard block
 block discarded – undo
149 149
 
150 150
         // Finally if they weren't allowed to bypass Basic Auth, trigger it
151 151
         if (!$allowWithoutAuth) {
152
-            $this->callWithSubsitesDisabled(function () {
152
+            $this->callWithSubsitesDisabled(function() {
153 153
                 BasicAuth::requireLogin(
154 154
                     $this->owner->getRequest(),
155 155
                     _t(__CLASS__ . '.LoginPrompt', "Please log in with your CMS credentials"),
Please login to merge, or discard this patch.
src/Extension/CwpHtmlEditorConfig.php 1 patch
Indentation   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -7,20 +7,20 @@
 block discarded – undo
7 7
 class CwpHtmlEditorConfig extends DataExtension
8 8
 {
9 9
 
10
-    /**
11
-     * @return string
12
-     *
13
-     * Override the default HtmlEditorConfig from 'cms' to 'cwp' defined in cwp-core/_config.php
14
-     * However if the group has a custom editor configuration set, use that instead.
15
-     */
16
-    public function getHtmlEditorConfig()
17
-    {
18
-        $originalConfig = $this->owner->getField("HtmlEditorConfig");
10
+	/**
11
+	 * @return string
12
+	 *
13
+	 * Override the default HtmlEditorConfig from 'cms' to 'cwp' defined in cwp-core/_config.php
14
+	 * However if the group has a custom editor configuration set, use that instead.
15
+	 */
16
+	public function getHtmlEditorConfig()
17
+	{
18
+		$originalConfig = $this->owner->getField("HtmlEditorConfig");
19 19
 
20
-        if ($originalConfig) {
21
-            return $originalConfig;
22
-        }
20
+		if ($originalConfig) {
21
+			return $originalConfig;
22
+		}
23 23
 
24
-        return 'cwp';
25
-    }
24
+		return 'cwp';
25
+	}
26 26
 }
Please login to merge, or discard this patch.
src/Extension/LoginAttemptNotifications.php 2 patches
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -53,12 +53,12 @@  discard block
 block discarded – undo
53 53
                 $failures = $meantimeLoginAttempts->filter(['Status' => 'Failure'])->count();
54 54
                 $IPs = array_unique($meantimeLoginAttempts->column('IP'));
55 55
 
56
-                if ($attempts == 1) {
56
+                if ($attempts==1) {
57 57
                     $statusString = $failures ? "a failed" : "a successful";
58 58
                     $message = "In the last $elapsed $statusString login attempt to your account was "
59 59
                         . "registered. The attempt was made from ${IPs[0]}. ";
60 60
                 } else {
61
-                    if ($failures == $attempts) {
61
+                    if ($failures==$attempts) {
62 62
                         $statusString = $failures ? "failed" : "successful";
63 63
                         $message = "In the last $elapsed $attempts $statusString login "
64 64
                             . "attempts to your account were registered. ";
@@ -85,7 +85,7 @@  discard block
 block discarded – undo
85 85
                 $date = $lastLoginAttempt->Created;
86 86
                 $message = "Last login attempt to your account was on $lastLoginAttempt->Created "
87 87
                     . "from $lastLoginAttempt->IP";
88
-                $message .= $lastLoginAttempt->Status == 'Failure' ? " and was successful." : "and has failed.";
88
+                $message .= $lastLoginAttempt->Status=='Failure' ? " and was successful." : "and has failed.";
89 89
             }
90 90
         }
91 91
 
Please login to merge, or discard this patch.
Indentation   +66 added lines, -66 removed lines patch added patch discarded remove patch
@@ -19,81 +19,81 @@
 block discarded – undo
19 19
 class LoginAttemptNotifications extends Extension
20 20
 {
21 21
 
22
-    /**
23
-     *
24
-     * @return mixed null
25
-     */
26
-    public function init()
27
-    {
22
+	/**
23
+	 *
24
+	 * @return mixed null
25
+	 */
26
+	public function init()
27
+	{
28 28
 
29
-        // Exclude default admin.
30
-        $member = Security::getCurrentUser();
31
-        if (!$member || !$member->ID) {
32
-            return;
33
-        }
29
+		// Exclude default admin.
30
+		$member = Security::getCurrentUser();
31
+		if (!$member || !$member->ID) {
32
+			return;
33
+		}
34 34
 
35
-        $message = null;
36
-        $session = $this->owner->getRequest()->getSession();
35
+		$message = null;
36
+		$session = $this->owner->getRequest()->getSession();
37 37
 
38
-        Requirements::javascript('cwp/cwp-core:javascript/LoginAttemptNotifications.js');
39
-        $sessionLastVisited = $session->get('LoginAttemptNotifications.SessionLastVisited');
40
-        if ($sessionLastVisited) {
41
-            // Session already in progress. Show all attempts since the session was last visited.
38
+		Requirements::javascript('cwp/cwp-core:javascript/LoginAttemptNotifications.js');
39
+		$sessionLastVisited = $session->get('LoginAttemptNotifications.SessionLastVisited');
40
+		if ($sessionLastVisited) {
41
+			// Session already in progress. Show all attempts since the session was last visited.
42 42
 
43
-            $meantimeLoginAttempts = LoginAttempt::get()->filter([
44
-                'MemberID' => $member->ID,
45
-                'Created:GreaterThan' => $sessionLastVisited
46
-            ]);
43
+			$meantimeLoginAttempts = LoginAttempt::get()->filter([
44
+				'MemberID' => $member->ID,
45
+				'Created:GreaterThan' => $sessionLastVisited
46
+			]);
47 47
 
48
-            $attempts = $meantimeLoginAttempts->count();
49
-            if ($attempts) {
50
-                $lastVisitedObj = DBDatetime::create();
51
-                $lastVisitedObj->setValue($sessionLastVisited);
52
-                $elapsed = $lastVisitedObj->TimeDiff();
53
-                $failures = $meantimeLoginAttempts->filter(['Status' => 'Failure'])->count();
54
-                $IPs = array_unique($meantimeLoginAttempts->column('IP'));
48
+			$attempts = $meantimeLoginAttempts->count();
49
+			if ($attempts) {
50
+				$lastVisitedObj = DBDatetime::create();
51
+				$lastVisitedObj->setValue($sessionLastVisited);
52
+				$elapsed = $lastVisitedObj->TimeDiff();
53
+				$failures = $meantimeLoginAttempts->filter(['Status' => 'Failure'])->count();
54
+				$IPs = array_unique($meantimeLoginAttempts->column('IP'));
55 55
 
56
-                if ($attempts == 1) {
57
-                    $statusString = $failures ? "a failed" : "a successful";
58
-                    $message = "In the last $elapsed $statusString login attempt to your account was "
59
-                        . "registered. The attempt was made from ${IPs[0]}. ";
60
-                } else {
61
-                    if ($failures == $attempts) {
62
-                        $statusString = $failures ? "failed" : "successful";
63
-                        $message = "In the last $elapsed $attempts $statusString login "
64
-                            . "attempts to your account were registered. ";
65
-                    } else {
66
-                        $message = "In the last $elapsed $attempts login attempts to your "
67
-                            . "account were registered, of which $failures failed. ";
68
-                    }
56
+				if ($attempts == 1) {
57
+					$statusString = $failures ? "a failed" : "a successful";
58
+					$message = "In the last $elapsed $statusString login attempt to your account was "
59
+						. "registered. The attempt was made from ${IPs[0]}. ";
60
+				} else {
61
+					if ($failures == $attempts) {
62
+						$statusString = $failures ? "failed" : "successful";
63
+						$message = "In the last $elapsed $attempts $statusString login "
64
+							. "attempts to your account were registered. ";
65
+					} else {
66
+						$message = "In the last $elapsed $attempts login attempts to your "
67
+							. "account were registered, of which $failures failed. ";
68
+					}
69 69
 
70
-                    $message .= "The attempts were from " . implode(', ', $IPs) . '. ';
70
+					$message .= "The attempts were from " . implode(', ', $IPs) . '. ';
71 71
 
72
-                    // TODO: add this call to action in a way that doesn't break out of the availabel space. Fix CSS?
73
-                    // $message .= "If you suspect somebody else might be trying to access
74
-                    //  . "your account, please contact support.";
75
-                }
76
-            }
77
-        } else {
78
-            // New session - show last login attempt.
79
-            // TODO: this currently does NOT surface to the frontend in any way.
80
-            $lastLoginAttempt = LoginAttempt::get()->filter([
81
-                        'MemberID' => $member->ID
82
-            ])->sort('Created DESC')->First();
72
+					// TODO: add this call to action in a way that doesn't break out of the availabel space. Fix CSS?
73
+					// $message .= "If you suspect somebody else might be trying to access
74
+					//  . "your account, please contact support.";
75
+				}
76
+			}
77
+		} else {
78
+			// New session - show last login attempt.
79
+			// TODO: this currently does NOT surface to the frontend in any way.
80
+			$lastLoginAttempt = LoginAttempt::get()->filter([
81
+						'MemberID' => $member->ID
82
+			])->sort('Created DESC')->First();
83 83
 
84
-            if ($lastLoginAttempt) {
85
-                $date = $lastLoginAttempt->Created;
86
-                $message = "Last login attempt to your account was on $lastLoginAttempt->Created "
87
-                    . "from $lastLoginAttempt->IP";
88
-                $message .= $lastLoginAttempt->Status == 'Failure' ? " and was successful." : "and has failed.";
89
-            }
90
-        }
84
+			if ($lastLoginAttempt) {
85
+				$date = $lastLoginAttempt->Created;
86
+				$message = "Last login attempt to your account was on $lastLoginAttempt->Created "
87
+					. "from $lastLoginAttempt->IP";
88
+				$message .= $lastLoginAttempt->Status == 'Failure' ? " and was successful." : "and has failed.";
89
+			}
90
+		}
91 91
 
92
-        $session->set(
93
-            'LoginAttemptNotifications.SessionLastVisited',
94
-            DBDatetime::now()->Format(DBDatetime::ISO_DATETIME)
95
-        );
92
+		$session->set(
93
+			'LoginAttemptNotifications.SessionLastVisited',
94
+			DBDatetime::now()->Format(DBDatetime::ISO_DATETIME)
95
+		);
96 96
 
97
-        $this->owner->getResponse()->addHeader('X-LoginAttemptNotifications', $message);
98
-    }
97
+		$this->owner->getResponse()->addHeader('X-LoginAttemptNotifications', $message);
98
+	}
99 99
 }
Please login to merge, or discard this patch.
src/Report/CwpStatsReport.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -52,7 +52,7 @@
 block discarded – undo
52 52
         $records = [];
53 53
 
54 54
         // Get the query to apply across all variants: looks at all subsites, translations, live stage only.
55
-        $crossVariant = (function ($dataQuery) {
55
+        $crossVariant = (function($dataQuery) {
56 56
             $params = [
57 57
                 'Subsite.filter' => false,
58 58
                 'Versioned.mode' => 'stage',
Please login to merge, or discard this patch.
Indentation   +105 added lines, -105 removed lines patch added patch discarded remove patch
@@ -16,7 +16,7 @@  discard block
 block discarded – undo
16 16
 use SilverStripe\Versioned\Versioned;
17 17
 
18 18
 if (!class_exists(Report::class)) {
19
-    return;
19
+	return;
20 20
 }
21 21
 
22 22
 /**
@@ -24,108 +24,108 @@  discard block
 block discarded – undo
24 24
  */
25 25
 class CwpStatsReport extends Report
26 26
 {
27
-    public function title()
28
-    {
29
-        return _t(__CLASS__ . '.Title', 'Summary statistics');
30
-    }
31
-
32
-    public function description()
33
-    {
34
-        return _t(
35
-            __CLASS__ . '.Description',
36
-            'This report provides various statistics for this site. The "total live page count" is the number that ' .
37
-                'can be compared against the instance size specifications.'
38
-        );
39
-    }
40
-
41
-    public function columns()
42
-    {
43
-        return [
44
-            'Name' => _t(__CLASS__ . '.Name', 'Name'),
45
-            'Count' => _t(__CLASS__ . '.Count', 'Count'),
46
-        ];
47
-    }
48
-
49
-    /**
50
-     * Manually create source records for the report. Agreggates cannot be provided as a column of a DataQuery result.
51
-     *
52
-     * {@inheritDoc}
53
-     */
54
-    public function sourceRecords($params = [], $sort = null, $limit = null)
55
-    {
56
-        $records = [];
57
-
58
-        // Get the query to apply across all variants: looks at all subsites, translations, live stage only.
59
-        $crossVariant = (function ($dataQuery) {
60
-            $params = [
61
-                'Subsite.filter' => false,
62
-                'Versioned.mode' => 'stage',
63
-                'Versioned.stage' => Versioned::LIVE,
64
-            ];
65
-
66
-            return $dataQuery->setDataQueryParam($params);
67
-        });
68
-
69
-        // Total.
70
-        $records[] = [
71
-            'Name' => _t(
72
-                __CLASS__ . '.TotalPageCount',
73
-                'Total live page count, across all translations and subsites'
74
-            ),
75
-            'Count' => $crossVariant(SiteTree::get())->count(),
76
-        ];
77
-
78
-        if (class_exists(Subsite::class)) {
79
-            // Main site.
80
-            $records[] = [
81
-                'Name' => _t(__CLASS__ . '.PagesForMainSite', '- in the main site'),
82
-                'Count' => $crossVariant(SiteTree::get())
83
-                    ->filter(['SubsiteID' => 0])
84
-                    ->count(),
85
-            ];
86
-
87
-            // Per subsite.
88
-            $subsites = Subsite::get();
89
-            foreach ($subsites as $subsite) {
90
-                $records[] = [
91
-                    'Name' => _t(
92
-                        __CLASS__ . '.PagesForSubsite',
93
-                        "- in the subsite '{SubsiteTitle}'",
94
-                        ['SubsiteTitle' => $subsite->Title]
95
-                    ),
96
-                    'Count' => $crossVariant(SiteTree::get())
97
-                        ->filter(['SubsiteID' => $subsite->ID])
98
-                        ->count(),
99
-                ];
100
-            }
101
-        }
102
-
103
-        // Files.
104
-        $records[] = [
105
-            'Name' => _t(__CLASS__ . '.FileCount', 'File count'),
106
-            'Count' => File::get()
107
-                ->setDataQueryParam('Subsite.filter', false)
108
-                ->filter(['ClassName:not' => Folder::class])
109
-                ->count(),
110
-        ];
111
-
112
-        return ArrayList::create($records);
113
-    }
114
-
115
-    /**
116
-     * @return GridField
117
-     */
118
-    public function getReportField()
119
-    {
120
-        /** @var GridField $gridField */
121
-        $gridField = parent::getReportField();
122
-
123
-        /** @var GridFieldConfig $gridConfig */
124
-        $gridConfig = $gridField->getConfig();
125
-        $gridConfig->removeComponentsByType(GridFieldPrintButton::class);
126
-        $gridConfig->removeComponentsByType(GridFieldExportButton::class);
127
-        $gridConfig->removeComponentsByType(GridFieldSortableHeader::class);
128
-
129
-        return $gridField;
130
-    }
27
+	public function title()
28
+	{
29
+		return _t(__CLASS__ . '.Title', 'Summary statistics');
30
+	}
31
+
32
+	public function description()
33
+	{
34
+		return _t(
35
+			__CLASS__ . '.Description',
36
+			'This report provides various statistics for this site. The "total live page count" is the number that ' .
37
+				'can be compared against the instance size specifications.'
38
+		);
39
+	}
40
+
41
+	public function columns()
42
+	{
43
+		return [
44
+			'Name' => _t(__CLASS__ . '.Name', 'Name'),
45
+			'Count' => _t(__CLASS__ . '.Count', 'Count'),
46
+		];
47
+	}
48
+
49
+	/**
50
+	 * Manually create source records for the report. Agreggates cannot be provided as a column of a DataQuery result.
51
+	 *
52
+	 * {@inheritDoc}
53
+	 */
54
+	public function sourceRecords($params = [], $sort = null, $limit = null)
55
+	{
56
+		$records = [];
57
+
58
+		// Get the query to apply across all variants: looks at all subsites, translations, live stage only.
59
+		$crossVariant = (function ($dataQuery) {
60
+			$params = [
61
+				'Subsite.filter' => false,
62
+				'Versioned.mode' => 'stage',
63
+				'Versioned.stage' => Versioned::LIVE,
64
+			];
65
+
66
+			return $dataQuery->setDataQueryParam($params);
67
+		});
68
+
69
+		// Total.
70
+		$records[] = [
71
+			'Name' => _t(
72
+				__CLASS__ . '.TotalPageCount',
73
+				'Total live page count, across all translations and subsites'
74
+			),
75
+			'Count' => $crossVariant(SiteTree::get())->count(),
76
+		];
77
+
78
+		if (class_exists(Subsite::class)) {
79
+			// Main site.
80
+			$records[] = [
81
+				'Name' => _t(__CLASS__ . '.PagesForMainSite', '- in the main site'),
82
+				'Count' => $crossVariant(SiteTree::get())
83
+					->filter(['SubsiteID' => 0])
84
+					->count(),
85
+			];
86
+
87
+			// Per subsite.
88
+			$subsites = Subsite::get();
89
+			foreach ($subsites as $subsite) {
90
+				$records[] = [
91
+					'Name' => _t(
92
+						__CLASS__ . '.PagesForSubsite',
93
+						"- in the subsite '{SubsiteTitle}'",
94
+						['SubsiteTitle' => $subsite->Title]
95
+					),
96
+					'Count' => $crossVariant(SiteTree::get())
97
+						->filter(['SubsiteID' => $subsite->ID])
98
+						->count(),
99
+				];
100
+			}
101
+		}
102
+
103
+		// Files.
104
+		$records[] = [
105
+			'Name' => _t(__CLASS__ . '.FileCount', 'File count'),
106
+			'Count' => File::get()
107
+				->setDataQueryParam('Subsite.filter', false)
108
+				->filter(['ClassName:not' => Folder::class])
109
+				->count(),
110
+		];
111
+
112
+		return ArrayList::create($records);
113
+	}
114
+
115
+	/**
116
+	 * @return GridField
117
+	 */
118
+	public function getReportField()
119
+	{
120
+		/** @var GridField $gridField */
121
+		$gridField = parent::getReportField();
122
+
123
+		/** @var GridFieldConfig $gridConfig */
124
+		$gridConfig = $gridField->getConfig();
125
+		$gridConfig->removeComponentsByType(GridFieldPrintButton::class);
126
+		$gridConfig->removeComponentsByType(GridFieldExportButton::class);
127
+		$gridConfig->removeComponentsByType(GridFieldSortableHeader::class);
128
+
129
+		return $gridField;
130
+	}
131 131
 }
Please login to merge, or discard this patch.
src/Feed/CwpAtomFeed.php 1 patch
Indentation   +49 added lines, -49 removed lines patch added patch discarded remove patch
@@ -18,57 +18,57 @@
 block discarded – undo
18 18
 
19 19
 class CwpAtomFeed extends RSSFeed
20 20
 {
21
-    public function __construct(
22
-        SS_List $entries,
23
-        $link,
24
-        $title,
25
-        $description = null,
26
-        $titleField = "Title",
27
-        $descriptionField = "Content",
28
-        $authorField = null,
29
-        $lastModified = null,
30
-        $etag = null
31
-    ) {
32
-        parent::__construct(
33
-            $entries,
34
-            $link,
35
-            $title,
36
-            $description,
37
-            $titleField,
38
-            $descriptionField,
39
-            $authorField,
40
-            $lastModified
41
-        );
21
+	public function __construct(
22
+		SS_List $entries,
23
+		$link,
24
+		$title,
25
+		$description = null,
26
+		$titleField = "Title",
27
+		$descriptionField = "Content",
28
+		$authorField = null,
29
+		$lastModified = null,
30
+		$etag = null
31
+	) {
32
+		parent::__construct(
33
+			$entries,
34
+			$link,
35
+			$title,
36
+			$description,
37
+			$titleField,
38
+			$descriptionField,
39
+			$authorField,
40
+			$lastModified
41
+		);
42 42
 
43
-        $this->setTemplate(__CLASS__);
44
-    }
43
+		$this->setTemplate(__CLASS__);
44
+	}
45 45
 
46
-    /**
47
-     * Include an link to the feed
48
-     *
49
-     * @param string $url URL of the feed
50
-     * @param string $title Title to show
51
-     */
52
-    public static function linkToFeed($url, $title = null)
53
-    {
54
-        $title = Convert::raw2xml($title);
55
-        Requirements::insertHeadTags(
56
-            '<link rel="alternate" type="application/atom+xml" title="' . $title .
57
-            '" href="' . $url . '" />'
58
-        );
59
-    }
46
+	/**
47
+	 * Include an link to the feed
48
+	 *
49
+	 * @param string $url URL of the feed
50
+	 * @param string $title Title to show
51
+	 */
52
+	public static function linkToFeed($url, $title = null)
53
+	{
54
+		$title = Convert::raw2xml($title);
55
+		Requirements::insertHeadTags(
56
+			'<link rel="alternate" type="application/atom+xml" title="' . $title .
57
+			'" href="' . $url . '" />'
58
+		);
59
+	}
60 60
 
61
-    /**
62
-     * Output the feed to the browser
63
-     *
64
-     * @return DBHTMLText
65
-     */
66
-    public function outputToBrowser()
67
-    {
68
-        $output = parent::outputToBrowser();
69
-        $response = Controller::curr()->getResponse();
70
-        $response->addHeader("Content-Type", "application/atom+xml");
61
+	/**
62
+	 * Output the feed to the browser
63
+	 *
64
+	 * @return DBHTMLText
65
+	 */
66
+	public function outputToBrowser()
67
+	{
68
+		$output = parent::outputToBrowser();
69
+		$response = Controller::curr()->getResponse();
70
+		$response->addHeader("Content-Type", "application/atom+xml");
71 71
 
72
-        return $output;
73
-    }
72
+		return $output;
73
+	}
74 74
 }
Please login to merge, or discard this patch.
src/Extension/RichLinksExtension.php 1 patch
Indentation   +44 added lines, -44 removed lines patch added patch discarded remove patch
@@ -16,48 +16,48 @@
 block discarded – undo
16 16
 class RichLinksExtension extends Extension
17 17
 {
18 18
 
19
-    /**
20
-     * @var array
21
-     */
22
-    private static $casting = [
23
-        'RichLinks' => 'HTMLText'
24
-    ];
25
-
26
-    /**
27
-     * @return string
28
-     */
29
-    public function RichLinks()
30
-    {
31
-        // Note:
32
-        // Assume we can use Regexes because the link will always be formatted
33
-        // in the same way coming from the CMS.
34
-
35
-        $content = $this->owner->value;
36
-
37
-        // Find all file links for processing.
38
-        preg_match_all('/<a.*href="\[file_link,id=([0-9]+)\].*".*>.*<\/a>/U', $content, $matches);
39
-
40
-        // Attach the file type and size to each of the links.
41
-        for ($i = 0; $i < count($matches[0]); $i++) {
42
-            $file = DataObject::get_by_id('File', $matches[1][$i]);
43
-            if ($file) {
44
-                $size = $file->getSize();
45
-                $ext = strtoupper($file->getExtension());
46
-                // Replace the closing </a> tag with the size span (and reattach the closing tag).
47
-                $newLink = substr($matches[0][$i], 0, strlen($matches[0][$i]) - 4)
48
-                    . "<span class='fileExt'> [$ext, $size]</span></a>";
49
-                $content = str_replace($matches[0][$i], $newLink, $content);
50
-            }
51
-        }
52
-
53
-        // Inject extra attributes into the external links.
54
-        $pattern = '/(<a.*)(href=\"https?:\/\/[^\"]*\"[^>]*>.*)(<\/a>)/iU';
55
-        $replacement = sprintf(
56
-            '$1class="external" rel="external" title="%s" $2<span class="nonvisual-indicator">(external link)</span>$3',
57
-            _t(__CLASS__ . '.OpenLinkTitle', 'Open external link')
58
-        );
59
-        $content = preg_replace($pattern, $replacement, $content, -1);
60
-
61
-        return $content;
62
-    }
19
+	/**
20
+	 * @var array
21
+	 */
22
+	private static $casting = [
23
+		'RichLinks' => 'HTMLText'
24
+	];
25
+
26
+	/**
27
+	 * @return string
28
+	 */
29
+	public function RichLinks()
30
+	{
31
+		// Note:
32
+		// Assume we can use Regexes because the link will always be formatted
33
+		// in the same way coming from the CMS.
34
+
35
+		$content = $this->owner->value;
36
+
37
+		// Find all file links for processing.
38
+		preg_match_all('/<a.*href="\[file_link,id=([0-9]+)\].*".*>.*<\/a>/U', $content, $matches);
39
+
40
+		// Attach the file type and size to each of the links.
41
+		for ($i = 0; $i < count($matches[0]); $i++) {
42
+			$file = DataObject::get_by_id('File', $matches[1][$i]);
43
+			if ($file) {
44
+				$size = $file->getSize();
45
+				$ext = strtoupper($file->getExtension());
46
+				// Replace the closing </a> tag with the size span (and reattach the closing tag).
47
+				$newLink = substr($matches[0][$i], 0, strlen($matches[0][$i]) - 4)
48
+					. "<span class='fileExt'> [$ext, $size]</span></a>";
49
+				$content = str_replace($matches[0][$i], $newLink, $content);
50
+			}
51
+		}
52
+
53
+		// Inject extra attributes into the external links.
54
+		$pattern = '/(<a.*)(href=\"https?:\/\/[^\"]*\"[^>]*>.*)(<\/a>)/iU';
55
+		$replacement = sprintf(
56
+			'$1class="external" rel="external" title="%s" $2<span class="nonvisual-indicator">(external link)</span>$3',
57
+			_t(__CLASS__ . '.OpenLinkTitle', 'Open external link')
58
+		);
59
+		$content = preg_replace($pattern, $replacement, $content, -1);
60
+
61
+		return $content;
62
+	}
63 63
 }
Please login to merge, or discard this patch.
tests/CwpStatsReportTest.php 1 patch
Indentation   +43 added lines, -43 removed lines patch added patch discarded remove patch
@@ -9,47 +9,47 @@
 block discarded – undo
9 9
 
10 10
 class CwpStatsReportTest extends SapphireTest
11 11
 {
12
-    protected static $fixture_file = 'CwpStatsReportTest.yml';
13
-
14
-    protected function setUp()
15
-    {
16
-        if (!class_exists(CwpStatsReport::class)) {
17
-            // If CMS is not installed, do not run this test class
18
-            static::$fixture_file = null;
19
-            parent::setUp();
20
-            $this->markTestSkipped('This test class requires the CMS module to be installed.');
21
-        }
22
-
23
-        parent::setUp();
24
-    }
25
-
26
-    public function testCount()
27
-    {
28
-        // Publish all pages apart from page3.
29
-        $this->objFromFixture(Page::class, 'page1')->publishRecursive();
30
-        $this->objFromFixture(Page::class, 'page2')->publishRecursive();
31
-        $this->objFromFixture(Page::class, 'page3')->publishRecursive();
32
-
33
-        // Add page5s to a subsite, if the module is installed.
34
-        $page5s = $this->objFromFixture(Page::class, 'page5s');
35
-        if (class_exists(Subsite::class)) {
36
-            $subsite = Subsite::create();
37
-            $subsite->Title = 'subsite';
38
-            $subsite->write();
39
-
40
-            $page5s->SubsiteID = $subsite->ID;
41
-            $page5s->write();
42
-        }
43
-        $page5s->publishRecursive();
44
-
45
-        $report = CwpStatsReport::create();
46
-        $records = $report->sourceRecords([])->toArray();
47
-        $i = 0;
48
-        $this->assertEquals($records[$i++]['Count'], 4, 'Four pages in total, across locales, subsites, live only.');
49
-        if (class_exists(Subsite::class)) {
50
-            $this->assertEquals($records[$i++]['Count'], 3, 'Three pages in the main site, if subsites installed.');
51
-            $this->assertEquals($records[$i++]['Count'], 1, 'One page in the subsite, if subsites installed');
52
-        }
53
-        $this->assertEquals($records[$i++]['Count'], 1, 'One file in total.');
54
-    }
12
+	protected static $fixture_file = 'CwpStatsReportTest.yml';
13
+
14
+	protected function setUp()
15
+	{
16
+		if (!class_exists(CwpStatsReport::class)) {
17
+			// If CMS is not installed, do not run this test class
18
+			static::$fixture_file = null;
19
+			parent::setUp();
20
+			$this->markTestSkipped('This test class requires the CMS module to be installed.');
21
+		}
22
+
23
+		parent::setUp();
24
+	}
25
+
26
+	public function testCount()
27
+	{
28
+		// Publish all pages apart from page3.
29
+		$this->objFromFixture(Page::class, 'page1')->publishRecursive();
30
+		$this->objFromFixture(Page::class, 'page2')->publishRecursive();
31
+		$this->objFromFixture(Page::class, 'page3')->publishRecursive();
32
+
33
+		// Add page5s to a subsite, if the module is installed.
34
+		$page5s = $this->objFromFixture(Page::class, 'page5s');
35
+		if (class_exists(Subsite::class)) {
36
+			$subsite = Subsite::create();
37
+			$subsite->Title = 'subsite';
38
+			$subsite->write();
39
+
40
+			$page5s->SubsiteID = $subsite->ID;
41
+			$page5s->write();
42
+		}
43
+		$page5s->publishRecursive();
44
+
45
+		$report = CwpStatsReport::create();
46
+		$records = $report->sourceRecords([])->toArray();
47
+		$i = 0;
48
+		$this->assertEquals($records[$i++]['Count'], 4, 'Four pages in total, across locales, subsites, live only.');
49
+		if (class_exists(Subsite::class)) {
50
+			$this->assertEquals($records[$i++]['Count'], 3, 'Three pages in the main site, if subsites installed.');
51
+			$this->assertEquals($records[$i++]['Count'], 1, 'One page in the subsite, if subsites installed');
52
+		}
53
+		$this->assertEquals($records[$i++]['Count'], 1, 'One file in total.');
54
+	}
55 55
 }
Please login to merge, or discard this patch.