1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SilverStripe\Forum\Models; |
4
|
|
|
|
5
|
|
|
use SilverStripe\Control\Controller; |
6
|
|
|
use SilverStripe\Forum\Pages\Forum; |
7
|
|
|
use SilverStripe\ORM\FieldType\DBBoolean; |
8
|
|
|
use SilverStripe\ORM\FieldType\DBInt; |
9
|
|
|
use SilverStripe\ORM\FieldType\DBText; |
10
|
|
|
use SilverStripe\ORM\FieldType\DBVarchar; |
11
|
|
|
use SilverStripe\ORM\HasManyList; |
12
|
|
|
use SilverStripe\Security\Member; |
13
|
|
|
use SilverStripe\ORM\DataObject; |
14
|
|
|
use SilverStripe\Control\Session; |
15
|
|
|
use SilverStripe\Core\Convert; |
16
|
|
|
use SilverStripe\ORM\DB; |
17
|
|
|
use SilverStripe\ORM\FieldType\DBField; |
18
|
|
|
use SilverStripe\Core\Config\Config; |
19
|
|
|
use SilverStripe\Control\Email\Email; |
20
|
|
|
use SilverStripe\Control\Director; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* A representation of a forum thread. A forum thread is 1 topic on the forum |
24
|
|
|
* which has multiple posts underneath it. |
25
|
|
|
* |
26
|
|
|
* @package forum |
27
|
|
|
* @property DBVarchar Title |
28
|
|
|
* @property DBInt NumViews |
29
|
|
|
* @property DBBoolean IsSticky |
30
|
|
|
* @property DBBoolean IsReadOnly |
31
|
|
|
* @property DBBoolean IsGlobalSticky |
32
|
|
|
* @method Forum Forum |
33
|
|
|
* @method HasManyList Posts |
34
|
|
|
*/ |
35
|
|
|
class ForumThread extends DataObject |
36
|
|
|
{ |
37
|
|
|
/** @var array */ |
38
|
|
|
private static $db = array( |
39
|
|
|
"Title" => "Varchar(255)", |
40
|
|
|
"NumViews" => "Int", |
41
|
|
|
"IsSticky" => "Boolean", |
42
|
|
|
"IsReadOnly" => "Boolean", |
43
|
|
|
"IsGlobalSticky" => "Boolean" |
44
|
|
|
); |
45
|
|
|
|
46
|
|
|
/** @var array */ |
47
|
|
|
private static $has_one = array( |
48
|
|
|
'Forum' => 'Forum' |
49
|
|
|
); |
50
|
|
|
|
51
|
|
|
/** @var array */ |
52
|
|
|
private static $has_many = array( |
53
|
|
|
'Posts' => 'Post' |
54
|
|
|
); |
55
|
|
|
|
56
|
|
|
/** @var array */ |
57
|
|
|
private static $defaults = array( |
58
|
|
|
'NumViews' => 0, |
59
|
|
|
'IsSticky' => false, |
60
|
|
|
'IsReadOnly' => false, |
61
|
|
|
'IsGlobalSticky' => false |
62
|
|
|
); |
63
|
|
|
|
64
|
|
|
/** @var array */ |
65
|
|
|
private static $indexes = array( |
66
|
|
|
'IsSticky' => true, |
67
|
|
|
'IsGlobalSticky' => true |
68
|
|
|
); |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* @var null|boolean Per-request cache, whether we should display signatures on a post. |
72
|
|
|
*/ |
73
|
|
|
private static $cacheDisplaySignatures = null; |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Check if the user can create new threads and add responses |
77
|
|
|
* |
78
|
|
|
* @param null|Member $member |
79
|
|
|
* |
80
|
|
|
* @return bool |
81
|
|
|
*/ |
82
|
|
|
public function canPost($member = null) |
83
|
|
|
{ |
84
|
|
|
if (!$member) { |
85
|
|
|
$member = Member::currentUser(); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
return ($this->Forum()->canPost($member) && !$this->IsReadOnly); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Check if user can moderate this thread |
93
|
|
|
* |
94
|
|
|
* @param null|Member $member |
95
|
|
|
* |
96
|
|
|
* @return bool |
97
|
|
|
*/ |
98
|
|
|
public function canModerate($member = null) |
99
|
|
|
{ |
100
|
|
|
if (!$member) { |
101
|
|
|
$member = Member::currentUser(); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
return $this->Forum()->canModerate($member); |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Check if user can view the thread |
109
|
|
|
* |
110
|
|
|
* @param null|Member $member |
111
|
|
|
* |
112
|
|
|
* @return bool |
113
|
|
|
*/ |
114
|
|
|
public function canView($member = null) |
115
|
|
|
{ |
116
|
|
|
if (!$member) { |
117
|
|
|
$member = Member::currentUser(); |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
return $this->Forum()->canView($member); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Hook up into moderation. |
125
|
|
|
* |
126
|
|
|
* @param null|Member $member |
127
|
|
|
* |
128
|
|
|
* @return bool |
129
|
|
|
*/ |
130
|
|
|
public function canEdit($member = null) |
131
|
|
|
{ |
132
|
|
|
if (!$member) { |
133
|
|
|
$member = Member::currentUser(); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
return $this->canModerate($member); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* Hook up into moderation - users cannot delete their own posts/threads because |
141
|
|
|
* we will loose history this way. |
142
|
|
|
* |
143
|
|
|
* @param null|Member $member |
144
|
|
|
* |
145
|
|
|
* @return bool |
146
|
|
|
*/ |
147
|
|
|
public function canDelete($member = null) |
148
|
|
|
{ |
149
|
|
|
if (!$member) { |
150
|
|
|
$member = Member::currentUser(); |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
return $this->canModerate($member); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Hook up into canPost check |
158
|
|
|
* |
159
|
|
|
* @param null|Member $member |
160
|
|
|
* |
161
|
|
|
* @return bool |
162
|
|
|
*/ |
163
|
|
|
public function canCreate($member = null) |
164
|
|
|
{ |
165
|
|
|
if (!$member) { |
166
|
|
|
$member = Member::currentUser(); |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
return $this->canPost($member); |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Are Forum Signatures on Member profiles allowed. |
174
|
|
|
* This only needs to be checked once, so we cache the initial value once per-request. |
175
|
|
|
* |
176
|
|
|
* @return bool |
177
|
|
|
*/ |
178
|
|
|
public function getDisplaySignatures() |
179
|
|
|
{ |
180
|
|
|
if (isset(self::$cacheDisplaySignatures) && self::$cacheDisplaySignatures !== null) { |
181
|
|
|
return self::$cacheDisplaySignatures; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
$result = $this->Forum()->Parent()->DisplaySignatures; |
185
|
|
|
self::$cacheDisplaySignatures = $result; |
186
|
|
|
|
187
|
|
|
return $result; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* Get the latest post from this thread. Nicer way then using an control |
192
|
|
|
* from the template |
193
|
|
|
* |
194
|
|
|
* @return Post |
195
|
|
|
*/ |
196
|
|
|
public function getLatestPost() |
197
|
|
|
{ |
198
|
|
|
return Post::get()->filter(['ThreadID' => $this->ID]) |
199
|
|
|
->sort('ID DESC') |
200
|
|
|
->first(); |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* Return the first post from the thread. Useful to working out the original author |
205
|
|
|
* |
206
|
|
|
* @return Post |
207
|
|
|
*/ |
208
|
|
|
public function getFirstPost() |
209
|
|
|
{ |
210
|
|
|
return Post::get()->filter(['ThreadID' => $this->ID]) |
211
|
|
|
->sort('ID ASC') |
212
|
|
|
->first(); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Return the number of posts in this thread. We could use count on |
217
|
|
|
* the dataobject set but that is slower and causes a performance overhead |
218
|
|
|
* |
219
|
|
|
* @todo SS4 compat |
220
|
|
|
* @return int |
221
|
|
|
*/ |
222
|
|
View Code Duplication |
public function getNumPosts() |
|
|
|
|
223
|
|
|
{ |
224
|
|
|
$sqlQuery = new SQLQuery(); |
225
|
|
|
$sqlQuery->setFrom('"Post"'); |
226
|
|
|
$sqlQuery->setSelect('COUNT("Post"."ID")'); |
227
|
|
|
$sqlQuery->addInnerJoin('SilverStripe\\Security\\Member', '"Post"."AuthorID" = "Member"."ID"'); |
228
|
|
|
$sqlQuery->addWhere('"Member"."ForumStatus" = \'Normal\''); |
229
|
|
|
$sqlQuery->addWhere('"ThreadID" = ' . $this->ID); |
230
|
|
|
|
231
|
|
|
return $sqlQuery->execute()->value(); |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
/** |
235
|
|
|
* Check if they have visited this thread before. If they haven't increment |
236
|
|
|
* the NumViews value by 1 and set visited to true. |
237
|
|
|
* |
238
|
|
|
* @return void |
239
|
|
|
*/ |
240
|
|
|
public function incNumViews() |
241
|
|
|
{ |
242
|
|
|
if (Session::get('ForumViewed-' . $this->ID)) { |
243
|
|
|
return; |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
Session::set('ForumViewed-' . $this->ID, 'true'); |
247
|
|
|
|
248
|
|
|
$this->NumViews++; |
249
|
|
|
$this->write(); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
/** |
253
|
|
|
* Link to this forum thread |
254
|
|
|
* |
255
|
|
|
* @param string $action |
256
|
|
|
* @param bool $showID |
257
|
|
|
* |
258
|
|
|
* @return String |
259
|
|
|
*/ |
260
|
|
|
public function Link($action = "show", $showID = true) |
261
|
|
|
{ |
262
|
|
|
/** @var Forum $forum */ |
263
|
|
|
$forum = Forum::get()->byID($this->ForumID); |
264
|
|
|
if (!$forum) { |
265
|
|
|
user_error("Bad ForumID '$this->ForumID'", E_USER_WARNING); |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
$baseLink = $forum->Link(); |
269
|
|
|
|
270
|
|
|
return ($action) ? Controller::join_links($baseLink, $action, ($showID) ? $this->ID : null) : $baseLink; |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
/** |
274
|
|
|
* Check to see if the user has subscribed to this thread |
275
|
|
|
* |
276
|
|
|
* @return bool |
277
|
|
|
*/ |
278
|
|
|
public function getHasSubscribed() |
279
|
|
|
{ |
280
|
|
|
$member = Member::currentUser(); |
281
|
|
|
|
282
|
|
|
return ($member) ? ForumThreadSubscription::alreadySubscribed($this->ID, $member->ID) : false; |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* Before deleting the thread remove all the posts |
287
|
|
|
* |
288
|
|
|
* @return void |
289
|
|
|
*/ |
290
|
|
|
public function onBeforeDelete() |
291
|
|
|
{ |
292
|
|
|
parent::onBeforeDelete(); |
293
|
|
|
|
294
|
|
|
if ($posts = $this->Posts()) { |
295
|
|
|
foreach ($posts as $post) { |
296
|
|
|
// attachment deletion is handled by the {@link Post::onBeforeDelete} |
297
|
|
|
$post->delete(); |
298
|
|
|
} |
299
|
|
|
} |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* Ensure the correct ForumID is applied to the record |
304
|
|
|
* |
305
|
|
|
* @return void |
306
|
|
|
*/ |
307
|
|
|
public function onAfterWrite() |
308
|
|
|
{ |
309
|
|
|
if ($this->isChanged('ForumID', 2)) { |
310
|
|
|
$posts = $this->Posts(); |
311
|
|
|
if ($posts && $posts->count()) { |
312
|
|
|
foreach ($posts as $post) { |
313
|
|
|
$post->ForumID = $this->ForumID; |
314
|
|
|
$post->write(); |
315
|
|
|
} |
316
|
|
|
} |
317
|
|
|
} |
318
|
|
|
parent::onAfterWrite(); |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
/** |
322
|
|
|
* @return DBText |
323
|
|
|
*/ |
324
|
|
|
public function getEscapedTitle() |
325
|
|
|
{ |
326
|
|
|
return DBField::create_field('Text', $this->dbObject('Title')->XML()); |
327
|
|
|
} |
328
|
|
|
} |
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.