PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.7.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.7.0
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.7.0, at app/Models/BaseSpace.php

657 lines 18.9 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 $settings = wp_parse_args($settings, $this->defaultSettings());
368
369 if (!defined('FLUENT_COMMUNITY_PRO')) {
370 $settings['document_library'] = 'no';
371 $settings['media_gallery'] = 'no';
372 }
373
374 return $settings;
375 }
376
377 public function defaultSettings()
378 {
379 return [
380 'restricted_post_only' => 'no',
381 'emoji' => '',
382 'shape_svg' => '',
383 'custom_lock_screen' => 'no',
384 'can_request_join' => 'no',
385 'layout_style' => 'timeline',
386 'show_sidebar' => 'yes',
387 'og_image' => '',
388 'links' => [],
389 'topic_required' => 'no',
390 'hide_members_count' => 'no', // yes / no
391 'members_page_status' => 'members_only', // members_only, everybody, logged_in, admin_only
392 ];
393 }
394
395 public function setSettingsAttribute($value)
396 {
397 $this->attributes['settings'] = maybe_serialize($value);
398 }
399
400 public function getPublicPermissions()
401 {
402 if ($this->privacy == 'public') {
403
404 $hasDocuments = defined('FLUENT_COMMUNITY_PRO') && Arr::get($this->settings, 'document_library') == 'yes';
405 $hasMediaGallery = defined('FLUENT_COMMUNITY_PRO') && Arr::get($this->settings, 'media_gallery') == 'yes';
406
407 return [
408 'can_view_info' => true,
409 'can_view_posts' => true,
410 'can_view_members' => $this->canViewMembers(null),
411 'can_create_post' => false,
412 'can_view_documents' => $hasDocuments && Arr::get($this->settings, 'document_access') == 'everybody',
413 'can_view_media' => $hasMediaGallery && Arr::get($this->settings, 'media_access') == 'everybody'
414 ];
415 }
416
417 return [
418 'can_view_info' => false,
419 'can_view_posts' => false,
420 ];
421 }
422
423 public function getUserPermissions($user = null)
424 {
425 if (!$user) {
426 return $this->getPublicPermissions();
427 }
428
429 return $user->getSpacePermissions($this);
430 }
431
432 public function canViewMembers($user)
433 {
434 $viewStatus = Arr::get($this->settings, 'members_page_status');
435 if ($viewStatus === 'everybody') {
436 return true;
437 }
438
439 if (!$user) {
440 return false;
441 }
442
443 if ($viewStatus === 'logged_in') {
444 return true;
445 }
446
447 if ($viewStatus === 'members_only') {
448 $membership = $this->getMembership($user->ID);
449 return $membership && isset($membership->pivot) && $membership->pivot->status === 'active';
450 }
451
452 return $this->isAdmin($user->ID, true);
453 }
454
455 public function verifyUserPermisson($user, $permission, $exception = true)
456 {
457 $permissions = $this->getUserPermissions($user);
458
459 $hasPermission = $permissions[$permission] ?? false;
460
461 if (!$hasPermission && $exception) {
462 /* translators: %s is the permission name */
463 throw new \Exception(esc_html(sprintf(__('Sorry you do not have permission %s', 'fluent-community'), $permission)), 403);
464 }
465
466 return $hasPermission;
467 }
468
469 public function setLockscreen($settingFields)
470 {
471 return $this->updateCustomMeta('lockscreen_settings', $settingFields);
472 }
473
474 public function getPermalink()
475 {
476 if ($this->type == 'community') {
477 return Helper::baseUrl('space/' . $this->slug . '/home');
478 }
479
480 if ($this->type == 'course') {
481 return Helper::baseUrl('course/' . $this->slug . '/lessons');
482 }
483
484 if ($this->type == 'sidebar_link') {
485 $permalink = Arr::get($this->settings, 'permalink');
486 if ($permalink) {
487 return $permalink;
488 }
489 }
490
491 return Helper::baseUrl('/');
492 }
493
494 public function getLockscreen()
495 {
496 return LockscreenService::getLockscreenSettings($this);
497 }
498
499 public function hasPaywallIntegration()
500 {
501 return !empty(Arr::get($this->settings, 'cart_product_ids', []));
502 }
503
504 public function getCustomMeta($key, $default = null)
505 {
506 return Helper::getSpaceMeta($this->id, $key, $default);
507 }
508
509 public function updateCustomMeta($key, $value)
510 {
511 return Helper::updateSpaceMeta($this->id, $key, $value);
512 }
513
514 public function getIconMark($isHtml = true)
515 {
516 if (!$isHtml) {
517 return '';
518 }
519
520 if ($this->logo) {
521 return '<img alt="" src="' . $this->logo . '"/>';
522 }
523
524 if ($imoji = Arr::get($this->settings, 'emoji')) {
525 return '<span class="fcom_emoji">' . $imoji . '</span>';
526 }
527
528 if ($svg = Arr::get($this->settings, 'shape_svg')) {
529 return '<span class="fcom_shape"><i class="el-icon">' . $svg . '</i></span>';
530 }
531
532 return '';
533 }
534
535 public function syncTopics($topicIds)
536 {
537 $topics = Term::where('taxonomy_name', 'post_topic')->whereIn('id', $topicIds)->get();
538 $topicIds = $topics->pluck('id')->toArray();
539 $existIds = [];
540
541 foreach ($topicIds as $topicId) {
542 $relation = Meta::where('object_id', $topicId)
543 ->where('object_type', 'term_space_relation')
544 ->where('meta_key', $this->id)
545 ->first();
546
547 if ($relation) {
548 $existIds[] = $relation->id;
549 continue;
550 }
551
552 // We have to create one
553 $new = Meta::create([
554 'object_id' => $topicId,
555 'meta_key' => $this->id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
556 'object_type' => 'term_space_relation'
557 ]);
558
559 $existIds[] = $new->id;
560 }
561
562 // Remove other relations
563 Meta::whereNotIn('id', $existIds)
564 ->where('object_type', 'term_space_relation')
565 ->where('meta_key', $this->id)
566 ->delete();
567
568 Utility::forgetCache('fluent_community_post_topics');
569
570 return $this;
571 }
572
573 protected static function generateNewSlug($newModel)
574 {
575 if ($newModel->title) {
576 // Remove the emojis
577 $title = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $newModel->title);
578 // get the first 30 char from the title
579 $title = mb_substr($title, 0, 30, 'UTF-8');
580 } else {
581 $title = static::$type . '-' . time();
582 }
583
584 $title = Helper::normalizeToAscii($title);
585 $title = remove_accents($title);
586
587 $title = strtolower($title);
588
589 $title = trim(preg_replace('/[^a-z0-9-_]/', ' ', $title));
590
591 $slugNum = self::withoutGlobalScopes()->where('type', static::$type)->count();
592
593 $title = sanitize_title($title, static::$type . '-' . $slugNum);
594
595 // check if the slug is already exists
596 $slug = $title;
597 $count = 1;
598 while (self::withoutGlobalScopes()->where('slug', $slug)->exists()) {
599 if ($count == 5) {
600 $count = time();
601 }
602 $slug = $title . '-' . ++$slugNum;
603 $count++;
604 }
605
606 return $slug;
607 }
608
609 public function formatSpaceData($user)
610 {
611 $userId = $user ? $user->ID : null;
612
613 $this->permissions = $this->getUserPermissions($user);
614 $this->description_rendered = wpautop($this->description);
615 $this->membership = $this->getMembership($userId);
616 $this->topics = Utility::getTopicsBySpaceId($this->id);
617
618 $headerLinks = [
619 [
620 'title' => __('Posts', 'fluent-community'),
621 'route' => [
622 'name' => 'space_feeds',
623 ]
624 ]
625 ];
626
627 if (Arr::get($this->permissions, 'can_view_members')) {
628 $headerLinks[] = [
629 'title' => __('Members', 'fluent-community'),
630 'route' => [
631 'name' => 'space_members',
632 ]
633 ];
634 }
635
636 $this->header_links = apply_filters('fluent_community/space_header_links', $headerLinks, $this);
637
638 if ($this->isAdmin($userId, true)) {
639 return $this;
640 }
641
642 $this->lockscreen_config = LockscreenService::getLockscreenConfig($this, $this->membership, true);
643
644 $spaceSettings = $this->settings;
645
646 $spaceLinks = Arr::get($spaceSettings, 'links', []);
647
648 $spaceSettings['links'] = array_values(array_filter($spaceLinks, function ($item) use ($user) {
649 return Helper::isLinkAccessible($item, $user);
650 }));
651
652 $this->settings = $spaceSettings;
653
654 return $this;
655 }
656 }
657