PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.6.01
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.6.01
2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 1.1.0 All 77 releases
fluent-community / app / Models / BaseSpace.php

BaseSpace.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.6.01, at app/Models/BaseSpace.php

648 lines 18.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\App\Models;
4
5 use FluentCommunity\App\App;
6 use FluentCommunity\App\Functions\Utility;
7 use FluentCommunity\App\Services\CustomSanitizer;
8 use FluentCommunity\App\Services\LockscreenService;
9 use FluentCommunity\App\Services\Helper;
10 use FluentCommunity\Framework\Support\Arr;
11
12 class BaseSpace extends Model
13 {
14 protected $table = 'fcom_spaces';
15
16 protected static $type = 'community';
17
18 protected $guarded = ['id'];
19
20 protected $fillable = [
21 'created_by',
22 'parent_id',
23 'title',
24 'slug',
25 'description',
26 'logo',
27 'cover_photo',
28 'type',
29 'privacy',
30 'status',
31 'serial',
32 'settings'
33 ];
34
35 protected $searchable = [
36 'title',
37 'description'
38 ];
39
40 public $_preloadedMembershipUserId = null;
41 public $_preloadedMembership = null;
42
43 public static function boot()
44 {
45 parent::boot();
46
47 static::creating(function ($model) {
48 if (empty($model->created_by)) {
49 $model->created_by = get_current_user_id();
50 }
51
52 if (empty($model->slug)) {
53 $model->slug = self::generateNewSlug($model);
54 }
55
56 $model->type = static::$type;
57 });
58
59 static::addGlobalScope('type', function ($query) {
60 if (static::$type) {
61 $query->where('type', static::$type);
62 }
63
64 return $query;
65 });
66
67 static::deleting(function ($space) {
68 Media::where('sub_object_id', $space->id)
69 ->whereIn('object_source', ['space_logo', 'space_cover_photo'])
70 ->update([
71 'is_active' => 0
72 ]);
73 });
74 }
75
76 public function owner()
77 {
78 return $this->belongsTo(User::class, 'created_by', 'ID');
79 }
80
81 public function space_pivot()
82 {
83 return $this->belongsTo(SpaceUserPivot::class, 'id', 'space_id');
84 }
85
86 public function admins()
87 {
88 return $this->belongsToMany(User::class, 'fcom_space_user', 'space_id', 'user_id')
89 ->where(function ($query) {
90 $query->where('role', 'admin')
91 ->orWhere('role', 'moderator');
92 });
93 }
94
95 public function scopeSearchBy($query, $search)
96 {
97 if ($search) {
98 $fields = $this->searchable;
99 $query->where(function ($query) use ($fields, $search) {
100 $query->where(array_shift($fields), 'LIKE', "%$search%");
101 foreach ($fields as $field) {
102 $query->orWhere($field, 'LIKE', "$search%");
103 }
104 });
105 }
106
107 return $query;
108 }
109
110 public function scopeOnlyMain($query)
111 {
112 return $query->withoutGlobalScopes()->whereIn('type', ['community', 'course']);
113 }
114
115 public function scopeFilterByUserId($query, $userId)
116 {
117 if (!$userId) {
118 return $query->where('privacy', 'public');
119 }
120
121 $ids = get_user_meta($userId, '_fcom_space_ids', true);
122
123 if (!$ids) {
124 return $query->where('privacy', 'public');
125 }
126
127 return $query->whereIn('id', $ids);
128 }
129
130 public function scopeByUserAccess($query, $userId)
131 {
132 if (!$userId) {
133 return $query->where('privacy', 'public');
134 }
135
136 return $this->where(function ($query) use ($userId) {
137 return $query->where('privacy', 'public')
138 ->orWhereHas('members', function ($query) use ($userId) {
139 return $query->where('user_id', $userId);
140 });
141 });
142 }
143
144 public function posts()
145 {
146 return $this->hasMany(Feed::class, 'space_id', 'id');
147 }
148
149 public function comments()
150 {
151 return $this->hasManyThrough(Comment::class, Feed::class, 'space_id', 'post_id');
152 }
153
154 public function members()
155 {
156 return $this->belongsToMany(User::class, 'fcom_space_user', 'space_id', 'user_id')
157 ->withPivot(['role', 'created_at', 'status']);
158 }
159
160 public function x_members()
161 {
162 return $this->belongsToMany(XProfile::class, 'fcom_space_user', 'space_id', 'user_id', 'id', 'user_id')
163 ->withPivot(['role', 'created_at', 'status']);
164 }
165
166 public function group()
167 {
168 return $this->belongsTo(SpaceGroup::class, 'parent_id', 'id');
169 }
170
171 public function getMembership($userId)
172 {
173 if (!$userId) {
174 return null;
175 }
176
177 if ($this->_preloadedMembershipUserId == $userId) {
178 return $this->_preloadedMembership;
179 }
180
181 return $this->members()->where('user_id', $userId)->first();
182 }
183
184 public static function preloadMemberships($spaces, $userId)
185 {
186 if (!$userId || $spaces->isEmpty()) {
187 return;
188 }
189
190 $spaceIds = $spaces->pluck('id')->toArray();
191
192 $pivots = SpaceUserPivot::where('user_id', $userId)
193 ->whereIn('space_id', $spaceIds)
194 ->get()
195 ->keyBy('space_id');
196
197 foreach ($spaces as $space) {
198 $space->_preloadedMembershipUserId = $userId;
199 $pivot = $pivots->get($space->id);
200 $space->_preloadedMembership = $pivot ? static::pivotToMembership($pivot) : null;
201 }
202 }
203
204 private static function pivotToMembership($pivot)
205 {
206 $membership = new \stdClass();
207 $membership->pivot = (object) $pivot->toArray();
208 $membership->ID = $pivot->user_id;
209 return $membership;
210 }
211
212 public function isCourseSpace()
213 {
214 return $this->type == 'course';
215 }
216
217 public function isAdmin($userId, $checkModerator = false)
218 {
219 if (Helper::isSiteAdmin($userId)) {
220 return true;
221 }
222
223 $roles = ['admin'];
224
225 if ($checkModerator) {
226 if (Helper::isModerator()) {
227 return true;
228 }
229 $roles[] = 'moderator';
230 }
231
232 $membership = $this->getMembership($userId);
233 if (!$membership) {
234 return false;
235 }
236
237 return in_array($membership->pivot->role, $roles);
238 }
239
240 public function updateCustomData($data, $removeSrc = false)
241 {
242 $deletePhotos = [];
243 if (isset($data['logo'])) {
244 $deletePhotos[] = $this->logo;
245 $this->logo = sanitize_url($data['logo']);
246 }
247
248 if (isset($data['cover_photo'])) {
249 $deletePhotos[] = $this->cover_photo;
250 $this->cover_photo = sanitize_url($data['cover_photo']);
251 }
252
253 if (isset($data['description'])) {
254 $this->description = wp_kses_post($data['description']);
255 }
256
257 if (isset($data['title'])) {
258 $this->title = sanitize_text_field($data['title']);
259 }
260
261 if (isset($data['privacy'])) {
262 $this->privacy = sanitize_text_field($data['privacy']);
263 }
264
265 if (isset($data['parent_id'])) {
266 if (!empty($data['parent_id'])) {
267 $group = SpaceGroup::find($data['parent_id']);
268 if (!$group) {
269 throw new \Exception(esc_html__('Invalid group id', 'fluent-community'), 400);
270 }
271 $this->parent_id = $group->id;
272 } else {
273 $this->parent_id = NULL;
274 }
275 }
276
277 if (isset($data['settings'])) {
278
279 $settings = CustomSanitizer::santizeSpaceSettings($data['settings'], $this->privacy);
280
281 if (is_wp_error($settings)) {
282 return $settings;
283 }
284
285 $exisitingSetting = $this->settings;
286 $settings['links'] = Arr::get($exisitingSetting, 'links', []);
287
288 if (isset($settings['og_image'])) {
289 $ogImageUrl = Arr::get($settings, 'og_image');
290 if ($ogImageUrl) {
291 $ogImageMedia = Helper::getMediaFromUrl($ogImageUrl);
292 if ($ogImageMedia && $ogImageMedia->is_active) {
293 unset($settings['og_image']);
294 } else if ($ogImageMedia) {
295 $ogImageMedia->update([
296 'is_active' => true,
297 'user_id' => get_current_user_id(),
298 'sub_object_id' => $this->id,
299 'object_source' => 'space_og_media'
300 ]);
301 $settings['og_image'] = $ogImageMedia->public_url;
302 } else {
303 $settings['og_image'] = sanitize_url($ogImageUrl);
304 }
305 }
306 } else {
307 $deletePhotos[] = Arr::get($exisitingSetting, 'og_image');
308 }
309
310 $settings = wp_parse_args($settings, $exisitingSetting);
311 $this->settings = $settings;
312 }
313
314 if (!empty($data['slug']) && $data['slug'] !== $this->slug) {
315 $newSlug = preg_replace('/[^a-zA-Z0-9-_]/', '', $data['slug']);
316
317 $newSlug = sanitize_title($newSlug);
318
319 if (empty($newSlug)) {
320 throw new \Exception('Invalid slug', 400);
321 }
322
323 $exist = App::getInstance('db')->table('fcom_spaces')->where('slug', $newSlug)
324 ->where('id', '!=', $this->id)
325 ->exists();
326
327 if ($exist) {
328 throw new \Exception(esc_html__('Slug already exists. Please use a different slug', 'fluent-community'), 400);
329 }
330
331 $this->slug = $newSlug;
332 }
333
334 $deletePhotos = array_filter($deletePhotos);
335 if ($removeSrc && $deletePhotos) {
336 $deletePhotos = array_filter($deletePhotos);
337 do_action('fluent_community/remove_medias_by_url', $deletePhotos, [
338 'sub_object_id' => $this->id,
339 ]);
340 }
341
342 $dirty = $this->getDirty();
343
344 if ($dirty) {
345 $this->save();
346 if ($this->type == 'community') {
347 do_action('fluent_community/space/updated', $this, $dirty);
348 }
349 }
350
351 return $this;
352 }
353
354 public function isContentSpace()
355 {
356 return in_array($this->type, ['community', 'course']);
357 }
358
359 public function getSettingsAttribute($value)
360 {
361 $settings = Utility::safeUnserialize($value);
362
363 if (!$settings) {
364 $settings = [];
365 }
366
367 return wp_parse_args($settings, $this->defaultSettings());
368 }
369
370 public function defaultSettings()
371 {
372 return [
373 'restricted_post_only' => 'no',
374 'emoji' => '',
375 'shape_svg' => '',
376 'custom_lock_screen' => 'no',
377 'can_request_join' => 'no',
378 'layout_style' => 'timeline',
379 'show_sidebar' => 'yes',
380 'og_image' => '',
381 'links' => [],
382 'topic_required' => 'no',
383 'hide_members_count' => 'no', // yes / no
384 'members_page_status' => 'members_only', // members_only, everybody, logged_in, admin_only
385 ];
386 }
387
388 public function setSettingsAttribute($value)
389 {
390 $this->attributes['settings'] = maybe_serialize($value);
391 }
392
393 public function getPublicPermissions()
394 {
395 if ($this->privacy == 'public') {
396
397 $hasDocuments = defined('FLUENT_COMMUNITY_PRO') && Arr::get($this->settings, 'document_library') == 'yes';
398
399 return [
400 'can_view_info' => true,
401 'can_view_posts' => true,
402 'can_view_members' => $this->canViewMembers(null),
403 'can_create_post' => false,
404 'can_view_documents' => $hasDocuments && Arr::get($this->settings, 'document_access') == 'everybody'
405 ];
406 }
407
408 return [
409 'can_view_info' => false,
410 'can_view_posts' => false,
411 ];
412 }
413
414 public function getUserPermissions($user = null)
415 {
416 if (!$user) {
417 return $this->getPublicPermissions();
418 }
419
420 return $user->getSpacePermissions($this);
421 }
422
423 public function canViewMembers($user)
424 {
425 $viewStatus = Arr::get($this->settings, 'members_page_status');
426 if ($viewStatus === 'everybody') {
427 return true;
428 }
429
430 if (!$user) {
431 return false;
432 }
433
434 if ($viewStatus === 'logged_in') {
435 return true;
436 }
437
438 if ($viewStatus === 'members_only') {
439 $membership = $this->getMembership($user->ID);
440 return $membership && isset($membership->pivot) && $membership->pivot->status === 'active';
441 }
442
443 return $this->isAdmin($user->ID, true);
444 }
445
446 public function verifyUserPermisson($user, $permission, $exception = true)
447 {
448 $permissions = $this->getUserPermissions($user);
449
450 $hasPermission = $permissions[$permission] ?? false;
451
452 if (!$hasPermission && $exception) {
453 /* translators: %s is the permission name */
454 throw new \Exception(esc_html(sprintf(__('Sorry you do not have permission %s', 'fluent-community'), $permission)), 403);
455 }
456
457 return $hasPermission;
458 }
459
460 public function setLockscreen($settingFields)
461 {
462 return $this->updateCustomMeta('lockscreen_settings', $settingFields);
463 }
464
465 public function getPermalink()
466 {
467 if ($this->type == 'community') {
468 return Helper::baseUrl('space/' . $this->slug . '/home');
469 }
470
471 if ($this->type == 'course') {
472 return Helper::baseUrl('course/' . $this->slug . '/lessons');
473 }
474
475 if ($this->type == 'sidebar_link') {
476 $permalink = Arr::get($this->settings, 'permalink');
477 if ($permalink) {
478 return $permalink;
479 }
480 }
481
482 return Helper::baseUrl('/');
483 }
484
485 public function getLockscreen()
486 {
487 return LockscreenService::getLockscreenSettings($this);
488 }
489
490 public function hasPaywallIntegration()
491 {
492 return !empty(Arr::get($this->settings, 'cart_product_ids', []));
493 }
494
495 public function getCustomMeta($key, $default = null)
496 {
497 return Helper::getSpaceMeta($this->id, $key, $default);
498 }
499
500 public function updateCustomMeta($key, $value)
501 {
502 return Helper::updateSpaceMeta($this->id, $key, $value);
503 }
504
505 public function getIconMark($isHtml = true)
506 {
507 if (!$isHtml) {
508 return '';
509 }
510
511 if ($this->logo) {
512 return '<img alt="" src="' . $this->logo . '"/>';
513 }
514
515 if ($imoji = Arr::get($this->settings, 'emoji')) {
516 return '<span class="fcom_emoji">' . $imoji . '</span>';
517 }
518
519 if ($svg = Arr::get($this->settings, 'shape_svg')) {
520 return '<span class="fcom_shape"><i class="el-icon">' . $svg . '</i></span>';
521 }
522
523 return '';
524 }
525
526 public function syncTopics($topicIds)
527 {
528 $topics = Term::where('taxonomy_name', 'post_topic')->whereIn('id', $topicIds)->get();
529 $topicIds = $topics->pluck('id')->toArray();
530 $existIds = [];
531
532 foreach ($topicIds as $topicId) {
533 $relation = Meta::where('object_id', $topicId)
534 ->where('object_type', 'term_space_relation')
535 ->where('meta_key', $this->id)
536 ->first();
537
538 if ($relation) {
539 $existIds[] = $relation->id;
540 continue;
541 }
542
543 // We have to create one
544 $new = Meta::create([
545 'object_id' => $topicId,
546 'meta_key' => $this->id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
547 'object_type' => 'term_space_relation'
548 ]);
549
550 $existIds[] = $new->id;
551 }
552
553 // Remove other relations
554 Meta::whereNotIn('id', $existIds)
555 ->where('object_type', 'term_space_relation')
556 ->where('meta_key', $this->id)
557 ->delete();
558
559 Utility::forgetCache('fluent_community_post_topics');
560
561 return $this;
562 }
563
564 protected static function generateNewSlug($newModel)
565 {
566 if ($newModel->title) {
567 // Remove the emojis
568 $title = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $newModel->title);
569 // get the first 30 char from the title
570 $title = mb_substr($title, 0, 30, 'UTF-8');
571 } else {
572 $title = static::$type . '-' . time();
573 }
574
575 $title = Helper::normalizeToAscii($title);
576 $title = remove_accents($title);
577
578 $title = strtolower($title);
579
580 $title = trim(preg_replace('/[^a-z0-9-_]/', ' ', $title));
581
582 $slugNum = self::withoutGlobalScopes()->where('type', static::$type)->count();
583
584 $title = sanitize_title($title, static::$type . '-' . $slugNum);
585
586 // check if the slug is already exists
587 $slug = $title;
588 $count = 1;
589 while (self::withoutGlobalScopes()->where('slug', $slug)->exists()) {
590 if ($count == 5) {
591 $count = time();
592 }
593 $slug = $title . '-' . ++$slugNum;
594 $count++;
595 }
596
597 return $slug;
598 }
599
600 public function formatSpaceData($user)
601 {
602 $userId = $user ? $user->ID : null;
603
604 $this->permissions = $this->getUserPermissions($user);
605 $this->description_rendered = wpautop($this->description);
606 $this->membership = $this->getMembership($userId);
607 $this->topics = Utility::getTopicsBySpaceId($this->id);
608
609 $headerLinks = [
610 [
611 'title' => __('Posts', 'fluent-community'),
612 'route' => [
613 'name' => 'space_feeds',
614 ]
615 ]
616 ];
617
618 if (Arr::get($this->permissions, 'can_view_members')) {
619 $headerLinks[] = [
620 'title' => __('Members', 'fluent-community'),
621 'route' => [
622 'name' => 'space_members',
623 ]
624 ];
625 }
626
627 $this->header_links = apply_filters('fluent_community/space_header_links', $headerLinks, $this);
628
629 if ($this->isAdmin($userId, true)) {
630 return $this;
631 }
632
633 $this->lockscreen_config = LockscreenService::getLockscreenConfig($this, $this->membership, true);
634
635 $spaceSettings = $this->settings;
636
637 $spaceLinks = Arr::get($spaceSettings, 'links', []);
638
639 $spaceSettings['links'] = array_values(array_filter($spaceLinks, function ($item) use ($user) {
640 return Helper::isLinkAccessible($item, $user);
641 }));
642
643 $this->settings = $spaceSettings;
644
645 return $this;
646 }
647 }
648