PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.7.5
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.7.5
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 / PostMigrator.php

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

653 lines 21.6 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\Models\Comment;
6 use FluentCommunity\App\Models\Feed;
7 use FluentCommunity\App\Models\Media;
8 use FluentCommunity\App\Models\Reaction;
9 use FluentCommunity\App\Services\FeedsHelper;
10 use FluentCommunity\Framework\Support\Arr;
11
12 class PostMigrator
13 {
14
15 private $metas = [];
16
17 private $attachments = [];
18
19 private $mediaItems = [];
20
21 private $childPostIds = [];
22
23 private $createdFeedId = null;
24
25 private $spaceId = null;
26
27 private $post = null;
28
29 public function __construct($post, $metaItems = null)
30 {
31 if (is_numeric($post)) {
32 $post = fluentCommunityApp('db')->table('bp_activity')->where('id', $post)->first();
33 }
34
35 $this->post = $post;
36 $this->spaceId = BPMigratorHelper::getFcomSpaceIdByGroupId($post->item_id);
37
38 if (!$metaItems) {
39 $postMetas = fluentCommunityApp('db')->table('bp_activity_meta')
40 ->where('activity_id', $post->id)
41 ->get();
42 foreach ($postMetas as $meta) {
43 $this->metas[$meta->meta_key] = $meta->meta_value;
44 }
45 } else {
46 $this->metas = $metaItems;
47 }
48 }
49
50 public function migrate()
51 {
52 $documentIds = Arr::get($this->metas, 'bp_document_ids', '');
53 $mediaIds = Arr::get($this->metas, 'bp_media_ids', '');
54 $videoIds = Arr::get($this->metas, 'bp_video_ids', '');
55
56 if ($videoIds) {
57 if ($mediaIds) {
58 $mediaIds .= ',';
59 }
60 $mediaIds .= $videoIds;
61 }
62
63 if ($documentIds) {
64 // It's a document post
65 $documentIdsArray = array_filter(array_filter(explode(',', $documentIds)));
66 if ($documentIdsArray) {
67 $documents = fluentCommunityApp('db')->table('bp_document')
68 ->whereIn('id', $documentIdsArray)
69 ->get();
70 foreach ($documents as $document) {
71 $file = get_attached_file($document->attachment_id, true);
72 if (!$file || !file_exists($file)) {
73 continue;
74 }
75 $this->childPostIds[] = $document->activity_id;
76
77 $this->attachments[] = [
78 'path' => $file,
79 'title' => $document->title
80 ];
81 }
82 }
83 }
84
85 if ($mediaIds) {
86 $mediaIdsArray = array_filter(array_filter(explode(',', $mediaIds)));
87
88 if ($mediaIdsArray) {
89 $medias = fluentCommunityApp('db')->table('bp_media')
90 ->whereIn('id', $mediaIdsArray)
91 ->get();
92 foreach ($medias as $media) {
93 $file = get_attached_file($media->attachment_id, true);
94
95 if (!$file || !file_exists($file)) {
96 continue;
97 }
98 $mediaType = mime_content_type($file);
99 $mediaType = explode('/', $mediaType)[0];
100
101
102 // check if the image is a valid image
103 if (!in_array($mediaType, ['image', 'video'])) {
104 continue;
105 }
106
107 $image_data = [];
108
109 if ($mediaType == 'image') {
110 $image_data = wp_get_attachment_image_src($media->attachment_id, 'full');
111 if (!$image_data) {
112 $image_data = [];
113 }
114 }
115
116 $this->childPostIds[] = $media->activity_id;
117
118 $this->mediaItems[] = [
119 'path' => $file,
120 'title' => $media->title,
121 'media_type' => $mediaType,
122 'width' => Arr::get($image_data, 1, ''),
123 'height' => Arr::get($image_data, 2, '')
124 ];
125 }
126 }
127 }
128
129 $content = BPMigratorHelper::cleanUpContent($this->post->content);
130
131
132 $linkPreviewData = [];
133 $linkPreview = maybe_unserialize(Arr::get($this->metas, '_link_preview_data', ''));
134
135 if ($linkPreview && !empty($linkPreview['url'])) {
136 $linkPreviewData = array_filter([
137 'title' => $linkPreview['title'] ?? '',
138 'description' => $linkPreview['description'] ?? '',
139 'url' => $linkPreview['url'],
140 'type' => 'meta_data',
141 'image' => $linkPreview['image_url'] ?? ''
142 ]);
143
144 if (!$content) {
145 $content = '[' . $linkPreviewData['url'] . '](' . $linkPreviewData['url'] . ')';
146 }
147 }
148
149 if (!$content && !$this->attachments && !$this->mediaItems) {
150 // error_log('Missing Post: ' . $this->post->id);
151 return false;
152 }
153
154 // let's create the posts
155 $feedData = [
156 'user_id' => $this->post->user_id,
157 'title' => $this->post->post_title ?? '',
158 'message' => BPMigratorHelper::toMarkdown($content),
159 'message_rendered' => FeedsHelper::mdToHtml($content),
160 'type' => 'text',
161 'content_type' => 'text',
162 'privacy' => 'public',
163 'status' => 'published',
164 'space_id' => $this->spaceId
165 ];
166
167 [$feedData, $media] = FeedsHelper::processFeedMetaData($feedData, []);
168
169 $mediaMeta = BPMigratorHelper::getActivityMediaPreview($this->post->id);
170
171 if ($mediaMeta) {
172 $feedData['meta'] = $mediaMeta;
173 }
174
175 if ($this->attachments) {
176 $feedData['content_type'] = 'document';
177 }
178
179 $feedData['meta']['bb_activity_id'] = $this->post->id;
180
181 $feed = new Feed();
182 $feed->fill($feedData);
183 $feed->created_at = $this->post->date_recorded;
184 $feed->updated_at = $this->post->date_recorded;
185 $feed->save();
186 $this->createdFeedId = $feed->id;
187 if (!$this->attachments && !$this->mediaItems && $linkPreviewData) {
188 $feedmeta = $feed->meta ? $feed->meta : [];
189 if (empty($feedmeta['media_preview'])) {
190 $feedData['meta']['media_preview'] = $linkPreviewData;
191 if (empty($linkPreview['image']) && !empty($linkPreview['attachment_id'])) {
192 $attachmentImagePath = get_attached_file($linkPreview['attachment_id']);
193 if ($attachmentImagePath && file_exists($attachmentImagePath)) {
194 $linkPreviewMedia = BPMigratorHelper::createMediaFromPath($attachmentImagePath, [
195 'object_source' => 'feed',
196 'user_id' => $this->post->user_id,
197 'sub_object_id' => $feed->id,
198 ]);
199
200 if ($linkPreviewMedia) {
201 $linkPreviewData['image'] = $linkPreviewMedia->media_url;
202 }
203 }
204 }
205
206 $feedmeta['media_preview'] = $linkPreviewData;
207 $feed->meta = $feedmeta;
208 $feed->save();
209 }
210 }
211
212 $feed = $this->maybeProcessDocuments($feed);
213 $feed = $this->maybeProcessMediaItems($feed);
214
215 $this->syncPostReactions();
216
217 // let's migrate the comments
218 $commentIdMaps = $this->syncComments($this->post->id, $feed);
219
220 $this->syncCommentsReactions($commentIdMaps);
221
222 $feed = $feed->recountStats();
223
224 return $feed;
225 }
226
227 private function isBuddyBoss()
228 {
229 return defined('BP_PLATFORM_VERSION');
230 }
231
232 private function syncComments()
233 {
234
235 $itemIds = array_filter(array_merge([$this->post->id], $this->childPostIds));
236
237 // Let's manage the comments
238 $comments = fluentCommunityApp('db')->table('bp_activity')
239 ->where('type', 'activity_comment')
240 ->whereIn('item_id', $itemIds)
241 ->orderBy('id', 'ASC')
242 ->get();
243
244 $comments = $this->buildCommentsTree($comments);
245 $commentMaps = [];
246
247 foreach ($comments as $comment) {
248 $newComment = $this->insertBBComment($comment);
249 $commentMaps[$comment['id']] = $newComment->id;
250 if ($comment['children']) {
251 foreach ($comment['children'] as $child) {
252 $childComment = $this->insertBBComment($child, $newComment->id);
253 $commentMaps[$child['id']] = $childComment->id;
254 }
255 }
256 }
257
258 return $commentMaps;
259 }
260
261 private function syncPostReactions()
262 {
263 if (!$this->isBuddyBoss()) {
264 return;
265 }
266
267 $allItemIds = array_filter(array_merge([$this->post->id], $this->childPostIds));
268
269 // feed likes
270 $reactions = fluentCommunityApp('db')->table('bb_user_reactions')
271 ->select(['user_id', 'date_created', 'item_id'])
272 ->whereIn('item_id', $allItemIds)
273 ->where('item_type', 'activity')
274 ->get();
275
276 $likesArray = [];
277 foreach ($reactions as $reaction) {
278 $likesArray[] = [
279 'user_id' => $reaction->user_id,
280 'object_id' => $this->createdFeedId,
281 'object_type' => 'feed',
282 'type' => 'like',
283 'created_at' => $reaction->date_created,
284 'updated_at' => $reaction->date_created
285 ];
286 }
287
288 if ($likesArray) {
289 Reaction::insert($likesArray);
290 }
291 }
292
293 private function syncCommentsReactions($commentIdMaps)
294 {
295 if (!$this->isBuddyBoss()) {
296 return;
297 }
298
299 $bbCommentIds = array_keys($commentIdMaps);
300
301 if (!$bbCommentIds) {
302 return false;
303 }
304
305 $reactions = fluentCommunityApp('db')->table('bb_user_reactions')
306 ->select(['user_id', 'date_created', 'item_id'])
307 ->whereIn('item_id', $bbCommentIds)
308 ->where('item_type', 'activity_comment')
309 ->get();
310
311 $likesArray = [];
312 $likesCount = [];
313 foreach ($reactions as $reaction) {
314 $commentId = (int)Arr::get($commentIdMaps, $reaction->item_id);
315 if ($commentId) {
316 if (empty($likesCount[$commentId])) {
317 $likesCount[$commentId] = 0;
318 }
319 $likesCount[$commentId] = $likesCount[$commentId] + 1;
320 $likesArray[] = [
321 'user_id' => $reaction->user_id,
322 'object_id' => $commentId,
323 'object_type' => 'comment',
324 'parent_id' => $this->createdFeedId,
325 'type' => 'like',
326 'created_at' => $reaction->date_created,
327 'updated_at' => $reaction->date_created
328 ];
329 }
330 }
331
332 if ($likesArray) {
333 Reaction::insert($likesArray);
334 foreach ($likesCount as $commentId => $count) {
335 Comment::where('id', $commentId)->update(['reactions_count' => $count]);
336 }
337 }
338 }
339
340 private function buildCommentsTree($comments)
341 {
342 $commentTree = [];
343 $commentMap = [];
344
345 // First pass: create a map of all comments
346 foreach ($comments as $comment) {
347 $commentMap[$comment->id] = [
348 'id' => $comment->id,
349 'content' => BPMigratorHelper::cleanUpContent($comment->content),
350 'user_id' => $comment->user_id,
351 'date_recorded' => $comment->date_recorded,
352 'mptt_left' => $comment->mptt_left,
353 'mptt_right' => $comment->mptt_right,
354 'children' => []
355 ];
356 }
357
358 // Second pass: build the tree structure
359 foreach ($comments as $comment) {
360 $parentId = $comment->secondary_item_id;
361
362 if ($parentId == $comment->item_id) {
363 // This is a top-level comment
364 $commentTree[] = &$commentMap[$comment->id];
365 } else {
366 // This is a reply to another comment
367 $commentMap[$parentId]['children'][] = &$commentMap[$comment->id];
368 }
369 }
370
371 // Sort the tree based on mptt_left values
372 usort($commentTree, function ($a, $b) {
373 return $a['mptt_left'] - $b['mptt_left'];
374 });
375
376 // Recursive function to sort children
377 $sortChildren = function (&$node) use (&$sortChildren) {
378 usort($node['children'], function ($a, $b) {
379 return $a['mptt_left'] - $b['mptt_left'];
380 });
381
382 foreach ($node['children'] as &$child) {
383 $sortChildren($child);
384 }
385 };
386
387 // Sort children for each top-level comment
388 foreach ($commentTree as &$topLevelComment) {
389 $sortChildren($topLevelComment);
390 }
391
392 foreach ($commentTree as &$comment) {
393 foreach ($comment['children'] as &$child) {
394 $childComments = Arr::get($child, 'children', []);
395 if ($childComments) {
396 $comment['children'] = array_merge($comment['children'], $childComments);
397 unset($child['children']);
398 }
399 }
400
401 // short the children
402 usort($comment['children'], function ($a, $b) {
403 return $a['id'] - $b['id'];
404 });
405 }
406
407 return $commentTree;
408 }
409
410 private function insertBBComment($comment, $parentId = null)
411 {
412 $content = BPMigratorHelper::toMarkdown($comment['content']);
413
414 $comemntData = [
415 'user_id' => $comment['user_id'],
416 'post_id' => $this->createdFeedId,
417 'message' => $content,
418 'message_rendered' => FeedsHelper::mdToHtml($content),
419 'type' => 'comment'
420 ];
421
422 $media = BPMigratorHelper::getActivityMediaPreview($comment['id']);
423
424 if ($media) {
425 $comemntData['meta'] = $media;
426 }
427
428 if ($parentId) {
429 $comemntData['parent_id'] = $parentId;
430 }
431
432 $newComment = new Comment();
433 $newComment->fill($comemntData);
434 $newComment->created_at = $comment['date_recorded'];
435 $newComment->updated_at = $comment['date_recorded'];
436 $newComment->save();
437
438 return $newComment;
439 }
440
441 private function maybeProcessDocuments($feed)
442 {
443 if (!$this->attachments) {
444 return $feed;
445 }
446
447 $formattedAttachments = [];
448
449 foreach ($this->attachments as $attachment) {
450 $path = $attachment['path'];
451 $fileName = basename($path);
452
453 $originalName = $fileName;
454
455 // get the path folder from path
456 $newFileName = 'fluentcom-' . md5(wp_generate_uuid4()) . '-fluentcom-' . $fileName;
457
458 // copy the existing file to uploads/fluentcom
459 $uploadDir = wp_upload_dir();
460 $newFolder = $uploadDir['basedir'] . '/fluent-community/space_documents/';
461 if (!file_exists($newFolder)) {
462 wp_mkdir_p($newFolder);
463 }
464
465 copy($path, $newFolder . $newFileName);
466 $newPath = $newFolder . $newFileName;
467 $newUrl = $uploadDir['baseurl'] . '/fluent-community/space_documents/' . $newFileName;
468
469 // Copy that to
470 $mediaType = mime_content_type($newPath);
471
472 $mediaData = [
473 'object_source' => 'space_document',
474 'media_key' => md5($attachment['path'] . '_' . time() . '_' . wp_rand(1000, 9999)),
475 'user_id' => $feed->user_id,
476 'feed_id' => $feed->id,
477 'is_active' => 1,
478 'media_type' => $mediaType,
479 'driver' => 'local',
480 'media_path' => $newPath,
481 'media_url' => $newUrl,
482 'settings' => [
483 'original_name' => $originalName
484 ]
485 ];
486
487 $newMedia = new Media();
488
489 $newMedia->fill($mediaData);
490 $newMedia->created_at = $feed->created_at;
491 $newMedia->updated_at = $feed->created_at;
492 $newMedia->save();
493 $formattedAttachments[] = [
494 'id' => $newMedia->id,
495 'url' => $newMedia->getPrivateDownloadUrl(),
496 'media_key' => $newMedia->media_key,
497 'title' => $originalName,
498 'type' => $mediaType
499 ];
500 }
501
502 $feedMeta = $feed->meta ? $feed->meta : [];
503 $feedMeta['document_lists'] = $formattedAttachments;
504
505 $feed->meta = $feedMeta;
506 $feed->save();
507
508 return $feed;
509 }
510
511 private function maybeProcessMediaItems($feed)
512 {
513 if (!$this->mediaItems) {
514 return $feed;
515 }
516
517 $fromattedAttachments = [];
518
519 foreach ($this->mediaItems as $mediaItem) {
520 $path = $mediaItem['path'];
521
522 $fileName = basename($path);
523
524 $originalName = $fileName;
525
526 // get the path folder from path
527 $newFileName = 'fluentcom-' . md5(wp_generate_uuid4()) . '-fluentcom-' . $fileName;
528
529 // copy the existing file to uploads/fluentcom
530 $uploadDir = wp_upload_dir();
531 $newFolder = $uploadDir['basedir'] . '/fluent-community/';
532 if (!file_exists($newFolder)) {
533 wp_mkdir_p($newFolder);
534 }
535
536 copy($path, $newFolder . $newFileName);
537 $newPath = $newFolder . $newFileName;
538 $newUrl = $uploadDir['baseurl'] . '/fluent-community/' . $newFileName;
539
540
541 if ($mediaItem['media_type'] === 'image') {
542 // we should optimize the image!
543 $width = $mediaItem['width'] ?? 0;
544 if ($width > 1600) {
545 $resizedPath = $this->resizeImage($newPath);
546 if ($resizedPath !== $newPath) {
547 $newPath = $resizedPath;
548 $newUrl = str_replace($uploadDir['basedir'], $uploadDir['baseurl'], $newPath);
549 $imageData = getimagesize($newPath);
550 if ($imageData && is_array($imageData) && isset($imageData[0]) && isset($imageData[1])) {
551 $mediaItem['width'] = (int)$imageData[0];
552 $mediaItem['height'] = (int)$imageData[1];
553 if (defined('WP_CLI')) {
554 \WP_CLI::line('Resized image: ' . $width . ' to ' . $mediaItem['width'] . 'x' . $mediaItem['height']);
555 }
556 }
557 }
558 }
559 }
560
561 // Copy that to
562
563 // get width and height of the image
564 $mediaType = mime_content_type($newPath);
565
566 $settings = array_filter([
567 'original_name' => $originalName,
568 'width' => $mediaItem['width'] ?? null,
569 'height' => $mediaItem['height'] ?? null
570 ]);
571
572 $mediaData = [
573 'object_source' => 'feed',
574 'media_key' => md5($mediaItem['path'] . '_' . time() . '_' . wp_rand(1000, 9999)),
575 'user_id' => $feed->user_id,
576 'feed_id' => $feed->id,
577 'is_active' => 1,
578 'media_type' => $mediaType,
579 'driver' => 'local',
580 'media_path' => $newPath,
581 'media_url' => $newUrl,
582 'settings' => $settings
583 ];
584
585 $newMedia = new Media();
586
587 $newMedia->fill($mediaData);
588 $newMedia->created_at = $feed->created_at;
589 $newMedia->updated_at = $feed->created_at;
590 $newMedia->save();
591 $fromattedAttachments[] = array_filter([
592 'media_id' => $newMedia->id,
593 'url' => $newUrl,
594 'title' => $mediaItem['title'] ?? $originalName,
595 'type' => $mediaItem['media_type'],
596 'width' => $mediaItem['width'] ?? null,
597 'height' => $mediaItem['height'] ?? null,
598 'provider' => 'uploader'
599 ]);
600 }
601
602 if (!$fromattedAttachments) {
603 return $feed;
604 }
605
606 $feedMeta = $feed->meta ? $feed->meta : [];
607 $feedMeta['media_items'] = $fromattedAttachments;
608
609 $feed->meta = $feedMeta;
610 $feed->save();
611
612 return $feed;
613 }
614
615 private function resizeImage($path)
616 {
617 $extension = pathinfo($path, PATHINFO_EXTENSION);
618 if (!in_array(strtolower($extension), ['jpg', 'jpeg', 'png'])) {
619 return $path;
620 }
621
622 $maxWidth = 1600;
623 $editor = wp_get_image_editor($path);
624
625 if (is_wp_error($editor) || $editor->get_size()['width'] < $maxWidth) {
626 return $path;
627 }
628
629 $imageExtensions = ['jpg', 'jpeg', 'png'];
630
631
632 $newFilePath = $path;
633
634 $imageExtensions = array_map(function ($ext) {
635 return '.' . $ext;
636 }, $imageExtensions);
637
638 $newFilePath = str_replace($imageExtensions, '.webp', $newFilePath);
639
640 $editor->resize($maxWidth, null, false);
641 $editor->set_quality(90);
642 $result = $editor->save($newFilePath, 'image/webp');
643 if (is_wp_error($result)) {
644 return $path;
645 }
646
647 wp_delete_file($path);
648
649 return $newFilePath;
650 }
651
652 }
653