diff --git a/app/Console/Commands/MigrateAnnouncementCommand.php b/app/Console/Commands/MigrateAnnouncementCommand.php new file mode 100644 index 0000000..868642d --- /dev/null +++ b/app/Console/Commands/MigrateAnnouncementCommand.php @@ -0,0 +1,62 @@ +info('Starting announcement migration...'); + + $legacyConn = DB::connection('mysql_second'); + + // Get legacy news with category 1 (Announcements) + $legacyAnnouncements = $legacyConn->table('news') + ->where('category', '1') + ->get(); + + if ($legacyAnnouncements->isEmpty()) { + $this->warn('No announcements found with category 1.'); + + return; + } + + $this->withProgressBar($legacyAnnouncements, function ($legacy) { + DB::table('announcements')->updateOrInsert( + ['id' => $legacy->id], + [ + 'title' => $legacy->title, + 'content' => $legacy->content, + 'type' => AnnouncementType::PUBLIC->value, + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + 'deleted_at' => $legacy->deleted_at, + ] + ); + }); + $this->newLine(); + + $this->info('Announcement migration completed successfully!'); + } +} diff --git a/app/Console/Commands/MigrateClassificationCommand.php b/app/Console/Commands/MigrateClassificationCommand.php new file mode 100644 index 0000000..9709f64 --- /dev/null +++ b/app/Console/Commands/MigrateClassificationCommand.php @@ -0,0 +1,76 @@ +info('Starting classification migration...'); + + $legacyConn = DB::connection('mysql_second'); + $newConn = DB::connection('mysql'); + + // Migrate Classifications + $this->info('Migrating classifications...'); + $legacyClassifications = $legacyConn->table('classifications')->get(); + + $this->withProgressBar($legacyClassifications, function ($legacy) { + DB::table('classifications')->updateOrInsert( + ['id' => $legacy->id], + [ + 'name' => $legacy->name, + 'is_active' => $legacy->status == '1' ? IsActive::ACTIVE->value : IsActive::INACTIVE->value, + 'sort_order' => $legacy->enhancer ?? 0, + 'created_at' => $legacy->created_at, + 'updated_at' => $legacy->updated_at, + 'deleted_at' => $legacy->deleted_at, + ] + ); + }); + $this->newLine(); + + // Migrate Sub Classifications + $this->info('Migrating sub classifications...'); + $legacySubClassifications = $legacyConn->table('sub_classifications')->get(); + + $this->withProgressBar($legacySubClassifications, function ($legacy) { + DB::table('sub_classifications')->updateOrInsert( + ['id' => $legacy->id], + [ + 'classification_id' => $legacy->classification, + 'name' => $legacy->name, + 'is_active' => $legacy->status == '1' ? IsActive::ACTIVE->value : IsActive::INACTIVE->value, + 'sort_order' => $legacy->enhancer ?? 0, + 'created_at' => $legacy->created_at, + 'updated_at' => $legacy->updated_at, + 'deleted_at' => $legacy->deleted_at, + ] + ); + }); + $this->newLine(); + + $this->info('Migration completed successfully!'); + } +} diff --git a/app/Console/Commands/MigrateCompanyCommand.php b/app/Console/Commands/MigrateCompanyCommand.php new file mode 100644 index 0000000..89e0018 --- /dev/null +++ b/app/Console/Commands/MigrateCompanyCommand.php @@ -0,0 +1,183 @@ +info('Starting company migration...'); + + $legacyConn = DB::connection('mysql_second'); + $legacyCompanies = $legacyConn->table('companies')->get(); + + if ($legacyCompanies->isEmpty()) { + $this->warn('No companies found in legacy database.'); + + return; + } + + $this->withProgressBar($legacyCompanies, function ($legacy) use ($legacyConn) { + // Find user in NEW database. Legcy enhancer is the user_id. + $user = DB::table('users')->where('id', $legacy->enhancer)->first(); + + // If not found by ID, try by email (sometimes IDs change during user migration if not careful) + if (! $user) { + $user = DB::table('users')->where('email', $legacy->email)->first(); + } + + if (! $user) { + // If user still not found, we skip this company for now as it's orphaned + return; + } + + // 1. Migrate Company + DB::table('companies')->updateOrInsert( + ['id' => $legacy->id], + [ + 'user_id' => $user->id, + 'name' => $legacy->name, + 'email' => $legacy->email, + 'address' => $legacy->address, + 'director_name' => $legacy->director, + 'director_nik' => $legacy->nick_director, + 'deed_incorporation' => str()->limit($legacy->deed_of_establishment, 150, ''), + 'trade_license' => str()->limit($legacy->trade_license, 150, ''), + 'tax_id_number' => str()->limit($legacy->tax_id_number, 150, ''), + 'taxable_enterprise' => str()->limit($legacy->taxable_enterprise, 150, ''), + 'annual_tax_return' => str()->limit($legacy->annual_tax_statement, 150, ''), + 'domicile_certificate' => str()->limit($legacy->domicile, 150, ''), + 'profile' => str()->limit($legacy->profile ?? '-', 150, ''), + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + 'deleted_at' => $legacy->deleted_at, + ] + ); + + // 2. Migrate Verification Request + $status = $this->mapVerificationStatus($legacy->status_edit); + DB::table('verification_requests')->updateOrInsert( + ['company_id' => $legacy->id], + [ + 'submitted_by' => $user->id, + 'status' => $status, + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + ] + ); + + // 3. Migrate Partner Media (from legacy 'media' table matching company ID) + $legacyMediaList = $legacyConn->table('media')->where('company', $legacy->id)->get(); + foreach ($legacyMediaList as $legacyMedia) { + DB::table('partner_media')->updateOrInsert( + ['id' => $legacyMedia->id], + [ + 'company_id' => $legacy->id, + 'name' => $legacyMedia->name, + 'address' => $legacyMedia->address, + 'link' => $this->extractMediaLink($legacyMedia->link), + 'type' => $this->mapMediaType($legacyMedia->type), + 'classification' => $this->mapMediaClassification($legacyMedia->classification), + 'journalism_organization' => $legacyMedia->organization, + 'press_council_certificate' => $legacyMedia->certificate, + 'created_at' => $legacyMedia->created_at ?? now(), + 'updated_at' => $legacyMedia->updated_at ?? now(), + 'deleted_at' => $legacyMedia->deleted_at, + ] + ); + } + }); + + $this->newLine(); + $this->info('Company migration completed successfully!'); + } + + private function mapVerificationStatus($legacyStatus): int + { + return match ((string) $legacyStatus) { + '1', '3' => VerificationStatus::APPROVED->value, + '2' => VerificationStatus::REJECTED->value, + default => VerificationStatus::PENDING->value, + }; + } + + private function mapMediaType($legacyType): int + { + return match (strtolower($legacyType ?? '')) { + 'online' => MediaType::ONLINE->value, + 'cetak' => MediaType::PRINT->value, + 'radio' => MediaType::RADIO->value, + 'televisi' => MediaType::TELEVISION->value, + default => MediaType::ONLINE->value, + }; + } + + private function mapMediaClassification($legacyClass): int + { + return match (strtolower($legacyClass ?? '')) { + 'lokal' => MediaClassification::LOCAL->value, + 'regional' => MediaClassification::REGIONAL->value, + 'nasional' => MediaClassification::NATIONAL->value, + default => MediaClassification::LOCAL->value, + }; + } + + private function extractMediaLink(?string $link): ?string + { + if (empty($link)) { + return null; + } + + // Find all URLs (starts with http or https) + preg_match_all('/(https?:\/\/[^\s]+)/i', $link, $matches); + + // If no URL found, return the original string truncated + if (empty($matches[0])) { + return Str::limit($link, 50, ''); + } + + // Try to find the main media website URL + foreach ($matches[0] as $url) { + $url = rtrim($url, ','); // clean trailing comma if any + + // Skip common non-media-website URLs if there's an alternative + if (count($matches[0]) > 1 && ( + str_contains($url, 'google.') || + str_contains($url, 'drive.') || + str_contains($url, 'share.') || + str_contains($url, 'bit.ly') + )) { + continue; + } + + return Str::limit($url, 50, ''); + } + + // Fallback to the first URL found + return Str::limit($matches[0][0], 50, ''); + } +} diff --git a/app/Console/Commands/MigrateContentRecapCommand.php b/app/Console/Commands/MigrateContentRecapCommand.php new file mode 100644 index 0000000..b42413b --- /dev/null +++ b/app/Console/Commands/MigrateContentRecapCommand.php @@ -0,0 +1,116 @@ +info('Starting content recap migration...'); + + $legacyConn = DB::connection('mysql_second'); + + // 1. Migrate Classifications first + $this->info('Migrating classifications for content recap...'); + $legacyRecapClasses = $legacyConn->table('content_recap_classifications')->get(); + $classMapping = []; + + foreach ($legacyRecapClasses as $legacyClass) { + // Check by name to avoid duplicates in classifications table + $existing = DB::table('classifications')->where('name', $legacyClass->name)->first(); + + if ($existing) { + $classMapping[$legacyClass->id] = $existing->id; + } else { + $newId = DB::table('classifications')->insertGetId([ + 'name' => $legacyClass->name, + 'is_active' => IsActive::ACTIVE->value, + 'sort_order' => 0, + 'created_at' => $legacyClass->created_at ?? now(), + 'updated_at' => $legacyClass->updated_at ?? now(), + 'deleted_at' => $legacyClass->deleted_at, + ]); + $classMapping[$legacyClass->id] = $newId; + } + } + + // 2. Migrate Content Recaps + $this->info('Migrating content recaps...'); + $legacyRecaps = $legacyConn->table('content_recap')->get(); + + if ($legacyRecaps->isEmpty()) { + $this->warn('No content recaps found in legacy database.'); + + return; + } + + $this->withProgressBar($legacyRecaps, function ($legacy) use ($classMapping) { + $classificationId = $classMapping[$legacy->classification_id] ?? null; + + // Fallback for classification if not found in master table mapping + if (! $classificationId && $legacy->classification) { + $classificationId = DB::table('classifications') + ->where('name', $legacy->classification) + ->value('id'); + } + + // If still no classification, skip or use a default one? + // The table requires classification_id so we must have one. + if (! $classificationId) { + return; // Skip if no classification + } + + DB::table('content_recaps')->updateOrInsert( + ['id' => $legacy->id], + [ + 'classification_id' => $classificationId, + 'title' => $legacy->title, + 'link' => Str::limit($legacy->link, 50, ''), + 'posting_date' => $legacy->posting_date === '0000-00-00' ? now()->toDateString() : $legacy->posting_date, + 'type' => $this->mapContentType($legacy->content_type), + 'channel' => Str::limit($legacy->channel, 20, ''), + 'social_media' => Str::limit($legacy->social_media ?? '-', 50, ''), + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + 'deleted_at' => $legacy->deleted_at, + ] + ); + }); + $this->newLine(); + + $this->info('Content recap migration completed successfully!'); + } + + private function mapContentType(?string $legacyType): string + { + return match (strtolower($legacyType ?? '')) { + 'foto' => ContentType::FOTO->value, + 'grafis' => ContentType::GRAPHIC->value, + 'audio video' => ContentType::VIDEO->value, + default => ContentType::FOTO->value, + }; + } +} diff --git a/app/Console/Commands/MigrateCooperationCommand.php b/app/Console/Commands/MigrateCooperationCommand.php new file mode 100644 index 0000000..590f6a3 --- /dev/null +++ b/app/Console/Commands/MigrateCooperationCommand.php @@ -0,0 +1,239 @@ +info('Starting comprehensive cooperation and report migration...'); + + $legacyConn = DB::connection('mysql_second'); + $offset = 10000; + + // 1. Migrate media_orders -> cooperations & task_assignments + $this->info('Migrating media_orders...'); + $legacyOrders = $legacyConn->table('media_orders')->get(); + $this->withProgressBar($legacyOrders, function ($legacy) { + $status = $legacy->deleted_at ? CooperationStatus::COMPLETED->value : CooperationStatus::ASSIGNMENT->value; + + DB::table('cooperations')->updateOrInsert( + ['id' => $legacy->id], + [ + 'title' => Str::limit($legacy->name, 200), + 'initial_submission_date' => $legacy->begin, + 'final_submission_date' => $legacy->end, + 'description' => $legacy->description ?? '-', + 'status' => $status, + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + 'deleted_at' => $legacy->deleted_at, + ] + ); + + // Task Assignment + DB::table('task_assignments')->updateOrInsert( + ['id' => $legacy->id], + [ + 'cooperation_id' => $legacy->id, + 'start_date' => $legacy->begin, + 'end_date' => $legacy->end, + 'task_description' => $legacy->description ?? '-', + 'report_amount' => $legacy->amount_report ?? 1, + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + ] + ); + + // Pivot Media + if ($legacy->media) { + $mediaIds = explode(',', $legacy->media); + $statuses = explode(',', $legacy->status); + foreach ($mediaIds as $index => $mediaId) { + $mediaId = trim($mediaId); + if (empty($mediaId)) { + continue; + } + + $mediaStatus = isset($statuses[$index]) ? trim($statuses[$index]) : '1'; + $approval = $this->mapApprovalStatus($mediaStatus); + + DB::table('cooperation_media')->updateOrInsert( + ['cooperation_id' => $legacy->id, 'partner_media_id' => $mediaId], + ['status' => $approval, 'created_at' => $legacy->created_at ?? now(), 'updated_at' => $legacy->updated_at ?? now()] + ); + + DB::table('media_task_assignment')->updateOrInsert( + ['task_assignment_id' => $legacy->id, 'partner_media_id' => $mediaId], + ['status' => $approval, 'created_at' => $legacy->created_at ?? now(), 'updated_at' => $legacy->updated_at ?? now()] + ); + } + } + }); + $this->newLine(); + + // 2. Migrate media_cooperation -> cooperations & task_assignments + $this->info('Migrating media_cooperation...'); + $legacyCoops = $legacyConn->table('media_cooperation')->get(); + $this->withProgressBar($legacyCoops, function ($legacy) use ($offset) { + $newId = $legacy->id + $offset; + $status = $legacy->deleted_at ? CooperationStatus::COMPLETED->value : CooperationStatus::PENDING->value; + + DB::table('cooperations')->updateOrInsert( + ['id' => $newId], + [ + 'title' => Str::limit($legacy->name, 200), + 'initial_submission_date' => $legacy->begin, + 'final_submission_date' => $legacy->end, + 'description' => $legacy->description ?? '-', + 'status' => $status, + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + 'deleted_at' => $legacy->deleted_at, + ] + ); + + // Task Assignment + DB::table('task_assignments')->updateOrInsert( + ['id' => $newId], + [ + 'cooperation_id' => $newId, + 'start_date' => $legacy->begin, + 'end_date' => $legacy->end, + 'task_description' => $legacy->description ?? '-', + 'report_amount' => 1, + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + ] + ); + + // Pivot Media + if ($legacy->media) { + $mediaIds = explode(',', $legacy->media); + foreach ($mediaIds as $mediaId) { + $mediaId = trim($mediaId); + if (empty($mediaId)) { + continue; + } + + DB::table('cooperation_media')->updateOrInsert( + ['cooperation_id' => $newId, 'partner_media_id' => $mediaId], + ['status' => ApprovalStatus::PENDING->value, 'created_at' => $legacy->created_at ?? now(), 'updated_at' => $legacy->updated_at ?? now()] + ); + + DB::table('media_task_assignment')->updateOrInsert( + ['task_assignment_id' => $newId, 'partner_media_id' => $mediaId], + ['status' => ApprovalStatus::PENDING->value, 'created_at' => $legacy->created_at ?? now(), 'updated_at' => $legacy->updated_at ?? now()] + ); + } + } + }); + $this->newLine(); + + // 3. Migrate proposals + $this->info('Migrating proposals...'); + $legacyProposals = $legacyConn->table('propose_media_cooperation')->get(); + $this->withProgressBar($legacyProposals, function ($legacy) use ($offset) { + $media = DB::table('partner_media') + ->join('companies', 'partner_media.company_id', '=', 'companies.id') + ->where('companies.user_id', $legacy->enhancer) + ->select('partner_media.id') + ->first(); + + if (! $media) { + return; + } + + DB::table('cooperation_proposals')->updateOrInsert( + ['id' => $legacy->id], + [ + 'cooperation_id' => $legacy->media_cooperation_id + $offset, + 'partner_media_id' => $media->id, + 'description' => $legacy->rejection_reason ?? '-', + 'e_catalog' => Str::limit($legacy->e_catalog, 200), + 'status' => $this->mapApprovalStatus($legacy->status), + 'submitted_at' => $legacy->created_at ?? now(), + 'responded_at' => $legacy->updated_at, + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + ] + ); + }); + $this->newLine(); + + // 4. Migrate reports + $this->info('Migrating reports...'); + $legacyReports = $legacyConn->table('reports')->get(); + $this->withProgressBar($legacyReports, function ($legacy) { + // Find media_task_assignment_id + // We assume media_order in reports refers to media_orders table primarily + $assignment = DB::table('media_task_assignment') + ->where('task_assignment_id', $legacy->media_order) + ->where('partner_media_id', $legacy->media) + ->first(); + + if (! $assignment) { + return; + } + + DB::table('reports')->updateOrInsert( + ['id' => $legacy->id], + [ + 'media_task_assignment_id' => $assignment->id, + 'title' => Str::limit($legacy->title, 200), + 'publication_date' => $legacy->broadcast ?? $legacy->created_at ?? now(), + 'link' => Str::limit($legacy->link, 50), + 'description' => 'Legacy Proof: '.($legacy->proof ?? '-'), + 'status' => $this->mapReportStatus($legacy->status), + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + 'deleted_at' => $legacy->deleted_at, + ] + ); + }); + $this->newLine(); + + $this->info('Cooperation and report migration completed!'); + } + + private function mapApprovalStatus($legacy): int + { + return match ((string) $legacy) { + '1', '4' => ApprovalStatus::ACCEPTED->value, + '2', '0' => ApprovalStatus::REJECTED->value, // Assume 0 is rejected/inactive in some contexts, but let's see + default => ApprovalStatus::PENDING->value, + }; + } + + private function mapReportStatus($legacy): int + { + return match ((string) $legacy) { + '1' => ApprovalStatus::ACCEPTED->value, + '2' => ApprovalStatus::REJECTED->value, + default => ApprovalStatus::PENDING->value, + }; + } +} diff --git a/app/Console/Commands/MigrateDepartmentCommand.php b/app/Console/Commands/MigrateDepartmentCommand.php new file mode 100644 index 0000000..01bcf78 --- /dev/null +++ b/app/Console/Commands/MigrateDepartmentCommand.php @@ -0,0 +1,57 @@ +info('Starting department migration...'); + + $legacyConn = DB::connection('mysql_second'); + + // Migrate Departments (Agencies) + $this->info('Migrating departments...'); + $legacyAgencies = $legacyConn->table('agencies')->get(); + + $this->withProgressBar($legacyAgencies, function ($legacy) { + DB::table('departments')->updateOrInsert( + ['id' => $legacy->id], + [ + 'name' => $legacy->name, + 'alias' => Str::limit($legacy->name, 20, ''), + 'is_active' => IsActive::ACTIVE->value, + 'sort_order' => 0, + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + 'deleted_at' => $legacy->deleted_at, + ] + ); + }); + $this->newLine(); + + $this->info('Migration completed successfully!'); + } +} diff --git a/app/Console/Commands/MigrateIssueManagementCommand.php b/app/Console/Commands/MigrateIssueManagementCommand.php new file mode 100644 index 0000000..148a4a0 --- /dev/null +++ b/app/Console/Commands/MigrateIssueManagementCommand.php @@ -0,0 +1,76 @@ +info('Starting issue management migration...'); + + $legacyConn = DB::connection('mysql_second'); + + // Get legacy issue management + $legacyIssues = $legacyConn->table('issue_management')->get(); + + if ($legacyIssues->isEmpty()) { + $this->warn('No issue management records found in legacy database.'); + + return; + } + + $this->withProgressBar($legacyIssues, function ($legacy) { + DB::table('issue_management')->updateOrInsert( + ['id' => $legacy->id], + [ + 'media_monitoring_id' => $legacy->media_monitoring, + 'location_id' => $legacy->locus, + 'sub_location_id' => $legacy->sub_locus, + 'classification_id' => $legacy->classification, + 'sub_classification_id' => $legacy->sub_classification, + 'issue' => $this->mapSentiment($legacy->issue), + 'response' => $this->mapSentiment($legacy->response), + 'description' => $legacy->description ?? '-', + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + 'deleted_at' => $legacy->deleted_at, + ] + ); + }); + $this->newLine(); + + $this->info('Issue management migration completed successfully!'); + } + + private function mapSentiment(string $legacyValue): string + { + return match (strtolower($legacyValue)) { + 'positif' => IssueSentiment::POSITIVE->value, + 'negatif' => IssueSentiment::NEGATIVE->value, + 'netral' => IssueSentiment::NEUTRAL->value, + 'krisis' => IssueSentiment::CRISIS->value, + default => IssueSentiment::NEUTRAL->value, + }; + } +} diff --git a/app/Console/Commands/MigrateJournalistCommand.php b/app/Console/Commands/MigrateJournalistCommand.php new file mode 100644 index 0000000..6c00ad9 --- /dev/null +++ b/app/Console/Commands/MigrateJournalistCommand.php @@ -0,0 +1,70 @@ +info('Starting journalist migration...'); + + $legacyConn = DB::connection('mysql_second'); + $legacyJournalists = $legacyConn->table('journalists')->get(); + + if ($legacyJournalists->isEmpty()) { + $this->warn('No journalists found in legacy database.'); + + return; + } + + $this->withProgressBar($legacyJournalists, function ($legacy) { + // Find corresponding partner media (outlet) in the NEW database + // legacy 'media' column is the partner_media_id + $partnerMedia = DB::table('partner_media')->where('id', $legacy->media)->first(); + + if (! $partnerMedia) { + // If partner media doesn't exist, we skip as it's required + return; + } + + DB::table('journalists')->updateOrInsert( + ['id' => $legacy->id], + [ + 'partner_media_id' => $partnerMedia->id, + 'name' => Str::limit($legacy->name, 100), + 'email' => Str::limit($legacy->email, 254), + 'phone_number' => Str::limit($legacy->phone, 20), + 'press_card' => Str::limit($legacy->press_card, 100), + 'ukw_certificate' => Str::limit($legacy->certificate, 100), + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + 'deleted_at' => $legacy->deleted_at, + ] + ); + }); + + $this->newLine(); + $this->info('Journalist migration completed successfully!'); + } +} diff --git a/app/Console/Commands/MigrateLocationCommand.php b/app/Console/Commands/MigrateLocationCommand.php new file mode 100644 index 0000000..ba96518 --- /dev/null +++ b/app/Console/Commands/MigrateLocationCommand.php @@ -0,0 +1,75 @@ +info('Starting location migration...'); + + $legacyConn = DB::connection('mysql_second'); + + // Migrate Locations (Locuses) + $this->info('Migrating locations...'); + $legacyLocations = $legacyConn->table('locuses')->get(); + + $this->withProgressBar($legacyLocations, function ($legacy) { + DB::table('locations')->updateOrInsert( + ['id' => $legacy->id], + [ + 'name' => $legacy->name, + 'is_active' => $legacy->status == '1' ? IsActive::ACTIVE->value : IsActive::INACTIVE->value, + 'sort_order' => $legacy->enhancer ?? 0, + 'created_at' => $legacy->created_at, + 'updated_at' => $legacy->updated_at, + 'deleted_at' => $legacy->deleted_at, + ] + ); + }); + $this->newLine(); + + // Migrate Sub Locations (Sub Locuses) + $this->info('Migrating sub locations...'); + $legacySubLocations = $legacyConn->table('sub_locuses')->get(); + + $this->withProgressBar($legacySubLocations, function ($legacy) { + DB::table('sub_locations')->updateOrInsert( + ['id' => $legacy->id], + [ + 'location_id' => $legacy->locus, + 'name' => $legacy->name, + 'is_active' => $legacy->status == '1' ? IsActive::ACTIVE->value : IsActive::INACTIVE->value, + 'sort_order' => $legacy->enhancer ?? 0, + 'created_at' => $legacy->created_at, + 'updated_at' => $legacy->updated_at, + 'deleted_at' => $legacy->deleted_at, + ] + ); + }); + $this->newLine(); + + $this->info('Migration completed successfully!'); + } +} diff --git a/app/Console/Commands/MigrateMediaMonitoringCommand.php b/app/Console/Commands/MigrateMediaMonitoringCommand.php new file mode 100644 index 0000000..f99a12d --- /dev/null +++ b/app/Console/Commands/MigrateMediaMonitoringCommand.php @@ -0,0 +1,100 @@ +info('Starting media monitoring migration...'); + + $legacyConn = DB::connection('mysql_second'); + + // Get legacy monitorings + $legacyMonitorings = $legacyConn->table('media_monitorings')->get(); + + if ($legacyMonitorings->isEmpty()) { + $this->warn('No media monitorings found in legacy database.'); + + return; + } + + $this->withProgressBar($legacyMonitorings, function ($legacy) { + // Main record + DB::table('media_monitorings')->updateOrInsert( + ['id' => $legacy->id], + [ + 'code' => Str::limit($legacy->code, 10, ''), + 'media_name' => Str::limit($legacy->media_name, 50, ''), + 'title' => $legacy->title, + 'channel' => Str::limit($legacy->channel, 20, ''), + 'writter' => Str::limit($legacy->writter, 50, ''), + 'link' => $legacy->link, + 'news_page' => Str::limit($legacy->news_page, 20, ''), + 'quote' => $legacy->quote, + 'content' => $legacy->content, + 'influencer' => Str::limit($legacy->influencer, 50, ''), + 'keyword' => Str::limit($legacy->keyword, 100, ''), + 'release_date' => $legacy->release_date, + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + 'deleted_at' => $legacy->deleted_at, + ] + ); + + // Handle Theme (Insert into themes table if not exists) + $themeName = trim($legacy->theme); + if (! empty($themeName)) { + $themeName = Str::limit($themeName, 50, ''); + + // Ensure theme exists in master table + DB::table('themes')->updateOrInsert( + ['name' => $themeName], + [ + 'is_active' => IsActive::ACTIVE->value, + 'sort_order' => 0, + 'created_at' => now(), + 'updated_at' => now(), + ] + ); + + $themeId = DB::table('themes')->where('name', $themeName)->value('id'); + + // Link to pivot table + if ($themeId) { + DB::table('media_monitoring_theme')->updateOrInsert( + [ + 'media_monitoring_id' => $legacy->id, + 'theme_id' => $themeId, + ] + ); + } + } + }); + $this->newLine(); + + $this->info('Media monitoring migration completed successfully!'); + } +} diff --git a/app/Console/Commands/MigrateNewsCommand.php b/app/Console/Commands/MigrateNewsCommand.php new file mode 100644 index 0000000..d8f4a87 --- /dev/null +++ b/app/Console/Commands/MigrateNewsCommand.php @@ -0,0 +1,85 @@ +info('Starting news migration...'); + + $legacyConn = DB::connection('mysql_second'); + + // Get legacy news with category 1 + $legacyNews = $legacyConn->table('news') + ->where('category', '0') + ->get(); + + if ($legacyNews->isEmpty()) { + $this->warn('No news found with category 1.'); + + return; + } + + // Get a default author ID (prefer Developer or Admin) + $defaultAuthorId = DB::table('users')->first()?->id ?? 1; + + $this->withProgressBar($legacyNews, function ($legacy) use ($defaultAuthorId) { + // Use updateOrInsert for the basic record + DB::table('news')->updateOrInsert( + ['id' => $legacy->id], + [ + 'author_id' => $defaultAuthorId, + 'title' => $legacy->title, + 'slug' => Str::slug($legacy->title).'-'.$legacy->id, + 'content' => $legacy->content, + 'excerpt' => Str::limit(strip_tags($legacy->content), 160), + 'link' => $legacy->link ? Str::limit($legacy->link, 50, '') : null, + 'views' => 0, + 'status' => NewsStatus::PUBLISHED->value, + 'published_at' => $legacy->created_at, + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + 'deleted_at' => $legacy->deleted_at, + ] + ); + + // Handle Tags using the Model (if tag exists in legacy) + if (! empty($legacy->tag)) { + $news = News::find($legacy->id); + if ($news) { + // split by comma or other delimiter if multiple tags, + // legacy 'tag' column is varchar(20), likely a single tag or comma separated + $tags = array_map('trim', explode(',', $legacy->tag)); + $news->syncTags($tags); + } + } + }); + $this->newLine(); + + $this->info('News migration completed successfully!'); + } +} diff --git a/app/Console/Commands/MigrateThemeCommand.php b/app/Console/Commands/MigrateThemeCommand.php new file mode 100644 index 0000000..4af2bfa --- /dev/null +++ b/app/Console/Commands/MigrateThemeCommand.php @@ -0,0 +1,60 @@ +info('Starting theme migration...'); + + $legacyConn = DB::connection('mysql_second'); + + // Get legacy themes + $legacyThemes = $legacyConn->table('proof_airing_themes')->get(); + + if ($legacyThemes->isEmpty()) { + $this->warn('No themes found in legacy database.'); + + return; + } + + $this->withProgressBar($legacyThemes, function ($legacy) { + DB::table('themes')->updateOrInsert( + ['id' => $legacy->id], + [ + 'name' => $legacy->name, + 'is_active' => IsActive::ACTIVE->value, + 'sort_order' => 0, + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + 'deleted_at' => $legacy->deleted_at, + ] + ); + }); + $this->newLine(); + + $this->info('Theme migration completed successfully!'); + } +} diff --git a/app/Console/Commands/MigrateUserCommand.php b/app/Console/Commands/MigrateUserCommand.php new file mode 100644 index 0000000..6fd8d09 --- /dev/null +++ b/app/Console/Commands/MigrateUserCommand.php @@ -0,0 +1,116 @@ +info('Starting user migration...'); + + $legacyConn = DB::connection('mysql_second'); + $legacyUsers = $legacyConn->table('users')->get(); + + if ($legacyUsers->isEmpty()) { + $this->warn('No users found in legacy database.'); + + return; + } + + $this->withProgressBar($legacyUsers, function ($legacy) { + // Skip if user already exists with same email, but ensure role is synced if needed + $user = User::where('email', $legacy->email)->first(); + + if (! $user) { + $username = $this->generateUniqueUsername($legacy->name, $legacy->email); + + // Using DB facade directly to insert ensures the legacy password hash + // is NOT double-hashed by the model's "hashed" cast. + $userId = DB::table('users')->insertGetId([ + 'name' => Str::limit($legacy->name, 100), + 'email' => $legacy->email, + 'username' => $username, + 'password' => $legacy->password, // Preserving legacy hash + 'is_active' => $legacy->status == '1' ? IsActive::ACTIVE->value : IsActive::INACTIVE->value, + 'email_verified_at' => now(), + 'created_at' => $legacy->created_at ?? now(), + 'updated_at' => $legacy->updated_at ?? now(), + ]); + + $user = User::find($userId); + } + + // Map and Assign Role + $roleName = $this->mapRole($legacy->role); + if ($roleName && $user) { + $user->syncRoles([$roleName]); + } + }); + + $this->newLine(); + $this->info('User migration completed successfully!'); + } + + private function generateUniqueUsername($name, $email) + { + // Try name first + $base = Str::limit(Str::slug($name, ''), 10, ''); + if (empty($base)) { + $base = Str::limit(explode('@', $email)[0], 10, ''); + } + + $username = $base; + $counter = 1; + + // Ensure unique within 10 chars + while (User::where('username', $username)->exists()) { + $suffix = (string) $counter; + $maxBaseLength = 10 - strlen($suffix); + $username = substr($base, 0, $maxBaseLength).$suffix; + $counter++; + + if ($counter > 999) { + break; + } // Safety + } + + return $username; + } + + private function mapRole($legacyRole): ?string + { + return match ((int) $legacyRole) { + 1 => RoleEnum::ADMINISTRATOR->value, + 2 => RoleEnum::ADMIN_MONITORING->value, + 3 => RoleEnum::PERUSAHAAN->value, + 4 => RoleEnum::ADMIN_KONTEN->value, + 5 => RoleEnum::ADMIN_KEUANGAN->value, + default => null, + }; + } +} diff --git a/app/Filament/Pages/Company.php b/app/Filament/Pages/Company.php index 1a513b1..7793447 100644 --- a/app/Filament/Pages/Company.php +++ b/app/Filament/Pages/Company.php @@ -113,7 +113,7 @@ public static function form(Schema $schema): Schema 'title' => 'Akta Pendirian', 'text_name' => 'deed_incorporation', 'placeholder' => '*****', - 'max' => 100, + 'max' => 150, 'max_size' => 1024 * 10, 'file_name' => 'deed_incorporation_docs', 'accept' => ['application/pdf'], @@ -123,7 +123,7 @@ public static function form(Schema $schema): Schema 'title' => 'SIUP / NIB', 'text_name' => 'trade_license', 'placeholder' => '*****', - 'max' => 100, + 'max' => 150, 'max_size' => 1024 * 10, 'file_name' => 'trade_license_docs', 'accept' => ['application/pdf'], @@ -133,7 +133,7 @@ public static function form(Schema $schema): Schema 'title' => 'NPWP', 'text_name' => 'tax_id_number', 'placeholder' => '*****', - 'max' => 100, + 'max' => 150, 'max_size' => 1024 * 10, 'file_name' => 'tax_id_number_docs', 'accept' => ['application/pdf'], @@ -143,7 +143,7 @@ public static function form(Schema $schema): Schema 'title' => 'PKP', 'text_name' => 'taxable_enterprise', 'placeholder' => '*****', - 'max' => 100, + 'max' => 150, 'max_size' => 1024 * 10, 'file_name' => 'taxable_enterprise_docs', 'accept' => ['application/pdf'], @@ -153,7 +153,7 @@ public static function form(Schema $schema): Schema 'title' => 'SPT Tahunan', 'text_name' => 'annual_tax_return', 'placeholder' => '*****', - 'max' => 100, + 'max' => 150, 'max_size' => 1024 * 10, 'file_name' => 'annual_tax_return_docs', 'accept' => ['application/pdf'], @@ -163,7 +163,7 @@ public static function form(Schema $schema): Schema 'title' => 'Suket Domisili', 'text_name' => 'domicile_certificate', 'placeholder' => '*****', - 'max' => 100, + 'max' => 150, 'max_size' => 1024 * 10, 'file_name' => 'domicile_certificate_docs', 'accept' => ['application/pdf'], @@ -173,7 +173,7 @@ public static function form(Schema $schema): Schema 'title' => 'Profil Perusahaan', 'text_name' => 'profile', 'placeholder' => '*****', - 'max' => 100, + 'max' => 150, 'max_size' => 1024 * 10, 'file_name' => 'profile_docs', 'accept' => ['application/pdf'], diff --git a/app/Filament/Pages/Media.php b/app/Filament/Pages/Media.php index c88c45e..01b3584 100644 --- a/app/Filament/Pages/Media.php +++ b/app/Filament/Pages/Media.php @@ -171,7 +171,7 @@ public static function form(Schema $schema): Schema ->placeholder('https://www.pwknews.com') ->autocomplete(false) ->nullable() - ->maxLength(50) + ->maxLength(255) ->url(), Textarea::make('address') diff --git a/app/Filament/Resources/Manage/Cooperations/RelationManagers/ReportRelationManager.php b/app/Filament/Resources/Manage/Cooperations/RelationManagers/ReportRelationManager.php index fa82fc2..00a95df 100644 --- a/app/Filament/Resources/Manage/Cooperations/RelationManagers/ReportRelationManager.php +++ b/app/Filament/Resources/Manage/Cooperations/RelationManagers/ReportRelationManager.php @@ -87,7 +87,7 @@ public function form(Schema $schema): Schema TextInput::make('link') ->label('Tautan') ->placeholder('https://example.com') - ->maxLength(50) + ->maxLength(255) ->url() ->required() ->autocomplete(false), diff --git a/app/Filament/Resources/Monitoring/ContentRecaps/Schemas/ContentRecapForm.php b/app/Filament/Resources/Monitoring/ContentRecaps/Schemas/ContentRecapForm.php index ae050df..cd85116 100644 --- a/app/Filament/Resources/Monitoring/ContentRecaps/Schemas/ContentRecapForm.php +++ b/app/Filament/Resources/Monitoring/ContentRecaps/Schemas/ContentRecapForm.php @@ -27,14 +27,14 @@ public static function configure(Schema $schema): Schema ->autocomplete(false) ->autofocus() ->required() - ->maxLength(255), + ->maxLength(1000), TextInput::make('link') ->label('Tautan') ->placeholder('https://www.pwknews.com') ->autocomplete(false) ->nullable() - ->maxLength(50) + ->maxLength(255) ->url(), Grid::make(2) diff --git a/app/Filament/Resources/Monitoring/MediaMonitorings/Schemas/MediaMonitoringForm.php b/app/Filament/Resources/Monitoring/MediaMonitorings/Schemas/MediaMonitoringForm.php index baa0db1..b27982c 100644 --- a/app/Filament/Resources/Monitoring/MediaMonitorings/Schemas/MediaMonitoringForm.php +++ b/app/Filament/Resources/Monitoring/MediaMonitorings/Schemas/MediaMonitoringForm.php @@ -57,7 +57,7 @@ public static function configure(Schema $schema): Schema ->placeholder('https://www.pwknews.com') ->autocomplete(false) ->nullable() - ->maxLength(50) + ->maxLength(255) ->url(), RichEditor::make('quote') diff --git a/app/Filament/Resources/Publication/News/Schemas/NewsForm.php b/app/Filament/Resources/Publication/News/Schemas/NewsForm.php index 87fdbfe..770dfde 100644 --- a/app/Filament/Resources/Publication/News/Schemas/NewsForm.php +++ b/app/Filament/Resources/Publication/News/Schemas/NewsForm.php @@ -51,7 +51,7 @@ public static function configure(Schema $schema): Schema ->placeholder('https://www.pwknews.com') ->autocomplete(false) ->nullable() - ->maxLength(50) + ->maxLength(255) ->url(), ])->columnSpan(2), diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 452e6b6..bd25ee9 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,8 @@ namespace App\Providers; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\URL; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -11,7 +13,42 @@ class AppServiceProvider extends ServiceProvider */ public function register(): void { - // + URL::macro( + 'alternateHasCorrectSignature', + function (Request $request, $absolute = true, array $ignoreQuery = []) { + $ignoreQuery[] = 'signature'; + + $absoluteUrl = url($request->path()); + $url = $absolute ? $absoluteUrl : '/'.$request->path(); + + $queryString = collect(explode('&', (string) $request + ->server->get('QUERY_STRING'))) + ->reject(fn ($parameter) => in_array(str()->before($parameter, '='), $ignoreQuery)) + ->join('&'); + + $original = rtrim($url.'?'.$queryString, '?'); + + // Use the application key as the HMAC key + $key = config('app.key'); // Ensure app.key is properly set in .env + + if (empty($key)) { + throw new \RuntimeException('Application key is not set.'); + } + + $signature = hash_hmac('sha256', $original, $key); + + return hash_equals($signature, (string) $request->query('signature', '')); + } + ); + + URL::macro('alternateHasValidSignature', function (Request $request, $absolute = true, array $ignoreQuery = []) { + return URL::alternateHasCorrectSignature($request, $absolute, $ignoreQuery) + && URL::signatureHasNotExpired($request); + }); + + Request::macro('hasValidSignature', function ($absolute = true, array $ignoreQuery = []) { + return URL::alternateHasValidSignature($this, $absolute, $ignoreQuery); + }); } /** @@ -19,6 +56,8 @@ public function register(): void */ public function boot(): void { - // + if (! app()->environment('local')) { + URL::forceScheme('https'); + } } } diff --git a/database/migrations/2025_11_20_041121_create_companies_table.php b/database/migrations/2025_11_20_041121_create_companies_table.php index 840f741..9bd0fc1 100644 --- a/database/migrations/2025_11_20_041121_create_companies_table.php +++ b/database/migrations/2025_11_20_041121_create_companies_table.php @@ -17,15 +17,15 @@ public function up(): void $table->string('name', 100)->index(); $table->string('email', 254)->index(); $table->text('address'); - $table->string('director_name', 100); + $table->string('director_name', 150); $table->string('director_nik', 16); - $table->string('deed_incorporation', 100); - $table->string('trade_license', 100); - $table->string('tax_id_number', 100); - $table->string('taxable_enterprise', 100); - $table->string('annual_tax_return', 100); - $table->string('domicile_certificate', 100); - $table->string('profile', 100); + $table->string('deed_incorporation', 150); + $table->string('trade_license', 150); + $table->string('tax_id_number', 150); + $table->string('taxable_enterprise', 150); + $table->string('annual_tax_return', 150); + $table->string('domicile_certificate', 150); + $table->string('profile', 150); $table->timestamp('created_at')->useCurrent(); $table->timestamp('updated_at')->nullable()->useCurrentOnUpdate(); $table->softDeletes(); diff --git a/database/migrations/2025_11_20_041125_create_partner_media_table.php b/database/migrations/2025_11_20_041125_create_partner_media_table.php index 7755714..8cab49f 100644 --- a/database/migrations/2025_11_20_041125_create_partner_media_table.php +++ b/database/migrations/2025_11_20_041125_create_partner_media_table.php @@ -18,7 +18,7 @@ public function up(): void $table->foreignId('company_id')->constrained()->cascadeOnDelete(); $table->string('name', 50)->index(); $table->text('address'); - $table->string('link', 50)->nullable(); + $table->string('link')->nullable(); $table->enum('type', MediaType::values())->default(MediaType::ONLINE)->comment(MediaType::comment())->index(); $table->enum('classification', MediaClassification::values())->comment(MediaClassification::comment())->index(); $table->string('journalism_organization')->nullable(); diff --git a/database/migrations/2025_12_05_095022_create_media_monitorings_table.php b/database/migrations/2025_12_05_095022_create_media_monitorings_table.php index 65f4873..561c4aa 100644 --- a/database/migrations/2025_12_05_095022_create_media_monitorings_table.php +++ b/database/migrations/2025_12_05_095022_create_media_monitorings_table.php @@ -18,7 +18,7 @@ public function up(): void $table->text('title', 255); $table->string('channel', 20)->index(); $table->string('writter', 50); - $table->text('link', 50)->nullable(); + $table->text('link')->nullable(); $table->string('news_page', 20)->nullable(); $table->text('quote')->nullable(); $table->text('content'); diff --git a/database/migrations/2025_12_09_113331_create_content_recaps_table.php b/database/migrations/2025_12_09_113331_create_content_recaps_table.php index 9250c5b..f1ce206 100644 --- a/database/migrations/2025_12_09_113331_create_content_recaps_table.php +++ b/database/migrations/2025_12_09_113331_create_content_recaps_table.php @@ -15,8 +15,8 @@ public function up(): void Schema::create('content_recaps', function (Blueprint $table) { $table->id(); $table->foreignId('classification_id')->constrained()->cascadeOnDelete(); - $table->string('title', 255); - $table->text('link', 50)->nullable(); + $table->text('title'); + $table->text('link')->nullable(); $table->date('posting_date')->index(); $table->enum('type', ContentType::values())->default(ContentType::FOTO)->index(); $table->string('channel', 20)->index(); diff --git a/database/migrations/2025_12_12_082036_create_news_table.php b/database/migrations/2025_12_12_082036_create_news_table.php index 8d3247b..477ecd4 100644 --- a/database/migrations/2025_12_12_082036_create_news_table.php +++ b/database/migrations/2025_12_12_082036_create_news_table.php @@ -19,7 +19,7 @@ public function up(): void $table->string('slug', 200)->index(); $table->text('content'); $table->text('excerpt'); - $table->string('link', 50)->nullable(); + $table->string('link')->nullable(); $table->unsignedInteger('views')->default(0)->index(); $table->enum('status', NewsStatus::values())->comment(NewsStatus::comment())->index(); $table->timestamp('published_at')->nullable()->index(); diff --git a/database/migrations/2025_12_12_135847_create_reports_table.php b/database/migrations/2025_12_12_135847_create_reports_table.php index a1b4505..3160de8 100644 --- a/database/migrations/2025_12_12_135847_create_reports_table.php +++ b/database/migrations/2025_12_12_135847_create_reports_table.php @@ -17,7 +17,7 @@ public function up(): void $table->foreignId('media_task_assignment_id')->constrained('media_task_assignment')->cascadeOnDelete(); $table->string('title', 200); $table->date('publication_date')->index(); - $table->string('link', 50); + $table->string('link'); $table->text('description'); $table->enum('status', ApprovalStatus::values())->default(ApprovalStatus::PENDING)->comment(ApprovalStatus::comment())->index(); $table->timestamp('created_at')->useCurrent();