One-way sync modes now do a full replace of the target
Outlook->Starface macht das Starface-Adressbuch zur exakten Kopie von Outlook: Kontakte, die nur in Starface existieren, werden geloescht. Starface->Outlook entsprechend umgekehrt (Phase 4). Sicherheit: - Loeschphase laeuft nur bei vollstaendig geladener Liste (unvollstaendige Ladevorgaenge brechen schon vorher ab). - Ist die Quelle komplett leer (z.B. falscher Ordner), wird die Loeschphase uebersprungen statt die Zielseite zu leeren. UI: Profil-Editor zeigt jetzt unter der Sync-Richtung einen Warnhinweis, der das jeweilige Verhalten erklaert. README/CHANGELOG aktualisiert. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,6 +38,13 @@ Versionsschema ist `x.x.x.x` (siehe `release.sh`).
|
||||
|
||||
### Geaendert
|
||||
|
||||
- **Ein-Richtungs-Modi sind jetzt echtes "Ersetzen".** Outlook->Starface macht
|
||||
das Starface-Adressbuch zu einer exakten Kopie von Outlook: Kontakte, die nur
|
||||
in Starface existieren (kein Pendant in Outlook), werden geloescht.
|
||||
Starface->Outlook entsprechend umgekehrt. Schutz: Ist die Quelle komplett
|
||||
leer (z.B. falscher Ordner gewaehlt), wird die Loeschphase uebersprungen
|
||||
statt die Zielseite zu leeren. Mass-Loeschungen finden nur bei vollstaendig
|
||||
geladener Liste statt (unvollstaendige Ladevorgaenge brechen vorher ab).
|
||||
- **Bidirektionale Loeschungen** werden jetzt erkannt. Wird ein Kontakt auf
|
||||
einer Seite geloescht und ist die andere Seite seit dem letzten Sync
|
||||
unveraendert (Abgleich ueber die gespeicherte Baseline), wird die Loeschung
|
||||
|
||||
@@ -8,7 +8,10 @@ Windows-Anwendung zur bidirektionalen Synchronisation von Kontakten zwischen Mic
|
||||
- **Profil-System** zum Verwalten mehrerer Sync-Konfigurationen (verschiedene Adressbuecher oder Anlagen)
|
||||
- **Starface-Adressbuecher**: Zentrales Adressbuch, persoenliches Adressbuch und Tag-basierte Adressbuecher
|
||||
- **Outlook-Kontaktordner**: Frei waehlbarer Kontaktordner als Sync-Ziel
|
||||
- **Sync-Richtung** konfigurierbar: Outlook -> Starface, Starface -> Outlook oder bidirektional
|
||||
- **Sync-Richtung** konfigurierbar:
|
||||
- *Bidirektional*: Aenderungen werden in beide Richtungen abgeglichen (inkl. Loeschungen)
|
||||
- *Outlook -> Starface*: Das Starface-Adressbuch wird zur exakten Kopie von Outlook (nur in Starface vorhandene Kontakte werden geloescht)
|
||||
- *Starface -> Outlook*: Der Outlook-Ordner wird zur exakten Kopie von Starface (nur in Outlook vorhandene Kontakte werden geloescht)
|
||||
- **Intelligentes Matching**: Kontakte werden anhand von E-Mail-Adresse oder Name abgeglichen
|
||||
- **Aenderungserkennung**: Nur geaenderte Kontakte werden uebertragen (Hash-basiert)
|
||||
- **Auto-Sync**: Optionaler automatischer Sync in konfigurierbarem Intervall
|
||||
|
||||
@@ -494,6 +494,77 @@ namespace StarfaceOutlookSync.Services
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Phase 4: Ersetzen-Modus - Zielseite an Quelle angleichen
|
||||
// ============================================
|
||||
// In den Ein-Richtungs-Modi soll die Zielseite eine exakte Kopie
|
||||
// der Quelle werden. Kontakte, die nur auf der Zielseite existieren
|
||||
// (kein Mapping, kein Treffer in der Quelle), werden geloescht.
|
||||
// Sicher, weil unvollstaendige Ladevorgaenge vorher abbrechen.
|
||||
if (profile.SyncDirection == SyncDirection.OutlookToStarface)
|
||||
{
|
||||
// Schutz gegen versehentliches Leerraeumen (z.B. falscher Ordner).
|
||||
if (outlookContacts.Count == 0)
|
||||
{
|
||||
Log("Ersetzen-Modus uebersprungen: Outlook-Ordner ist leer (Schutz vor versehentlichem Leeren).");
|
||||
}
|
||||
else
|
||||
{
|
||||
var leftover = starfaceContacts
|
||||
.Where(c => !string.IsNullOrEmpty(c.StarfaceId) && !processedStarfaceIds.Contains(c.StarfaceId))
|
||||
.ToList();
|
||||
if (leftover.Count > 0)
|
||||
Log($"Ersetzen-Modus: entferne {leftover.Count} Kontakt(e) aus Starface, die nicht in Outlook existieren");
|
||||
foreach (var sc in leftover)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (await starface.DeleteContactAsync(sc.StarfaceId))
|
||||
{
|
||||
result.Updated++;
|
||||
Log($" Geloescht (nur in Starface): {sc.DisplayName}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Errors++;
|
||||
result.ErrorMessages.Add($"Loeschen SF {sc.DisplayName}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (profile.SyncDirection == SyncDirection.StarfaceToOutlook)
|
||||
{
|
||||
if (starfaceContacts.Count == 0)
|
||||
{
|
||||
Log("Ersetzen-Modus uebersprungen: Starface-Adressbuch ist leer (Schutz vor versehentlichem Leeren).");
|
||||
}
|
||||
else
|
||||
{
|
||||
var leftover = outlookContacts
|
||||
.Where(c => !string.IsNullOrEmpty(c.OutlookEntryId) && !processedOutlookIds.Contains(c.OutlookEntryId))
|
||||
.ToList();
|
||||
if (leftover.Count > 0)
|
||||
Log($"Ersetzen-Modus: entferne {leftover.Count} Kontakt(e) aus Outlook, die nicht in Starface existieren");
|
||||
foreach (var oc in leftover)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_outlookService.DeleteContact(oc.OutlookEntryId))
|
||||
{
|
||||
result.Updated++;
|
||||
Log($" Geloescht (nur in Outlook): {oc.DisplayName}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Errors++;
|
||||
result.ErrorMessages.Add($"Loeschen OL {oc.DisplayName}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mappings speichern
|
||||
_profileManager.SaveMappings(profile.Id, newMappings);
|
||||
_profileManager.UpdateLastSync(profile.Id);
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace StarfaceOutlookSync.UI
|
||||
private NumericUpDown _numAutoSync;
|
||||
private Button _btnTest, _btnLoadBooks, _btnSave, _btnCancel;
|
||||
private Label _lblTestResult;
|
||||
private Label _lblDirectionHint;
|
||||
|
||||
private List<StarfaceAddressBook> _addressBooks = new List<StarfaceAddressBook>();
|
||||
private List<string> _outlookFolderPaths = new List<string>();
|
||||
@@ -101,7 +102,18 @@ namespace StarfaceOutlookSync.UI
|
||||
_cmbDirection = new ComboBox { Left = 12, Top = y, Width = 250, DropDownStyle = ComboBoxStyle.DropDownList };
|
||||
_cmbDirection.Items.AddRange(new object[] { "Bidirektional", "Outlook -> Starface", "Starface -> Outlook" });
|
||||
_cmbDirection.SelectedIndex = 0;
|
||||
panel.Controls.Add(_cmbDirection); y += 32;
|
||||
panel.Controls.Add(_cmbDirection); y += 28;
|
||||
|
||||
_lblDirectionHint = new Label
|
||||
{
|
||||
Left = 12, Top = y, Width = 360, Height = 32,
|
||||
ForeColor = Color.FromArgb(150, 80, 0),
|
||||
Font = new Font("Segoe UI", 8.25f)
|
||||
};
|
||||
_cmbDirection.SelectedIndexChanged += (s, e) => UpdateDirectionHint();
|
||||
panel.Controls.Add(_lblDirectionHint);
|
||||
UpdateDirectionHint();
|
||||
y += 36;
|
||||
|
||||
panel.Controls.Add(MakeLabel("Auto-Sync Intervall (Minuten, 0 = manuell):", 12, y)); y += 22;
|
||||
_numAutoSync = new NumericUpDown { Left = 12, Top = y, Width = 80, Minimum = 0, Maximum = 1440, Value = 0 };
|
||||
@@ -122,6 +134,22 @@ namespace StarfaceOutlookSync.UI
|
||||
CancelButton = _btnCancel;
|
||||
}
|
||||
|
||||
private void UpdateDirectionHint()
|
||||
{
|
||||
switch (_cmbDirection.SelectedIndex)
|
||||
{
|
||||
case 1: // Outlook -> Starface
|
||||
_lblDirectionHint.Text = "Achtung: Das Starface-Adressbuch wird zur exakten Kopie von Outlook.\nKontakte, die nur in Starface existieren, werden geloescht.";
|
||||
break;
|
||||
case 2: // Starface -> Outlook
|
||||
_lblDirectionHint.Text = "Achtung: Der Outlook-Ordner wird zur exakten Kopie von Starface.\nKontakte, die nur in Outlook existieren, werden geloescht.";
|
||||
break;
|
||||
default: // Bidirektional
|
||||
_lblDirectionHint.Text = "Aenderungen werden in beide Richtungen abgeglichen.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private Label MakeLabel(string text, int x, int y)
|
||||
{
|
||||
return new Label { Text = text, Left = x, Top = y, AutoSize = true };
|
||||
|
||||
Reference in New Issue
Block a user