Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba0b96f16a | ||
|
|
e7a87fbdf1 | ||
|
|
0076d84062 |
+5
-2
@@ -16,8 +16,11 @@ Versionsschema ist `x.x.x.x` (siehe `release.sh`).
|
||||
Felder aus den Dubletten aufgefuellt werden (kein Datenverlust), die uebrigen
|
||||
werden geloescht. Verwaiste Zuordnungen werden bereinigt, sodass der naechste
|
||||
bidirektionale Sync die Gewinner sauber 1:1 verbindet. Laeuft unter derselben
|
||||
Sperre wie ein Sync (kein gleichzeitiger Lauf), nutzt dieselbe Kontakt-
|
||||
Erkennung wie der Sync und wird vollstaendig protokolliert.
|
||||
Sperre wie ein Sync (kein gleichzeitiger Lauf - wartet bei laufendem Sync bis
|
||||
zu 60s statt abzubrechen), nutzt dieselbe Kontakt-Erkennung wie der Sync und
|
||||
wird vollstaendig protokolliert. Outlook- und Anlagen-Seite werden unabhaengig
|
||||
verarbeitet (eine streikende Anlage verhindert nicht die Outlook-Bereinigung);
|
||||
fehlgeschlagene Loeschungen werden gemeldet.
|
||||
|
||||
### Behoben
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
; Erfordert Inno Setup 6.x (https://jrsoftware.org/isinfo.php)
|
||||
|
||||
#define MyAppName "Starface Outlook Sync"
|
||||
#define MyAppVersion "0.0.3.2"
|
||||
#define MyAppVersion "0.0.3.3"
|
||||
#define MyAppPublisher "HackerSoft - Hacker-Net Telekommunikation"
|
||||
#define MyAppURL "https://www.hacker-net.de"
|
||||
#define MyAppExeName "StarfaceOutlookSync.exe"
|
||||
|
||||
@@ -90,90 +90,106 @@ namespace StarfaceOutlookSync.Services
|
||||
var deletedStarfaceIds = new HashSet<string>();
|
||||
var deletedOutlookIds = new HashSet<string>();
|
||||
|
||||
var outlookContacts = _outlookService.GetContacts(profile.OutlookFolderPath);
|
||||
|
||||
using (var backend = ContactBackendFactory.Create(profile.Connection))
|
||||
// --- Outlook (lokal, unabhaengig - laeuft auch wenn die Anlage streikt) ---
|
||||
try
|
||||
{
|
||||
backend.OnDebug += Log;
|
||||
if (!await backend.LoginAsync())
|
||||
throw new Exception("Login an der Telefonanlage fehlgeschlagen.");
|
||||
|
||||
var remoteContacts = await backend.GetContactsAsync(profile.AddressBook);
|
||||
|
||||
// --- Telefonanlage ---
|
||||
foreach (var g in GroupDuplicates(remoteContacts, c => mappedStarface.Contains(c.StarfaceId)))
|
||||
var outlookContacts = _outlookService.GetContacts(profile.OutlookFolderPath);
|
||||
var groups = GroupDuplicates(outlookContacts, c => mappedOutlook.Contains(c.OutlookEntryId));
|
||||
Log($"Outlook: {groups.Count} Gruppe(n) mit Dubletten.");
|
||||
foreach (var g in groups)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool changed = ContactMerger.FillEmptyInto(g.Survivor, g.Duplicates);
|
||||
bool survivorOk = true;
|
||||
if (changed)
|
||||
survivorOk = await backend.UpdateContactAsync(g.Survivor.StarfaceId, g.Survivor, profile.AddressBook) != null;
|
||||
|
||||
if (!survivorOk)
|
||||
if (changed && _outlookService.UpdateContact(g.Survivor.OutlookEntryId, g.Survivor) == null)
|
||||
{
|
||||
// Gewinner konnte nicht ergaenzt werden -> Dubletten NICHT loeschen
|
||||
// (sonst gingen die nur dort vorhandenen Felder verloren).
|
||||
report.Errors++;
|
||||
report.Messages.Add($"Anlage: '{g.Survivor.DisplayName}' konnte nicht ergaenzt werden - Gruppe uebersprungen.");
|
||||
report.Messages.Add($"Outlook: '{g.Survivor.DisplayName}' konnte nicht ergaenzt werden - uebersprungen.");
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var dup in g.Duplicates)
|
||||
{
|
||||
if (await backend.DeleteContactAsync(dup.StarfaceId))
|
||||
if (_outlookService.DeleteContact(dup.OutlookEntryId))
|
||||
{
|
||||
deletedStarfaceIds.Add(dup.StarfaceId);
|
||||
report.RemoteDeleted++;
|
||||
Log($" Anlage: Dublette geloescht -> zusammengefuehrt in '{g.Survivor.DisplayName}'");
|
||||
deletedOutlookIds.Add(dup.OutlookEntryId);
|
||||
report.OutlookDeleted++;
|
||||
Log($" Outlook: Dublette geloescht -> '{g.Survivor.DisplayName}'");
|
||||
}
|
||||
else
|
||||
{
|
||||
report.Errors++;
|
||||
report.Messages.Add($"Outlook: Dublette von '{g.Survivor.DisplayName}' konnte nicht geloescht werden.");
|
||||
}
|
||||
}
|
||||
report.RemoteMerged++;
|
||||
report.OutlookMerged++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
report.Errors++;
|
||||
report.Messages.Add($"Anlage '{g.Survivor.DisplayName}': {ex.Message}");
|
||||
report.Messages.Add($"Outlook '{g.Survivor.DisplayName}': {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
await backend.LogoutAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
report.Errors++;
|
||||
report.Messages.Add("Outlook gesamt: " + ex.Message);
|
||||
}
|
||||
|
||||
// --- Outlook ---
|
||||
foreach (var g in GroupDuplicates(outlookContacts, c => mappedOutlook.Contains(c.OutlookEntryId)))
|
||||
// --- Telefonanlage (unabhaengig) ---
|
||||
try
|
||||
{
|
||||
try
|
||||
using (var backend = ContactBackendFactory.Create(profile.Connection))
|
||||
{
|
||||
bool changed = ContactMerger.FillEmptyInto(g.Survivor, g.Duplicates);
|
||||
bool survivorOk = true;
|
||||
if (changed)
|
||||
survivorOk = _outlookService.UpdateContact(g.Survivor.OutlookEntryId, g.Survivor) != null;
|
||||
backend.OnDebug += Log;
|
||||
if (!await backend.LoginAsync())
|
||||
throw new Exception("Login an der Telefonanlage fehlgeschlagen.");
|
||||
|
||||
if (!survivorOk)
|
||||
var remoteContacts = await backend.GetContactsAsync(profile.AddressBook);
|
||||
var groups = GroupDuplicates(remoteContacts, c => mappedStarface.Contains(c.StarfaceId));
|
||||
Log($"Telefonanlage: {groups.Count} Gruppe(n) mit Dubletten.");
|
||||
foreach (var g in groups)
|
||||
{
|
||||
report.Errors++;
|
||||
report.Messages.Add($"Outlook: '{g.Survivor.DisplayName}' konnte nicht ergaenzt werden - Gruppe uebersprungen.");
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var dup in g.Duplicates)
|
||||
{
|
||||
if (_outlookService.DeleteContact(dup.OutlookEntryId))
|
||||
try
|
||||
{
|
||||
deletedOutlookIds.Add(dup.OutlookEntryId);
|
||||
report.OutlookDeleted++;
|
||||
Log($" Outlook: Dublette geloescht -> zusammengefuehrt in '{g.Survivor.DisplayName}'");
|
||||
bool changed = ContactMerger.FillEmptyInto(g.Survivor, g.Duplicates);
|
||||
if (changed && await backend.UpdateContactAsync(g.Survivor.StarfaceId, g.Survivor, profile.AddressBook) == null)
|
||||
{
|
||||
report.Errors++;
|
||||
report.Messages.Add($"Anlage: '{g.Survivor.DisplayName}' konnte nicht ergaenzt werden - uebersprungen.");
|
||||
continue;
|
||||
}
|
||||
foreach (var dup in g.Duplicates)
|
||||
{
|
||||
if (await backend.DeleteContactAsync(dup.StarfaceId))
|
||||
{
|
||||
deletedStarfaceIds.Add(dup.StarfaceId);
|
||||
report.RemoteDeleted++;
|
||||
Log($" Anlage: Dublette geloescht -> '{g.Survivor.DisplayName}'");
|
||||
}
|
||||
else
|
||||
{
|
||||
report.Errors++;
|
||||
report.Messages.Add($"Anlage: Dublette von '{g.Survivor.DisplayName}' konnte nicht geloescht werden.");
|
||||
}
|
||||
}
|
||||
report.RemoteMerged++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
report.Errors++;
|
||||
report.Messages.Add($"Anlage '{g.Survivor.DisplayName}': {ex.Message}");
|
||||
}
|
||||
}
|
||||
report.OutlookMerged++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
report.Errors++;
|
||||
report.Messages.Add($"Outlook '{g.Survivor.DisplayName}': {ex.Message}");
|
||||
|
||||
await backend.LogoutAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
report.Errors++;
|
||||
report.Messages.Add("Telefonanlage gesamt: " + ex.Message);
|
||||
}
|
||||
|
||||
// Verwaiste Zuordnungen (zeigen auf geloeschte Dubletten) entfernen.
|
||||
// Der naechste Sync verbindet die Gewinner sauber neu.
|
||||
|
||||
@@ -123,10 +123,18 @@ namespace StarfaceOutlookSync.Services
|
||||
/// </summary>
|
||||
public async Task<bool> RunExclusiveAsync(Func<Task> action, Action<string> status)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref _running, 1, 0) != 0)
|
||||
// Auf einen gerade laufenden Sync warten (bis 60s), statt sofort
|
||||
// abzubrechen - der Auto-Sync feuert ja jede Minute.
|
||||
var guardDeadline = DateTime.UtcNow.AddSeconds(60);
|
||||
while (Interlocked.CompareExchange(ref _running, 1, 0) != 0)
|
||||
{
|
||||
status?.Invoke("Es laeuft bereits ein Sync / eine Aktion - bitte warten.");
|
||||
return false;
|
||||
if (DateTime.UtcNow >= guardDeadline)
|
||||
{
|
||||
status?.Invoke("Es laeuft bereits ein Sync - bitte spaeter erneut versuchen.");
|
||||
return false;
|
||||
}
|
||||
status?.Invoke("Warte, bis der laufende Sync fertig ist...");
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
SyncLock crossLock = null;
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
<AssemblyTitle>Starface Outlook Sync</AssemblyTitle>
|
||||
<Company>HackerSoft - Hacker-Net Telekommunikation</Company>
|
||||
<Product>Starface Outlook Sync</Product>
|
||||
<Version>0.0.3.2</Version>
|
||||
<AssemblyVersion>0.0.3.2</AssemblyVersion>
|
||||
<FileVersion>0.0.3.2</FileVersion>
|
||||
<Version>0.0.3.3</Version>
|
||||
<AssemblyVersion>0.0.3.3</AssemblyVersion>
|
||||
<FileVersion>0.0.3.3</FileVersion>
|
||||
<Description>Synchronisiert Outlook-Kontakte mit Starface Telefonanlage</Description>
|
||||
<Copyright>Stefan Hacker - HackerSoft</Copyright>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace StarfaceOutlookSync.UI
|
||||
|
||||
var lblVersion = new Label
|
||||
{
|
||||
Text = "Version 0.0.3.2",
|
||||
Text = "Version 0.0.3.3",
|
||||
Left = 0, Top = 56, Width = 340, Height = 20,
|
||||
TextAlign = ContentAlignment.MiddleCenter,
|
||||
ForeColor = Color.Gray
|
||||
|
||||
@@ -89,8 +89,31 @@ namespace StarfaceOutlookSync.UI
|
||||
|
||||
private void AppendLog(string message)
|
||||
{
|
||||
if (InvokeRequired) { Invoke(new Action(() => AppendLog(message))); return; }
|
||||
_txtLog.AppendText(message + "\r\n");
|
||||
// Der Aufruf kann aus dem Hintergrund kommen, nachdem das Fenster
|
||||
// bereits geschlossen/entsorgt wurde -> defensiv absichern.
|
||||
if (IsDisposed || Disposing) return;
|
||||
try
|
||||
{
|
||||
if (InvokeRequired) { BeginInvoke(new Action(() => AppendLog(message))); return; }
|
||||
if (_txtLog == null || _txtLog.IsDisposed) return;
|
||||
_txtLog.AppendText(message + "\r\n");
|
||||
}
|
||||
catch (ObjectDisposedException) { }
|
||||
catch (InvalidOperationException) { } // Handle noch nicht/nicht mehr da
|
||||
}
|
||||
|
||||
protected override void OnFormClosing(FormClosingEventArgs e)
|
||||
{
|
||||
// Nicht schliessen, solange Analyse/Zusammenfuehrung laeuft - sonst
|
||||
// greifen die Hintergrund-Fortsetzungen auf entsorgte Controls zu.
|
||||
if (_busy)
|
||||
{
|
||||
e.Cancel = true;
|
||||
_lblSummary.Text = "Bitte warten - der Vorgang laeuft noch...";
|
||||
_lblSummary.ForeColor = Color.OrangeRed;
|
||||
return;
|
||||
}
|
||||
base.OnFormClosing(e);
|
||||
}
|
||||
|
||||
private async Task Analyze()
|
||||
@@ -160,7 +183,15 @@ namespace StarfaceOutlookSync.UI
|
||||
|
||||
if (!ran)
|
||||
{
|
||||
AppendLog("Abgebrochen (Sync/Aktion laeuft oder anderer Arbeitsplatz aktiv).");
|
||||
AppendLog("Abgebrochen: gerade laeuft ein Sync (oder ein anderer Arbeitsplatz ist aktiv).");
|
||||
_lblSummary.Text = "Nicht ausgefuehrt - es laeuft gerade ein Sync. Bitte kurz warten und erneut versuchen.";
|
||||
_lblSummary.ForeColor = Color.OrangeRed;
|
||||
MessageBox.Show(this,
|
||||
"Die Zusammenfuehrung wurde NICHT ausgefuehrt, weil gerade ein Sync laeuft " +
|
||||
"(oder ein anderer Arbeitsplatz aktiv ist).\n\nBitte kurz warten und erneut auf " +
|
||||
"'Zusammenfuehren starten' klicken.",
|
||||
"Dubletten", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
_btnStart.Enabled = true; // erneuter Versuch moeglich
|
||||
}
|
||||
else if (report != null)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user