diff --git a/BackupFolder.ps1 b/BackupFolder.ps1 index 6c5da6c..d71c686 100644 --- a/BackupFolder.ps1 +++ b/BackupFolder.ps1 @@ -1,289 +1,694 @@ -# BackupFolder.ps1 -# Script to backup and compress folders from Windows context menu +# BackupFolder.ps1 - Klasör Yedekleme Scripti +# Sağ tık menüsünden veya doğrudan çalıştırma için +# UTF-8 with BOM olarak kaydedin param( [Parameter(Mandatory=$true)] [string]$FolderPath ) -# Function to show message -function Show-Message { +Add-Type -AssemblyName System.Windows.Forms +Add-Type -AssemblyName System.Drawing + +#region ¦¦ RENK & STİL PALETİ ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ + +$Colors = @{ + Background = [System.Drawing.Color]::FromArgb(18, 18, 28) + Surface = [System.Drawing.Color]::FromArgb(28, 28, 42) + SurfaceAlt = [System.Drawing.Color]::FromArgb(36, 36, 54) + Border = [System.Drawing.Color]::FromArgb(55, 55, 80) + Accent = [System.Drawing.Color]::FromArgb(99, 102, 241) + AccentHover = [System.Drawing.Color]::FromArgb(129, 132, 255) + Success = [System.Drawing.Color]::FromArgb(34, 197, 94) + Warning = [System.Drawing.Color]::FromArgb(251, 191, 36) + Danger = [System.Drawing.Color]::FromArgb(239, 68, 68) + TextPrimary = [System.Drawing.Color]::FromArgb(240, 240, 255) + TextSecondary= [System.Drawing.Color]::FromArgb(148, 148, 180) + TextMuted = [System.Drawing.Color]::FromArgb(90, 90, 120) + LogBg = [System.Drawing.Color]::FromArgb(10, 10, 18) +} + +function New-StyledButton { param( - [string]$Message, - [string]$Title = "Klasör Yedekleme", - [int]$Type = 0 # 0=Info, 1=Error, 2=Warning + [string]$Text, + [System.Drawing.Point]$Location, + [System.Drawing.Size]$Size, + [System.Drawing.Color]$BgColor, + [System.Drawing.Color]$HoverColor, + [System.Windows.Forms.DialogResult]$DialogResult = "None" ) - - switch ($Type) { - 0 { $icon = 64 } # Information - 1 { $icon = 16 } # Error - 2 { $icon = 48 } # Warning - default { $icon = 64 } + $btn = New-Object System.Windows.Forms.Button + $btn.Text = $Text + $btn.Location = $Location + $btn.Size = $Size + $btn.FlatStyle = "Flat" + $btn.FlatAppearance.BorderSize = 0 + $btn.BackColor = $BgColor + $btn.ForeColor = [System.Drawing.Color]::FromArgb(240, 240, 255) + $btn.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) + $btn.Cursor = [System.Windows.Forms.Cursors]::Hand + # Renkleri Tag'e göm — closure scope sorununu önler + $btn.Tag = @{ Normal = $BgColor; Hover = $HoverColor } + if ($DialogResult -ne "None") { $btn.DialogResult = $DialogResult } + $btn.Add_MouseEnter({ $args[0].BackColor = $args[0].Tag.Hover }) + $btn.Add_MouseLeave({ $args[0].BackColor = $args[0].Tag.Normal }) + return $btn +} + +#endregion + +#region ¦¦ GİTIGNORE OKUMA ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ + +function Get-GitIgnorePatterns { + param([string]$FolderPath) + + $patterns = @() + # Klasör içinde ve üst klasörlerde .gitignore ara + $searchPath = $FolderPath + for ($i = 0; $i -lt 4; $i++) { + $gitIgnoreFile = Join-Path $searchPath ".gitignore" + if (Test-Path $gitIgnoreFile) { + $lines = Get-Content $gitIgnoreFile -Encoding UTF8 -ErrorAction SilentlyContinue + foreach ($line in $lines) { + $trimmed = $line.Trim() + # Yorum satırlarını ve boş satırları atla + if ($trimmed -and -not $trimmed.StartsWith("#")) { + # Sadece klasör/dosya adı veya pattern olan satırları al + $clean = $trimmed.TrimStart("/").TrimEnd("/") + if ($clean -and $clean -notmatch "[*?!]") { + $patterns += $clean + } + # Basit glob: node_modules/ gibi + elseif ($clean -match "^[\w\.\-]+/?$") { + $patterns += $clean.TrimEnd("/") + } + } + } + break # İlk bulunan .gitignore yeterli + } + $parent = Split-Path $searchPath -Parent + if (-not $parent -or $parent -eq $searchPath) { break } + $searchPath = $parent } - - [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null - [System.Windows.Forms.MessageBox]::Show($Message, $Title, 0, $icon) | Out-Null + return ($patterns | Sort-Object -Unique) } -# Function to show confirmation dialog with Yes/No options -function Show-Confirmation { +#endregion + +#region ¦¦ DEV KLASÖR TARAMA ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ + +# Varsayılan hariç tutulacak klasörler +$DefaultExcludedFolders = @( + "node_modules", "vendor", "build", "dist", + ".git", ".svn", ".hg", + "bin", "obj", "__pycache__", + ".next", ".nuxt", ".output", + "target", "out", ".gradle", + "venv", ".venv", "env", + "coverage", ".nyc_output" +) + +function Find-DevFolders { param( - [string]$Message, - [string]$Title = "Klasör Yedekleme" + [string]$FolderPath, + [string[]]$ExcludeList ) - - [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null - $result = [System.Windows.Forms.MessageBox]::Show($Message, $Title, 4, 64) # 4 = YesNo buttons, 64 = Information icon - return ($result -eq "Yes") + $found = @() + # Sadece 2 seviye derinliğe kadar tara (performans) + foreach ($name in $ExcludeList) { + # Root seviye + if (Test-Path (Join-Path $FolderPath $name)) { + $found += $name + continue + } + # 1 seviye alt + $subDirs = Get-ChildItem -Path $FolderPath -Directory -ErrorAction SilentlyContinue + foreach ($sub in $subDirs) { + if (Test-Path (Join-Path $sub.FullName $name)) { + $found += "$($sub.Name)\$name" + } + } + } + return ($found | Sort-Object -Unique) } -# Function to show advanced options dialog -function Show-AdvancedOptions { +#endregion + +#region ¦¦ ONAY DİYALOGU ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ + +function Show-OptionsDialog { param( - [string]$Message, - [string]$Title = "Geliştirme Klasörleri Bulundu" + [string]$FolderName, + [string[]]$FoundDevFolders, + [string[]]$GitIgnorePatterns, + [string[]]$AllExcluded ) - - # Create a form with custom buttons - Add-Type -AssemblyName System.Windows.Forms - + $form = New-Object System.Windows.Forms.Form - $form.Text = $Title - $form.Size = New-Object System.Drawing.Size(400, 200) - $form.StartPosition = "CenterScreen" + $form.Text = "Yedekleme Ayarlari" + $form.Size = New-Object System.Drawing.Size(560, 530) + $form.MinimumSize = New-Object System.Drawing.Size(560, 530) + $form.StartPosition = "CenterScreen" $form.FormBorderStyle = "FixedDialog" - $form.Topmost = $true - - # Add label with message - $label = New-Object System.Windows.Forms.Label - $label.Location = New-Object System.Drawing.Point(10, 20) - $label.Size = New-Object System.Drawing.Size(360, 80) - $label.Text = $Message - $label.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 9) - $form.Controls.Add($label) - - # Add "Include All" button - $buttonIncludeAll = New-Object System.Windows.Forms.Button - $buttonIncludeAll.Location = New-Object System.Drawing.Point(10, 120) - $buttonIncludeAll.Size = New-Object System.Drawing.Size(100, 30) - $buttonIncludeAll.Text = "Tümünü Dahil Et" - $buttonIncludeAll.DialogResult = "OK" - $form.Controls.Add($buttonIncludeAll) - - # Add "Skip Dev Folders" button - $buttonSkip = New-Object System.Windows.Forms.Button - $buttonSkip.Location = New-Object System.Drawing.Point(120, 120) - $buttonSkip.Size = New-Object System.Drawing.Size(120, 30) - $buttonSkip.Text = "Dev Klasörleri Atla" - $buttonSkip.DialogResult = "Yes" - $form.Controls.Add($buttonSkip) - - # Add "Cancel" button - $buttonCancel = New-Object System.Windows.Forms.Button - $buttonCancel.Location = New-Object System.Drawing.Point(250, 120) - $buttonCancel.Size = New-Object System.Drawing.Size(100, 30) - $buttonCancel.Text = "İptal" - $buttonCancel.DialogResult = "No" - $form.Controls.Add($buttonCancel) - - # Set default button - $form.AcceptButton = $buttonIncludeAll - $form.CancelButton = $buttonCancel - - # Show dialog and return result - $result = $form.ShowDialog() - return $result + $form.MaximizeBox = $false + $form.BackColor = $Colors.Background + $form.ForeColor = $Colors.TextPrimary + $form.Font = New-Object System.Drawing.Font("Segoe UI", 9) + + # ¦¦ Başlık paneli + $panelHeader = New-Object System.Windows.Forms.Panel + $panelHeader.Dock = "Top" + $panelHeader.Height = 70 + $panelHeader.BackColor = $Colors.Surface + $form.Controls.Add($panelHeader) + + $lblIcon = New-Object System.Windows.Forms.Label + $lblIcon.Text = "??" + $lblIcon.Location = New-Object System.Drawing.Point(16, 14) + $lblIcon.Size = New-Object System.Drawing.Size(40, 40) + $lblIcon.Font = New-Object System.Drawing.Font("Segoe UI Emoji", 20) + $panelHeader.Controls.Add($lblIcon) + + $lblTitle = New-Object System.Windows.Forms.Label + $lblTitle.Text = "Klasör Yedekleme" + $lblTitle.Location = New-Object System.Drawing.Point(62, 12) + $lblTitle.Size = New-Object System.Drawing.Size(420, 24) + $lblTitle.Font = New-Object System.Drawing.Font("Segoe UI", 13, [System.Drawing.FontStyle]::Bold) + $lblTitle.ForeColor= $Colors.TextPrimary + $panelHeader.Controls.Add($lblTitle) + + $lblSubtitle = New-Object System.Windows.Forms.Label + $lblSubtitle.Text = $FolderName + $lblSubtitle.Location = New-Object System.Drawing.Point(63, 38) + $lblSubtitle.Size = New-Object System.Drawing.Size(430, 20) + $lblSubtitle.Font = New-Object System.Drawing.Font("Segoe UI", 8) + $lblSubtitle.ForeColor = $Colors.TextSecondary + $panelHeader.Controls.Add($lblSubtitle) + + # ¦¦ İçerik alanı + $panelBody = New-Object System.Windows.Forms.Panel + $panelBody.Location = New-Object System.Drawing.Point(0, 70) + $panelBody.Size = New-Object System.Drawing.Size(544, 370) + $panelBody.BackColor = $Colors.Background + $form.Controls.Add($panelBody) + + # Dev klasörler bölümü + $lblDevTitle = New-Object System.Windows.Forms.Label + $lblDevTitle.Text = " ? Geliştirme Klasörleri Tespit Edildi" + $lblDevTitle.Location = New-Object System.Drawing.Point(12, 14) + $lblDevTitle.Size = New-Object System.Drawing.Size(520, 22) + $lblDevTitle.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) + $lblDevTitle.ForeColor = $Colors.Warning + $panelBody.Controls.Add($lblDevTitle) + + # Dev klasörler listesi kutusu + $listBox = New-Object System.Windows.Forms.ListBox + $listBox.Location = New-Object System.Drawing.Point(12, 42) + $listBox.Size = New-Object System.Drawing.Size(518, 110) + $listBox.BackColor = $Colors.LogBg + $listBox.ForeColor = $Colors.Warning + $listBox.Font = New-Object System.Drawing.Font("Consolas", 8.5) + $listBox.BorderStyle = "FixedSingle" + $listBox.SelectionMode = "None" + foreach ($f in $FoundDevFolders) { $listBox.Items.Add(" ?? $f") | Out-Null } + $panelBody.Controls.Add($listBox) + + # .gitignore bölümü + $yGit = 162 + if ($GitIgnorePatterns.Count -gt 0) { + $lblGitTitle = New-Object System.Windows.Forms.Label + $lblGitTitle.Text = " ? .gitignore'dan ek desenler ($($GitIgnorePatterns.Count) adet)" + $lblGitTitle.Location = New-Object System.Drawing.Point(12, $yGit) + $lblGitTitle.Size = New-Object System.Drawing.Size(520, 20) + $lblGitTitle.Font = New-Object System.Drawing.Font("Segoe UI", 8.5) + $lblGitTitle.ForeColor = $Colors.Success + $panelBody.Controls.Add($lblGitTitle) + + $txtGit = New-Object System.Windows.Forms.TextBox + $txtGit.Location = New-Object System.Drawing.Point(12, ($yGit + 24)) + $txtGit.Size = New-Object System.Drawing.Size(518, 55) + $txtGit.Multiline = $true + $txtGit.ReadOnly = $true + $txtGit.BackColor = $Colors.LogBg + $txtGit.ForeColor = $Colors.Success + $txtGit.Font = New-Object System.Drawing.Font("Consolas", 8) + $txtGit.BorderStyle= "FixedSingle" + $txtGit.ScrollBars = "Vertical" + $txtGit.Text = ($GitIgnorePatterns -join ", ") + $panelBody.Controls.Add($txtGit) + $yGit += 90 + } else { + $lblNoGit = New-Object System.Windows.Forms.Label + $lblNoGit.Text = " ? .gitignore dosyası bulunamadı" + $lblNoGit.Location = New-Object System.Drawing.Point(12, $yGit) + $lblNoGit.Size = New-Object System.Drawing.Size(520, 20) + $lblNoGit.Font = New-Object System.Drawing.Font("Segoe UI", 8.5) + $lblNoGit.ForeColor = $Colors.TextMuted + $panelBody.Controls.Add($lblNoGit) + $yGit += 28 + } + + # Seçenek başlığı + $lblChoose = New-Object System.Windows.Forms.Label + $lblChoose.Text = "Ne yapmak istersiniz?" + $lblChoose.Location = New-Object System.Drawing.Point(12, ($yGit + 10)) + $lblChoose.Size = New-Object System.Drawing.Size(520, 20) + $lblChoose.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) + $lblChoose.ForeColor = $Colors.TextSecondary + $panelBody.Controls.Add($lblChoose) + + # ¦¦ Alt buton alanı + $panelFooter = New-Object System.Windows.Forms.Panel + $panelFooter.Dock = "Bottom" + $panelFooter.Height = 72 + $panelFooter.BackColor = $Colors.Surface + $form.Controls.Add($panelFooter) + + $btnSkip = New-StyledButton ` + -Text " Dev Klasorleri Atla" ` + -Location (New-Object System.Drawing.Point(14, 18)) ` + -Size (New-Object System.Drawing.Size(168, 38)) ` + -BgColor $Colors.Accent ` + -HoverColor $Colors.AccentHover ` + -DialogResult "Yes" + $panelFooter.Controls.Add($btnSkip) + + $btnAll = New-StyledButton ` + -Text " Tumunu Dahil Et" ` + -Location (New-Object System.Drawing.Point(192, 18)) ` + -Size (New-Object System.Drawing.Size(160, 38)) ` + -BgColor $Colors.SurfaceAlt ` + -HoverColor $Colors.Border ` + -DialogResult "OK" + $panelFooter.Controls.Add($btnAll) + + $btnCancel = New-StyledButton ` + -Text " Iptal" ` + -Location (New-Object System.Drawing.Point(362, 18)) ` + -Size (New-Object System.Drawing.Size(160, 38)) ` + -BgColor ([System.Drawing.Color]::FromArgb(80, 30, 30)) ` + -HoverColor $Colors.Danger ` + -DialogResult "No" + $panelFooter.Controls.Add($btnCancel) + + $form.AcceptButton = $btnSkip + $form.CancelButton = $btnCancel + + return $form.ShowDialog() } -# Check if folder exists +#endregion + +#region ¦¦ İLERLEME / LOG FORMU ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ + +$script:ProgressForm = $null +$script:ProgressBar = $null +$script:LogBox = $null +$script:StatusLabel = $null + +function Show-ProgressForm { + param([string]$FolderName) + + $script:ProgressForm = New-Object System.Windows.Forms.Form + $script:ProgressForm.Text = "Yedekleniyor..." + $script:ProgressForm.Size = New-Object System.Drawing.Size(560, 420) + $script:ProgressForm.StartPosition = "CenterScreen" + $script:ProgressForm.FormBorderStyle = "FixedDialog" + $script:ProgressForm.MaximizeBox = $false + $script:ProgressForm.ControlBox = $false + $script:ProgressForm.BackColor = $Colors.Background + + # Header + $panH = New-Object System.Windows.Forms.Panel + $panH.Dock = "Top" + $panH.Height = 60 + $panH.BackColor = $Colors.Surface + $script:ProgressForm.Controls.Add($panH) + + $lblH = New-Object System.Windows.Forms.Label + $lblH.Text = "? Yedekleme İşlemi" + $lblH.Location = New-Object System.Drawing.Point(14, 10) + $lblH.Size = New-Object System.Drawing.Size(530, 24) + $lblH.Font = New-Object System.Drawing.Font("Segoe UI", 12, [System.Drawing.FontStyle]::Bold) + $lblH.ForeColor = $Colors.TextPrimary + $panH.Controls.Add($lblH) + + $lblSub = New-Object System.Windows.Forms.Label + $lblSub.Text = $FolderName + $lblSub.Location = New-Object System.Drawing.Point(15, 36) + $lblSub.Size = New-Object System.Drawing.Size(500, 18) + $lblSub.Font = New-Object System.Drawing.Font("Segoe UI", 8) + $lblSub.ForeColor = $Colors.TextSecondary + $panH.Controls.Add($lblSub) + + # Durum etiketi + $script:StatusLabel = New-Object System.Windows.Forms.Label + $script:StatusLabel.Text = "Başlatılıyor..." + $script:StatusLabel.Location = New-Object System.Drawing.Point(14, 72) + $script:StatusLabel.Size = New-Object System.Drawing.Size(530, 20) + $script:StatusLabel.Font = New-Object System.Drawing.Font("Segoe UI", 9) + $script:StatusLabel.ForeColor = $Colors.TextSecondary + $script:ProgressForm.Controls.Add($script:StatusLabel) + + # Progress bar + $script:ProgressBar = New-Object System.Windows.Forms.ProgressBar + $script:ProgressBar.Location = New-Object System.Drawing.Point(14, 96) + $script:ProgressBar.Size = New-Object System.Drawing.Size(526, 18) + $script:ProgressBar.Style = "Marquee" + $script:ProgressBar.MarqueeAnimationSpeed = 25 + $script:ProgressForm.Controls.Add($script:ProgressBar) + + # Log paneli başlığı + $lblLog = New-Object System.Windows.Forms.Label + $lblLog.Text = "İşlem Günlüğü" + $lblLog.Location = New-Object System.Drawing.Point(14, 124) + $lblLog.Size = New-Object System.Drawing.Size(200, 18) + $lblLog.Font = New-Object System.Drawing.Font("Segoe UI", 8, [System.Drawing.FontStyle]::Bold) + $lblLog.ForeColor = $Colors.TextMuted + $script:ProgressForm.Controls.Add($lblLog) + + # Log kutusu + $script:LogBox = New-Object System.Windows.Forms.RichTextBox + $script:LogBox.Location = New-Object System.Drawing.Point(14, 146) + $script:LogBox.Size = New-Object System.Drawing.Size(526, 200) + $script:LogBox.BackColor = $Colors.LogBg + $script:LogBox.ForeColor = $Colors.TextSecondary + $script:LogBox.Font = New-Object System.Drawing.Font("Consolas", 8.5) + $script:LogBox.BorderStyle = "FixedSingle" + $script:LogBox.ReadOnly = $true + $script:LogBox.ScrollBars = "Vertical" + $script:ProgressForm.Controls.Add($script:LogBox) + + $script:ProgressForm.Show() + $script:ProgressForm.Refresh() +} + +function Write-Log { + param( + [string]$Message, + [string]$Level = "INFO" # INFO, OK, WARN, ERR + ) + if (-not $script:LogBox) { return } + + $timestamp = Get-Date -Format "HH:mm:ss" + $color = switch ($Level) { + "OK" { $Colors.Success } + "WARN" { $Colors.Warning } + "ERR" { $Colors.Danger } + default{ $Colors.TextSecondary } + } + + $prefix = switch ($Level) { + "OK" { "?" } + "WARN" { "?" } + "ERR" { "?" } + default{ "›" } + } + + $script:LogBox.SelectionStart = $script:LogBox.TextLength + $script:LogBox.SelectionLength = 0 + $script:LogBox.SelectionColor = $Colors.TextMuted + $script:LogBox.AppendText("[$timestamp] ") + $script:LogBox.SelectionColor = $color + $script:LogBox.AppendText("$prefix $Message`n") + $script:LogBox.ScrollToCaret() + $script:ProgressForm.Refresh() +} + +function Update-Status { + param([string]$Text) + if ($script:StatusLabel) { + $script:StatusLabel.Text = $Text + $script:ProgressForm.Refresh() + } +} + +function Close-ProgressForm { + if ($script:ProgressForm) { + $script:ProgressBar.Style = "Continuous" + $script:ProgressBar.Value = 100 + Start-Sleep -Milliseconds 400 + $script:ProgressForm.Close() + $script:ProgressForm = $null + } +} + +#endregion + +#region ¦¦ SONUÇ DİYALOGU ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ + +function Show-ResultDialog { + param( + [bool]$Success, + [string]$Message, + [string]$FilePath = "" + ) + + $form = New-Object System.Windows.Forms.Form + $form.Text = if ($Success) { "Yedekleme Tamamlandi" } else { "Hata" } + $form.Size = New-Object System.Drawing.Size(500, 240) + $form.StartPosition = "CenterScreen" + $form.FormBorderStyle = "FixedDialog" + $form.MaximizeBox = $false + $form.BackColor = $Colors.Background + + $accentColor = if ($Success) { $Colors.Success } else { $Colors.Danger } + $icon = if ($Success) { "?" } else { "?" } + + # Üst renkli şerit + $strip = New-Object System.Windows.Forms.Panel + $strip.Dock = "Top" + $strip.Height = 5 + $strip.BackColor = $accentColor + $form.Controls.Add($strip) + + $lblIcon = New-Object System.Windows.Forms.Label + $lblIcon.Text = $icon + $lblIcon.Location = New-Object System.Drawing.Point(16, 24) + $lblIcon.Size = New-Object System.Drawing.Size(40, 40) + $lblIcon.Font = New-Object System.Drawing.Font("Segoe UI", 22, [System.Drawing.FontStyle]::Bold) + $lblIcon.ForeColor = $accentColor + $form.Controls.Add($lblIcon) + + $lblMsg = New-Object System.Windows.Forms.Label + $lblMsg.Text = $Message + $lblMsg.Location = New-Object System.Drawing.Point(62, 24) + $lblMsg.Size = New-Object System.Drawing.Size(400, 60) + $lblMsg.Font = New-Object System.Drawing.Font("Segoe UI", 9) + $lblMsg.ForeColor = $Colors.TextPrimary + $form.Controls.Add($lblMsg) + + if ($FilePath -and $Success) { + $lblFile = New-Object System.Windows.Forms.Label + $lblFile.Text = $FilePath + $lblFile.Location = New-Object System.Drawing.Point(62, 90) + $lblFile.Size = New-Object System.Drawing.Size(400, 36) + $lblFile.Font = New-Object System.Drawing.Font("Consolas", 8) + $lblFile.ForeColor = $Colors.TextSecondary + $form.Controls.Add($lblFile) + } + + $btnOK = New-StyledButton ` + -Text "Tamam" ` + -Location (New-Object System.Drawing.Point(150, 158)) ` + -Size (New-Object System.Drawing.Size(120, 36)) ` + -BgColor $accentColor ` + -HoverColor $Colors.AccentHover ` + -DialogResult "OK" + $form.Controls.Add($btnOK) + + if ($FilePath -and $Success) { + $script:_ResultFilePath = $FilePath + $btnOpen = New-StyledButton ` + -Text "Klasoru Ac" ` + -Location (New-Object System.Drawing.Point(280, 158)) ` + -Size (New-Object System.Drawing.Size(120, 36)) ` + -BgColor $Colors.SurfaceAlt ` + -HoverColor $Colors.Border + $btnOpen.Add_Click({ + Start-Process explorer.exe (Split-Path $script:_ResultFilePath -Parent) + }) + $form.Controls.Add($btnOpen) + } + + $form.AcceptButton = $btnOK + $form.ShowDialog() | Out-Null +} + +#endregion + +#region ¦¦ ANA AKIŞ ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ + +# Klasör var mı? if (-not (Test-Path $FolderPath)) { - Show-Message -Message "Belirtilen klasör mevcut değil: $FolderPath" -Type 1 + Show-ResultDialog -Success $false -Message "Belirtilen klasör bulunamadı:`n$FolderPath" exit 1 } -# Define common development folders that should be excluded -$ExcludedFolders = @("node_modules", "vendor", "build", "dist", ".git", ".svn", "bin", "obj") +$FolderName = Split-Path $FolderPath -Leaf +$OutputDir = Split-Path $FolderPath -Parent + +# .gitignore desenlerini oku +$GitIgnorePatterns = Get-GitIgnorePatterns -FolderPath $FolderPath + +# Tüm exclusion listesini birleştir +$AllExcluded = ($DefaultExcludedFolders + $GitIgnorePatterns) | Sort-Object -Unique + +# Dev klasörleri tara +$FoundDevFolders = Find-DevFolders -FolderPath $FolderPath -ExcludeList $AllExcluded -# Check if the folder contains any development directories -$HasDevFolders = $false -$FoundDevFolders = @() $SkipDevFolders = $false -foreach ($excludedFolder in $ExcludedFolders) { - $devFolderPath = Join-Path $FolderPath $excludedFolder - if (Test-Path $devFolderPath) { - $HasDevFolders = $true - $FoundDevFolders += $excludedFolder +if ($FoundDevFolders.Count -gt 0) { + $choice = Show-OptionsDialog ` + -FolderName $FolderName ` + -FoundDevFolders $FoundDevFolders ` + -GitIgnorePatterns $GitIgnorePatterns ` + -AllExcluded $AllExcluded + + switch ($choice) { + "No" { exit 0 } # İptal + "Yes" { $SkipDevFolders = $true } # Dev klasörlerini atla + "OK" { $SkipDevFolders = $false } # Tümünü dahil et + default { exit 0 } } } -# If development folders are found, prompt user for action -if ($HasDevFolders) { - $folderList = ($FoundDevFolders -join ", ") - $message = "Bu klasör aşağıdaki geliştirme klasörlerini içeriyor:`n$folderList`n`nBu klasörleri yedeklemeye ne yapmak istersiniz?" - - $userChoice = Show-AdvancedOptions -Message $message -Title "Geliştirme Klasörleri Bulundu" - - switch ($userChoice) { - "No" { - # Cancel backup - Show-Message -Message "Yedekleme iptal edildi." -Type 2 - exit 0 - } - "Yes" { - # Skip dev folders - $SkipDevFolders = $true - } - "OK" { - # Include all folders - continue normally - } - default { - # Cancel backup for any other response - Show-Message -Message "Yedekleme iptal edildi." -Type 2 - exit 0 - } - } -} +# Arşiv araçlarını tespit et +$WinRarPath = "${env:ProgramFiles}\WinRAR\rar.exe" +$SevenZipPath= "${env:ProgramFiles}\7-Zip\7z.exe" +$UseWinRar = Test-Path $WinRarPath +$UseSevenZip = -not $UseWinRar -and (Test-Path $SevenZipPath) +$UsePS = -not $UseWinRar -and -not $UseSevenZip -# Get folder name -$FolderName = Split-Path $FolderPath -Leaf - -# Get current date in YYYYMMDD format -$Date = Get-Date -Format "yyyyMMdd" - -# Determine output directory (same as source folder) -$OutputDir = Split-Path $FolderPath -Parent - -# Find existing backups with same name and date to determine next number -$Pattern = "^${FolderName}_${Date}_\d{3}\.(rar|zip)$" -$ExistingBackups = Get-ChildItem -Path $OutputDir -Filter "${FolderName}_${Date}_*" | Where-Object { $_.Name -match $Pattern } +# Dosya adı +$Date = Get-Date -Format "yyyyMMdd" +$Ext = if ($UseWinRar) { "rar" } else { "zip" } +$Pattern = "^${FolderName}_${Date}_\d{3}\.$Ext$" +$ExistingBackups = Get-ChildItem -Path $OutputDir -Filter "${FolderName}_${Date}_*" | + Where-Object { $_.Name -match $Pattern } $NextNumber = 1 if ($ExistingBackups) { - $Numbers = $ExistingBackups.Name | ForEach-Object { - if ($_ -match "_([0-9]{3})\.(rar|zip)$") { [int]$matches[1] } + $nums = $ExistingBackups.Name | ForEach-Object { + if ($_ -match "_(\d{3})\.$Ext$") { [int]$Matches[1] } } | Sort-Object - if ($Numbers) { - $NextNumber = ($Numbers | Select-Object -Last 1) + 1 - } + if ($nums) { $NextNumber = ($nums | Select-Object -Last 1) + 1 } } +$NumFmt = "{0:D3}" -f $NextNumber +$OutputFile = Join-Path $OutputDir "${FolderName}_${Date}_${NumFmt}.$Ext" -$NumberFormatted = "{0:D3}" -f $NextNumber +# İlerleme formunu göster +Show-ProgressForm -FolderName $FolderName -# Try WinRAR first -$WinRarPath = "${env:ProgramFiles}\WinRAR\rar.exe" -$UseWinRar = Test-Path $WinRarPath - -# If WinRAR not found, try 7-Zip -$SevenZipPath = "${env:ProgramFiles}\7-Zip\7z.exe" -$UseSevenZip = Test-Path $SevenZipPath - -# If neither found, use PowerShell Compress-Archive -$UsePowerShellArchive = -not ($UseWinRar -or $UseSevenZip) - -# Create backup filename -if ($UseWinRar) { - $OutputFile = Join-Path $OutputDir "${FolderName}_${Date}_${NumberFormatted}.rar" +$tool = if ($UseWinRar) { "WinRAR" } elseif ($UseSevenZip) { "7-Zip" } else { "PowerShell" } +Write-Log "Araç: $tool" +Write-Log "Kaynak: $FolderPath" +Write-Log "Hedef: $OutputFile" +if ($SkipDevFolders) { + Write-Log "Hariç tutulacak: $($AllExcluded -join ', ')" -Level "WARN" } else { - $OutputFile = Join-Path $OutputDir "${FolderName}_${Date}_${NumberFormatted}.zip" + Write-Log "Tüm dosyalar dahil ediliyor" } try { - if ($UseWinRar -and -not $SkipDevFolders) { - # Use WinRAR (full backup) - # Change to the parent directory and use relative path to avoid full path in archive - Push-Location $OutputDir - try { - $Arguments = "a -r `"$OutputFile`" `".\$FolderName`"" - Start-Process -FilePath $WinRarPath -ArgumentList $Arguments -Wait -NoNewWindow - } - finally { - Pop-Location - } - } - elseif ($UseWinRar -and $SkipDevFolders) { - # Use WinRAR with exclusion patterns - Push-Location $OutputDir - try { - # Build exclusion arguments for each dev folder - $ExcludeArgs = "" - foreach ($excludedFolder in $ExcludedFolders) { - $ExcludeArgs += " -x`".\$FolderName\$excludedFolder\*`"" + Push-Location $OutputDir + + if ($UseWinRar) { + Update-Status "WinRAR ile sıkıştırılıyor..." + Write-Log "WinRAR başlatılıyor..." + + if ($SkipDevFolders) { + # Her exclusion için ayrı -x argümanı oluştur + $excludeArgs = $AllExcluded | ForEach-Object { + # Hem root hem alt klasör desenleri + "-x`"$FolderName\$_\*`"", + "-x`"*\$_\*`"" } - $Arguments = "a -r $ExcludeArgs `"$OutputFile`" `".\$FolderName`"" - Start-Process -FilePath $WinRarPath -ArgumentList $Arguments -Wait -NoNewWindow + $argStr = "a -r -ep1 $($excludeArgs -join ' ') `"$OutputFile`" `"$FolderName`"" + } else { + $argStr = "a -r -ep1 `"$OutputFile`" `"$FolderName`"" } - finally { - Pop-Location + + Write-Log "Komut: rar.exe $argStr" + $proc = Start-Process -FilePath $WinRarPath -ArgumentList $argStr -Wait -NoNewWindow -PassThru + if ($proc.ExitCode -notin @(0, 1)) { + throw "WinRAR çıkış kodu: $($proc.ExitCode)" } } - elseif ($UseSevenZip -and -not $SkipDevFolders) { - # Use 7-Zip (full backup) - # Change to the parent directory and use relative path to avoid full path in archive - Push-Location $OutputDir - try { - $Arguments = "a `"$OutputFile`" `".\$FolderName`"" - Start-Process -FilePath $SevenZipPath -ArgumentList $Arguments -Wait -NoNewWindow - } - finally { - Pop-Location - } - } - elseif ($UseSevenZip -and $SkipDevFolders) { - # Use 7-Zip with exclusion patterns - Push-Location $OutputDir - try { - # Build exclusion arguments for each dev folder - $ExcludeArgs = "" - foreach ($excludedFolder in $ExcludedFolders) { - $ExcludeArgs += " -x!$FolderName\$excludedFolder\*" + elseif ($UseSevenZip) { + Update-Status "7-Zip ile sıkıştırılıyor..." + Write-Log "7-Zip başlatılıyor..." + + if ($SkipDevFolders) { + $excludeArgs = $AllExcluded | ForEach-Object { + "-x!$FolderName\$_", + "-xr!$_" } - $Arguments = "a $ExcludeArgs `"$OutputFile`" `".\$FolderName`"" - Start-Process -FilePath $SevenZipPath -ArgumentList $Arguments -Wait -NoNewWindow + $argStr = "a $($excludeArgs -join ' ') `"$OutputFile`" `"$FolderName`"" + } else { + $argStr = "a `"$OutputFile`" `"$FolderName`"" } - finally { - Pop-Location + + Write-Log "Komut: 7z.exe $argStr" + $proc = Start-Process -FilePath $SevenZipPath -ArgumentList $argStr -Wait -NoNewWindow -PassThru + if ($proc.ExitCode -ne 0) { + throw "7-Zip çıkış kodu: $($proc.ExitCode)" } } else { - # Use PowerShell built-in Compress-Archive - # Fix the path issue by changing to the parent directory and using relative path - Push-Location $OutputDir - try { - if ($SkipDevFolders) { - # For PowerShell, we need to manually filter out dev folders - # Get all items except the excluded ones - $ItemsToCompress = Get-ChildItem -Path ".\$FolderName" -Recurse | Where-Object { - $itemPath = $_.FullName.Substring((Resolve-Path ".\$FolderName").Path.Length) - $shouldExclude = $false - foreach ($excludedFolder in $ExcludedFolders) { - if ($itemPath -like "\$excludedFolder*" -or $itemPath -like "*\$excludedFolder*" -or $itemPath -like "*\$excludedFolder\*") { - $shouldExclude = $true - break - } + Update-Status "PowerShell Compress-Archive ile sıkıştırılıyor..." + Write-Log "PowerShell Compress-Archive kullanılıyor..." -Level "WARN" + + if ($SkipDevFolders) { + Write-Log "Dosyalar filtreleniyor..." + $basePath = Resolve-Path ".\$FolderName" + $items = Get-ChildItem -Path ".\$FolderName" -Recurse -Force -ErrorAction SilentlyContinue | + Where-Object { + $rel = $_.FullName.Substring($basePath.Path.Length).TrimStart("\") + $parts = $rel -split "\\" + $exclude = $false + foreach ($part in $parts) { + if ($AllExcluded -contains $part) { $exclude = $true; break } } - -not $shouldExclude - } - - if ($ItemsToCompress) { - Compress-Archive -Path $ItemsToCompress.FullName -DestinationPath $OutputFile -Force - } else { - # If no items left after filtering, create an empty archive with just the folder structure - Compress-Archive -Path ".\$FolderName" -DestinationPath $OutputFile -Force - # Then remove the dev folders from the archive (this is complex, so we'll just warn the user) - Show-Message -Message "NOT: PowerShell ile dev klasörlerini atlamak tam olarak mümkün değildir. Tüm klasörler yedeklendi.`n`nGelecek sürümlerde bu özellik WinRAR veya 7-Zip ile daha iyi çalışacaktır." -Type 2 + -not $exclude } + + if ($items) { + Write-Log "$($items.Count) dosya/klasör dahil edilecek" + Compress-Archive -Path $items.FullName -DestinationPath $OutputFile -Force } else { - Compress-Archive -Path ".\$FolderName" -DestinationPath $OutputFile -Force + throw "Filtre sonrası dahil edilecek dosya kalmadı." } - } - finally { - Pop-Location + } else { + Compress-Archive -Path ".\$FolderName" -DestinationPath $OutputFile -Force } } - - Show-Message -Message "Yedekleme başarıyla tamamlandı!`n`nDosya: $OutputFile" + + Pop-Location + + # Boyut bilgisi + $fileInfo = Get-Item $OutputFile -ErrorAction SilentlyContinue + $sizeMB = if ($fileInfo) { [math]::Round($fileInfo.Length / 1MB, 2) } else { "?" } + + Write-Log "Tamamlandi! Boyut: $sizeMB MB" -Level "OK" + Update-Status "Tamamlandi!" + Start-Sleep -Milliseconds 600 + + Close-ProgressForm + + Show-ResultDialog ` + -Success $true ` + -Message "Yedekleme başarıyla tamamlandı!`nBoyut: $sizeMB MB" ` + -FilePath $OutputFile } catch { - Show-Message -Message "Yedekleme sırasında hata oluştu:`n$($_.Exception.Message)" -Type 1 -} \ No newline at end of file + if ((Get-Location).Path -ne $OutputDir) { Pop-Location } + Write-Log "HATA: $($_.Exception.Message)" -Level "ERR" + Start-Sleep -Milliseconds 800 + Close-ProgressForm + Show-ResultDialog -Success $false -Message "Yedekleme sırasında hata oluştu:`n$($_.Exception.Message)" + exit 1 +} + +#endregion \ No newline at end of file