1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Handles the retrieving and display of a users posts, attachments, stats, permissions |
5
|
|
|
* warnings and the like |
6
|
|
|
* |
7
|
|
|
* @package ElkArte Forum |
8
|
|
|
* @copyright ElkArte Forum contributors |
9
|
|
|
* @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
10
|
|
|
* |
11
|
|
|
* This file contains code covered by: |
12
|
|
|
* copyright: 2011 Simple Machines (http://www.simplemachines.org) |
13
|
|
|
* |
14
|
|
|
* @version 2.0 dev |
15
|
|
|
* |
16
|
|
|
*/ |
17
|
|
|
|
18
|
|
|
namespace ElkArte\Profile; |
19
|
|
|
|
20
|
|
|
use BBC\ParserWrapper; |
21
|
|
|
use ElkArte\AbstractController; |
22
|
|
|
use ElkArte\Action; |
23
|
|
|
use ElkArte\Exceptions\Exception; |
24
|
|
|
use ElkArte\Helper\FileFunctions; |
25
|
|
|
use ElkArte\Helper\Util; |
26
|
|
|
use ElkArte\Languages\Txt; |
27
|
|
|
use ElkArte\Member; |
28
|
|
|
use ElkArte\MembersList; |
29
|
|
|
use ElkArte\MessagesDelete; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Access all profile summary areas for a user including overall summary, |
33
|
|
|
* post listing, attachment listing, user statistics user permissions, user warnings |
34
|
|
|
*/ |
35
|
|
|
class ProfileInfo extends AbstractController |
36
|
|
|
{ |
37
|
|
|
/** @var int Member id for the profile being worked with */ |
38
|
|
|
private $_memID = 0; |
39
|
|
|
|
40
|
|
|
/** @var Member The \ElkArte\Member object is stored here to avoid some global */ |
41
|
|
|
private $_profile; |
42
|
|
|
|
43
|
|
|
/** @var array Holds the current summary tabs to load */ |
44
|
|
|
private $_summary_areas; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Called before all other methods when coming from the dispatcher or |
48
|
|
|
* action class. |
49
|
|
|
* |
50
|
|
|
* - If you initiate the class outside of those methods, call this method. |
51
|
|
|
* or setup the class yourself or fall awaits. |
52
|
|
|
*/ |
53
|
|
|
public function pre_dispatch() |
54
|
|
|
{ |
55
|
|
|
global $context; |
56
|
|
|
|
57
|
|
|
require_once(SUBSDIR . '/Profile.subs.php'); |
58
|
|
|
|
59
|
|
|
$this->_memID = currentMemberID(); |
60
|
|
|
$this->_profile = MembersList::get($this->_memID); |
|
|
|
|
61
|
|
|
|
62
|
|
|
if (!isset($context['user']['is_owner'])) |
63
|
|
|
{ |
64
|
|
|
$context['user']['is_owner'] = $this->_memID === (int) $this->user->id; |
|
|
|
|
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
// Attempt to load the member's profile data. |
68
|
|
|
if ($this->_profile->isEmpty()) |
69
|
|
|
{ |
70
|
|
|
throw new Exception('not_a_user', false); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
$this->_profile->loadContext(); |
74
|
|
|
|
75
|
|
|
Txt::load('Profile'); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Intended as entry point which delegates to methods in this class... |
80
|
|
|
* |
81
|
|
|
* - But here, today, for now, the methods are mainly called from other places |
82
|
|
|
* like menu picks and the like. |
83
|
|
|
*/ |
84
|
|
|
public function action_index() |
85
|
|
|
{ |
86
|
|
|
global $context; |
87
|
|
|
|
88
|
|
|
// What do we do, do you even know what you do? |
89
|
|
|
$subActions = [ |
90
|
|
|
'buddies' => [$this, 'action_profile_buddies'], |
91
|
|
|
'recent' => [$this, 'action_profile_recent'], |
92
|
|
|
'summary' => ['controller' => Profile::class, 'function' => 'action_index'], |
93
|
|
|
]; |
94
|
|
|
|
95
|
|
|
// Action control |
96
|
|
|
$action = new Action('profile_info'); |
97
|
|
|
|
98
|
|
|
// By default we want the summary |
99
|
|
|
$subAction = $action->initialize($subActions, 'summary'); |
100
|
|
|
|
101
|
|
|
// Final bits |
102
|
|
|
$context['sub_action'] = $subAction; |
103
|
|
|
|
104
|
|
|
// Call the right function for this sub-action. |
105
|
|
|
$action->dispatch($subAction); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* View the user profile summary. |
110
|
|
|
* |
111
|
|
|
* @uses ProfileInfo template |
112
|
|
|
*/ |
113
|
|
|
public function action_summary() |
114
|
|
|
{ |
115
|
|
|
global $context, $modSettings; |
116
|
|
|
|
117
|
|
|
// To make tabs work, we need jQueryUI |
118
|
|
|
$modSettings['jquery_include_ui'] = true; |
119
|
|
|
$context['start_tabs'] = true; |
120
|
|
|
loadCSSFile('jquery.ui.tabs.css'); |
121
|
|
|
|
122
|
|
|
theme()->getTemplates()->load('ProfileInfo'); |
123
|
|
|
Txt::load('Profile'); |
124
|
|
|
|
125
|
|
|
// Set a canonical URL for this page. |
126
|
|
|
$context['canonical_url'] = getUrl('action', ['action' => 'profile', 'u' => $this->_memID]); |
127
|
|
|
|
128
|
|
|
// Are there things we don't show? |
129
|
|
|
$context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : []; |
130
|
|
|
|
131
|
|
|
// Disable Menu tab |
132
|
|
|
$context[$context['profile_menu_name']]['tab_data'] = []; |
133
|
|
|
|
134
|
|
|
// Profile summary tabs, like Summary, Recent, Buddies |
135
|
|
|
$this->_register_summarytabs(); |
136
|
|
|
|
137
|
|
|
// Load in everything we know about the user to preload the summary tab |
138
|
|
|
$this->_define_user_values(); |
139
|
|
|
$this->_load_summary(); |
140
|
|
|
|
141
|
|
|
// To finish this off, custom profile fields. |
142
|
|
|
$profileFields = new ProfileFields(); |
143
|
|
|
$profileFields->loadCustomFields($this->_memID); |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* Prepares the tabs for the profile summary page |
148
|
|
|
* |
149
|
|
|
* What it does: |
150
|
|
|
* |
151
|
|
|
* - Tab information for use in the summary page |
152
|
|
|
* - Each tab template defines a div, the value of which are the template(s) to load in that div |
153
|
|
|
* - array(array(1, 2), array(3, 4)) <div>template 1, template 2</div><div>template 3 template 4</div> |
154
|
|
|
* - Templates are named template_profile_block_YOURNAME |
155
|
|
|
* - Tabs with href defined will not preload/create any page divs but instead be loaded via ajax |
156
|
|
|
*/ |
157
|
|
|
private function _register_summarytabs() |
158
|
|
|
{ |
159
|
|
|
global $txt, $context, $modSettings; |
160
|
|
|
|
161
|
|
|
$context['summarytabs'] = [ |
162
|
|
|
'summary' => [ |
163
|
|
|
'name' => $txt['summary'], |
164
|
|
|
'templates' => [ |
165
|
|
|
['summary', 'user_info'], |
166
|
|
|
['contact', 'other_info'], |
167
|
|
|
['user_customprofileinfo', 'moderation'], |
168
|
|
|
], |
169
|
|
|
'active' => true, |
170
|
|
|
], |
171
|
|
|
'recent' => [ |
172
|
|
|
'name' => $txt['profile_recent_activity'], |
173
|
|
|
'templates' => ['posts', 'topics', 'attachments'], |
174
|
|
|
'active' => true, |
175
|
|
|
'href' => getUrl('action', ['action' => 'profileinfo', 'sa' => 'recent', 'api' => 'html', 'u' => $this->_memID, '{session_data}']), |
176
|
|
|
], |
177
|
|
|
'buddies' => [ |
178
|
|
|
'name' => $txt['buddies'], |
179
|
|
|
'templates' => ['buddies'], |
180
|
|
|
'active' => !empty($modSettings['enable_buddylist']) && $context['user']['is_owner'], |
181
|
|
|
'href' => getUrl('action', ['action' => 'profileinfo', 'sa' => 'buddies', 'api' => 'html', 'u' => $this->_memID, '{session_data}']), |
182
|
|
|
] |
183
|
|
|
]; |
184
|
|
|
|
185
|
|
|
// Let addons add or remove to the tabs array |
186
|
|
|
call_integration_hook('integrate_profile_summary', [$this->_memID]); |
187
|
|
|
|
188
|
|
|
// Go forward with whats left after integration adds or removes |
189
|
|
|
$summary_areas = ''; |
190
|
|
|
foreach ($context['summarytabs'] as $id => $tab) |
191
|
|
|
{ |
192
|
|
|
// If the tab is active we add it |
193
|
|
|
if (!$tab['active']) |
194
|
|
|
{ |
195
|
|
|
unset($context['summarytabs'][$id]); |
196
|
|
|
} |
197
|
|
|
else |
198
|
|
|
{ |
199
|
|
|
// All the active templates, used to prevent processing data we don't need |
200
|
|
|
foreach ($tab['templates'] as $template) |
201
|
|
|
{ |
202
|
|
|
$summary_areas .= is_array($template) ? implode(',', $template) : ',' . $template; |
203
|
|
|
} |
204
|
|
|
} |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
$this->_summary_areas = explode(',', $summary_areas); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* Sets in to context what we know about a given user |
212
|
|
|
* |
213
|
|
|
* - Defines various user permissions for profile views |
214
|
|
|
*/ |
215
|
|
|
private function _define_user_values() |
216
|
|
|
{ |
217
|
|
|
global $context, $modSettings, $txt; |
218
|
|
|
|
219
|
|
|
// Set up the context stuff and load the user. |
220
|
|
|
$context += [ |
221
|
|
|
'page_title' => sprintf($txt['profile_of_username'], $this->_profile['name']), |
222
|
|
|
'can_send_pm' => allowedTo('pm_send'), |
223
|
|
|
'can_send_email' => allowedTo('send_email_to_members'), |
224
|
|
|
'can_have_buddy' => allowedTo('profile_identity_own') && !empty($modSettings['enable_buddylist']), |
225
|
|
|
'can_issue_warning' => featureEnabled('w') && allowedTo('issue_warning') && !empty($modSettings['warning_enable']), |
226
|
|
|
'can_view_warning' => featureEnabled('w') && ((allowedTo('issue_warning') && !$context['user']['is_owner']) || (!empty($modSettings['warning_show']) && ($modSettings['warning_show'] > 1 || $context['user']['is_owner']))) |
227
|
|
|
]; |
228
|
|
|
|
229
|
|
|
// @critical: potential problem here |
230
|
|
|
$context['member'] = $this->_profile; |
231
|
|
|
$context['member']->loadContext(); |
232
|
|
|
$context['member']['id'] = $this->_memID; |
233
|
|
|
|
234
|
|
|
// Is the signature even enabled on this forum? |
235
|
|
|
$context['signature_enabled'] = strpos($modSettings['signature_settings'], "1") === 0; |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* Loads the information needed to create the profile summary view |
240
|
|
|
*/ |
241
|
|
|
private function _load_summary() |
242
|
|
|
{ |
243
|
|
|
// Load all areas of interest in to context for template use |
244
|
|
|
$this->_determine_warning_level(); |
245
|
|
|
$this->_determine_posts_per_day(); |
246
|
|
|
$this->_determine_age_birth(); |
247
|
|
|
$this->_determine_member_ip(); |
248
|
|
|
$this->_determine_member_action(); |
249
|
|
|
$this->_determine_member_activation(); |
250
|
|
|
$this->_determine_member_bans(); |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* If they have been disciplined, show the warning level for those that can see it. |
255
|
|
|
*/ |
256
|
|
|
private function _determine_warning_level() |
257
|
|
|
{ |
258
|
|
|
global $modSettings, $context, $txt; |
259
|
|
|
|
260
|
|
|
// See if they have broken any warning levels... |
261
|
|
|
if (!empty($modSettings['warning_mute']) && $modSettings['warning_mute'] <= $context['member']['warning']) |
262
|
|
|
{ |
263
|
|
|
$context['warning_status'] = $txt['profile_warning_is_muted']; |
264
|
|
|
} |
265
|
|
|
elseif (!empty($modSettings['warning_moderate']) && $modSettings['warning_moderate'] <= $context['member']['warning']) |
266
|
|
|
{ |
267
|
|
|
$context['warning_status'] = $txt['profile_warning_is_moderation']; |
268
|
|
|
} |
269
|
|
|
elseif (!empty($modSettings['warning_watch']) && $modSettings['warning_watch'] <= $context['member']['warning']) |
270
|
|
|
{ |
271
|
|
|
$context['warning_status'] = $txt['profile_warning_is_watch']; |
272
|
|
|
} |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* Gives their spam level as a posts per day kind of statistic |
277
|
|
|
*/ |
278
|
|
|
private function _determine_posts_per_day() |
279
|
|
|
{ |
280
|
|
|
global $context, $txt; |
281
|
|
|
|
282
|
|
|
// They haven't even been registered for a full day!? |
283
|
|
|
$days_registered = (int) ((time() - $this->_profile['registered_raw']) / (3600 * 24)); |
284
|
|
|
if (empty($this->_profile['date_registered']) || $days_registered < 1) |
285
|
|
|
{ |
286
|
|
|
$context['member']['posts_per_day'] = $txt['not_applicable']; |
287
|
|
|
} |
288
|
|
|
else |
289
|
|
|
{ |
290
|
|
|
$context['member']['posts_per_day'] = comma_format($context['member']['real_posts'] / $days_registered, 3); |
291
|
|
|
} |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* Show age and birthday data if applicable. |
296
|
|
|
*/ |
297
|
|
|
private function _determine_age_birth() |
298
|
|
|
{ |
299
|
|
|
global $context, $txt; |
300
|
|
|
|
301
|
|
|
// Set the age... |
302
|
|
|
if (empty($context['member']['birth_date'])) |
303
|
|
|
{ |
304
|
|
|
$context['member']['age'] = $txt['not_applicable']; |
305
|
|
|
$context['member']['today_is_birthday'] = false; |
306
|
|
|
} |
307
|
|
|
else |
308
|
|
|
{ |
309
|
|
|
[$birth_year, $birth_month, $birth_day] = sscanf($context['member']['birth_date'], '%d-%d-%d'); |
310
|
|
|
$datearray = getdate(forum_time()); |
311
|
|
|
$context['member']['age'] = $birth_year <= 4 ? $txt['not_applicable'] : $datearray['year'] - $birth_year - (($datearray['mon'] > $birth_month || ($datearray['mon'] === $birth_month && $datearray['mday'] >= $birth_day)) ? 0 : 1); |
312
|
|
|
$context['member']['today_is_birthday'] = $datearray['mon'] === $birth_month && $datearray['mday'] === $birth_day; |
313
|
|
|
|
314
|
|
|
} |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* Show IP and hostname information for the users current IP of record. |
319
|
|
|
*/ |
320
|
|
|
private function _determine_member_ip() |
321
|
|
|
{ |
322
|
|
|
global $context, $modSettings; |
323
|
|
|
|
324
|
|
|
if (allowedTo('moderate_forum')) |
325
|
|
|
{ |
326
|
|
|
// Make sure it's a valid ip address; otherwise, don't bother... |
327
|
|
|
if (empty($modSettings['disableHostnameLookup']) && filter_var($this->_profile['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) |
328
|
|
|
{ |
329
|
|
|
$context['member']['hostname'] = host_from_ip($this->_profile['ip']); |
330
|
|
|
} |
331
|
|
|
else |
332
|
|
|
{ |
333
|
|
|
$context['member']['hostname'] = ''; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
$context['can_see_ip'] = true; |
337
|
|
|
} |
338
|
|
|
else |
339
|
|
|
{ |
340
|
|
|
$context['can_see_ip'] = false; |
341
|
|
|
} |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* Determines what action user is "doing" at the time of the summary view |
346
|
|
|
*/ |
347
|
|
|
private function _determine_member_action() |
348
|
|
|
{ |
349
|
|
|
global $context, $modSettings; |
350
|
|
|
|
351
|
|
|
if (!empty($modSettings['who_enabled']) && $context['member']['online']['is_online']) |
352
|
|
|
{ |
353
|
|
|
include_once(SUBSDIR . '/Who.subs.php'); |
354
|
|
|
$action = determineActions($this->_profile['url']); |
355
|
|
|
Txt::load('index'); |
356
|
|
|
|
357
|
|
|
if ($action !== false) |
|
|
|
|
358
|
|
|
{ |
359
|
|
|
$context['member']['action'] = $action; |
360
|
|
|
} |
361
|
|
|
} |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* Checks if hte member is activated |
366
|
|
|
* |
367
|
|
|
* - Creates a link if the viewing member can activate a user |
368
|
|
|
*/ |
369
|
|
|
private function _determine_member_activation() |
370
|
|
|
{ |
371
|
|
|
global $context, $txt; |
372
|
|
|
|
373
|
|
|
// If the user is awaiting activation, and the viewer has permission - setup some activation context messages. |
374
|
|
|
if ($context['member']['is_activated'] % 10 !== 1 && allowedTo('moderate_forum')) |
375
|
|
|
{ |
376
|
|
|
$context['activate_type'] = $context['member']['is_activated']; |
377
|
|
|
|
378
|
|
|
// What should the link text be? |
379
|
|
|
$context['activate_link_text'] = in_array((int) $context['member']['is_activated'], [3, 4, 5, 13, 14, 15]) |
380
|
|
|
? $txt['account_approve'] |
381
|
|
|
: $txt['account_activate']; |
382
|
|
|
|
383
|
|
|
// Should we show a custom message? |
384
|
|
|
$context['activate_message'] = $txt['account_activate_method_' . $context['member']['is_activated'] % 10] ?? $txt['account_not_activated']; |
385
|
|
|
|
386
|
|
|
$context['activate_url'] = getUrl('action', ['action' => 'profile', 'save', 'area' => 'activateaccount', 'u' => $this->_memID, '{session_data}', $context['profile-aa' . $this->_memID . '_token_var'] => $context['profile-aa' . $this->_memID . '_token']]); |
387
|
|
|
} |
388
|
|
|
} |
389
|
|
|
|
390
|
|
|
/** |
391
|
|
|
* Checks if a member has been banned |
392
|
|
|
*/ |
393
|
|
|
private function _determine_member_bans() |
394
|
|
|
{ |
395
|
|
|
global $context; |
396
|
|
|
|
397
|
|
|
// How about, are they banned? |
398
|
|
|
if (allowedTo('moderate_forum')) |
399
|
|
|
{ |
400
|
|
|
require_once(SUBSDIR . '/Bans.subs.php'); |
401
|
|
|
|
402
|
|
|
$hostname = empty($context['member']['hostname']) ? '' : $context['member']['hostname']; |
403
|
|
|
$email = empty($context['member']['email']) ? '' : $context['member']['email']; |
404
|
|
|
$context['member']['bans'] = BanCheckUser($this->_memID, $hostname, $email); |
405
|
|
|
|
406
|
|
|
// Can they edit the ban? |
407
|
|
|
$context['can_edit_ban'] = allowedTo('manage_bans'); |
408
|
|
|
} |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
/** |
412
|
|
|
* Show all posts by the current user. |
413
|
|
|
* |
414
|
|
|
* @todo This function needs to be split up properly. |
415
|
|
|
*/ |
416
|
|
|
public function action_showPosts() |
417
|
|
|
{ |
418
|
|
|
global $txt, $modSettings, $context, $board; |
419
|
|
|
|
420
|
|
|
// Some initial context. |
421
|
|
|
$context['start'] = $this->_req->getQuery('start', 'intval', 0); |
422
|
|
|
$context['current_member'] = $this->_memID; |
423
|
|
|
|
424
|
|
|
// What are we viewing |
425
|
|
|
$action = $this->_req->getQuery('sa', 'trim', ''); |
426
|
|
|
$action_title = ['messages' => 'Messages', 'attach' => 'Attachments', 'topics' => 'Topics', 'unwatchedtopics' => 'Unwatched']; |
427
|
|
|
$action_title = $action_title[$action] ?? 'Posts'; |
428
|
|
|
|
429
|
|
|
theme()->getTemplates()->load('ProfileInfo'); |
430
|
|
|
|
431
|
|
|
// Create the tabs for the template. |
432
|
|
|
$context[$context['profile_menu_name']]['object']->prepareTabData([ |
433
|
|
|
'title' => $txt['show' . $action_title], |
434
|
|
|
'description' => $txt['show' . $action_title . '_help'] ?? sprintf($txt['showGeneric_help'], $txt['show' . $action_title]), |
435
|
|
|
'class' => 'i-post-text', |
436
|
|
|
]); |
437
|
|
|
|
438
|
|
|
// Set the page title |
439
|
|
|
$context['page_title'] = $txt['showPosts'] . ' - ' . $this->_profile['real_name']; |
440
|
|
|
|
441
|
|
|
// Is the load average too high to allow searching just now? |
442
|
|
|
if ($this->isOverLoadAverage()) |
443
|
|
|
{ |
444
|
|
|
throw new Exception('loadavg_show_posts_disabled', false); |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
// If we're specifically dealing with attachments use that function! |
448
|
|
|
if ($action === 'attach') |
449
|
|
|
{ |
450
|
|
|
$this->action_showAttachments(); |
451
|
|
|
return; |
452
|
|
|
} |
453
|
|
|
|
454
|
|
|
// Instead, if we're dealing with unwatched topics (and the feature is enabled) use that other function. |
455
|
|
|
if ($action === 'unwatchedtopics' && $modSettings['enable_unwatch']) |
456
|
|
|
{ |
457
|
|
|
$this->action_showUnwatched(); |
458
|
|
|
return; |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
// Are we just viewing topics? |
462
|
|
|
$context['is_topics'] = $action === 'topics'; |
463
|
|
|
|
464
|
|
|
// If just deleting a message, do it and then redirect back. |
465
|
|
|
if (isset($this->_req->query->delete) && !$context['is_topics']) |
466
|
|
|
{ |
467
|
|
|
checkSession('get'); |
468
|
|
|
|
469
|
|
|
// We can be lazy, since removeMessage() will check the permissions for us. |
470
|
|
|
$remover = new MessagesDelete($modSettings['recycle_enable'], $modSettings['recycle_board']); |
471
|
|
|
$remover->removeMessage((int) $this->_req->query->delete); |
472
|
|
|
|
473
|
|
|
// Back to... where we are now ;). |
474
|
|
|
redirectexit('action=profile;u=' . $this->_memID . ';area=showposts;start=' . $context['start']); |
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
$msgCount = $context['is_topics'] ? count_user_topics($this->_memID, $board) : count_user_posts($this->_memID, $board); |
478
|
|
|
|
479
|
|
|
[$min_msg_member, $max_msg_member] = findMinMaxUserMessage($this->_memID, $board); |
480
|
|
|
$range_limit = ''; |
481
|
|
|
$maxIndex = (int) $modSettings['defaultMaxMessages']; |
482
|
|
|
|
483
|
|
|
// Make sure the starting place makes sense and construct our friend the page index. |
484
|
|
|
$context['page_index'] = constructPageIndex('{scripturl}?action=profile;u=' . $this->_memID . ';area=showposts' . ($context['is_topics'] ? ';sa=topics' : ';sa=messages') . (empty($board) ? '' : ';board=' . $board), $context['start'], $msgCount, $maxIndex); |
485
|
|
|
$context['current_page'] = $context['start'] / $maxIndex; |
486
|
|
|
|
487
|
|
|
// Reverse the query if we're past 50% of the pages for better performance. |
488
|
|
|
$start = $context['start']; |
489
|
|
|
$reverse = $this->_req->getQuery('start', 'intval', 0) > $msgCount / 2; |
490
|
|
|
if ($reverse) |
491
|
|
|
{ |
492
|
|
|
$maxIndex = $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] + 1 && $msgCount > $context['start'] ? $msgCount - $context['start'] : (int) $modSettings['defaultMaxMessages']; |
493
|
|
|
$start = $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] + 1 || $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] ? 0 : $msgCount - $context['start'] - $modSettings['defaultMaxMessages']; |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
// Guess the range of messages to be shown to help minimize what the query needs to do |
497
|
|
|
if ($msgCount > 1000) |
498
|
|
|
{ |
499
|
|
|
$margin = floor(($max_msg_member - $min_msg_member) * (($start + $modSettings['defaultMaxMessages']) / $msgCount) + .1 * ($max_msg_member - $min_msg_member)); |
500
|
|
|
|
501
|
|
|
// Make a bigger margin for topics only. |
502
|
|
|
if ($context['is_topics']) |
503
|
|
|
{ |
504
|
|
|
$margin *= 5; |
505
|
|
|
$range_limit = $reverse ? 't.id_first_msg < ' . ($min_msg_member + $margin) : 't.id_first_msg > ' . ($max_msg_member - $margin); |
506
|
|
|
} |
507
|
|
|
else |
508
|
|
|
{ |
509
|
|
|
$range_limit = $reverse ? 'm.id_msg < ' . ($min_msg_member + $margin) : 'm.id_msg > ' . ($max_msg_member - $margin); |
510
|
|
|
} |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
// Find this user's posts or topics started |
514
|
|
|
if ($context['is_topics']) |
515
|
|
|
{ |
516
|
|
|
$rows = load_user_topics($this->_memID, $start, $maxIndex, $range_limit, $reverse, $board); |
517
|
|
|
} |
518
|
|
|
else |
519
|
|
|
{ |
520
|
|
|
$rows = load_user_posts($this->_memID, $start, $maxIndex, $range_limit, $reverse, $board); |
521
|
|
|
} |
522
|
|
|
|
523
|
|
|
// Start counting at the number of the first message displayed. |
524
|
|
|
$counter = $reverse ? $context['start'] + $maxIndex + 1 : $context['start']; |
525
|
|
|
$context['posts'] = []; |
526
|
|
|
$board_ids = ['own' => [], 'any' => []]; |
527
|
|
|
$bbc_parser = ParserWrapper::instance(); |
528
|
|
|
foreach ($rows as $row) |
529
|
|
|
{ |
530
|
|
|
// Censor.... |
531
|
|
|
$row['body'] = censor($row['body']); |
532
|
|
|
$row['subject'] = censor($row['subject']); |
533
|
|
|
|
534
|
|
|
// Do the code. |
535
|
|
|
$row['body'] = $bbc_parser->parseMessage($row['body'], $row['smileys_enabled']); |
536
|
|
|
|
537
|
|
|
// And the array... |
538
|
|
|
$context['posts'][$counter += $reverse ? -1 : 1] = [ |
539
|
|
|
'body' => $row['body'], |
540
|
|
|
'counter' => $counter, |
541
|
|
|
'category' => [ |
542
|
|
|
'name' => $row['cname'], |
543
|
|
|
'id' => $row['id_cat'] |
544
|
|
|
], |
545
|
|
|
'board' => [ |
546
|
|
|
'name' => $row['bname'], |
547
|
|
|
'id' => $row['id_board'], |
548
|
|
|
'link' => '<a href="' . getUrl('board', ['board' => $row['id_board'], 'start' => 0, 'name' => $row['bname']]) . '">' . $row['bname'] . '</a>', |
549
|
|
|
], |
550
|
|
|
'topic' => [ |
551
|
|
|
'id' => $row['id_topic'], |
552
|
|
|
'link' => '<a href="' . getUrl('topic', ['topic' => $row['id_topic'], 'msg' => $row['id_msg'], 'subject' => $row['subject'], 'start' => '0']) . '#msg' . $row['id_msg'] . '">' . $row['subject'] . '</a>', |
553
|
|
|
], |
554
|
|
|
'subject' => $row['subject'], |
555
|
|
|
'start' => 'msg' . $row['id_msg'], |
556
|
|
|
'time' => standardTime($row['poster_time']), |
557
|
|
|
'html_time' => htmlTime($row['poster_time']), |
558
|
|
|
'timestamp' => forum_time(true, $row['poster_time']), |
559
|
|
|
'id' => $row['id_msg'], |
560
|
|
|
'tests' => [ |
561
|
|
|
'can_reply' => false, |
562
|
|
|
'can_mark_notify' => false, |
563
|
|
|
'can_delete' => false, |
564
|
|
|
], |
565
|
|
|
'delete_possible' => ($row['id_first_msg'] != $row['id_msg'] || $row['id_last_msg'] == $row['id_msg']) && (empty($modSettings['edit_disable_time']) || $row['poster_time'] + $modSettings['edit_disable_time'] * 60 >= time()), |
566
|
|
|
'approved' => $row['approved'], |
567
|
|
|
|
568
|
|
|
'buttons' => [ |
569
|
|
|
// How about... even... remove it entirely?! |
570
|
|
|
'remove' => [ |
571
|
|
|
'href' => getUrl('action', ['action' => 'deletemsg', 'msg' => $row['id_msg'], 'topic' => $row['id_topic'], 'profile', 'u' => $context['member']['id'], 'start' => $context['start'], '{session_data}']), |
572
|
|
|
'text' => $txt['remove'], |
573
|
|
|
'test' => 'can_delete', |
574
|
|
|
'custom' => 'onclick="return confirm(' . JavaScriptEscape($txt['remove_message'] . '?') . ');"', |
575
|
|
|
], |
576
|
|
|
// Can we request notification of topics? |
577
|
|
|
'notify' => [ |
578
|
|
|
'href' => getUrl('action', ['action' => 'notify', 'topic' => $row['id_topic'], 'msg' => $row['id_msg']]), |
579
|
|
|
'text' => $txt['notify'], |
580
|
|
|
'test' => 'can_mark_notify', |
581
|
|
|
], |
582
|
|
|
// If they *can* reply? |
583
|
|
|
'reply' => [ |
584
|
|
|
'href' => getUrl('action', ['action' => 'post', 'topic' => $row['id_topic'], 'msg' => $row['id_msg']]), |
585
|
|
|
'text' => $txt['reply'], |
586
|
|
|
'test' => 'can_reply', |
587
|
|
|
], |
588
|
|
|
// If they *can* quote? |
589
|
|
|
'quote' => [ |
590
|
|
|
'href' => getUrl('action', ['action' => 'post', 'topic' => $row['id_topic'], 'msg' => $row['id_msg'], 'quote' => $row['id_msg']]), |
591
|
|
|
'text' => $txt['quote'], |
592
|
|
|
'test' => 'can_quote', |
593
|
|
|
], |
594
|
|
|
] |
595
|
|
|
]; |
596
|
|
|
|
597
|
|
|
if ($this->user->id == $row['id_member_started']) |
|
|
|
|
598
|
|
|
{ |
599
|
|
|
$board_ids['own'][$row['id_board']][] = $counter; |
600
|
|
|
} |
601
|
|
|
|
602
|
|
|
$board_ids['any'][$row['id_board']][] = $counter; |
603
|
|
|
} |
604
|
|
|
|
605
|
|
|
// All posts were retrieved in reverse order, get them right again. |
606
|
|
|
if ($reverse) |
607
|
|
|
{ |
608
|
|
|
$context['posts'] = array_reverse($context['posts'], true); |
609
|
|
|
} |
610
|
|
|
|
611
|
|
|
// These are all the permissions that are different from board to board.. |
612
|
|
|
if ($context['is_topics']) |
613
|
|
|
{ |
614
|
|
|
$permissions = [ |
615
|
|
|
'own' => [ |
616
|
|
|
'post_reply_own' => 'can_reply', |
617
|
|
|
], |
618
|
|
|
'any' => [ |
619
|
|
|
'post_reply_any' => 'can_reply', |
620
|
|
|
'mark_any_notify' => 'can_mark_notify', |
621
|
|
|
] |
622
|
|
|
]; |
623
|
|
|
} |
624
|
|
|
else |
625
|
|
|
{ |
626
|
|
|
$permissions = [ |
627
|
|
|
'own' => [ |
628
|
|
|
'post_reply_own' => 'can_reply', |
629
|
|
|
'delete_own' => 'can_delete', |
630
|
|
|
], |
631
|
|
|
'any' => [ |
632
|
|
|
'post_reply_any' => 'can_reply', |
633
|
|
|
'mark_any_notify' => 'can_mark_notify', |
634
|
|
|
'delete_any' => 'can_delete', |
635
|
|
|
] |
636
|
|
|
]; |
637
|
|
|
} |
638
|
|
|
|
639
|
|
|
// For every permission in the own/any lists... |
640
|
|
|
foreach ($permissions as $type => $list) |
641
|
|
|
{ |
642
|
|
|
foreach ($list as $permission => $allowed) |
643
|
|
|
{ |
644
|
|
|
// Get the boards they can do this on... |
645
|
|
|
$boards = boardsAllowedTo($permission); |
646
|
|
|
|
647
|
|
|
// Hmm, they can do it on all boards, can they? |
648
|
|
|
if (!empty($boards) && $boards[0] == 0) |
649
|
|
|
{ |
650
|
|
|
$boards = array_keys($board_ids[$type]); |
651
|
|
|
} |
652
|
|
|
|
653
|
|
|
// Now go through each board they can do the permission on. |
654
|
|
|
foreach ($boards as $board_id) |
655
|
|
|
{ |
656
|
|
|
// There aren't any posts displayed from this board. |
657
|
|
|
if (!isset($board_ids[$type][$board_id])) |
658
|
|
|
{ |
659
|
|
|
continue; |
660
|
|
|
} |
661
|
|
|
|
662
|
|
|
// Set the permission to true ;). |
663
|
|
|
foreach ($board_ids[$type][$board_id] as $counter) |
664
|
|
|
{ |
665
|
|
|
$context['posts'][$counter]['tests'][$allowed] = true; |
666
|
|
|
} |
667
|
|
|
} |
668
|
|
|
} |
669
|
|
|
} |
670
|
|
|
|
671
|
|
|
// Clean up after posts that cannot be deleted and quoted. |
672
|
|
|
$quote_enabled = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']), true); |
673
|
|
|
foreach ($context['posts'] as $counter => $dummy) |
674
|
|
|
{ |
675
|
|
|
$context['posts'][$counter]['tests']['can_delete'] = $context['posts'][$counter]['tests']['can_delete'] && $context['posts'][$counter]['delete_possible']; |
676
|
|
|
$context['posts'][$counter]['tests']['can_quote'] = $context['posts'][$counter]['tests']['can_reply'] && $quote_enabled; |
677
|
|
|
} |
678
|
|
|
} |
679
|
|
|
|
680
|
|
|
/** |
681
|
|
|
* Show all the attachments of a user. |
682
|
|
|
*/ |
683
|
|
|
public function action_showAttachments() |
684
|
|
|
{ |
685
|
|
|
global $txt, $modSettings, $context; |
686
|
|
|
|
687
|
|
|
// OBEY permissions! |
688
|
|
|
$boardsAllowed = boardsAllowedTo('view_attachments'); |
689
|
|
|
|
690
|
|
|
// Make sure we can't actually see anything... |
691
|
|
|
if (empty($boardsAllowed)) |
692
|
|
|
{ |
693
|
|
|
$boardsAllowed = [-1]; |
694
|
|
|
} |
695
|
|
|
|
696
|
|
|
// This is all the information required to list attachments. |
697
|
|
|
$listOptions = [ |
698
|
|
|
'id' => 'profile_attachments', |
699
|
|
|
'title' => $txt['showAttachments'] . ($context['user']['is_owner'] ? '' : ' - ' . $context['member']['name']), |
700
|
|
|
'items_per_page' => $modSettings['defaultMaxMessages'], |
701
|
|
|
'no_items_label' => $txt['show_attachments_none'], |
702
|
|
|
'base_href' => getUrl('action', ['action' => 'profile', 'area' => 'showposts', 'sa' => 'attach', 'u' => $this->_memID]), |
703
|
|
|
'default_sort_col' => 'filename', |
704
|
|
|
'get_items' => [ |
705
|
|
|
'function' => fn($start, $items_per_page, $sort, $boardsAllowed) => $this->list_getAttachments($start, $items_per_page, $sort, $boardsAllowed), |
706
|
|
|
'params' => [ |
707
|
|
|
$boardsAllowed, |
708
|
|
|
], |
709
|
|
|
], |
710
|
|
|
'get_count' => [ |
711
|
|
|
'function' => fn($boardsAllowed) => $this->list_getNumAttachments($boardsAllowed), |
712
|
|
|
'params' => [ |
713
|
|
|
$boardsAllowed, |
714
|
|
|
], |
715
|
|
|
], |
716
|
|
|
'data_check' => [ |
717
|
|
|
'class' => static fn($data) => $data['approved'] ? '' : 'approvebg', |
718
|
|
|
], |
719
|
|
|
'columns' => [ |
720
|
|
|
'filename' => [ |
721
|
|
|
'header' => [ |
722
|
|
|
'value' => $txt['show_attach_filename'], |
723
|
|
|
'class' => 'lefttext grid25', |
724
|
|
|
], |
725
|
|
|
'data' => [ |
726
|
|
|
'db' => 'filename', |
727
|
|
|
], |
728
|
|
|
'sort' => [ |
729
|
|
|
'default' => 'a.filename', |
730
|
|
|
'reverse' => 'a.filename DESC', |
731
|
|
|
], |
732
|
|
|
], |
733
|
|
|
'thumb' => [ |
734
|
|
|
'header' => [ |
735
|
|
|
'value' => '', |
736
|
|
|
], |
737
|
|
|
'data' => [ |
738
|
|
|
'function' => static function ($rowData) { |
739
|
|
|
if ($rowData['is_image'] && !empty($rowData['id_thumb'])) |
740
|
|
|
{ |
741
|
|
|
return '<img src="' . getUrl('action', ['action' => 'dlattach', 'attach' => $rowData['id_thumb'], 'image']) . '" loading="lazy" />'; |
742
|
|
|
} |
743
|
|
|
|
744
|
|
|
return '<img src="' . getUrl('action', ['action' => 'dlattach', 'attach' => $rowData['id'], 'thumb']) . '" loading="lazy" />'; |
745
|
|
|
}, |
746
|
|
|
'class' => 'centertext recent_attachments', |
747
|
|
|
], |
748
|
|
|
'sort' => [ |
749
|
|
|
'default' => 'a.filename', |
750
|
|
|
'reverse' => 'a.filename DESC', |
751
|
|
|
], |
752
|
|
|
], |
753
|
|
|
'downloads' => [ |
754
|
|
|
'header' => [ |
755
|
|
|
'value' => $txt['show_attach_downloads'], |
756
|
|
|
'class' => 'centertext', |
757
|
|
|
], |
758
|
|
|
'data' => [ |
759
|
|
|
'db' => 'downloads', |
760
|
|
|
'comma_format' => true, |
761
|
|
|
'class' => 'centertext', |
762
|
|
|
], |
763
|
|
|
'sort' => [ |
764
|
|
|
'default' => 'a.downloads', |
765
|
|
|
'reverse' => 'a.downloads DESC', |
766
|
|
|
], |
767
|
|
|
], |
768
|
|
|
'subject' => [ |
769
|
|
|
'header' => [ |
770
|
|
|
'value' => $txt['message'], |
771
|
|
|
'class' => 'lefttext grid30', |
772
|
|
|
], |
773
|
|
|
'data' => [ |
774
|
|
|
'db' => 'subject', |
775
|
|
|
], |
776
|
|
|
'sort' => [ |
777
|
|
|
'default' => 'm.subject', |
778
|
|
|
'reverse' => 'm.subject DESC', |
779
|
|
|
], |
780
|
|
|
], |
781
|
|
|
'posted' => [ |
782
|
|
|
'header' => [ |
783
|
|
|
'value' => $txt['show_attach_posted'], |
784
|
|
|
'class' => 'lefttext', |
785
|
|
|
], |
786
|
|
|
'data' => [ |
787
|
|
|
'db' => 'posted', |
788
|
|
|
'timeformat' => true, |
789
|
|
|
], |
790
|
|
|
'sort' => [ |
791
|
|
|
'default' => 'm.poster_time', |
792
|
|
|
'reverse' => 'm.poster_time DESC', |
793
|
|
|
], |
794
|
|
|
], |
795
|
|
|
], |
796
|
|
|
]; |
797
|
|
|
|
798
|
|
|
// Create the request list. |
799
|
|
|
createList($listOptions); |
800
|
|
|
|
801
|
|
|
$context['sub_template'] = 'show_list'; |
802
|
|
|
$context['default_list'] = 'profile_attachments'; |
803
|
|
|
} |
804
|
|
|
|
805
|
|
|
/** |
806
|
|
|
* Get a list of attachments for this user |
807
|
|
|
* Callback for createList() |
808
|
|
|
* |
809
|
|
|
* @param int $start The item to start with (for pagination purposes) |
810
|
|
|
* @param int $items_per_page The number of items to show per page |
811
|
|
|
* @param string $sort A string indicating how to sort the results |
812
|
|
|
* @param int[] $boardsAllowed |
813
|
|
|
* |
814
|
|
|
* @return array |
815
|
|
|
*/ |
816
|
|
|
public function list_getAttachments($start, $items_per_page, $sort, $boardsAllowed) |
817
|
|
|
{ |
818
|
|
|
// @todo tweak this method to use $context, etc, |
819
|
|
|
// then call subs function with params set. |
820
|
|
|
return profileLoadAttachments($start, $items_per_page, $sort, $boardsAllowed, $this->_memID); |
821
|
|
|
} |
822
|
|
|
|
823
|
|
|
/** |
824
|
|
|
* Callback for createList() |
825
|
|
|
* |
826
|
|
|
* @param int[] $boardsAllowed |
827
|
|
|
* |
828
|
|
|
* @return int |
829
|
|
|
*/ |
830
|
|
|
public function list_getNumAttachments($boardsAllowed) |
831
|
|
|
{ |
832
|
|
|
// @todo tweak this method to use $context, etc, |
833
|
|
|
// then call subs function with params set. |
834
|
|
|
return getNumAttachments($boardsAllowed, $this->_memID); |
835
|
|
|
} |
836
|
|
|
|
837
|
|
|
/** |
838
|
|
|
* Show all the unwatched topics. |
839
|
|
|
*/ |
840
|
|
|
public function action_showUnwatched() |
841
|
|
|
{ |
842
|
|
|
global $txt, $modSettings, $context; |
843
|
|
|
|
844
|
|
|
// Only the owner can see the list (if the function is enabled of course) |
845
|
|
|
if ($this->user->id != $this->_memID || !$modSettings['enable_unwatch']) |
|
|
|
|
846
|
|
|
{ |
847
|
|
|
return; |
848
|
|
|
} |
849
|
|
|
|
850
|
|
|
// And here they are: the topics you don't like |
851
|
|
|
$listOptions = [ |
852
|
|
|
'id' => 'unwatched_topics', |
853
|
|
|
'title' => $txt['showUnwatched'], |
854
|
|
|
'items_per_page' => $modSettings['defaultMaxMessages'], |
855
|
|
|
'no_items_label' => $txt['unwatched_topics_none'], |
856
|
|
|
'base_href' => getUrl('action', ['action' => 'profile', 'area' => 'showposts', 'sa' => 'unwatchedtopics', 'u' => $this->_memID]), |
857
|
|
|
'default_sort_col' => 'started_on', |
858
|
|
|
'get_items' => [ |
859
|
|
|
'function' => fn($start, $items_per_page, $sort) => $this->list_getUnwatched($start, $items_per_page, $sort), |
860
|
|
|
], |
861
|
|
|
'get_count' => [ |
862
|
|
|
'function' => fn() => $this->list_getNumUnwatched(), |
863
|
|
|
], |
864
|
|
|
'columns' => [ |
865
|
|
|
'subject' => [ |
866
|
|
|
'header' => [ |
867
|
|
|
'value' => $txt['subject'], |
868
|
|
|
'class' => 'lefttext', |
869
|
|
|
'style' => 'width: 30%;', |
870
|
|
|
], |
871
|
|
|
'data' => [ |
872
|
|
|
'sprintf' => [ |
873
|
|
|
'format' => '<a href="' . getUrl('profile', ['topic' => '%1$d.0']) . '">%2$s</a>', |
874
|
|
|
'params' => [ |
875
|
|
|
'id_topic' => false, |
876
|
|
|
'subject' => false, |
877
|
|
|
], |
878
|
|
|
], |
879
|
|
|
], |
880
|
|
|
'sort' => [ |
881
|
|
|
'default' => 'm.subject', |
882
|
|
|
'reverse' => 'm.subject DESC', |
883
|
|
|
], |
884
|
|
|
], |
885
|
|
|
'started_by' => [ |
886
|
|
|
'header' => [ |
887
|
|
|
'value' => $txt['started_by'], |
888
|
|
|
'style' => 'width: 15%;', |
889
|
|
|
], |
890
|
|
|
'data' => [ |
891
|
|
|
'db' => 'started_by', |
892
|
|
|
], |
893
|
|
|
'sort' => [ |
894
|
|
|
'default' => 'mem.real_name', |
895
|
|
|
'reverse' => 'mem.real_name DESC', |
896
|
|
|
], |
897
|
|
|
], |
898
|
|
|
'started_on' => [ |
899
|
|
|
'header' => [ |
900
|
|
|
'value' => $txt['on'], |
901
|
|
|
'class' => 'lefttext', |
902
|
|
|
'style' => 'width: 20%;', |
903
|
|
|
], |
904
|
|
|
'data' => [ |
905
|
|
|
'db' => 'started_on', |
906
|
|
|
'timeformat' => true, |
907
|
|
|
], |
908
|
|
|
'sort' => [ |
909
|
|
|
'default' => 'm.poster_time', |
910
|
|
|
'reverse' => 'm.poster_time DESC', |
911
|
|
|
], |
912
|
|
|
], |
913
|
|
|
'last_post_by' => [ |
914
|
|
|
'header' => [ |
915
|
|
|
'value' => $txt['last_post'], |
916
|
|
|
'style' => 'width: 15%;', |
917
|
|
|
], |
918
|
|
|
'data' => [ |
919
|
|
|
'db' => 'last_post_by', |
920
|
|
|
], |
921
|
|
|
'sort' => [ |
922
|
|
|
'default' => 'mem.real_name', |
923
|
|
|
'reverse' => 'mem.real_name DESC', |
924
|
|
|
], |
925
|
|
|
], |
926
|
|
|
'last_post_on' => [ |
927
|
|
|
'header' => [ |
928
|
|
|
'value' => $txt['on'], |
929
|
|
|
'class' => 'lefttext', |
930
|
|
|
'style' => 'width: 20%;', |
931
|
|
|
], |
932
|
|
|
'data' => [ |
933
|
|
|
'db' => 'last_post_on', |
934
|
|
|
'timeformat' => true, |
935
|
|
|
], |
936
|
|
|
'sort' => [ |
937
|
|
|
'default' => 'm.poster_time', |
938
|
|
|
'reverse' => 'm.poster_time DESC', |
939
|
|
|
], |
940
|
|
|
], |
941
|
|
|
], |
942
|
|
|
]; |
943
|
|
|
|
944
|
|
|
// Create the request list. |
945
|
|
|
createList($listOptions); |
946
|
|
|
|
947
|
|
|
$context['sub_template'] = 'show_list'; |
948
|
|
|
$context['default_list'] = 'unwatched_topics'; |
949
|
|
|
} |
950
|
|
|
|
951
|
|
|
/** |
952
|
|
|
* Get the relevant topics in the unwatched list |
953
|
|
|
* Callback for createList() |
954
|
|
|
* |
955
|
|
|
* @param int $start The item to start with (for pagination purposes) |
956
|
|
|
* @param int $items_per_page The number of items to show per page |
957
|
|
|
* @param string $sort A string indicating how to sort the results |
958
|
|
|
* |
959
|
|
|
* @return array |
960
|
|
|
*/ |
961
|
|
|
public function list_getUnwatched($start, $items_per_page, $sort) |
962
|
|
|
{ |
963
|
|
|
return getUnwatchedBy($start, $items_per_page, $sort, $this->_memID); |
964
|
|
|
} |
965
|
|
|
|
966
|
|
|
/** |
967
|
|
|
* Count the number of topics in the unwatched list |
968
|
|
|
* Callback for createList() |
969
|
|
|
*/ |
970
|
|
|
public function list_getNumUnwatched() |
971
|
|
|
{ |
972
|
|
|
return getNumUnwatchedBy($this->_memID); |
973
|
|
|
} |
974
|
|
|
|
975
|
|
|
/** |
976
|
|
|
* Gets the user stats for display. |
977
|
|
|
*/ |
978
|
|
|
public function action_statPanel() |
979
|
|
|
{ |
980
|
|
|
global $txt, $context, $modSettings; |
981
|
|
|
|
982
|
|
|
require_once(SUBSDIR . '/Stats.subs.php'); |
983
|
|
|
loadJavascriptFile(['ext/chart.min.js', 'elk_chart.js']); |
984
|
|
|
|
985
|
|
|
$context['page_title'] = $txt['statPanel_showStats'] . ' ' . $this->_profile['real_name']; |
986
|
|
|
|
987
|
|
|
// Is the load average too high to allow searching just now? |
988
|
|
|
if (!empty($modSettings['loadavg_userstats']) && $modSettings['current_load'] >= $modSettings['loadavg_userstats']) |
989
|
|
|
{ |
990
|
|
|
throw new Exception('loadavg_userstats_disabled', false); |
991
|
|
|
} |
992
|
|
|
|
993
|
|
|
theme()->getTemplates()->load('ProfileInfo'); |
994
|
|
|
|
995
|
|
|
// General user statistics. |
996
|
|
|
$timeDays = floor($this->_profile['total_time_logged_in'] / 86400); |
997
|
|
|
$timeHours = floor(($this->_profile['total_time_logged_in'] % 86400) / 3600); |
998
|
|
|
$context['time_logged_in'] = ($timeDays > 0 ? $timeDays . $txt['totalTimeLogged2'] : '') . ($timeHours > 0 ? $timeHours . $txt['totalTimeLogged3'] : '') . floor(($this->_profile['total_time_logged_in'] % 3600) / 60) . $txt['totalTimeLogged4']; |
999
|
|
|
$context['num_posts'] = comma_format($this->_profile['posts']); |
1000
|
|
|
$context['likes_given'] = comma_format($this->_profile['likes_given']); |
1001
|
|
|
$context['likes_received'] = comma_format($this->_profile['likes_received']); |
1002
|
|
|
|
1003
|
|
|
// Menu tab |
1004
|
|
|
$context[$context['profile_menu_name']]['object']->prepareTabData([ |
1005
|
|
|
'title' => $txt['statPanel_generalStats'] . ' - ' . $context['member']['name'], |
1006
|
|
|
'class' => 'i-poll' |
1007
|
|
|
]); |
1008
|
|
|
|
1009
|
|
|
// Number of topics started. |
1010
|
|
|
$context['num_topics'] = UserStatsTopicsStarted($this->_memID); |
1011
|
|
|
|
1012
|
|
|
// Number of polls started. |
1013
|
|
|
$context['num_polls'] = UserStatsPollsStarted($this->_memID); |
1014
|
|
|
|
1015
|
|
|
// Number of polls voted in. |
1016
|
|
|
$context['num_votes'] = UserStatsPollsVoted($this->_memID); |
1017
|
|
|
|
1018
|
|
|
// Format the numbers... |
1019
|
|
|
$context['num_topics'] = comma_format($context['num_topics']); |
1020
|
|
|
$context['num_polls'] = comma_format($context['num_polls']); |
1021
|
|
|
$context['num_votes'] = comma_format($context['num_votes']); |
1022
|
|
|
|
1023
|
|
|
// Grab the boards this member posted in most often. |
1024
|
|
|
$context['popular_boards'] = UserStatsMostPostedBoard($this->_memID); |
1025
|
|
|
|
1026
|
|
|
// Now get the 10 boards this user has most often participated in. |
1027
|
|
|
$context['board_activity'] = UserStatsMostActiveBoard($this->_memID); |
1028
|
|
|
|
1029
|
|
|
// Posting activity by time. |
1030
|
|
|
$context['posts_by_time'] = UserStatsPostingTime($this->_memID); |
1031
|
|
|
|
1032
|
|
|
// Custom stats (just add a template_layer to add it to the template!) |
1033
|
|
|
call_integration_hook('integrate_profile_stats', [$this->_memID]); |
1034
|
|
|
} |
1035
|
|
|
|
1036
|
|
|
/** |
1037
|
|
|
* Show permissions for a user. |
1038
|
|
|
*/ |
1039
|
|
|
public function action_showPermissions() |
1040
|
|
|
{ |
1041
|
|
|
global $txt, $board, $context; |
1042
|
|
|
|
1043
|
|
|
// Verify if the user has sufficient permissions. |
1044
|
|
|
isAllowedTo('manage_permissions'); |
1045
|
|
|
|
1046
|
|
|
Txt::load('ManagePermissions'); |
1047
|
|
|
Txt::load('Admin'); |
1048
|
|
|
theme()->getTemplates()->load('ManageMembers'); |
1049
|
|
|
theme()->getTemplates()->load('ProfileInfo'); |
1050
|
|
|
|
1051
|
|
|
// Load all the permission profiles. |
1052
|
|
|
require_once(SUBSDIR . '/ManagePermissions.subs.php'); |
1053
|
|
|
loadPermissionProfiles(); |
1054
|
|
|
|
1055
|
|
|
$context['member']['id'] = $this->_memID; |
1056
|
|
|
$context['member']['name'] = $this->_profile['real_name']; |
1057
|
|
|
|
1058
|
|
|
$context['page_title'] = $txt['showPermissions']; |
1059
|
|
|
$board = empty($board) ? 0 : (int) $board; |
1060
|
|
|
$context['board'] = $board; |
1061
|
|
|
|
1062
|
|
|
$curGroups = empty($this->_profile['additional_groups']) ? [] : explode(',', $this->_profile['additional_groups']); |
|
|
|
|
1063
|
|
|
$curGroups[] = $this->_profile['id_group']; |
1064
|
|
|
$curGroups[] = $this->_profile['id_post_group']; |
1065
|
|
|
$curGroups = array_map('intval', $curGroups); |
1066
|
|
|
|
1067
|
|
|
// Load a list of boards for the jump box - except the defaults. |
1068
|
|
|
require_once(SUBSDIR . '/Boards.subs.php'); |
1069
|
|
|
$board_list = getBoardList(['moderator' => $this->_memID], true); |
1070
|
|
|
|
1071
|
|
|
$context['boards'] = []; |
1072
|
|
|
$context['no_access_boards'] = []; |
1073
|
|
|
foreach ($board_list as $row) |
1074
|
|
|
{ |
1075
|
|
|
$row['id_board'] = (int) $row['id_board']; |
1076
|
|
|
$row['id_profile'] = (int) $row['id_profile']; |
1077
|
|
|
if (!$row['is_mod'] && array_intersect($curGroups, explode(',', $row['member_groups'])) === []) |
1078
|
|
|
{ |
1079
|
|
|
$context['no_access_boards'][] = [ |
1080
|
|
|
'id' => $row['id_board'], |
1081
|
|
|
'name' => $row['board_name'], |
1082
|
|
|
'is_last' => false, |
1083
|
|
|
]; |
1084
|
|
|
} |
1085
|
|
|
elseif ($row['id_profile'] !== 1 || $row['is_mod']) |
1086
|
|
|
{ |
1087
|
|
|
$context['boards'][$row['id_board']] = [ |
1088
|
|
|
'id' => $row['id_board'], |
1089
|
|
|
'name' => $row['board_name'], |
1090
|
|
|
'url' => getUrl('board', ['board' => $row['id_board'], 'start' => 0, 'name' => $row['board_name']]), |
1091
|
|
|
'selected' => $board === $row['id_board'], |
1092
|
|
|
'profile' => $row['id_profile'], |
1093
|
|
|
'profile_name' => $context['profiles'][$row['id_profile']]['name'], |
1094
|
|
|
]; |
1095
|
|
|
} |
1096
|
|
|
} |
1097
|
|
|
|
1098
|
|
|
if (!empty($context['no_access_boards'])) |
1099
|
|
|
{ |
1100
|
|
|
$context['no_access_boards'][count($context['no_access_boards']) - 1]['is_last'] = true; |
1101
|
|
|
} |
1102
|
|
|
|
1103
|
|
|
$context['member']['permissions'] = [ |
1104
|
|
|
'general' => [], |
1105
|
|
|
'board' => [] |
1106
|
|
|
]; |
1107
|
|
|
|
1108
|
|
|
// If you're an admin we know you can do everything, we might as well leave. |
1109
|
|
|
$context['member']['has_all_permissions'] = in_array(1, $curGroups, true); |
1110
|
|
|
if ($context['member']['has_all_permissions']) |
1111
|
|
|
{ |
1112
|
|
|
return; |
1113
|
|
|
} |
1114
|
|
|
|
1115
|
|
|
// Get all general and board permissions for the groups this member is in |
1116
|
|
|
$context['member']['permissions'] = [ |
1117
|
|
|
'general' => getMemberGeneralPermissions($curGroups), |
1118
|
|
|
'board' => getMemberBoardPermissions($this->_memID, $curGroups, $board) |
1119
|
|
|
]; |
1120
|
|
|
} |
1121
|
|
|
|
1122
|
|
|
/** |
1123
|
|
|
* View a members warnings. |
1124
|
|
|
*/ |
1125
|
|
|
public function action_viewWarning() |
1126
|
|
|
{ |
1127
|
|
|
global $modSettings, $context, $txt; |
1128
|
|
|
|
1129
|
|
|
// Firstly, can we actually even be here? |
1130
|
|
|
if ((empty($modSettings['warning_show']) || ((int) $modSettings['warning_show'] === 1 && !$context['user']['is_owner'])) && !allowedTo('issue_warning')) |
|
|
|
|
1131
|
|
|
{ |
1132
|
|
|
throw new Exception('no_access', false); |
1133
|
|
|
} |
1134
|
|
|
|
1135
|
|
|
theme()->getTemplates()->load('ProfileInfo'); |
1136
|
|
|
|
1137
|
|
|
// We need this because of template_load_warning_variables |
1138
|
|
|
theme()->getTemplates()->load('Profile'); |
1139
|
|
|
|
1140
|
|
|
// Make sure things which are disabled stay disabled. |
1141
|
|
|
$modSettings['warning_watch'] = empty($modSettings['warning_watch']) ? 110 : $modSettings['warning_watch']; |
1142
|
|
|
$modSettings['warning_moderate'] = !empty($modSettings['warning_moderate']) && !empty($modSettings['postmod_active']) ? $modSettings['warning_moderate'] : 110; |
1143
|
|
|
$modSettings['warning_mute'] = empty($modSettings['warning_mute']) ? 110 : $modSettings['warning_mute']; |
1144
|
|
|
|
1145
|
|
|
// Let's use a generic list to get all the current warnings |
1146
|
|
|
// and use the issue warnings grab-a-granny thing. |
1147
|
|
|
$listOptions = [ |
1148
|
|
|
'id' => 'view_warnings', |
1149
|
|
|
'title' => $txt['profile_viewwarning_previous_warnings'], |
1150
|
|
|
'items_per_page' => $modSettings['defaultMaxMessages'], |
1151
|
|
|
'no_items_label' => $txt['profile_viewwarning_no_warnings'], |
1152
|
|
|
'base_href' => getUrl('action', ['action' => 'profile', 'area' => 'viewwarning', 'sa' => 'user', 'u' => $this->_memID]), |
1153
|
|
|
'default_sort_col' => 'log_time', |
1154
|
|
|
'get_items' => [ |
1155
|
|
|
'function' => 'list_getUserWarnings', |
1156
|
|
|
'params' => [ |
1157
|
|
|
$this->_memID, |
1158
|
|
|
], |
1159
|
|
|
], |
1160
|
|
|
'get_count' => [ |
1161
|
|
|
'function' => 'list_getUserWarningCount', |
1162
|
|
|
'params' => [ |
1163
|
|
|
$this->_memID, |
1164
|
|
|
], |
1165
|
|
|
], |
1166
|
|
|
'columns' => [ |
1167
|
|
|
'log_time' => [ |
1168
|
|
|
'header' => [ |
1169
|
|
|
'value' => $txt['profile_warning_previous_time'], |
1170
|
|
|
], |
1171
|
|
|
'data' => [ |
1172
|
|
|
'db' => 'time', |
1173
|
|
|
], |
1174
|
|
|
'sort' => [ |
1175
|
|
|
'default' => 'lc.log_time DESC', |
1176
|
|
|
'reverse' => 'lc.log_time', |
1177
|
|
|
], |
1178
|
|
|
], |
1179
|
|
|
'reason' => [ |
1180
|
|
|
'header' => [ |
1181
|
|
|
'value' => $txt['profile_warning_previous_reason'], |
1182
|
|
|
'style' => 'width: 50%;', |
1183
|
|
|
], |
1184
|
|
|
'data' => [ |
1185
|
|
|
'db' => 'reason', |
1186
|
|
|
], |
1187
|
|
|
], |
1188
|
|
|
'level' => [ |
1189
|
|
|
'header' => [ |
1190
|
|
|
'value' => $txt['profile_warning_previous_level'], |
1191
|
|
|
], |
1192
|
|
|
'data' => [ |
1193
|
|
|
'db' => 'counter', |
1194
|
|
|
], |
1195
|
|
|
'sort' => [ |
1196
|
|
|
'default' => 'lc.counter DESC', |
1197
|
|
|
'reverse' => 'lc.counter', |
1198
|
|
|
], |
1199
|
|
|
], |
1200
|
|
|
], |
1201
|
|
|
'additional_rows' => [ |
1202
|
|
|
[ |
1203
|
|
|
'position' => 'after_title', |
1204
|
|
|
'value' => $txt['profile_viewwarning_desc'], |
1205
|
|
|
'class' => 'smalltext', |
1206
|
|
|
'style' => 'padding: 2ex;', |
1207
|
|
|
], |
1208
|
|
|
], |
1209
|
|
|
]; |
1210
|
|
|
|
1211
|
|
|
// Create the list for viewing. |
1212
|
|
|
createList($listOptions); |
1213
|
|
|
|
1214
|
|
|
// Create some common text bits for the template. |
1215
|
|
|
$context['level_effects'] = [ |
1216
|
|
|
0 => '', |
1217
|
|
|
$modSettings['warning_watch'] => $txt['profile_warning_effect_own_watched'], |
1218
|
|
|
$modSettings['warning_moderate'] => $txt['profile_warning_effect_own_moderated'], |
1219
|
|
|
$modSettings['warning_mute'] => $txt['profile_warning_effect_own_muted'], |
1220
|
|
|
]; |
1221
|
|
|
$context['current_level'] = 0; |
1222
|
|
|
$context['sub_template'] = 'viewWarning'; |
1223
|
|
|
|
1224
|
|
|
foreach ($context['level_effects'] as $limit => $dummy) |
1225
|
|
|
{ |
1226
|
|
|
if ($context['member']['warning'] >= $limit) |
1227
|
|
|
{ |
1228
|
|
|
$context['current_level'] = $limit; |
1229
|
|
|
} |
1230
|
|
|
} |
1231
|
|
|
} |
1232
|
|
|
|
1233
|
|
|
/** |
1234
|
|
|
* Collect and output data related to the profile buddy tab |
1235
|
|
|
* |
1236
|
|
|
* - Ajax call from profile info buddy tab |
1237
|
|
|
*/ |
1238
|
|
|
public function action_profile_buddies() |
1239
|
|
|
{ |
1240
|
|
|
global $context; |
1241
|
|
|
|
1242
|
|
|
checkSession('get'); |
1243
|
|
|
|
1244
|
|
|
// Need the ProfileInfo and Index (for helper functions) templates |
1245
|
|
|
theme()->getTemplates()->load('ProfileInfo'); |
1246
|
|
|
|
1247
|
|
|
// Prep for a buddy check |
1248
|
|
|
$this->_register_summarytabs(); |
1249
|
|
|
$this->_define_user_values(); |
1250
|
|
|
|
1251
|
|
|
// This is returned only for ajax request to a jqueryUI tab |
1252
|
|
|
theme()->getLayers()->removeAll(); |
1253
|
|
|
|
1254
|
|
|
// Some buddies for you |
1255
|
|
|
if (in_array('buddies', $this->_summary_areas, true)) |
1256
|
|
|
{ |
1257
|
|
|
$this->_load_buddies(); |
1258
|
|
|
$context['sub_template'] = 'profile_block_buddies'; |
1259
|
|
|
} |
1260
|
|
|
} |
1261
|
|
|
|
1262
|
|
|
/** |
1263
|
|
|
* Load the buddies tab with their buddies, real or imaginary |
1264
|
|
|
*/ |
1265
|
|
|
private function _load_buddies() |
1266
|
|
|
{ |
1267
|
|
|
global $context, $modSettings; |
1268
|
|
|
|
1269
|
|
|
// Would you be mine? Could you be mine? Be my buddy :D |
1270
|
|
|
$context['buddies'] = []; |
1271
|
|
|
if (empty($modSettings['enable_buddylist'])) |
1272
|
|
|
{ |
1273
|
|
|
return; |
1274
|
|
|
} |
1275
|
|
|
if (!$context['user']['is_owner']) |
1276
|
|
|
{ |
1277
|
|
|
return; |
1278
|
|
|
} |
1279
|
|
|
if (empty($this->user->buddies)) |
|
|
|
|
1280
|
|
|
{ |
1281
|
|
|
return; |
1282
|
|
|
} |
1283
|
|
|
if (!in_array('buddies', $this->_summary_areas, true)) |
1284
|
|
|
{ |
1285
|
|
|
return; |
1286
|
|
|
} |
1287
|
|
|
if (!MembersList::load($this->user->buddies, false, 'profile')) |
1288
|
|
|
{ |
1289
|
|
|
return; |
1290
|
|
|
} |
1291
|
|
|
|
1292
|
|
|
// Get the info for this buddy |
1293
|
|
|
foreach ($this->user->buddies as $buddy) |
1294
|
|
|
{ |
1295
|
|
|
$member = MembersList::get($buddy); |
1296
|
|
|
$member->loadContext(true); |
1297
|
|
|
|
1298
|
|
|
$context['buddies'][$buddy] = $member; |
1299
|
|
|
} |
1300
|
|
|
} |
1301
|
|
|
|
1302
|
|
|
/** |
1303
|
|
|
* Collect and output data related to the profile recent tab |
1304
|
|
|
* |
1305
|
|
|
* - Ajax call from profile info recent tab |
1306
|
|
|
*/ |
1307
|
|
|
public function action_profile_recent() |
1308
|
|
|
{ |
1309
|
|
|
global $context; |
1310
|
|
|
|
1311
|
|
|
checkSession('get'); |
1312
|
|
|
|
1313
|
|
|
// Prep for recent activity |
1314
|
|
|
$this->_register_summarytabs(); |
1315
|
|
|
$this->_define_user_values(); |
1316
|
|
|
|
1317
|
|
|
// The block templates are here |
1318
|
|
|
theme()->getTemplates()->load('ProfileInfo'); |
1319
|
|
|
$context['sub_template'] = 'profile_blocks'; |
1320
|
|
|
$context['profile_blocks'] = []; |
1321
|
|
|
|
1322
|
|
|
// Flush everything since we intend to return the information to an ajax handler |
1323
|
|
|
theme()->getLayers()->removeAll(); |
1324
|
|
|
|
1325
|
|
|
// So, just what have you been up to? |
1326
|
|
|
if (in_array('posts', $this->_summary_areas, true)) |
1327
|
|
|
{ |
1328
|
|
|
$this->_load_recent_posts(); |
1329
|
|
|
$context['profile_blocks'][] = 'template_profile_block_posts'; |
1330
|
|
|
} |
1331
|
|
|
|
1332
|
|
|
if (in_array('topics', $this->_summary_areas, true)) |
1333
|
|
|
{ |
1334
|
|
|
$this->_load_recent_topics(); |
1335
|
|
|
$context['profile_blocks'][] = 'template_profile_block_topics'; |
1336
|
|
|
} |
1337
|
|
|
|
1338
|
|
|
if (in_array('attachments', $this->_summary_areas, true)) |
1339
|
|
|
{ |
1340
|
|
|
$this->_load_recent_attachments(); |
1341
|
|
|
$context['profile_blocks'][] = 'template_profile_block_attachments'; |
1342
|
|
|
} |
1343
|
|
|
} |
1344
|
|
|
|
1345
|
|
|
/** |
1346
|
|
|
* Load a members most recent posts |
1347
|
|
|
*/ |
1348
|
|
|
private function _load_recent_posts() |
1349
|
|
|
{ |
1350
|
|
|
global $context, $modSettings; |
1351
|
|
|
|
1352
|
|
|
// How about their most recent posts? |
1353
|
|
|
if (in_array('posts', $this->_summary_areas, true)) |
1354
|
|
|
{ |
1355
|
|
|
// Is the load average too high just now, then let them know |
1356
|
|
|
$context['loadaverage'] = $this->isOverLoadAverage(); |
1357
|
|
|
if (!$context['loadaverage']) |
1358
|
|
|
{ |
1359
|
|
|
// Set up to get the last 10 posts of this member |
1360
|
|
|
$msgCount = count_user_posts($this->_memID); |
1361
|
|
|
$range_limit = ''; |
1362
|
|
|
$maxIndex = 10; |
1363
|
|
|
$start = $this->_req->getQuery('start', 'intval', 0); |
1364
|
|
|
|
1365
|
|
|
// If they are a frequent poster, we guess the range to help minimize what the query work |
1366
|
|
|
if ($msgCount > 1000) |
1367
|
|
|
{ |
1368
|
|
|
[$min_msg_member, $max_msg_member] = findMinMaxUserMessage($this->_memID); |
1369
|
|
|
$margin = floor(($max_msg_member - $min_msg_member) * (($start + $modSettings['defaultMaxMessages']) / $msgCount) + .1 * ($max_msg_member - $min_msg_member)); |
1370
|
|
|
$range_limit = 'm.id_msg > ' . ($max_msg_member - $margin); |
1371
|
|
|
} |
1372
|
|
|
|
1373
|
|
|
// Find this user's most recent posts |
1374
|
|
|
$rows = load_user_posts($this->_memID, 0, $maxIndex, $range_limit); |
1375
|
|
|
$bbc_parser = ParserWrapper::instance(); |
1376
|
|
|
$context['posts'] = []; |
1377
|
|
|
foreach ($rows as $row) |
1378
|
|
|
{ |
1379
|
|
|
// Censor.... |
1380
|
|
|
$row['body'] = censor($row['body']); |
1381
|
|
|
$row['subject'] = censor($row['subject']); |
1382
|
|
|
|
1383
|
|
|
// Do the code. |
1384
|
|
|
$row['body'] = $bbc_parser->parseMessage($row['body'], $row['smileys_enabled']); |
1385
|
|
|
$preview = strip_tags(strtr($row['body'], ['<br />' => ' '])); |
1386
|
|
|
$preview = Util::shorten_text($preview, empty($modSettings['ssi_preview_length']) ? 128 : $modSettings['ssi_preview_length']); |
1387
|
|
|
$short_subject = Util::shorten_text($row['subject'], empty($modSettings['ssi_subject_length']) ? 24 : $modSettings['ssi_subject_length']); |
1388
|
|
|
|
1389
|
|
|
// And the array... |
1390
|
|
|
$context['posts'][] = [ |
1391
|
|
|
'body' => $preview, |
1392
|
|
|
'board' => [ |
1393
|
|
|
'name' => $row['bname'], |
1394
|
|
|
'link' => '<a href="' . getUrl('board', ['board' => $row['id_board'], 'start' => 0, 'name' => $row['bname']]) . '">' . $row['bname'] . '</a>' |
1395
|
|
|
], |
1396
|
|
|
'subject' => $row['subject'], |
1397
|
|
|
'short_subject' => $short_subject, |
1398
|
|
|
'time' => standardTime($row['poster_time']), |
1399
|
|
|
'html_time' => htmlTime($row['poster_time']), |
1400
|
|
|
'timestamp' => forum_time(true, $row['poster_time']), |
1401
|
|
|
'link' => '<a href="' . getUrl('topic', ['topic' => $row['id_topic'], 'start' => 0, 'msg' => $row['id_msg'], 'subject' => $row['subject'], 'hash' => '#msg' . $row['id_msg']]) . '" rel="nofollow">' . $short_subject . '</a>', |
1402
|
|
|
]; |
1403
|
|
|
} |
1404
|
|
|
} |
1405
|
|
|
} |
1406
|
|
|
} |
1407
|
|
|
|
1408
|
|
|
/** |
1409
|
|
|
* Load a users recent topics |
1410
|
|
|
*/ |
1411
|
|
|
private function _load_recent_topics() |
1412
|
|
|
{ |
1413
|
|
|
global $context, $modSettings; |
1414
|
|
|
|
1415
|
|
|
// How about the most recent topics that they started? |
1416
|
|
|
if (in_array('topics', $this->_summary_areas, true)) |
1417
|
|
|
{ |
1418
|
|
|
// Is the load average still too high? |
1419
|
|
|
$context['loadaverage'] = $this->isOverLoadAverage(); |
1420
|
|
|
if (!$context['loadaverage']) |
1421
|
|
|
{ |
1422
|
|
|
// Set up to get the last 10 topics of this member |
1423
|
|
|
$topicCount = count_user_topics($this->_memID); |
1424
|
|
|
$range_limit = ''; |
1425
|
|
|
$maxIndex = 10; |
1426
|
|
|
$start = $this->_req->getQuery('start', 'intval', 0); |
1427
|
|
|
|
1428
|
|
|
// If they are a frequent topic starter we guess the range to help the query |
1429
|
|
|
if ($topicCount > 1000) |
1430
|
|
|
{ |
1431
|
|
|
[$min_topic_member, $max_topic_member] = findMinMaxUserTopic($this->_memID); |
1432
|
|
|
$margin = floor(($max_topic_member - $min_topic_member) * (($start + $modSettings['defaultMaxMessages']) / $topicCount) + .1 * ($max_topic_member - $min_topic_member)); |
1433
|
|
|
$margin *= 5; |
1434
|
|
|
$range_limit = 't.id_first_msg > ' . ($max_topic_member - $margin); |
1435
|
|
|
} |
1436
|
|
|
|
1437
|
|
|
// Find this user's most recent topics |
1438
|
|
|
$rows = load_user_topics($this->_memID, 0, $maxIndex, $range_limit); |
1439
|
|
|
$context['topics'] = []; |
1440
|
|
|
$bbc_parser = ParserWrapper::instance(); |
1441
|
|
|
|
1442
|
|
|
foreach ($rows as $row) |
1443
|
|
|
{ |
1444
|
|
|
// Censor.... |
1445
|
|
|
$row['body'] = censor($row['body']); |
1446
|
|
|
$row['subject'] = censor($row['subject']); |
1447
|
|
|
|
1448
|
|
|
// Do the code. |
1449
|
|
|
$row['body'] = $bbc_parser->parseMessage($row['body'], $row['smileys_enabled']); |
1450
|
|
|
$preview = strip_tags(strtr($row['body'], ['<br />' => ' '])); |
1451
|
|
|
$preview = Util::shorten_text($preview, empty($modSettings['ssi_preview_length']) ? 128 : $modSettings['ssi_preview_length']); |
1452
|
|
|
$short_subject = Util::shorten_text($row['subject'], empty($modSettings['ssi_subject_length']) ? 24 : $modSettings['ssi_subject_length']); |
1453
|
|
|
|
1454
|
|
|
// And the array... |
1455
|
|
|
$context['topics'][] = [ |
1456
|
|
|
'board' => [ |
1457
|
|
|
'name' => $row['bname'], |
1458
|
|
|
'link' => '<a href="' . getUrl('board', ['board' => $row['id_board'], 'start' => 0, 'name' => $row['bname']]) . '">' . $row['bname'] . '</a>' |
1459
|
|
|
], |
1460
|
|
|
'subject' => $row['subject'], |
1461
|
|
|
'short_subject' => $short_subject, |
1462
|
|
|
'body' => $preview, |
1463
|
|
|
'time' => standardTime($row['poster_time']), |
1464
|
|
|
'html_time' => htmlTime($row['poster_time']), |
1465
|
|
|
'timestamp' => forum_time(true, $row['poster_time']), |
1466
|
|
|
'link' => '<a href="' . getUrl('topic', ['topic' => $row['id_topic'], 'start' => 0, 'msg' => $row['id_msg'], 'subject' => $row['subject'], 'hash' => '#msg' . $row['id_msg']]) . '" rel="nofollow">' . $short_subject . '</a>', |
1467
|
|
|
]; |
1468
|
|
|
} |
1469
|
|
|
} |
1470
|
|
|
} |
1471
|
|
|
} |
1472
|
|
|
|
1473
|
|
|
/** |
1474
|
|
|
* If they have made recent attachments, lets get a list of them to display |
1475
|
|
|
*/ |
1476
|
|
|
private function _load_recent_attachments() |
1477
|
|
|
{ |
1478
|
|
|
global $context, $modSettings, $settings; |
1479
|
|
|
|
1480
|
|
|
$context['thumbs'] = []; |
1481
|
|
|
|
1482
|
|
|
// Load up the most recent attachments for this user for use in profile views etc. |
1483
|
|
|
if (!empty($modSettings['attachmentEnable']) |
1484
|
|
|
&& !empty($settings['attachments_on_summary']) |
1485
|
|
|
&& in_array('attachments', $this->_summary_areas, true)) |
1486
|
|
|
{ |
1487
|
|
|
$boardsAllowed = boardsAllowedTo('view_attachments'); |
1488
|
|
|
|
1489
|
|
|
if (empty($boardsAllowed)) |
1490
|
|
|
{ |
1491
|
|
|
$boardsAllowed = [-1]; |
1492
|
|
|
} |
1493
|
|
|
|
1494
|
|
|
$attachments = $this->list_getAttachments(0, $settings['attachments_on_summary'], 'm.poster_time DESC', $boardsAllowed); |
1495
|
|
|
|
1496
|
|
|
// Some generic images for mime types |
1497
|
|
|
$mime_images_url = $settings['default_images_url'] . '/mime_images/'; |
1498
|
|
|
$mime_path = $settings['default_theme_dir'] . '/images/mime_images/'; |
1499
|
|
|
|
1500
|
|
|
// Load them in to $context for use in the template |
1501
|
|
|
foreach ($attachments as $i => $attachment) |
1502
|
|
|
{ |
1503
|
|
|
$context['thumbs'][$i] = [ |
1504
|
|
|
'url' => getUrl('action', ['action' => 'dlattach', 'topic' => $attachment['topic'] . '.0', 'attach' => $attachment['id']]), |
1505
|
|
|
'img' => '', |
1506
|
|
|
'filename' => $attachment['filename'], |
1507
|
|
|
'downloads' => $attachment['downloads'], |
1508
|
|
|
'subject' => $attachment['subject'], |
1509
|
|
|
'id' => $attachment['id'], |
1510
|
|
|
]; |
1511
|
|
|
|
1512
|
|
|
// Show a thumbnail image as well? |
1513
|
|
|
if ($attachment['is_image'] && !empty($modSettings['attachmentShowImages']) && !empty($modSettings['attachmentThumbnails'])) |
1514
|
|
|
{ |
1515
|
|
|
if (!empty($attachment['id_thumb'])) |
1516
|
|
|
{ |
1517
|
|
|
$context['thumbs'][$i]['img'] = '<img id="thumb_' . $attachment['id'] . '" src="' . getUrl('action', ['action' => 'dlattach', 'topic' => $attachment['topic'] . '.0', 'attach' => $attachment['id_thumb'], 'image']) . '" title="" alt="" loading="lazy" />'; |
1518
|
|
|
} |
1519
|
|
|
elseif (!empty($modSettings['attachmentThumbWidth']) && !empty($modSettings['attachmentThumbHeight'])) |
1520
|
|
|
{ |
1521
|
|
|
// No thumbnail available ... use html instead |
1522
|
|
|
if ($attachment['width'] > $modSettings['attachmentThumbWidth'] || $attachment['height'] > $modSettings['attachmentThumbHeight']) |
1523
|
|
|
{ |
1524
|
|
|
$context['thumbs'][$i]['img'] = '<img id="thumb_' . $attachment['id'] . '" src="' . getUrl('action', ['action' => 'dlattach', 'topic' => $attachment['topic'] . '.0', 'attach' => $attachment['id']]) . '" title="" alt="" width="' . $modSettings['attachmentThumbWidth'] . '" height="' . $modSettings['attachmentThumbHeight'] . '" loading="lazy" />'; |
1525
|
|
|
} |
1526
|
|
|
else |
1527
|
|
|
{ |
1528
|
|
|
$context['thumbs'][$i]['img'] = '<img id="thumb_' . $attachment['id'] . '" src="' . getUrl('action', ['action' => 'dlattach', 'topic' => $attachment['topic'] . '.0', 'attach' => $attachment['id']]) . '" title="" alt="" width="' . $attachment['width'] . '" height="' . $attachment['height'] . '" loading="lazy" />'; |
1529
|
|
|
} |
1530
|
|
|
} |
1531
|
|
|
} |
1532
|
|
|
// Not an image so set a mime thumbnail based off the filetype |
1533
|
|
|
elseif ((!empty($modSettings['attachmentThumbWidth']) && !empty($modSettings['attachmentThumbHeight'])) && (128 > $modSettings['attachmentThumbWidth'] || 128 > $modSettings['attachmentThumbHeight'])) |
1534
|
|
|
{ |
1535
|
|
|
$context['thumbs'][$i]['img'] = '<img src="' . $mime_images_url . (FileFunctions::instance()->fileExists($mime_path . $attachment['fileext'] . '.png') ? $attachment['fileext'] : 'default') . '.png" title="" alt="" width="' . $modSettings['attachmentThumbWidth'] . '" height="' . $modSettings['attachmentThumbHeight'] . '" loading="lazy" />'; |
1536
|
|
|
} |
1537
|
|
|
else |
1538
|
|
|
{ |
1539
|
|
|
$context['thumbs'][$i]['img'] = '<img src="' . $mime_images_url . (FileFunctions::instance()->fileExists($mime_path . $attachment['fileext'] . '.png') ? $attachment['fileext'] : 'default') . '.png" title="" alt="" loading="lazy" />'; |
1540
|
|
|
} |
1541
|
|
|
} |
1542
|
|
|
} |
1543
|
|
|
} |
1544
|
|
|
|
1545
|
|
|
/** |
1546
|
|
|
* Checks if the current load average exceeds a specified threshold. |
1547
|
|
|
* |
1548
|
|
|
* @return bool Returns true if the current load average is higher than the specified threshold, otherwise false. |
1549
|
|
|
*/ |
1550
|
|
|
private function isOverLoadAverage() |
1551
|
|
|
{ |
1552
|
|
|
global $modSettings; |
1553
|
|
|
|
1554
|
|
|
// Is the load average too high just now, then let them know |
1555
|
|
|
return !empty($modSettings['loadavg_show_posts']) && $modSettings['current_load'] >= $modSettings['loadavg_show_posts']; |
1556
|
|
|
} |
1557
|
|
|
} |
1558
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.