PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.10.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.10.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 / Modules / Migrations / Helpers / BPMigratorHelper.php

BPMigratorHelper.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.10.0, at Modules/Migrations/Helpers/BPMigratorHelper.php

829 lines 28.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\Modules\Migrations\Helpers;
4
5 use FluentCommunity\App\Functions\Utility;
6 use FluentCommunity\App\Models\BaseSpace;
7 use FluentCommunity\App\Models\Comment;
8 use FluentCommunity\App\Models\Feed;
9 use FluentCommunity\App\Models\Media;
10 use FluentCommunity\App\Models\Reaction;
11 use FluentCommunity\App\Models\Space;
12 use FluentCommunity\App\Models\SpaceGroup;
13 use FluentCommunity\App\Models\User;
14 use FluentCommunity\App\Services\Helper;
15 use FluentCommunity\Framework\Support\Arr;
16
17 class BPMigratorHelper
18 {
19 public static function getBbDataStats()
20 {
21 return [
22 'groups' => fluentCommunityApp('db')->table('bp_groups')->count(),
23 'total_posts' => fluentCommunityApp('db')->table('bp_activity')->where('type', 'activity_update')->count(),
24 'total_comments' => fluentCommunityApp('db')->table('bp_activity')->where('type', 'activity_comment')->count(),
25 'total_reactions' => class_exists('\BB_Reaction') ? fluentCommunityApp('db')->table('bb_user_reactions')->whereIn('item_type', ['activity_comment', 'activity'])->count() : 0,
26 'total_community_users' => User::count(),
27 ];
28 }
29
30 public static function migratePost($post, $spaceId = null)
31 {
32 return (new PostMigrator($post))->migrate();
33 }
34
35 public static function syncComments($activityId, Feed $feed)
36 {
37 // Let's manage the comments
38 $comments = fluentCommunityApp('db')->table('bp_activity')
39 ->where('type', 'activity_comment')
40 ->where('item_id', $activityId)
41 ->orderBy('id', 'ASC')
42 ->get();
43
44 $comments = self::buildCommentsTree($comments);
45 $commentMaps = [];
46
47 foreach ($comments as $comment) {
48 $newComment = self::insertBBComment($comment, $feed);
49 $commentMaps[$comment['id']] = $newComment->id;
50 if ($comment['children']) {
51 foreach ($comment['children'] as $child) {
52 $childComment = self::insertBBComment($child, $feed, $newComment->id);
53 $commentMaps[$child['id']] = $childComment->id;
54 }
55 }
56 }
57
58 return $commentMaps;
59 }
60
61 public static function insertBBComment($comment, $feed, $parentId = null)
62 {
63 $comemntData = [
64 'user_id' => $comment['user_id'],
65 'post_id' => $feed->id,
66 'message' => self::toMarkdown($comment['content']),
67 'message_rendered' => $comment['content'],
68 'type' => 'comment'
69 ];
70
71 $media = self::getActivityMediaPreview($comment['id']);
72
73 if ($media) {
74 $comemntData['meta'] = $media;
75 }
76
77 if ($parentId) {
78 $comemntData['parent_id'] = $parentId;
79 }
80
81 $newComment = new Comment();
82 $newComment->fill($comemntData);
83 $newComment->created_at = $comment['date_recorded'];
84 $newComment->updated_at = $comment['date_recorded'];
85 $newComment->save();
86
87 return $newComment;
88 }
89
90 public static function buildCommentsTree($comments)
91 {
92 $commentTree = [];
93 $commentMap = [];
94
95 // First pass: create a map of all comments
96 foreach ($comments as $comment) {
97 $commentMap[$comment->id] = [
98 'id' => $comment->id,
99 'content' => self::cleanUpContent($comment->content),
100 'user_id' => $comment->user_id,
101 'date_recorded' => $comment->date_recorded,
102 'mptt_left' => $comment->mptt_left,
103 'mptt_right' => $comment->mptt_right,
104 'children' => []
105 ];
106 }
107
108 // Second pass: build the tree structure
109 foreach ($comments as $comment) {
110 $parentId = $comment->secondary_item_id;
111
112 if ($parentId == $comment->item_id) {
113 // This is a top-level comment
114 $commentTree[] = &$commentMap[$comment->id];
115 } else {
116 // This is a reply to another comment
117 $commentMap[$parentId]['children'][] = &$commentMap[$comment->id];
118 }
119 }
120
121 // Sort the tree based on mptt_left values
122 usort($commentTree, function ($a, $b) {
123 return $a['mptt_left'] - $b['mptt_left'];
124 });
125
126 // Recursive function to sort children
127 $sortChildren = function (&$node) use (&$sortChildren) {
128 usort($node['children'], function ($a, $b) {
129 return $a['mptt_left'] - $b['mptt_left'];
130 });
131
132 foreach ($node['children'] as &$child) {
133 $sortChildren($child);
134 }
135 };
136
137 // Sort children for each top-level comment
138 foreach ($commentTree as &$topLevelComment) {
139 $sortChildren($topLevelComment);
140 }
141
142 foreach ($commentTree as &$comment) {
143 foreach ($comment['children'] as &$child) {
144 $childComments = $child['children'];
145 if ($childComments) {
146 $comment['children'] = array_merge($comment['children'], $childComments);
147 unset($child['children']);
148 }
149 }
150
151 // short the children
152 usort($comment['children'], function ($a, $b) {
153 return $a['id'] - $b['id'];
154 });
155 }
156
157 return $commentTree;
158 }
159
160 public static function cleanUpContent($content)
161 {
162 $pattern = '/<a class=\'bp-suggestions-mention\'[^>]*>(@[\w-]+)<\/a>/is';
163 $replacement = '$1';
164 $content = preg_replace($pattern, $replacement, $content);
165
166 // replace \" or \' with " or '
167 $content = str_replace(['\"', "\'"], ['"', "'"], $content);
168
169 // Shortcut: decode all HTML entities (including hex and decimal) to their UTF-8 characters, including emoji
170 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8');
171
172 return trim($content);
173 }
174
175 public static function toMarkdown($html)
176 {
177 // Replace header tags
178 $html = preg_replace('/<h1>(.*?)<\/h1>/', '# $1', $html);
179 $html = preg_replace('/<h2>(.*?)<\/h2>/', '## $1', $html);
180 $html = preg_replace('/<h3>(.*?)<\/h3>/', '### $1', $html);
181 $html = preg_replace('/<h4>(.*?)<\/h4>/', '#### $1', $html);
182 $html = preg_replace('/<h5>(.*?)<\/h5>/', '##### $1', $html);
183 $html = preg_replace('/<h6>(.*?)<\/h6>/', '###### $1', $html);
184
185 // Replace bold and italic tags
186 $html = preg_replace('/<strong>(.*?)<\/strong>/', '**$1**', $html);
187 $html = preg_replace('/<b>(.*?)<\/b>/', '**$1**', $html);
188 $html = preg_replace('/<em>(.*?)<\/em>/', '*$1*', $html);
189 $html = preg_replace('/<i>(.*?)<\/i>/', '*$1*', $html);
190
191 // Replace unordered lists
192 $html = preg_replace('/<ul>(.*?)<\/ul>/', "\n$0\n", $html);
193 $html = preg_replace('/<li>(.*?)<\/li>/', "- $1\n", $html);
194
195 // Replace ordered lists
196 $html = preg_replace('/<ol>(.*?)<\/ol>/', "\n$0\n", $html);
197 $html = preg_replace('/<li>(.*?)<\/li>/', "1. $1\n", $html);
198
199 // Replace links
200 $html = preg_replace('/<a href="(.*?)".*?>(.*?)<\/a>/', '[$2]($1)', $html);
201
202 // Replace images
203 $html = preg_replace('/<img src="(.*?)".*?\/?>/', '![](=$1)', $html);
204
205 // Replace blockquotes
206 $html = preg_replace('/<blockquote>(.*?)<\/blockquote>/', "> $1\n", $html);
207
208 // Replace horizontal rules
209 $html = preg_replace('/<hr\/>/', "---\n", $html);
210
211 // Replace pre and code tags
212 $html = preg_replace('/<pre><code>(.*?)<\/code><\/pre>/', "```\n\$1\n```", $html);
213 $html = preg_replace('/<code>(.*?)<\/code>/', "`$1`", $html);
214
215 return trim($html);
216 }
217
218 public static function getActivityMediaPreview($activityId)
219 {
220 return self::getMediaItems($activityId);
221 }
222
223 private static function getMediaItems($activityId)
224 {
225 $mediaPreviews = [];
226 $mediaMetas = fluentCommunityApp('db')->table('bp_activity_meta')
227 ->select(['meta_value', 'meta_key'])
228 ->where('activity_id', $activityId)
229 ->whereIn('meta_key', ['bp_media_ids', '_gif_raw_data'])
230 ->get()
231 ->keyBy('meta_key')
232 ->toArray();
233
234 if (!$mediaMetas) {
235 return null;
236 }
237
238 if (!empty($mediaMetas['bp_media_ids']) && !empty($mediaMetas['bp_media_ids']->meta_value)) {
239 $mediaMeta = $mediaMetas['bp_media_ids'];
240 $mediaIds = explode(',', $mediaMeta->meta_value);
241
242 $mediaItems = fluentCommunityApp('db')->table('bp_media')
243 ->select(['attachment_id'])
244 ->whereIn('id', $mediaIds)
245 ->get()
246 ->pluck('attachment_id')
247 ->toArray();
248
249 $mediaPosts = fluentCommunityApp('db')->table('posts')
250 ->whereIn('id', $mediaItems)
251 ->where('post_type', 'attachment')
252 ->get();
253
254 foreach ($mediaPosts as $mediaPost) {
255 if (strpos($mediaPost->post_mime_type, 'image/') === false) {
256 continue;
257 }
258 $meta = (array)get_post_meta($mediaPost->ID, '_wp_attachment_metadata', true);
259 $mediaPreviews[] = [
260 'media_id' => NULL,
261 'url' => $mediaPost->guid,
262 'type' => 'image',
263 'width' => Arr::get($meta, 'width'),
264 'height' => Arr::get($meta, 'height'),
265 'provider' => 'external'
266 ];
267 }
268 } else if (!empty($mediaMetas['_gif_raw_data'])) {
269 $giphyMeta = Utility::safeUnserialize($mediaMetas['_gif_raw_data']->meta_value);
270 $giphyMedia = Arr::get($giphyMeta, 'images.downsized_medium', []);
271 if (!$giphyMedia || empty($giphyMedia['url'])) {
272 return null;
273 }
274
275 return [
276 'media_preview' => array_filter([
277 'image' => sanitize_url($giphyMedia['url']),
278 'type' => 'image',
279 'provider' => 'giphy',
280 'height' => (int)Arr::get($giphyMedia, 'height', 0),
281 'width' => (int)Arr::get($giphyMedia, 'width', 0),
282 ])
283 ];
284 }
285
286 if (!$mediaPreviews) {
287 return null;
288 }
289
290 if (count($mediaPreviews) == 1) {
291 $media = $mediaPreviews[0];
292 $media['image'] = $media['url'];
293 unset($media['url']);
294 return [
295 'media_preview' => $media
296 ];
297 }
298
299 return [
300 'media_items' => $mediaPreviews
301 ];
302 }
303
304 public static function isBuddyBoss()
305 {
306 return defined('BP_PLATFORM_VERSION');
307 }
308
309 private static function syncPostReactions($postsId, $feedId)
310 {
311 if (!self::isBuddyBoss()) {
312 return;
313 }
314
315 // feed likes
316 $reactions = fluentCommunityApp('db')->table('bb_user_reactions')
317 ->select(['user_id', 'date_created', 'item_id'])
318 ->where('item_id', $postsId)
319 ->where('item_type', 'activity')
320 ->get();
321
322 $likesArray = [];
323 foreach ($reactions as $reaction) {
324 $likesArray[] = [
325 'user_id' => $reaction->user_id,
326 'object_id' => $feedId,
327 'object_type' => 'feed',
328 'type' => 'like',
329 'created_at' => $reaction->date_created,
330 'updated_at' => $reaction->date_created
331 ];
332 }
333
334 if ($likesArray) {
335 Reaction::insert($likesArray);
336 }
337 }
338
339 private static function syncCommentsReactions($commentIdMaps, $feedId)
340 {
341 if (!self::isBuddyBoss()) {
342 return;
343 }
344
345 $bbCommentIds = array_keys($commentIdMaps);
346
347 if (!$bbCommentIds) {
348 return false;
349 }
350
351 $reactions = fluentCommunityApp('db')->table('bb_user_reactions')
352 ->select(['user_id', 'date_created', 'item_id'])
353 ->whereIn('item_id', $bbCommentIds)
354 ->where('item_type', 'activity_comment')
355 ->get();
356
357 $likesArray = [];
358 $likesCount = [];
359 foreach ($reactions as $reaction) {
360 $commentId = (int)Arr::get($commentIdMaps, $reaction->item_id);
361 if ($commentId) {
362 if (empty($likesCount[$commentId])) {
363 $likesCount[$commentId] = 0;
364 }
365 $likesCount[$commentId] = $likesCount[$commentId] + 1;
366 $likesArray[] = [
367 'user_id' => $reaction->user_id,
368 'object_id' => $commentId,
369 'object_type' => 'comment',
370 'parent_id' => $feedId,
371 'type' => 'like',
372 'created_at' => $reaction->date_created,
373 'updated_at' => $reaction->date_created
374 ];
375 }
376 }
377
378 if ($likesArray) {
379 Reaction::insert($likesArray);
380 foreach ($likesCount as $commentId => $count) {
381 Comment::where('id', $commentId)->update(['reactions_count' => $count]);
382 }
383 }
384 }
385
386 public static function syncUser(User $user)
387 {
388 $syncedXprofile = $user->syncXProfile();
389 if (!$syncedXprofile) {
390 return false;
391 }
392
393 $xprofile = $user->xprofile;
394 // Let's sync the cover photo and avatar
395 if (!$xprofile->hasCustomAvatar()) {
396 /** @phpstan-ignore argument.type (BuddyPress overrides get_avatar_url with different signature; WP stub doesn't match BP variant) */
397 $avatar = get_avatar_url($user->ID, 'full', true);
398
399 $coverPhoto = bp_attachments_get_attachment(
400 'url',
401 array(
402 'object_dir' => 'members',
403 'item_id' => $user->ID,
404 )
405 );
406
407 $hasChange = false;
408 if ($coverPhoto) {
409 $path = self::getFilePathFromUrl($coverPhoto);
410 if ($path) {
411 $media = self::createMediaFromPath($path, [
412 'object_source' => 'user_cover_photo',
413 'user_id' => $user->ID
414 ]);
415 if ($media) {
416 $meta = $xprofile->meta;
417 $meta['cover_photo'] = $coverPhoto;
418 $xprofile->meta = $meta;
419 $hasChange = true;
420 }
421 }
422 }
423
424 if ($avatar && !strpos($avatar, 'gravatar.com')) {
425 $path = self::getFilePathFromUrl($avatar);
426 if ($path) {
427 $media = self::createMediaFromPath($path, [
428 'object_source' => 'user_photo',
429 'user_id' => $user->ID
430 ]);
431 if ($media) {
432 $avatar = $media->media_url;
433 $xprofile->avatar = $avatar;
434 $hasChange = true;
435 }
436 }
437 }
438
439 if ($hasChange) {
440 $xprofile->save();
441 }
442 }
443
444 if (!self::isBuddyBoss()) {
445 $favIds = get_user_meta($user->ID, 'bp_favorite_activities', true);
446 if ($favIds) {
447 $favFeedIds = fluentCommunityApp('db')->table('bp_activity_meta')
448 ->whereIn('activity_id', $favIds)
449 ->select(['meta_value'])
450 ->where('meta_key', '_fcom_feed_id')
451 ->get()
452 ->pluck('meta_value')
453 ->toArray();
454
455 if ($favFeedIds) {
456 $favPosts = Feed::whereIn('id', $favFeedIds)->get();
457 foreach ($favPosts as $favPost) {
458 $exist = Reaction::where('user_id', $user->ID)
459 ->where('object_id', $favPost->id)
460 ->where('type', 'like')
461 ->objectType('feed')
462 ->exists();
463
464 if (!$exist) {
465 Reaction::create([
466 'user_id' => get_current_user_id(),
467 'object_id' => $favPost->id,
468 'type' => 'like',
469 'object_type' => 'feed'
470 ]);
471
472 $favPost->reactions_count = (int)$favPost->reactions_count + 1;
473 $favPost->save();
474 }
475 }
476 }
477 }
478 }
479
480 return true;
481 }
482
483 public static function migrateGroupData($group, $force = true)
484 {
485 $exitMeta = fluentCommunityApp('db')->table('bp_groups_groupmeta')
486 ->where('group_id', $group->id)
487 ->where('meta_key', '_fcom_space_id')
488 ->first();
489
490 $existingSpace = null;
491 if ($exitMeta) {
492 $existingSpace = Space::find($exitMeta->meta_value);
493 if ($existingSpace && !$force) {
494 return $existingSpace;
495 }
496 }
497
498 // Create a new space group
499 $spaceGroup = null;
500 if (!empty($group->space_menu_id)) {
501 $spaceGroup = SpaceGroup::find($group->space_menu_id);
502 }
503
504 $serial = BaseSpace::when($spaceGroup, function ($q) use ($spaceGroup) {
505 $q->where('parent_id', $spaceGroup->id);
506 })->max('serial') + 1;
507
508 $privacy = $group->status;
509
510 if ($privacy == 'hidden') {
511 $privacy = 'secret';
512 } else if (!in_array($privacy, ['public', 'private'])) {
513 $privacy = 'private';
514 }
515
516 $postBy = fluentCommunityApp('db')->table('bp_groups_groupmeta')
517 ->where('group_id', $group->id)
518 ->where('meta_key', 'activity_feed_status')
519 ->first();
520
521 $restrictedPostOnly = 'no';
522 if ($postBy && $postBy->meta_value != 'members') {
523 $restrictedPostOnly = 'yes';
524 }
525
526 $settings = [
527 'restricted_post_only' => $restrictedPostOnly
528 ];
529
530 $documentStatus = fluentCommunityApp('db')->table('bp_groups_groupmeta')
531 ->where('group_id', $group->id)
532 ->where('meta_key', 'document_status')
533 ->first();
534
535 if ($documentStatus) {
536 $settings = wp_parse_args($settings, [
537 'document_library' => 'yes',
538 'document_access' => 'members_only',
539 'document_upload' => $documentStatus === 'mods' ? 'admin_only' : 'members_only'
540 ]);
541 }
542
543 $groupData = [
544 'title' => sanitize_text_field($group->name),
545 'slug' => Utility::slugify($group->slug),
546 'privacy' => $privacy,
547 'description' => wp_kses_post(wp_unslash($group->description)),
548 'settings' => $settings,
549 'parent_id' => $spaceGroup ? $spaceGroup->id : null,
550 'serial' => $serial ?: 1
551 ];
552
553 if ($existingSpace) {
554 $existingSpace->fill($groupData);
555 $existingSpace->save();
556 } else {
557 $exist = BaseSpace::where('slug', $groupData['slug'])
558 ->exists();
559
560 if ($exist) {
561 $groupData['slug'] = $groupData['slug'] . '-' . time();
562 }
563
564 $existingSpace = Space::create($groupData);
565
566 fluentCommunityApp('db')->table('bp_groups_groupmeta')
567 ->insert([
568 'group_id' => $group->id,
569 'meta_key' => '_fcom_space_id', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
570 'meta_value' => $existingSpace->id // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
571 ]);
572 }
573
574 $groupLogo = bp_core_fetch_avatar(
575 array(
576 'item_id' => $group->id,
577 'avatar_dir' => 'group-avatars',
578 'object' => 'group',
579 'type' => 'full',
580 'html' => false,
581 )
582 );
583 if ($groupLogo) {
584 $filePath = self::getFilePathFromUrl($groupLogo);
585 if ($filePath) {
586 $media = self::createMediaFromPath($filePath, [
587 'object_source' => 'space_logo',
588 'sub_object_id' => $existingSpace->id
589 ]);
590 if ($media) {
591 $existingSpace->logo = $media->media_url;
592 }
593 }
594 }
595
596 $group_cover_image = bp_attachments_get_attachment(
597 'url',
598 array(
599 'object_dir' => 'groups',
600 'item_id' => $group->id,
601 )
602 );
603 if ($group_cover_image) {
604 if ($groupLogo) {
605 $filePath = self::getFilePathFromUrl($group_cover_image);
606 if ($filePath) {
607 $media = self::createMediaFromPath($filePath, [
608 'object_source' => 'space_cover_photo',
609 'sub_object_id' => $existingSpace->id
610 ]);
611 if ($media) {
612 $existingSpace->cover_photo = $media->media_url;
613 }
614 }
615 }
616 }
617
618 $existingSpace->save();
619
620 return $existingSpace;
621 }
622
623 public static function deleteCurrentData()
624 {
625 // Capture an audit context before the wipe so this destructive erasure of member PII is
626 // traceable (who/when/how much) and downstream modules can purge remote copies.
627 $auditContext = [
628 'user_id' => get_current_user_id(),
629 'timestamp' => current_time('mysql'),
630 'counts' => [
631 'feeds' => \FluentCommunity\App\Models\Feed::count(),
632 'comments' => \FluentCommunity\App\Models\Comment::count(),
633 'reactions' => \FluentCommunity\App\Models\Reaction::count(),
634 'media' => \FluentCommunity\App\Models\Media::count(),
635 'activities' => \FluentCommunity\App\Models\Activity::count(),
636 'profiles' => \FluentCommunity\App\Models\XProfile::count(),
637 'spaces' => \FluentCommunity\App\Models\Space::where('type', 'community')->count(),
638 ],
639 ];
640
641 do_action('fluent_community/migration/before_delete_current_data', $auditContext);
642
643 // delete the folder in uploads folder fluent-community with php please
644 $uploadDir = wp_upload_dir();
645 $fcomDir = $uploadDir['basedir'] . '/fluent-community';
646 if (is_dir($fcomDir)) {
647 // delete the directory and all its content
648 self::deleteDirectory($fcomDir);
649 }
650
651 // reset fluent community data
652 \FluentCommunity\App\Models\Feed::truncate();
653 \FluentCommunity\App\Models\Comment::truncate();
654 \FluentCommunity\App\Models\Reaction::truncate();
655 \FluentCommunity\App\Models\Media::truncate();
656 \FluentCommunity\App\Models\Activity::truncate();
657 \FluentCommunity\App\Models\XProfile::truncate();
658 \FluentCommunity\App\Models\Space::where('type', 'community')->delete();
659
660 // reset buddypress meta data
661 fluentCommunityApp('db')->table('bp_groups_groupmeta')->where('meta_key', '_fcom_space_id')->delete();
662 fluentCommunityApp('db')->table('bp_activity_meta')->where('meta_key', '_fcom_feed_id')->delete();
663
664 delete_option('_bp_fcom_group_maps');
665 delete_option('_bp_fcom_last_post_id');
666 delete_option('_bp_fcom_last_user_id');
667 delete_option('_bp_fcom_last_migrated_member_id');
668
669 do_action('fluent_community/migration/after_delete_current_data', $auditContext);
670 }
671
672 public static function getFcomSpaceIdByGroupId($bbGroupId)
673 {
674 if (!$bbGroupId) {
675 return null;
676 }
677
678 static $groupMaps;
679
680 if (!$groupMaps) {
681 $groupMaps = get_option('_bp_fcom_group_maps', []);
682 }
683
684 return isset($groupMaps[$bbGroupId]) ? $groupMaps[$bbGroupId] : null;
685
686 }
687
688 private static function deleteDirectory($dir)
689 {
690 // Include WordPress filesystem API
691 if (!function_exists('WP_Filesystem')) {
692 require_once ABSPATH . 'wp-admin/includes/file.php';
693 }
694
695 // Initialize the WP_Filesystem
696 global $wp_filesystem;
697 if (!WP_Filesystem()) {
698 return false; // Filesystem initialization failed
699 }
700
701 // Sanitize the directory path
702 $dir = trailingslashit($dir);
703
704 // Check if directory exists
705 if (!$wp_filesystem->is_dir($dir)) {
706 return false; // Directory doesn't exist
707 }
708
709 // Delete directory and its contents recursively
710 $result = $wp_filesystem->rmdir($dir, true);
711
712 return $result;
713 }
714
715 private static function getFilePathFromUrl($url)
716 {
717 // Parse the URL to get its components
718 $parsed_url = wp_parse_url($url);
719 // Get the path from the URL
720 $url_path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
721
722 // Remove the site URL part to get the relative path
723
724 // handle http / https both
725 $site_url = site_url(''); // e.g., https://example.com
726 if (strpos($url, $site_url) === false) {
727 $site_url = str_replace('https://', 'http://', $site_url);
728 }
729 if (strpos($url, $site_url) === false) {
730 $site_url = str_replace('http://', 'https://', $site_url);
731 }
732
733 $relative_path = str_replace($site_url, '', $url);
734
735 // Combine with ABSPATH to get the full file path
736 $file_path = ABSPATH . ltrim($relative_path, '/');
737
738 // Check if the file exists
739 if (file_exists($file_path)) {
740 return $file_path;
741 }
742
743 return false; // Return false if the file doesn't exist
744 }
745
746 public static function createMediaFromPath($path, $mediaArgs = [])
747 {
748 $defaults = [
749 'media_key' => md5(wp_generate_uuid4()),
750 'is_active' => 1,
751 'driver' => 'local',
752 'media_path' => '',
753 'media_url' => '',
754 'settings' => [],
755 'object_source' => ''
756 ];
757
758 $mediaArgs = wp_parse_args($mediaArgs, $defaults);
759
760
761 if (empty($mediaArgs['object_source'])) {
762 return null;
763 }
764
765 $fileName = basename($path);
766
767 $mediaArgs['settings']['original_name'] = $fileName;
768
769 // copy the file to uploads/fluent-community
770 $uploadDir = wp_upload_dir();
771 $fcomDir = $uploadDir['basedir'] . '/fluent-community';
772 if (!is_dir($fcomDir)) {
773 wp_mkdir_p($fcomDir);
774 }
775
776 $newFilePath = $fcomDir . '/fluentcom-' . md5(wp_generate_uuid4()) . '-fluentcom-' . $fileName;
777
778
779 if (!copy($path, $newFilePath)) {
780 return null;
781 }
782
783 $fileUrl = str_replace($fcomDir, wp_upload_dir()['baseurl'] . '/fluent-community', $newFilePath);
784
785 $mediaArgs['media_path'] = $newFilePath;
786 $mediaArgs['media_url'] = $fileUrl;
787
788 $mediaArgs['media_type '] = mime_content_type($newFilePath);
789
790 $imageSizes = wp_getimagesize($newFilePath);
791
792
793 if ($imageSizes && count($imageSizes) >= 2) {
794 $mediaArgs['settings']['width'] = $imageSizes[0];
795 $mediaArgs['settings']['height'] = $imageSizes[1];
796 }
797
798 return Media::create($mediaArgs);
799 }
800
801 public static function recalculateUserPoints($userId)
802 {
803 // SUM of all the points of the Comment Model
804 $commentPoints = Comment::where('user_id', $userId)
805 ->sum('reactions_count');
806
807 $postsPoints = Feed::where('user_id', $userId)
808 ->sum('reactions_count');
809
810 return $commentPoints + $postsPoints;
811 }
812
813 public static function maybeEnableFollowersModule()
814 {
815 if (!defined('FLUENT_COMMUNITY_PRO')) {
816 return;
817 }
818
819 $isEnabled = Helper::isFeatureEnabled('followers_module');
820 if ($isEnabled) {
821 return;
822 }
823
824 if (fluentCommunityApp('db')->table('bp_friends')->exists()) {
825 \FluentCommunityPro\App\Modules\Followers\FollowerHelper::updateSettings(['is_enabled' => 'yes']);
826 }
827 }
828 }
829