1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file contains functionality relating to database objects that have a custom URL such as teams and 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 Model that has a URL and an alias |
11
|
|
|
* @package BZiON\Models |
12
|
|
|
*/ |
13
|
|
|
abstract class AliasModel extends UrlModel implements NamedModel |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* The name of the object |
17
|
|
|
* @var string |
18
|
|
|
*/ |
19
|
|
|
protected $name; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* A unique URL-friendly identifier for the object |
23
|
|
|
* @var string |
24
|
|
|
*/ |
25
|
|
|
protected $alias; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Get the name of the object |
29
|
|
|
* @return string |
30
|
|
|
*/ |
31
|
39 |
|
public function getName() |
32
|
|
|
{ |
33
|
39 |
|
return $this->name; |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Get the name of the team, safe for use in your HTML |
38
|
|
|
* |
39
|
|
|
* @return string The name of the team |
40
|
|
|
*/ |
41
|
1 |
|
public function getEscapedName() |
42
|
|
|
{ |
43
|
1 |
|
if (!$this->valid) { |
44
|
|
|
return "<em>None</em>"; |
45
|
|
|
} |
46
|
|
|
|
47
|
1 |
|
return $this->escape($this->name); |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Change the object's name |
52
|
|
|
* |
53
|
|
|
* @return self |
54
|
|
|
*/ |
55
|
|
|
public function setName($name) |
56
|
|
|
{ |
57
|
|
|
$this->updateProperty($this->name, 'name', $name); |
58
|
|
|
$this->resetAlias(); |
59
|
|
|
|
60
|
|
|
return $this; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Get an object's alias |
65
|
|
|
* @return string|int The alias (or ID if the alias doesn't exist) |
66
|
|
|
*/ |
67
|
5 |
|
public function getAlias() |
68
|
|
|
{ |
69
|
5 |
|
if ($this->alias !== null && $this->alias != "") { |
70
|
4 |
|
return $this->alias; |
71
|
|
|
} |
72
|
|
|
|
73
|
1 |
|
return $this->getId(); |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Set a model's alias |
78
|
|
|
* @param string $alias The new alias |
79
|
|
|
* @return void |
80
|
|
|
*/ |
81
|
|
|
public function setAlias($alias) |
82
|
|
|
{ |
83
|
|
|
$this->updateProperty($this->alias, 'alias', $alias); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* Reset a model's alias based on its name |
88
|
|
|
* @return self |
89
|
|
|
*/ |
90
|
38 |
|
public function resetAlias() |
91
|
|
|
{ |
92
|
38 |
|
$alias = static::generateAlias($this->name, $this->id); |
93
|
|
|
|
94
|
38 |
|
return $this->updateProperty($this->alias, 'alias', $alias); |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* {@inheritdoc} |
99
|
|
|
* |
100
|
1 |
|
* @todo Redirect models with wrong alias when bzion.site.url_type === 'permalink' |
101
|
|
|
*/ |
102
|
1 |
|
public function getURL($action = 'show', $absolute = false, $params = array(), $vanity = false) |
103
|
|
|
{ |
104
|
|
|
if (!$this->isValid()) { |
105
|
|
|
return ""; |
106
|
1 |
|
} |
107
|
|
|
|
108
|
|
|
$forbiddenAliases = array('edit', 'delete', 'kick', 'invite', 'change-leader', 'matches', 'members'); |
109
|
1 |
|
|
110
|
|
|
if (Service::getParameter('bzion.site.url_type') === 'permalink' && $this instanceof DuplexUrlInterface && !$vanity) { |
111
|
1 |
|
// Add both the alias and the ID to the URL if the model supports them |
112
|
|
|
|
113
|
|
|
// Make sure the alias is not forbidden |
114
|
1 |
View Code Duplication |
if (in_array(strtolower($action), $forbiddenAliases)) { |
|
|
|
|
115
|
|
|
$alias = $this->getId(); |
116
|
|
|
} else { |
117
|
|
|
$alias = $this->alias; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
// Add the alias to the query parameters |
121
|
|
|
$params = $params + array('alias' => $alias); |
122
|
3 |
|
$alias = $this->getId(); |
123
|
|
View Code Duplication |
} else { |
|
|
|
|
124
|
3 |
|
if (in_array(strtolower($action), $forbiddenAliases)) { |
125
|
|
|
$alias = $this->getId(); |
126
|
|
|
} else { |
127
|
|
|
$alias = $this->getAlias(); |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
|
131
|
3 |
|
return $this->getLink($alias, $action, $absolute, $params); |
132
|
|
|
} |
133
|
3 |
|
|
134
|
|
|
/** |
135
|
2 |
|
* Gets an entity from the supplied alias |
136
|
|
|
* @param string $alias The object's alias |
137
|
|
|
* @return AliasModel |
138
|
2 |
|
*/ |
139
|
|
|
public static function fetchFromAlias($alias) |
140
|
|
|
{ |
141
|
|
|
return static::get(self::fetchIdFrom($alias, "alias")); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* {@inheritdoc} |
146
|
|
|
* @return AliasModel |
147
|
|
|
*/ |
148
|
|
|
public static function fetchFromSlug($slug) |
149
|
39 |
|
{ |
150
|
|
|
if (ctype_digit((string) $slug)) { |
151
|
|
|
// Slug is an integer, we can fetch by ID |
152
|
39 |
|
return static::get((int) $slug); |
153
|
|
|
} else { |
154
|
|
|
// Slug is something else, we can fetch by alias |
155
|
39 |
|
return self::fetchFromAlias($slug); |
156
|
|
|
} |
157
|
39 |
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
39 |
|
* Generate a URL-friendly unique alias for an object name |
161
|
|
|
* |
162
|
39 |
|
* @param string $name The original object name |
163
|
|
|
* @param int|null $id The ID of the object, if it's being edited and not created |
164
|
|
|
* @return string|null The generated alias, or Null if we couldn't make one |
165
|
1 |
|
*/ |
166
|
|
|
public static function generateAlias($name, $id = null) |
167
|
|
|
{ |
168
|
|
|
// Convert name to lowercase |
169
|
|
|
$name = strtolower($name); |
170
|
|
|
|
171
|
39 |
|
// List of characters which should be converted to dashes |
172
|
1 |
|
$makeDash = array(' ', '_'); |
173
|
|
|
|
174
|
|
|
$name = str_replace($makeDash, '-', $name); |
175
|
39 |
|
|
176
|
|
|
// Only keep letters, numbers and dashes - delete everything else |
177
|
|
|
$name = preg_replace("/[^a-zA-Z\-0-9]+/", "", $name); |
178
|
|
|
|
179
|
|
|
if (str_replace('-', '', $name) == '') { |
180
|
|
|
// The name only contains symbols or Unicode characters! |
181
|
|
|
// This means we can't convert it to an alias |
182
|
|
|
return null; |
183
|
|
|
} |
184
|
|
|
|
185
|
39 |
|
// An alias name can't only contain numbers, because it will be |
186
|
|
|
// indistinguishable from an ID. If it does, add a dash in the end. |
187
|
|
|
// Also prevent aliases from taking names such as "new", |
188
|
39 |
|
while (preg_match("/^[0-9]+$/", $name)) { |
189
|
39 |
|
$name = $name . '-'; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
return self::getUniqueAlias($name, ($id) ?: 0); |
193
|
39 |
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* Make sure that the generated alias provided is unique |
197
|
|
|
* |
198
|
39 |
|
* @param string $alias The alias |
199
|
39 |
|
* @param int $id The ID of the object, if it's being edited and not created |
200
|
39 |
|
* @return string An alias that is guaranteed to be unique |
201
|
39 |
|
*/ |
202
|
39 |
|
private static function getUniqueAlias($alias, $id = 0) |
203
|
|
|
{ |
204
|
|
|
// Try to find duplicates |
205
|
26 |
|
$db = Database::getInstance(); |
206
|
|
|
$result = $db->query("SELECT alias FROM " . static::TABLE . " WHERE id != ? AND alias REGEXP ?", array($id, "^" . $alias . "[0-9]*$")); |
207
|
|
|
|
208
|
39 |
|
// Convert the multi-dimensional array that $db->query() gave us into |
209
|
|
|
// a single-dimensional one. |
210
|
|
|
$aliases = (is_array($result)) ? array_column($result, 'alias') : array(); |
211
|
|
|
|
212
|
|
|
// If there's already an entry with the alias we generated, put a number |
213
|
|
|
// in the end of it and keep incrementing it until there is we find |
214
|
|
|
// an open spot. |
215
|
|
|
$currentAlias = $alias; |
216
|
|
|
for ($i = 2;; ++$i) { |
217
|
|
|
if (!in_array($currentAlias, $aliases) |
218
|
|
|
&& !in_array($currentAlias, static::getDisallowedAliases())) { |
219
|
|
|
break; |
220
|
|
|
} |
221
|
|
|
|
222
|
39 |
|
$currentAlias = $alias . $i; |
223
|
|
|
} |
224
|
39 |
|
|
225
|
|
|
return $currentAlias; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* Get a list of aliases that should not be given to objects |
230
|
|
|
* |
231
|
|
|
* For example, you want to prevent teams from getting the "new" alias. |
232
|
|
|
* Otherwise, the team's link would be http://example.com/bzion/teams/new, |
233
|
|
|
* and the user would go to the team creation page instead of the team's page. |
234
|
|
|
* Disallowed aliases will have a number appended, so the URL would be |
235
|
|
|
* http://example.com/bzion/teams/new2 |
236
|
|
|
* |
237
|
|
|
* @return string[] |
238
|
|
|
*/ |
239
|
|
|
protected static function getDisallowedAliases() |
240
|
|
|
{ |
241
|
|
|
return array('new'); |
242
|
|
|
} |
243
|
|
|
} |
244
|
|
|
|
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.