Completed
Push — master ( c26f0f...c7af1e )
by Konstantinos
18:52
created

Ban::addBan()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 34
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 6.0208

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 34
ccs 22
cts 24
cp 0.9167
rs 8.439
cc 6
eloc 22
nc 8
nop 7
crap 6.0208
1
<?php
2
/**
3
 * This file contains functionality relating to the banned league players
4
 *
5
 * @package    BZiON\Models
6
 * @license    https://github.com/allejo/bzion/blob/master/LICENSE.md GNU General Public License Version 3
7
 */
8
9
/**
10
 * A ban imposed by an admin on a player
11
 * @package BZiON\Models
12
 */
13
class Ban extends UrlModel implements NamedModel
14
{
15
    /**
16
     * The id of the banned player
17
     * @var int
18
     */
19
    protected $player;
20
21
    /**
22
     * The ban expiration date
23
     * @var TimeDate
24
     */
25
    protected $expiration;
26
27
    /**
28
     * The message that will appear when a player is denied connecting to a game server
29
     * @var string
30
     */
31
    protected $srvmsg;
32
33
    /**
34
     * The ban reason
35
     * @var string
36
     */
37
    protected $reason;
38
39
    /**
40
     * Whether or not a player is allowed to join a server when they are banned
41
     * @var bool
42
     */
43
    protected $allowServerJoin;
44
45
    /**
46
     * The ban creation date
47
     * @var TimeDate
48
     */
49
    protected $created;
50
51
    /**
52
     * The date the ban was last updated
53
     * @var TimeDate
54
     */
55
    protected $updated;
56
57
    /**
58
     * The id of the ban author
59
     * @var int
60
     */
61
    protected $author;
62
63
    /**
64
     * The IP of the banned player if the league would like to implement a global ban list
65
     * @var string[]
66
     */
67
    protected $ipAddresses;
68
69
    /**
70
     * The ban's status
71
     * @var string
72
     */
73
    protected $status;
74
75
    /**
76
     * The name of the database table used for queries
77
     */
78
    const TABLE = "bans";
79
80
    const CREATE_PERMISSION = Permission::ADD_BAN;
81
    const EDIT_PERMISSION = Permission::EDIT_BAN;
82
    const SOFT_DELETE_PERMISSION = Permission::SOFT_DELETE_BAN;
83
    const HARD_DELETE_PERMISSION = Permission::HARD_DELETE_BAN;
84
85
    /**
86
     * {@inheritdoc}
87
     */
88 2
    protected function assignResult($ban)
89
    {
90 2
        $this->player = $ban['player'];
91 2
        $this->expiration = ($ban['expiration'] === null)
92 2
                          ? null
93 2
                          : TimeDate::fromMysql($ban['expiration']);
94 2
        $this->srvmsg = $ban['server_message'];
95 2
        $this->reason = $ban['reason'];
96 2
        $this->allowServerJoin = $ban['allow_server_join'];
97 2
        $this->created = TimeDate::fromMysql($ban['created']);
98 2
        $this->updated = TimeDate::fromMysql($ban['updated']);
99 2
        $this->author = $ban['author'];
100 2
        $this->status = $ban['status'];
101 2
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106
    protected function assignLazyResult($result)
107
    {
108
        $this->ipAddresses = self::fetchIds("WHERE ban_id = ?", array($this->getId()), "banned_ips", "ip_address");
0 ignored issues
show
Documentation Bug introduced by
It seems like self::fetchIds('WHERE ba...ned_ips', 'ip_address') of type array<integer,integer> is incompatible with the declared type array<integer,string> of property $ipAddresses.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
109
    }
110
111
    /**
112
     * Add an IP to the ban
113
     *
114
     * @param string $ipAddress The IP to add to a ban
115
     */
116 View Code Duplication
    public function addIP($ipAddress)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
117
    {
118
        $this->lazyLoad();
119
120
        $this->ipAddresses[] = $ipAddress;
121
        $this->db->execute("INSERT IGNORE INTO banned_ips (id, ban_id, ip_address) VALUES (NULL, ?, ?)", array($this->getId(), $ipAddress));
122
    }
123
124
    /**
125
     * Remove an IP from the ban
126
     *
127
     * @param string $ipAddress The IP to remove from the ban
128
     */
129 View Code Duplication
    public function removeIP($ipAddress)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
130
    {
131
        $this->lazyLoad();
132
133
        // Remove $ipAddress from $this->ipAddresses
134
        $this->ipAddresses = array_diff($this->ipAddresses, array($ipAddress));
135
        $this->db->execute("DELETE FROM banned_ips WHERE ban_id = ? AND ip_address = ?", array($this->getId(), $ipAddress));
136
    }
137
138
    /**
139
     * Set the IP addresses of the ban
140
     *
141
     * @todo   Is it worth making this faster?
142
     * @param  string[] $ipAddresses The new IP addresses of the ban
143
     * @return self
144
     */
145
    public function setIPs($ipAddresses)
146
    {
147
        $this->lazyLoad();
148
149
        $oldIPs = $this->ipAddresses;
150
        $this->ipAddresses = $ipAddresses;
151
152
        $newIPs     = array_diff($ipAddresses, $oldIPs);
153
        $removedIPs = array_diff($oldIPs, $ipAddresses);
154
155
        foreach ($newIPs as $ip) {
156
            $this->addIP($ip);
157
        }
158
159
        foreach ($removedIPs as $ip) {
160
            $this->removeIP($ip);
161
        }
162
163
        return $this;
164
    }
165
166
    /**
167
     * Check whether or not a player is allowed to join a server when they've been banned
168
     * @return bool Whether or not a player is allowed to join
169
     */
170
    public function allowedServerJoin()
171
    {
172
        return $this->allowServerJoin;
173
    }
174
175
    /**
176
     * Get the user who imposed the ban
177
     * @return Player The banner
178
     */
179
    public function getAuthor()
180
    {
181
        return Player::get($this->author);
182
    }
183
184
    /**
185
     * Get the creation time of the ban
186
     * @return TimeDate The creation time
187
     */
188
    public function getCreated()
189
    {
190
        return $this->created->copy();
191
    }
192
193
    /**
194
     * Get the expiration time of the ban
195
     * @return TimeDate
196
     */
197
    public function getExpiration()
198
    {
199
        return $this->expiration->copy();
200
    }
201
202
    /**
203
     * Get the ban's description
204
     * @return string
205
     */
206
    public function getReason()
207
    {
208
        return $this->reason;
209
    }
210
211
    /**
212
     * Get the ban summary that will appear when a player is denied access to a league server on join
213
     * @return string The ban summary
214
     */
215
    public function getServerMessage()
216
    {
217
        if ($this->allowedServerJoin()) {
218
            return '';
219
        }
220
221
        return $this->srvmsg;
222
    }
223
224
    /**
225
     * Get the IP address of the banned player
226
     * @return string[]
227
     */
228
    public function getIpAddresses()
229
    {
230
        $this->lazyLoad();
231
232
        return $this->ipAddresses;
233
    }
234
235
    /**
236
     * Get the time when the ban was last updated
237
     * @return TimeDate
238
     */
239
    public function getUpdated()
240
    {
241
        return $this->updated->copy();
242
    }
243
244
    /**
245
     * Get the player who was banned
246
     * @return Player The banned player
247
     */
248
    public function getVictim()
249
    {
250
        return Player::get($this->player);
251
    }
252
253
    /**
254
     * Get the ID of the player who was banned
255
     * @return int The ID of the victim of the ban
256
     */
257
    public function getVictimID()
258
    {
259
        return $this->player;
260
    }
261
262
    /**
263
     * Calculate whether a ban has expired or not.
264
     *
265
     * @return bool True if the ban's expiration time has already passed
266
     */
267
    public function isExpired()
268
    {
269
        if ($this->expiration === null) {
270
            return false;
271
        }
272
273
        return TimeDate::now()->gte($this->expiration);
274
    }
275
276
    /**
277
     * Check whether the ban will expire automatically
278
     *
279
     * @return bool
280
     */
281
    public function willExpire()
282
    {
283
        return $this->expiration !== null;
284
    }
285
286
    /**
287
     * Mark the ban as expired
288
     *
289
     * @return self
290
     */
291
    public function expire()
292
    {
293
        $this->setExpiration(TimeDate::now());
294
        $this->getVictim()->markAsUnbanned();
295
296
        return $this;
297
    }
298
299
    /**
300
     * Set the expiration date of the ban
301
     * @param  TimeDate $expiration The expiration
302
     * @return self
303
     */
304 1
    public function setExpiration($expiration)
305
    {
306 1
        if ($expiration !== null) {
307 1
            $expiration = TimeDate::from($expiration);
308 1
        }
309
310 1
        return $this->updateProperty($this->expiration, 'expiration', $expiration);
311
    }
312
313
    /**
314
     * Set the server message of the ban
315
     * @param  string $message The new server message
316
     * @return self
317
     */
318
    public function setServerMessage($message)
319
    {
320
        return $this->updateProperty($this->srvmsg, 'server_message', $message);
321
    }
322
323
    /**
324
     * Set the reason of the ban
325
     * @param  string $reason The new ban reason
326
     * @return self
327
     */
328
    public function setReason($reason)
329
    {
330
        return $this->updateProperty($this->reason, 'reason', $reason);
331
    }
332
333
    /**
334
     * Update the last edit timestamp
335
     * @return self
336
     */
337
    public function updateEditTimestamp()
338
    {
339
        return $this->updateProperty($this->updated, "updated", TimeDate::now());
340
    }
341
342
    /**
343
     * Set whether the ban's victim is allowed to enter a match server
344
     * @param  bool $allowServerJoin
345
     * @return self
346
     */
347
    public function setAllowServerJoin($allowServerJoin)
348
    {
349
        return $this->updateProperty($this->allowServerJoin, 'allow_server_join', (bool) $allowServerJoin);
350
    }
351
352
    /**
353
     * Add a new ban
354
     *
355
     * @param int         $playerID        The ID of the victim of the ban
356
     * @param int         $authorID        The ID of the player responsible for the ban
357
     * @param mixed|null $expiration      The expiration of the ban (set to NULL so that it never expires)
358
     * @param string      $reason          The full reason for the ban
359
     * @param string      $srvmsg          A summary of the ban to be displayed on server banlists (max 150 characters)
360
     * @param string[]    $ipAddresses     An array of IPs that have been banned
361
     * @param bool        $allowServerJoin Whether or not the player is allowed to join match servers
362
     *
363
     * @return Ban An object representing the ban that was just entered or false if the ban was not created
364
     */
365 2
    public static function addBan($playerID, $authorID, $expiration, $reason, $srvmsg = "", $ipAddresses = array(), $allowServerJoin = false)
366
    {
367 2
        $player = Player::get($playerID);
368
369 2
        if ($expiration !== null) {
370 1
            $expiration = TimeDate::from($expiration)->toMysql();
371 1
        } else {
372 1
            $player->markAsBanned();
373
        }
374
375
        // If there are no IPs to banned or no server ban message, then we'll allow the players to join as observers
376 2
        if (empty($srvmsg) || empty($ipAddresses)) {
377 2
            $allowServerJoin = true;
378 2
        }
379
380 2
        $ban = self::create(array(
381 2
            'player'            => $playerID,
382 2
            'expiration'        => $expiration,
383 2
            'server_message'    => $srvmsg,
384 2
            'reason'            => $reason,
385 2
            'allow_server_join' => $allowServerJoin,
386 2
            'author'            => $authorID,
387 2
        ), array('created', 'updated'));
388
389 2
        if (is_array($ipAddresses)) {
390 2
            foreach ($ipAddresses as $ip) {
391
                $ban->addIP($ip);
392 2
            }
393 2
        } else {
394
            $ban->addIP($ipAddresses);
395
        }
396
397 2
        return $ban;
398
    }
399
400
    /**
401
     * Get a query builder for news
402
     * @return QueryBuilder
403
     */
404
    public static function getQueryBuilder()
405
    {
406
        return new QueryBuilder('Ban', array(
407
            'columns' => array(
408
                'status'  => 'status',
409
                'updated' => 'updated'
410
            ),
411
        ));
412
    }
413
414
    /**
415
     * {@inheritdoc}
416
     */
417
    public function getName()
418
    {
419
        return 'Ban against ' . $this->getVictim()->getUsername();
420
    }
421
422
    /**
423
     * {@inheritdoc}
424
     */
425
    public function delete()
426
    {
427
        $this->getVictim()->markAsUnbanned();
428
        parent::delete();
429
    }
430
431
    /**
432
     * {@inheritdoc}
433
     */
434
    public static function getActiveStatuses()
435
    {
436
        return array('public');
437
    }
438
439
    /**
440
     * {@inheritdoc}
441
     */
442
    public static function getLazyColumns()
443
    {
444
        return null;
445
    }
446
447
    /**
448
     * Get all the bans in the database that aren't disabled or deleted
449
     * @return Ban[] An array of ban objects
450
     */
451
    public static function getBans()
452
    {
453
        return self::arrayIdToModel(self::fetchIds("ORDER BY updated DESC"));
454
    }
455
456
    /**
457
     * Get an active ban for the player
458
     * @param  int      $playerId The player's ID
459
     * @return Ban|null null if the player isn't currently banned
460
     */
461 38
    public static function getBan($playerId)
462
    {
463 38
        $bans = self::fetchIdsFrom('player', array($playerId), false, "AND (expiration IS NULL OR expiration > UTC_TIMESTAMP())");
464
465 38
        if (empty($bans)) {
466 38
            return null;
467
        }
468
469 2
        return self::get($bans[0]);
470
    }
471
}
472