MemberAdminWindow: Add feature to export .vcf files
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Test / Run tests (push) Successful in 1m42s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Test / Run tests (push) Successful in 1m42s
				
			This commit is contained in:
		
							
								
								
									
										65
									
								
								Elwig/Helpers/Export/VCard.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								Elwig/Helpers/Export/VCard.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
using Elwig.Models.Entities;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Elwig.Helpers.Export {
 | 
			
		||||
    public class VCard : IExporter<Member> {
 | 
			
		||||
 | 
			
		||||
        public static string FileExtension => "vcf";
 | 
			
		||||
 | 
			
		||||
        private readonly StreamWriter _writer;
 | 
			
		||||
 | 
			
		||||
        public VCard(string filename) : this(filename, Utils.UTF8) { }
 | 
			
		||||
 | 
			
		||||
        public VCard(string filename, Encoding encoding) {
 | 
			
		||||
            _writer = new StreamWriter(filename, false, encoding);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Dispose() {
 | 
			
		||||
            GC.SuppressFinalize(this);
 | 
			
		||||
            _writer.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ValueTask DisposeAsync() {
 | 
			
		||||
            GC.SuppressFinalize(this);
 | 
			
		||||
            return _writer.DisposeAsync();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task ExportAsync(IEnumerable<Member> data, IProgress<double>? progress = null) {
 | 
			
		||||
            progress?.Report(0.0);
 | 
			
		||||
            int count = data.Count() + 1, i = 0;
 | 
			
		||||
 | 
			
		||||
            foreach (var row in data) {
 | 
			
		||||
                var billingAddr = row.BillingAddress != null ? $"ADR;TYPE=work;LANGUAGE=de;LABEL=\"{row.BillingAddress.FullName}\\n{row.BillingAddress.Address}\\n{row.BillingAddress.PostalDest.AtPlz?.Plz} {row.BillingAddress.PostalDest.AtPlz?.Ort.Name}\\nÖsterreich\":;;{row.BillingAddress.Address};{row.BillingAddress.PostalDest.AtPlz?.Ort.Name};;{row.BillingAddress.PostalDest.AtPlz?.Plz};Österreich\r\n" : null;
 | 
			
		||||
                var tel = string.Join("", row.TelephoneNumbers
 | 
			
		||||
                    .Where(n => n.Type != "fax")
 | 
			
		||||
                    .Select(n => $"TEL;TYPE={(n.Type == "mobile" ? "cell" : "voice")}:{n.Number}\r\n"));
 | 
			
		||||
                var email = string.Join("", row.EmailAddresses.Select(a => $"EMAIL:{a.Address}\r\n"));
 | 
			
		||||
                await _writer.WriteLineAsync($"""
 | 
			
		||||
                    BEGIN:VCARD
 | 
			
		||||
                    VERSION:4.0
 | 
			
		||||
                    UID:mg{row.MgNr}@{App.Client.NameToken.ToLower()}.elwig.at
 | 
			
		||||
                    NOTE:MgNr. {row.MgNr}
 | 
			
		||||
                    FN:{row.AdministrativeName}
 | 
			
		||||
                    N:{row.Name};{row.GivenName};{row.MiddleName};{row.Prefix};{row.Suffix}
 | 
			
		||||
                    KIND:{(row.IsJuridicalPerson ? "org" : "individual")}
 | 
			
		||||
                    ADR{(billingAddr == null ? "" : ";TYPE=home")};LANGUAGE=de;LABEL="{row.Address}\n{row.PostalDest.AtPlz?.Plz} {row.PostalDest.AtPlz?.Ort.Name}\nÖsterreich":;;{row.Address};{row.PostalDest.AtPlz?.Ort.Name};;{row.PostalDest.AtPlz?.Plz};Österreich
 | 
			
		||||
                    {billingAddr}{tel}{email}REV:{row.ModifiedAt.ToUniversalTime():yyyyMMdd\THHmmss\Z}
 | 
			
		||||
                    END:VCARD
 | 
			
		||||
                    """);
 | 
			
		||||
                progress?.Report(100.0 * ++i / count);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await _writer.FlushAsync();
 | 
			
		||||
            progress?.Report(100.0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Export(IEnumerable<Member> data, IProgress<double>? progress = null) {
 | 
			
		||||
            ExportAsync(data, progress).GetAwaiter().GetResult();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
namespace Elwig.Helpers {
 | 
			
		||||
    public enum ExportMode {
 | 
			
		||||
        Show, SaveList, SavePdf, Print, Email, Export, Upload
 | 
			
		||||
        Show, SaveList, SavePdf, Print, Email, Vcf, Export, Upload
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -493,6 +493,32 @@ namespace Elwig.Services {
 | 
			
		||||
                    });
 | 
			
		||||
                    Mouse.OverrideCursor = null;
 | 
			
		||||
                }
 | 
			
		||||
            } else if (mode == ExportMode.Vcf) {
 | 
			
		||||
                var d = new SaveFileDialog() {
 | 
			
		||||
                    FileName = "Mitglieder.vcf",
 | 
			
		||||
                    DefaultExt = "vcf",
 | 
			
		||||
                    Filter = "vCard-Datei (*.vcf)|*.vcf",
 | 
			
		||||
                    Title = "Kontakte speichern unter - Elwig"
 | 
			
		||||
                };
 | 
			
		||||
                if (d.ShowDialog() == true) {
 | 
			
		||||
                    Mouse.OverrideCursor = Cursors.Wait;
 | 
			
		||||
                    await Task.Run(async () => {
 | 
			
		||||
                        try {
 | 
			
		||||
                            var members = await query
 | 
			
		||||
                                .OrderBy(m => m.MgNr)
 | 
			
		||||
                                .Include(m => m.BillingAddress)
 | 
			
		||||
                                .Include(m => m.TelephoneNumbers)
 | 
			
		||||
                                .Include(m => m.EmailAddresses)
 | 
			
		||||
                                .AsSplitQuery()
 | 
			
		||||
                                .ToListAsync();
 | 
			
		||||
                            using var exporter = new VCard(d.FileName);
 | 
			
		||||
                            await exporter.ExportAsync(members);
 | 
			
		||||
                        } catch (Exception exc) {
 | 
			
		||||
                            MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                    Mouse.OverrideCursor = null;
 | 
			
		||||
                }
 | 
			
		||||
            } else if (mode == ExportMode.Export) {
 | 
			
		||||
                var d = new SaveFileDialog() {
 | 
			
		||||
                    FileName = subject == ExportSubject.Selected ? $"Mitglied_{vm.SelectedMember?.MgNr}.elwig.zip" : $"Mitglieder_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
 | 
			
		||||
 
 | 
			
		||||
@@ -164,6 +164,14 @@
 | 
			
		||||
                <MenuItem x:Name="Menu_Export_UploadAll" Header="...von allen Mitgliedern hochladen"
 | 
			
		||||
                          Click="Menu_Export_UploadAll_Click"/>
 | 
			
		||||
            </MenuItem>
 | 
			
		||||
            <MenuItem Header="Kontakte">
 | 
			
		||||
                <MenuItem x:Name="Menu_Contacts_Selected" Header="...von ausgewähltem Mitglied speichern..." IsEnabled="False"
 | 
			
		||||
                          Click="Menu_Contacts_Selected_Click"/>
 | 
			
		||||
                <MenuItem x:Name="Menu_Contacts_Filters" Header="...aus Filtern speichern..."
 | 
			
		||||
                          Click="Menu_Contacts_Filters_Click"/>
 | 
			
		||||
                <MenuItem x:Name="Menu_Contacts_All" Header="...von allen Mitgliedern speichern..."
 | 
			
		||||
                          Click="Menu_Contacts_All_Click"/>
 | 
			
		||||
            </MenuItem>
 | 
			
		||||
        </Menu>
 | 
			
		||||
 | 
			
		||||
        <Grid Grid.Row="1" Margin="5,0,0,0">
 | 
			
		||||
 
 | 
			
		||||
@@ -319,9 +319,11 @@ namespace Elwig.Windows {
 | 
			
		||||
            if (MemberList.SelectedItem is Member m) {
 | 
			
		||||
                Menu_Export_ExportSelected.IsEnabled = !IsEditing && !IsCreating;
 | 
			
		||||
                Menu_Export_UploadSelected.IsEnabled = !IsEditing && !IsCreating && App.Config.SyncUrl != null;
 | 
			
		||||
                Menu_Contacts_Selected.IsEnabled = !IsEditing && !IsCreating;
 | 
			
		||||
            } else {
 | 
			
		||||
                Menu_Export_ExportSelected.IsEnabled = false;
 | 
			
		||||
                Menu_Export_UploadSelected.IsEnabled = false;
 | 
			
		||||
                Menu_Contacts_Selected.IsEnabled = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -649,6 +651,13 @@ namespace Elwig.Windows {
 | 
			
		||||
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.Selected, ExportMode.Upload);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async void Menu_Contacts_All_Click(object sender, RoutedEventArgs evt) =>
 | 
			
		||||
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.All, ExportMode.Vcf);
 | 
			
		||||
        private async void Menu_Contacts_Filters_Click(object sender, RoutedEventArgs evt) =>
 | 
			
		||||
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.FromFilters, ExportMode.Vcf);
 | 
			
		||||
        private async void Menu_Contacts_Selected_Click(object sender, RoutedEventArgs evt) =>
 | 
			
		||||
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.Selected, ExportMode.Vcf);
 | 
			
		||||
 | 
			
		||||
        private async void Menu_List_Order_Click(object sender, RoutedEventArgs evt) {
 | 
			
		||||
            Menu_List.IsSubmenuOpen = true;
 | 
			
		||||
            if (sender == Menu_List_OrderMgNr) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user