Failed Conditions
Pull Request — oauthcreation (#531)
by Simon
18:38 queued 08:37
created
includes/Fragments/RequestData.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -129,7 +129,7 @@
 block discarded – undo
129 129
     /**
130 130
      * Assigns a Smarty variable
131 131
      *
132
-     * @param  array|string $name  the template variable name(s)
132
+     * @param  string $name  the template variable name(s)
133 133
      * @param  mixed        $value the value to assign
134 134
      */
135 135
     abstract protected function assign($name, $value);
Please login to merge, or discard this patch.
Indentation   +320 added lines, -320 removed lines patch added patch discarded remove patch
@@ -24,324 +24,324 @@
 block discarded – undo
24 24
 
25 25
 trait RequestData
26 26
 {
27
-    /**
28
-     * @var array Array of IP address classed as 'private' by RFC1918.
29
-     */
30
-    protected static $rfc1918ips = array(
31
-        "10.0.0.0"    => "10.255.255.255",
32
-        "172.16.0.0"  => "172.31.255.255",
33
-        "192.168.0.0" => "192.168.255.255",
34
-        "169.254.0.0" => "169.254.255.255",
35
-        "127.0.0.0"   => "127.255.255.255",
36
-    );
37
-
38
-    /**
39
-     * Gets a request object
40
-     *
41
-     * @param PdoDatabase $database  The database connection
42
-     * @param int         $requestId The ID of the request to retrieve
43
-     *
44
-     * @return Request
45
-     * @throws ApplicationLogicException
46
-     */
47
-    protected function getRequest(PdoDatabase $database, $requestId)
48
-    {
49
-        if ($requestId === null) {
50
-            throw new ApplicationLogicException("No request specified");
51
-        }
52
-
53
-        $request = Request::getById($requestId, $database);
54
-        if ($request === false || !is_a($request, Request::class)) {
55
-            throw new ApplicationLogicException('Could not load the requested request!');
56
-        }
57
-
58
-        return $request;
59
-    }
60
-
61
-    /**
62
-     * Returns a value stating whether the user is allowed to see private data or not
63
-     *
64
-     * @param Request $request
65
-     * @param User    $currentUser
66
-     *
67
-     * @return bool
68
-     * @category Security-Critical
69
-     */
70
-    protected function isAllowedPrivateData(Request $request, User $currentUser)
71
-    {
72
-        // Test the main security barrier for private data access using SecurityManager
73
-        if ($this->barrierTest('alwaysSeePrivateData', $currentUser, 'RequestData')) {
74
-            // Tool admins/check-users can always see private data
75
-            return true;
76
-        }
77
-
78
-        // reserving user is allowed to see the data
79
-        if ($currentUser->getId() === $request->getReserved()
80
-            && $request->getReserved() !== null
81
-            && $this->barrierTest('seePrivateDataWhenReserved', $currentUser, 'RequestData')
82
-        ) {
83
-            return true;
84
-        }
85
-
86
-        // user has the reveal hash
87
-        if (WebRequest::getString('hash') === $request->getRevealHash()
88
-            && $this->barrierTest('seePrivateDataWithHash', $currentUser, 'RequestData')
89
-        ) {
90
-            return true;
91
-        }
92
-
93
-        // nope. Not allowed.
94
-        return false;
95
-    }
96
-
97
-    /**
98
-     * Tests the security barrier for a specified action.
99
-     *
100
-     * Don't use within templates
101
-     *
102
-     * @param string      $action
103
-     *
104
-     * @param User        $user
105
-     * @param null|string $pageName
106
-     *
107
-     * @return bool
108
-     * @category Security-Critical
109
-     */
110
-    abstract protected function barrierTest($action, User $user, $pageName = null);
111
-
112
-    /**
113
-     * Gets the name of the route that has been passed from the request router.
114
-     * @return string
115
-     */
116
-    abstract protected function getRouteName();
117
-
118
-    /** @return SecurityManager */
119
-    abstract protected function getSecurityManager();
120
-
121
-    /**
122
-     * Sets the name of the template this page should display.
123
-     *
124
-     * @param string $name
125
-     */
126
-    abstract protected function setTemplate($name);
127
-
128
-    /** @return IXffTrustProvider */
129
-    abstract protected function getXffTrustProvider();
130
-
131
-    /** @return ILocationProvider */
132
-    abstract protected function getLocationProvider();
133
-
134
-    /** @return IRDnsProvider */
135
-    abstract protected function getRdnsProvider();
136
-
137
-    /**
138
-     * Assigns a Smarty variable
139
-     *
140
-     * @param  array|string $name  the template variable name(s)
141
-     * @param  mixed        $value the value to assign
142
-     */
143
-    abstract protected function assign($name, $value);
144
-
145
-    /**
146
-     * @param int         $requestReservationId
147
-     * @param PdoDatabase $database
148
-     * @param User        $currentUser
149
-     */
150
-    protected function setupReservationDetails($requestReservationId, PdoDatabase $database, User $currentUser)
151
-    {
152
-        $requestIsReserved = $requestReservationId !== null;
153
-        $this->assign('requestIsReserved', $requestIsReserved);
154
-        $this->assign('requestIsReservedByMe', false);
155
-
156
-        if ($requestIsReserved) {
157
-            $this->assign('requestReservedByName', User::getById($requestReservationId, $database)->getUsername());
158
-            $this->assign('requestReservedById', $requestReservationId);
159
-
160
-            if ($requestReservationId === $currentUser->getId()) {
161
-                $this->assign('requestIsReservedByMe', true);
162
-            }
163
-        }
164
-
165
-        $this->assign('canBreakReservation', $this->barrierTest('force', $currentUser, PageBreakReservation::class));
166
-    }
167
-
168
-    /**
169
-     * Adds private request data to Smarty. DO NOT USE WITHOUT FIRST CHECKING THAT THE USER IS AUTHORISED!
170
-     *
171
-     * @param Request           $request
172
-     * @param User              $currentUser
173
-     * @param SiteConfiguration $configuration
174
-     *
175
-     * @param PdoDatabase       $database
176
-     */
177
-    protected function setupPrivateData(
178
-        $request,
179
-        User $currentUser,
180
-        SiteConfiguration $configuration,
181
-        PdoDatabase $database
182
-    ) {
183
-        $xffProvider = $this->getXffTrustProvider();
184
-
185
-        $relatedEmailRequests = RequestSearchHelper::get($database)
186
-            ->byEmailAddress($request->getEmail())
187
-            ->withConfirmedEmail()
188
-            ->excludingPurgedData($configuration)
189
-            ->excludingRequest($request->getId())
190
-            ->fetch();
191
-
192
-        $this->assign('requestEmail', $request->getEmail());
193
-        $emailDomain = explode("@", $request->getEmail())[1];
194
-        $this->assign("emailurl", $emailDomain);
195
-        $this->assign('requestRelatedEmailRequestsCount', count($relatedEmailRequests));
196
-        $this->assign('requestRelatedEmailRequests', $relatedEmailRequests);
197
-
198
-        $trustedIp = $xffProvider->getTrustedClientIp($request->getIp(), $request->getForwardedIp());
199
-        $this->assign('requestTrustedIp', $trustedIp);
200
-        $this->assign('requestRealIp', $request->getIp());
201
-        $this->assign('requestForwardedIp', $request->getForwardedIp());
202
-
203
-        $trustedIpLocation = $this->getLocationProvider()->getIpLocation($trustedIp);
204
-        $this->assign('requestTrustedIpLocation', $trustedIpLocation);
205
-
206
-        $this->assign('requestHasForwardedIp', $request->getForwardedIp() !== null);
207
-
208
-        $relatedIpRequests = RequestSearchHelper::get($database)
209
-            ->byIp($trustedIp)
210
-            ->withConfirmedEmail()
211
-            ->excludingPurgedData($configuration)
212
-            ->excludingRequest($request->getId())
213
-            ->fetch();
214
-
215
-        $this->assign('requestRelatedIpRequestsCount', count($relatedIpRequests));
216
-        $this->assign('requestRelatedIpRequests', $relatedIpRequests);
217
-
218
-        $this->assign('showRevealLink', false);
219
-        if ($request->getReserved() === $currentUser->getId() ||
220
-            $this->barrierTest('alwaysSeeHash', $currentUser, 'RequestData')
221
-        ) {
222
-            $this->assign('showRevealLink', true);
223
-            $this->assign('revealHash', $request->getRevealHash());
224
-        }
225
-
226
-        $this->setupForwardedIpData($request);
227
-    }
228
-
229
-    /**
230
-     * Adds checkuser request data to Smarty. DO NOT USE WITHOUT FIRST CHECKING THAT THE USER IS AUTHORISED!
231
-     *
232
-     * @param Request $request
233
-     */
234
-    protected function setupCheckUserData(Request $request)
235
-    {
236
-        $this->assign('requestUserAgent', $request->getUserAgent());
237
-    }
238
-
239
-    /**
240
-     * Sets up the basic data for this request, and adds it to Smarty
241
-     *
242
-     * @param Request           $request
243
-     * @param SiteConfiguration $config
244
-     */
245
-    protected function setupBasicData(Request $request, SiteConfiguration $config)
246
-    {
247
-        $this->assign('requestId', $request->getId());
248
-        $this->assign('updateVersion', $request->getUpdateVersion());
249
-        $this->assign('requestName', $request->getName());
250
-        $this->assign('requestDate', $request->getDate());
251
-        $this->assign('requestStatus', $request->getStatus());
252
-
253
-        $isClosed = !array_key_exists($request->getStatus(), $config->getRequestStates())
254
-            && $request->getStatus() !== RequestStatus::HOSPITAL;
255
-        $this->assign('requestIsClosed', $isClosed);
256
-    }
257
-
258
-    /**
259
-     * Sets up the forwarded IP data for this request and adds it to Smarty
260
-     *
261
-     * @param Request $request
262
-     */
263
-    protected function setupForwardedIpData(Request $request)
264
-    {
265
-        if ($request->getForwardedIp() !== null) {
266
-            $requestProxyData = array(); // Initialize array to store data to be output in Smarty template.
267
-            $proxyIndex = 0;
268
-
269
-            // Assuming [client] <=> [proxy1] <=> [proxy2] <=> [proxy3] <=> [us], we will see an XFF header of [client],
270
-            // [proxy1], [proxy2], and our actual IP will be [proxy3]
271
-            $proxies = explode(",", $request->getForwardedIp());
272
-            $proxies[] = $request->getIp();
273
-
274
-            // Origin is the supposed "client" IP.
275
-            $origin = $proxies[0];
276
-            $this->assign("forwardedOrigin", $origin);
277
-
278
-            // We step through the servers in reverse order, from closest to furthest
279
-            $proxies = array_reverse($proxies);
280
-
281
-            // By default, we have trust, because the first in the chain is now REMOTE_ADDR, which is hardest to spoof.
282
-            $trust = true;
283
-
284
-            /**
285
-             * @var int    $index     The zero-based index of the proxy.
286
-             * @var string $proxyData The proxy IP address (although possibly not!)
287
-             */
288
-            foreach ($proxies as $index => $proxyData) {
289
-                $proxyAddress = trim($proxyData);
290
-                $requestProxyData[$proxyIndex]['ip'] = $proxyAddress;
291
-
292
-                // get data on this IP.
293
-                $thisProxyIsTrusted = $this->getXffTrustProvider()->isTrusted($proxyAddress);
294
-
295
-                $proxyIsInPrivateRange = $this->getXffTrustProvider()
296
-                    ->ipInRange(self::$rfc1918ips, $proxyAddress);
297
-
298
-                if (!$proxyIsInPrivateRange) {
299
-                    $proxyReverseDns = $this->getRdnsProvider()->getReverseDNS($proxyAddress);
300
-                    $proxyLocation = $this->getLocationProvider()->getIpLocation($proxyAddress);
301
-                }
302
-                else {
303
-                    // this is going to fail, so why bother trying?
304
-                    $proxyReverseDns = false;
305
-                    $proxyLocation = false;
306
-                }
307
-
308
-                // current trust chain status BEFORE this link
309
-                $preLinkTrust = $trust;
310
-
311
-                // is *this* link trusted? Note, this will be true even if there is an untrusted link before this!
312
-                $requestProxyData[$proxyIndex]['trustedlink'] = $thisProxyIsTrusted;
313
-
314
-                // set the trust status of the chain to this point
315
-                $trust = $trust & $thisProxyIsTrusted;
316
-
317
-                // If this is the origin address, and the chain was trusted before this point, then we can trust
318
-                // the origin.
319
-                if ($preLinkTrust && $proxyAddress == $origin) {
320
-                    // if this is the origin, then we are at the last point in the chain.
321
-                    // @todo: this is probably the cause of some bugs when an IP appears twice - we're missing a check
322
-                    // to see if this is *really* the last in the chain, rather than just the same IP as it.
323
-                    $trust = true;
324
-                }
325
-
326
-                $requestProxyData[$proxyIndex]['trust'] = $trust;
327
-
328
-                $requestProxyData[$proxyIndex]['rdnsfailed'] = $proxyReverseDns === false;
329
-                $requestProxyData[$proxyIndex]['rdns'] = $proxyReverseDns;
330
-                $requestProxyData[$proxyIndex]['routable'] = !$proxyIsInPrivateRange;
331
-
332
-                $requestProxyData[$proxyIndex]['location'] = $proxyLocation;
333
-
334
-                if ($proxyReverseDns === $proxyAddress && $proxyIsInPrivateRange === false) {
335
-                    $requestProxyData[$proxyIndex]['rdns'] = null;
336
-                }
337
-
338
-                $showLinks = (!$trust || $proxyAddress == $origin) && !$proxyIsInPrivateRange;
339
-                $requestProxyData[$proxyIndex]['showlinks'] = $showLinks;
340
-
341
-                $proxyIndex++;
342
-            }
343
-
344
-            $this->assign("requestProxyData", $requestProxyData);
345
-        }
346
-    }
27
+	/**
28
+	 * @var array Array of IP address classed as 'private' by RFC1918.
29
+	 */
30
+	protected static $rfc1918ips = array(
31
+		"10.0.0.0"    => "10.255.255.255",
32
+		"172.16.0.0"  => "172.31.255.255",
33
+		"192.168.0.0" => "192.168.255.255",
34
+		"169.254.0.0" => "169.254.255.255",
35
+		"127.0.0.0"   => "127.255.255.255",
36
+	);
37
+
38
+	/**
39
+	 * Gets a request object
40
+	 *
41
+	 * @param PdoDatabase $database  The database connection
42
+	 * @param int         $requestId The ID of the request to retrieve
43
+	 *
44
+	 * @return Request
45
+	 * @throws ApplicationLogicException
46
+	 */
47
+	protected function getRequest(PdoDatabase $database, $requestId)
48
+	{
49
+		if ($requestId === null) {
50
+			throw new ApplicationLogicException("No request specified");
51
+		}
52
+
53
+		$request = Request::getById($requestId, $database);
54
+		if ($request === false || !is_a($request, Request::class)) {
55
+			throw new ApplicationLogicException('Could not load the requested request!');
56
+		}
57
+
58
+		return $request;
59
+	}
60
+
61
+	/**
62
+	 * Returns a value stating whether the user is allowed to see private data or not
63
+	 *
64
+	 * @param Request $request
65
+	 * @param User    $currentUser
66
+	 *
67
+	 * @return bool
68
+	 * @category Security-Critical
69
+	 */
70
+	protected function isAllowedPrivateData(Request $request, User $currentUser)
71
+	{
72
+		// Test the main security barrier for private data access using SecurityManager
73
+		if ($this->barrierTest('alwaysSeePrivateData', $currentUser, 'RequestData')) {
74
+			// Tool admins/check-users can always see private data
75
+			return true;
76
+		}
77
+
78
+		// reserving user is allowed to see the data
79
+		if ($currentUser->getId() === $request->getReserved()
80
+			&& $request->getReserved() !== null
81
+			&& $this->barrierTest('seePrivateDataWhenReserved', $currentUser, 'RequestData')
82
+		) {
83
+			return true;
84
+		}
85
+
86
+		// user has the reveal hash
87
+		if (WebRequest::getString('hash') === $request->getRevealHash()
88
+			&& $this->barrierTest('seePrivateDataWithHash', $currentUser, 'RequestData')
89
+		) {
90
+			return true;
91
+		}
92
+
93
+		// nope. Not allowed.
94
+		return false;
95
+	}
96
+
97
+	/**
98
+	 * Tests the security barrier for a specified action.
99
+	 *
100
+	 * Don't use within templates
101
+	 *
102
+	 * @param string      $action
103
+	 *
104
+	 * @param User        $user
105
+	 * @param null|string $pageName
106
+	 *
107
+	 * @return bool
108
+	 * @category Security-Critical
109
+	 */
110
+	abstract protected function barrierTest($action, User $user, $pageName = null);
111
+
112
+	/**
113
+	 * Gets the name of the route that has been passed from the request router.
114
+	 * @return string
115
+	 */
116
+	abstract protected function getRouteName();
117
+
118
+	/** @return SecurityManager */
119
+	abstract protected function getSecurityManager();
120
+
121
+	/**
122
+	 * Sets the name of the template this page should display.
123
+	 *
124
+	 * @param string $name
125
+	 */
126
+	abstract protected function setTemplate($name);
127
+
128
+	/** @return IXffTrustProvider */
129
+	abstract protected function getXffTrustProvider();
130
+
131
+	/** @return ILocationProvider */
132
+	abstract protected function getLocationProvider();
133
+
134
+	/** @return IRDnsProvider */
135
+	abstract protected function getRdnsProvider();
136
+
137
+	/**
138
+	 * Assigns a Smarty variable
139
+	 *
140
+	 * @param  array|string $name  the template variable name(s)
141
+	 * @param  mixed        $value the value to assign
142
+	 */
143
+	abstract protected function assign($name, $value);
144
+
145
+	/**
146
+	 * @param int         $requestReservationId
147
+	 * @param PdoDatabase $database
148
+	 * @param User        $currentUser
149
+	 */
150
+	protected function setupReservationDetails($requestReservationId, PdoDatabase $database, User $currentUser)
151
+	{
152
+		$requestIsReserved = $requestReservationId !== null;
153
+		$this->assign('requestIsReserved', $requestIsReserved);
154
+		$this->assign('requestIsReservedByMe', false);
155
+
156
+		if ($requestIsReserved) {
157
+			$this->assign('requestReservedByName', User::getById($requestReservationId, $database)->getUsername());
158
+			$this->assign('requestReservedById', $requestReservationId);
159
+
160
+			if ($requestReservationId === $currentUser->getId()) {
161
+				$this->assign('requestIsReservedByMe', true);
162
+			}
163
+		}
164
+
165
+		$this->assign('canBreakReservation', $this->barrierTest('force', $currentUser, PageBreakReservation::class));
166
+	}
167
+
168
+	/**
169
+	 * Adds private request data to Smarty. DO NOT USE WITHOUT FIRST CHECKING THAT THE USER IS AUTHORISED!
170
+	 *
171
+	 * @param Request           $request
172
+	 * @param User              $currentUser
173
+	 * @param SiteConfiguration $configuration
174
+	 *
175
+	 * @param PdoDatabase       $database
176
+	 */
177
+	protected function setupPrivateData(
178
+		$request,
179
+		User $currentUser,
180
+		SiteConfiguration $configuration,
181
+		PdoDatabase $database
182
+	) {
183
+		$xffProvider = $this->getXffTrustProvider();
184
+
185
+		$relatedEmailRequests = RequestSearchHelper::get($database)
186
+			->byEmailAddress($request->getEmail())
187
+			->withConfirmedEmail()
188
+			->excludingPurgedData($configuration)
189
+			->excludingRequest($request->getId())
190
+			->fetch();
191
+
192
+		$this->assign('requestEmail', $request->getEmail());
193
+		$emailDomain = explode("@", $request->getEmail())[1];
194
+		$this->assign("emailurl", $emailDomain);
195
+		$this->assign('requestRelatedEmailRequestsCount', count($relatedEmailRequests));
196
+		$this->assign('requestRelatedEmailRequests', $relatedEmailRequests);
197
+
198
+		$trustedIp = $xffProvider->getTrustedClientIp($request->getIp(), $request->getForwardedIp());
199
+		$this->assign('requestTrustedIp', $trustedIp);
200
+		$this->assign('requestRealIp', $request->getIp());
201
+		$this->assign('requestForwardedIp', $request->getForwardedIp());
202
+
203
+		$trustedIpLocation = $this->getLocationProvider()->getIpLocation($trustedIp);
204
+		$this->assign('requestTrustedIpLocation', $trustedIpLocation);
205
+
206
+		$this->assign('requestHasForwardedIp', $request->getForwardedIp() !== null);
207
+
208
+		$relatedIpRequests = RequestSearchHelper::get($database)
209
+			->byIp($trustedIp)
210
+			->withConfirmedEmail()
211
+			->excludingPurgedData($configuration)
212
+			->excludingRequest($request->getId())
213
+			->fetch();
214
+
215
+		$this->assign('requestRelatedIpRequestsCount', count($relatedIpRequests));
216
+		$this->assign('requestRelatedIpRequests', $relatedIpRequests);
217
+
218
+		$this->assign('showRevealLink', false);
219
+		if ($request->getReserved() === $currentUser->getId() ||
220
+			$this->barrierTest('alwaysSeeHash', $currentUser, 'RequestData')
221
+		) {
222
+			$this->assign('showRevealLink', true);
223
+			$this->assign('revealHash', $request->getRevealHash());
224
+		}
225
+
226
+		$this->setupForwardedIpData($request);
227
+	}
228
+
229
+	/**
230
+	 * Adds checkuser request data to Smarty. DO NOT USE WITHOUT FIRST CHECKING THAT THE USER IS AUTHORISED!
231
+	 *
232
+	 * @param Request $request
233
+	 */
234
+	protected function setupCheckUserData(Request $request)
235
+	{
236
+		$this->assign('requestUserAgent', $request->getUserAgent());
237
+	}
238
+
239
+	/**
240
+	 * Sets up the basic data for this request, and adds it to Smarty
241
+	 *
242
+	 * @param Request           $request
243
+	 * @param SiteConfiguration $config
244
+	 */
245
+	protected function setupBasicData(Request $request, SiteConfiguration $config)
246
+	{
247
+		$this->assign('requestId', $request->getId());
248
+		$this->assign('updateVersion', $request->getUpdateVersion());
249
+		$this->assign('requestName', $request->getName());
250
+		$this->assign('requestDate', $request->getDate());
251
+		$this->assign('requestStatus', $request->getStatus());
252
+
253
+		$isClosed = !array_key_exists($request->getStatus(), $config->getRequestStates())
254
+			&& $request->getStatus() !== RequestStatus::HOSPITAL;
255
+		$this->assign('requestIsClosed', $isClosed);
256
+	}
257
+
258
+	/**
259
+	 * Sets up the forwarded IP data for this request and adds it to Smarty
260
+	 *
261
+	 * @param Request $request
262
+	 */
263
+	protected function setupForwardedIpData(Request $request)
264
+	{
265
+		if ($request->getForwardedIp() !== null) {
266
+			$requestProxyData = array(); // Initialize array to store data to be output in Smarty template.
267
+			$proxyIndex = 0;
268
+
269
+			// Assuming [client] <=> [proxy1] <=> [proxy2] <=> [proxy3] <=> [us], we will see an XFF header of [client],
270
+			// [proxy1], [proxy2], and our actual IP will be [proxy3]
271
+			$proxies = explode(",", $request->getForwardedIp());
272
+			$proxies[] = $request->getIp();
273
+
274
+			// Origin is the supposed "client" IP.
275
+			$origin = $proxies[0];
276
+			$this->assign("forwardedOrigin", $origin);
277
+
278
+			// We step through the servers in reverse order, from closest to furthest
279
+			$proxies = array_reverse($proxies);
280
+
281
+			// By default, we have trust, because the first in the chain is now REMOTE_ADDR, which is hardest to spoof.
282
+			$trust = true;
283
+
284
+			/**
285
+			 * @var int    $index     The zero-based index of the proxy.
286
+			 * @var string $proxyData The proxy IP address (although possibly not!)
287
+			 */
288
+			foreach ($proxies as $index => $proxyData) {
289
+				$proxyAddress = trim($proxyData);
290
+				$requestProxyData[$proxyIndex]['ip'] = $proxyAddress;
291
+
292
+				// get data on this IP.
293
+				$thisProxyIsTrusted = $this->getXffTrustProvider()->isTrusted($proxyAddress);
294
+
295
+				$proxyIsInPrivateRange = $this->getXffTrustProvider()
296
+					->ipInRange(self::$rfc1918ips, $proxyAddress);
297
+
298
+				if (!$proxyIsInPrivateRange) {
299
+					$proxyReverseDns = $this->getRdnsProvider()->getReverseDNS($proxyAddress);
300
+					$proxyLocation = $this->getLocationProvider()->getIpLocation($proxyAddress);
301
+				}
302
+				else {
303
+					// this is going to fail, so why bother trying?
304
+					$proxyReverseDns = false;
305
+					$proxyLocation = false;
306
+				}
307
+
308
+				// current trust chain status BEFORE this link
309
+				$preLinkTrust = $trust;
310
+
311
+				// is *this* link trusted? Note, this will be true even if there is an untrusted link before this!
312
+				$requestProxyData[$proxyIndex]['trustedlink'] = $thisProxyIsTrusted;
313
+
314
+				// set the trust status of the chain to this point
315
+				$trust = $trust & $thisProxyIsTrusted;
316
+
317
+				// If this is the origin address, and the chain was trusted before this point, then we can trust
318
+				// the origin.
319
+				if ($preLinkTrust && $proxyAddress == $origin) {
320
+					// if this is the origin, then we are at the last point in the chain.
321
+					// @todo: this is probably the cause of some bugs when an IP appears twice - we're missing a check
322
+					// to see if this is *really* the last in the chain, rather than just the same IP as it.
323
+					$trust = true;
324
+				}
325
+
326
+				$requestProxyData[$proxyIndex]['trust'] = $trust;
327
+
328
+				$requestProxyData[$proxyIndex]['rdnsfailed'] = $proxyReverseDns === false;
329
+				$requestProxyData[$proxyIndex]['rdns'] = $proxyReverseDns;
330
+				$requestProxyData[$proxyIndex]['routable'] = !$proxyIsInPrivateRange;
331
+
332
+				$requestProxyData[$proxyIndex]['location'] = $proxyLocation;
333
+
334
+				if ($proxyReverseDns === $proxyAddress && $proxyIsInPrivateRange === false) {
335
+					$requestProxyData[$proxyIndex]['rdns'] = null;
336
+				}
337
+
338
+				$showLinks = (!$trust || $proxyAddress == $origin) && !$proxyIsInPrivateRange;
339
+				$requestProxyData[$proxyIndex]['showlinks'] = $showLinks;
340
+
341
+				$proxyIndex++;
342
+			}
343
+
344
+			$this->assign("requestProxyData", $requestProxyData);
345
+		}
346
+	}
347 347
 }
Please login to merge, or discard this patch.
includes/Router/PublicRequestRouter.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -47,7 +47,7 @@
 block discarded – undo
47 47
     /**
48 48
      * Gets the default route if no explicit route is requested.
49 49
      *
50
-     * @return callable
50
+     * @return string[]
51 51
      */
52 52
     protected function getDefaultRoute()
53 53
     {
Please login to merge, or discard this patch.
Indentation   +37 added lines, -37 removed lines patch added patch discarded remove patch
@@ -15,42 +15,42 @@
 block discarded – undo
15 15
 
16 16
 class PublicRequestRouter extends RequestRouter
17 17
 {
18
-    /**
19
-     * Gets the route map to be used by this request router.
20
-     *
21
-     * @return array
22
-     */
23
-    protected function getRouteMap()
24
-    {
25
-        return array(
26
-            // Page showing a message stating the request has been submitted to our internal queues
27
-            'requestSubmitted'          =>
28
-                array(
29
-                    'class'   => PageRequestSubmitted::class,
30
-                    'actions' => array(),
31
-                ),
32
-            // Page showing a message stating that email confirmation is required to continue
33
-            'emailConfirmationRequired' =>
34
-                array(
35
-                    'class'   => PageEmailConfirmationRequired::class,
36
-                    'actions' => array(),
37
-                ),
38
-            // Action page which handles email confirmation
39
-            'confirmEmail'              =>
40
-                array(
41
-                    'class'   => PageConfirmEmail::class,
42
-                    'actions' => array(),
43
-                ),
44
-        );
45
-    }
18
+	/**
19
+	 * Gets the route map to be used by this request router.
20
+	 *
21
+	 * @return array
22
+	 */
23
+	protected function getRouteMap()
24
+	{
25
+		return array(
26
+			// Page showing a message stating the request has been submitted to our internal queues
27
+			'requestSubmitted'          =>
28
+				array(
29
+					'class'   => PageRequestSubmitted::class,
30
+					'actions' => array(),
31
+				),
32
+			// Page showing a message stating that email confirmation is required to continue
33
+			'emailConfirmationRequired' =>
34
+				array(
35
+					'class'   => PageEmailConfirmationRequired::class,
36
+					'actions' => array(),
37
+				),
38
+			// Action page which handles email confirmation
39
+			'confirmEmail'              =>
40
+				array(
41
+					'class'   => PageConfirmEmail::class,
42
+					'actions' => array(),
43
+				),
44
+		);
45
+	}
46 46
 
47
-    /**
48
-     * Gets the default route if no explicit route is requested.
49
-     *
50
-     * @return callable
51
-     */
52
-    protected function getDefaultRoute()
53
-    {
54
-        return array(PageRequestAccount::class, 'main');
55
-    }
47
+	/**
48
+	 * Gets the default route if no explicit route is requested.
49
+	 *
50
+	 * @return callable
51
+	 */
52
+	protected function getDefaultRoute()
53
+	{
54
+		return array(PageRequestAccount::class, 'main');
55
+	}
56 56
 }
57 57
\ No newline at end of file
Please login to merge, or discard this patch.
includes/Router/RequestRouter.php 4 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -435,7 +435,7 @@
 block discarded – undo
435 435
     }
436 436
 
437 437
     /**
438
-     * @return callable
438
+     * @return string[]
439 439
      */
440 440
     protected function getDefaultRoute()
441 441
     {
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -358,7 +358,7 @@
 block discarded – undo
358 358
         $routeMap = $this->routePathSegments($classSegment, $requestedAction);
359 359
 
360 360
         if ($routeMap[0] === Page404::class) {
361
-            $routeMap = $this->routeSinglePathSegment($classSegment . '/' . $requestedAction);
361
+            $routeMap = $this->routeSinglePathSegment($classSegment.'/'.$requestedAction);
362 362
         }
363 363
 
364 364
         return $routeMap;
Please login to merge, or discard this patch.
Unused Use Statements   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -15,30 +15,20 @@  discard block
 block discarded – undo
15 15
 use Waca\Pages\PageEmailManagement;
16 16
 use Waca\Pages\PageExpandedRequestList;
17 17
 use Waca\Pages\PageJobQueue;
18
-use Waca\Pages\RequestAction\PageCreateRequest;
19
-use Waca\Pages\UserAuth\Login\PageOtpLogin;
20
-use Waca\Pages\UserAuth\Login\PagePasswordLogin;
21
-use Waca\Pages\UserAuth\Login\PageU2FLogin;
22
-use Waca\Pages\UserAuth\PageChangePassword;
23
-use Waca\Pages\UserAuth\PageForgotPassword;
24 18
 use Waca\Pages\PageLog;
25
-use Waca\Pages\UserAuth\PageLogout;
26 19
 use Waca\Pages\PageMain;
27
-use Waca\Pages\UserAuth\MultiFactor\PageMultiFactor;
28
-use Waca\Pages\UserAuth\PageOAuth;
29
-use Waca\Pages\UserAuth\PageOAuthCallback;
30
-use Waca\Pages\UserAuth\PagePreferences;
31
-use Waca\Pages\Registration\PageRegisterStandard;
32
-use Waca\Pages\Registration\PageRegisterOption;
33 20
 use Waca\Pages\PageSearch;
34 21
 use Waca\Pages\PageSiteNotice;
35 22
 use Waca\Pages\PageTeam;
36 23
 use Waca\Pages\PageUserManagement;
37 24
 use Waca\Pages\PageViewRequest;
38 25
 use Waca\Pages\PageWelcomeTemplateManagement;
26
+use Waca\Pages\Registration\PageRegisterOption;
27
+use Waca\Pages\Registration\PageRegisterStandard;
39 28
 use Waca\Pages\RequestAction\PageBreakReservation;
40 29
 use Waca\Pages\RequestAction\PageCloseRequest;
41 30
 use Waca\Pages\RequestAction\PageComment;
31
+use Waca\Pages\RequestAction\PageCreateRequest;
42 32
 use Waca\Pages\RequestAction\PageCustomClose;
43 33
 use Waca\Pages\RequestAction\PageDeferRequest;
44 34
 use Waca\Pages\RequestAction\PageDropRequest;
@@ -52,6 +42,16 @@  discard block
 block discarded – undo
52 42
 use Waca\Pages\Statistics\StatsTemplateStats;
53 43
 use Waca\Pages\Statistics\StatsTopCreators;
54 44
 use Waca\Pages\Statistics\StatsUsers;
45
+use Waca\Pages\UserAuth\Login\PageOtpLogin;
46
+use Waca\Pages\UserAuth\Login\PagePasswordLogin;
47
+use Waca\Pages\UserAuth\Login\PageU2FLogin;
48
+use Waca\Pages\UserAuth\MultiFactor\PageMultiFactor;
49
+use Waca\Pages\UserAuth\PageChangePassword;
50
+use Waca\Pages\UserAuth\PageForgotPassword;
51
+use Waca\Pages\UserAuth\PageLogout;
52
+use Waca\Pages\UserAuth\PageOAuth;
53
+use Waca\Pages\UserAuth\PageOAuthCallback;
54
+use Waca\Pages\UserAuth\PagePreferences;
55 55
 use Waca\Tasks\IRoutedTask;
56 56
 use Waca\WebRequest;
57 57
 
Please login to merge, or discard this patch.
Indentation   +434 added lines, -434 removed lines patch added patch discarded remove patch
@@ -62,438 +62,438 @@
 block discarded – undo
62 62
  */
63 63
 class RequestRouter implements IRequestRouter
64 64
 {
65
-    /**
66
-     * This is the core routing table for the application. The basic idea is:
67
-     *
68
-     *      array(
69
-     *          "foo" =>
70
-     *              array(
71
-     *                  "class"   => PageFoo::class,
72
-     *                  "actions" => array("bar", "other")
73
-     *              ),
74
-     * );
75
-     *
76
-     * Things to note:
77
-     *     - If no page is requested, we go to PageMain. PageMain can't have actions defined.
78
-     *
79
-     *     - If a page is defined and requested, but no action is requested, go to that page's main() method
80
-     *     - If a page is defined and requested, and an action is defined and requested, go to that action's method.
81
-     *     - If a page is defined and requested, and an action NOT defined and requested, go to Page404 and it's main()
82
-     *       method.
83
-     *     - If a page is NOT defined and requested, go to Page404 and it's main() method.
84
-     *
85
-     *     - Query parameters are ignored.
86
-     *
87
-     * The key point here is request routing with validation that this is allowed, before we start hitting the
88
-     * filesystem through the AutoLoader, and opening random files. Also, so that we validate the action requested
89
-     * before we start calling random methods through the web UI.
90
-     *
91
-     * Examples:
92
-     * /internal.php                => returns instance of PageMain, routed to main()
93
-     * /internal.php?query          => returns instance of PageMain, routed to main()
94
-     * /internal.php/foo            => returns instance of PageFoo, routed to main()
95
-     * /internal.php/foo?query      => returns instance of PageFoo, routed to main()
96
-     * /internal.php/foo/bar        => returns instance of PageFoo, routed to bar()
97
-     * /internal.php/foo/bar?query  => returns instance of PageFoo, routed to bar()
98
-     * /internal.php/foo/baz        => returns instance of Page404, routed to main()
99
-     * /internal.php/foo/baz?query  => returns instance of Page404, routed to main()
100
-     * /internal.php/bar            => returns instance of Page404, routed to main()
101
-     * /internal.php/bar?query      => returns instance of Page404, routed to main()
102
-     * /internal.php/bar/baz        => returns instance of Page404, routed to main()
103
-     * /internal.php/bar/baz?query  => returns instance of Page404, routed to main()
104
-     *
105
-     * Take care when changing this - a lot of places rely on the array key for redirects and other links. If you need
106
-     * to change the key, then you'll likely have to update a lot of files.
107
-     *
108
-     * @var array
109
-     */
110
-    private $routeMap = array(
111
-
112
-        //////////////////////////////////////////////////////////////////////////////////////////////////
113
-        // Login and registration
114
-        'logout'                      =>
115
-            array(
116
-                'class'   => PageLogout::class,
117
-                'actions' => array(),
118
-            ),
119
-        'login'                       =>
120
-            array(
121
-                'class'   => PagePasswordLogin::class,
122
-                'actions' => array(),
123
-            ),
124
-        'login/otp'                   =>
125
-            array(
126
-                'class'   => PageOtpLogin::class,
127
-                'actions' => array(),
128
-            ),
129
-        'login/u2f'                   =>
130
-            array(
131
-                'class'   => PageU2FLogin::class,
132
-                'actions' => array(),
133
-            ),
134
-        'forgotPassword'              =>
135
-            array(
136
-                'class'   => PageForgotPassword::class,
137
-                'actions' => array('reset'),
138
-            ),
139
-        'register'                    =>
140
-            array(
141
-                'class'   => PageRegisterOption::class,
142
-                'actions' => array(),
143
-            ),
144
-        'register/standard'           =>
145
-            array(
146
-                'class'   => PageRegisterStandard::class,
147
-                'actions' => array('done'),
148
-            ),
149
-
150
-        //////////////////////////////////////////////////////////////////////////////////////////////////
151
-        // Discovery
152
-        'search'                      =>
153
-            array(
154
-                'class'   => PageSearch::class,
155
-                'actions' => array(),
156
-            ),
157
-        'logs'                        =>
158
-            array(
159
-                'class'   => PageLog::class,
160
-                'actions' => array(),
161
-            ),
162
-
163
-        //////////////////////////////////////////////////////////////////////////////////////////////////
164
-        // Administration
165
-        'bans'                        =>
166
-            array(
167
-                'class'   => PageBan::class,
168
-                'actions' => array('set', 'remove'),
169
-            ),
170
-        'userManagement'              =>
171
-            array(
172
-                'class'   => PageUserManagement::class,
173
-                'actions' => array(
174
-                    'approve',
175
-                    'decline',
176
-                    'rename',
177
-                    'editUser',
178
-                    'suspend',
179
-                    'editRoles',
180
-                ),
181
-            ),
182
-        'siteNotice'                  =>
183
-            array(
184
-                'class'   => PageSiteNotice::class,
185
-                'actions' => array(),
186
-            ),
187
-        'emailManagement'             =>
188
-            array(
189
-                'class'   => PageEmailManagement::class,
190
-                'actions' => array('create', 'edit', 'view'),
191
-            ),
192
-        'jobQueue'                    =>
193
-            array(
194
-                'class'   => PageJobQueue::class,
195
-                'actions' => array('acknowledge', 'requeue', 'view', 'all'),
196
-            ),
197
-
198
-        //////////////////////////////////////////////////////////////////////////////////////////////////
199
-        // Personal preferences
200
-        'preferences'                 =>
201
-            array(
202
-                'class'   => PagePreferences::class,
203
-                'actions' => array(),
204
-            ),
205
-        'changePassword'              =>
206
-            array(
207
-                'class'   => PageChangePassword::class,
208
-                'actions' => array(),
209
-            ),
210
-        'multiFactor'                 =>
211
-            array(
212
-                'class'   => PageMultiFactor::class,
213
-                'actions' => array(
214
-                    'scratch',
215
-                    'enableYubikeyOtp',
216
-                    'disableYubikeyOtp',
217
-                    'enableTotp',
218
-                    'disableTotp',
219
-                    'enableU2F',
220
-                    'disableU2F',
221
-                ),
222
-            ),
223
-        'oauth'                       =>
224
-            array(
225
-                'class'   => PageOAuth::class,
226
-                'actions' => array('detach', 'attach'),
227
-            ),
228
-        'oauth/callback'              =>
229
-            array(
230
-                'class'   => PageOAuthCallback::class,
231
-                'actions' => array('authorise', 'create'),
232
-            ),
233
-
234
-        //////////////////////////////////////////////////////////////////////////////////////////////////
235
-        // Welcomer configuration
236
-        'welcomeTemplates'            =>
237
-            array(
238
-                'class'   => PageWelcomeTemplateManagement::class,
239
-                'actions' => array('select', 'edit', 'delete', 'add', 'view'),
240
-            ),
241
-
242
-        //////////////////////////////////////////////////////////////////////////////////////////////////
243
-        // Statistics
244
-        'statistics'                  =>
245
-            array(
246
-                'class'   => StatsMain::class,
247
-                'actions' => array(),
248
-            ),
249
-        'statistics/fastCloses'       =>
250
-            array(
251
-                'class'   => StatsFastCloses::class,
252
-                'actions' => array(),
253
-            ),
254
-        'statistics/inactiveUsers'    =>
255
-            array(
256
-                'class'   => StatsInactiveUsers::class,
257
-                'actions' => array(),
258
-            ),
259
-        'statistics/monthlyStats'     =>
260
-            array(
261
-                'class'   => StatsMonthlyStats::class,
262
-                'actions' => array(),
263
-            ),
264
-        'statistics/reservedRequests' =>
265
-            array(
266
-                'class'   => StatsReservedRequests::class,
267
-                'actions' => array(),
268
-            ),
269
-        'statistics/templateStats'    =>
270
-            array(
271
-                'class'   => StatsTemplateStats::class,
272
-                'actions' => array(),
273
-            ),
274
-        'statistics/topCreators'      =>
275
-            array(
276
-                'class'   => StatsTopCreators::class,
277
-                'actions' => array(),
278
-            ),
279
-        'statistics/users'            =>
280
-            array(
281
-                'class'   => StatsUsers::class,
282
-                'actions' => array('detail'),
283
-            ),
284
-
285
-        //////////////////////////////////////////////////////////////////////////////////////////////////
286
-        // Zoom page
287
-        'viewRequest'                 =>
288
-            array(
289
-                'class'   => PageViewRequest::class,
290
-                'actions' => array(),
291
-            ),
292
-        'viewRequest/reserve'         =>
293
-            array(
294
-                'class'   => PageReservation::class,
295
-                'actions' => array(),
296
-            ),
297
-        'viewRequest/breakReserve'    =>
298
-            array(
299
-                'class'   => PageBreakReservation::class,
300
-                'actions' => array(),
301
-            ),
302
-        'viewRequest/defer'           =>
303
-            array(
304
-                'class'   => PageDeferRequest::class,
305
-                'actions' => array(),
306
-            ),
307
-        'viewRequest/comment'         =>
308
-            array(
309
-                'class'   => PageComment::class,
310
-                'actions' => array(),
311
-            ),
312
-        'viewRequest/sendToUser'      =>
313
-            array(
314
-                'class'   => PageSendToUser::class,
315
-                'actions' => array(),
316
-            ),
317
-        'viewRequest/close'           =>
318
-            array(
319
-                'class'   => PageCloseRequest::class,
320
-                'actions' => array(),
321
-            ),
322
-        'viewRequest/create'          =>
323
-            array(
324
-                'class'   => PageCreateRequest::class,
325
-                'actions' => array(),
326
-            ),
327
-        'viewRequest/drop'            =>
328
-            array(
329
-                'class'   => PageDropRequest::class,
330
-                'actions' => array(),
331
-            ),
332
-        'viewRequest/custom'          =>
333
-            array(
334
-                'class'   => PageCustomClose::class,
335
-                'actions' => array(),
336
-            ),
337
-        'editComment'                 =>
338
-            array(
339
-                'class'   => PageEditComment::class,
340
-                'actions' => array(),
341
-            ),
342
-
343
-        //////////////////////////////////////////////////////////////////////////////////////////////////
344
-        // Misc stuff
345
-        'team'                        =>
346
-            array(
347
-                'class'   => PageTeam::class,
348
-                'actions' => array(),
349
-            ),
350
-        'requestList'                 =>
351
-            array(
352
-                'class'   => PageExpandedRequestList::class,
353
-                'actions' => array(),
354
-            ),
355
-    );
356
-
357
-    /**
358
-     * @return IRoutedTask
359
-     * @throws Exception
360
-     */
361
-    final public function route()
362
-    {
363
-        $pathInfo = WebRequest::pathInfo();
364
-
365
-        list($pageClass, $action) = $this->getRouteFromPath($pathInfo);
366
-
367
-        /** @var IRoutedTask $page */
368
-        $page = new $pageClass();
369
-
370
-        // Dynamic creation, so we've got to be careful here. We can't use built-in language type protection, so
371
-        // let's use our own.
372
-        if (!($page instanceof IRoutedTask)) {
373
-            throw new Exception('Expected a page, but this is not a page.');
374
-        }
375
-
376
-        // OK, I'm happy at this point that we know we're running a page, and we know it's probably what we want if it
377
-        // inherits PageBase and has been created from the routing map.
378
-        $page->setRoute($action);
379
-
380
-        return $page;
381
-    }
382
-
383
-    /**
384
-     * @param $pathInfo
385
-     *
386
-     * @return array
387
-     */
388
-    protected function getRouteFromPath($pathInfo)
389
-    {
390
-        if (count($pathInfo) === 0) {
391
-            // No pathInfo, so no page to load. Load the main page.
392
-            return $this->getDefaultRoute();
393
-        }
394
-        elseif (count($pathInfo) === 1) {
395
-            // Exactly one path info segment, it's got to be a page.
396
-            $classSegment = $pathInfo[0];
397
-
398
-            return $this->routeSinglePathSegment($classSegment);
399
-        }
400
-
401
-        // OK, we have two or more segments now.
402
-        if (count($pathInfo) > 2) {
403
-            // Let's handle more than two, and collapse it down into two.
404
-            $requestedAction = array_pop($pathInfo);
405
-            $classSegment = implode('/', $pathInfo);
406
-        }
407
-        else {
408
-            // Two path info segments.
409
-            $classSegment = $pathInfo[0];
410
-            $requestedAction = $pathInfo[1];
411
-        }
412
-
413
-        $routeMap = $this->routePathSegments($classSegment, $requestedAction);
414
-
415
-        if ($routeMap[0] === Page404::class) {
416
-            $routeMap = $this->routeSinglePathSegment($classSegment . '/' . $requestedAction);
417
-        }
418
-
419
-        return $routeMap;
420
-    }
421
-
422
-    /**
423
-     * @param $classSegment
424
-     *
425
-     * @return array
426
-     */
427
-    final protected function routeSinglePathSegment($classSegment)
428
-    {
429
-        $routeMap = $this->getRouteMap();
430
-        if (array_key_exists($classSegment, $routeMap)) {
431
-            // Route exists, but we don't have an action in path info, so default to main.
432
-            $pageClass = $routeMap[$classSegment]['class'];
433
-            $action = 'main';
434
-
435
-            return array($pageClass, $action);
436
-        }
437
-        else {
438
-            // Doesn't exist in map. Fall back to 404
439
-            $pageClass = Page404::class;
440
-            $action = "main";
441
-
442
-            return array($pageClass, $action);
443
-        }
444
-    }
445
-
446
-    /**
447
-     * @param $classSegment
448
-     * @param $requestedAction
449
-     *
450
-     * @return array
451
-     */
452
-    final protected function routePathSegments($classSegment, $requestedAction)
453
-    {
454
-        $routeMap = $this->getRouteMap();
455
-        if (array_key_exists($classSegment, $routeMap)) {
456
-            // Route exists, but we don't have an action in path info, so default to main.
457
-
458
-            if (isset($routeMap[$classSegment]['actions'])
459
-                && array_search($requestedAction, $routeMap[$classSegment]['actions']) !== false
460
-            ) {
461
-                // Action exists in allowed action list. Allow both the page and the action
462
-                $pageClass = $routeMap[$classSegment]['class'];
463
-                $action = $requestedAction;
464
-
465
-                return array($pageClass, $action);
466
-            }
467
-            else {
468
-                // Valid page, invalid action. 404 our way out.
469
-                $pageClass = Page404::class;
470
-                $action = 'main';
471
-
472
-                return array($pageClass, $action);
473
-            }
474
-        }
475
-        else {
476
-            // Class doesn't exist in map. Fall back to 404
477
-            $pageClass = Page404::class;
478
-            $action = 'main';
479
-
480
-            return array($pageClass, $action);
481
-        }
482
-    }
483
-
484
-    /**
485
-     * @return array
486
-     */
487
-    protected function getRouteMap()
488
-    {
489
-        return $this->routeMap;
490
-    }
491
-
492
-    /**
493
-     * @return callable
494
-     */
495
-    protected function getDefaultRoute()
496
-    {
497
-        return array(PageMain::class, "main");
498
-    }
65
+	/**
66
+	 * This is the core routing table for the application. The basic idea is:
67
+	 *
68
+	 *      array(
69
+	 *          "foo" =>
70
+	 *              array(
71
+	 *                  "class"   => PageFoo::class,
72
+	 *                  "actions" => array("bar", "other")
73
+	 *              ),
74
+	 * );
75
+	 *
76
+	 * Things to note:
77
+	 *     - If no page is requested, we go to PageMain. PageMain can't have actions defined.
78
+	 *
79
+	 *     - If a page is defined and requested, but no action is requested, go to that page's main() method
80
+	 *     - If a page is defined and requested, and an action is defined and requested, go to that action's method.
81
+	 *     - If a page is defined and requested, and an action NOT defined and requested, go to Page404 and it's main()
82
+	 *       method.
83
+	 *     - If a page is NOT defined and requested, go to Page404 and it's main() method.
84
+	 *
85
+	 *     - Query parameters are ignored.
86
+	 *
87
+	 * The key point here is request routing with validation that this is allowed, before we start hitting the
88
+	 * filesystem through the AutoLoader, and opening random files. Also, so that we validate the action requested
89
+	 * before we start calling random methods through the web UI.
90
+	 *
91
+	 * Examples:
92
+	 * /internal.php                => returns instance of PageMain, routed to main()
93
+	 * /internal.php?query          => returns instance of PageMain, routed to main()
94
+	 * /internal.php/foo            => returns instance of PageFoo, routed to main()
95
+	 * /internal.php/foo?query      => returns instance of PageFoo, routed to main()
96
+	 * /internal.php/foo/bar        => returns instance of PageFoo, routed to bar()
97
+	 * /internal.php/foo/bar?query  => returns instance of PageFoo, routed to bar()
98
+	 * /internal.php/foo/baz        => returns instance of Page404, routed to main()
99
+	 * /internal.php/foo/baz?query  => returns instance of Page404, routed to main()
100
+	 * /internal.php/bar            => returns instance of Page404, routed to main()
101
+	 * /internal.php/bar?query      => returns instance of Page404, routed to main()
102
+	 * /internal.php/bar/baz        => returns instance of Page404, routed to main()
103
+	 * /internal.php/bar/baz?query  => returns instance of Page404, routed to main()
104
+	 *
105
+	 * Take care when changing this - a lot of places rely on the array key for redirects and other links. If you need
106
+	 * to change the key, then you'll likely have to update a lot of files.
107
+	 *
108
+	 * @var array
109
+	 */
110
+	private $routeMap = array(
111
+
112
+		//////////////////////////////////////////////////////////////////////////////////////////////////
113
+		// Login and registration
114
+		'logout'                      =>
115
+			array(
116
+				'class'   => PageLogout::class,
117
+				'actions' => array(),
118
+			),
119
+		'login'                       =>
120
+			array(
121
+				'class'   => PagePasswordLogin::class,
122
+				'actions' => array(),
123
+			),
124
+		'login/otp'                   =>
125
+			array(
126
+				'class'   => PageOtpLogin::class,
127
+				'actions' => array(),
128
+			),
129
+		'login/u2f'                   =>
130
+			array(
131
+				'class'   => PageU2FLogin::class,
132
+				'actions' => array(),
133
+			),
134
+		'forgotPassword'              =>
135
+			array(
136
+				'class'   => PageForgotPassword::class,
137
+				'actions' => array('reset'),
138
+			),
139
+		'register'                    =>
140
+			array(
141
+				'class'   => PageRegisterOption::class,
142
+				'actions' => array(),
143
+			),
144
+		'register/standard'           =>
145
+			array(
146
+				'class'   => PageRegisterStandard::class,
147
+				'actions' => array('done'),
148
+			),
149
+
150
+		//////////////////////////////////////////////////////////////////////////////////////////////////
151
+		// Discovery
152
+		'search'                      =>
153
+			array(
154
+				'class'   => PageSearch::class,
155
+				'actions' => array(),
156
+			),
157
+		'logs'                        =>
158
+			array(
159
+				'class'   => PageLog::class,
160
+				'actions' => array(),
161
+			),
162
+
163
+		//////////////////////////////////////////////////////////////////////////////////////////////////
164
+		// Administration
165
+		'bans'                        =>
166
+			array(
167
+				'class'   => PageBan::class,
168
+				'actions' => array('set', 'remove'),
169
+			),
170
+		'userManagement'              =>
171
+			array(
172
+				'class'   => PageUserManagement::class,
173
+				'actions' => array(
174
+					'approve',
175
+					'decline',
176
+					'rename',
177
+					'editUser',
178
+					'suspend',
179
+					'editRoles',
180
+				),
181
+			),
182
+		'siteNotice'                  =>
183
+			array(
184
+				'class'   => PageSiteNotice::class,
185
+				'actions' => array(),
186
+			),
187
+		'emailManagement'             =>
188
+			array(
189
+				'class'   => PageEmailManagement::class,
190
+				'actions' => array('create', 'edit', 'view'),
191
+			),
192
+		'jobQueue'                    =>
193
+			array(
194
+				'class'   => PageJobQueue::class,
195
+				'actions' => array('acknowledge', 'requeue', 'view', 'all'),
196
+			),
197
+
198
+		//////////////////////////////////////////////////////////////////////////////////////////////////
199
+		// Personal preferences
200
+		'preferences'                 =>
201
+			array(
202
+				'class'   => PagePreferences::class,
203
+				'actions' => array(),
204
+			),
205
+		'changePassword'              =>
206
+			array(
207
+				'class'   => PageChangePassword::class,
208
+				'actions' => array(),
209
+			),
210
+		'multiFactor'                 =>
211
+			array(
212
+				'class'   => PageMultiFactor::class,
213
+				'actions' => array(
214
+					'scratch',
215
+					'enableYubikeyOtp',
216
+					'disableYubikeyOtp',
217
+					'enableTotp',
218
+					'disableTotp',
219
+					'enableU2F',
220
+					'disableU2F',
221
+				),
222
+			),
223
+		'oauth'                       =>
224
+			array(
225
+				'class'   => PageOAuth::class,
226
+				'actions' => array('detach', 'attach'),
227
+			),
228
+		'oauth/callback'              =>
229
+			array(
230
+				'class'   => PageOAuthCallback::class,
231
+				'actions' => array('authorise', 'create'),
232
+			),
233
+
234
+		//////////////////////////////////////////////////////////////////////////////////////////////////
235
+		// Welcomer configuration
236
+		'welcomeTemplates'            =>
237
+			array(
238
+				'class'   => PageWelcomeTemplateManagement::class,
239
+				'actions' => array('select', 'edit', 'delete', 'add', 'view'),
240
+			),
241
+
242
+		//////////////////////////////////////////////////////////////////////////////////////////////////
243
+		// Statistics
244
+		'statistics'                  =>
245
+			array(
246
+				'class'   => StatsMain::class,
247
+				'actions' => array(),
248
+			),
249
+		'statistics/fastCloses'       =>
250
+			array(
251
+				'class'   => StatsFastCloses::class,
252
+				'actions' => array(),
253
+			),
254
+		'statistics/inactiveUsers'    =>
255
+			array(
256
+				'class'   => StatsInactiveUsers::class,
257
+				'actions' => array(),
258
+			),
259
+		'statistics/monthlyStats'     =>
260
+			array(
261
+				'class'   => StatsMonthlyStats::class,
262
+				'actions' => array(),
263
+			),
264
+		'statistics/reservedRequests' =>
265
+			array(
266
+				'class'   => StatsReservedRequests::class,
267
+				'actions' => array(),
268
+			),
269
+		'statistics/templateStats'    =>
270
+			array(
271
+				'class'   => StatsTemplateStats::class,
272
+				'actions' => array(),
273
+			),
274
+		'statistics/topCreators'      =>
275
+			array(
276
+				'class'   => StatsTopCreators::class,
277
+				'actions' => array(),
278
+			),
279
+		'statistics/users'            =>
280
+			array(
281
+				'class'   => StatsUsers::class,
282
+				'actions' => array('detail'),
283
+			),
284
+
285
+		//////////////////////////////////////////////////////////////////////////////////////////////////
286
+		// Zoom page
287
+		'viewRequest'                 =>
288
+			array(
289
+				'class'   => PageViewRequest::class,
290
+				'actions' => array(),
291
+			),
292
+		'viewRequest/reserve'         =>
293
+			array(
294
+				'class'   => PageReservation::class,
295
+				'actions' => array(),
296
+			),
297
+		'viewRequest/breakReserve'    =>
298
+			array(
299
+				'class'   => PageBreakReservation::class,
300
+				'actions' => array(),
301
+			),
302
+		'viewRequest/defer'           =>
303
+			array(
304
+				'class'   => PageDeferRequest::class,
305
+				'actions' => array(),
306
+			),
307
+		'viewRequest/comment'         =>
308
+			array(
309
+				'class'   => PageComment::class,
310
+				'actions' => array(),
311
+			),
312
+		'viewRequest/sendToUser'      =>
313
+			array(
314
+				'class'   => PageSendToUser::class,
315
+				'actions' => array(),
316
+			),
317
+		'viewRequest/close'           =>
318
+			array(
319
+				'class'   => PageCloseRequest::class,
320
+				'actions' => array(),
321
+			),
322
+		'viewRequest/create'          =>
323
+			array(
324
+				'class'   => PageCreateRequest::class,
325
+				'actions' => array(),
326
+			),
327
+		'viewRequest/drop'            =>
328
+			array(
329
+				'class'   => PageDropRequest::class,
330
+				'actions' => array(),
331
+			),
332
+		'viewRequest/custom'          =>
333
+			array(
334
+				'class'   => PageCustomClose::class,
335
+				'actions' => array(),
336
+			),
337
+		'editComment'                 =>
338
+			array(
339
+				'class'   => PageEditComment::class,
340
+				'actions' => array(),
341
+			),
342
+
343
+		//////////////////////////////////////////////////////////////////////////////////////////////////
344
+		// Misc stuff
345
+		'team'                        =>
346
+			array(
347
+				'class'   => PageTeam::class,
348
+				'actions' => array(),
349
+			),
350
+		'requestList'                 =>
351
+			array(
352
+				'class'   => PageExpandedRequestList::class,
353
+				'actions' => array(),
354
+			),
355
+	);
356
+
357
+	/**
358
+	 * @return IRoutedTask
359
+	 * @throws Exception
360
+	 */
361
+	final public function route()
362
+	{
363
+		$pathInfo = WebRequest::pathInfo();
364
+
365
+		list($pageClass, $action) = $this->getRouteFromPath($pathInfo);
366
+
367
+		/** @var IRoutedTask $page */
368
+		$page = new $pageClass();
369
+
370
+		// Dynamic creation, so we've got to be careful here. We can't use built-in language type protection, so
371
+		// let's use our own.
372
+		if (!($page instanceof IRoutedTask)) {
373
+			throw new Exception('Expected a page, but this is not a page.');
374
+		}
375
+
376
+		// OK, I'm happy at this point that we know we're running a page, and we know it's probably what we want if it
377
+		// inherits PageBase and has been created from the routing map.
378
+		$page->setRoute($action);
379
+
380
+		return $page;
381
+	}
382
+
383
+	/**
384
+	 * @param $pathInfo
385
+	 *
386
+	 * @return array
387
+	 */
388
+	protected function getRouteFromPath($pathInfo)
389
+	{
390
+		if (count($pathInfo) === 0) {
391
+			// No pathInfo, so no page to load. Load the main page.
392
+			return $this->getDefaultRoute();
393
+		}
394
+		elseif (count($pathInfo) === 1) {
395
+			// Exactly one path info segment, it's got to be a page.
396
+			$classSegment = $pathInfo[0];
397
+
398
+			return $this->routeSinglePathSegment($classSegment);
399
+		}
400
+
401
+		// OK, we have two or more segments now.
402
+		if (count($pathInfo) > 2) {
403
+			// Let's handle more than two, and collapse it down into two.
404
+			$requestedAction = array_pop($pathInfo);
405
+			$classSegment = implode('/', $pathInfo);
406
+		}
407
+		else {
408
+			// Two path info segments.
409
+			$classSegment = $pathInfo[0];
410
+			$requestedAction = $pathInfo[1];
411
+		}
412
+
413
+		$routeMap = $this->routePathSegments($classSegment, $requestedAction);
414
+
415
+		if ($routeMap[0] === Page404::class) {
416
+			$routeMap = $this->routeSinglePathSegment($classSegment . '/' . $requestedAction);
417
+		}
418
+
419
+		return $routeMap;
420
+	}
421
+
422
+	/**
423
+	 * @param $classSegment
424
+	 *
425
+	 * @return array
426
+	 */
427
+	final protected function routeSinglePathSegment($classSegment)
428
+	{
429
+		$routeMap = $this->getRouteMap();
430
+		if (array_key_exists($classSegment, $routeMap)) {
431
+			// Route exists, but we don't have an action in path info, so default to main.
432
+			$pageClass = $routeMap[$classSegment]['class'];
433
+			$action = 'main';
434
+
435
+			return array($pageClass, $action);
436
+		}
437
+		else {
438
+			// Doesn't exist in map. Fall back to 404
439
+			$pageClass = Page404::class;
440
+			$action = "main";
441
+
442
+			return array($pageClass, $action);
443
+		}
444
+	}
445
+
446
+	/**
447
+	 * @param $classSegment
448
+	 * @param $requestedAction
449
+	 *
450
+	 * @return array
451
+	 */
452
+	final protected function routePathSegments($classSegment, $requestedAction)
453
+	{
454
+		$routeMap = $this->getRouteMap();
455
+		if (array_key_exists($classSegment, $routeMap)) {
456
+			// Route exists, but we don't have an action in path info, so default to main.
457
+
458
+			if (isset($routeMap[$classSegment]['actions'])
459
+				&& array_search($requestedAction, $routeMap[$classSegment]['actions']) !== false
460
+			) {
461
+				// Action exists in allowed action list. Allow both the page and the action
462
+				$pageClass = $routeMap[$classSegment]['class'];
463
+				$action = $requestedAction;
464
+
465
+				return array($pageClass, $action);
466
+			}
467
+			else {
468
+				// Valid page, invalid action. 404 our way out.
469
+				$pageClass = Page404::class;
470
+				$action = 'main';
471
+
472
+				return array($pageClass, $action);
473
+			}
474
+		}
475
+		else {
476
+			// Class doesn't exist in map. Fall back to 404
477
+			$pageClass = Page404::class;
478
+			$action = 'main';
479
+
480
+			return array($pageClass, $action);
481
+		}
482
+	}
483
+
484
+	/**
485
+	 * @return array
486
+	 */
487
+	protected function getRouteMap()
488
+	{
489
+		return $this->routeMap;
490
+	}
491
+
492
+	/**
493
+	 * @return callable
494
+	 */
495
+	protected function getDefaultRoute()
496
+	{
497
+		return array(PageMain::class, "main");
498
+	}
499 499
 }
Please login to merge, or discard this patch.
includes/Tasks/IRoutedTask.php 1 patch
Indentation   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -12,21 +12,21 @@
 block discarded – undo
12 12
 
13 13
 interface IRoutedTask extends ITask
14 14
 {
15
-    /**
16
-     * Sets the route the request will take. Only should be called from the request router.
17
-     *
18
-     * @param $routeName string
19
-     *
20
-     * @return void
21
-     *
22
-     * @throws Exception
23
-     * @category Security-Critical
24
-     */
25
-    public function setRoute($routeName);
15
+	/**
16
+	 * Sets the route the request will take. Only should be called from the request router.
17
+	 *
18
+	 * @param $routeName string
19
+	 *
20
+	 * @return void
21
+	 *
22
+	 * @throws Exception
23
+	 * @category Security-Critical
24
+	 */
25
+	public function setRoute($routeName);
26 26
 
27
-    /**
28
-     * Gets the name of the route that has been passed from the request router.
29
-     * @return string
30
-     */
31
-    public function getRouteName();
27
+	/**
28
+	 * Gets the name of the route that has been passed from the request router.
29
+	 * @return string
30
+	 */
31
+	public function getRouteName();
32 32
 }
33 33
\ No newline at end of file
Please login to merge, or discard this patch.
includes/Tasks/ApiPageBase.php 2 patches
Indentation   +93 added lines, -93 removed lines patch added patch discarded remove patch
@@ -16,97 +16,97 @@
 block discarded – undo
16 16
 
17 17
 abstract class ApiPageBase extends TaskBase implements IRoutedTask, IApiAction
18 18
 {
19
-    /**
20
-     * API result document
21
-     * @var DOMDocument
22
-     */
23
-    protected $document;
24
-
25
-    public function __construct()
26
-    {
27
-        $this->document = new DOMDocument('1.0');
28
-    }
29
-
30
-    final public function execute()
31
-    {
32
-        $this->main();
33
-    }
34
-
35
-    /**
36
-     * @param string $routeName
37
-     */
38
-    public function setRoute($routeName)
39
-    {
40
-        // no-op
41
-    }
42
-
43
-    /**
44
-     * @return string
45
-     */
46
-    public function getRouteName()
47
-    {
48
-        return 'main';
49
-    }
50
-
51
-    /**
52
-     * Main function for this page, when no specific actions are called.
53
-     *
54
-     * @throws ApiException
55
-     * @return void
56
-     */
57
-    final protected function main()
58
-    {
59
-        if (headers_sent()) {
60
-            throw new ApiException('Headers have already been sent - this indicates a bug in the application!');
61
-        }
62
-
63
-        header("Content-Type: text/xml");
64
-
65
-        // javascript access control
66
-        $httpOrigin = WebRequest::origin();
67
-
68
-        if ($httpOrigin !== null) {
69
-            $CORSallowed = $this->getSiteConfiguration()->getCrossOriginResourceSharingHosts();
70
-
71
-            if (in_array($httpOrigin, $CORSallowed)) {
72
-                header("Access-Control-Allow-Origin: " . $httpOrigin);
73
-            }
74
-        }
75
-
76
-        $responseData = $this->runApiPage();
77
-
78
-        ob_end_clean();
79
-        print($responseData);
80
-        ob_start();
81
-    }
82
-
83
-    /**
84
-     * Method that runs API action
85
-     *
86
-     * @param DOMElement $apiDocument
87
-     *
88
-     * @return DOMElement
89
-     */
90
-    abstract public function executeApiAction(DOMElement $apiDocument);
91
-
92
-    /**
93
-     * @return string
94
-     */
95
-    final public function runApiPage()
96
-    {
97
-        $apiDocument = $this->document->createElement("api");
98
-
99
-        try {
100
-            $apiDocument = $this->executeApiAction($apiDocument);
101
-        }
102
-        catch (ApiException $ex) {
103
-            $exception = $this->document->createElement("error");
104
-            $exception->setAttribute("message", $ex->getMessage());
105
-            $apiDocument->appendChild($exception);
106
-        }
107
-
108
-        $this->document->appendChild($apiDocument);
109
-
110
-        return $this->document->saveXML();
111
-    }
19
+	/**
20
+	 * API result document
21
+	 * @var DOMDocument
22
+	 */
23
+	protected $document;
24
+
25
+	public function __construct()
26
+	{
27
+		$this->document = new DOMDocument('1.0');
28
+	}
29
+
30
+	final public function execute()
31
+	{
32
+		$this->main();
33
+	}
34
+
35
+	/**
36
+	 * @param string $routeName
37
+	 */
38
+	public function setRoute($routeName)
39
+	{
40
+		// no-op
41
+	}
42
+
43
+	/**
44
+	 * @return string
45
+	 */
46
+	public function getRouteName()
47
+	{
48
+		return 'main';
49
+	}
50
+
51
+	/**
52
+	 * Main function for this page, when no specific actions are called.
53
+	 *
54
+	 * @throws ApiException
55
+	 * @return void
56
+	 */
57
+	final protected function main()
58
+	{
59
+		if (headers_sent()) {
60
+			throw new ApiException('Headers have already been sent - this indicates a bug in the application!');
61
+		}
62
+
63
+		header("Content-Type: text/xml");
64
+
65
+		// javascript access control
66
+		$httpOrigin = WebRequest::origin();
67
+
68
+		if ($httpOrigin !== null) {
69
+			$CORSallowed = $this->getSiteConfiguration()->getCrossOriginResourceSharingHosts();
70
+
71
+			if (in_array($httpOrigin, $CORSallowed)) {
72
+				header("Access-Control-Allow-Origin: " . $httpOrigin);
73
+			}
74
+		}
75
+
76
+		$responseData = $this->runApiPage();
77
+
78
+		ob_end_clean();
79
+		print($responseData);
80
+		ob_start();
81
+	}
82
+
83
+	/**
84
+	 * Method that runs API action
85
+	 *
86
+	 * @param DOMElement $apiDocument
87
+	 *
88
+	 * @return DOMElement
89
+	 */
90
+	abstract public function executeApiAction(DOMElement $apiDocument);
91
+
92
+	/**
93
+	 * @return string
94
+	 */
95
+	final public function runApiPage()
96
+	{
97
+		$apiDocument = $this->document->createElement("api");
98
+
99
+		try {
100
+			$apiDocument = $this->executeApiAction($apiDocument);
101
+		}
102
+		catch (ApiException $ex) {
103
+			$exception = $this->document->createElement("error");
104
+			$exception->setAttribute("message", $ex->getMessage());
105
+			$apiDocument->appendChild($exception);
106
+		}
107
+
108
+		$this->document->appendChild($apiDocument);
109
+
110
+		return $this->document->saveXML();
111
+	}
112 112
 }
113 113
\ No newline at end of file
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -69,7 +69,7 @@
 block discarded – undo
69 69
             $CORSallowed = $this->getSiteConfiguration()->getCrossOriginResourceSharingHosts();
70 70
 
71 71
             if (in_array($httpOrigin, $CORSallowed)) {
72
-                header("Access-Control-Allow-Origin: " . $httpOrigin);
72
+                header("Access-Control-Allow-Origin: ".$httpOrigin);
73 73
             }
74 74
         }
75 75
 
Please login to merge, or discard this patch.
includes/Tasks/PublicInterfacePageBase.php 1 patch
Indentation   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -10,21 +10,21 @@
 block discarded – undo
10 10
 
11 11
 abstract class PublicInterfacePageBase extends PageBase
12 12
 {
13
-    /**
14
-     * PublicInterfaceInternalPageBase constructor.
15
-     */
16
-    public function __construct()
17
-    {
18
-        $this->template = 'publicbase.tpl';
19
-    }
13
+	/**
14
+	 * PublicInterfaceInternalPageBase constructor.
15
+	 */
16
+	public function __construct()
17
+	{
18
+		$this->template = 'publicbase.tpl';
19
+	}
20 20
 
21
-    final public function execute()
22
-    {
23
-        parent::execute();
24
-    }
21
+	final public function execute()
22
+	{
23
+		parent::execute();
24
+	}
25 25
 
26
-    final public function finalisePage()
27
-    {
28
-        parent::finalisePage();
29
-    }
26
+	final public function finalisePage()
27
+	{
28
+		parent::finalisePage();
29
+	}
30 30
 }
31 31
\ No newline at end of file
Please login to merge, or discard this patch.
includes/DataObject.php 1 patch
Indentation   +120 added lines, -120 removed lines patch added patch discarded remove patch
@@ -23,124 +23,124 @@
 block discarded – undo
23 23
  */
24 24
 abstract class DataObject
25 25
 {
26
-    /** @var int ID of the object */
27
-    protected $id = null;
28
-    /** @var int update version for optimistic locking */
29
-    protected $updateversion = 0;
30
-    /**
31
-     * @var PdoDatabase
32
-     */
33
-    protected $dbObject;
34
-
35
-    /**
36
-     * Retrieves a data object by it's row ID.
37
-     *
38
-     * @param int         $id
39
-     * @param PdoDatabase $database
40
-     *
41
-     * @return DataObject|false
42
-     */
43
-    public static function getById($id, PdoDatabase $database)
44
-    {
45
-        $array = explode('\\', get_called_class());
46
-        $realClassName = strtolower(end($array));
47
-
48
-        $statement = $database->prepare("SELECT * FROM {$realClassName} WHERE id = :id LIMIT 1;");
49
-        $statement->bindValue(":id", $id);
50
-
51
-        $statement->execute();
52
-
53
-        $resultObject = $statement->fetchObject(get_called_class());
54
-
55
-        if ($resultObject != false) {
56
-            $resultObject->setDatabase($database);
57
-        }
58
-
59
-        return $resultObject;
60
-    }
61
-
62
-    public function setDatabase(PdoDatabase $db)
63
-    {
64
-        $this->dbObject = $db;
65
-    }
66
-
67
-    /**
68
-     * Gets the database associated with this data object.
69
-     * @return PdoDatabase
70
-     */
71
-    public function getDatabase()
72
-    {
73
-        return $this->dbObject;
74
-    }
75
-
76
-    /**
77
-     * Saves a data object to the database, either updating or inserting a record.
78
-     *
79
-     * @return void
80
-     */
81
-    abstract public function save();
82
-
83
-    /**
84
-     * Retrieves the ID attribute
85
-     */
86
-    public function getId()
87
-    {
88
-        return (int)$this->id;
89
-    }
90
-
91
-    /**
92
-     * Deletes the object from the database
93
-     */
94
-    public function delete()
95
-    {
96
-        if ($this->id === null) {
97
-            // wtf?
98
-            return;
99
-        }
100
-
101
-        $array = explode('\\', get_called_class());
102
-        $realClassName = strtolower(end($array));
103
-
104
-        $deleteQuery = "DELETE FROM {$realClassName} WHERE id = :id AND updateversion = :updateversion LIMIT 1;";
105
-        $statement = $this->dbObject->prepare($deleteQuery);
106
-
107
-        $statement->bindValue(":id", $this->id);
108
-        $statement->bindValue(":updateversion", $this->updateversion);
109
-        $statement->execute();
110
-
111
-        if ($statement->rowCount() !== 1) {
112
-            throw new OptimisticLockFailedException();
113
-        }
114
-
115
-        $this->id = null;
116
-    }
117
-
118
-    /**
119
-     * @return int
120
-     */
121
-    public function getUpdateVersion()
122
-    {
123
-        return $this->updateversion;
124
-    }
125
-
126
-    /**
127
-     * Sets the update version.
128
-     *
129
-     * You should never call this to change the value of the update version. You should only call it when passing user
130
-     * input through.
131
-     *
132
-     * @param int $updateVersion
133
-     */
134
-    public function setUpdateVersion($updateVersion)
135
-    {
136
-        $this->updateversion = $updateVersion;
137
-    }
138
-
139
-    /**
140
-     * @return bool
141
-     */
142
-    public function isNew()
143
-    {
144
-        return $this->id === null;
145
-    }
26
+	/** @var int ID of the object */
27
+	protected $id = null;
28
+	/** @var int update version for optimistic locking */
29
+	protected $updateversion = 0;
30
+	/**
31
+	 * @var PdoDatabase
32
+	 */
33
+	protected $dbObject;
34
+
35
+	/**
36
+	 * Retrieves a data object by it's row ID.
37
+	 *
38
+	 * @param int         $id
39
+	 * @param PdoDatabase $database
40
+	 *
41
+	 * @return DataObject|false
42
+	 */
43
+	public static function getById($id, PdoDatabase $database)
44
+	{
45
+		$array = explode('\\', get_called_class());
46
+		$realClassName = strtolower(end($array));
47
+
48
+		$statement = $database->prepare("SELECT * FROM {$realClassName} WHERE id = :id LIMIT 1;");
49
+		$statement->bindValue(":id", $id);
50
+
51
+		$statement->execute();
52
+
53
+		$resultObject = $statement->fetchObject(get_called_class());
54
+
55
+		if ($resultObject != false) {
56
+			$resultObject->setDatabase($database);
57
+		}
58
+
59
+		return $resultObject;
60
+	}
61
+
62
+	public function setDatabase(PdoDatabase $db)
63
+	{
64
+		$this->dbObject = $db;
65
+	}
66
+
67
+	/**
68
+	 * Gets the database associated with this data object.
69
+	 * @return PdoDatabase
70
+	 */
71
+	public function getDatabase()
72
+	{
73
+		return $this->dbObject;
74
+	}
75
+
76
+	/**
77
+	 * Saves a data object to the database, either updating or inserting a record.
78
+	 *
79
+	 * @return void
80
+	 */
81
+	abstract public function save();
82
+
83
+	/**
84
+	 * Retrieves the ID attribute
85
+	 */
86
+	public function getId()
87
+	{
88
+		return (int)$this->id;
89
+	}
90
+
91
+	/**
92
+	 * Deletes the object from the database
93
+	 */
94
+	public function delete()
95
+	{
96
+		if ($this->id === null) {
97
+			// wtf?
98
+			return;
99
+		}
100
+
101
+		$array = explode('\\', get_called_class());
102
+		$realClassName = strtolower(end($array));
103
+
104
+		$deleteQuery = "DELETE FROM {$realClassName} WHERE id = :id AND updateversion = :updateversion LIMIT 1;";
105
+		$statement = $this->dbObject->prepare($deleteQuery);
106
+
107
+		$statement->bindValue(":id", $this->id);
108
+		$statement->bindValue(":updateversion", $this->updateversion);
109
+		$statement->execute();
110
+
111
+		if ($statement->rowCount() !== 1) {
112
+			throw new OptimisticLockFailedException();
113
+		}
114
+
115
+		$this->id = null;
116
+	}
117
+
118
+	/**
119
+	 * @return int
120
+	 */
121
+	public function getUpdateVersion()
122
+	{
123
+		return $this->updateversion;
124
+	}
125
+
126
+	/**
127
+	 * Sets the update version.
128
+	 *
129
+	 * You should never call this to change the value of the update version. You should only call it when passing user
130
+	 * input through.
131
+	 *
132
+	 * @param int $updateVersion
133
+	 */
134
+	public function setUpdateVersion($updateVersion)
135
+	{
136
+		$this->updateversion = $updateVersion;
137
+	}
138
+
139
+	/**
140
+	 * @return bool
141
+	 */
142
+	public function isNew()
143
+	{
144
+		return $this->id === null;
145
+	}
146 146
 }
Please login to merge, or discard this patch.
includes/Offline.php 1 patch
Indentation   +43 added lines, -43 removed lines patch added patch discarded remove patch
@@ -15,55 +15,55 @@
 block discarded – undo
15 15
  */
16 16
 class Offline
17 17
 {
18
-    /**
19
-     * Determines if the tool is offline
20
-     * @return bool
21
-     */
22
-    public static function isOffline()
23
-    {
24
-        global $dontUseDb;
18
+	/**
19
+	 * Determines if the tool is offline
20
+	 * @return bool
21
+	 */
22
+	public static function isOffline()
23
+	{
24
+		global $dontUseDb;
25 25
 
26
-        return (bool)$dontUseDb;
27
-    }
26
+		return (bool)$dontUseDb;
27
+	}
28 28
 
29
-    /**
30
-     * Gets the offline message
31
-     *
32
-     * @param bool $external
33
-     * @param null $message
34
-     *
35
-     * @return string
36
-     */
37
-    public static function getOfflineMessage($external, $message = null)
38
-    {
39
-        global $dontUseDbCulprit, $dontUseDbReason, $baseurl;
29
+	/**
30
+	 * Gets the offline message
31
+	 *
32
+	 * @param bool $external
33
+	 * @param null $message
34
+	 *
35
+	 * @return string
36
+	 */
37
+	public static function getOfflineMessage($external, $message = null)
38
+	{
39
+		global $dontUseDbCulprit, $dontUseDbReason, $baseurl;
40 40
 
41
-        $smarty = new Smarty();
42
-        $smarty->assign("baseurl", $baseurl);
43
-        $smarty->assign("toolversion", Environment::getToolVersion());
41
+		$smarty = new Smarty();
42
+		$smarty->assign("baseurl", $baseurl);
43
+		$smarty->assign("toolversion", Environment::getToolVersion());
44 44
 
45
-        if (!headers_sent()) {
46
-            header("HTTP/1.1 503 Service Unavailable");
47
-        }
45
+		if (!headers_sent()) {
46
+			header("HTTP/1.1 503 Service Unavailable");
47
+		}
48 48
 
49
-        if ($external) {
50
-            return $smarty->fetch("offline/external.tpl");
51
-        }
52
-        else {
53
-            $hideCulprit = true;
49
+		if ($external) {
50
+			return $smarty->fetch("offline/external.tpl");
51
+		}
52
+		else {
53
+			$hideCulprit = true;
54 54
 
55
-            // Use the provided message if possible
56
-            if ($message === null) {
57
-                $hideCulprit = false;
58
-                $message = $dontUseDbReason;
59
-            }
55
+			// Use the provided message if possible
56
+			if ($message === null) {
57
+				$hideCulprit = false;
58
+				$message = $dontUseDbReason;
59
+			}
60 60
 
61
-            $smarty->assign("hideCulprit", $hideCulprit);
62
-            $smarty->assign("dontUseDbCulprit", $dontUseDbCulprit);
63
-            $smarty->assign("dontUseDbReason", $message);
64
-            $smarty->assign("alerts", array());
61
+			$smarty->assign("hideCulprit", $hideCulprit);
62
+			$smarty->assign("dontUseDbCulprit", $dontUseDbCulprit);
63
+			$smarty->assign("dontUseDbReason", $message);
64
+			$smarty->assign("alerts", array());
65 65
 
66
-            return $smarty->fetch("offline/internal.tpl");
67
-        }
68
-    }
66
+			return $smarty->fetch("offline/internal.tpl");
67
+		}
68
+	}
69 69
 }
Please login to merge, or discard this patch.
includes/Pages/PageTeam.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -19,7 +19,7 @@
 block discarded – undo
19 19
      */
20 20
     protected function main()
21 21
     {
22
-        $path = $this->getSiteConfiguration()->getFilePath() . '/team.json';
22
+        $path = $this->getSiteConfiguration()->getFilePath().'/team.json';
23 23
         $json = file_get_contents($path);
24 24
 
25 25
         $teamData = json_decode($json, true);
Please login to merge, or discard this patch.
Indentation   +23 added lines, -23 removed lines patch added patch discarded remove patch
@@ -12,31 +12,31 @@
 block discarded – undo
12 12
 
13 13
 class PageTeam extends InternalPageBase
14 14
 {
15
-    /**
16
-     * Main function for this page, when no specific actions are called.
17
-     * @return void
18
-     */
19
-    protected function main()
20
-    {
21
-        $path = $this->getSiteConfiguration()->getFilePath() . '/team.json';
22
-        $json = file_get_contents($path);
15
+	/**
16
+	 * Main function for this page, when no specific actions are called.
17
+	 * @return void
18
+	 */
19
+	protected function main()
20
+	{
21
+		$path = $this->getSiteConfiguration()->getFilePath() . '/team.json';
22
+		$json = file_get_contents($path);
23 23
 
24
-        $teamData = json_decode($json, true);
24
+		$teamData = json_decode($json, true);
25 25
 
26
-        $active = array();
27
-        $inactive = array();
26
+		$active = array();
27
+		$inactive = array();
28 28
 
29
-        foreach ($teamData as $name => $item) {
30
-            if (count($item['Role']) == 0) {
31
-                $inactive[$name] = $item;
32
-            }
33
-            else {
34
-                $active[$name] = $item;
35
-            }
36
-        }
29
+		foreach ($teamData as $name => $item) {
30
+			if (count($item['Role']) == 0) {
31
+				$inactive[$name] = $item;
32
+			}
33
+			else {
34
+				$active[$name] = $item;
35
+			}
36
+		}
37 37
 
38
-        $this->assign('developer', $active);
39
-        $this->assign('inactiveDeveloper', $inactive);
40
-        $this->setTemplate('team/team.tpl');
41
-    }
38
+		$this->assign('developer', $active);
39
+		$this->assign('inactiveDeveloper', $inactive);
40
+		$this->setTemplate('team/team.tpl');
41
+	}
42 42
 }
Please login to merge, or discard this patch.