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

646 lines 18.3 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('Invalid group id', 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 throw new \Exception('Sorry you do not have permission ' . esc_html($permission), 403);
454 }
455
456 return $hasPermission;
457 }
458
459 public function setLockscreen($settingFields)
460 {
461 return $this->updateCustomMeta('lockscreen_settings', $settingFields);
462 }
463
464 public function getPermalink()
465 {
466 if ($this->type == 'community') {
467 return Helper::baseUrl('space/' . $this->slug . '/home');
468 }
469
470 if ($this->type == 'course') {
471 return Helper::baseUrl('course/' . $this->slug . '/lessons');
472 }
473
474 if ($this->type == 'sidebar_link') {
475 $permalink = Arr::get($this->settings, 'permalink');
476 if ($permalink) {
477 return $permalink;
478 }
479 }
480
481 return Helper::baseUrl('/');
482 }
483
484 public function getLockscreen()
485 {
486 return LockscreenService::getLockscreenSettings($this);
487 }
488
489 public function hasPaywallIntegration()
490 {
491 return !empty(Arr::get($this->settings, 'cart_product_ids', []));
492 }
493
494 public function getCustomMeta($key, $default = null)
495 {
496 return Helper::getSpaceMeta($this->id, $key, $default);
497 }
498
499 public function updateCustomMeta($key, $value)
500 {
501 return Helper::updateSpaceMeta($this->id, $key, $value);
502 }
503
504 public function getIconMark($isHtml = true)
505 {
506 if (!$isHtml) {
507 return '';
508 }
509
510 if ($this->logo) {
511 return '<img alt="" src="' . $this->logo . '"/>';
512 }
513
514 if ($imoji = Arr::get($this->settings, 'emoji')) {
515 return '<span class="fcom_emoji">' . $imoji . '</span>';
516 }
517
518 if ($svg = Arr::get($this->settings, 'shape_svg')) {
519 return '<span class="fcom_shape"><i class="el-icon">' . $svg . '</i></span>';
520 }
521
522 return '';
523 }
524
525 public function syncTopics($topicIds)
526 {
527 $topics = Term::where('taxonomy_name', 'post_topic')->whereIn('id', $topicIds)->get();
528 $topicIds = $topics->pluck('id')->toArray();
529 $existIds = [];
530
531 foreach ($topicIds as $topicId) {
532 $relation = Meta::where('object_id', $topicId)
533 ->where('object_type', 'term_space_relation')
534 ->where('meta_key', $this->id)
535 ->first();
536
537 if ($relation) {
538 $existIds[] = $relation->id;
539 continue;
540 }
541
542 // We have to create one
543 $new = Meta::create([
544 'object_id' => $topicId,
545 'meta_key' => $this->id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
546 'object_type' => 'term_space_relation'
547 ]);
548
549 $existIds[] = $new->id;
550 }
551
552 // Remove other relations
553 Meta::whereNotIn('id', $existIds)
554 ->where('object_type', 'term_space_relation')
555 ->where('meta_key', $this->id)
556 ->delete();
557
558 Utility::forgetCache('fluent_community_post_topics');
559
560 return $this;
561 }
562
563 protected static function generateNewSlug($newModel)
564 {
565 if ($newModel->title) {
566 // Remove the emojis
567 $title = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $newModel->title);
568 // get the first 30 char from the title
569 $title = substr($title, 0, 30);
570 } else {
571 $title = static::$type . '-' . time();
572 }
573
574 $title = remove_accents($title);
575
576 $title = strtolower($title);
577
578 $title = trim(preg_replace('/[^a-z0-9-_]/', ' ', $title));
579
580 $slugNum = self::withoutGlobalScopes()->where('type', static::$type)->count();
581
582 $title = sanitize_title($title, static::$type . '-' . $slugNum);
583
584 // check if the slug is already exists
585 $slug = $title;
586 $count = 1;
587 while (self::withoutGlobalScopes()->where('slug', $slug)->exists()) {
588 if ($count == 5) {
589 $count = time();
590 }
591 $slug = $title . '-' . ++$slugNum;
592 $count++;
593 }
594
595 return $slug;
596 }
597
598 public function formatSpaceData($user)
599 {
600 $userId = $user ? $user->ID : null;
601
602 $this->permissions = $this->getUserPermissions($user);
603 $this->description_rendered = wpautop($this->description);
604 $this->membership = $this->getMembership($userId);
605 $this->topics = Utility::getTopicsBySpaceId($this->id);
606
607 $headerLinks = [
608 [
609 'title' => __('Posts', 'fluent-community'),
610 'route' => [
611 'name' => 'space_feeds',
612 ]
613 ]
614 ];
615
616 if (Arr::get($this->permissions, 'can_view_members')) {
617 $headerLinks[] = [
618 'title' => __('Members', 'fluent-community'),
619 'route' => [
620 'name' => 'space_members',
621 ]
622 ];
623 }
624
625 $this->header_links = apply_filters('fluent_community/space_header_links', $headerLinks, $this);
626
627 if ($this->isAdmin($userId, true)) {
628 return $this;
629 }
630
631 $this->lockscreen_config = LockscreenService::getLockscreenConfig($this, $this->membership, true);
632
633 $spaceSettings = $this->settings;
634
635 $spaceLinks = Arr::get($spaceSettings, 'links', []);
636
637 $spaceSettings['links'] = array_values(array_filter($spaceLinks, function ($item) use ($user) {
638 return Helper::isLinkAccessible($item, $user);
639 }));
640
641 $this->settings = $spaceSettings;
642
643 return $this;
644 }
645 }
646