diff --git a/backend/Config/CIPPDBCacheTypes.json b/backend/Config/CIPPDBCacheTypes.json index 08da042719..74de9ec87d 100644 --- a/backend/Config/CIPPDBCacheTypes.json +++ b/backend/Config/CIPPDBCacheTypes.json @@ -274,6 +274,11 @@ "friendlyName": "Mailbox Usage", "description": "Exchange Online mailbox usage statistics" }, + { + "type": "MailTrafficSummary", + "friendlyName": "Mail Traffic Summary", + "description": "Top mail senders and recipients by message volume over the last 30 days" + }, { "type": "OneDriveSiteListing", "collectedBy": "OneDriveUsage", diff --git a/backend/Config/ReportSamples/bec.json b/backend/Config/ReportSamples/bec.json new file mode 100644 index 0000000000..16086f5aa9 --- /dev/null +++ b/backend/Config/ReportSamples/bec.json @@ -0,0 +1,217 @@ +{ + "userData": { + "displayName": "Sample User", + "userPrincipalName": "sample.user@example.com" + }, + "becData": { + "ExtractedAt": "2026-08-05T09:00:00Z", + "ExtractResult": "Successfully extracted logs from auditlog", + "AnalysisWindowDays": 7, + "NewRules": [ + { + "Name": "Sample forwarding rule", + "Description": "Move messages from billing@example.com to folder RSS Feeds", + "MoveToFolder": "RSS Feeds", + "RecentlyChanged": true + } + ], + "InboxRuleChanges": [ + { + "Operation": "New-InboxRule", + "UserKey": "sample.user@example.com", + "RuleName": "Sample forwarding rule", + "Parameters": "MoveToFolder=RSS Feeds; MarkAsRead=True", + "Date": "2026-08-03T11:24:00Z", + "ClientIP": "203.0.113.10", + "Country": "NG", + "City": "Lagos", + "ForeignLocation": true + } + ], + "NewUsers": [ + { + "displayName": "Sample Contractor", + "userPrincipalName": "sample.contractor@example.com", + "createdDateTime": "2026-08-02T08:00:00Z" + } + ], + "AddedApps": [ + { + "displayName": "Sample OAuth app", + "appId": "00000000-0000-0000-0000-000000000001", + "publisher": "Sample Publisher", + "createdDateTime": "2026-08-01T10:00:00Z", + "MaliciousMatch": null + } + ], + "MaliciousSPs": [ + { + "displayName": "Sample Mail Sync Tool", + "appId": "00000000-0000-0000-0000-000000000002", + "accountEnabled": true, + "createdDateTime": "2026-07-30T09:30:00Z", + "CatalogName": "Sample Mail Sync Tool", + "Categories": [ + "Mailbox exfiltration", + "Business Email Compromise" + ], + "Description": "Sample catalog entry used for preview data." + } + ], + "MailboxPermissionChanges": [ + { + "Operation": "Add-MailboxPermission", + "UserKey": "admin@example.com", + "ObjectId": "sample.user@example.com", + "Permissions": "FullAccess", + "TargetsSuspect": true + } + ], + "SentMessages": [ + { + "MessageTraceId": "00000000-0000-0000-0000-000000000003", + "Status": "Delivered", + "Subject": "Sample invoice", + "RecipientAddress": "supplier@example.net", + "Received": "2026-08-04 15:02:11Z", + "FromIP": "203.0.113.10", + "Country": "NG", + "City": "Lagos", + "ForeignLocation": true + } + ], + "SentMessageAnalysis": { + "TotalMessages": 47, + "TotalRecipients": 212, + "RepeatedSubjects": [ + { + "Subject": "Sample invoice", + "MessageCount": 38, + "RecipientCount": 190, + "FirstSent": "2026-08-04 14:55:00Z", + "LastSent": "2026-08-04 15:20:00Z", + "Flagged": true + } + ], + "FlaggedSubjectCount": 1, + "Bursts": [ + { + "WindowStart": "2026-08-04 15:00:00Z", + "WindowMinutes": 10, + "MessageCount": 31, + "RecipientCount": 160, + "TopSubject": "Sample invoice" + } + ], + "Flagged": true + }, + "MFADevices": [ + { + "@odata.type": "#microsoft.graph.microsoftAuthenticatorAuthenticationMethod", + "displayName": "Sample phone", + "createdDateTime": "2026-08-03T12:00:00Z" + } + ], + "ChangedPasswords": [ + { + "displayName": "Sample User", + "userPrincipalName": "sample.user@example.com", + "lastPasswordChangeDateTime": "2026-08-03T12:05:00Z" + } + ], + "TrustedSenders": [ + "trusted@example.net", + "example-partner.com" + ], + "BlockedSenders": [ + "security-alerts@example.org" + ], + "SafelistChanges": [ + { + "Operation": "Set-MailboxJunkEmailConfiguration", + "UserKey": "sample.user@example.com", + "Date": "2026-08-03T11:30:00Z", + "ClientIP": "203.0.113.10", + "Country": "NG", + "City": "Lagos", + "ForeignLocation": true, + "Trusted": [ + "attacker-domain.example" + ], + "Blocked": null + } + ], + "SharingChanges": [ + { + "Operation": "AnonymousLinkCreated", + "UserKey": "sample.user@example.com", + "Date": "2026-08-04T10:15:00Z", + "Workload": "OneDrive", + "FileName": "Payroll Q3.xlsx", + "ItemUrl": "https://example-my.sharepoint.com/personal/sample_user/Documents/Payroll Q3.xlsx", + "Target": null, + "TargetType": null, + "ClientIP": "203.0.113.10", + "Country": "NG", + "City": "Lagos", + "ForeignLocation": true + } + ], + "IntuneDevices": [ + { + "id": "00000000-0000-0000-0000-000000000004", + "deviceName": "SAMPLE-VM01", + "operatingSystem": "Windows", + "osVersion": "10.0.26100", + "complianceState": "noncompliant", + "enrolledDateTime": "2026-08-03T13:00:00Z", + "lastSyncDateTime": "2026-08-05T08:00:00Z", + "deviceEnrollmentType": "windowsAzureADJoin", + "serialNumber": "SAMPLE1234" + } + ], + "SuspectUserSignIns": [ + { + "CreatedDateTime": "2026-08-04T22:14:00Z", + "AppDisplayName": "Office 365 Exchange Online", + "ClientAppUsed": "Browser", + "Status": "Success", + "IPAddress": "203.0.113.10", + "Country": "NG", + "City": "Lagos", + "ForeignLocation": true + }, + { + "CreatedDateTime": "2026-08-04T09:02:00Z", + "AppDisplayName": "Microsoft Teams", + "ClientAppUsed": "Mobile Apps and Desktop clients", + "Status": "Success", + "IPAddress": "198.51.100.24", + "Country": "US", + "City": "Seattle", + "ForeignLocation": false + } + ], + "LocationAnalysis": { + "UsageLocation": "US", + "UserRegisteredCountry": "United States", + "SignInCountries": [ + { + "Country": "US", + "Count": 41 + }, + { + "Country": "NG", + "Count": 9 + } + ], + "ForeignSignInCount": 9, + "ForeignSuccessfulSignInCount": 8, + "ForeignRuleChangeCount": 1, + "ForeignSafelistChangeCount": 1, + "ForeignSharingChangeCount": 1, + "ForeignSentMessageCount": 1, + "Note": null + } + } +} diff --git a/backend/Config/ReportSamples/executive.json b/backend/Config/ReportSamples/executive.json new file mode 100644 index 0000000000..fa10f17dd7 --- /dev/null +++ b/backend/Config/ReportSamples/executive.json @@ -0,0 +1,183 @@ +{ + "UserStats": { + "licensedUsers": 128, + "unlicensedUsers": 12, + "guests": 9, + "globalAdmins": 3, + "permanentGlobalAdmins": 2, + "eligibleGlobalAdmins": 1, + "pimCapable": true + }, + "SecureScore": { + "currentScore": 61, + "maxScore": 100, + "percentageCurrent": 61, + "percentageVsSimilar": 54, + "percentageVsAllTenants": 49, + "trend": [ + { + "label": "Jul 29", + "value": 50 + }, + { + "label": "Jul 30", + "value": 52 + }, + { + "label": "Jul 31", + "value": 55 + }, + { + "label": "Aug 1", + "value": 57 + }, + { + "label": "Aug 2", + "value": 58 + }, + { + "label": "Aug 3", + "value": 60 + }, + { + "label": "Aug 4", + "value": 61 + } + ] + }, + "Licenses": [ + { + "name": "Microsoft 365 E3", + "used": "88", + "available": "12", + "total": "100" + }, + { + "name": "Exchange Online (Plan 1)", + "used": "27", + "available": "3", + "total": "30" + }, + { + "name": "Microsoft Defender for Office 365", + "used": "64", + "available": "36", + "total": "100" + } + ], + "Devices": [ + { + "name": "SAMPLE-LT-001", + "os": "Windows", + "compliance": "compliant", + "compliant": true, + "lastSync": "Aug 5, 2026", + "encrypted": true + }, + { + "name": "SAMPLE-LT-002", + "os": "Windows", + "compliance": "noncompliant", + "compliant": false, + "lastSync": "Jul 28, 2026", + "encrypted": false + }, + { + "name": "SAMPLE-MB-003", + "os": "iOS", + "compliance": "compliant", + "compliant": true, + "lastSync": "Aug 5, 2026", + "encrypted": true + }, + { + "name": "SAMPLE-MB-004", + "os": "Android", + "compliance": "compliant", + "compliant": true, + "lastSync": "Aug 4, 2026", + "encrypted": true + }, + { + "name": "CPC-SAMPLE-005", + "os": "Windows", + "compliance": "compliant", + "compliant": true, + "lastSync": "Aug 5, 2026", + "encrypted": true + } + ], + "CAPolicies": [ + { + "name": "Require MFA for all users", + "state": "enabled", + "controls": [ + "mfa" + ], + "controlsText": "MFA", + "applications": "All" + }, + { + "name": "Block legacy authentication", + "state": "enabled", + "controls": [ + "block" + ], + "controlsText": "Block", + "applications": "All" + }, + { + "name": "Require compliant device for Exchange", + "state": "enabledForReportingButNotEnforced", + "controls": [ + "compliantDevice" + ], + "controlsText": "Compliant Device", + "applications": "Exchange Online" + } + ], + "SecurityControls": [ + { + "name": "Anti-Phishing Policy", + "description": "Protects users from impersonation and spoofing attempts in email.", + "tags": "Exchange, Defender", + "status": "Compliant" + }, + { + "name": "Safe Links Policy", + "description": "Rewrites and scans links in mail and Office documents at click time.", + "tags": "Exchange, Defender", + "status": "Compliant" + }, + { + "name": "Unified Audit Log", + "description": "Records tenant activity so incidents can be investigated after the fact.", + "tags": "Compliance", + "status": "Compliant" + }, + { + "name": "Guest Access Restrictions", + "description": "Limits what guest accounts can see and do in the directory.", + "tags": "Identity", + "status": "Review" + }, + { + "name": "Password Expiry Disabled", + "description": "Removes forced rotation in favour of strong, unique passwords.", + "tags": "Identity", + "status": "Compliant" + }, + { + "name": "Disable Basic Authentication", + "description": "Blocks legacy protocols that cannot enforce multi-factor authentication.", + "tags": "Exchange, Identity", + "status": "Review" + }, + { + "name": "Windows Device Compliance", + "description": "Intune compliance policy requiring encryption, updates and a supported OS.", + "tags": "Intune", + "status": "Compliant" + } + ] +} diff --git a/backend/Config/ReportSamples/mailFlow.json b/backend/Config/ReportSamples/mailFlow.json new file mode 100644 index 0000000000..0253b7165d --- /dev/null +++ b/backend/Config/ReportSamples/mailFlow.json @@ -0,0 +1,188 @@ +{ + "days": 14, + "totals": { + "GoodMail": 48210, + "TransportRules": 1340, + "SpamDetections": 6120, + "EdgeBlockSpam": 3980, + "EmailPhish": 412, + "EmailMalware": 37 + }, + "directionTotals": { + "Inbound": 39280, + "Outbound": 8940, + "IntraOrg": 11879 + }, + "daily": [ + { + "date": "2026-08-04", + "GoodMail": 3410, + "TransportRules": 96, + "SpamDetections": 430, + "EdgeBlockSpam": 281, + "EmailPhish": 29, + "EmailMalware": 3 + }, + { + "date": "2026-08-05", + "GoodMail": 3688, + "TransportRules": 104, + "SpamDetections": 468, + "EdgeBlockSpam": 302, + "EmailPhish": 33, + "EmailMalware": 2 + }, + { + "date": "2026-08-06", + "GoodMail": 3572, + "TransportRules": 88, + "SpamDetections": 451, + "EdgeBlockSpam": 295, + "EmailPhish": 31, + "EmailMalware": 4 + }, + { + "date": "2026-08-07", + "GoodMail": 3740, + "TransportRules": 112, + "SpamDetections": 502, + "EdgeBlockSpam": 318, + "EmailPhish": 38, + "EmailMalware": 1 + }, + { + "date": "2026-08-08", + "GoodMail": 3495, + "TransportRules": 97, + "SpamDetections": 476, + "EdgeBlockSpam": 304, + "EmailPhish": 35, + "EmailMalware": 3 + }, + { + "date": "2026-08-09", + "GoodMail": 1180, + "TransportRules": 21, + "SpamDetections": 268, + "EdgeBlockSpam": 174, + "EmailPhish": 12, + "EmailMalware": 0 + }, + { + "date": "2026-08-10", + "GoodMail": 1042, + "TransportRules": 18, + "SpamDetections": 251, + "EdgeBlockSpam": 166, + "EmailPhish": 10, + "EmailMalware": 1 + }, + { + "date": "2026-08-11", + "GoodMail": 3820, + "TransportRules": 118, + "SpamDetections": 529, + "EdgeBlockSpam": 341, + "EmailPhish": 41, + "EmailMalware": 5 + }, + { + "date": "2026-08-12", + "GoodMail": 3903, + "TransportRules": 121, + "SpamDetections": 544, + "EdgeBlockSpam": 352, + "EmailPhish": 44, + "EmailMalware": 4 + }, + { + "date": "2026-08-13", + "GoodMail": 3766, + "TransportRules": 109, + "SpamDetections": 511, + "EdgeBlockSpam": 329, + "EmailPhish": 36, + "EmailMalware": 2 + }, + { + "date": "2026-08-14", + "GoodMail": 3841, + "TransportRules": 114, + "SpamDetections": 498, + "EdgeBlockSpam": 321, + "EmailPhish": 34, + "EmailMalware": 3 + }, + { + "date": "2026-08-15", + "GoodMail": 3612, + "TransportRules": 102, + "SpamDetections": 470, + "EdgeBlockSpam": 303, + "EmailPhish": 30, + "EmailMalware": 4 + }, + { + "date": "2026-08-16", + "GoodMail": 1214, + "TransportRules": 22, + "SpamDetections": 264, + "EdgeBlockSpam": 172, + "EmailPhish": 20, + "EmailMalware": 3 + }, + { + "date": "2026-08-17", + "GoodMail": 1127, + "TransportRules": 18, + "SpamDetections": 258, + "EdgeBlockSpam": 322, + "EmailPhish": 19, + "EmailMalware": 2 + } + ], + "topSenders": [ + { + "Name": "notifications@sample-crm.example.com", + "Count": 4820 + }, + { + "Name": "billing@example.com", + "Count": 3115 + }, + { + "Name": "scanner-3f@example.com", + "Count": 2064 + }, + { + "Name": "sample.user@example.com", + "Count": 1893 + }, + { + "Name": "helpdesk@example.com", + "Count": 1477 + } + ], + "topSpamRecipients": [ + { + "Name": "info@example.com", + "Count": 1840 + }, + { + "Name": "sales@example.com", + "Count": 1226 + }, + { + "Name": "sample.user@example.com", + "Count": 744 + }, + { + "Name": "accounts@example.com", + "Count": 517 + }, + { + "Name": "careers@example.com", + "Count": 388 + } + ] +} diff --git a/backend/Config/ReportSamples/permissions.json b/backend/Config/ReportSamples/permissions.json new file mode 100644 index 0000000000..4a0fd72568 --- /dev/null +++ b/backend/Config/ReportSamples/permissions.json @@ -0,0 +1,27 @@ +{ + "summary": { + "sitesScanned": 12, + "librariesScanned": 34, + "totalAssignments": 156, + "broadClaimGrants": 2, + "externalGrants": 5, + "directFullControlGrants": 3, + "uniquePermissionLibraries": 7 + }, + "assignments": [ + { + "siteName": "Sample Marketing", + "libraryName": "Documents", + "principal": "Everyone except external users", + "permission": "Edit", + "type": "Broad claim" + }, + { + "siteName": "Sample Finance", + "libraryName": "Documents", + "principal": "partner@example.com", + "permission": "Full Control", + "type": "External" + } + ] +} diff --git a/backend/Config/ReportSamples/reportBuilder.json b/backend/Config/ReportSamples/reportBuilder.json new file mode 100644 index 0000000000..d00cea8b06 --- /dev/null +++ b/backend/Config/ReportSamples/reportBuilder.json @@ -0,0 +1,259 @@ +{ + "blocks": [ + { + "type": "cover", + "title": "Quarterly Security", + "coverAccent": "Review", + "subtitle": "How the tenant stood this quarter, what changed, and what comes next.", + "coverLabel": "Security Review", + "static": true + }, + { + "id": "sample-1", + "type": "blank", + "title": "Summary", + "static": true, + "content": "

This preview uses sample data so you can see how your branding lands before running a report against a real tenant.

" + }, + { + "type": "infobox", + "title": "Why this matters", + "content": "Identity is the perimeter: **every** control in this report starts with who can sign in, and from where.", + "static": true + }, + { + "type": "richbullets", + "title": "Key points", + "items": [ + { + "label": "MFA is enforced.", + "text": "All 128 licensed users are covered by a Conditional Access policy." + }, + { + "label": "Legacy authentication is blocked.", + "text": "No sign-ins over IMAP, POP or SMTP AUTH in the last 30 days." + } + ], + "static": true + }, + { + "type": "note", + "content": "Figures are taken from the last completed sync, not read live.", + "static": true + }, + { + "type": "page", + "title": "Findings in detail", + "subtitle": "What we found, and what to do about it", + "static": true + }, + { + "id": "sample-2", + "type": "scorecard", + "title": "Environment Overview", + "static": true, + "stats": [ + { + "label": "Licensed Users", + "value": "128" + }, + { + "label": "Devices", + "value": "96" + }, + { + "label": "Global Admins", + "value": "3", + "caption": "Target: 2–4" + }, + { + "label": "Guests", + "value": "9" + } + ] + }, + { + "id": "sample-3", + "type": "chart", + "title": "Device Compliance", + "static": true, + "chartKind": "donut", + "chartCentreLabel": "Devices", + "chartData": [ + { + "label": "Compliant", + "value": 78 + }, + { + "label": "Non-compliant", + "value": 14 + }, + { + "label": "Not evaluated", + "value": 4 + } + ] + }, + { + "id": "sample-4", + "type": "chart", + "title": "Secure Score Trend", + "static": true, + "chartKind": "trend", + "chartMax": 100, + "chartCaption": "Current: 61 / 100 (61%)", + "chartData": [ + { + "label": "Jul 29", + "value": 50 + }, + { + "label": "Jul 31", + "value": 55 + }, + { + "label": "Aug 2", + "value": 58 + }, + { + "label": "Aug 4", + "value": 61 + } + ] + }, + { + "id": "sample-5", + "type": "progress", + "title": "Control Coverage", + "static": true, + "items": [ + { + "label": "MFA enforced", + "value": 92, + "max": 100 + }, + { + "label": "Disk encryption", + "value": 78, + "max": 100 + }, + { + "label": "Defender onboarded", + "value": 64, + "max": 100 + } + ] + }, + { + "type": "richtable", + "title": "Top findings", + "columns": [ + { + "header": "Finding", + "key": "c1" + }, + { + "header": "Impact", + "key": "c2" + }, + { + "header": "Owner", + "key": "c3" + } + ], + "rows": [ + { + "c1": "3 stale guest accounts", + "c2": "Medium", + "c3": "IT" + }, + { + "c1": "2 devices out of compliance", + "c2": "Low", + "c3": "Service desk" + } + ], + "static": true + }, + { + "type": "infoboxcolumns", + "columns": 2, + "items": [ + { + "title": "Done this quarter", + "content": "Passwordless sign-in rolled out to 40% of staff." + }, + { + "title": "Next quarter", + "content": "Extend it to the remaining sites and the shared mailboxes." + } + ], + "static": true + }, + { + "id": "sample-6", + "type": "pagebreak", + "title": "", + "static": true + }, + { + "id": "sample-7", + "type": "hero", + "title": "seconds", + "static": true, + "heroHighlight": "39", + "heroSubText": "a business falls victim to ransomware", + "heroFooterText": "Proactive defense beats reactive recovery", + "heroImage": "/reportImages/working.jpg" + }, + { + "id": "sample-8", + "type": "test", + "title": "Multi-factor authentication", + "status": "Failed", + "static": false, + "content": "14 of 128 accounts can still sign in without a second factor.\n\n## Results\n\n| Account | Method | Last sign-in |\n| --- | --- | --- |\n| sample.one@example.com | None | 2 days ago |\n| sample.two@example.com | None | 9 days ago |\n" + }, + { + "id": "sample-9", + "type": "chart", + "title": "Devices by Platform", + "static": true, + "chartKind": "bar", + "chartData": [ + { + "label": "Windows", + "value": 62 + }, + { + "label": "macOS", + "value": 18 + }, + { + "label": "iOS", + "value": 12 + }, + { + "label": "Android", + "value": 4 + } + ] + }, + { + "id": "sample-10", + "type": "database", + "title": "Query Results", + "static": true, + "format": "text", + "content": "| Policy | Identifier | State |\n| --- | --- | --- |\n| Baseline | 8f2a1c4e-6b3d-4f5a-9e7c-1d2b3a4c5e6f | Enabled |\n| Hardened | Microsoft_Defender_for_Business_Servers | Report only |\n" + }, + { + "id": "sample-11", + "type": "database", + "title": "Raw Response", + "static": true, + "format": "json", + "content": "{\n \"tenant\": \"contoso.com\",\n \"policies\": 191,\n \"enabled\": 5\n}" + } + ] +} diff --git a/backend/Config/ReportSamples/shadowAI.json b/backend/Config/ReportSamples/shadowAI.json new file mode 100644 index 0000000000..c5a4f9f6fc --- /dev/null +++ b/backend/Config/ReportSamples/shadowAI.json @@ -0,0 +1,103 @@ +{ + "summary": { + "aiToolsDetected": 18, + "deviceInstalls": 36, + "consentedAiApps": 5, + "highRiskTools": 3, + "sanctionedTools": 2 + }, + "byRisk": [ + { + "risk": "High", + "tools": 3 + }, + { + "risk": "Medium", + "tools": 7 + }, + { + "risk": "Low", + "tools": 6 + }, + { + "risk": "Informational", + "tools": 2 + } + ], + "topTools": [ + { + "tool": "Sample AI Assistant", + "category": "Chat", + "status": "Unsanctioned", + "devices": 22, + "users": 18 + }, + { + "tool": "Sample Code Helper", + "category": "Development", + "status": "Unsanctioned", + "devices": 14, + "users": 9 + }, + { + "tool": "Sample Notetaker", + "category": "Meetings", + "status": "Sanctioned", + "devices": 11, + "users": 24 + } + ], + "detectedApps": [ + { + "application": "Sample AI Assistant Desktop", + "aiTool": "Sample AI Assistant", + "vendor": "Example Corp", + "category": "Chat", + "risk": "High", + "status": "Unsanctioned", + "deviceCount": 22 + }, + { + "application": "Sample Code Helper", + "aiTool": "Sample Code Helper", + "vendor": "Example Labs", + "category": "Development", + "risk": "Medium", + "status": "Unsanctioned", + "deviceCount": 14 + }, + { + "application": "Sample Notetaker", + "aiTool": "Sample Notetaker", + "vendor": "Example Corp", + "category": "Meetings", + "risk": "Informational", + "status": "Sanctioned", + "deviceCount": 11 + } + ], + "consentedApps": [ + { + "applicationId": "00000000-0000-0000-0000-000000000001", + "application": "Sample AI Connector", + "aiTool": "Sample AI Assistant", + "vendor": "Example Corp", + "category": "Chat", + "risk": "High", + "status": "Unsanctioned", + "activeUsersLast7Days": 18, + "firstConsentedDateTime": "2026-06-11T10:22:00Z" + }, + { + "applicationId": "00000000-0000-0000-0000-000000000002", + "application": "Sample Meeting Notes", + "aiTool": "Sample Notetaker", + "vendor": "Example Corp", + "category": "Meetings", + "risk": "Informational", + "status": "Sanctioned", + "activeUsersLast7Days": 24, + "firstConsentedDateTime": "2026-03-02T14:05:00Z" + } + ] +} diff --git a/backend/Config/ReportSamples/sharing.json b/backend/Config/ReportSamples/sharing.json new file mode 100644 index 0000000000..d7f89977f7 --- /dev/null +++ b/backend/Config/ReportSamples/sharing.json @@ -0,0 +1,53 @@ +{ + "summary": { + "totalLinks": 24, + "itemsShared": 18, + "externalRecipients": 6, + "anonymousLinks": 4, + "anonymousEditLinks": 1, + "neverExpiringAnonymous": 2, + "folderShares": 3, + "externalLinks": 6, + "sharePointSites": 5, + "teamsSites": 3, + "oneDriveAccounts": 12 + }, + "links": [ + { + "itemName": "Sample Proposal.docx", + "siteName": "Sample Marketing", + "linkType": "Anonymous", + "scope": "Edit", + "expires": "Never", + "recipients": "Anyone with the link" + }, + { + "itemName": "Sample Budget.xlsx", + "siteName": "Sample Finance", + "linkType": "External", + "scope": "View", + "expires": "2026-12-31", + "recipients": "partner@example.com" + } + ], + "topRecipients": [ + { + "recipient": "partner@example.com", + "links": 5 + }, + { + "recipient": "supplier@example.net", + "links": 3 + } + ], + "topLibraries": [ + { + "library": "Sample Marketing / Documents", + "links": 9 + }, + { + "library": "Sample Finance / Documents", + "links": 6 + } + ] +} diff --git a/backend/Config/openapi.json b/backend/Config/openapi.json index 0ef4f0d2de..c2d605c85b 100644 --- a/backend/Config/openapi.json +++ b/backend/Config/openapi.json @@ -15255,7 +15255,7 @@ "tags": [ "Identity > Administration > Users" ], - "description": "Collates the run's stored results, one CSV per finding set, the score, the containment history, every logbook entry stamped with the case id and the browser-rendered PDF reports (pdfBase64, pdfSummaryBase64) into a ZIP. Nothing is stored: the ZIP is returned base64-encoded (ZipBase64) for the browser to save. Metadata only - nothing in the package is message content.", + "description": "Collates the run's stored results, one CSV per finding set, the score, the containment history, every logbook entry stamped with the case id and the full and C-suite-summary report PDFs (rendered server-side) into a ZIP. Nothing is stored: the ZIP is returned base64-encoded (ZipBase64) for the browser to save. Metadata only - nothing in the package is message content.", "requestBody": { "required": true, "content": { @@ -15266,13 +15266,6 @@ "caseId": { "type": "string" }, - "pdfBase64": { - "type": "string", - "description": "optional: the report PDFs rendered in the browser, base64-encoded (full report + C-suite summary)" - }, - "pdfSummaryBase64": { - "type": "string" - }, "tenantFilter": { "type": "string" } @@ -15693,7 +15686,8 @@ "type": "string" }, "kind": { - "type": "string" + "type": "string", + "description": "A name for a gallery image, so it can be picked by name where the image is offered elsewhere: the report builder lists uploaded covers as Infographic page backgrounds." }, "logoImageId": { "type": "string" @@ -15720,6 +15714,10 @@ "showPageNumbers": { "type": "boolean" }, + "tenantLabel": { + "type": "string", + "description": "Which of the tenant's names the preset's reports print; see the Set action." + }, "watermarkEnabled": { "type": "boolean" }, @@ -22745,46 +22743,7 @@ "application/json": { "schema": { "type": "object", - "description": "Derived from the fields written into the storage table it reads. Fields taken from the storage writers may be omitted by this endpoint, and the response may carry computed fields not listed here.", - "properties": { - "Blocks": { - "type": "string", - "x-cipp-field-source": "storage" - }, - "ETag": { - "type": "string", - "x-cipp-field-source": "storage" - }, - "GeneratedAt": { - "type": "string", - "x-cipp-field-source": "storage" - }, - "PartitionKey": { - "x-cipp-field-source": "storage" - }, - "RowKey": { - "type": "string", - "x-cipp-field-source": "storage" - }, - "Settings": { - "x-cipp-field-source": "storage" - }, - "Status": { - "x-cipp-field-source": "storage" - }, - "TemplateName": { - "type": "string", - "x-cipp-field-source": "storage" - }, - "TenantFilter": { - "type": "string", - "x-cipp-field-source": "storage" - }, - "Timestamp": { - "type": "string", - "x-cipp-field-source": "storage" - } - } + "description": "Not described statically: this endpoint returns the upstream response as-is, so its fields are determined by the upstream API rather than by CIPP. Call the endpoint to see the actual shape, or add a response schema in backend/Config/openapi-overrides." } } } @@ -22868,6 +22827,315 @@ "x-cipp-any-tenant": true } }, + "/api/ExecGetBaselineWhatIfReportPdf": { + "post": { + "summary": "ExecGetBaselineWhatIfReportPdf", + "operationId": "ExecGetBaselineWhatIfReportPdf", + "tags": [ + "Tenant > Standards" + ], + "description": "Server-renders the Baseline What-If report as application/pdf bytes: what applying the\nconfigured standards would change for a tenant today, what each planned stage will change,\noptionally what assigning one more baseline would roll out, and the agreed exceptions. Reads\nthe same alignment payload the Baselines page shows (Get-CIPPBaselineAlignment), composes it\nthrough the shared CIPPSharp component kit (Build-CippBaselineWhatIfReportTree) and returns\nthe finished PDF. Nothing is changed by producing it.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "simulatedTemplateId": { + "type": "string", + "description": "Optional: the GUID of a baseline not assigned to the tenant, to preview what assigning it would roll out stage by stage." + }, + "tenantFilter": { + "type": "string" + } + }, + "required": [ + "tenantFilter" + ] + } + } + } + }, + "parameters": [ + { + "name": "simulatedTemplateId", + "in": "query", + "description": "Optional: the GUID of a baseline not assigned to the tenant, to preview what assigning it would roll out stage by stage.", + "required": false, + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/tenantFilter" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Not described statically: this endpoint returns the upstream response as-is, so its fields are determined by the upstream API rather than by CIPP. Call the endpoint to see the actual shape, or add a response schema in backend/Config/openapi-overrides." + } + } + } + }, + "400": { + "description": "Bad request - missing required field or invalid input" + }, + "401": { + "description": "Unauthorized - invalid or missing bearer token" + }, + "403": { + "description": "Forbidden - caller lacks the required RBAC role" + }, + "500": { + "description": "Internal server error" + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "x-cipp-role": "Tenant.Baselines.Read" + } + }, + "/api/ExecGetBecReportPdf": { + "post": { + "summary": "ExecGetBecReportPdf", + "operationId": "ExecGetBecReportPdf", + "tags": [ + "Identity > Administration > Users" + ], + "description": "Server-renders a stored Business Email Compromise (BEC) run as application/pdf bytes. Reads the\nrun through Get-CIPPBecReport (the BecReports metadata row plus its BecResults payload) and\ncomposes it through the shared CIPPSharp component kit (Build-CippBecReportTree) - the server-side\nreplacement for the client react-pdf BECRemediationReportButton. A run is named by its caseId;\npass userId instead to render the user's newest completed run. The run must have completed - the\nreport reads its stored result, it does not trigger a new investigation.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "caseId": { + "type": "string", + "description": "The stored run to render. A caseId names it directly; a userId picks the user's newest completed run." + }, + "tenantFilter": { + "type": "string" + }, + "userDisplayName": { + "type": "string", + "description": "The investigated user labels the cover and footer; the run stored who it was for." + }, + "userId": { + "type": "string" + }, + "userName": { + "type": "string" + } + }, + "required": [ + "tenantFilter" + ] + } + } + } + }, + "parameters": [ + { + "name": "caseId", + "in": "query", + "description": "The stored run to render. A caseId names it directly; a userId picks the user's newest completed run.", + "required": false, + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/tenantFilter" + }, + { + "name": "userDisplayName", + "in": "query", + "description": "The investigated user labels the cover and footer; the run stored who it was for.", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "userId", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "userName", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Not described statically: this endpoint returns the upstream response as-is, so its fields are determined by the upstream API rather than by CIPP. Call the endpoint to see the actual shape, or add a response schema in backend/Config/openapi-overrides." + } + } + } + }, + "400": { + "description": "Bad request - missing required field or invalid input" + }, + "401": { + "description": "Unauthorized - invalid or missing bearer token" + }, + "403": { + "description": "Forbidden - caller lacks the required RBAC role" + }, + "404": { + "description": "Not found" + }, + "500": { + "description": "Internal server error" + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "x-cipp-role": "Identity.User.Read" + } + }, + "/api/ExecGetExecutiveReportPdf": { + "post": { + "summary": "ExecGetExecutiveReportPdf", + "operationId": "ExecGetExecutiveReportPdf", + "tags": [ + "Tenant > Standards" + ], + "description": "Server-renders the Executive Summary report as application/pdf bytes. Every figure is read from the\nnightly Reporting DB cache via New-CIPPDbRequest (Users/Guests/Roles for the environment overview,\nLicenseOverview, ManagedDevices, ConditionalAccessPolicies and SecureScore) plus the standards\ncomparison from the CippStandardsReports table resolved against the standards catalog - the same\ncached data the rest of CIPP reports from, with no live Graph or cross-endpoint HTTP calls. The\nshaped data is composed through the shared CIPPSharp component kit (Build-CippExecutiveReportTree),\nthe server-side replacement for the client react-pdf ExecutiveReportButton. Each source is gathered\ndefensively: a source with no cached data simply drops its section.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "brandingPresetId": { + "type": "string", + "description": "The branding preset a caller picked for this render, else the global branding settings." + }, + "sectionConfig": { + "type": "string" + }, + "tenantFilter": { + "type": "string" + } + }, + "required": [ + "tenantFilter" + ] + } + } + } + }, + "parameters": [ + { + "name": "brandingPresetId", + "in": "query", + "description": "The branding preset a caller picked for this render, else the global branding settings.", + "required": false, + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/tenantFilter" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Derived from the fields written into the storage table it reads. Fields taken from the storage writers may be omitted by this endpoint, and the response may carry computed fields not listed here.", + "properties": { + "CurrentValue": { + "type": "string", + "x-cipp-field-source": "storage" + }, + "ETag": { + "type": "string", + "x-cipp-field-source": "storage" + }, + "ExpectedValue": { + "type": "string", + "x-cipp-field-source": "storage" + }, + "LicenseAvailable": { + "type": "boolean", + "x-cipp-field-source": "storage" + }, + "PartitionKey": { + "type": "string", + "x-cipp-field-source": "storage" + }, + "RowKey": { + "type": "string", + "x-cipp-field-source": "storage" + }, + "TemplateId": { + "type": "string", + "x-cipp-field-source": "storage" + }, + "Timestamp": { + "type": "string", + "x-cipp-field-source": "storage" + }, + "Value": { + "x-cipp-field-source": "storage" + } + } + } + } + } + }, + "400": { + "description": "Bad request - missing required field or invalid input" + }, + "401": { + "description": "Unauthorized - invalid or missing bearer token" + }, + "403": { + "description": "Forbidden - caller lacks the required RBAC role" + }, + "500": { + "description": "Internal server error" + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "x-cipp-role": "Tenant.Standards.Read" + } + }, "/api/ExecGetLocalAdminPassword": { "post": { "summary": "ExecGetLocalAdminPassword", @@ -22929,6 +23197,143 @@ "x-cipp-role": "Endpoint.Device.Read" } }, + "/api/ExecGetMailFlowReportPdf": { + "post": { + "summary": "ExecGetMailFlowReportPdf", + "operationId": "ExecGetMailFlowReportPdf", + "tags": [ + "Email-Exchange > Reports" + ], + "description": "Server-renders the Exchange mail flow report as application/pdf bytes. Reads the same three Exchange\nreports the Mail Flow page uses (Get-MailFlowStatusReport plus the TopMailSender and\nTopSpamRecipient traffic summaries), aggregates the daily disposition rows the way the page does,\nand composes them through the shared CIPPSharp kit (Build-CippMailFlowReportTree) - the server-side\nreplacement for the client react-pdf MailFlowReportButton.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "tenantFilter": { + "type": "string" + } + }, + "required": [ + "tenantFilter" + ] + } + } + } + }, + "parameters": [ + { + "name": "days", + "in": "query", + "description": "Reporting window in days (1-90).", + "required": false, + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/tenantFilter" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Not described statically: this endpoint returns the upstream response as-is, so its fields are determined by the upstream API rather than by CIPP. Call the endpoint to see the actual shape, or add a response schema in backend/Config/openapi-overrides." + } + } + } + }, + "400": { + "description": "Bad request - missing required field or invalid input" + }, + "401": { + "description": "Unauthorized - invalid or missing bearer token" + }, + "403": { + "description": "Forbidden - caller lacks the required RBAC role" + }, + "500": { + "description": "Internal server error" + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "x-cipp-role": "Exchange.Mailbox.Read" + } + }, + "/api/ExecGetPermissionsReportPdf": { + "post": { + "summary": "ExecGetPermissionsReportPdf", + "operationId": "ExecGetPermissionsReportPdf", + "tags": [ + "Teams-Sharepoint" + ], + "description": "Server-renders the SharePoint Permissions report as application/pdf bytes. Gathers the same shaped\ndata the Permissions page uses (ListSharePointPermissions, from the CIPP reporting cache), composes\nit through the shared CIPPSharp component kit (Build-CippPermissionsReportTree) and returns the\nfinished PDF - the server-side replacement for the client react-pdf PermissionsReportButton.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "tenantFilter": { + "type": "string" + } + }, + "required": [ + "tenantFilter" + ] + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/tenantFilter" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Not described statically: this endpoint returns the upstream response as-is, so its fields are determined by the upstream API rather than by CIPP. Call the endpoint to see the actual shape, or add a response schema in backend/Config/openapi-overrides." + } + } + } + }, + "400": { + "description": "Bad request - missing required field or invalid input" + }, + "401": { + "description": "Unauthorized - invalid or missing bearer token" + }, + "403": { + "description": "Forbidden - caller lacks the required RBAC role" + }, + "500": { + "description": "Internal server error" + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "x-cipp-role": "Sharepoint.Site.Read" + } + }, "/api/ExecGetRecoveryKey": { "post": { "summary": "ExecGetRecoveryKey", @@ -23009,6 +23414,205 @@ "x-cipp-role": "Endpoint.Device.Read" } }, + "/api/ExecGetReportBuilderPdf": { + "get": { + "summary": "ExecGetReportBuilderPdf", + "operationId": "ExecGetReportBuilderPdf", + "tags": [ + "Tools > Report-Builder" + ], + "description": "Returns the server-rendered PDF for a generated Report Builder report as application/pdf bytes.\nBacks both the in-app preview (shown in an iframe) and the download button on the view page.\n404 when the report has no rendered PDF (generated before server-side rendering, or its render\nfailed).", + "parameters": [ + { + "name": "id", + "in": "query", + "description": "The generated report's GUID.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "ReportGUID", + "in": "query", + "description": "The generated report's GUID.", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Not described statically: this endpoint returns the upstream response as-is, so its fields are determined by the upstream API rather than by CIPP. Call the endpoint to see the actual shape, or add a response schema in backend/Config/openapi-overrides." + } + } + } + }, + "400": { + "description": "Bad request - missing required field or invalid input" + }, + "401": { + "description": "Unauthorized - invalid or missing bearer token" + }, + "403": { + "description": "Forbidden - caller lacks the required RBAC role" + }, + "404": { + "description": "Not found" + }, + "500": { + "description": "Internal server error" + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "x-cipp-role": "CIPP.Core.Read", + "x-cipp-any-tenant": true + } + }, + "/api/ExecGetShadowAIReportPdf": { + "post": { + "summary": "ExecGetShadowAIReportPdf", + "operationId": "ExecGetShadowAIReportPdf", + "tags": [ + "Tenant > Standards" + ], + "description": "Server-renders the Shadow AI report as application/pdf bytes. Gathers the same shaped data the\nShadow AI page uses (ListShadowAI) and composes it through the shared CIPPSharp component kit\n(Build-CippShadowAIReportTree) - the server-side replacement for the client react-pdf\nShadowAIReportButton.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sectionConfig": { + "type": "array", + "items": { + "type": "string" + } + }, + "tenantFilter": { + "type": "string" + } + }, + "required": [ + "tenantFilter" + ] + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/tenantFilter" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Not described statically: this endpoint returns the upstream response as-is, so its fields are determined by the upstream API rather than by CIPP. Call the endpoint to see the actual shape, or add a response schema in backend/Config/openapi-overrides." + } + } + } + }, + "400": { + "description": "Bad request - missing required field or invalid input" + }, + "401": { + "description": "Unauthorized - invalid or missing bearer token" + }, + "403": { + "description": "Forbidden - caller lacks the required RBAC role" + }, + "500": { + "description": "Internal server error" + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "x-cipp-role": "Tenant.Standards.Read" + } + }, + "/api/ExecGetSharingReportPdf": { + "post": { + "summary": "ExecGetSharingReportPdf", + "operationId": "ExecGetSharingReportPdf", + "tags": [ + "Teams-Sharepoint" + ], + "description": "Server-renders the SharePoint & OneDrive Sharing report as application/pdf bytes. Gathers the same\nshaped data the Sharing page uses (ListSharePointSharing, from the CIPP reporting cache), composes\nit through the shared CIPPSharp component kit (Build-CippSharingReportTree) and returns the finished\nPDF - the server-side replacement for the client react-pdf SharingReportButton.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "tenantFilter": { + "type": "string" + } + }, + "required": [ + "tenantFilter" + ] + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/tenantFilter" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Not described statically: this endpoint returns the upstream response as-is, so its fields are determined by the upstream API rather than by CIPP. Call the endpoint to see the actual shape, or add a response schema in backend/Config/openapi-overrides." + } + } + } + }, + "400": { + "description": "Bad request - missing required field or invalid input" + }, + "401": { + "description": "Unauthorized - invalid or missing bearer token" + }, + "403": { + "description": "Forbidden - caller lacks the required RBAC role" + }, + "500": { + "description": "Internal server error" + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "x-cipp-role": "Sharepoint.Site.Read" + } + }, "/api/ExecGitHubAction": { "post": { "summary": "Invoke GitHub Action", @@ -29428,6 +30032,151 @@ "x-cipp-role": "Identity.Role.ReadWrite" } }, + "/api/ExecPreviewBrandingReportPdf": { + "post": { + "summary": "ExecPreviewBrandingReportPdf", + "operationId": "ExecPreviewBrandingReportPdf", + "tags": [ + "CIPP > Settings" + ], + "description": "Renders a sample report against a branding configuration - normally the unsaved state of the\nbranding editor - and returns it as application/pdf bytes, so a colour, logo, cover, footer or\nwatermark can be judged on every page of a real report before it is saved. Each report type has\na fixed sample of the data its builder needs (Config/ReportSamples/.json); nothing is read\nfrom a tenant.", + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "branding": { + "type": "object", + "properties": { + "tenantLabel": { + "type": "string", + "description": "The sample tenant, named the way the branding under edit would name a real one." + } + }, + "description": "The branding to render against, in the shape Get-CIPPBrandingSettings returns: colours (flat or under roleColours), a data-URL logo and cover or a coverStock path, footer, watermark and tenantLabel. Omitted -> the saved branding settings." + }, + "reportType": { + "type": "string", + "description": "Which report to preview: executive, reportBuilder, shadowAI, bec, sharing, permissions or mailFlow." + }, + "tenantFilter": { + "type": "string", + "description": "Optional: the tenant whose %variables% (%cippurl%, custom variables) resolve in the footer and cover." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Not described statically: this endpoint returns the upstream response as-is, so its fields are determined by the upstream API rather than by CIPP. Call the endpoint to see the actual shape, or add a response schema in backend/Config/openapi-overrides." + } + } + } + }, + "400": { + "description": "Bad request - missing required field or invalid input" + }, + "401": { + "description": "Unauthorized - invalid or missing bearer token" + }, + "403": { + "description": "Forbidden - caller lacks the required RBAC role" + }, + "500": { + "description": "Internal server error" + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "x-cipp-role": "CIPP.AppSettings.Read", + "x-cipp-any-tenant": true + } + }, + "/api/ExecPreviewReportBuilderPdf": { + "post": { + "summary": "ExecPreviewReportBuilderPdf", + "operationId": "ExecPreviewReportBuilderPdf", + "tags": [ + "Tools > Report-Builder" + ], + "description": "Renders the current (unsaved) Report Builder state to a PDF and returns it as application/pdf\nbytes without persisting a generated-report row. Powers the builder's live preview and download.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "Blocks": { + "type": "string" + }, + "Settings": { + "type": "string" + }, + "TemplateGUID": { + "type": "string" + }, + "TemplateName": { + "type": "string" + }, + "TenantFilter": { + "type": "string" + } + }, + "required": [ + "TenantFilter" + ] + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/tenantFilter" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Not described statically: this endpoint returns the upstream response as-is, so its fields are determined by the upstream API rather than by CIPP. Call the endpoint to see the actual shape, or add a response schema in backend/Config/openapi-overrides." + } + } + } + }, + "401": { + "description": "Unauthorized - invalid or missing bearer token" + }, + "403": { + "description": "Forbidden - caller lacks the required RBAC role" + }, + "500": { + "description": "Internal server error" + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "x-cipp-role": "CIPP.Core.ReadWrite" + } + }, "/api/ExecQuarantineManagement": { "post": { "summary": "ExecQuarantineManagement", @@ -44231,7 +44980,7 @@ "tags": [ "CIPP > Core" ], - "description": "Retrieves cached tenant data from the CIPP reporting database (CippReportingDB). This is the fastest\nand most efficient way to query tenant data across single or multiple tenants. The database is populated\nnightly by background cache jobs, so data is typically at most 24 hours old.\n\nRequired query parameters:\n - tenantFilter: The tenant domain or 'AllTenants' to query all managed tenants.\n - type: The cache collection to retrieve (e.g. Users, Groups, Mailboxes, Devices, etc.).\n Not required when countsOnly=true.\n\nOptional query parameters:\n - countsOnly: When 'true', returns one row per tenant per collection containing only the record\n count and the time that collection was last cached. This reads the pre-computed\n '-Count' rows, so it is a single table query regardless of tenant count and\n never materializes the underlying records. Combine with tenantFilter=AllTenants to\n get an estate-wide inventory and per-tenant cache freshness in one call. Pass a\n type alongside it to restrict the result to a single collection.\n\nUse type=_availableTypes to discover which cache collections exist for a given tenant. Omitting the\ntype parameter also returns the available types.\n\nPERFORMANCE GUIDANCE: For AllTenants queries or any bulk/cross-tenant data retrieval, prefer\nListDBCache over calling individual endpoints (e.g. ListUsers, ListGroups, ListMailboxes) directly.\nIndividual endpoints make live API calls per tenant which is significantly slower and may hit\nthrottling limits. ListDBCache reads pre-cached data from Azure Table Storage and returns results\nin seconds regardless of tenant count.\n\nNote that tenantFilter=AllTenants WITH a type performs a cross-partition scan and materializes every\nrecord of that collection across every tenant, so it grows with the size of the estate. Where only\ntotals are needed, countsOnly=true is dramatically cheaper and should always be preferred.\n\nRecommended workflow for MCP tool selection:\n 1. Call ListDBCache with type=_availableTypes to discover available cache collections.\n 2. If the data you need exists as a cache type, use ListDBCache with that type.\n 3. Only fall back to individual List* endpoints when you need real-time data for a single tenant\n or when the data is not available in the cache.\n\nCommon cache types include: Users, Groups, Mailboxes, Devices, ConditionalAccess, Applications,\nIntunePolicy, CompliancePolicy, and many more. The exact set depends on what has been configured.", + "description": "Retrieves cached tenant data from the CIPP reporting database (CippReportingDB). This is the fastest\nand most efficient way to query tenant data across single or multiple tenants. The database is populated\nnightly by background cache jobs, so data is typically at most 24 hours old.\n\nRequired query parameters:\n - tenantFilter: The tenant domain or 'AllTenants' to query all managed tenants.\n - type: The cache collection to retrieve (e.g. Users, Groups, Mailboxes, Devices, etc.).\n Not required when countsOnly=true.\n\nOptional query parameters:\n - countsOnly: When 'true', returns one row per tenant per collection containing only the record\n count and the time that collection was last cached. This reads the pre-computed\n '-Count' rows, so it is a single table query regardless of tenant count and\n never materializes the underlying records. Combine with tenantFilter=AllTenants to\n get an estate-wide inventory and per-tenant cache freshness in one call. Pass a\n type alongside it to restrict the result to a single collection.\n\nUse type=_shape for every collection's row count and fields (recorded when the cache was written).\nUse type=_availableTypes to discover which cache collections exist for a given tenant. Omitting the\ntype parameter also returns the available types.\n\nPERFORMANCE GUIDANCE: For AllTenants queries or any bulk/cross-tenant data retrieval, prefer\nListDBCache over calling individual endpoints (e.g. ListUsers, ListGroups, ListMailboxes) directly.\nIndividual endpoints make live API calls per tenant which is significantly slower and may hit\nthrottling limits. ListDBCache reads pre-cached data from Azure Table Storage and returns results\nin seconds regardless of tenant count.\n\nNote that tenantFilter=AllTenants WITH a type performs a cross-partition scan and materializes every\nrecord of that collection across every tenant, so it grows with the size of the estate. Where only\ntotals are needed, countsOnly=true is dramatically cheaper and should always be preferred.\n\nRecommended workflow for MCP tool selection:\n 1. Call ListDBCache with type=_availableTypes to discover available cache collections.\n 2. If the data you need exists as a cache type, use ListDBCache with that type.\n 3. Only fall back to individual List* endpoints when you need real-time data for a single tenant\n or when the data is not available in the cache.\n\nCommon cache types include: Users, Groups, Mailboxes, Devices, ConditionalAccess, Applications,\nIntunePolicy, CompliancePolicy, and many more. The exact set depends on what has been configured.", "parameters": [ { "name": "countsOnly", @@ -56181,359 +56930,7 @@ "type": "array", "items": { "type": "object", - "description": "Derived from the Microsoft Graph entity it queries, and the fields the endpoint selects onto each record. This endpoint returns the Graph response as-is without selecting fields, so these are the properties the entity CAN carry (x-cipp-field-source: graph-entity) rather than a proven projection - Graph returns a default subset unless asked otherwise.", - "properties": { - "agent": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "aiToolsDetected": { - "x-cipp-field-source": "backend" - }, - "appDisplayName": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "appId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "appliedConditionalAccessPolicies": { - "type": "array", - "x-cipp-field-source": "graph-entity" - }, - "appliedEventListeners": { - "type": "array", - "x-cipp-field-source": "graph-entity" - }, - "appOwnerTenantId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "appTokenProtectionStatus": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "authenticationAppDeviceDetails": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "authenticationAppPolicyEvaluationDetails": { - "type": "array", - "x-cipp-field-source": "graph-entity" - }, - "authenticationContextClassReferences": { - "type": "array", - "x-cipp-field-source": "graph-entity" - }, - "authenticationDetails": { - "type": "array", - "x-cipp-field-source": "graph-entity" - }, - "authenticationMethodsUsed": { - "type": "array", - "x-cipp-field-source": "graph-entity" - }, - "authenticationProcessingDetails": { - "type": "array", - "x-cipp-field-source": "graph-entity" - }, - "authenticationProtocol": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "authenticationRequirement": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "authenticationRequirementPolicies": { - "type": "array", - "x-cipp-field-source": "graph-entity" - }, - "autonomousSystemNumber": { - "type": "integer", - "x-cipp-field-source": "graph-entity" - }, - "azureResourceId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "byCategory": { - "x-cipp-field-source": "backend" - }, - "byRisk": { - "x-cipp-field-source": "backend" - }, - "clientAppUsed": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "clientCredentialType": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "clientSessionId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "conditionalAccessAudiences": { - "type": "array", - "x-cipp-field-source": "graph-entity" - }, - "conditionalAccessStatus": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "consentedAiApps": { - "x-cipp-field-source": "backend" - }, - "consentedApps": { - "x-cipp-field-source": "backend" - }, - "correlationId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "createdDateTime": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "crossTenantAccessType": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "detectedApps": { - "x-cipp-field-source": "backend" - }, - "deviceDetail": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "deviceInstalls": { - "x-cipp-field-source": "backend" - }, - "entraSynced": { - "x-cipp-field-source": "backend" - }, - "federatedCredentialId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "flaggedForReview": { - "type": "boolean", - "x-cipp-field-source": "graph-entity" - }, - "globalSecureAccessIpAddress": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "highRiskTools": { - "x-cipp-field-source": "backend" - }, - "homeTenantId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "homeTenantName": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "id": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "incomingTokenType": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "intuneSynced": { - "x-cipp-field-source": "backend" - }, - "ipAddress": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "ipAddressFromResourceProvider": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "isInteractive": { - "type": "boolean", - "x-cipp-field-source": "graph-entity" - }, - "isTenantRestricted": { - "type": "boolean", - "x-cipp-field-source": "graph-entity" - }, - "isThroughGlobalSecureAccess": { - "type": "boolean", - "x-cipp-field-source": "graph-entity" - }, - "lastDataRefresh": { - "x-cipp-field-source": "backend" - }, - "location": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "managedServiceIdentity": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "mfaDetail": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "networkLocationDetails": { - "type": "array", - "x-cipp-field-source": "graph-entity" - }, - "originalRequestId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "originalTransferMethod": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "privateLinkDetails": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "processingTimeInMilliseconds": { - "type": "integer", - "x-cipp-field-source": "graph-entity" - }, - "resourceDisplayName": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "resourceId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "resourceOwnerTenantId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "resourceServicePrincipalId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "resourceTenantId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "riskDetail": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "riskEventTypes_v2": { - "type": "array", - "x-cipp-field-source": "graph-entity" - }, - "riskLevelAggregated": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "riskLevelDuringSignIn": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "riskState": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "sanctionedTools": { - "x-cipp-field-source": "backend" - }, - "servicePrincipalCredentialKeyId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "servicePrincipalCredentialThumbprint": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "servicePrincipalId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "servicePrincipalName": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "sessionId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "sessionLifetimePolicies": { - "type": "array", - "x-cipp-field-source": "graph-entity" - }, - "signInEventTypes": { - "type": "array", - "x-cipp-field-source": "graph-entity" - }, - "signInIdentifier": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "signInIdentifierType": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "signInTokenProtectionStatus": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "status": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "summary": { - "x-cipp-field-source": "backend" - }, - "tokenIssuerName": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "tokenIssuerType": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "tokenProtectionStatusDetails": { - "type": "object", - "x-cipp-field-source": "graph-entity" - }, - "topTools": { - "x-cipp-field-source": "backend" - }, - "uniqueTokenIdentifier": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "userAgent": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "userDisplayName": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "userId": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "userPrincipalName": { - "type": "string", - "x-cipp-field-source": "graph-entity" - }, - "userType": { - "type": "object", - "x-cipp-field-source": "graph-entity" - } - } + "description": "Not described statically: this endpoint returns the upstream response as-is, so its fields are determined by the upstream API rather than by CIPP. Call the endpoint to see the actual shape, or add a response schema in backend/Config/openapi-overrides." } } } @@ -56813,78 +57210,7 @@ "type": "array", "items": { "type": "object", - "description": "Derived from the fields the endpoint selects onto each record. The response may carry more; these are the ones known to exist.", - "properties": { - "assignments": { - "x-cipp-field-source": "backend" - }, - "broadClaimGrants": { - "x-cipp-field-source": "backend" - }, - "byBroadClaim": { - "x-cipp-field-source": "backend" - }, - "byPermissionLevel": { - "x-cipp-field-source": "backend" - }, - "byPrincipalType": { - "x-cipp-field-source": "backend" - }, - "claim": { - "x-cipp-field-source": "backend" - }, - "directFullControlGrants": { - "x-cipp-field-source": "backend" - }, - "externalGrants": { - "x-cipp-field-source": "backend" - }, - "grants": { - "x-cipp-field-source": "backend" - }, - "lastDataRefresh": { - "x-cipp-field-source": "backend" - }, - "level": { - "x-cipp-field-source": "backend" - }, - "libraries": { - "x-cipp-field-source": "backend" - }, - "librariesScanned": { - "x-cipp-field-source": "backend" - }, - "permissionsSynced": { - "x-cipp-field-source": "backend" - }, - "site": { - "x-cipp-field-source": "backend" - }, - "sitesScanned": { - "x-cipp-field-source": "backend" - }, - "sitesSkipped": { - "x-cipp-field-source": "backend" - }, - "skippedSites": { - "x-cipp-field-source": "backend" - }, - "summary": { - "x-cipp-field-source": "backend" - }, - "topSitesByUniqueLibraries": { - "x-cipp-field-source": "backend" - }, - "totalAssignments": { - "x-cipp-field-source": "backend" - }, - "type": { - "x-cipp-field-source": "backend" - }, - "uniquePermissionLibraries": { - "x-cipp-field-source": "backend" - } - } + "description": "Not described statically: this endpoint returns the upstream response as-is, so its fields are determined by the upstream API rather than by CIPP. Call the endpoint to see the actual shape, or add a response schema in backend/Config/openapi-overrides." } } } @@ -57135,111 +57461,7 @@ "type": "array", "items": { "type": "object", - "description": "Derived from the fields the endpoint selects onto each record. The response may carry more; these are the ones known to exist.", - "properties": { - "anonymousEditLinks": { - "x-cipp-field-source": "backend" - }, - "anonymousLinks": { - "x-cipp-field-source": "backend" - }, - "byLinkType": { - "x-cipp-field-source": "backend" - }, - "byScope": { - "x-cipp-field-source": "backend" - }, - "externalLinks": { - "x-cipp-field-source": "backend" - }, - "externalRecipients": { - "x-cipp-field-source": "backend" - }, - "folderShares": { - "x-cipp-field-source": "backend" - }, - "internalLinks": { - "x-cipp-field-source": "backend" - }, - "itemsShared": { - "x-cipp-field-source": "backend" - }, - "lastDataRefresh": { - "x-cipp-field-source": "backend" - }, - "library": { - "x-cipp-field-source": "backend" - }, - "links": { - "x-cipp-field-source": "backend" - }, - "linksSynced": { - "x-cipp-field-source": "backend" - }, - "neverExpiringAnonymous": { - "x-cipp-field-source": "backend" - }, - "oneDriveAccounts": { - "x-cipp-field-source": "backend" - }, - "oneDriveFiles": { - "x-cipp-field-source": "backend" - }, - "oneDriveStorageUsedGB": { - "x-cipp-field-source": "backend" - }, - "passwordProtectedLinks": { - "x-cipp-field-source": "backend" - }, - "recipient": { - "x-cipp-field-source": "backend" - }, - "scope": { - "x-cipp-field-source": "backend" - }, - "sharePointFiles": { - "x-cipp-field-source": "backend" - }, - "sharePointSites": { - "x-cipp-field-source": "backend" - }, - "sharePointStorageUsedGB": { - "x-cipp-field-source": "backend" - }, - "site": { - "x-cipp-field-source": "backend" - }, - "summary": { - "x-cipp-field-source": "backend" - }, - "teamsFiles": { - "x-cipp-field-source": "backend" - }, - "teamsSites": { - "x-cipp-field-source": "backend" - }, - "teamsStorageUsedGB": { - "x-cipp-field-source": "backend" - }, - "topLibraries": { - "x-cipp-field-source": "backend" - }, - "topRecipients": { - "x-cipp-field-source": "backend" - }, - "topSites": { - "x-cipp-field-source": "backend" - }, - "totalLinks": { - "x-cipp-field-source": "backend" - }, - "type": { - "x-cipp-field-source": "backend" - }, - "usageSynced": { - "x-cipp-field-source": "backend" - } - } + "description": "Not described statically: this endpoint returns the upstream response as-is, so its fields are determined by the upstream API rather than by CIPP. Call the endpoint to see the actual shape, or add a response schema in backend/Config/openapi-overrides." } } } diff --git a/backend/Modules/CIPPCore/Public/Add-CIPPDbItem.ps1 b/backend/Modules/CIPPCore/Public/Add-CIPPDbItem.ps1 index 597a479adb..53cd4d1343 100644 --- a/backend/Modules/CIPPCore/Public/Add-CIPPDbItem.ps1 +++ b/backend/Modules/CIPPCore/Public/Add-CIPPDbItem.ps1 @@ -57,6 +57,52 @@ function Add-CIPPDbItem { $RunStartUtc = [DateTimeOffset]::UtcNow.AddMinutes(-$SkewMarginMinutes) $TotalProcessed = 0 + + # The collection's shape - its fields and their types, one nested level deep - sampled from the + # first rows written and stored on the '-Count' row (Shape), so the report builder can + # offer a collection's fields without reading the collection. Sampled, not exhaustive: a field + # only some rows carry may be missed, which the picker tolerates by accepting a typed name. + $ShapeFields = [ordered]@{} + $ShapeSampled = 0 + $ShapeSampleSize = 50 + $ShapeMaxFields = 300 + $TypeOf = { + param($Value) + if ($null -eq $Value) { return 'null' } + if ($Value -is [bool]) { return 'boolean' } + if ($Value -is [datetime] -or $Value -is [DateTimeOffset]) { return 'date' } + if ($Value -is [string]) { return $(if ($Value -match '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}') { 'date' } else { 'string' }) } + if ($Value.GetType().IsPrimitive -or $Value -is [decimal]) { return 'number' } + if ($Value -is [System.Collections.IDictionary] -or $Value -is [System.Management.Automation.PSCustomObject]) { return 'object' } + if ($Value -is [System.Collections.IEnumerable]) { return 'array' } + 'string' + } + $PropertiesOf = { + param($Value) + if ($Value -is [System.Collections.IDictionary]) { foreach ($Key in $Value.Keys) { @{ Name = "$Key"; Value = $Value[$Key] } } } + elseif ($Value -is [System.Management.Automation.PSCustomObject]) { foreach ($Property in $Value.PSObject.Properties) { @{ Name = $Property.Name; Value = $Property.Value } } } + } + $NoteField = { + param([string]$Name, $Value) + if ($ShapeFields.Count -ge $ShapeMaxFields -and -not $ShapeFields.Contains($Name)) { return } + $Kind = & $TypeOf $Value + if (-not $ShapeFields.Contains($Name) -or $ShapeFields[$Name] -eq 'null') { $ShapeFields[$Name] = $Kind } + $Kind + } + $NoteShape = { + param($Item) + if ($ShapeSampled -ge $ShapeSampleSize) { return } + $ShapeSampled++ + foreach ($Property in @(& $PropertiesOf $Item)) { + $Kind = & $NoteField $Property.Name $Property.Value + # one level in: an object's own fields, or the fields of an array's first object + $Inner = if ($Kind -eq 'object') { $Property.Value } elseif ($Kind -eq 'array') { @($Property.Value | Select-Object -First 1)[0] } + if ($null -ne $Inner -and (& $TypeOf $Inner) -eq 'object') { + foreach ($Child in @(& $PropertiesOf $Inner)) { $null = & $NoteField "$($Property.Name).$($Child.Name)" $Child.Value } + } + } + } + # Cache regex instances so each row pays only the match cost, not regex compilation. # Two passes preserve the original semantics: path/wildcard chars → '_', control chars → stripped. $RowKeyPathRegex = [regex]::new('[/\\#?]') @@ -83,6 +129,7 @@ function Add-CIPPDbItem { $ItemId = $Item.ExternalDirectoryObjectId ?? $Item.id ?? $Item.Identity ?? $Item.skuId ?? $Item.userPrincipalName ?? [guid]::NewGuid().ToString() $RowKey = $RowKeyControlRegex.Replace($RowKeyPathRegex.Replace("$Type-$ItemId", '_'), '') if ($SeenInBatch.Add($RowKey)) { + & $NoteShape $Item $Batch.Add(@{ PartitionKey = $TenantFilter RowKey = $RowKey @@ -154,16 +201,32 @@ function Add-CIPPDbItem { if ($Count.IsPresent -or $AddCount.IsPresent) { $NewCount = $TotalProcessed - if ($Append.IsPresent) { + # The existing count row is read when appending (to add to its count) and when this call + # sampled no rows (a count-only call after the rows went in separately), so a shape + # already recorded is kept rather than wiped. + $ExistingCount = $null + if ($Append.IsPresent -or $ShapeFields.Count -eq 0) { $Filter = "PartitionKey eq '{0}' and RowKey eq '{1}-Count'" -f $TenantFilter, $Type $ExistingCount = Get-CIPPAzDataTableEntity @Table -Filter $Filter - if ($ExistingCount.DataCount) { $NewCount += [int]$ExistingCount.DataCount } } + if ($Append.IsPresent -and $ExistingCount.DataCount) { $NewCount += [int]$ExistingCount.DataCount } + if (($Append.IsPresent -or $ShapeFields.Count -eq 0) -and $ExistingCount.Shape) { + try { + foreach ($Known in @((ConvertFrom-Json -InputObject "$($ExistingCount.Shape)").fields)) { + if ($Known.name -and -not $ShapeFields.Contains([string]$Known.name)) { $ShapeFields[[string]$Known.name] = [string]$Known.type } + } + } catch { Write-Verbose "Unreadable shape on $Type-Count; recording afresh." } + } + $ShapeJson = ConvertTo-Json -InputObject @{ + fields = @(foreach ($Name in $ShapeFields.Keys) { @{ name = $Name; type = $ShapeFields[$Name] } }) + sampledAt = (Get-Date).ToUniversalTime().ToString('o') + } -Depth 5 -Compress $null = Add-CIPPAzDataTableEntity @Table -Entity @{ PartitionKey = $TenantFilter RowKey = "$Type-Count" DataCount = [int]$NewCount Type = $Type + Shape = $ShapeJson } -Force } diff --git a/backend/Modules/CIPPCore/Public/Add-CIPPImage.ps1 b/backend/Modules/CIPPCore/Public/Add-CIPPImage.ps1 index 2168999eb7..804fb7581f 100644 --- a/backend/Modules/CIPPCore/Public/Add-CIPPImage.ps1 +++ b/backend/Modules/CIPPCore/Public/Add-CIPPImage.ps1 @@ -28,8 +28,17 @@ function Add-CIPPImage { if ($Data -notmatch '^data:image\/([^;]+);base64,') { throw 'Invalid image format. Expected a data URL image (data:image/...;base64,...).' } + $Subtype = $Matches[1].ToLowerInvariant() - $ContentType = "image/$($Matches[1])" + # The formats the report engine draws (ReportComponents.NormaliseImage: rasters handed to OfficeIMO + # as-is, SVG rasterised once). Anything else would be stored, sent down on every page load, and then + # dropped at render time. The branding page enforces the same list before uploading. + $SupportedTypes = @('png', 'jpeg', 'jpg', 'gif', 'bmp', 'tiff', 'webp', 'svg+xml') + if ($Subtype -notin $SupportedTypes) { + throw "Unsupported image format 'image/$Subtype'. Use PNG, JPEG, GIF, BMP, TIFF, WebP or SVG." + } + + $ContentType = "image/$Subtype" $Base64Data = $Data -replace '^data:image\/[^;]+;base64,', '' try { $ImageBytes = [Convert]::FromBase64String($Base64Data) diff --git a/backend/Modules/CIPPCore/Public/BEC/New-CIPPBecEvidencePackage.ps1 b/backend/Modules/CIPPCore/Public/BEC/New-CIPPBecEvidencePackage.ps1 index a77fabb7ed..76f7fe6a58 100644 --- a/backend/Modules/CIPPCore/Public/BEC/New-CIPPBecEvidencePackage.ps1 +++ b/backend/Modules/CIPPCore/Public/BEC/New-CIPPBecEvidencePackage.ps1 @@ -5,18 +5,14 @@ function New-CIPPBecEvidencePackage { .DESCRIPTION Collates everything CIPP holds about a case into one ZIP: the results payload as JSON, one CSV per finding set, the score, the containment history, every logbook line stamped with the - case id, and the browser-rendered PDF reports when supplied. Nothing is stored: the ZIP is - returned to the caller to hand to the browser. Everything inside is metadata the run already - collected; passwords were redacted before they were stored and are scrubbed from the logbook - copy again here. + case id, and the full and C-suite-summary report PDFs rendered server-side. Nothing is stored: + the ZIP is returned to the caller to hand to the browser. Everything inside is metadata the run + already collected; passwords were redacted before they were stored and are scrubbed from the + logbook copy again here. .PARAMETER TenantFilter Tenant default domain name. .PARAMETER CaseId The run to package. - .PARAMETER PdfBase64 - Optional base64-encoded full PDF report rendered by the frontend. - .PARAMETER PdfSummaryBase64 - Optional base64-encoded C-suite summary PDF rendered by the frontend. .PARAMETER Headers CIPP request headers, for logging. .PARAMETER APIName @@ -28,8 +24,6 @@ function New-CIPPBecEvidencePackage { param( [Parameter(Mandatory = $true)][string]$TenantFilter, [Parameter(Mandatory = $true)][string]$CaseId, - [string]$PdfBase64, - [string]$PdfSummaryBase64, $Headers, [string]$APIName = 'BECEvidenceExport' ) @@ -97,18 +91,31 @@ function New-CIPPBecEvidencePackage { } $Files['logbook.json'] = $Utf8.GetBytes((ConvertTo-Json -InputObject @($LogRows) -Depth 10)) - # The frontend renders the reports client-side (react-pdf), so it hands the PDFs in. Validate and - # add each supplied one - the full report and the C-suite summary. - $AddPdf = { - param([string]$Base64, [string]$Name) - if ([string]::IsNullOrWhiteSpace($Base64)) { return } - $PdfBytes = [System.Convert]::FromBase64String(($Base64 -replace '^data:application/pdf;base64,', '')) - if ($PdfBytes.Length -gt 25MB) { throw "The PDF report ($Name) exceeds 25 MB" } - if ($PdfBytes.Length -lt 4 -or [System.Text.Encoding]::ASCII.GetString($PdfBytes, 0, 4) -ne '%PDF') { throw "The supplied $Name report is not a PDF" } - $Files[$Name] = $PdfBytes + # Render the report PDFs server-side (the full report and the C-suite summary) through the same + # builder and kit the report endpoint uses. The builder reads containment off becData.Run, so + # attach the run block exactly as ExecGetBecReportPdf does. A render failure must not lose the rest + # of the evidence, so it is caught and the package ships without the PDFs. + try { + $BecData = $Results + $RunBlock = [pscustomobject]@{ + CaseId = $Run.CaseId + Status = $Run.Status + ExtractedAt = $Run.ExtractedAt + RequestedAt = $Run.RequestedAt + RequestedBy = $Run.RequestedBy + Containment = $Run.Containment + } + $BecData | Add-Member -NotePropertyName 'Run' -NotePropertyValue $RunBlock -Force + $TenantName = Get-CippReportTenantName -TenantFilter $TenantFilter + $DisplayName = @($Run.DisplayName, $Run.UserPrincipalName, $Run.UserId) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -First 1 + $UserData = [pscustomobject]@{ displayName = $DisplayName; userPrincipalName = $Run.UserPrincipalName; id = $Run.UserId } + $Full = Build-CippBecReportTree -UserData $UserData -BecData $BecData -TenantName $TenantName + $Files['report-full.pdf'] = ConvertTo-CippReportPdf -Blocks $Full.Blocks -Variables $Full.Variables -TenantName $TenantName -TenantFilter $TenantFilter -ReportName 'BEC Analysis Report' + $Summary = Build-CippBecReportTree -UserData $UserData -BecData $BecData -TenantName $TenantName -Variant summary + $Files['report-summary.pdf'] = ConvertTo-CippReportPdf -Blocks $Summary.Blocks -Variables $Summary.Variables -TenantName $TenantName -TenantFilter $TenantFilter -ReportName 'BEC Analysis Report' + } catch { + Write-Information "BEC evidence: server-side PDF render failed for $CaseId`: $($_.Exception.Message)" } - & $AddPdf $PdfBase64 'report-full.pdf' - & $AddPdf $PdfSummaryBase64 'report-summary.pdf' $Stream = [System.IO.MemoryStream]::new() $Archive = [System.IO.Compression.ZipArchive]::new($Stream, [System.IO.Compression.ZipArchiveMode]::Create, $true) diff --git a/backend/Modules/CIPPCore/Public/Get-CIPPBrandingPreset.ps1 b/backend/Modules/CIPPCore/Public/Get-CIPPBrandingPreset.ps1 index 76de098da4..b32b3e60a7 100644 --- a/backend/Modules/CIPPCore/Public/Get-CIPPBrandingPreset.ps1 +++ b/backend/Modules/CIPPCore/Public/Get-CIPPBrandingPreset.ps1 @@ -90,6 +90,7 @@ function Get-CIPPBrandingPreset { showPageNumbers = if ($null -eq $Entity.showPageNumbers) { $true } else { [bool]$Entity.showPageNumbers } watermarkText = if ($Entity.watermarkText) { "$($Entity.watermarkText)" } else { '' } watermarkEnabled = if ($null -eq $Entity.watermarkEnabled) { $true } else { [bool]$Entity.watermarkEnabled } + tenantLabel = if (@('alias', 'name', 'domain') -contains "$($Entity.tenantLabel)") { "$($Entity.tenantLabel)" } else { 'alias' } } } diff --git a/backend/Modules/CIPPCore/Public/Get-CIPPBrandingSettings.ps1 b/backend/Modules/CIPPCore/Public/Get-CIPPBrandingSettings.ps1 index 3548ad5333..db5590381b 100644 --- a/backend/Modules/CIPPCore/Public/Get-CIPPBrandingSettings.ps1 +++ b/backend/Modules/CIPPCore/Public/Get-CIPPBrandingSettings.ps1 @@ -265,6 +265,13 @@ function Get-CIPPBrandingSettings { $ShowPageNumbers = if ($null -eq $BrandingConfig.showPageNumbers) { $true } else { [bool]$BrandingConfig.showPageNumbers } $WatermarkEnabled = if ($null -eq $BrandingConfig.watermarkEnabled) { $true } else { [bool]$BrandingConfig.watermarkEnabled } + # The gallery covers by id and name, without their data: what a picker elsewhere (an Infographic + # page's background in the report builder) needs to offer them. + $CoverNames = Get-CIPPImageNameMap -PartitionKey 'brandingCover' + $CoverImages = @(foreach ($Cid in $CoverImageIds) { + [pscustomobject]@{ id = [string]$Cid; name = if ($CoverNames.ContainsKey([string]$Cid)) { $CoverNames[[string]$Cid] } else { '' } } + }) + return [pscustomobject]@{ colour = if ($BrandingConfig.colour) { $BrandingConfig.colour } else { '#F77F00' } secondaryColour = $SecondaryColour @@ -277,6 +284,7 @@ function Get-CIPPBrandingSettings { logoUploads = [string[]]@($LogoUploads) coverImage = $CoverImageData coverUploads = [string[]]@($CoverUploads) + coverImages = $CoverImages footerText = $FooterText coverFooterText = $CoverFooterText showFooter = $ShowFooter @@ -285,5 +293,7 @@ function Get-CIPPBrandingSettings { watermarkEnabled = $WatermarkEnabled reportDefaults = $ReportDefaults roleColours = $RoleColours + # Which of the tenant's names a report prints; 'alias' is the name CIPP shows, the old behaviour. + tenantLabel = if (@('alias', 'name', 'domain') -contains "$($BrandingConfig.tenantLabel)") { "$($BrandingConfig.tenantLabel)" } else { 'alias' } } } diff --git a/backend/Modules/CIPPCore/Public/Get-CIPPDbItem.ps1 b/backend/Modules/CIPPCore/Public/Get-CIPPDbItem.ps1 index ba4f2d4a5d..3fd2708e64 100644 --- a/backend/Modules/CIPPCore/Public/Get-CIPPDbItem.ps1 +++ b/backend/Modules/CIPPCore/Public/Get-CIPPDbItem.ps1 @@ -30,7 +30,11 @@ function Get-CIPPDbItem { [string]$Type, [Parameter(Mandatory = $false)] - [switch]$CountsOnly + [switch]$CountsOnly, + + # With -CountsOnly: also return each collection's recorded Shape (fields and types). + [Parameter(Mandatory = $false)] + [switch]$IncludeShape ) try { @@ -64,7 +68,8 @@ function Get-CIPPDbItem { $Filter = [string]::Join(' and ', $Conditions) # -Property does the projection server-side; the trailing Select-Object was # redundant (and rebuilt every row as a NoteProperty bag, slowing later filters). - $Results = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property 'PartitionKey', 'RowKey', 'DataCount', 'Timestamp' + $Properties = @('PartitionKey', 'RowKey', 'DataCount', 'Timestamp'; if ($IncludeShape) { 'Shape' }) + $Results = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property $Properties } else { if (-not $Type) { throw 'Type parameter is required when CountsOnly is not specified' diff --git a/backend/Modules/CIPPCore/Public/Get-CIPPImageNameMap.ps1 b/backend/Modules/CIPPCore/Public/Get-CIPPImageNameMap.ps1 new file mode 100644 index 0000000000..90448b8520 --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Get-CIPPImageNameMap.ps1 @@ -0,0 +1,32 @@ +function Get-CIPPImageNameMap { + <# + .FUNCTIONALITY + Internal + .SYNOPSIS + The names given to the gallery images of one kind, as a hashtable of image id -> name. + .DESCRIPTION + A name lives on a small row of its own (PartitionKey imageMeta, RowKey the image id) rather + than on the image row: an image payload is split across several entities, and renaming it + must not mean rewriting them. Images that were never named are simply absent from the map. + .PARAMETER PartitionKey + The image kind the names belong to (logo, brandingCover). + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$PartitionKey + ) + + $Map = @{} + try { + $Table = Get-CIPPTable -TableName 'Images' + $Rows = Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq 'imageMeta' and kind eq '$($PartitionKey.Replace("'", "''"))'" + foreach ($Row in @($Rows)) { + if ($Row.RowKey -and -not [string]::IsNullOrWhiteSpace([string]$Row.name)) { $Map[[string]$Row.RowKey] = [string]$Row.name } + } + } catch { + Write-Warning "Failed to read image names for '$PartitionKey': $($_.Exception.Message)" + } + return $Map +} diff --git a/backend/Modules/CIPPCore/Public/Get-CIPPShadowAIReport.ps1 b/backend/Modules/CIPPCore/Public/Get-CIPPShadowAIReport.ps1 new file mode 100644 index 0000000000..c121c58351 --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Get-CIPPShadowAIReport.ps1 @@ -0,0 +1,247 @@ +function Get-CIPPShadowAIReport { + <# + .FUNCTIONALITY + Internal + .SYNOPSIS + The Shadow AI overview for a tenant, compiled from the CIPP reporting cache. + .DESCRIPTION + Matches the cached DetectedApps, ServicePrincipals and OAuth2PermissionGrants datasets against + the curated AI catalog (Config/ShadowAI.json) into the shape the Shadow AI page (ListShadowAI) + and the Shadow AI PDF consume: a summary, tools by category and risk, the top tools across both + sources, the AI software found on managed devices and the AI applications in Entra. No live + Graph enumeration is performed (refresh via ExecCIPPDBCache); the only live call is a bounded, + best-effort 7-day sign-in lookup for the matched AI applications. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$TenantFilter + ) + + # Curated, PR-editable catalog of known AI tools/apps. + try { + $Catalog = @(Get-Content (Join-Path $env:CIPPRootPath 'Config\ShadowAI.json') -ErrorAction Stop | ConvertFrom-Json) + } catch { + Write-LogMessage -API 'ShadowAI' -tenant $TenantFilter -message "Could not load Shadow AI catalog. Error: $($_.Exception.Message)" -Sev 'Error' + $Catalog = @() + } + + # Returns the first catalog entry whose matchNames appear (case-insensitive substring) in $Text. + function Get-AiMatch { + param($Text, $Catalog) + if ([string]::IsNullOrWhiteSpace($Text)) { return $null } + $Haystack = $Text.ToLower() + foreach ($Entry in $Catalog) { + foreach ($Match in $Entry.matchNames) { + if ($Match -and $Haystack.Contains($Match.ToLower())) { return $Entry } + } + } + return $null + } + + $SanctionedTools = @{} + try { + $SanctionTable = Get-CIPPTable -TableName 'ShadowAIConfig' + $EscapedTenant = $TenantFilter -replace "'", "''" + foreach ($Row in @(Get-CIPPAzDataTableEntity @SanctionTable -Filter "PartitionKey eq '$EscapedTenant'")) { + $ToolName = if ($Row.Tool) { $Row.Tool } else { $Row.RowKey } + if ($ToolName) { $SanctionedTools[$ToolName.ToLower()] = $true } + } + } catch { + Write-LogMessage -API 'ShadowAI' -tenant $TenantFilter -message "Could not load sanctioned AI tools: $($_.Exception.Message)" -Sev 'Warning' + } + + # --- Cached datasets from the CIPP reporting database (no live Graph enumeration) --- + $CacheData = @{} + $CacheTimestamps = [System.Collections.Generic.List[object]]::new() + foreach ($Type in @('DetectedApps', 'ServicePrincipals', 'OAuth2PermissionGrants')) { + $CacheData[$Type] = try { @(New-CIPPDbRequest -TenantFilter $TenantFilter -Type $Type) } catch { @() } + $CountRow = try { Get-CIPPDbItem -TenantFilter $TenantFilter -Type $Type -CountsOnly | Select-Object -First 1 } catch { $null } + if ($CountRow.Timestamp) { $CacheTimestamps.Add($CountRow.Timestamp) } + } + $LastDataRefresh = $CacheTimestamps | Sort-Object | Select-Object -First 1 + + # 1) Installed AI tools from the cached Intune detected apps. The inventory reports a separate + # application entry per version (and per install flavor, e.g. 'Copilot' vs 'Microsoft.Copilot'), + # so merge everything that matches the same catalog tool into ONE row: distinct devices only, + # with the observed application names, versions and platforms combined. + $DetectedAppMap = [ordered]@{} + foreach ($App in $CacheData['DetectedApps']) { + $Match = Get-AiMatch -Text "$($App.displayName) $($App.publisher)" -Catalog $Catalog + if (-not $Match) { continue } + if (-not $DetectedAppMap.Contains($Match.name)) { + $DetectedAppMap[$Match.name] = [PSCustomObject]@{ + Match = $Match + Sanctioned = $SanctionedTools.ContainsKey($Match.name.ToLower()) + Applications = [System.Collections.Generic.List[string]]::new() + Publishers = [System.Collections.Generic.List[string]]::new() + Versions = [System.Collections.Generic.List[string]]::new() + Platforms = [System.Collections.Generic.List[string]]::new() + Devices = [ordered]@{} + } + } + $Entry = $DetectedAppMap[$Match.name] + if ($App.displayName -and $Entry.Applications -notcontains [string]$App.displayName) { $Entry.Applications.Add([string]$App.displayName) } + if ($App.publisher -and $Entry.Publishers -notcontains [string]$App.publisher) { $Entry.Publishers.Add([string]$App.publisher) } + if ($App.version -and $Entry.Versions -notcontains [string]$App.version) { $Entry.Versions.Add([string]$App.version) } + $Platform = if ([string]::IsNullOrWhiteSpace($App.platform)) { 'Unknown' } else { [string]$App.platform } + if ($Entry.Platforms -notcontains $Platform) { $Entry.Platforms.Add($Platform) } + foreach ($Device in @($App.managedDevices ?? @())) { + $DeviceKey = if ($Device.id) { [string]$Device.id } else { [string]$Device.deviceName } + if ($DeviceKey -and -not $Entry.Devices.Contains($DeviceKey)) { $Entry.Devices[$DeviceKey] = $Device } + } + } + + $DetectedApps = @(foreach ($Entry in $DetectedAppMap.Values) { + $Match = $Entry.Match + [PSCustomObject]@{ + application = ($Entry.Applications | Sort-Object) -join ', ' + aiTool = $Match.name + vendor = $Match.vendor + category = $Match.category + risk = if ($Entry.Sanctioned) { 'Informational' } else { $Match.risk } + catalogRisk = $Match.risk + status = if ($Entry.Sanctioned) { 'Sanctioned' } else { 'Unsanctioned' } + toolDescription = $Match.description + riskReason = $Match.riskReason + # Inventory rows mix clean publisher names with full certificate subjects - show the shortest. + publisher = $Entry.Publishers | Sort-Object -Property Length | Select-Object -First 1 + version = ($Entry.Versions | Sort-Object) -join ', ' + platform = ($Entry.Platforms | Sort-Object) -join ', ' + deviceCount = $Entry.Devices.Count + managedDevices = @($Entry.Devices.Values) + } + }) + + # 2) AI applications in Entra: match ALL cached service principals (not only those with + # delegated grants), then attach any granted permissions. First consented = when the + # service principal was created in the tenant (the oauth2 grant startTime is unreliable). + $GrantsBySp = @{} + foreach ($Group in ($CacheData['OAuth2PermissionGrants'] | Where-Object { $_.clientId } | Group-Object clientId)) { + $GrantsBySp[$Group.Name] = $Group.Group + } + + $ConsentedApps = [System.Collections.Generic.List[object]]::new() + $SeenApps = @{} + foreach ($Sp in $CacheData['ServicePrincipals']) { + $Match = Get-AiMatch -Text $Sp.displayName -Catalog $Catalog + if (-not $Match) { continue } + $Key = [string]($Sp.appId ?? $Sp.id) + if ($SeenApps.ContainsKey($Key)) { continue } + # Individual scopes as a string array so the frontend renders them as chips. + $Permissions = if ($GrantsBySp.ContainsKey($Sp.id)) { + @((@($GrantsBySp[$Sp.id].scope) -join ' ') -split '\s+' | Where-Object { $_ } | Sort-Object -Unique) + } else { + @() + } + $IsSanctioned = $SanctionedTools.ContainsKey($Match.name.ToLower()) + $Consent = [PSCustomObject]@{ + application = $Sp.displayName + aiTool = $Match.name + vendor = $Match.vendor + category = $Match.category + risk = if ($IsSanctioned) { 'Informational' } else { $Match.risk } + catalogRisk = $Match.risk + status = if ($IsSanctioned) { 'Sanctioned' } else { 'Unsanctioned' } + toolDescription = $Match.description + riskReason = $Match.riskReason + applicationId = $Sp.appId + approvedPermissions = @($Permissions) + firstConsentedDateTime = $Sp.createdDateTime + signInsLast7Days = 0 + activeUsersLast7Days = 0 + applicationUsers = @() + } + $SeenApps[$Key] = $Consent + $ConsentedApps.Add($Consent) + } + + # 2b) Best-effort: recent sign-in usage (last 7 days) for the matched AI apps. This is the only + # live Graph call: a single bounded query, skipped gracefully when unavailable (needs P1). + $AiAppIds = @($ConsentedApps.applicationId | Where-Object { $_ } | Select-Object -Unique -First 15) + if ($AiAppIds.Count -gt 0) { + try { + $StartDate = (Get-Date).AddDays(-7).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') + $AppFilter = ($AiAppIds | ForEach-Object { "appId eq '$_'" }) -join ' or ' + $SignInFilter = "createdDateTime ge $StartDate and ($AppFilter)" + $SignIns = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/auditLogs/signIns?`$filter=$SignInFilter" -tenantid $TenantFilter + $SignInGroups = $SignIns | Group-Object appId + foreach ($Consent in $ConsentedApps) { + $Group = $SignInGroups | Where-Object { $_.Name -eq $Consent.applicationId } + if ($Group) { + $Consent.signInsLast7Days = $Group.Count + $Consent.activeUsersLast7Days = @($Group.Group.userId | Select-Object -Unique).Count + $Consent.applicationUsers = @($Group.Group | Group-Object userPrincipalName | ForEach-Object { + [PSCustomObject]@{ + userPrincipalName = $_.Name + userDisplayName = ($_.Group | Select-Object -First 1).userDisplayName + signIns = $_.Count + lastSignInDateTime = ($_.Group.createdDateTime | Sort-Object -Descending | Select-Object -First 1) + } + }) + } + } + } catch { + Write-LogMessage -API 'ShadowAI' -tenant $TenantFilter -message "Sign-in usage enrichment skipped (requires Entra ID P1). Error: $($_.Exception.Message)" -Sev 'Info' + } + } + + # --- Roll up distinct AI tools across BOTH sources for the summary and charts: device installs + # from the Intune rows, active users (last 7 days) from the Entra rows, the first row seen + # (Intune first) naming the tool's category, risk and status. --- + $Tools = @($DetectedApps + @($ConsentedApps) | Group-Object aiTool | ForEach-Object { + [PSCustomObject]@{ + Tool = $_.Name + Category = $_.Group[0].category + Risk = $_.Group[0].risk + Status = $_.Group[0].status + Devices = [int](($_.Group | ForEach-Object { [int]($_.deviceCount ?? 0) } | Measure-Object -Sum).Sum) + Users = [int](($_.Group | ForEach-Object { [int]($_.activeUsersLast7Days ?? 0) } | Measure-Object -Sum).Sum) + } + }) + + # Built as @(...) so a tenant with no AI tools gets empty lists, not a single null entry. + $ByCategory = @(foreach ($Group in ($Tools | Group-Object Category)) { + [PSCustomObject]@{ + category = $Group.Name + tools = $Group.Count + devices = [int](($Group.Group | Measure-Object -Property Devices -Sum).Sum) + } + }) + $ByRisk = @(foreach ($Group in ($Tools | Group-Object Risk)) { + [PSCustomObject]@{ + risk = $Group.Name + tools = $Group.Count + } + }) + # Top tools across BOTH sources: device installs (Intune) + active users (Entra, last 7 days). + $TopTools = @($Tools | Sort-Object -Property { $_.Devices + $_.Users } -Descending | Select-Object -First 8 | ForEach-Object { + [PSCustomObject]@{ + tool = $_.Tool + devices = $_.Devices + users = $_.Users + footprint = $_.Devices + $_.Users + category = $_.Category + status = $_.Status + } + }) + + $Body = [PSCustomObject]@{ + summary = [PSCustomObject]@{ + aiToolsDetected = $Tools.Count + deviceInstalls = [int](($DetectedApps | Measure-Object -Property deviceCount -Sum).Sum) + consentedAiApps = $ConsentedApps.Count + highRiskTools = @($Tools | Where-Object { $_.Risk -eq 'High' }).Count + sanctionedTools = @($Tools | Where-Object { $_.Status -eq 'Sanctioned' }).Count + intuneSynced = $CacheData['DetectedApps'].Count -gt 0 + entraSynced = $CacheData['ServicePrincipals'].Count -gt 0 + lastDataRefresh = $LastDataRefresh + } + byCategory = @($ByCategory) + byRisk = @($ByRisk) + topTools = @($TopTools) + detectedApps = @($DetectedApps) + consentedApps = @($ConsentedApps) + } + return $Body +} diff --git a/backend/Modules/CIPPCore/Public/Get-CIPPSharePointPermissionsReport.ps1 b/backend/Modules/CIPPCore/Public/Get-CIPPSharePointPermissionsReport.ps1 new file mode 100644 index 0000000000..b142255987 --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Get-CIPPSharePointPermissionsReport.ps1 @@ -0,0 +1,111 @@ +function Get-CIPPSharePointPermissionsReport { + <# + .FUNCTIONALITY + Internal + .SYNOPSIS + The SharePoint permissions report for a tenant, compiled from the CIPP reporting cache. + .DESCRIPTION + Rolls up the cached SharePointPermissions dataset into the shape the Permissions page + (ListSharePointPermissions) and the permissions PDF consume: the scan summary, the oversharing + signals worth acting on, chart datasets and the individual permission assignments. No live + enumeration is performed; refresh the data by syncing that cache (ExecCIPPDBCache). + + Signals reported: + - Broad claims: grants to Everyone, Everyone except external users, or All Users. A library + carrying one of these is reachable by the whole tenant regardless of who was meant to + have it, which is the classic oversharing footgun. + - External grants: permissions held by guest or external identities. + - Direct Full Control: Full Control held by something other than a SharePoint group, i.e. + granted to a user or directory group rather than through the site's Owners group. + - Unique permission libraries: libraries that no longer inherit from their site, so site + level permission changes no longer reach them. + + Limited Access assignments (isSystemManaged) are excluded from every signal - SharePoint + creates them itself so a user can traverse to an item, and they grant nothing on their own. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$TenantFilter + ) + + # A readable label for a site that has no display name, taken from the last path segment of + # its URL: '.../sites/AllCompany' becomes 'AllCompany', '.../search' becomes 'search'. + function Get-CIPPSiteLabel { + param([string]$SiteUrl) + if ([string]::IsNullOrWhiteSpace($SiteUrl)) { return 'Unnamed site' } + try { + $Path = ([System.Uri]$SiteUrl).AbsolutePath.Trim('/') + if ($Path) { return ($Path -split '/')[-1] } + return 'Root site' + } catch { + return 'Unnamed site' + } + } + # Counts per key, largest first, as { ; } rows for the charts and tables. + function Get-RankedCount($Rows, [scriptblock]$Key, [string]$KeyName, [string]$ValueName) { + @($Rows | Group-Object $Key | Where-Object { $_.Name } | Sort-Object -Property Count -Descending | + ForEach-Object { [PSCustomObject]@{ $KeyName = $_.Name; $ValueName = $_.Count } }) + } + + # --- Cached dataset from the CIPP reporting database --- + $CacheRows = try { @(New-CIPPDbRequest -TenantFilter $TenantFilter -Type 'SharePointPermissions') } catch { @() } + $CountRow = try { Get-CIPPDbItem -TenantFilter $TenantFilter -Type 'SharePointPermissions' -CountsOnly | Select-Object -First 1 } catch { $null } + + $SiteRows = @($CacheRows | Where-Object { $_.rowType -eq 'Site' }) + $Assignments = @($CacheRows | Where-Object { $_.rowType -eq 'Assignment' }) + $SkippedSites = @($SiteRows | Where-Object { $_.collectionStatus -eq 'Skipped' } | ForEach-Object { + [PSCustomObject]@{ siteName = $_.siteName; siteUrl = $_.siteUrl; error = $_.collectionError } + }) + # Placeholder rows for a unique-permission library with nothing granted carry no principal, and + # SharePoint maintains Limited Access itself; neither grants anything on its own. + $RealAssignments = @($Assignments | Where-Object { $_.principalId -and $_.isSystemManaged -ne $true }) + $Level = { [string]($_.permissionLevel ?? 'Unknown') } + + $Body = [PSCustomObject]@{ + summary = [PSCustomObject]@{ + sitesScanned = $SiteRows.Count + sitesSkipped = $SkippedSites.Count + librariesScanned = [int](($SiteRows | ForEach-Object { [int]($_.librariesScanned ?? 0) } | Measure-Object -Sum).Sum) + uniquePermissionLibraries = [int](($SiteRows | ForEach-Object { [int]($_.librariesWithUniquePermissions ?? 0) } | Measure-Object -Sum).Sum) + totalAssignments = $RealAssignments.Count + broadClaimGrants = @($RealAssignments | Where-Object { $_.broadClaim }).Count + externalGrants = @($RealAssignments | Where-Object { $_.isGuest -eq $true }).Count + # Full Control held by anything other than a SharePoint group was granted directly rather + # than through the site's Owners group, which every site has by default. + directFullControlGrants = @($RealAssignments | Where-Object { (& $Level) -eq 'Full Control' -and $_.principalType -ne 'SharePoint Group' }).Count + permissionsSynced = [bool]$CountRow + lastDataRefresh = $CountRow.Timestamp + } + byPermissionLevel = @(Get-RankedCount $RealAssignments $Level 'level' 'grants') + byPrincipalType = @(Get-RankedCount $RealAssignments { [string]($_.principalType ?? 'Other') } 'type' 'grants') + byBroadClaim = @(Get-RankedCount @($RealAssignments | Where-Object { $_.broadClaim }) { [string]$_.broadClaim } 'claim' 'grants') + # Libraries that no longer inherit, counted per site for the chart. + topSitesByUniqueLibraries = @($SiteRows | Where-Object { [int]($_.librariesWithUniquePermissions ?? 0) -gt 0 } | + Group-Object { [string]($_.siteName ?? $_.siteUrl) } | Where-Object { $_.Name } | + ForEach-Object { [PSCustomObject]@{ site = $_.Name; libraries = [int](($_.Group | ForEach-Object { [int]$_.librariesWithUniquePermissions } | Measure-Object -Sum).Sum) } } | + Sort-Object -Property libraries -Descending | Select-Object -First 10) + skippedSites = @($SkippedSites) + # Display fields are derived here rather than stored, so existing cached data gains them + # without waiting for a re-scan. + # + # appliesTo spells out what scope means for a reader scanning the table. Every Library row + # is by definition a library that stopped inheriting - libraries that still inherit are not + # collected, because their permissions are the site's repeated. + # + # siteName falls back to a label built from the URL for the handful of system sites that + # have no name. The URL itself is not used: the tables render any value starting with http + # as a link, and a column of links where names should be is worse than a plain label. + assignments = @($Assignments | ForEach-Object { + $AppliesTo = if ($_.scope -eq 'Library') { 'This library only' } else { 'Whole site' } + $_ | Add-Member -NotePropertyName 'appliesTo' -NotePropertyValue $AppliesTo -Force + + $SiteName = [string]$_.siteName + if ([string]::IsNullOrWhiteSpace($SiteName) -or $SiteName -like 'http*') { + $_ | Add-Member -NotePropertyName 'siteName' -NotePropertyValue (Get-CIPPSiteLabel -SiteUrl $_.siteUrl) -Force + } + $_ + }) + } + return $Body +} diff --git a/backend/Modules/CIPPCore/Public/Get-CIPPSharePointSharingReport.ps1 b/backend/Modules/CIPPCore/Public/Get-CIPPSharePointSharingReport.ps1 new file mode 100644 index 0000000000..af022696ac --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Get-CIPPSharePointSharingReport.ps1 @@ -0,0 +1,103 @@ +function Get-CIPPSharePointSharingReport { + <# + .FUNCTIONALITY + Internal + .SYNOPSIS + The SharePoint & OneDrive sharing report for a tenant, compiled from the CIPP reporting cache. + .DESCRIPTION + Rolls up the cached SharePointSharingLinks, SharePointSiteUsage and OneDriveUsage datasets into + the shape the Sharing page (ListSharePointSharing) and the sharing PDF consume: environment, + file and storage summaries per workload, link counts by classification, the sharing sprawl + signals (anonymous links that allow editing, anonymous links with no expiry, folder-level + external shares, password-protected links), the busiest sites, libraries and external + recipients, and the individual link rows. No live Graph enumeration is performed; refresh the + data by syncing those caches (ExecCIPPDBCache). + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$TenantFilter + ) + + # Usage report values can arrive as numbers, strings or empty strings depending on the tenant. + function ConvertTo-SafeDouble { + param($Value) + $Parsed = [double]0 + if ($null -ne $Value -and [double]::TryParse("$Value", [ref]$Parsed)) { return $Parsed } + return [double]0 + } + function Get-Total($Rows, [string]$Property) { + ($Rows | ForEach-Object { ConvertTo-SafeDouble -Value $_.$Property } | Measure-Object -Sum).Sum + } + # Counts per key, largest first, as { ; } rows for the charts and tables. + function Get-RankedCount($Rows, [scriptblock]$Key, [string]$KeyName, [string]$ValueName, [int]$First = 0) { + $Groups = @($Rows | Group-Object $Key | Where-Object { $_.Name } | Sort-Object -Property Count -Descending) + if ($First -gt 0) { $Groups = @($Groups | Select-Object -First $First) } + @($Groups | ForEach-Object { [PSCustomObject]@{ $KeyName = $_.Name; $ValueName = $_.Count } }) + } + + # --- Cached datasets, whether each has ever synced, and the oldest refresh across them --- + $CacheData = @{} + $CacheSynced = @{} + $CacheTimestamps = [System.Collections.Generic.List[object]]::new() + foreach ($Type in @('SharePointSharingLinks', 'SharePointSiteUsage', 'OneDriveUsage')) { + $CacheData[$Type] = try { @(New-CIPPDbRequest -TenantFilter $TenantFilter -Type $Type) } catch { @() } + $CountRow = try { Get-CIPPDbItem -TenantFilter $TenantFilter -Type $Type -CountsOnly | Select-Object -First 1 } catch { $null } + $CacheSynced[$Type] = [bool]$CountRow + if ($CountRow.Timestamp) { $CacheTimestamps.Add($CountRow.Timestamp) } + } + $LastDataRefresh = $CacheTimestamps | Sort-Object | Select-Object -First 1 + + # --- Environment summaries per workload. Teams-connected sites (rootWebTemplate 'Group') are + # reported separately from the remaining SharePoint sites; OneDrive is per account. --- + $TeamsSiteRows = @($CacheData['SharePointSiteUsage'] | Where-Object { $_.rootWebTemplate -eq 'Group' }) + $SharePointSiteRows = @($CacheData['SharePointSiteUsage'] | Where-Object { $_.rootWebTemplate -ne 'Group' }) + $OneDriveRows = $CacheData['OneDriveUsage'] + + # --- Sharing link rollups --- + $Links = $CacheData['SharePointSharingLinks'] + $AnonymousLinks = @($Links | Where-Object { $_.classification -eq 'Anonymous' }) + $ExternalLinks = @($Links | Where-Object { $_.classification -eq 'External' }) + # 'write' and 'owner' both mean the recipient can change the content. + $CanEdit = { param($Link) @($Link.roles) -contains 'write' -or @($Link.roles) -contains 'owner' } + $SiteOf = { param($Link) [string]($Link.siteName ?? $Link.siteUrl) } + # Who the tenant is sharing with, counted from named external recipients only: anonymous links + # have no recipient and internal ones are not sprawl. + $ExternalRecipients = @($ExternalLinks | ForEach-Object { @($_.sharedWith) } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { [string]$_ }) + $TopRecipients = @(Get-RankedCount $ExternalRecipients { $_ } 'recipient' 'links') + + $Body = [PSCustomObject]@{ + summary = [PSCustomObject]@{ + sharePointSites = $SharePointSiteRows.Count + sharePointFiles = [int64](Get-Total $SharePointSiteRows 'fileCount') + sharePointStorageUsedGB = [math]::Round((Get-Total $SharePointSiteRows 'storageUsedInBytes') / 1GB, 2) + teamsSites = $TeamsSiteRows.Count + teamsFiles = [int64](Get-Total $TeamsSiteRows 'fileCount') + teamsStorageUsedGB = [math]::Round((Get-Total $TeamsSiteRows 'storageUsedInBytes') / 1GB, 2) + oneDriveAccounts = $OneDriveRows.Count + oneDriveFiles = [int64](Get-Total $OneDriveRows 'fileCount') + oneDriveStorageUsedGB = [math]::Round((Get-Total $OneDriveRows 'storageUsedInBytes') / 1GB, 2) + totalLinks = $Links.Count + anonymousLinks = $AnonymousLinks.Count + externalLinks = $ExternalLinks.Count + internalLinks = $Links.Count - $AnonymousLinks.Count - $ExternalLinks.Count + itemsShared = @($Links | Where-Object { $_.driveId -and $_.itemId } | ForEach-Object { "$($_.driveId)|$($_.itemId)" } | Sort-Object -Unique).Count + anonymousEditLinks = @($AnonymousLinks | Where-Object { & $CanEdit $_ }).Count + neverExpiringAnonymous = @($AnonymousLinks | Where-Object { -not $_.expirationDateTime }).Count + # A share on a folder exposes everything below it, so it counts differently to a file share. + folderShares = @($Links | Where-Object { $_.itemType -eq 'Folder' -and $_.classification -in @('Anonymous', 'External') }).Count + passwordProtectedLinks = @($Links | Where-Object { $_.hasPassword -eq $true }).Count + externalRecipients = $TopRecipients.Count + linksSynced = $CacheSynced['SharePointSharingLinks'] + usageSynced = ($CacheSynced['SharePointSiteUsage'] -or $CacheSynced['OneDriveUsage']) + lastDataRefresh = $LastDataRefresh + } + byScope = @(Get-RankedCount $Links { [string]($_.classification ?? 'Internal') } 'scope' 'links') + byLinkType = @(Get-RankedCount $Links { [string]($_.linkType ?? 'link') } 'type' 'links') + topSites = @(Get-RankedCount $Links { & $SiteOf $_ } 'site' 'links' 10) + topLibraries = @(Get-RankedCount @($Links | Where-Object { $_.driveName }) { $Site = & $SiteOf $_; if ($Site) { "$Site / $($_.driveName)" } else { [string]$_.driveName } } 'library' 'links' 10) + topRecipients = @($TopRecipients | Select-Object -First 10) + links = @($Links) + } + return $Body +} diff --git a/backend/Modules/CIPPCore/Public/Invoke-CIPPDBCacheCollection.ps1 b/backend/Modules/CIPPCore/Public/Invoke-CIPPDBCacheCollection.ps1 index a81a95e7c0..7402eebb70 100644 --- a/backend/Modules/CIPPCore/Public/Invoke-CIPPDBCacheCollection.ps1 +++ b/backend/Modules/CIPPCore/Public/Invoke-CIPPDBCacheCollection.ps1 @@ -126,6 +126,7 @@ function Invoke-CIPPDBCacheCollection { ExchangeData = @( 'CASMailboxes' 'MailboxUsage' + 'MailTrafficSummary' 'OfficeActivations' 'HVEAccounts' ) diff --git a/backend/Modules/CIPPCore/Public/Remove-CIPPImage.ps1 b/backend/Modules/CIPPCore/Public/Remove-CIPPImage.ps1 index fed55f8957..0e2dbac54e 100644 --- a/backend/Modules/CIPPCore/Public/Remove-CIPPImage.ps1 +++ b/backend/Modules/CIPPCore/Public/Remove-CIPPImage.ps1 @@ -36,5 +36,7 @@ function Remove-CIPPImage { } catch { Write-Warning "Failed to remove image '$ImageId' in partition '$PartitionKey': $($_.Exception.Message)" } + # Its name row (see Set-CIPPImageName) goes with it; there may not be one. + try { Remove-CIPPAzDataTableEntity @Table -Entity @{ PartitionKey = 'imageMeta'; RowKey = $ImageId } -Force | Out-Null } catch { Write-Verbose "No name row for image '$ImageId'." } } } diff --git a/backend/Modules/CIPPCore/Public/Set-CIPPImageName.ps1 b/backend/Modules/CIPPCore/Public/Set-CIPPImageName.ps1 new file mode 100644 index 0000000000..9feef21904 --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Set-CIPPImageName.ps1 @@ -0,0 +1,44 @@ +function Set-CIPPImageName { + <# + .FUNCTIONALITY + Internal + .SYNOPSIS + Names a gallery image, so it can be picked by name wherever the image is offered. + .DESCRIPTION + Writes the name row Get-CIPPImageNameMap reads (PartitionKey imageMeta, RowKey the image id). + An empty name removes the row, which reads back as "unnamed". + .PARAMETER PartitionKey + The image kind (logo, brandingCover). + .PARAMETER Id + The image's RowKey GUID. + .PARAMETER Name + The name to give it; blank to clear. + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$PartitionKey, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$Id, + + [AllowEmptyString()] + [string]$Name = '' + ) + + $Table = Get-CIPPTable -TableName 'Images' + $Entity = @{ + PartitionKey = 'imageMeta' + RowKey = $Id.Trim() + kind = $PartitionKey + name = $Name.Trim() + } + if (-not $PSCmdlet.ShouldProcess($Id, "Name image '$($Entity.name)'")) { return } + if ([string]::IsNullOrWhiteSpace($Entity.name)) { + try { Remove-CIPPAzDataTableEntity @Table -Entity $Entity -Force | Out-Null } catch { Write-Warning "Failed to clear the name of image '$Id': $($_.Exception.Message)" } + return + } + Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force | Out-Null +} diff --git a/backend/Modules/CIPPCore/Public/Tools/ConvertTo-CippReportPdf.ps1 b/backend/Modules/CIPPCore/Public/Tools/ConvertTo-CippReportPdf.ps1 new file mode 100644 index 0000000000..2963e229d0 --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Tools/ConvertTo-CippReportPdf.ps1 @@ -0,0 +1,92 @@ +function ConvertTo-CippReportPdf { + <# + .SYNOPSIS + Render a report component tree to PDF bytes server-side. + .DESCRIPTION + Thin wrapper over the CIPPSharp component kit ([CIPP.Reporting.ReportPdf]::Render), which is + loaded with CIPPCore via RequiredAssemblies. Takes the declarative component tree (Report + Builder blocks, or a fixed report's composed component nodes), the resolved branding, and the + %variable% values, and returns the finished PDF as a byte array. All layout lives in the shared + component kit - callers never touch OfficeIMO. + .PARAMETER Blocks + The component tree: an array of block/component nodes, or a JSON string of the same. + .PARAMETER Branding + Branding settings as an object or JSON string. Omit it to render against the tenant/global + branding settings, or name a preset with -BrandingPresetId (a missing preset falls back to the + settings, so a report is always branded). + .PARAMETER Variables + %variable% values for footer/watermark/cover text, as a hashtable or JSON string. + .PARAMETER TenantName + Client name shown on the cover and available as %tenantname%. + .PARAMETER ReportName + Report title shown on the cover and in the page header. + .PARAMETER GeneratedOn + Human-readable generation date shown on the cover / available as %reportdate%. + #> + [CmdletBinding()] + [OutputType([byte[]])] + param( + [Parameter(Mandatory = $true)]$Blocks, + $Branding, + [string]$BrandingPresetId, + $Variables, + [string]$TenantName = 'Organization', + [string]$ReportName = 'Report', + [string]$GeneratedOn = ((Get-Date).ToString('MMMM d, yyyy')), + [string]$PageSize = 'A4', + [switch]$Landscape, + # The tenant whose %variables% (global + tenant custom vars + built-ins like %cippurl%) resolve + # the branding footer/watermark and cover text. Omit to skip variable replacement. + [string]$TenantFilter + ) + + if ($null -eq $Branding) { + $Branding = try { + $Preset = if ($BrandingPresetId) { Get-CIPPBrandingPreset -Id $BrandingPresetId | Select-Object -First 1 } + if ($Preset) { $Preset } else { Get-CIPPBrandingSettings } + } catch { @{} } + } + + # An Infographic page can use a cover from the branding gallery ('gallery:', as the report + # builder stores it); the renderer takes image bytes, so the image is read here. An image that is + # gone leaves the page's plain background rather than failing the report. + if ($Blocks -isnot [string]) { + foreach ($Block in @($Blocks)) { + $HeroImage = [string]$Block.heroImage + if ($Block.type -ne 'hero' -or $HeroImage -notlike 'gallery:*') { continue } + $Image = try { Get-CIPPImage -PartitionKey 'brandingCover' -Id $HeroImage.Substring(8) } catch { $null } + $Resolved = if ($Image.data) { [string]$Image.data } else { '' } + if ($Block -is [System.Collections.IDictionary]) { $Block['heroImage'] = $Resolved } + else { $Block | Add-Member -NotePropertyName 'heroImage' -NotePropertyValue $Resolved -Force } + } + } + + # Accept objects or pre-serialised JSON for each structured input. + $BlocksJson = if ($Blocks -is [string]) { $Blocks } else { ConvertTo-Json -InputObject @($Blocks) -Depth 20 -Compress } + $BrandingJson = if ($null -eq $Branding) { '{}' } elseif ($Branding -is [string]) { $Branding } else { ConvertTo-Json -InputObject $Branding -Depth 10 -Compress } + $VariablesJson = if ($null -eq $Variables) { '{}' } elseif ($Variables -is [string]) { $Variables } else { ConvertTo-Json -InputObject $Variables -Depth 5 -Compress } + + # Resolve CIPP %variables% (%cippurl%, %tenantname%, custom CippReplacemap vars, ...) through the one + # central replacement script, JSON-escaped so a value splices safely into these serialized strings. + # The branding footer/watermark and cover text carry these tokens; unknown tokens are left as written + # for the component kit to substitute (report-specific vars like %footerlabel%). No-op without a tenant. + if (-not [string]::IsNullOrWhiteSpace($TenantFilter)) { + # %tenantname% names the tenant the way the report does (the branding's tenantLabel, resolved + # by the caller into -TenantName) rather than the way the tenant cache does, so a footer and + # the cover agree. Substituted before the general replacement, which would use the cache. + if ($PSBoundParameters.ContainsKey('TenantName')) { + $EscapedName = (ConvertTo-Json -InputObject $TenantName -Compress).Trim('"') + $BrandingJson = [regex]::Replace($BrandingJson, '%tenantname%', $EscapedName, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase) + } + $BrandingJson = Get-CIPPTextReplacement -TenantFilter $TenantFilter -Text $BrandingJson -EscapeForJson + $VariablesJson = Get-CIPPTextReplacement -TenantFilter $TenantFilter -Text $VariablesJson -EscapeForJson + } + + # Unary comma: return the byte[] as a single object so PowerShell does not unroll it into a stream + # of bytes (which would reach callers as object[] and only work by implicit coercion). + return , [CIPP.Reporting.ReportPdf]::Render( + $BlocksJson, $BrandingJson, $VariablesJson, + $TenantName, $ReportName, $GeneratedOn, + $PageSize, [bool]$Landscape + ) +} diff --git a/backend/Modules/CIPPCore/Public/Tools/Get-CippReportTenantName.ps1 b/backend/Modules/CIPPCore/Public/Tools/Get-CippReportTenantName.ps1 new file mode 100644 index 0000000000..7c8441f5d8 --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Tools/Get-CippReportTenantName.ps1 @@ -0,0 +1,50 @@ +function Get-CippReportTenantName { + <# + .SYNOPSIS + How a report names its tenant, as the branding decides. + .DESCRIPTION + Every server-rendered report puts the tenant's name on its cover, in its prose and, through + %tenantname%, in its footer. The branding's tenantLabel says which name that is: + + alias - the name CIPP shows for the tenant: its alias when one is set, else its name (default) + name - the Microsoft 365 organisation name, alias or not + domain - the tenant's default domain + + A preset's choice wins over the global setting when a preset id is given, exactly as the + rest of the branding resolves. Anything that cannot be read falls back to the next best + thing, and finally to the tenant filter itself, so a report always has something to say. + .FUNCTIONALITY + Internal + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)][string]$TenantFilter, + [string]$BrandingPresetId, + # Already-resolved branding, when the caller has it; otherwise the preset or global settings are read. + $Branding + ) + + $Tenant = try { Get-Tenants -TenantFilter $TenantFilter | Select-Object -First 1 } catch { $null } + $Shown = if ($Tenant.displayName) { [string]$Tenant.displayName } else { $TenantFilter } + + if ($null -eq $Branding) { + $Branding = try { + $Preset = if ($BrandingPresetId) { Get-CIPPBrandingPreset -Id $BrandingPresetId -SkipImageData | Select-Object -First 1 } + if ($Preset) { $Preset } else { Get-CIPPBrandingSettings } + } catch { $null } + } + + switch ([string]$Branding.tenantLabel) { + 'domain' { + if ($Tenant.defaultDomainName) { [string]$Tenant.defaultDomainName } else { $TenantFilter } + } + 'name' { + # The organisation's own name, which the tenant cache overwrites with an alias when one is set. + $Organisation = try { + New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/organization?$select=displayName' -tenantid $TenantFilter -AsApp $true | Select-Object -First 1 + } catch { $null } + if ($Organisation.displayName) { [string]$Organisation.displayName } else { $Shown } + } + default { $Shown } + } +} diff --git a/backend/Modules/CIPPCore/Public/Tools/Push-ExecGenerateReportBuilderReport.ps1 b/backend/Modules/CIPPCore/Public/Tools/Push-ExecGenerateReportBuilderReport.ps1 index 6235acfa63..ae16edbf70 100644 --- a/backend/Modules/CIPPCore/Public/Tools/Push-ExecGenerateReportBuilderReport.ps1 +++ b/backend/Modules/CIPPCore/Public/Tools/Push-ExecGenerateReportBuilderReport.ps1 @@ -13,7 +13,10 @@ function Push-ExecGenerateReportBuilderReport { $Blocks, $TemplateGUID, $IncludeRawAttachments, - $Settings + $Settings, + # Render and return the PDF without persisting a generated-report row - powers the builder's + # live preview/download from the current unsaved state. + [switch]$PreviewOnly ) try { @@ -21,9 +24,8 @@ function Push-ExecGenerateReportBuilderReport { throw 'TenantFilter is required' } - # Page setup and branding for this report — page size, orientation, cover, footer, - # watermark and which branding preset to render against. Carried through untouched: the - # PDF is rendered in the browser, so this side only has to store what it was given. + # Page setup and branding for this report: page size, orientation, cover, footer, + # watermark and which branding preset to render against. $ParsedSettings = $null if ($Settings) { if ($Settings -is [string]) { @@ -190,21 +192,65 @@ function Push-ExecGenerateReportBuilderReport { $Block }) + # Data tokens (&Users&, &Devices.complianceState=compliant&, a chart or table's data source) + # resolve against the reporting database here, on the server, so a scheduled run and a + # preview read the same data. + $EnrichedBlocks = @(Resolve-CippReportDataToken -Blocks $EnrichedBlocks -TenantFilter $TenantFilter) + + # -- Render the PDF server-side via the shared CIPPSharp component kit -- + # A render failure must not lose the report: the enriched blocks are still stored so the report + # exists and can be re-rendered, and the failure is logged rather than thrown. + $PdfBytes = $null + try { + # The tenant's name for the cover, as the branding names it (alias, organisation name or + # domain). The template's chosen preset wins, else the global branding settings - the same + # resolution ConvertTo-CippReportPdf applies to the rest of the branding. + $TenantDisplayName = Get-CippReportTenantName -TenantFilter $TenantFilter -BrandingPresetId ([string]$ParsedSettings.brandingPresetId) + $PageSizePref = if ($ParsedSettings -and $ParsedSettings.size) { [string]$ParsedSettings.size } else { 'A4' } + $IsLandscape = ($ParsedSettings -and "$($ParsedSettings.orientation)" -eq 'landscape') + + $PdfBytes = ConvertTo-CippReportPdf -Blocks $EnrichedBlocks -BrandingPresetId ([string]$ParsedSettings.brandingPresetId) ` + -TenantName $TenantDisplayName -TenantFilter $TenantFilter -ReportName ($TemplateName ?? 'Report') ` + -PageSize $PageSizePref -Landscape:$IsLandscape + } catch { + $PdfError = Get-CippException -Exception $_ + Write-LogMessage -API 'ReportBuilder' -tenant $TenantFilter -message "PDF render failed, storing report without a PDF: $($PdfError.NormalizedError)" -Sev 'Warning' -LogData $PdfError + } + + # Preview: hand the freshly rendered bytes straight back without persisting a report row. + if ($PreviewOnly) { + return @{ PdfBytes = $PdfBytes } + } + $PdfBase64 = if ($PdfBytes) { [Convert]::ToBase64String($PdfBytes) } else { '' } + $PdfFileName = ("$($TemplateName ?? 'Report')_$TenantFilter" -replace '[^a-zA-Z0-9_\-]', '_') + '.pdf' + # Store the generated report $ReportGUID = (New-Guid).GUID $ReportTable = Get-CippTable -tablename 'ReportBuilderReports' $ReportEntity = @{ - PartitionKey = $TenantFilter - RowKey = [string]$ReportGUID - TemplateName = [string]($TemplateName ?? 'Scheduled Report') - TenantFilter = [string]$TenantFilter - Blocks = [string](ConvertTo-Json -InputObject @($EnrichedBlocks) -Depth 20 -Compress) - GeneratedAt = [string](Get-Date).ToString('o') - Status = 'Completed' - Settings = if ($ParsedSettings) { [string](ConvertTo-Json -InputObject $ParsedSettings -Depth 10 -Compress) } else { '' } + PartitionKey = $TenantFilter + RowKey = [string]$ReportGUID + TemplateName = [string]($TemplateName ?? 'Scheduled Report') + TenantFilter = [string]$TenantFilter + Blocks = [string](ConvertTo-Json -InputObject @($EnrichedBlocks) -Depth 20 -Compress) + GeneratedAt = [string](Get-Date).ToString('o') + Status = 'Completed' + Settings = if ($ParsedSettings) { [string](ConvertTo-Json -InputObject $ParsedSettings -Depth 10 -Compress) } else { '' } } - Add-CIPPAzDataTableEntity @ReportTable -Entity $ReportEntity -Force + + # The finished PDF goes in its own table, keyed by the report GUID, so listing reports never pulls + # the base64. Add-CIPPAzDataTableEntity auto-splits the oversized property across part rows, so a + # multi-MB report survives the Azure Table 64KB/property limit. + if ($PdfBase64) { + $PdfTable = Get-CippTable -tablename 'ReportBuilderPdfs' + Add-CIPPAzDataTableEntity @PdfTable -Force -Entity @{ + PartitionKey = $TenantFilter + RowKey = [string]$ReportGUID + FileName = $PdfFileName + Pdf = $PdfBase64 + } + } Write-LogMessage -API 'ReportBuilder' -tenant $TenantFilter -message "Generated report builder report '$TemplateName' with GUID $ReportGUID" -Sev 'Info' # Build result message with direct link @@ -218,9 +264,20 @@ function Push-ExecGenerateReportBuilderReport { $ResultMessage += ". View report: $ReportLink" } - # Build file attachments for database blocks if requested + # Attachments for the scheduled-email path (Send-CIPPScheduledTaskAlert forwards TaskAttachments). + # The rendered PDF always attaches - that is the whole point of rendering server-side; the raw + # CSV/JSON data files stay behind the IncludeRawAttachments toggle. + $TaskAttachments = [System.Collections.Generic.List[object]]::new() + + if ($PdfBase64) { + $TaskAttachments.Add(@{ + Name = $PdfFileName + ContentType = 'application/pdf' + ContentBytes = $PdfBase64 + }) + } + if ($IncludeRawAttachments -eq 'true') { - $TaskAttachments = @() foreach ($Block in $EnrichedBlocks) { if ($Block.type -ne 'database' -or -not $Block.dbType) { continue } $Format = if ($Block.format) { $Block.format } else { 'csv' } @@ -260,18 +317,18 @@ function Push-ExecGenerateReportBuilderReport { } } $Bytes = [System.Text.Encoding]::UTF8.GetBytes($FileContent) - $Base64 = [Convert]::ToBase64String($Bytes) - $TaskAttachments += @{ - Name = $FileName - ContentType = $ContentType - ContentBytes = $Base64 - } + $TaskAttachments.Add(@{ + Name = $FileName + ContentType = $ContentType + ContentBytes = [Convert]::ToBase64String($Bytes) + }) } - if ($TaskAttachments.Count -gt 0) { - return @{ - TaskAttachments = $TaskAttachments - Results = $ResultMessage - } + } + + if ($TaskAttachments.Count -gt 0) { + return @{ + TaskAttachments = @($TaskAttachments) + Results = $ResultMessage } } diff --git a/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippBaselineWhatIfReportTree.ps1 b/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippBaselineWhatIfReportTree.ps1 new file mode 100644 index 0000000000..b0c4c59f64 --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippBaselineWhatIfReportTree.ps1 @@ -0,0 +1,154 @@ +function Build-CippBaselineWhatIfReportTree { + <# + .SYNOPSIS + Compose the Baseline What-If report as a component tree (server port of CippBaselineWhatIfReport.jsx). + .DESCRIPTION + Pure composition from an already-gathered alignment payload: what applying the configured + standards would change today, what each planned stage will change, optionally what assigning + one more baseline would roll out, and the deviations that have been agreed and will be left + alone. Nothing is changed by producing it. Returns @{ Blocks; Variables }. + .PARAMETER Data + TenantName, TenantFilter, summary (alignedPercentage, verifiedPercentage), rows[] (the + resolved standards rows), stageStates[] (per assigned baseline), simulatedTemplate (a baseline + not assigned to the tenant, or $null) and catalog[] (the definition catalog: name, label, + executiveText). + #> + [CmdletBinding()] + param([Parameter(Mandatory)][hashtable]$Data) + + $summary = if ($Data.summary) { $Data.summary } else { @{} } + # `?? @()` so a missing list is empty rather than @($null), which would render as one blank row. + $rows = @($Data.rows ?? @()) + $stageStates = @($Data.stageStates ?? @()) + $simulated = $Data.simulatedTemplate + $catalog = @{} + foreach ($entry in @($Data.catalog ?? @())) { if ($entry.name) { $catalog[[string]$entry.name] = $entry } } + function nz($v) { if ($null -eq $v) { 0 } else { [int]$v } } + function plural($c, $s, $p) { "$c $(if ($c -eq 1) { $s } else { if ($p) { $p } else { "${s}s" } })" } + function html($s) { [System.Net.WebUtility]::HtmlEncode([string]$s) } + + # A standard's plain-language line for an executive reader, falling back to its label. + $textFor = { + param($name, $fallback) + $entry = $catalog[[string]$name] + if ($entry -and $entry.executiveText) { [string]$entry.executiveText } else { [string]$fallback } + } + $labelFor = { + param($name) + $entry = $catalog[[string]$name] + if ($entry -and $entry.label) { [string]$entry.label } else { [string]$name } + } + + # A stage's graduation conditions in words (client describeStageConditions). + $operatorLabels = @{ eq = 'equals'; ne = 'does not equal'; startsWith = 'starts with'; notStartsWith = 'does not start with' } + $describeConditions = { + param($stage) + $conditions = @($stage.conditions ?? @()) + if ($conditions.Count -eq 0) { return 'no conditions configured' } + $parts = foreach ($c in $conditions) { + switch ([string]$c.type) { + 'time' { "$($c.days) $($c.unit ?? 'days') in the previous stage" } + 'variable' { "$($c.variable) $($operatorLabels[[string]$c.operator] ?? $c.operator) '$($c.value)'" } + 'success' { 'all previous stage items applied successfully' } + 'manual' { 'manual approval by an operator' } + default { [string]$c.type } + } + } + $parts -join $(if ($stage.logic -eq 'or') { ' OR ' } else { ' AND ' }) + } + # When a time condition will be met, from the day the tenant entered its current stage. + $estimatedAt = { + param($state) + $time = @($state.nextStage.conditions ?? @()) | Where-Object { $_.type -eq 'time' } | Select-Object -First 1 + if (-not $time) { return $null } + $entered = if ($state.enteredStageAt -is [DateTimeOffset]) { $state.enteredStageAt.UtcDateTime } else { $state.enteredStageAt -as [datetime] } + if (-not $entered) { return $null } + $days = ([double]($time.days ?? 0)) * $(if ($time.unit -eq 'weeks') { 7 } else { 1 }) + $entered.AddDays($days).ToString('MMMM d, yyyy') + } + + $changesNow = @($rows | Where-Object { @('Drift', 'Partially Accepted', 'Denied - Remediate Pending', 'Denied - Delete Pending') -contains $_.status }) + $accepted = @($rows | Where-Object { $_.status -eq 'Accepted' }) + $planned = @($stageStates | Where-Object { $_.nextStage }) + + $blocks = [System.Collections.Generic.List[object]]::new() + + # -- Where you stand today -- + $blocks.Add((New-CippReportPage -Title 'Baseline What-If Report' -Subtitle 'What applying the configured standards would change')) + $blocks.Add((New-CippReportParagraph -Html ('

This report previews what applying the standards configured for {0} would change: what would change today, what each planned stage will change when it is reached, and the deviations that have been agreed and will be left alone. No changes have been made by producing it.

' -f (html $Data.TenantName)))) + $blocks.Add((New-CippReportHeading -Title 'Where you stand today')) + $blocks.Add((New-CippReportStatRow -Stats @( + @{ value = "$(nz $summary.alignedPercentage)%"; label = 'Compliant incl. accepted deviations' } + @{ value = "$(nz $summary.verifiedPercentage)%"; label = 'Compliant with baseline' } + @{ value = $changesNow.Count; label = 'Changes to make' } + @{ value = $accepted.Count; label = 'Agreed exceptions' } + ))) + + # -- Changes we would make now -- + $blocks.Add((New-CippReportHeading -Title "Changes we would make now ($($changesNow.Count))")) + if ($changesNow.Count -eq 0) { + $blocks.Add((New-CippReportClearBox -Title 'Nothing to change' -Content 'Every enforced standard is already in its expected state.')) + } else { + $blocks.Add((New-CippReportBullets -Items @($changesNow | ForEach-Object { + $meta = @( + if ($_.impact) { [string]$_.impact } + if ((nz $_.secureScoreImpact) -gt 0) { "increases Secure Score by up to $(nz $_.secureScoreImpact) points" } + if ([string]$_.status -like 'Denied*') { 'deviation denied, fix pending' } + ) -join ' - ' + @{ label = [string]$_.standardLabel; text = "$(& $textFor $_.standardName $_.standardLabel)$(if ($meta) { " ($meta)" })" } + }))) + } + + # -- Planned future changes -- + $blocks.Add((New-CippReportHeading -Title 'Planned future changes (staged rollout)')) + if ($planned.Count -eq 0) { + $blocks.Add((New-CippReportClearBox -Title 'No further staged changes' -Content 'This tenant is in the final stage of every assigned baseline.')) + } + foreach ($state in $planned) { + $when = & $estimatedAt $state + $blocks.Add((New-CippReportInfoBox -Title ([string]$state.templateName) -Content ('Currently in Stage {0} of {1} ({2}). Next: Stage {3} ({4}) - advances when {5}{6}.' -f (nz $state.currentStage), (nz $state.totalStages), $state.stageName, ((nz $state.currentStage) + 1), $state.nextStageName, (& $describeConditions $state.nextStage), $(if ($when) { ", estimated around $when" })))) + $standards = @($state.nextStage.standards ?? @()) | ForEach-Object { ([string]$_).Split('#')[0] } | Select-Object -Unique + $items = @($standards | Where-Object { $catalog.ContainsKey([string]$_) } | ForEach-Object { @{ label = (& $labelFor $_); text = (& $textFor $_ $_) } }) + if ($items.Count -gt 0) { $blocks.Add((New-CippReportBullets -Items $items)) } + } + + # -- What-if: one more baseline -- + if ($simulated) { + $blocks.Add((New-CippReportPage -Title "What-if: assigning the $($simulated.templateName) baseline" -Subtitle 'What one more baseline would roll out, stage by stage')) + $blocks.Add((New-CippReportParagraph -Text ("$(if ($simulated.description) { "$($simulated.description). " })This baseline is not assigned to the tenant today - below is what assigning it would roll out, stage by stage."))) + $stageIndex = 0 + foreach ($stage in @($simulated.stages ?? @())) { + $stageIndex++ + $timing = if ($stageIndex -eq 1) { 'applies immediately' } else { "advances when $(& $describeConditions $stage)" } + $blocks.Add((New-CippReportHeading -Title "Stage ${stageIndex}: $($stage.name) - $timing")) + $standards = @($stage.standards ?? @()) | ForEach-Object { ([string]$_).Split('#')[0] } | Select-Object -Unique + $items = @($standards | Where-Object { $catalog.ContainsKey([string]$_) } | ForEach-Object { + $name = [string]$_ + $current = $rows | Where-Object { $_.standardName -eq $name } | Select-Object -First 1 + $effect = if ($current -and $current.status -eq 'Compliant') { "No change - already aligned today (configured by $($current.sourceTemplate))." } else { 'Would change this tenant when the stage applies.' } + @{ label = (& $labelFor $name); text = "$(& $textFor $name $name) $effect" } + }) + if ($items.Count -gt 0) { $blocks.Add((New-CippReportBullets -Items $items)) } + else { $blocks.Add((New-CippReportNote -Text 'No catalogued standards in this stage.')) } + } + } + + # -- Agreed exceptions -- + if ($accepted.Count -gt 0) { + $blocks.Add((New-CippReportHeading -Title "Agreed exceptions we will not change ($($accepted.Count))")) + $blocks.Add((New-CippReportBullets -Items @($accepted | ForEach-Object { @{ label = [string]$_.standardLabel; text = $(if ($_.deviationReason) { [string]$_.deviationReason } else { 'Accepted deviation.' }) } }))) + } + + @{ + Blocks = @($blocks) + Variables = @{ + coverlabel = 'Baseline What-If' + coversubtitle = "What applying the configured standards would change at $($Data.TenantName), today and at each planned stage. No changes have been made." + covermeta = ('{0} to make / {1} / {2}' -f (plural $changesNow.Count 'change'), (plural $planned.Count 'staged rollout'), (plural $accepted.Count 'agreed exception')) + covermetanote = "Compliant with baseline: $(nz $summary.verifiedPercentage)%" + coverfooternote = 'What-if preview - no changes were made' + coverfallbackimage = '/reportImages/working.jpg' + footerlabel = "$($Data.TenantName) - Baseline What-If" + } + } +} diff --git a/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippBecReportTree.ps1 b/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippBecReportTree.ps1 new file mode 100644 index 0000000000..b0d511294b --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippBecReportTree.ps1 @@ -0,0 +1,1063 @@ +function Build-CippBecReportTree { + <# + .SYNOPSIS + Compose the BEC (Business Email Compromise) analysis report as a component tree - the server + port of BECRemediationReportButton's BECRemediationReportDocument. + .DESCRIPTION + Returns @{ Blocks; Variables }: the content blocks plus the cover/footer report variables. The + cover names the compromised user rather than the tenant. Detail callouts use -Lines so each + label/value line is a tight line break. + + The report leads with an executive intelligence section (results roll-up, findings by attacker + objective, evidence-driven priority actions, an order-of-events timeline and any containment + already run), then educational context, the per-check detail (Checks 1-21), and recommendations + and compliance. Every derived value mirrors the client renderer and the case workspace helpers + (bec-objectives / bec-timeline) so the PDF and the on-screen case agree. The threat level is read + from the server-computed Score on the run; the analysis window is AnalysisWindowDays. + .PARAMETER UserData + The investigated user: displayName, userPrincipalName. + .PARAMETER BecData + The completed BEC results payload (from BecResults), with a .Run block attached carrying the + run's CaseId and containment history (as the client receives it from execBECCheck). + .PARAMETER TenantName + The tenant the user belongs to. + #> + [CmdletBinding()] + param([Parameter(Mandatory)]$UserData, [Parameter(Mandatory)]$BecData, [string]$TenantName, [ValidateSet('full', 'summary')][string]$Variant = 'full') + + # 'summary' = the executive pages only (cover + Executive Summary), for a C-suite reader; 'full' = + # every page. Mirrors the client BECRemediationReportDocument variant. + $isSummary = $Variant -eq 'summary' + $bec = $BecData + $loc = $bec.LocationAnalysis + $ana = $bec.SentMessageAnalysis + $windowDays = if ($bec.AnalysisWindowDays) { [int]$bec.AnalysisWindowDays } else { 7 } + + # -- formatting/utility helpers -- + function Cnt($x) { if ($null -eq $x) { return 0 }; @($x).Count } + function AsInt($v) { if ($null -eq $v) { return 0 }; try { [int]$v } catch { 0 } } + function ToDate($v) { if (-not $v) { return $null }; try { [datetime]$v } catch { $null } } + function FmtDate($d) { + if (-not $d) { return 'N/A' } + try { return ([datetime]$d).ToString('MMM d, yyyy, hh:mm tt', [Globalization.CultureInfo]::InvariantCulture) } catch { return "$d" } + } + function FmtSafelist($v) { + if (-not $v) { return 'unchanged' } + if ($v -is [array]) { $j = ($v -join ', '); if ($j) { return $j } else { return 'unchanged' } } + return "$v" + } + function CleanStr($v) { $t = "$v".Trim(); if ($t.Length -gt 0) { $t } else { $null } } + function JoinDetail { param([object[]]$Parts) (@($Parts | ForEach-Object { CleanStr $_ } | Where-Object { $_ })) -join ' - ' } + + # analysis window: windowDays before extraction (mirrors becWindowStart) + $extractedAt = ToDate $bec.ExtractedAt + if (-not $extractedAt) { $extractedAt = Get-Date } + $windowStart = $extractedAt.AddDays(-$windowDays) + + # -- statistics (mirrors the client stats object) -- + $stats = @{ + newRules = Cnt $bec.NewRules + ruleChanges = Cnt $bec.InboxRuleChanges + newUsers = Cnt $bec.NewUsers + newApps = Cnt $bec.AddedApps + permissionChanges = Cnt $bec.MailboxPermissionChanges + permissionChangesTargetingUser = Cnt @($bec.MailboxPermissionChanges | Where-Object { $_.TargetsSuspect -eq $true }) + mfaDevices = Cnt $bec.MFADevices + passwordChanges = Cnt $bec.ChangedPasswords + sentMessages = Cnt $bec.SentMessages + trustedSenders = Cnt $bec.TrustedSenders + blockedSenders = Cnt $bec.BlockedSenders + safelistChanges = Cnt $bec.SafelistChanges + sharingChanges = Cnt $bec.SharingChanges + anonymousLinks = Cnt @($bec.SharingChanges | Where-Object { "$($_.Operation)".StartsWith('AnonymousLink') }) + intuneDevices = Cnt $bec.IntuneDevices + signIns = Cnt $bec.SuspectUserSignIns + sentTotalMessages = AsInt $ana.TotalMessages + sentTotalRecipients = AsInt $ana.TotalRecipients + repeatedSubjects = AsInt $ana.FlaggedSubjectCount + sendBursts = Cnt $ana.Bursts + massMailFlagged = ($ana.Flagged -eq $true) + maliciousApps = (Cnt @($bec.AddedApps | Where-Object { $_.MaliciousMatch })) + (Cnt $bec.MaliciousSPs) + foreignSignIns = AsInt $loc.ForeignSignInCount + foreignSuccessfulSignIns = AsInt $loc.ForeignSuccessfulSignInCount + foreignSentMessages = AsInt $loc.ForeignSentMessageCount + } + $stats.foreignActivity = (AsInt $loc.ForeignRuleChangeCount) + (AsInt $loc.ForeignSafelistChangeCount) + + (AsInt $loc.ForeignSharingChangeCount) + (AsInt $loc.ForeignSentMessageCount) + $stats.recentIntuneDevices = Cnt @($bec.IntuneDevices | Where-Object { $d = ToDate $_.enrolledDateTime; $d -and $d -ge $windowStart }) + $stats.recentMfaDevices = Cnt @($bec.MFADevices | Where-Object { $d = ToDate $_.createdDateTime; $d -and $d -ge $windowStart }) + + # successful foreign sign-ins first + $foreignSignInList = @($bec.SuspectUserSignIns | Where-Object { $_.ForeignLocation -eq $true } | + Sort-Object -Property @{ Expression = { $_.Status -eq 'Success' }; Descending = $true }) + $sortedIntune = @($bec.IntuneDevices | Sort-Object -Property @{ Expression = { $d = ToDate $_.enrolledDateTime; if ($d) { $d } else { [datetime]0 } }; Descending = $true }) + + # -- threat level (server-computed, read from the run's Score) -- + $threatLevel = if ($bec.Score.Level) { "$($bec.Score.Level)" } else { 'Low' } + $threatValue = AsInt $bec.Score.Value + $threatColour = @{ High = '#742A2A'; Medium = '#744210'; Low = '#22543D' }[$threatLevel] + if (-not $threatColour) { $threatColour = '#22543D' } + $appliedSignals = @($bec.Score.Breakdown | Where-Object { $_.Applied }) + + # -- completeness (skipped / partial checks) -- + $completeness = $bec.Completeness + $completenessEntries = if ($completeness) { @($completeness.PSObject.Properties) } else { @() } + $skippedCollectors = @($completenessEntries | Where-Object { $_.Value -and $_.Value.Skipped }) + $incompleteCollectors = @($completenessEntries | Where-Object { $_.Value -and $_.Value.Complete -eq $false -and -not $_.Value.Skipped }) + + # -- flagged subsets used by the executive section and the deep checks -- + $flaggedDelegations = @($bec.Delegations | Where-Object { $_.Flagged }) + $flaggedGrants = @($bec.UserGrants | Where-Object { $_.Flagged }) + $flaggedTransportChanges = @($bec.TransportRuleChanges | Where-Object { $_.Flagged }) + $flaggedTransportRules = @($bec.TransportRulesFlagged) + $flaggedAddIns = @($bec.MailboxAddIns | Where-Object { $_.Flagged }) + $receivedFindings = @($bec.ReceivedMailFindings) + $deliveredThreats = @($bec.DefenderDetections | Where-Object { $_.Delivered }) + $flaggedAudits = @($bec.DirectoryAudits | Where-Object { $_.Flagged }) + $recentRegisteredDevices = @($bec.RegisteredDevices | Where-Object { $_.RegisteredInWindow }) + $foreignNonInteractive = @($bec.NonInteractiveSignIns | Where-Object { $_.ForeignLocation -eq $true -and $_.Status -eq 'Success' }) + $mailActivitySummary = $bec.MailActivitySummary + $riskState = $bec.RiskState + + $forwardingAddress = if ($bec.MailboxState.ForwardingSmtpAddress) { "$($bec.MailboxState.ForwardingSmtpAddress)" } elseif ($bec.MailboxState.ForwardingAddress) { "$($bec.MailboxState.ForwardingAddress)" } else { $null } + $hasForwarding = [bool]($bec.MailboxState.HasForwarding -or $forwardingAddress) + + $upn = $UserData.userPrincipalName + $usageLoc = $loc.UsageLocation + + # ============================================================================================ + # Findings by attacker objective (the same five-objective lens and flag predicates the case + # workspace uses - becFindingFlags / becGroupFlagged). Only the per-finding counts feed the bars. + # ============================================================================================ + $flagCounts = @{ + SuspectUserSignIns = AsInt $loc.ForeignSuccessfulSignInCount + NonInteractiveSignIns = Cnt @($bec.NonInteractiveSignIns | Where-Object { $_.ForeignLocation -eq $true -and $_.Status -eq 'Success' }) + MFADevices = $stats.recentMfaDevices + RiskState = $(if ($riskState.Listed) { 1 } else { 0 }) + RegisteredDevices = $recentRegisteredDevices.Count + IntuneDevices = $stats.recentIntuneDevices + NewRules = Cnt @($bec.NewRules | Where-Object { $_.Suspicious -or (@($_.RiskReasons).Count -gt 0) }) + Delegations = $flaggedDelegations.Count + UserGrants = $flaggedGrants.Count + MailboxAddIns = $flaggedAddIns.Count + AddedApps = (Cnt @($bec.AddedApps | Where-Object { $_.MaliciousMatch })) + (Cnt $bec.MaliciousSPs) + MailboxState = @(if ($bec.MailboxState.HasForwarding) { 1 }; if ($bec.MailboxState.AutoReplyState -and $bec.MailboxState.AutoReplyState -ne 'Disabled') { 1 }).Count + TrustedSenders = $stats.safelistChanges + TransportRuleChanges = $flaggedTransportChanges.Count + MailboxPermissionChanges = $stats.permissionChangesTargetingUser + SentMessages = $(if ($stats.massMailFlagged) { 1 } else { 0 }) + SharingChanges = $stats.anonymousLinks + MailActivity = $(if ($mailActivitySummary.HardDeleteExceeded) { 1 } else { 0 }) + ReceivedMailFindings = $receivedFindings.Count + $deliveredThreats.Count + NewUsers = $stats.newUsers + DirectoryAudits = $flaggedAudits.Count + } + $groupKeys = [ordered]@{ + access = @('SuspectUserSignIns', 'NonInteractiveSignIns', 'MFADevices', 'RiskState', 'RegisteredDevices', 'IntuneDevices') + persistence = @('NewRules', 'Delegations', 'UserGrants', 'MailboxAddIns', 'AddedApps') + mailflow = @('MailboxState', 'TrustedSenders', 'TransportRuleChanges', 'MailboxPermissionChanges') + exfil = @('SentMessages', 'SharingChanges', 'MailActivity', 'ReceivedMailFindings') + blast = @('PartnerActions', 'NewUsers', 'ChangedPasswords', 'DirectoryAudits') + } + $objectiveMeta = @{ + access = @{ label = 'Access'; colour = '#3182CE' } + persistence = @{ label = 'Persistence'; colour = '#805AD5' } + mailflow = @{ label = 'Mail flow'; colour = '#DD6B20' } + exfil = @{ label = 'Exfiltration'; colour = '#E53E3E' } + blast = @{ label = 'Blast radius'; colour = '#718096' } + } + $objectiveData = @(foreach ($id in $groupKeys.Keys) { + $sum = (@($groupKeys[$id] | ForEach-Object { AsInt $flagCounts[$_] }) | Measure-Object -Sum).Sum + @{ label = $objectiveMeta[$id].label; value = [int]$sum; colour = $objectiveMeta[$id].colour } + }) + $totalFindings = (@($objectiveData | ForEach-Object { $_.value }) | Measure-Object -Sum).Sum + $objectiveMax = [Math]::Max((@($objectiveData | ForEach-Object { $_.value }) | Measure-Object -Maximum).Maximum, 1) + + # ============================================================================================ + # Results roll-up: every check as one row, flagged (with a high-risk sub-count) or clear. + # ============================================================================================ + $summarySource = @( + @{ area = 'Inbox rules & changes'; count = ($stats.newRules + $stats.ruleChanges) } + @{ area = 'Mailbox delegations'; count = $flaggedDelegations.Count } + @{ area = 'Application consents'; count = $flaggedGrants.Count; danger = $flaggedGrants.Count } + @{ area = 'New / rogue applications'; count = $stats.newApps; danger = $stats.maliciousApps } + @{ area = 'Mailbox permission changes'; count = $stats.permissionChanges } + @{ area = 'Transport rules'; count = ($flaggedTransportRules.Count + $flaggedTransportChanges.Count) } + @{ area = 'Mailbox add-ins'; count = $flaggedAddIns.Count } + @{ area = 'Forwarding & auto-reply'; count = $(if ($hasForwarding) { 1 } else { 0 }) } + @{ area = 'Trusted / blocked sender changes'; count = $stats.safelistChanges } + @{ area = 'Sent mail / mass-mail'; count = $(if ($stats.massMailFlagged) { 1 } else { 0 }) } + @{ area = 'Received phishing & threats'; count = ($receivedFindings.Count + $deliveredThreats.Count); danger = $deliveredThreats.Count } + @{ area = 'Sharing links'; count = $stats.sharingChanges; danger = $stats.anonymousLinks } + @{ area = 'MFA methods (new in window)'; count = $stats.recentMfaDevices } + @{ area = 'Registered devices (new in window)'; count = $recentRegisteredDevices.Count } + @{ area = 'Intune devices (new in window)'; count = $stats.recentIntuneDevices } + @{ area = 'Foreign successful sign-ins'; count = $stats.foreignSuccessfulSignIns; danger = $stats.foreignSuccessfulSignIns } + @{ area = 'Directory audit events'; count = $flaggedAudits.Count } + @{ area = 'Identity Protection risk'; count = $(if ($riskState.Listed) { 1 } else { 0 }) } + ) + $summaryData = @($summarySource | ForEach-Object { + $danger = AsInt $_.danger + $flagged = AsInt $_.count + $result = if ($danger -gt 0) { "$flagged flagged - $danger high-risk" } elseif ($flagged -gt 0) { "$flagged flagged" } else { 'Clear' } + $tone = if ($danger -gt 0) { 'fail' } elseif ($flagged -gt 0) { 'warn' } else { 'pass' } + @{ area = $_.area; result = $result; tone = $tone } + }) + $flaggedAreaCount = @($summaryData | Where-Object { $_.result -ne 'Clear' }).Count + + # ============================================================================================ + # Priority remediation actions, written from what was actually found (mirrors tailoredActions). + # ============================================================================================ + $isHighOrMed = ($threatLevel -eq 'High' -or $threatLevel -eq 'Medium') + $ruleNames = (@($bec.NewRules | ForEach-Object { $_.Name } | Where-Object { $_ } | Select-Object -First 3)) -join ', ' + $rogueAppNames = (@( + @($bec.AddedApps | Where-Object { $_.MaliciousMatch } | ForEach-Object { if ($_.DisplayName) { $_.DisplayName } else { $_.AppId } }) + @($bec.MaliciousSPs | ForEach-Object { if ($_.DisplayName) { $_.DisplayName } else { $_.AppId } }) + ) | Where-Object { $_ } | Select-Object -First 3) -join ', ' + $consentNames = (@($flaggedGrants | ForEach-Object { if ($_.ClientDisplayName) { $_.ClientDisplayName } else { $_.ClientAppId } } | Where-Object { $_ } | Select-Object -First 3)) -join ', ' + + $tailoredActions = @( + if ($isHighOrMed) { @{ tag = 'Critical'; text = "Reset $(if ($upn) { $upn } else { 'the user' })'s password and revoke all active sessions to cut off any current attacker access." } } + if ($threatLevel -eq 'High') { @{ tag = 'Critical'; text = 'Block sign-in for the account until the mailbox and identity are confirmed clean.' } } + if ($flaggedGrants.Count -gt 0 -or $stats.maliciousApps -gt 0) { + $names = if ($consentNames) { $consentNames } elseif ($rogueAppNames) { $rogueAppNames } else { '' } + @{ tag = 'Critical'; text = "Revoke $($flaggedGrants.Count + $stats.maliciousApps) risky application consent(s)$(if ($names) { " ($names)" }) - consent survives a password reset." } + } + if ($stats.maliciousApps -gt 0) { @{ tag = 'Critical'; text = "Disable the catalog-matched rogue application(s)$(if ($rogueAppNames) { " ($rogueAppNames)" }) tenant-wide." } } + if ($stats.newRules -gt 0 -or $stats.ruleChanges -gt 0) { @{ tag = 'High'; text = "Disable the $($stats.newRules + $stats.ruleChanges) suspicious inbox rule(s)/change(s)$(if ($ruleNames) { " ($ruleNames)" }) that hide replies or auto-forward mail." } } + if ($hasForwarding) { @{ tag = 'High'; text = "Clear mailbox forwarding$(if ($forwardingAddress) { " to $forwardingAddress" }), which silently copies mail out of the tenant." } } + if ($flaggedDelegations.Count -gt 0) { @{ tag = 'High'; text = "Remove $($flaggedDelegations.Count) flagged mailbox delegation(s) - a delegate keeps access after a reset." } } + if ($stats.anonymousLinks -gt 0 -or $stats.sharingChanges -gt 0) { @{ tag = 'High'; text = "Remove the $($stats.sharingChanges) sharing-link change(s)$(if ($stats.anonymousLinks) { ", including $($stats.anonymousLinks) 'anyone' link(s)" }) and disable OneDrive sharing - anonymous links expose data past any reset." } } + if ($stats.massMailFlagged) { @{ tag = 'High'; text = "The mailbox sent a mass-mail campaign ($($stats.sentTotalMessages) message(s) to $($stats.sentTotalRecipients) recipient(s)). Scope the wave and warn recipients before anything is purged." } } + if ($stats.foreignSuccessfulSignIns -gt 0) { @{ tag = 'High'; text = "$($stats.foreignSuccessfulSignIns) successful sign-in(s) from outside the assigned usage location confirm access - treat the account as compromised." } } + if (($flaggedTransportRules.Count + $flaggedTransportChanges.Count) -gt 0) { @{ tag = 'High'; text = "Review and disable $($flaggedTransportRules.Count + $flaggedTransportChanges.Count) tenant transport rule(s) changed in the window - these affect every mailbox." } } + if ($stats.recentMfaDevices -gt 0) { @{ tag = 'Medium'; text = "Remove $($stats.recentMfaDevices) MFA method(s) registered during the window, then re-register trusted ones." } } + if ($recentRegisteredDevices.Count -gt 0) { @{ tag = 'Medium'; text = "Disable $($recentRegisteredDevices.Count) device(s) registered during the window so they cannot satisfy device-based Conditional Access." } } + if ($stats.safelistChanges -gt 0) { @{ tag = 'Medium'; text = "Review $($stats.safelistChanges) trusted-sender / safelist change(s) that would let an attacker's future mail skip filtering." } } + if ($flaggedAddIns.Count -gt 0) { @{ tag = 'Medium'; text = "Disable $($flaggedAddIns.Count) flagged mailbox add-in(s)." } } + ) + $priorityActions = if ($tailoredActions.Count -gt 0) { $tailoredActions } else { + @(@{ tag = 'Monitor'; text = 'No specific indicators require remediation. Continue monitoring the account for 30 days and keep MFA enforced as a precaution.' }) + } + $priorityRows = @($priorityActions | ForEach-Object { + $tone = switch ($_.tag) { 'Critical' { 'fail' } 'High' { 'fail' } 'Medium' { 'warn' } default { 'pass' } } + @{ tag = $_.tag; text = $_.text; tone = $tone } + }) + + # -- impact findings (the plain-terms outcome; mirrors impactFindings) -- + $impactFindings = @( + if ($stats.foreignSuccessfulSignIns -gt 0 -or $foreignNonInteractive.Count -gt 0) { "Unauthorized access is confirmed - $($stats.foreignSuccessfulSignIns + $foreignNonInteractive.Count) successful sign-in(s) came from outside the account's assigned location." } + if ($riskState.Listed) { "Microsoft Identity Protection currently flags this account as at risk$(if ($riskState.RiskLevel) { " ($($riskState.RiskLevel) risk)" })." } + if ($hasForwarding) { "Incoming mail is being copied out of the organization$(if ($forwardingAddress) { " to $forwardingAddress" }), so the attacker keeps reading it even after a reset." } + if ($stats.newRules -gt 0 -or $stats.ruleChanges -gt 0) { "$($stats.newRules + $stats.ruleChanges) inbox rule(s) or change(s) hide, delete or redirect the user's mail." } + if ($stats.anonymousLinks -gt 0) { "$($stats.anonymousLinks) `"anyone with the link`" sharing link(s) expose files to anyone holding the URL, past any later reset." } + if ($stats.massMailFlagged) { "The mailbox sent a mass-mail wave - $($stats.sentTotalMessages) message(s) to $($stats.sentTotalRecipients) recipient(s) - so it is now being used to attack others." } + if ($flaggedGrants.Count -gt 0 -or $stats.maliciousApps -gt 0) { "$($flaggedGrants.Count + $stats.maliciousApps) risky application consent(s) or app(s) retain access to data independently of the password." } + if ($stats.recentMfaDevices -gt 0 -or $recentRegisteredDevices.Count -gt 0) { "New sign-in persistence was added - $($stats.recentMfaDevices) MFA method(s) and $($recentRegisteredDevices.Count) device(s) registered during the window." } + if ($flaggedDelegations.Count -gt 0) { "$($flaggedDelegations.Count) mailbox delegation(s) let another account read this mailbox." } + ) + + # ============================================================================================ + # Order of events - the correlated timeline (mirrors buildBecTimeline), collapsed per minute. + # ============================================================================================ + $partnerKinds = @('Partner', 'OtherPartner', 'CIPP') + $rawEvents = [System.Collections.Generic.List[object]]::new() + foreach ($s in @($bec.SuspectUserSignIns | Where-Object { $_.ForeignLocation -eq $true })) { + $rawEvents.Add(@{ + date = ToDate (@($s.CreatedDateTime, $s.createdDateTime, $s.Timestamp) | Where-Object { $_ } | Select-Object -First 1) + label = "Sign-in $(if ($s.Status -eq 'Success') { 'success' } else { "($(if ($s.Status) { $s.Status } else { 'attempt' }))" })" + ip = CleanStr (@($s.IPAddress, $s.ClientIP) | Where-Object { $_ } | Select-Object -First 1) + app = CleanStr (@($s.AppDisplayName, $s.ClientAppUsed) | Where-Object { $_ } | Select-Object -First 1) + location = CleanStr ((@($s.City, $s.Country) | Where-Object { $_ }) -join ', ') + }) + } + foreach ($a in @($bec.DirectoryAudits | Where-Object { $_.Flagged })) { + $rawEvents.Add(@{ + date = ToDate $a.ActivityDateTime + label = if ($a.Activity) { "$($a.Activity)" } else { 'Directory change' } + ip = CleanStr $a.ClientIP + actor = CleanStr (@($a.ActorResolved, $a.InitiatedBy) | Where-Object { $_ } | Select-Object -First 1) + partner = ($partnerKinds -contains "$($a.ActorKind)") + }) + } + foreach ($c in @($bec.InboxRuleChanges)) { + $rawEvents.Add(@{ + date = ToDate $c.Date + label = if ($c.Operation) { "$($c.Operation)" } else { 'Inbox rule change' } + ip = CleanStr $c.ClientIP + target = CleanStr $c.RuleName + foreign = ($c.ForeignLocation -eq $true) + partner = ($partnerKinds -contains "$($c.ActorKind)") + }) + } + foreach ($c in @($bec.MailboxPermissionChanges)) { + $rawEvents.Add(@{ + date = ToDate $c.Date + label = if ($c.Operation) { "$($c.Operation)" } else { 'Mailbox permission change' } + ip = CleanStr $c.ClientIP + targetsSuspect = [bool]$c.TargetsSuspect + partner = ($partnerKinds -contains "$($c.ActorKind)") + }) + } + foreach ($c in @($bec.SafelistChanges)) { + $rawEvents.Add(@{ + date = ToDate $c.Date + label = if ($c.Operation) { "$($c.Operation)" } else { 'Safelist change' } + ip = CleanStr $c.ClientIP + partner = ($partnerKinds -contains "$($c.ActorKind)") + }) + } + foreach ($c in @($bec.SharingChanges)) { + $rawEvents.Add(@{ + date = ToDate $c.Date + label = if ($c.Operation) { "$($c.Operation)" } else { 'Sharing change' } + ip = CleanStr $c.ClientIP + target = CleanStr $c.FileName + partner = ($partnerKinds -contains "$($c.ActorKind)") + }) + } + # Sent mail: one event each for a handful, else folded into one event per hour and source IP. + $sentMsgs = @($bec.SentMessages) + if ($sentMsgs.Count -le 25) { + foreach ($m in $sentMsgs) { + $rawEvents.Add(@{ + date = ToDate $m.Received + label = 'Sent mail' + ip = CleanStr $m.FromIP + target = CleanStr $m.Subject + recipient = CleanStr $m.RecipientAddress + }) + } + } else { + $buckets = @{} + foreach ($m in $sentMsgs) { + $d = ToDate $m.Received + if (-not $d) { continue } + $hour = $d.Date.AddHours($d.Hour) + $ip = CleanStr $m.FromIP + $key = '{0:o}|{1}' -f $hour, $ip + if (-not $buckets.ContainsKey($key)) { $buckets[$key] = @{ date = $hour; ip = $ip; rows = [System.Collections.Generic.List[object]]::new() } } + $buckets[$key].rows.Add($m) + } + foreach ($bucket in $buckets.Values) { + $rows = @($bucket.rows) + $emails = (@($rows | ForEach-Object { $_.MessageTraceId } | Where-Object { $_ } | Select-Object -Unique)).Count + if ($emails -eq 0) { $emails = $rows.Count } + $recipients = (@($rows | ForEach-Object { CleanStr $_.RecipientAddress } | Where-Object { $_ } | Select-Object -Unique)).Count + $subjectGroups = $rows | Group-Object -Property { $s = CleanStr $_.Subject; if ($s) { $s } else { '(no subject)' } } | Sort-Object -Property Count -Descending + $topSubject = ($subjectGroups | Select-Object -First 1).Name + $rawEvents.Add(@{ + date = $bucket.date + label = "$emails email$(if ($emails -eq 1) { '' } else { 's' }) sent" + ip = $bucket.ip + target = JoinDetail @("to $recipients recipient$(if ($recipients -eq 1) { '' } else { 's' })", $(if ($topSubject) { "`"$topSubject`"" })) + }) + } + } + foreach ($f in @($bec.ReceivedMailFindings)) { + $rawEvents.Add(@{ + date = ToDate $f.Received + label = "Received: $(if ($f.FindingType) { $f.FindingType } else { 'finding' })" + target = CleanStr $f.Subject + sender = CleanStr $f.SenderAddress + }) + } + foreach ($t in @($bec.DefenderDetections | Where-Object { $_.Delivered })) { + $rawEvents.Add(@{ + date = ToDate $t.ReceivedDateTime + label = 'Threat delivered' + target = CleanStr $t.Subject + sender = CleanStr $t.SenderAddress + }) + } + foreach ($u in @($bec.NewUsers)) { + $rawEvents.Add(@{ date = ToDate $u.createdDateTime; label = 'User created'; target = CleanStr $u.displayName }) + } + foreach ($m in @($bec.MFADevices | Where-Object { $d = ToDate $_.createdDateTime; $d -and $d -ge $windowStart })) { + $rawEvents.Add(@{ date = ToDate $m.createdDateTime; label = 'MFA method registered'; target = ("$($m.'@odata.type')" -replace '#microsoft\.graph\.', '') }) + } + foreach ($dev in @($bec.RegisteredDevices | Where-Object { $_.RegisteredInWindow })) { + $rawEvents.Add(@{ date = ToDate (@($dev.registrationDateTime, $dev.createdDateTime) | Where-Object { $_ } | Select-Object -First 1); label = 'Device registered'; target = CleanStr (@($dev.displayName, $dev.deviceId) | Where-Object { $_ } | Select-Object -First 1) }) + } + foreach ($dev in @($bec.IntuneDevices | Where-Object { $d = ToDate $_.enrolledDateTime; $d -and $d -ge $windowStart })) { + $rawEvents.Add(@{ date = ToDate $dev.enrolledDateTime; label = 'Intune device enrolled'; target = CleanStr (@($dev.deviceName, $dev.model) | Where-Object { $_ } | Select-Object -First 1) }) + } + foreach ($u in @($bec.ChangedPasswords)) { + $rawEvents.Add(@{ date = ToDate $u.lastPasswordChangeDateTime; label = 'Password changed'; target = CleanStr (@($u.displayName, $u.userPrincipalName) | Where-Object { $_ } | Select-Object -First 1) }) + } + + $sortedEvents = @($rawEvents | Where-Object { $_.date } | Sort-Object -Property date) + $timelineEvents = [System.Collections.Generic.List[object]]::new() + $prev = $null + foreach ($e in $sortedEvents) { + $detail = JoinDetail @( + $e.location, $e.app, $e.actor, $e.target, + $(if ($e.recipient) { "to $($e.recipient)" }), + $e.sender, + $(if ($e.foreign) { 'foreign' }), + $(if ($e.partner) { 'partner action' }), + $(if ($e.targetsSuspect) { 'targets this mailbox' }), + $e.ip + ) + $minute = $e.date.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm') + if ($prev -and $prev.minute -eq $minute -and $prev.label -eq $e.label -and $prev.detail -eq $detail) { + $prev.count++ + continue + } + $prev = @{ minute = $minute; label = $e.label; detail = $detail; date = $e.date; count = 1 } + $timelineEvents.Add($prev) + } + $timelineRows = @($timelineEvents | ForEach-Object { + @{ when = (FmtDate $_.date); event = $(if ($_.count -gt 1) { "$($_.label) (x$($_.count))" } else { $_.label }); detail = $_.detail } + }) + + # -- remediation already run (from the run's stored containment history) -- + $remediationRows = @(@($bec.Run.Containment) | Where-Object { $_ } | Sort-Object -Property { ToDate $_.At } -Descending | ForEach-Object { + $entry = $_ + foreach ($row in @($entry.Results)) { + $state = "$($row.state)" + $tone = switch ($state) { 'success' { 'pass' } 'error' { 'fail' } 'warning' { 'warn' } default { '' } } + @{ + when = (FmtDate $entry.At) + action = (("$($row.Action)" -replace '([a-z0-9])([A-Z])', '$1 $2').Trim()) + target = "$($row.Target)" + result = "$($row.resultText)" + state = $state + tone = $tone + } + } + }) + + $b = [System.Collections.Generic.List[object]]::new() + + # === PAGE 1: EXECUTIVE SUMMARY === + $b.Add((New-CippReportPage -Title 'Executive Summary' -Subtitle 'Overview of Business Email Compromise investigation findings')) + $b.Add((New-CippReportParagraph -Html ('

This report documents the findings of a Business Email Compromise (BEC) investigation performed for the user account {0} within {1}. The investigation analyzed suspicious activity indicators including mailbox rules, permission changes, new applications, authentication patterns, and sign-in locations over a {2}-day period.

' -f [System.Net.WebUtility]::HtmlEncode([string]$upn), [System.Net.WebUtility]::HtmlEncode([string]$TenantName), $windowDays))) + $b.Add((New-CippReportParagraph -Text 'Business Email Compromise is a sophisticated scam targeting organizations that regularly perform wire transfers or have established relationships with foreign suppliers. Attackers compromise legitimate email accounts through social engineering or computer intrusion techniques to conduct unauthorized fund transfers, steal sensitive information, or impersonate executives.')) + + $b.Add((New-CippReportHeading -Title 'Investigation Overview')) + $b.Add((New-CippReportStatRow -Stats @( + @{ value = $stats.newRules; label = 'Mailbox Rules' } + @{ value = $stats.permissionChanges; label = 'Permission Changes' } + @{ value = $stats.foreignSignIns; label = 'Foreign Sign-ins' } + @{ value = $stats.maliciousApps; label = 'Malicious Apps' } + ))) + $threatText = switch ($threatLevel) { + 'High' { 'HIGH RISK: Multiple indicators of compromise detected. Immediate remediation actions are strongly recommended. This account shows patterns consistent with active Business Email Compromise attacks.' } + 'Medium' { 'MEDIUM RISK: Suspicious activity patterns detected. Review findings and consider implementing recommended security measures. Some indicators suggest potential unauthorized access.' } + default { 'LOW RISK: Minimal suspicious activity detected. The findings show standard user behavior with no significant indicators of compromise. Continue monitoring as a precautionary measure.' } + } + $b.Add((New-CippReportAlertBox -Colour $threatColour -Title "Threat Assessment: $threatLevel (score $threatValue)" -Content $threatText)) + if ($appliedSignals.Count -gt 0) { + $b.Add((New-CippReportInfoBox -Lines -Title 'Signals that contributed to the score' -Content ((@($appliedSignals | ForEach-Object { "+$(AsInt $_.Weight) $($_.Description) ($(AsInt $_.Count))" })) -join "`n"))) + } + + $b.Add((New-CippReportHeading -Title 'What We Found')) + if ($impactFindings.Count -gt 0) { + $b.Add((New-CippReportParagraph -Html ('

In plain terms, this is what the investigation established about {0}:

' -f [System.Net.WebUtility]::HtmlEncode([string]$upn)))) + $b.Add((New-CippReportBullets -Items @($impactFindings | ForEach-Object { @{ text = $_ } }))) + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] No indicators of account compromise' -Content "None of the investigation's checks returned evidence that this account was accessed, altered or misused during the analysis window.")) + } + + $b.Add((New-CippReportHeading -Title 'Findings at a Glance')) + $b.Add((New-CippReportParagraph -Text ("Every check in this investigation and its result. $(if (-not $isSummary) { 'Flagged rows are expanded in the detailed findings later in this report. ' })$flaggedAreaCount of $($summaryData.Count) checks returned something to review."))) + if ($totalFindings -gt 0) { + $b.Add((New-CippReportParagraph -Html '

Findings by attacker objective - grouped by what each finding would let an attacker do:

')) + $b.Add((New-CippReportProgress -Items @($objectiveData | ForEach-Object { @{ label = $_.label; value = $_.value; max = $objectiveMax; display = "$($_.value)"; colour = $_.colour } }))) + } + $b.Add((New-CippReportTable -Columns @( + @{ header = 'Check'; key = 'area'; width = 3; bold = $true } + @{ header = 'Result'; key = 'result'; width = 2; toneField = 'tone' } + ) -Rows @($summaryData) -Limit $summaryData.Count)) + if (-not $isSummary) { + $b.Add((New-CippReportNote -Text 'Checks that could not run (missing a licence, permission, mailbox or service) are itemised under Data Source Information below - a check that did not run is not a pass.')) + } + + $b.Add((New-CippReportHeading -Title 'Priority Remediation Actions')) + $b.Add((New-CippReportParagraph -Text ("Actions specific to what this investigation found, most urgent first. Your IT or security team should carry these out without delay$(if (-not $isSummary) { '; the strategic and preventative measures follow later in this report.' } else { '.' })"))) + $b.Add((New-CippReportTable -Columns @( + @{ header = 'Priority'; key = 'tag'; width = 1; toneField = 'tone' } + @{ header = 'Action'; key = 'text'; width = 5 } + ) -Rows @($priorityRows) -Limit $priorityRows.Count)) + + $b.Add((New-CippReportHeading -Title 'Order of Events')) + if ($timelineRows.Count -gt 0) { + $b.Add((New-CippReportParagraph -Text 'Every timestamped signal - sign-ins, directory and mailbox changes, sharing, and the mail itself - in the order it happened, with who and where where known. Read it as the shape of the intrusion over time, not as isolated findings.')) + $b.Add((New-CippReportTable -Columns @( + @{ header = 'When'; key = 'when'; width = 2; bold = $true } + @{ header = 'Event'; key = 'event'; width = 2 } + @{ header = 'Detail'; key = 'detail'; width = 3 } + ) -Rows @($timelineRows) -Limit 40)) + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] No timestamped events in the window' -Content 'None of the checks returned a dated event inside the analysis window. This usually means no changes were made to the account in the period, not that data was missing.')) + } + + if ($remediationRows.Count -gt 0) { + $b.Add((New-CippReportHeading -Title 'Remediation Taken')) + $b.Add((New-CippReportParagraph -Text 'The containment actions already run for this account during the investigation, and their result for each target.')) + $b.Add((New-CippReportTable -Columns @( + @{ header = 'When'; key = 'when'; width = 2; bold = $true } + @{ header = 'Action'; key = 'action'; width = 2 } + @{ header = 'Target'; key = 'target'; width = 2 } + @{ header = 'Result'; key = 'result'; width = 3 } + @{ header = 'State'; key = 'state'; width = 1; toneField = 'tone' } + ) -Rows @($remediationRows) -Limit $remediationRows.Count)) + } + + # Data Source Information and every detail page (Understanding BEC, Checks 1-21, Recommendations, + # Compliance) are the full report only; the summary variant stops after the executive lead. + if (-not $isSummary) { + $b.Add((New-CippReportHeading -Title 'Data Source Information')) + $b.Add((New-CippReportInfoBox -Title 'Audit Log Status' -Content $(if ($bec.ExtractResult) { "$($bec.ExtractResult)" } else { 'Unknown' }))) + $b.Add((New-CippReportInfoBox -Title 'Analysis Period' -Content ("Last $windowDays days ending {0}" -f (FmtDate $bec.ExtractedAt)))) + if ($bec.Run.CaseId -or $bec.CaseId) { + $caseId = if ($bec.Run.CaseId) { $bec.Run.CaseId } else { $bec.CaseId } + $b.Add((New-CippReportInfoBox -Title 'Case' -Content "$caseId - full investigation. Metadata only: audit records, sign-ins, trace headers, permissions, rules and devices were collected; no message content was read.")) + } + if ($skippedCollectors.Count -gt 0) { + $b.Add((New-CippReportAlertBox -Lines -Title "[!] $($skippedCollectors.Count) check(s) could not run (not applicable to this tenant or user)" -Content ((@($skippedCollectors | ForEach-Object { "$($_.Name): $(if ($_.Value.Requirement) { $_.Value.Requirement } elseif ($_.Value.Error) { $_.Value.Error } else { 'not checked' })" })) -join "`n"))) + } + if ($incompleteCollectors.Count -gt 0) { + $b.Add((New-CippReportAlertBox -Lines -Title "[!] $($incompleteCollectors.Count) check(s) returned partial data" -Content ((@($incompleteCollectors | ForEach-Object { "$($_.Name): $(if ($_.Value.Error) { $_.Value.Error } else { "capped at $($_.Value.Cap)" })" })) -join "`n"))) + } + $b.Add((New-CippReportInfoBox -Title 'Assigned Usage Location' -Content $(if ($usageLoc) { "$usageLoc" } else { 'Not assigned - sign-ins and activity could not be compared against an expected country' }))) + + # === PAGE 2: UNDERSTANDING BEC === + $b.Add((New-CippReportPage -Title 'Understanding Business Email Compromise' -Subtitle 'What is BEC and why does it matter?')) + $b.Add((New-CippReportParagraph -Title 'What is Business Email Compromise?' -Text 'Business Email Compromise (BEC) is a type of cyberattack where criminals gain unauthorized access to a business email account. Once inside, attackers can:')) + $b.Add((New-CippReportBullets -Items @( + @{ label = 'Monitor communications:'; text = 'Read sensitive emails to learn about business operations, financial processes, and key relationships.' } + @{ label = 'Impersonate executives:'; text = 'Send fraudulent emails appearing to come from company leadership requesting wire transfers or sensitive data.' } + @{ label = 'Manipulate transactions:'; text = 'Intercept legitimate invoices and alter payment information to redirect funds to attacker-controlled accounts.' } + @{ label = 'Hide their tracks:'; text = 'Create email rules to automatically delete or hide messages, preventing detection.' } + ))) + $b.Add((New-CippReportParagraph -Title 'Common Attack Methods' -Text 'Attackers typically gain access to email accounts through:')) + $b.Add((New-CippReportBullets -Items @( + @{ label = 'Phishing:'; text = 'Deceptive emails that trick users into providing their login credentials on fake websites.' } + @{ label = 'Password Spraying:'; text = 'Automated attempts to log in using common passwords across many accounts.' } + @{ label = 'Credential Stuffing:'; text = 'Using usernames and passwords leaked from other breached websites.' } + @{ label = 'Malware:'; text = 'Software that captures keystrokes or steals stored passwords from compromised devices.' } + ))) + $b.Add((New-CippReportParagraph -Title 'Why This Investigation Was Performed' -Text 'This analysis was initiated because suspicious activity was detected or reported for this user account. The investigation examines multiple indicators that might suggest account compromise, including unusual mailbox rules, unexpected permission changes, new application authorizations, and abnormal sign-in patterns. Early detection is critical to minimize potential damage and prevent financial loss or data theft.')) + + # === PAGE 3: DETAILED FINDINGS - Check 1 === + $b.Add((New-CippReportPage -Title 'Detailed Findings' -Subtitle 'Investigation results and analysis')) + $b.Add((New-CippReportHeading -Title 'Check 1: Mailbox Rules')) + $b.Add((New-CippReportInfoBox -Title 'Why We Check This' -Content 'Attackers often create email rules to automatically forward, delete, or hide messages so victims never see evidence of fraudulent activity. A rule is flagged when it forwards or redirects mail (especially to an external address), deletes messages, moves them to a low-visibility folder (RSS, Archive, Deleted Items), stops processing other rules, targets financial keywords, or takes any of these actions on all incoming mail with no condition.')) + if ($stats.newRules -gt 0) { + $b.Add((New-CippReportAlertBox -Title "[!] $($stats.newRules) Mailbox Rule(s) Found" -Content 'The following mailbox rules were detected. Review each rule carefully to determine if it was created by the user or by an attacker. Rules that forward emails or move them to unusual folders are particularly suspicious.')) + foreach ($rule in @($bec.NewRules | Select-Object -First 10)) { + $lines = @( + if ($rule.MoveToFolder) { "Moves mail to: $($rule.MoveToFolder)" } + if ($rule.ForwardTo) { "Forwards to: $($rule.ForwardTo)" } + if ($rule.ForwardAsAttachmentTo) { "Forwards as attachment to: $($rule.ForwardAsAttachmentTo)" } + if ($rule.RedirectTo) { "Redirects to: $($rule.RedirectTo)" } + if ($rule.DeleteMessage) { 'Deletes messages' } + if ($rule.MarkAsRead) { 'Marks messages read' } + if ($rule.StopProcessingRules) { 'Stops processing further rules' } + if ($rule.SubjectContainsWords) { "On subject words: $(if ($rule.SubjectContainsWords -is [array]) { $rule.SubjectContainsWords -join ', ' } else { $rule.SubjectContainsWords })" } + if ($rule.RecentlyChanged) { 'Created or changed in the window' } + if ($rule.Enabled -eq $false) { 'Currently disabled' } + ) + $content = if ($lines.Count -gt 0) { $lines -join "`n" } elseif ($rule.Description) { "$($rule.Description)" } else { 'No actions recorded on this rule' } + $b.Add((New-CippReportInfoBox -Lines -Title "Rule: $(if ($rule.Name) { $rule.Name } else { 'Unnamed Rule' })" -Content $content)) + } + if ($stats.newRules -gt 10) { $b.Add((New-CippReportNote -Text "... and $($stats.newRules - 10) more rules (in the retained investigation record)")) } + } + if ($stats.ruleChanges -gt 0) { + $b.Add((New-CippReportAlertBox -Title "[!] $($stats.ruleChanges) Rule Change(s) in the Last $windowDays Days" -Content 'The audit log recorded inbox rules being created, changed or removed on this mailbox. Rules that were removed after use are a common way for attackers to cover their tracks.')) + foreach ($change in @($bec.InboxRuleChanges | Select-Object -First 10)) { + $lines = @( + "Date: $(if ($change.Date) { $change.Date } else { 'Unknown' })" + "By: $(if ($change.UserKey) { $change.UserKey } else { 'Unknown' })" + if ($change.ClientIP) { "From: $($change.ClientIP)$(if ($change.Country) { " ($($change.Country))" })" } + if ($change.ForeignLocation -eq $true) { '[!] Originated outside the assigned usage location' } + if ($change.Parameters) { "Parameters: $($change.Parameters)" } + ) + $b.Add((New-CippReportInfoBox -Lines -Title "$(if ($change.Operation) { $change.Operation } else { 'Rule Change' }): $(if ($change.RuleName) { $change.RuleName } else { 'Unnamed Rule' })" -Content ($lines -join "`n"))) + } + if ($stats.ruleChanges -gt 10) { $b.Add((New-CippReportNote -Text "... and $($stats.ruleChanges - 10) more changes (see the retained investigation record for the full list)")) } + } + if ($stats.newRules -eq 0 -and $stats.ruleChanges -eq 0) { + $b.Add((New-CippReportClearBox -Title '[Pass] No Suspicious Rules Found' -Content 'No mailbox rules were detected that match suspicious patterns. This is a positive indicator.')) + } + + # === PAGE 4: DETAILED FINDINGS (Continued) - Check 2, 3 === + $b.Add((New-CippReportPage -Title 'Detailed Findings (Continued)' -Subtitle 'Investigation results and analysis')) + $b.Add((New-CippReportHeading -Title 'Check 2: Recently Created Users')) + $b.Add((New-CippReportInfoBox -Title 'Why We Check This' -Content 'Attackers sometimes create new user accounts to maintain persistent access or to use as staging accounts for fraudulent activities. Reviewing recently created users helps identify unauthorized account creation.')) + if ($stats.newUsers -gt 0) { + $b.Add((New-CippReportAlertBox -Title "[i] $($stats.newUsers) New User(s) Found" -Content "The following users were created in the last $windowDays days. Verify that each account creation was authorized and legitimate.")) + foreach ($u in @($bec.NewUsers | Select-Object -First 8)) { + $b.Add((New-CippReportInfoBox -Lines -Title "$(if ($u.displayName) { $u.displayName } else { 'Unknown' })" -Content ("Email: $(if ($u.userPrincipalName) { $u.userPrincipalName } else { 'N/A' })`nCreated: $(FmtDate $u.createdDateTime)"))) + } + if ($stats.newUsers -gt 8) { $b.Add((New-CippReportNote -Text "... and $($stats.newUsers - 8) more users (see the retained investigation record for the full list)")) } + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] No New Users Found' -Content 'No new user accounts were created during the analysis period.')) + } + + $b.Add((New-CippReportHeading -Title 'Check 3: New Applications')) + $b.Add((New-CippReportInfoBox -Title 'Why We Check This' -Content "Attackers may authorize malicious or suspicious third-party applications to access your email and data. These applications can read emails, send messages, and access files without the user's explicit knowledge.")) + if ($stats.maliciousApps -gt 0) { + $b.Add((New-CippReportAlertBox -Title "[!] $($stats.maliciousApps) Known-Malicious Application(s) Detected" -Content 'One or more applications in this tenant match the CIPP known-malicious application catalog. Consent-based access survives a password reset, so these applications should be removed unless their presence is explained.')) + } + if ($stats.newApps -gt 0) { + $b.Add((New-CippReportAlertBox -Title "[!] $($stats.newApps) New Application(s) Found" -Content 'New applications were granted access during the analysis period. Review each application to ensure it was authorized and is from a trusted publisher.')) + foreach ($app in @($bec.AddedApps | Select-Object -First 6)) { + $lines = @( + "Publisher: $(if ($app.publisher) { $app.publisher } else { 'Unknown' })" + "App ID: $(if ($app.appId) { $app.appId } else { 'N/A' })" + "Created: $(FmtDate $app.createdDateTime)" + if ($app.MaliciousMatch) { + $cats = if ($app.MaliciousMatch.Categories) { " ($($app.MaliciousMatch.Categories -join ', '))" } else { '' } + "[!] Matches known-malicious catalog entry `"$($app.MaliciousMatch.Name)`"$cats" + } + ) + $b.Add((New-CippReportInfoBox -Lines -Title "$(if ($app.displayName) { $app.displayName } elseif ($app.appDisplayName) { $app.appDisplayName } else { 'Unknown' })" -Content ($lines -join "`n"))) + } + if ($stats.newApps -gt 6) { $b.Add((New-CippReportNote -Text "... and $($stats.newApps - 6) more apps (see the retained investigation record for the full list)")) } + } elseif ((Cnt $bec.MaliciousSPs) -eq 0) { + $b.Add((New-CippReportClearBox -Title '[Pass] No New Applications Found' -Content 'No new applications were authorized during the analysis period, and no known malicious applications are present in the tenant.')) + } + if ((Cnt $bec.MaliciousSPs) -gt 0) { + foreach ($app in @($bec.MaliciousSPs | Select-Object -First 6)) { + $lines = @( + "Catalog entry: $(if ($app.CatalogName) { $app.CatalogName } else { 'Unknown' })" + "App ID: $(if ($app.appId) { $app.appId } else { 'N/A' })" + "Categories: $(if ($app.Categories) { $app.Categories -join ', ' } else { 'N/A' })" + "Enabled: $(if ($null -ne $app.accountEnabled) { $app.accountEnabled } else { 'Unknown' })" + "First seen: $(FmtDate $app.createdDateTime)" + ) + $b.Add((New-CippReportInfoBox -Lines -Title "[!] $(if ($app.displayName) { $app.displayName } else { 'Unknown' }) (present in tenant)" -Content ($lines -join "`n"))) + } + if ((Cnt $bec.MaliciousSPs) -gt 6) { $b.Add((New-CippReportNote -Text "... and $((Cnt $bec.MaliciousSPs) - 6) more (see the retained investigation record for the full list)")) } + } + + # === PAGE 5: ADDITIONAL SECURITY CHECKS - Check 4,5,6,7 === + $b.Add((New-CippReportPage -Title 'Additional Security Checks' -Subtitle 'Permissions, outbound mail, authentication, and access patterns')) + $b.Add((New-CippReportHeading -Title 'Check 4: Mailbox Permission Changes')) + $b.Add((New-CippReportInfoBox -Title 'Why We Check This' -Content 'Unauthorized changes to mailbox permissions can allow attackers to grant themselves or accomplices access to read, send, or manage emails. This is a common technique to maintain persistent access.')) + if ($stats.permissionChanges -gt 0) { + $b.Add((New-CippReportAlertBox -Title "[!] $($stats.permissionChanges) Permission Change(s) Found" -Content 'Mailbox permission changes were detected. Verify that each change was authorized and necessary for legitimate business purposes.')) + foreach ($change in @($bec.MailboxPermissionChanges | Select-Object -First 5)) { + $lines = @( + "User: $(if ($change.UserKey) { $change.UserKey } else { 'Unknown' })" + "Target: $(if ($change.ObjectId) { $change.ObjectId } else { 'N/A' })" + "Permissions: $(if ($change.Permissions) { $change.Permissions } else { 'Unknown' })" + if ($change.TargetsSuspect -eq $true) { '[!] Targets the investigated mailbox' } + ) + $b.Add((New-CippReportInfoBox -Lines -Title "$(if ($change.Operation) { $change.Operation } else { 'Permission Change' })" -Content ($lines -join "`n"))) + } + if ($stats.permissionChanges -gt 5) { $b.Add((New-CippReportNote -Text "... and $($stats.permissionChanges - 5) more changes")) } + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] No Permission Changes Found' -Content 'No mailbox permission changes were detected during the analysis period.')) + } + + $b.Add((New-CippReportHeading -Title 'Check 5: Sent Messages')) + $b.Add((New-CippReportInfoBox -Title 'Why We Check This' -Content 'Attackers use a compromised mailbox to send fraudulent invoices, phishing, or internal impersonation mail. The message trace shows what actually left the mailbox during the analysis period, including the IP address it was sent from.')) + if ($stats.sentMessages -gt 0) { + $totMsg = if ($stats.sentTotalMessages) { $stats.sentTotalMessages } else { $stats.sentMessages } + $totRcp = if ($stats.sentTotalRecipients) { $stats.sentTotalRecipients } else { $stats.sentMessages } + $foreignTail = if ($stats.foreignSentMessages -gt 0) { ", including $($stats.foreignSentMessages) from an IP outside the user's assigned usage location." } else { '.' } + $b.Add((New-CippReportParagraph -Indent -Text "[i] $totMsg message(s) to $totRcp recipient(s) were sent by this mailbox during the analysis period$foreignTail")) + if ($stats.massMailFlagged) { + $mm = -join @( + if ($stats.repeatedSubjects -gt 0) { "$($stats.repeatedSubjects) subject(s) were sent as many separate messages or to many recipients. " } + if ($stats.sendBursts -gt 0) { "$($stats.sendBursts) short burst(s) of high-volume sending were detected. " } + 'Identical-subject mass mail and send bursts are how a compromised mailbox spreads phishing or fraudulent invoices. Review the campaigns below and warn the recipients if the content was malicious.' + ) + $b.Add((New-CippReportAlertBox -Title '[!] Mass-Mail Pattern Detected' -Content $mm)) + } + foreach ($g in @($ana.RepeatedSubjects | Select-Object -First 5)) { + $b.Add((New-CippReportInfoBox -Lines -Title "$(if ($g.Flagged) { '[!] ' })Repeated subject: $(if ($g.Subject) { $g.Subject } else { '(no subject)' })" -Content ("Messages: $($g.MessageCount)`nRecipients: $($g.RecipientCount)`nFirst sent: $(if ($g.FirstSent) { $g.FirstSent } else { 'N/A' })`nLast sent: $(if ($g.LastSent) { $g.LastSent } else { 'N/A' })"))) + } + if ((Cnt $ana.RepeatedSubjects) -gt 5) { $b.Add((New-CippReportNote -Text "... and $((Cnt $ana.RepeatedSubjects) - 5) more repeated subjects (see the retained investigation record for the full list)")) } + foreach ($burst in @($ana.Bursts | Select-Object -First 5)) { + $win = if ($burst.WindowMinutes) { $burst.WindowMinutes } else { 10 } + $content = @( + "Starting: $(if ($burst.WindowStart) { $burst.WindowStart } else { 'N/A' })" + if ($burst.TopSubject) { "Most common subject: $($burst.TopSubject)" } + ) -join "`n" + $b.Add((New-CippReportInfoBox -Lines -Title "[!] Send burst: $($burst.MessageCount) message(s) to $($burst.RecipientCount) recipient(s) in $win minutes" -Content $content)) + } + if ((Cnt $ana.Bursts) -gt 5) { $b.Add((New-CippReportNote -Text "... and $((Cnt $ana.Bursts) - 5) more bursts (see the retained investigation record for the full list)")) } + foreach ($msg in @($bec.SentMessages | Select-Object -First 10)) { + $lines = @( + "To: $(if ($msg.RecipientAddress) { $msg.RecipientAddress } else { 'N/A' })" + "Status: $(if ($msg.Status) { $msg.Status } else { 'N/A' })" + "Received: $(if ($msg.Received) { $msg.Received } else { 'N/A' })" + if ($msg.FromIP) { "From IP: $($msg.FromIP)$(if ($msg.Country) { " ($($msg.Country))" })" } + if ($msg.ForeignLocation -eq $true) { '[!] Sent from outside the assigned usage location' } + ) + $b.Add((New-CippReportInfoBox -Lines -Title "$(if ($msg.Subject) { $msg.Subject } else { '(no subject)' })" -Content ($lines -join "`n"))) + } + if ($stats.sentMessages -gt 10) { $b.Add((New-CippReportNote -Text "... and $($stats.sentMessages - 10) more messages (see the retained investigation record for the full list)")) } + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] No Sent Messages Found' -Content 'No messages were sent by this mailbox during the analysis period.')) + } + + $b.Add((New-CippReportHeading -Title 'Check 6: MFA Devices')) + $b.Add((New-CippReportInfoBox -Title 'Why We Check This' -Content 'Multi-factor authentication (MFA) devices provide an additional layer of security. Reviewing registered MFA methods helps identify if attackers have added unauthorized devices to bypass security controls.')) + if ($stats.mfaDevices -gt 0) { + $mfaTail = if ($stats.recentMfaDevices -gt 0) { ", including $($stats.recentMfaDevices) registered in the last $windowDays days. Verify the recent registrations were made by the user - attackers register their own method to keep access after a password reset." } else { '. Verify each device belongs to the user.' } + $b.Add((New-CippReportParagraph -Indent -Text "[i] $($stats.mfaDevices) MFA device(s) registered$mfaTail")) + $sortedMfa = @($bec.MFADevices | Sort-Object -Property @{ Expression = { $d = ToDate $_.createdDateTime; if ($d) { $d } else { [datetime]0 } }; Descending = $true } | Select-Object -First 5) + foreach ($device in $sortedMfa) { + $type = "$($device.'@odata.type')".Replace('#microsoft.graph.', '').Replace('AuthenticationMethod', '') + $lines = @( + "Display Name: $(if ($device.displayName) { $device.displayName } else { 'N/A' })" + "Registered: $(FmtDate $device.createdDateTime)" + $(if (($d = ToDate $device.createdDateTime) -and $d -ge $windowStart) { '[!] Registered in the last 7 days' }) + ) + $b.Add((New-CippReportInfoBox -Lines -Title "$(if ($type) { $type } else { 'Unknown' })" -Content ($lines -join "`n"))) + } + if ($stats.mfaDevices -gt 5) { $b.Add((New-CippReportNote -Text "... and $($stats.mfaDevices - 5) more methods (see the retained investigation record for the full list)")) } + } else { + $b.Add((New-CippReportInfoBox -Tone warn -Title '[!] No MFA Devices Found' -Content 'No multi-factor authentication devices are registered. MFA is highly recommended to prevent unauthorized access.')) + } + + $b.Add((New-CippReportHeading -Title 'Check 7: Recent Password Changes')) + $b.Add((New-CippReportInfoBox -Title 'Why We Check This' -Content "Attackers often change passwords to lock out legitimate users. Reviewing recent password changes in the tenant helps identify if the compromised account's password was changed or if other accounts were affected.")) + if ($stats.passwordChanges -gt 0) { + $b.Add((New-CippReportParagraph -Indent -Text "[i] $($stats.passwordChanges) password change(s) detected in the tenant during the analysis period.")) + foreach ($u in @($bec.ChangedPasswords | Select-Object -First 5)) { + $b.Add((New-CippReportInfoBox -Lines -Title "$(if ($u.displayName) { $u.displayName } else { 'Unknown' })" -Content ("Email: $(if ($u.userPrincipalName) { $u.userPrincipalName } else { 'N/A' })`nLast Password Change: $(FmtDate $u.lastPasswordChangeDateTime)"))) + } + if ($stats.passwordChanges -gt 5) { $b.Add((New-CippReportNote -Text "... and $($stats.passwordChanges - 5) more (see the retained investigation record for the full list)")) } + } else { + $b.Add((New-CippReportParagraph -Indent -Text '[i] No password changes detected during the analysis period.')) + } + + # === PAGE 6: MAILBOX LISTS, DEVICES & LOCATIONS - Check 8,9,10,11 === + $b.Add((New-CippReportPage -Title 'Mailbox Lists, Devices & Locations' -Subtitle 'Sender lists, managed devices, and sign-in origins')) + $b.Add((New-CippReportHeading -Title 'Check 8: Trusted & Blocked Senders')) + $b.Add((New-CippReportInfoBox -Title 'Why We Check This' -Content 'Attackers may add their own domain to the Trusted Senders list so their fraudulent messages bypass spam filtering, or add finance/security domains to the Blocked Senders list so warnings and alerts are hidden from the victim in the Junk Email folder.')) + if ($bec.SafelistError) { + $b.Add((New-CippReportAlertBox -Lines -Title '[!] Could Not Retrieve Sender Lists' -Content ("$($bec.SafelistError)`nAn empty list here does not mean the mailbox has no trusted or blocked senders."))) + } + if ($stats.safelistChanges -gt 0) { + $b.Add((New-CippReportAlertBox -Title "[!] $($stats.safelistChanges) Safelist Change(s) in the Last $windowDays Days" -Content 'The audit log recorded changes to the Trusted/Blocked Senders and Domains list on this mailbox. Review each change carefully.')) + foreach ($change in @($bec.SafelistChanges | Select-Object -First 10)) { + $lines = @( + "Date: $(FmtDate $change.Date)" + if ($change.ClientIP) { "From: $($change.ClientIP)$(if ($change.Country) { " ($($change.Country))" })" } + if ($change.ForeignLocation -eq $true) { '[!] Originated outside the assigned usage location' } + "Trusted: $(FmtSafelist $change.Trusted)" + "Blocked: $(FmtSafelist $change.Blocked)" + ) + $b.Add((New-CippReportInfoBox -Lines -Title "$(if ($change.Operation) { $change.Operation } else { 'Safelist Change' }) by $(if ($change.UserKey) { $change.UserKey } else { 'Unknown' })" -Content ($lines -join "`n"))) + } + if ($stats.safelistChanges -gt 10) { $b.Add((New-CippReportNote -Text "... and $($stats.safelistChanges - 10) more changes (see the retained investigation record for the full list)")) } + } + if ($stats.trustedSenders -gt 0) { + $b.Add((New-CippReportInfoBox -Title "Trusted Senders/Domains ($($stats.trustedSenders))" -Content (@($bec.TrustedSenders | Select-Object -First 15) -join ', '))) + } + if ($stats.trustedSenders -gt 15) { $b.Add((New-CippReportNote -Text "... and $($stats.trustedSenders - 15) more trusted entries (see the retained investigation record for the full list)")) } + if ($stats.blockedSenders -gt 0) { + $b.Add((New-CippReportInfoBox -Title "Blocked Senders/Domains ($($stats.blockedSenders))" -Content (@($bec.BlockedSenders | Select-Object -First 15) -join ', '))) + } + if ($stats.blockedSenders -gt 15) { $b.Add((New-CippReportNote -Text "... and $($stats.blockedSenders - 15) more blocked entries (see the retained investigation record for the full list)")) } + if (-not $bec.SafelistError -and $stats.trustedSenders -eq 0 -and $stats.blockedSenders -eq 0 -and $stats.safelistChanges -eq 0) { + $b.Add((New-CippReportClearBox -Title '[Pass] No Trusted or Blocked Senders Found' -Content 'No trusted or blocked sender/domain entries were found on this mailbox.')) + } + + $b.Add((New-CippReportHeading -Title 'Check 9: Intune Devices')) + $b.Add((New-CippReportInfoBox -Title 'Why We Check This' -Content 'Newly enrolled Intune devices can indicate an attacker standing up a VM or BYOD endpoint under the compromised identity, including paths that re-register Windows Hello for Business. Review devices enrolled during the analysis window first.')) + if ($completeness.IntuneDevices.Skipped) { + $b.Add((New-CippReportAlertBox -Lines -Title '[!] Intune Not Checked' -Content $(if ($completeness.IntuneDevices.Requirement) { "Not checked - $($completeness.IntuneDevices.Requirement). This is not a pass; the result is unknown." } else { "$($completeness.IntuneDevices.Error)" }))) + } elseif ($bec.IntuneDevicesError) { + $b.Add((New-CippReportAlertBox -Lines -Title '[!] Could Not Retrieve Intune Devices' -Content ("$(if ($completeness.IntuneDevices.Error) { $completeness.IntuneDevices.Error } else { $bec.IntuneDevicesError })`nAn empty device list here does not mean the user has no Intune devices."))) + } elseif ($stats.intuneDevices -gt 0) { + $intuneTail = if ($stats.recentIntuneDevices -gt 0) { ", including $($stats.recentIntuneDevices) enrolled in the last $windowDays days." } else { ". None were enrolled in the last $windowDays days." } + $b.Add((New-CippReportParagraph -Indent -Text "[i] $($stats.intuneDevices) Intune-managed device(s) associated with this user$intuneTail")) + foreach ($device in @($sortedIntune | Select-Object -First 5)) { + $lines = @( + "OS: $(if ($device.operatingSystem) { $device.operatingSystem } else { 'N/A' })$(if ($device.osVersion) { " $($device.osVersion)" })" + "Enrolled: $(FmtDate $device.enrolledDateTime)" + "Compliance: $(if ($device.complianceState) { $device.complianceState } else { 'N/A' })" + "Enrollment Type: $(if ($device.deviceEnrollmentType) { $device.deviceEnrollmentType } else { 'N/A' })" + if ($device.serialNumber) { "Serial: $($device.serialNumber)" } + ) + $b.Add((New-CippReportInfoBox -Lines -Title "$(if ($device.deviceName) { $device.deviceName } else { 'Unknown device' })" -Content ($lines -join "`n"))) + } + if ((Cnt $sortedIntune) -gt 5) { $b.Add((New-CippReportNote -Text "... and $((Cnt $sortedIntune) - 5) more devices (see the retained investigation record for the full list)")) } + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] No Intune Devices Found' -Content 'No Intune-managed devices were found for this user.')) + } + + $b.Add((New-CippReportHeading -Title 'Check 10: Sign-in Locations')) + $b.Add((New-CippReportInfoBox -Title 'Why We Check This' -Content ("Sign-ins from countries the user does not work from are one of the strongest compromise indicators. Each sign-in is compared against the user's assigned usage location in Entra ID$(if ($usageLoc) { " ($usageLoc)" }), and the client IPs behind rule changes, safelist changes, sharing changes, and sent mail are geo-located and compared the same way."))) + if ($bec.SuspectUserSignInsError) { + $b.Add((New-CippReportAlertBox -Lines -Title '[!] Could Not Retrieve Sign-in Logs' -Content ("$($bec.SuspectUserSignInsError)`nAn empty list here does not mean the user has not signed in."))) + } else { + if (-not $usageLoc) { + $b.Add((New-CippReportInfoBox -Tone warn -Title '[!] No Usage Location Assigned' -Content $(if ($loc.Note) { "$($loc.Note)" } else { 'The user has no usage location assigned in Entra ID, so activity cannot be compared against an expected country.' }))) + } + if ((Cnt $loc.SignInCountries) -gt 0) { + $b.Add((New-CippReportInfoBox -Lines -Title "Sign-in Countries Observed (last $($stats.signIns) sign-ins)" -Content ((@($loc.SignInCountries | ForEach-Object { "$($_.Country): $($_.Count) sign-in(s)" })) -join "`n"))) + } + if ($stats.foreignSignIns -gt 0 -or $stats.foreignActivity -gt 0) { + $b.Add((New-CippReportAlertBox -Title '[!] Activity Outside the Assigned Usage Location' -Content ("$($stats.foreignSignIns) sign-in(s) (of which $($stats.foreignSuccessfulSignIns) succeeded), $(AsInt $loc.ForeignRuleChangeCount) inbox rule change(s), $(AsInt $loc.ForeignSafelistChangeCount) safelist change(s), $(AsInt $loc.ForeignSharingChangeCount) sharing change(s), and $(AsInt $loc.ForeignSentMessageCount) sent message(s) originated outside $usageLoc. Failed foreign sign-ins are mostly password-spray noise; the successful ones prove access. Review each carefully - a single legitimate trip can explain some of this, but rule, safelist, or sharing changes from a foreign IP rarely have an innocent explanation."))) + foreach ($signIn in @($foreignSignInList | Select-Object -First 10)) { + $b.Add((New-CippReportInfoBox -Lines -Title "$(FmtDate $signIn.CreatedDateTime) - $(if ($signIn.Country) { $signIn.Country } else { 'Unknown' })" -Content ("Application: $(if ($signIn.AppDisplayName) { $signIn.AppDisplayName } else { 'N/A' })`nIP Address: $(if ($signIn.IPAddress) { $signIn.IPAddress } else { 'N/A' })`nCity: $(if ($signIn.City) { $signIn.City } else { 'N/A' })`nResult: $(if ($signIn.Status) { $signIn.Status } else { 'N/A' })"))) + } + if ((Cnt $foreignSignInList) -gt 10) { $b.Add((New-CippReportNote -Text "... and $((Cnt $foreignSignInList) - 10) more foreign sign-ins (see the retained investigation record for the full list)")) } + } elseif ($usageLoc) { + $b.Add((New-CippReportClearBox -Title '[Pass] No Foreign Activity Detected' -Content "All located sign-ins and activity match the user's assigned usage location ($usageLoc).")) + } + } + + $b.Add((New-CippReportHeading -Title 'Check 11: Sharing Links')) + $b.Add((New-CippReportInfoBox -Title 'Why We Check This' -Content 'Attackers share OneDrive and SharePoint folders to give themselves a data feed that survives a password reset, and anonymous links expose the content to anyone holding the URL. This check lists every sharing link the account created or changed during the analysis period, including the IP address it was done from.')) + if ($stats.sharingChanges -gt 0) { + $anon = if ($stats.anonymousLinks -gt 0) { "$($stats.anonymousLinks) of these involve anonymous links, which anyone with the URL can open. " } else { '' } + $b.Add((New-CippReportAlertBox -Title "[!] $($stats.sharingChanges) Sharing Change(s) in the Last $windowDays Days" -Content ("${anon}Review each link and remove any that are not explained, even if the account has since been remediated."))) + foreach ($change in @($bec.SharingChanges | Select-Object -First 10)) { + $lines = @( + "Date: $(FmtDate $change.Date)" + "Workload: $(if ($change.Workload) { $change.Workload } else { 'N/A' })" + if ($change.Target) { "Shared with: $($change.Target)" } + if ($change.ClientIP) { "From: $($change.ClientIP)$(if ($change.Country) { " ($($change.Country))" })" } + if ($change.ForeignLocation -eq $true) { '[!] Originated outside the assigned usage location' } + ) + $b.Add((New-CippReportInfoBox -Lines -Title "$(if ($change.Operation) { $change.Operation } else { 'Sharing Change' }): $(if ($change.FileName) { $change.FileName } elseif ($change.ItemUrl) { $change.ItemUrl } else { 'Unknown item' })" -Content ($lines -join "`n"))) + } + if ($stats.sharingChanges -gt 10) { $b.Add((New-CippReportNote -Text "... and $($stats.sharingChanges - 10) more changes (see the retained investigation record for the full list)")) } + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] No Sharing Changes Found' -Content 'No sharing links were created or changed by this account during the analysis period.')) + } + + # === PAGE 7: FULL INVESTIGATION FINDINGS - Check 12-21 === + $b.Add((New-CippReportPage -Title 'Full Investigation Findings' -Subtitle 'Delegations, consents, transport rules, received mail, directory audit, devices and risk state')) + + $b.Add((New-CippReportHeading -Title 'Check 12: Mailbox Delegations and State')) + $b.Add((New-CippReportInfoBox -Title 'Why We Check This' -Content 'A delegate with FullAccess or SendAs, a forwarding address, or an automatic reply lets an attacker keep reading and impersonating after the password is changed.')) + if ($bec.MailboxState.HasForwarding) { + $fwd = if ($bec.MailboxState.ForwardingSmtpAddress) { $bec.MailboxState.ForwardingSmtpAddress } else { $bec.MailboxState.ForwardingAddress } + $b.Add((New-CippReportAlertBox -Title '[!] Mail forwarding is configured' -Content ("Mail is forwarded to $fwd$(if ($bec.MailboxState.DeliverToMailboxAndForward) { ' (a copy stays in the mailbox)' })."))) + } + if ($flaggedDelegations.Count -gt 0) { + $b.Add((New-CippReportAlertBox -Title "[!] $($flaggedDelegations.Count) Flagged Delegation(s)" -Content 'External, guest or catch-all principals hold rights on this mailbox. Remove any the user cannot explain.')) + foreach ($d in @($flaggedDelegations | Select-Object -First 10)) { + $b.Add((New-CippReportInfoBox -Lines -Title "$($d.PermissionType): $($d.Trustee)" -Content ("Rights: $($d.AccessRights)`nResource: $($d.Resource)"))) + } + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] No Flagged Delegations' -Content "$(Cnt $bec.Delegations) delegation(s) exist, none to an external, guest or catch-all principal.")) + } + + $b.Add((New-CippReportHeading -Title 'Check 13: Application Consents')) + $b.Add((New-CippReportInfoBox -Title 'Why We Check This' -Content 'Applications the user consented to keep their access after a password reset. A rogue-catalog match or a high-risk scope from an unverified publisher is how mailboxes are synchronised out of the tenant.')) + if ($flaggedGrants.Count -gt 0) { + $b.Add((New-CippReportAlertBox -Title "[!] $($flaggedGrants.Count) Flagged Consent(s)" -Content 'Revoke the grants below unless the user can explain them.')) + foreach ($g in @($flaggedGrants | Select-Object -First 10)) { + $lines = @( + "Scopes: $(if ($g.Scope) { $g.Scope } else { 'N/A' })" + "Publisher: $(if ($g.Publisher) { $g.Publisher } else { 'Unknown' }) $(if ($g.PublisherVerified) { '(verified)' } else { '(not verified)' })" + $(if ($g.CatalogMatch.Name) { "Catalog: $($g.CatalogMatch.Name) ($($g.CatalogMatch.Source))" }) + ) + $b.Add((New-CippReportInfoBox -Lines -Title "$(if ($g.ClientDisplayName) { $g.ClientDisplayName } else { $g.ClientAppId }) ($($g.Risk))" -Content ($lines -join "`n"))) + } + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] No Flagged Consents' -Content "$(Cnt $bec.UserGrants) consent(s) and role assignment(s) exist, none matching the rogue-app catalogs or carrying a high-risk scope from an unverified publisher.")) + } + + $b.Add((New-CippReportHeading -Title 'Check 14: Transport Rules')) + $b.Add((New-CippReportInfoBox -Title 'Why We Check This' -Content 'A tenant-wide transport rule that BCCs, redirects, deletes or quarantines mail keeps a feed open after the mailbox itself is cleaned.')) + if ($flaggedTransportChanges.Count -gt 0 -or $flaggedTransportRules.Count -gt 0) { + $b.Add((New-CippReportAlertBox -Title "[!] $($flaggedTransportChanges.Count) risky change(s) in the window, $($flaggedTransportRules.Count) current rule(s) with diversion or suppression actions" -Content 'Review each rule; disable any that cannot be explained.')) + foreach ($c in @($flaggedTransportChanges | Select-Object -First 5)) { + $lines = @( + "Date: $(FmtDate $c.Date)" + "By: $(if ($c.Actor) { $c.Actor } else { 'Unknown' })" + if ($c.ClientIP) { "From: $($c.ClientIP)$(if ($c.Country) { " ($($c.Country))" })" } + "Risky parameters: $(if ($c.RiskyParameters -is [array]) { $c.RiskyParameters -join ', ' } else { $c.RiskyParameters })" + ) + $b.Add((New-CippReportInfoBox -Lines -Title "$($c.Operation): $($c.RuleName)" -Content ($lines -join "`n"))) + } + foreach ($r in @($flaggedTransportRules | Select-Object -First 5)) { + $reasons = if ($r.RiskReasons -is [array]) { $r.RiskReasons -join "`n" } else { "$($r.RiskReasons)" } + $b.Add((New-CippReportInfoBox -Lines -Title "Rule: $($r.Name) ($($r.State), $($r.Mode))" -Content $reasons)) + } + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] No Risky Transport Rules' -Content 'No transport rule with a diversion or suppression action was changed in the window or exists in the tenant.')) + } + + $b.Add((New-CippReportHeading -Title 'Check 15: Mailbox Add-ins')) + if ($flaggedAddIns.Count -gt 0) { + $b.Add((New-CippReportAlertBox -Lines -Title "[!] $($flaggedAddIns.Count) user-installed non-Microsoft add-in(s)" -Content ((@($flaggedAddIns | ForEach-Object { "$($_.DisplayName) ($(if ($_.ProviderName) { $_.ProviderName } else { 'unknown provider' }))" })) -join "`n"))) + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] No Flagged Add-ins' -Content 'No enabled user-installed add-in from a non-Microsoft provider was found.')) + } + + $b.Add((New-CippReportHeading -Title 'Check 16: Received Mail')) + $b.Add((New-CippReportInfoBox -Title 'Why We Check This' -Content 'The message that started the compromise usually arrived in the window. Trace metadata is checked for phishing-shaped subjects and look-alike sender domains; Defender for Office 365 verdicts are included where licensed. No message content is read.')) + if ($bec.ReceivedMailSummary) { + $b.Add((New-CippReportParagraph -Indent -Text "[i] $($bec.ReceivedMailSummary.TotalMessages) message(s) from $($bec.ReceivedMailSummary.UniqueSenders) sender(s) were received in the window.")) + } + if ($receivedFindings.Count -gt 0 -or $deliveredThreats.Count -gt 0) { + $b.Add((New-CippReportAlertBox -Title "[!] $($receivedFindings.Count) finding(s), $($deliveredThreats.Count) Defender-classified threat(s) delivered" -Content 'Look-alike sender domains are the strongest signal; subject patterns are leads for review, not verdicts.')) + foreach ($f in @($receivedFindings | Select-Object -First 8)) { + $b.Add((New-CippReportInfoBox -Lines -Title "$($f.FindingType): $($f.SenderAddress)" -Content ("Subject: $(if ($f.Subject) { $f.Subject } else { '(no subject)' })`nReason: $($f.Reason)`nReceived: $(if ($f.Received) { $f.Received } else { 'N/A' }) - $(if ($f.Status) { $f.Status } else { 'N/A' })"))) + } + foreach ($d in @($deliveredThreats | Select-Object -First 5)) { + $b.Add((New-CippReportInfoBox -Lines -Title "Defender: $(if ($d.ThreatTypes -is [array]) { $d.ThreatTypes -join ', ' } else { $d.ThreatTypes })" -Content ("From: $(if ($d.SenderAddress) { $d.SenderAddress } else { 'Unknown' })`nSubject: $(if ($d.Subject) { $d.Subject } else { '(no subject)' })`nDelivery: $(if ($d.DeliveryAction) { $d.DeliveryAction } else { 'N/A' }) / $(if ($d.LatestDeliveryLocation) { $d.LatestDeliveryLocation } else { 'N/A' })"))) + } + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] No Received-mail Findings' -Content 'No phishing-shaped subjects, look-alike sender domains or delivered Defender detections were found.')) + } + + $b.Add((New-CippReportHeading -Title 'Check 17: Entra Directory Audit')) + if ($flaggedAudits.Count -gt 0) { + $b.Add((New-CippReportAlertBox -Title "[!] $($flaggedAudits.Count) flagged directory event(s)" -Content 'Security-info registration, consent, service principal, device, password, token or role events involving this user.')) + foreach ($a in @($flaggedAudits | Select-Object -First 8)) { + $lines = @( + "Date: $(FmtDate $a.ActivityDateTime)" + "By: $(if ($a.InitiatedBy) { $a.InitiatedBy } else { 'Unknown' })" + if ($a.ClientIP) { "From: $($a.ClientIP)$(if ($a.Country) { " ($($a.Country))" })" } + if ($a.Targets) { "Targets: $($a.Targets)" } + ) + $b.Add((New-CippReportInfoBox -Lines -Title "$($a.Activity) ($($a.Result))" -Content ($lines -join "`n"))) + } + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] No Flagged Directory Events' -Content "$(Cnt $bec.DirectoryAudits) directory event(s) involved this user in the window, none of the flagged kinds.")) + } + + $b.Add((New-CippReportHeading -Title 'Check 18 and 19: Registered Devices and Non-interactive Sign-ins')) + if ($recentRegisteredDevices.Count -gt 0) { + $b.Add((New-CippReportAlertBox -Lines -Title "[!] $($recentRegisteredDevices.Count) Entra device(s) registered in the window" -Content ((@($recentRegisteredDevices | ForEach-Object { "$(if ($_.displayName) { $_.displayName } else { $_.deviceId }) ($(if ($_.operatingSystem) { $_.operatingSystem } else { 'unknown OS' }), $(if ($_.trustType) { $_.trustType } else { 'unknown trust' })) registered $(FmtDate $_.registrationDateTime)" })) -join "`n"))) + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] No Devices Registered in the Window' -Content "$(Cnt $bec.RegisteredDevices) registered device(s), none new.")) + } + if ($foreignNonInteractive.Count -gt 0) { + $b.Add((New-CippReportAlertBox -Lines -Title "[!] $($foreignNonInteractive.Count) successful non-interactive sign-in(s) from outside the usage location" -Content ((@($foreignNonInteractive | Select-Object -First 8 | ForEach-Object { "$(FmtDate $_.CreatedDateTime) - $(if ($_.AppDisplayName) { $_.AppDisplayName } else { 'N/A' }) from $(if ($_.IPAddress) { $_.IPAddress } else { 'N/A' }) ($(if ($_.Country) { $_.Country } else { 'Unknown' }))" })) -join "`n"))) + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] No Foreign Non-interactive Sign-ins' -Content "$(Cnt $bec.NonInteractiveSignIns) recent non-interactive sign-in(s), none successful from outside the usage location.")) + } + + $b.Add((New-CippReportHeading -Title 'Check 20 and 21: Mailbox Activity and Identity Protection')) + if ($mailActivitySummary) { + $lines = @( + "Item accesses: $($mailActivitySummary.MailItemsAccessedCount)" + "Hard deletes: $($mailActivitySummary.HardDeleteCount)$(if ($mailActivitySummary.HardDeleteExceeded) { " [!] exceeds the $($mailActivitySummary.HardDeleteThreshold) threshold" })" + "Soft deletes: $($mailActivitySummary.SoftDeleteCount)" + "Sends: $($mailActivitySummary.SendCount)" + "Distinct client IPs: $($mailActivitySummary.DistinctClientIPs)" + $(if ($mailActivitySummary.SendAsByOthersCount -gt 0) { "Sent as/on behalf by others: $($mailActivitySummary.SendAsByOthersCount)" }) + 'Counts only - no items were read.' + ) + $b.Add((New-CippReportInfoBox -Lines -Tone $(if ($mailActivitySummary.HardDeleteExceeded) { 'warn' } else { '' }) -Title 'Mailbox activity counts' -Content ($lines -join "`n"))) + } else { + $b.Add((New-CippReportNote -Text 'Mailbox activity counts were not available for this run.')) + } + if ($completeness.RiskState.Skipped) { + $b.Add((New-CippReportAlertBox -Lines -Title '[!] Identity Protection Not Checked' -Content $(if ($completeness.RiskState.Requirement) { "Not checked - $($completeness.RiskState.Requirement). This is not a pass; whether the account is flagged as risky is unknown." } else { "$($completeness.RiskState.Error)" }))) + } elseif ($riskState.Listed) { + $b.Add((New-CippReportAlertBox -Title "[!] Identity Protection: $($riskState.RiskState) at $($riskState.RiskLevel) risk" -Content ("$(if ($riskState.RiskDetail) { $riskState.RiskDetail } else { 'No detail' }) - last updated $(FmtDate $riskState.RiskLastUpdatedDateTime).$(if ((Cnt $riskState.Detections) -gt 0) { " $(Cnt $riskState.Detections) risk detection(s) in the window." })"))) + } else { + $b.Add((New-CippReportClearBox -Title '[Pass] Not Listed as Risky' -Content 'Identity Protection does not list this user as risky.')) + } + + # === PAGE 8: RECOMMENDATIONS === + $b.Add((New-CippReportPage -Title 'Recommendations' -Subtitle 'Actions to take and prevention best practices')) + $b.Add((New-CippReportParagraph -Title 'Immediate Actions Required' -Text 'Based on the investigation findings, the following actions should be taken immediately:')) + $b.Add((New-CippReportBullets -Items @( + @{ marker = '1.'; label = 'Reset Password:'; text = "Change the user's password immediately to prevent further unauthorized access." } + @{ marker = '2.'; label = 'Revoke Sessions:'; text = 'Sign out the user from all active sessions to terminate any attacker access.' } + @{ marker = '3.'; label = 'Remove Suspicious Rules:'; text = 'Delete any mailbox rules that forward, redirect, or hide emails, especially those moving messages to unusual folders.' } + @{ marker = '4.'; label = 'Review MFA Devices:'; text = "Remove any MFA devices that the user doesn't recognize and re-register legitimate devices." } + @{ marker = '5.'; label = 'Audit Permissions:'; text = 'Review and revoke any unauthorized mailbox permissions or application consents.' } + @{ marker = '6.'; label = 'Monitor Account:'; text = 'Continue monitoring the account for suspicious activity for at least 30 days.' } + ))) + $b.Add((New-CippReportParagraph -Title 'Long-Term Prevention Strategies' -Text 'To prevent future Business Email Compromise attacks, implement these security best practices:')) + $b.Add((New-CippReportBullets -Items @( + @{ label = 'Enforce Multi-Factor Authentication (MFA):'; text = 'Require MFA for all users, especially those with administrative privileges or access to financial systems.' } + @{ label = 'Implement Security Awareness Training:'; text = 'Educate employees about phishing, social engineering, and how to identify suspicious emails. Regular training significantly reduces successful attacks.' } + @{ label = 'Enable Advanced Threat Protection:'; text = 'Use email security solutions that detect and block phishing, malware, and suspicious attachments.' } + @{ label = 'Configure Conditional Access Policies:'; text = 'Restrict access based on location, device compliance, and risk level to prevent unauthorized sign-ins.' } + @{ label = 'Monitor Audit Logs:'; text = 'Regularly review audit logs for suspicious activities such as unusual sign-in patterns, rule creation, or permission changes.' } + @{ label = 'Establish Financial Controls:'; text = 'Implement multi-person approval processes for wire transfers and payment changes to prevent fraudulent transactions.' } + ))) + $b.Add((New-CippReportParagraph -Title 'User Education Points' -Text 'Share these key points with the affected user to help prevent future compromises:')) + $b.Add((New-CippReportBullets -Items @( + @{ text = 'Never click on links or open attachments in unexpected emails, even if they appear to come from known contacts.' } + @{ text = 'Always verify unusual requests for money transfers or sensitive information through a separate communication channel (phone call, in person).' } + @{ text = 'Use strong, unique passwords for each account and consider using a password manager.' } + @{ text = 'Be cautious when authorizing new applications or granting permissions to third-party services.' } + @{ text = 'Report suspicious emails or activities to your IT security team immediately.' } + ))) + + # === PAGE 9: COMPLIANCE & DOCUMENTATION === + $b.Add((New-CippReportPage -Title 'Compliance & Documentation' -Subtitle 'Meeting regulatory and audit requirements')) + $b.Add((New-CippReportParagraph -Title 'Compliance Considerations' -Text 'This report supports compliance and documentation requirements for various security frameworks and regulatory standards:')) + $b.Add((New-CippReportBullets -Items @( + @{ label = 'ISO 27001:'; text = 'Demonstrates incident detection, analysis, and response procedures (Controls A.16.1.1 - A.16.1.7).' } + @{ label = 'CMMC Level 2:'; text = 'Provides evidence of security incident monitoring, analysis, and documentation (AC.L2-3.1.12, AU.L2-3.3.1).' } + @{ label = 'SOC 2 Type II:'; text = 'Documents detective and responsive controls for security incidents (CC7.3, CC7.4).' } + @{ label = 'NIST CSF:'; text = 'Aligns with Detect (DE.AE, DE.CM) and Respond (RS.AN, RS.MI) functions.' } + @{ label = 'GDPR:'; text = 'Demonstrates security breach detection and potential data breach assessment (Articles 32, 33).' } + ))) + $b.Add((New-CippReportParagraph -Title 'Audit Trail' -Text 'This investigation and resulting documentation provide an audit trail for security incident response:')) + $b.Add((New-CippReportInfoBox -Lines -Title 'Investigation Details' -Content (@( + "Investigation Date: $(FmtDate $bec.ExtractedAt)" + "Analyzed User: $upn" + "Organization: $TenantName" + "Analysis Period: $windowDays days" + "Assigned Usage Location: $(if ($usageLoc) { $usageLoc } else { 'Not assigned' })" + "Audit Log Status: $(if ($bec.ExtractResult) { $bec.ExtractResult } else { 'Unknown' })" + ) -join "`n"))) + $b.Add((New-CippReportInfoBox -Lines -Title 'Findings Summary' -Content (@( + "Threat Level: $threatLevel (score $threatValue)" + "Mailbox Rules Found: $($stats.newRules)" + "Rule Changes: $($stats.ruleChanges)" + "Permission Changes: $($stats.permissionChanges) ($($stats.permissionChangesTargetingUser) targeting this mailbox)" + "New Applications: $($stats.newApps)" + "Known-Malicious Applications: $($stats.maliciousApps)" + "Flagged Application Consents: $($flaggedGrants.Count)" + "Flagged Mailbox Delegations: $($flaggedDelegations.Count)" + "Flagged Transport Rules/Changes: $($flaggedTransportRules.Count + $flaggedTransportChanges.Count)" + "New Users: $($stats.newUsers)" + "Sent Messages: $(if ($stats.sentTotalMessages) { $stats.sentTotalMessages } else { $stats.sentMessages })" + "Repeated Subject Campaigns: $($stats.repeatedSubjects)" + "Send Bursts: $($stats.sendBursts)" + "MFA Devices: $($stats.mfaDevices)" + "Recent MFA Registrations ($windowDays d): $($stats.recentMfaDevices)" + "Password Changes: $($stats.passwordChanges)" + "Trusted Senders: $($stats.trustedSenders)" + "Blocked Senders: $($stats.blockedSenders)" + "Safelist Changes: $($stats.safelistChanges)" + "Sharing Changes: $($stats.sharingChanges)" + "Anonymous Links: $($stats.anonymousLinks)" + "Intune Devices: $($stats.intuneDevices)" + "Recent Intune Enrollments ($windowDays d): $($stats.recentIntuneDevices)" + "Received-mail Findings: $($receivedFindings.Count)" + "Delivered Threats: $($deliveredThreats.Count)" + "Foreign Sign-ins: $($stats.foreignSignIns) ($($stats.foreignSuccessfulSignIns) successful)" + "Foreign Non-interactive Sign-ins: $($foreignNonInteractive.Count)" + "Identity Protection Listed: $(if ($riskState.Listed) { "Yes ($($riskState.RiskLevel))" } else { 'No' })" + ) -join "`n"))) + $b.Add((New-CippReportParagraph -Title 'Document Retention' -Text "This report should be retained according to your organization's document retention policy and regulatory requirements. Typical retention periods range from 3-7 years depending on applicable compliance frameworks. Store this document securely with restricted access as it contains sensitive security information.")) + $b.Add((New-CippReportParagraph -Title 'Additional Resources' -Text 'For more information about Business Email Compromise and cybersecurity best practices:')) + $b.Add((New-CippReportBullets -Items @( + @{ text = 'FBI IC3: Internet Crime Complaint Center (ic3.gov)' } + @{ text = 'CISA: Cybersecurity & Infrastructure Security Agency (cisa.gov)' } + @{ text = 'Microsoft Security: Business Email Compromise resources' } + ))) + } + + @{ + Blocks = @($b) + Variables = @{ + coverlabel = 'Security Incident Report' + covertitle = 'BEC Compromise' + coveraccent = 'Analysis' + covertenant = [string]$UserData.displayName + coversubtitle = "Business Email Compromise Investigation Report for $TenantName" + covermeta = [string]$upn + covermetanote = "Analysis Date: $(FmtDate $bec.ExtractedAt)" + coverfallbackimage = '/reportImages/soc.jpg' + coverfooternote = 'Confidential & Proprietary - For Internal Use Only' + footerlabel = "$TenantName - BEC Analysis Report for $($UserData.displayName)" + } + } +} diff --git a/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippExecutiveReportTree.ps1 b/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippExecutiveReportTree.ps1 new file mode 100644 index 0000000000..c82a422ad0 --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippExecutiveReportTree.ps1 @@ -0,0 +1,206 @@ +function Build-CippExecutiveReportTree { + <# + .SYNOPSIS + Compose the Executive report as a component tree (server port of ExecutiveReportButton.jsx). + .DESCRIPTION + Pure composition: takes the already-gathered/shaped report data and returns @{ Blocks; Variables } + - the component nodes for ConvertTo-CippReportPdf and the cover/footer report variables. Holds no + data gathering, so it is unit-testable with sample data and drives the same layout the client + ExecutiveReportDocument produces. + .PARAMETER Data + Hashtable of the report's data: + TenantName, UserStats, SecureScore (currentScore/maxScore/percentageCurrent/percentageVsSimilar/ + percentageVsAllTenants/trend), Licenses[], Devices[], CAPolicies[] (raw state), SecurityControls[] + (name/description/tags/status). + .PARAMETER SectionConfig + Which sections to include (executiveSummary/securityStandards/secureScore/licenseManagement/ + deviceManagement/conditionalAccess/infographics). + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)][hashtable]$Data, + [hashtable]$SectionConfig = @{} + ) + + $cfg = @{ + executiveSummary = $true; securityStandards = $true; secureScore = $true + licenseManagement = $true; deviceManagement = $true; conditionalAccess = $true; infographics = $true + } + foreach ($k in $SectionConfig.Keys) { $cfg[$k] = $SectionConfig[$k] } + + $tenant = $Data.TenantName + $blocks = [System.Collections.Generic.List[object]]::new() + # Chapter dividers over the bundled stock photos (the renderer resolves /reportImages/ paths). + function Hero($image, $ov, $hi, $hl, $sub, $ft) { + if ($cfg.infographics) { + $blocks.Add((New-CippReportHero -Image "/reportImages/$image.jpg" -Overtitle $ov -Highlight $hi -Headline $hl -SubText $sub -FooterText $ft)) + } + } + + # -- Executive Summary -- + if ($cfg.executiveSummary) { + $blocks.Add((New-CippReportPage -Title 'Executive Summary' -Subtitle 'Strategic overview of your Microsoft 365 security posture')) + $blocks.Add((New-CippReportParagraph -Html ("

This security assessment for {0} provides a clear picture of your organization's cybersecurity posture and readiness against modern threats. We've evaluated your current security measures against industry best practices to identify strengths and opportunities for improvement.

" -f [System.Net.WebUtility]::HtmlEncode([string]$tenant)))) + $blocks.Add((New-CippReportParagraph -Html '

Our assessment follows globally recognized security standards to ensure your organization meets regulatory requirements and industry benchmarks. This approach helps protect your business assets, maintain customer trust, and reduce operational risks from cyber threats.

')) + $us = $Data.UserStats + $gaCaption = if ($us.pimCapable) { "$($us.permanentGlobalAdmins) permanent, $($us.eligibleGlobalAdmins) eligible" } else { $null } + $gaStat = @{ value = "$($us.globalAdmins)"; label = 'Global Admins' } + if ($gaCaption) { $gaStat.caption = $gaCaption } + $blocks.Add((New-CippReportStatRow -Title 'Environment Overview' -Stats @( + @{ value = "$($us.licensedUsers)"; label = 'Licensed Users' } + @{ value = "$($us.unlicensedUsers)"; label = 'Unlicensed Users' } + @{ value = "$($us.guests)"; label = 'Guest Users' } + $gaStat + ))) + } + + Hero 'board' $null '83%' $null "of organizations experienced`nmore than one cyberattack`nin the past year" "Proactive security prevents`nrepeated attacks" + + # -- Security Standards -- + if ($cfg.securityStandards -and $Data.SecurityControls -and @($Data.SecurityControls).Count -gt 0) { + $blocks.Add((New-CippReportPage -Title 'Security Standards Assessment' -Subtitle 'Detailed evaluation of implemented security standards')) + $blocks.Add((New-CippReportParagraph -Html '

Your security standards have been carefully evaluated against industry best practices to protect your business from cyber threats while ensuring smooth daily operations. These standards help maintain business continuity, protect sensitive data, and meet regulatory requirements that are essential for your industry.

')) + $rows = foreach ($c in $Data.SecurityControls) { + @{ name = $c.name; description = $c.description; tags = $c.tags; status = $c.status + tone = @{ Compliant = 'pass'; Partial = 'warn'; Review = 'fail'; 'Review Required' = 'fail' }[$c.status] + } + } + $blocks.Add((New-CippReportTable -Title 'Security Standards Status' -Limit @($Data.SecurityControls).Count -Columns @( + @{ header = 'Standard'; key = 'name'; width = 2; bold = $true } + @{ header = 'Description'; key = 'description'; width = 4 } + @{ header = 'Tags'; key = 'tags'; width = 2 } + @{ header = 'Status'; key = 'status'; width = 1.5; toneField = 'tone' } + ) -Rows @($rows))) + $blocks.Add((New-CippReportBullets -Title 'Key Recommendations' -Items @( + @{ label = 'Immediate Actions:'; text = 'Address standards marked as "Review" to enhance security posture' } + @{ label = 'Compliance:'; text = 'Ensure all security standards are properly implemented and maintained' } + @{ label = 'Monitoring:'; text = 'Establish regular review cycles for all security standards' } + @{ label = 'Training:'; text = 'Implement security awareness programs to reduce human risk factors' } + ))) + } + + Hero 'glasses' $null '95%' $null "of successful cyber attacks`ncould have been prevented with`nproactive security measures" "Your security resilience is`nour primary mission" + + # -- Microsoft Secure Score -- + if ($cfg.secureScore -and $Data.SecureScore) { + $ss = $Data.SecureScore + $blocks.Add((New-CippReportPage -Title 'Microsoft Secure Score' -Subtitle 'Comprehensive security posture measurement and benchmarking')) + $blocks.Add((New-CippReportParagraph -Html '

Microsoft Secure Score measures how well your organization is protected against cyber threats. This score reflects the effectiveness of your current security measures and helps identify areas where additional protection could strengthen your business resilience.

')) + $blocks.Add((New-CippReportStatRow -Title 'Score Comparison' -Stats @( + @{ value = "$(if ($null -ne $ss.currentScore) { $ss.currentScore } else { 'N/A' })"; label = 'Current Score' } + @{ value = "$(if ($null -ne $ss.maxScore) { $ss.maxScore } else { 'N/A' })"; label = 'Max Score' } + @{ value = "$(if ($null -ne $ss.percentageVsSimilar) { $ss.percentageVsSimilar } else { 'N/A' })%"; label = 'vs Similar Orgs' } + @{ value = "$(if ($null -ne $ss.percentageVsAllTenants) { $ss.percentageVsAllTenants } else { 'N/A' })%"; label = 'vs All Orgs' } + ))) + $blocks.Add((New-CippReportHeading -Title '7-Day Score Trend')) + $blocks.Add((New-CippReportChart -Title 'Secure Score Progress' -Kind trend -Max ([double]$ss.maxScore) -Caption ("Current: {0} / {1} ({2}%)" -f $ss.currentScore, $ss.maxScore, $ss.percentageCurrent) -Data @($ss.trend))) + $blocks.Add((New-CippReportInfoBox -Title 'What Your Score Means' -Content ("Your current score of {0} represents {1}% of the maximum protection level available. This indicates how well your organization is currently defended against common cyber threats and data breaches." -f $ss.currentScore, $ss.percentageCurrent))) + $blocks.Add((New-CippReportInfoBox -Title 'Why Scores Change' -Content "- Business growth and new employees may temporarily lower scores until security measures are applied`n- Changes in software licenses can affect available security features`n- New security threats require updated protections, which may impact scores`n- Regular security improvements help maintain and increase your protection level")) + } + + Hero 'working' 'Every' '39' 'seconds' "a business falls victim to`nransomware attacks" "Proactive defense beats`nreactive recovery" + + # -- License Management -- + if ($cfg.licenseManagement -and $Data.Licenses -and @($Data.Licenses).Count -gt 0) { + $blocks.Add((New-CippReportPage -Title 'License Management' -Subtitle 'Microsoft 365 license allocation and utilization analysis')) + $blocks.Add((New-CippReportParagraph -Html '

Smart license management helps control costs while ensuring your team has the tools they need to be productive. This analysis shows how your current licenses are being used and identifies opportunities to optimize spending without compromising business operations.

')) + $blocks.Add((New-CippReportTable -Title 'License Allocation Summary' -Limit @($Data.Licenses).Count -Columns @( + @{ header = 'License Type'; key = 'name'; width = 5; bold = $true } + @{ header = 'Used'; key = 'used'; width = 1.5; align = 'center'; bold = $true } + @{ header = 'Available'; key = 'available'; width = 1.5; align = 'center'; bold = $true } + @{ header = 'Total'; key = 'total'; width = 1.5; align = 'center'; bold = $true } + ) -Rows @($Data.Licenses))) + $blocks.Add((New-CippReportBullets -Title 'License Optimization Recommendations' -Items @( + @{ label = 'Usage Monitoring:'; text = 'Track how licenses are being used to identify cost-saving opportunities' } + @{ label = 'Cost Control:'; text = 'Review unused licenses to reduce unnecessary spending' } + @{ label = 'Growth Planning:'; text = 'Ensure you have enough licenses for business expansion without overspending' } + @{ label = 'Regular Reviews:'; text = 'Conduct quarterly reviews to maintain cost-effective license allocation' } + ))) + } + + Hero 'laptop' $null '$4.45M' $null "average cost of a`ndata breach in 2024" "Investment in security`nsaves millions in recovery" + + # -- Device Management -- + if ($cfg.deviceManagement -and $Data.Devices -and @($Data.Devices).Count -gt 0) { + $devices = @($Data.Devices) + $compliant = @($devices | Where-Object { $_.compliant }).Count + $blocks.Add((New-CippReportPage -Title 'Device Management' -Subtitle 'Device compliance status and management overview')) + $blocks.Add((New-CippReportParagraph -Html '

Managing employee devices is essential for protecting your business data and maintaining productivity. This analysis shows which devices meet your security standards and identifies any that may need attention to prevent data breaches or operational disruptions.

')) + $blocks.Add((New-CippReportStatRow -Title 'Device Compliance Overview' -Stats @( + @{ value = "$($devices.Count)"; label = 'Total Devices' } + @{ value = "$compliant"; label = 'Compliant' } + @{ value = "$($devices.Count - $compliant)"; label = 'Non-Compliant' } + @{ value = "$([math]::Round(($compliant / $devices.Count) * 100))%"; label = 'Compliance Rate' } + ))) + $devRows = foreach ($d in $devices) { + @{ name = $d.name; os = $d.os; compliance = $d.compliance; lastSync = $d.lastSync + tone = if ($d.compliant) { 'pass' } else { 'fail' } + } + } + $blocks.Add((New-CippReportTable -Title 'Device Management Summary' -Limit 8 -Columns @( + @{ header = 'Device Name'; key = 'name'; width = 3; bold = $true } + @{ header = 'OS'; key = 'os'; width = 2; bold = $true } + @{ header = 'Compliance'; key = 'compliance'; width = 2; toneField = 'tone' } + @{ header = 'Last Sync'; key = 'lastSync'; width = 2; bold = $true } + ) -Rows @($devRows))) + $blocks.Add((New-CippReportStatRow -Title 'Device Insights' -Stats @( + @{ value = "$(@($devices | Where-Object { $_.os -eq 'Windows' }).Count)"; label = 'Windows Devices' } + @{ value = "$(@($devices | Where-Object { $_.os -eq 'iOS' }).Count)"; label = 'iOS Devices' } + @{ value = "$(@($devices | Where-Object { $_.os -eq 'Android' }).Count)"; label = 'Android Devices' } + @{ value = "$(@($devices | Where-Object { $_.encrypted }).Count)"; label = 'Encrypted' } + ))) + $blocks.Add((New-CippReportInfoBox -Title 'Device Management Recommendations' -Content 'Keep devices updated and secure to protect business data. Regularly check that all employee devices meet security standards and address any issues promptly. Consider automated policies to maintain consistent security across all devices and conduct regular reviews to identify potential risks.')) + } + + Hero 'city' $null '277' 'days' "average time to identify and`ncontain a data breach" "Early detection minimizes`nbusiness impact" + + # -- Conditional Access -- + if ($cfg.conditionalAccess -and $Data.CAPolicies -and @($Data.CAPolicies).Count -gt 0) { + $ca = @($Data.CAPolicies) + $stateLabel = @{ enabled = 'Enabled'; enabledForReportingButNotEnforced = 'Report Only'; disabled = 'Disabled' } + $stateTone = @{ enabled = 'pass'; enabledForReportingButNotEnforced = 'warn'; disabled = 'fail' } + $enabled = @($ca | Where-Object { $_.state -eq 'enabled' }).Count + $reportOnly = @($ca | Where-Object { $_.state -eq 'enabledForReportingButNotEnforced' }).Count + $mfa = @($ca | Where-Object { $_.controls -contains 'mfa' -or $_.controlsText -eq 'MFA' }).Count + $blocks.Add((New-CippReportPage -Title 'Conditional Access Policies' -Subtitle 'Identity and access management security controls')) + $blocks.Add((New-CippReportParagraph -Html '

Access control policies help protect your business by ensuring only the right people can access sensitive information under appropriate circumstances. These smart security measures automatically evaluate each access request and apply additional verification when needed, balancing security with employee productivity.

')) + $blocks.Add((New-CippReportParagraph -Title 'How Access Controls Protect Your Business' -Html '

These policies work like intelligent security guards, making decisions based on who is trying to access what, from where, and when. For example, accessing email from the office might be seamless, but accessing it from an unusual location might require additional verification. This approach protects your data while minimizing disruption to daily work.

')) + $caRows = foreach ($p in $ca) { + @{ name = $p.name; state = ($stateLabel[$p.state] ?? $p.state); tone = ($stateTone[$p.state]); applications = $p.applications; controls = $p.controlsText } + } + $blocks.Add((New-CippReportTable -Title 'Current Policy Configuration' -Limit 8 -Columns @( + @{ header = 'Policy Name'; key = 'name'; width = 4; bold = $true } + @{ header = 'State'; key = 'state'; width = 2; toneField = 'tone' } + @{ header = 'Applications'; key = 'applications'; width = 2; bold = $true } + @{ header = 'Controls'; key = 'controls'; width = 3; bold = $true } + ) -Rows @($caRows))) + $blocks.Add((New-CippReportStatRow -Title 'Policy Overview' -Stats @( + @{ value = "$($ca.Count)"; label = 'Total Policies' } + @{ value = "$enabled"; label = 'Enabled' } + @{ value = "$reportOnly"; label = 'Report Only' } + @{ value = "$mfa"; label = 'MFA Policies' } + ))) + $blocks.Add((New-CippReportBullets -Title 'Policy Analysis' -Items @( + @{ label = 'Policy Coverage:'; text = "$($ca.Count) conditional access policies configured" } + @{ label = 'Enforcement Status:'; text = "$enabled policies actively enforced" } + @{ label = 'Testing Phase:'; text = "$reportOnly policies in report-only mode" } + @{ label = 'Security Controls:'; text = 'Multi-factor authentication and access blocking implemented' } + ))) + $caRec = if ($reportOnly -gt 0) { "Consider activating $reportOnly policies currently in testing mode after ensuring they don't disrupt business operations. " } else { 'Your access controls are properly configured. ' } + $blocks.Add((New-CippReportInfoBox -Title 'Access Control Recommendations' -Content ($caRec + 'Regularly review how these policies affect employee productivity and adjust as needed. Consider additional location-based protections for enhanced security without impacting daily operations.'))) + } + + @{ + Blocks = @($blocks) + Variables = @{ + coverlabel = 'SECURITY ASSESSMENT' + covertitle = 'Executive' + coveraccent = 'Summary' + covertenant = [string]$tenant + coversubtitle = "Security & Compliance Assessment for $tenant" + coverfooternote = 'Confidential - For Internal Use Only' + coverfallbackimage = '/reportImages/soc.jpg' + footerlabel = "$tenant - Executive Summary" + } + } +} diff --git a/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippMailFlowReportTree.ps1 b/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippMailFlowReportTree.ps1 new file mode 100644 index 0000000000..d8f2ec4fea --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippMailFlowReportTree.ps1 @@ -0,0 +1,126 @@ +function Build-CippMailFlowReportTree { + <# + .SYNOPSIS + Compose the Exchange Mail Flow report as a component tree (server port of MailFlowReportButton.jsx). + .PARAMETER Data + Mail flow data: days, totals, directionTotals, daily[], topSenders[], topSpamRecipients[]. + Returns @{ Blocks; Variables }. + #> + [CmdletBinding()] + param([Parameter(Mandatory)][hashtable]$Data) + + function nz($v) { if ($null -eq $v) { 0 } else { [double]$v } } + function num($v) { '{0:N0}' -f (nz $v) } + function pct($part, $whole) { if ($whole -gt 0) { [math]::Round(($part / $whole) * 1000) / 10 } else { 0 } } + $dispositions = @( + @{ key = 'GoodMail'; label = 'Good mail' }, @{ key = 'TransportRules'; label = 'Transport rules' } + @{ key = 'SpamDetections'; label = 'Spam' }, @{ key = 'EdgeBlockSpam'; label = 'Edge blocked spam' } + @{ key = 'EmailPhish'; label = 'Phish' }, @{ key = 'EmailMalware'; label = 'Malware' } + ) + $days = if ((nz $Data.days) -gt 0) { [int]$Data.days } else { 14 } + $totals = if ($Data.totals) { $Data.totals } else { @{} } + $dir = if ($Data.directionTotals) { $Data.directionTotals } else { @{} } + # `?? @()` so a missing list is empty rather than @($null), which would render as one blank row. + $daily = @($Data.daily ?? @()) + $topSenders = @($Data.topSenders ?? @()) + $topSpam = @($Data.topSpamRecipients ?? @()) + + $totalMail = ($dispositions | ForEach-Object { nz $totals[$_.key] } | Measure-Object -Sum).Sum + $goodPct = pct (nz $totals.GoodMail) $totalMail + $phish = nz $totals.EmailPhish; $malware = nz $totals.EmailMalware; $threats = $phish + $malware + $transportRules = nz $totals.TransportRules + $targeted = pct $threats $totalMail + $spam = pct ((nz $totals.SpamDetections) + (nz $totals.EdgeBlockSpam)) $totalMail + $hygiene = if ($totalMail -le 0) { 'Good' } + elseif ($targeted -gt 1 -or $spam -gt 25) { 'Attention Needed' } + elseif ($targeted -gt 0.25 -or $spam -gt 10) { 'Fair' } + else { 'Good' } + $dangerC = '#742A2A'; $warnC = '#744210' + $sevColour = @{ 'Attention Needed' = $dangerC; Fair = $warnC; Good = '#22543D' }[$hygiene] + + $dayLabel = { param($v) if ($v) { ([datetime]$v).ToString('MMM d') } else { '' } } + $volumeSeries = foreach ($row in $daily) { @{ label = (& $dayLabel $row.date); value = ($dispositions | ForEach-Object { nz $row[$_.key] } | Measure-Object -Sum).Sum } } + $directionSeries = @( + @{ label = 'Inbound'; value = (nz $dir.Inbound) } + @{ label = 'Outbound'; value = (nz $dir.Outbound) } + @{ label = 'Intra-org'; value = (nz $dir.IntraOrg) } + ) + $dispositionRows = foreach ($d in $dispositions) { @{ disposition = $d.label; messages = (num $totals[$d.key]); share = "$(pct (nz $totals[$d.key]) $totalMail)%" } } + + $blocks = [System.Collections.Generic.List[object]]::new() + + # -- Executive Summary -- + $blocks.Add((New-CippReportPage -Title 'Executive Summary' -Subtitle "Email traffic over the last $days days")) + $blocks.Add((New-CippReportParagraph -Html ('

Every message entering or leaving the organisation is given a disposition - delivered, held by a transport rule, filtered as spam, or blocked as phishing or malware. Those dispositions are the clearest single measure of what the mail environment is being asked to handle, because they count what the filters actually did rather than what they are configured to do. This report covers the last {0} days of mail flow at {1}.

' -f $days, [System.Net.WebUtility]::HtmlEncode([string]$Data.TenantName)))) + $blocks.Add((New-CippReportStatRow -Stats @( + @{ value = (num $totalMail); label = 'Total Messages' } + @{ value = "$goodPct%"; label = 'Delivered Clean' } + @{ value = (num $phish); label = 'Phish Blocked'; colour = $(if ($phish -gt 0) { $warnC }) } + @{ value = (num $malware); label = 'Malware Blocked'; colour = $(if ($malware -gt 0) { $dangerC }) } + ))) + $hygText = switch ($hygiene) { + 'Attention Needed' { 'Threat traffic is a material share of total mail. At this rate the organisation is being targeted rather than incidentally caught by bulk campaigns, and the filters are absorbing volume that protection policy and user awareness should be reducing at source. Treat the recommendations as current work.' } + 'Fair' { 'Threats are being caught at a level that is normal for an organisation of this profile, but not negligible. The filtering is working; the value now is in checking which users absorb most of it and whether their protection matches their exposure.' } + default { 'Threat traffic is a small fraction of total mail and is being stopped before delivery. Nothing here needs action beyond keeping the review cadence, since a change in this profile is usually the first visible sign of a campaign starting.' } + } + $blocks.Add((New-CippReportAlertBox -Title "Mail Hygiene: $hygiene" -Colour $sevColour -Content $hygText)) + $blocks.Add((New-CippReportInfoBox -Title 'What this data is' -Content "Figures come from Microsoft's mail flow status report for the tenant, aggregated as daily counts per disposition and direction. It is a count of messages, not a record of them: individual senders, subjects and recipients are not part of this data set, and a message appears once under the disposition that was applied to it.")) + $blocks.Add((New-CippReportInfoBox -Title 'What it does not show' -Content 'A blocked message is a filter working, not an incident. Nothing here indicates that a threat reached a user or that an account was compromised - that requires message trace and sign-in data, which are reviewed separately. Equally, a clean result does not prove nothing got through; it proves nothing was recognised.')) + + # -- Volume & Dispositions -- + $blocks.Add((New-CippReportPage -Title 'Volume & Dispositions' -Subtitle 'How much mail, and what happened to it')) + $blocks.Add((New-CippReportHeading -Title 'Daily Volume')) + $blocks.Add((New-CippReportParagraph -Text 'Total messages handled per day across every disposition. Steady volume with occasional peaks is normal; a sustained step change usually reflects a business event - a campaign, an onboarding, a new integration - and is worth being able to explain.')) + $blocks.Add((New-CippReportChart -Kind trend -Title 'Messages per day' -Caption ("{0} messages over {1} days" -f (num $totalMail), $days) -Data @($volumeSeries))) + $blocks.Add((New-CippReportHeading -Title 'Dispositions')) + $blocks.Add((New-CippReportParagraph -Text 'The share each disposition accounts for matters more than the counts. Good mail should dominate; anything else growing as a proportion is the signal.')) + $blocks.Add((New-CippReportTable -Columns @(@{ header = 'Disposition'; key = 'disposition'; width = 2.4 }, @{ header = 'Messages'; key = 'messages'; width = 1.2 }, @{ header = '% of Total'; key = 'share'; width = 1 }) -Rows @($dispositionRows))) + $blocks.Add((New-CippReportHeading -Title 'Direction')) + $blocks.Add((New-CippReportParagraph -Text 'Inbound, outbound and internal traffic in proportion. An unusual outbound share is the one to watch: mail leaving in volume that the business did not generate is how a compromised mailbox or an unsecured relay first shows up in these figures.')) + $blocks.Add((New-CippReportChart -Kind donut -Title 'Messages by direction' -CentreLabel 'messages' -Data @($directionSeries))) + + # -- Senders & Spam Targets -- + $blocks.Add((New-CippReportPage -Title 'Senders & Spam Targets' -Subtitle 'Who sends the most, and who is targeted')) + $blocks.Add((New-CippReportHeading -Title 'Top Mail Senders')) + $blocks.Add((New-CippReportInfoBox -Title 'Why this matters' -Content 'The heaviest senders are normally the ones you would expect - shared mailboxes, ticketing systems, scan-to-email devices, marketing platforms. What is worth a second look is a name that does not belong on that list. A user account sending at machine volume is either an unmanaged automation nobody documented, or a mailbox someone else is using.')) + if ($topSenders.Count -gt 0) { + $blocks.Add((New-CippReportTable -Limit 10 -Columns @(@{ header = 'Sender'; key = 'name'; width = 3 }, @{ header = 'Messages'; key = 'count'; width = 1 }) -Rows @($topSenders | ForEach-Object { @{ name = ($_.Name ?? $_.name ?? 'Unknown'); count = (num ($_.Count ?? $_.count)) } }))) + } else { $blocks.Add((New-CippReportClearBox -Title 'No sender data' -Content 'Microsoft returned no top-sender breakdown for this window.')) } + $blocks.Add((New-CippReportHeading -Title 'Top Spam Recipients')) + $blocks.Add((New-CippReportInfoBox -Title 'Why this matters' -Content 'Unwanted mail does not spread evenly. A handful of addresses - usually the published ones, and the people whose names appear on the website - absorb most of it, and those same addresses are the ones a targeted attempt will use. Concentration here identifies exactly who benefits most from stricter policy and from being asked to be careful.')) + if ($topSpam.Count -gt 0) { + $blocks.Add((New-CippReportTable -Limit 10 -Columns @(@{ header = 'Recipient'; key = 'name'; width = 3 }, @{ header = 'Messages'; key = 'count'; width = 1 }) -Rows @($topSpam | ForEach-Object { @{ name = ($_.Name ?? $_.name ?? 'Unknown'); count = (num ($_.Count ?? $_.count)) } }))) + } else { $blocks.Add((New-CippReportClearBox -Title 'No concentrated spam targets' -Content 'No recipient stands out as absorbing spam over this window.')) } + + # -- Recommendations -- + $blocks.Add((New-CippReportPage -Title 'Recommendations' -Subtitle 'What to do with these figures')) + $blocks.Add((New-CippReportHeading -Title 'Priority Actions')) + $blocks.Add((New-CippReportParagraph -Text "Ordered by what this window's data actually shows, rather than by a generic checklist.")) + $priority = [System.Collections.Generic.List[object]]::new() + if ($threats -gt 0) { $priority.Add(@{ label = 'Review anti-phishing and anti-malware policy strength.'; text = "$(num $threats) messages were blocked as phishing or malware in this window. Confirm the tenant is on the current preset security policies, that impersonation protection lists the people who would actually be impersonated, and that Safe Links and Safe Attachments cover every mailbox rather than a pilot group." }) } + if ($topSpam.Count -gt 0) { $priority.Add(@{ label = 'Give the most-targeted users stronger protection.'; text = 'The recipients listed in this report absorb a disproportionate share of unwanted mail. Priority accounts, tighter quarantine policy and a short conversation about what they are receiving cost little and are aimed exactly where the traffic is going.' }) } + $priority.Add(@{ label = 'Verify SPF, DKIM and DMARC are published and enforcing.'; text = 'These records decide whether mail claiming to be from the domain is accepted elsewhere. A DMARC policy left at p=none reports abuse without stopping it, which means the organisation can be impersonated to its own customers regardless of how well inbound filtering performs.' }) + if ($transportRules -gt 0) { $priority.Add(@{ label = 'Audit the transport rules acting on mail.'; text = "Transport rules handled $(num $transportRules) messages here. Rules accumulate, outlive the reason they were written, and silently override filtering decisions - confirm each one is still wanted and that none bypasses protection for a sender that no longer needs the exception." }) } + $i = 0; $priorityItems = foreach ($p in $priority) { $i++; @{ marker = "$i."; label = $p.label; text = $p.text } } + $blocks.Add((New-CippReportBullets -Items @($priorityItems))) + $blocks.Add((New-CippReportHeading -Title 'Keeping It That Way')) + $blocks.Add((New-CippReportBullets -Items @( + @{ label = 'Review mail flow on a fixed cadence.'; text = 'These figures are only meaningful against previous ones. A monthly look establishes the normal shape of the traffic, which is what makes an abnormal month visible at a glance.' } + @{ label = 'Watch the outbound share, not just the inbound.'; text = 'Inbound threat volume reflects the internet. Outbound volume reflects the organisation, so a change there is far more likely to mean something has gone wrong inside it.' } + @{ label = 'Alert on the conditions, not the counts.'; text = 'Configure alert policies for outbound spam and unusual sending volume. A report read monthly finds a compromised mailbox weeks late; an alert finds it the same day.' } + @{ label = 'Keep quarantine reviewed and released promptly.'; text = 'Filtering only holds if people trust it. Where legitimate mail sits in quarantine unattended, users route around the controls - and that habit costs more than the filtering saves.' } + ))) + + @{ + Blocks = @($blocks) + Variables = @{ + coverlabel = 'Email Traffic Review' + coversubtitle = "Where email at $($Data.TenantName) came from over the last $days days, how much of it was delivered, and what was stopped before it reached a mailbox." + covermeta = ('{0:N0} messages / {1}% delivered / {2:N0} threats caught' -f $totalMail, $goodPct, $threats) + covermetanote = "Mail hygiene: $hygiene" + coverfooternote = 'Confidential - For Internal Use Only' + coverfallbackimage = '/reportImages/city.jpg' + footerlabel = "$($Data.TenantName) - Mail Flow" + } + } +} diff --git a/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippPermissionsReportTree.ps1 b/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippPermissionsReportTree.ps1 new file mode 100644 index 0000000000..92fef6e42f --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippPermissionsReportTree.ps1 @@ -0,0 +1,125 @@ +function Build-CippPermissionsReportTree { + <# + .SYNOPSIS + Compose the SharePoint Permissions report as a component tree (server port of + PermissionsReportButton.jsx). + .PARAMETER Data + Permissions data: summary (counts), assignments[], skippedSites[]. Returns @{ Blocks; Variables }. + #> + [CmdletBinding()] + param([Parameter(Mandatory)][hashtable]$Data) + + $summary = if ($Data.summary) { $Data.summary } else { @{} } + # `?? @()` so a missing list is empty rather than @($null), which would count as one row. + $assignments = @($Data.assignments ?? @()) + $skipped = @($Data.skippedSites ?? @()) + function nz($v) { if ($null -eq $v) { 0 } else { [int]$v } } + function plural($c, $s, $p) { "$c $(if ($c -eq 1) { $s } else { if ($p) { $p } else { "${s}s" } })" } + + $score = (@( + if ((nz $summary.broadClaimGrants) -gt 0) { 5 } + if ((nz $summary.externalGrants) -gt 0) { 3 } + if ((nz $summary.directFullControlGrants) -gt 0) { 2 } + if ((nz $summary.uniquePermissionLibraries) -gt 0) { 1 } + ) | Measure-Object -Sum).Sum + $exposure = if ($score -ge 7) { 'High' } elseif ($score -ge 3) { 'Medium' } else { 'Low' } + $dangerC = '#742A2A'; $warnC = '#744210' + $sevColour = @{ High = $dangerC; Medium = $warnC; Low = '#22543D' }[$exposure] + $claimLabels = @{ Everyone = 'Everyone (includes external users)'; EveryoneExceptExternal = 'Everyone except external users'; AllUsers = 'All Users' } + + $real = @($assignments | Where-Object { $_.principalId -and -not $_.isSystemManaged }) + $broad = @($real | Where-Object { $_.broadClaim }) + $external = @($real | Where-Object { $_.isGuest -eq $true }) + $fullCtl = @($real | Where-Object { $_.permissionLevel -eq 'Full Control' -and $_.principalType -ne 'SharePoint Group' }) + $libRows = @($real | Where-Object { $_.scope -eq 'Library' }) + $siteLabel = { param($r) if ($r.siteName) { $r.siteName } elseif ($r.siteUrl) { $r.siteUrl } else { 'Unnamed site' } } + $scopeLabel = { param($r) if ($r.scope -eq 'Library') { "$(& $siteLabel $r) / $($r.libraryTitle)" } else { (& $siteLabel $r) } } + + $blocks = [System.Collections.Generic.List[object]]::new() + + # -- Executive Summary -- + $blocks.Add((New-CippReportPage -Title 'Executive Summary' -Subtitle 'Who is allowed in, and how widely')) + $blocks.Add((New-CippReportParagraph -Html ('

Permissions are set by administrators on a site or document library and decide who is structurally allowed in. They change rarely, which is what makes them worth auditing: a permission granted for one project stays in place indefinitely, and a permission granted to the whole organisation looks identical to one granted to a single team until somebody reads it. This report covers {0}.

' -f [System.Net.WebUtility]::HtmlEncode([string]$Data.TenantName)))) + $blocks.Add((New-CippReportStatRow -Stats @( + @{ value = (nz $summary.broadClaimGrants); label = 'Tenant-Wide Grants'; colour = $(if ((nz $summary.broadClaimGrants) -gt 0) { $dangerC }) } + @{ value = (nz $summary.externalGrants); label = 'External Grants'; colour = $(if ((nz $summary.externalGrants) -gt 0) { $warnC }) } + @{ value = (nz $summary.directFullControlGrants); label = 'Direct Full Control'; colour = $(if ((nz $summary.directFullControlGrants) -gt 0) { $warnC }) } + @{ value = (nz $summary.uniquePermissionLibraries); label = 'Detached Libraries' } + ))) + $expText = switch ($exposure) { + 'High' { 'Content is reachable by people it was never meant for. A tenant-wide grant is present, which opens the content to the entire organisation regardless of who the site membership says should have it - and it is the most common reason material turns up unexpectedly in search results and AI assistant answers. Treat the findings below as immediate remediation work.' } + 'Medium' { 'Access extends past the intended audience in places. Each finding below is individually manageable, but each one widens what a single compromised account reaches.' } + default { 'Permissions broadly match what the structure intends. No tenant-wide grants were found. Continue reviewing periodically, particularly after site or library changes.' } + } + $blocks.Add((New-CippReportAlertBox -Title "Permission Exposure: $exposure" -Colour $sevColour -Content $expText)) + $blocks.Add((New-CippReportInfoBox -Title 'What was examined' -Content ("{0} SharePoint sites and {1} document libraries were read, producing {2} permission assignments. Data is taken from the last completed sync, not read live." -f (nz $summary.sitesScanned), (nz $summary.librariesScanned), (nz $summary.totalAssignments)))) + $blocks.Add((New-CippReportInfoBox -Title 'What is not covered' -Content 'Permissions are reported as grant paths, not effective access - a group holding a permission is one entry and its members are not expanded, so a person may hold access that shows here only via their group. Permissions on individual folders and files are not enumerated. OneDrive personal sites are out of scope. Access handed out by sharing link is a separate path, covered by the Sharing Report.')) + if ($skipped.Count -gt 0) { + $blocks.Add((New-CippReportAlertBox -Title ("$(plural $skipped.Count 'site') could not be read") -Colour $warnC -Content 'These sites could not be read on the most recent scan. Where a site was read successfully before, its earlier results are still shown and are as old as that scan; a site never read successfully contributes nothing. Either way, an absence of findings for these sites is not evidence of good configuration.')) + } + + # -- Findings -- + $blocks.Add((New-CippReportPage -Title 'Findings' -Subtitle 'Permissions worth reviewing, most urgent first')) + $blocks.Add((New-CippReportHeading -Title 'Finding 1: Tenant-Wide Grants')) + $blocks.Add((New-CippReportInfoBox -Title 'Why this matters' -Content "SharePoint offers a handful of special audiences - Everyone, Everyone except external users, and All Users - that resolve to the whole organisation rather than to named people. A library carrying one is readable by every employee no matter what the site's membership says, and it is the single most common cause of data appearing in search results or AI assistant answers where it was not expected.")) + if ($broad.Count -gt 0) { + $blocks.Add((New-CippReportAlertBox -Title ("$(plural $broad.Count 'tenant-wide grant') found") -Colour $dangerC -Content 'Confirm the content is genuinely meant to be organisation-wide. If not, replace the grant with a specific group - one edit removes access for everyone who was never meant to have it.')) + $blocks.Add((New-CippReportTable -Columns @(@{ header = 'Location'; key = 'location'; width = 2.4 }, @{ header = 'Audience'; key = 'audience'; width = 2 }, @{ header = 'Permission'; key = 'level'; width = 1.2 }) -Rows @($broad | ForEach-Object { @{ location = (& $scopeLabel $_); audience = ($claimLabels[$_.broadClaim] ?? $_.broadClaim); level = $_.permissionLevel } }))) + } else { $blocks.Add((New-CippReportClearBox -Title 'No tenant-wide grants found' -Content 'No site or library grants access to Everyone, Everyone except external users, or All Users.')) } + $blocks.Add((New-CippReportHeading -Title 'Finding 2: External and Guest Access')) + $blocks.Add((New-CippReportInfoBox -Title 'Why this matters' -Content 'Guest accounts holding permissions retain that access until somebody removes it - unlike a sharing link, nothing expires it. Guests from finished projects are a common source of standing access nobody is reviewing.')) + if ($external.Count -gt 0) { + $blocks.Add((New-CippReportAlertBox -Title ("$(plural $external.Count 'grant') held by external identities") -Colour $warnC -Content 'Verify each guest still needs access and that the relationship is current.')) + $blocks.Add((New-CippReportTable -Columns @(@{ header = 'Location'; key = 'location'; width = 2.2 }, @{ header = 'Identity'; key = 'identity'; width = 2.4 }, @{ header = 'Permission'; key = 'level'; width = 1.2 }) -Rows @($external | ForEach-Object { @{ location = (& $scopeLabel $_); identity = ($_.email ?? $_.title ?? $_.loginName); level = $_.permissionLevel } }))) + } else { $blocks.Add((New-CippReportClearBox -Title 'No external grants found' -Content 'No guest or external identity holds a permission on a scanned site or library.')) } + + # -- Findings continued -- + $blocks.Add((New-CippReportPage -Title 'Findings (continued)' -Subtitle 'Elevated rights and inheritance')) + $blocks.Add((New-CippReportHeading -Title 'Finding 3: Directly Granted Full Control')) + $blocks.Add((New-CippReportInfoBox -Title 'Why this matters' -Content 'Every site has an Owners group that holds Full Control by design, and that is expected. Full Control granted straight to a person or a directory group is different: it sits outside the membership structure, so it is not removed when someone leaves a team and it is easy to overlook when reviewing who administers a site.')) + if ($fullCtl.Count -gt 0) { + $blocks.Add((New-CippReportAlertBox -Title (plural $fullCtl.Count 'direct Full Control grant') -Colour $warnC -Content "Move these into the site's Owners group where the access is legitimate, so membership changes take effect automatically.")) + $blocks.Add((New-CippReportTable -Columns @(@{ header = 'Location'; key = 'location'; width = 2.2 }, @{ header = 'Principal'; key = 'principal'; width = 2.2 }, @{ header = 'Type'; key = 'type'; width = 1.2 }) -Rows @($fullCtl | ForEach-Object { @{ location = (& $scopeLabel $_); principal = ($_.title ?? $_.email ?? $_.loginName); type = $_.principalType } }))) + } else { $blocks.Add((New-CippReportClearBox -Title 'Full Control is held through Owners groups' -Content "No user or directory group holds Full Control outside a site's Owners group.")) } + $blocks.Add((New-CippReportHeading -Title 'Finding 4: Libraries With Their Own Permissions')) + $blocks.Add((New-CippReportInfoBox -Title 'Why this matters' -Content 'A library normally inherits from its site, so managing the site manages everything in it. A detached library keeps its own permissions and later site-level changes no longer reach it. That is legitimate when deliberate and a blind spot when not - removing somebody from the site does not remove them here.')) + if ((nz $summary.uniquePermissionLibraries) -gt 0) { + $blocks.Add((New-CippReportParagraph -Text ("{0} of {1} libraries no longer inherit from their site. Their assignments are listed in the appendix. Review whether each detachment was intentional and is still needed." -f (nz $summary.uniquePermissionLibraries), (nz $summary.librariesScanned)))) + } else { $blocks.Add((New-CippReportClearBox -Title 'All libraries inherit from their site' -Content 'Every scanned library takes its permissions from its site, so site-level access management covers them all.')) } + + # -- Recommendations -- + $blocks.Add((New-CippReportPage -Title 'Recommendations' -Subtitle 'What to do about the findings')) + $blocks.Add((New-CippReportHeading -Title 'Priority Actions')) + $blocks.Add((New-CippReportParagraph -Text 'Ordered by how much access each removes relative to the effort involved.')) + $blocks.Add((New-CippReportBullets -Items @( + @{ marker = '1.'; label = 'Replace tenant-wide grants.'; text = 'Swap Everyone and All Users grants for a specific group. This is the highest-value change available: a single edit removes access for everyone who was never meant to have it.' } + @{ marker = '2.'; label = 'Review guest permissions.'; text = 'Guests keep permissions indefinitely. Remove those whose projects have ended, and prefer time-boxed sharing for new external work.' } + @{ marker = '3.'; label = 'Move direct Full Control into Owners groups.'; text = 'Administrative rights held through the Owners group follow joiners and leavers. Held directly, they have to be remembered.' } + @{ marker = '4.'; label = 'Re-inherit libraries detached without reason.'; text = "Restoring inheritance brings a library back under site-level management. Only do this where the detachment was not deliberate - it discards the library's own permissions." } + @{ marker = '5.'; label = 'Prefer groups over individual grants.'; text = 'A permission held by a group updates itself as people join and leave. One held by a person does not.' } + ))) + $blocks.Add((New-CippReportHeading -Title 'Keeping It That Way')) + $blocks.Add((New-CippReportBullets -Items @( + @{ label = 'Re-run this review regularly.'; text = 'Permissions drift as projects start and end. A periodic review catches drift while it is still small.' } + @{ label = 'Manage access at the site, not the library.'; text = 'Leaving libraries inherited keeps one place to look when somebody joins or leaves.' } + @{ label = 'Check effective access, not just the lists.'; text = 'A group holding Edit says nothing about who is in it. Use the access check on a site to confirm what a specific person can actually reach.' } + @{ label = 'Avoid tenant-wide audiences by default.'; text = 'Where content genuinely is organisation-wide, say so deliberately and review it, rather than reaching for Everyone because it is convenient.' } + ))) + + # -- Appendix -- + $blocks.Add((New-CippReportPage -Title 'Appendix: Detached Library Permissions' -Subtitle 'Assignments on libraries that no longer inherit from their site')) + $blocks.Add((New-CippReportTable -Limit 40 -Columns @(@{ header = 'Site'; key = 'site'; width = 1.8 }, @{ header = 'Library'; key = 'library'; width = 1.6 }, @{ header = 'Principal'; key = 'principal'; width = 2.2 }, @{ header = 'Permission'; key = 'level'; width = 1.2 }) -Rows @($libRows | ForEach-Object { @{ site = (& $siteLabel $_); library = $_.libraryTitle; principal = ($_.title ?? $_.email ?? $_.loginName); level = $_.permissionLevel } }))) + + @{ + Blocks = @($blocks) + Variables = @{ + coverlabel = 'Access Review' + coversubtitle = "Who is structurally allowed into SharePoint sites and document libraries at $($Data.TenantName), and where that access reaches further than intended." + covermeta = ('{0} sites / {1} libraries / {2} permission assignments' -f (nz $summary.sitesScanned), (nz $summary.librariesScanned), (nz $summary.totalAssignments)) + covermetanote = "Permission exposure: $exposure" + coverfooternote = 'Confidential - For Internal Use Only' + coverfallbackimage = '/reportImages/soc.jpg' + footerlabel = "$($Data.TenantName) - SharePoint Permissions" + } + } +} diff --git a/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippShadowAIReportTree.ps1 b/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippShadowAIReportTree.ps1 new file mode 100644 index 0000000000..d018285942 --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippShadowAIReportTree.ps1 @@ -0,0 +1,162 @@ +function Build-CippShadowAIReportTree { + <# + .SYNOPSIS + Compose the Shadow AI report as a component tree (server port of ShadowAIReportButton's + ShadowAIReportPages). + .PARAMETER Data + Shadow AI data: summary, detectedApps[], consentedApps[], topTools[], byRisk[]. Returns + @{ Blocks; Variables }. + .PARAMETER SectionConfig + Which sections to include, keyed executiveSummary/infographics/background/riskLevels/ + sanctionedTools/detectedSoftware/entraApplications/recommendations. Empty (the default) includes + every section; a populated map includes only the keys set to $true (mirrors the client's section + toggles). + #> + [CmdletBinding()] + param([Parameter(Mandatory)][hashtable]$Data, [hashtable]$SectionConfig = @{}) + + $summary = if ($Data.summary) { $Data.summary } else { @{} } + # `?? @()` so a missing list is empty rather than @($null), which would render as one blank row. + $detected = @($Data.detectedApps ?? @()) + $consented = @($Data.consentedApps ?? @()) + $topTools = @($Data.topTools ?? @()) + $byRisk = @($Data.byRisk ?? @()) + $riskColours = @{ high = '#EF4444'; medium = '#F59E0B'; low = '#3B82F6'; informational = '#10B981' } + function RiskColour($r) { $k = "$r".ToLower(); if ($riskColours.ContainsKey($k)) { $riskColours[$k] } else { '#A0AEC0' } } + function nz($v) { if ($null -eq $v) { 0 } else { $v } } + # No config supplied -> full report; a supplied config includes only the keys explicitly set true. + $cfg = $SectionConfig + $allOn = ($null -eq $cfg -or $cfg.Count -eq 0) + function on($k) { if ($allOn) { $true } else { [bool]$cfg[$k] } } + + $riskAreas = @( + @{ title = 'Data Leakage'; colour = '#EF4444'; text = 'Customer records, credentials, source code and financials pasted into consumer AI tools may be retained by the provider and used to train future models, permanently placing them outside your control. Every prompt is a data transfer to a third party.' } + @{ title = 'Compliance & Legal Exposure'; colour = '#F59E0B'; text = 'Processing personal data through unvetted AI services can breach GDPR, HIPAA and industry-specific regulations, and undermines contractual confidentiality commitments made to customers.' } + @{ title = 'Excessive Application Permissions'; colour = '#D69E2E'; text = 'AI meeting assistants and productivity plugins often request broad access to mailboxes, calendars and files. A single user consent can expose organization-wide data to a third-party service, and that access persists until the consent is revoked.' } + @{ title = 'Unreliable Output in Business Processes'; colour = '#3B82F6'; text = 'AI-generated content flows into quotes, contracts and customer communication without review. Errors produced by unmanaged tools are difficult to trace because the organization does not know the tools are in use.' } + ) + $riskLevels = @( + @{ name = 'High'; text = 'Consumer tools that train on submitted data, retain prompts indefinitely, or operate from jurisdictions without adequate data protection. Content pasted into these tools should be treated as disclosed to an unvetted third party.' } + @{ name = 'Medium'; text = 'Tools with business-grade privacy options that are typically used through personal, unmanaged accounts. Risk depends heavily on the plan and account type in use.' } + @{ name = 'Low'; text = 'Tools with enterprise controls, contractual data-processing terms and no training on customer data when configured correctly.' } + @{ name = 'Informational'; text = 'Company sanctioned tools that have been explicitly approved for use in this tenant. They remain in the report for visibility but no longer contribute to the risk figures.' } + ) + + # Distinct sanctioned tools across both sources: device installs come from the Intune inventory + # rows, 7-day users from the Entra consent rows, summed per tool. + $sanctionedList = @($detected + $consented | Where-Object { $_.status -eq 'Sanctioned' } | Group-Object { $_.aiTool } | ForEach-Object { + @{ + tool = $_.Name + vendor = $_.Group[0].vendor + category = $_.Group[0].category + devices = ($_.Group | ForEach-Object { nz $_.deviceCount } | Measure-Object -Sum).Sum + users = ($_.Group | ForEach-Object { nz $_.activeUsersLast7Days } | Measure-Object -Sum).Sum + } + } | Select-Object -First 18) + $detectedRows = @($detected | Select-Object -First 18) + $consentedRows = @($consented | Select-Object -First 18) + + $blocks = [System.Collections.Generic.List[object]]::new() + + # -- AI Summary -- + if (on 'executiveSummary') { + $blocks.Add((New-CippReportPage -Title 'AI Summary' -Subtitle 'Strategic overview of AI usage in your Microsoft 365 environment')) + $blocks.Add((New-CippReportParagraph -Html ('

This report identifies the artificial intelligence tools discovered in the {0} environment, combining software inventory from managed devices (Intune) with cloud application consent data from Entra ID. Each tool is matched against a curated catalog of known AI services and assigned a risk level based on its data handling practices.

' -f [System.Net.WebUtility]::HtmlEncode([string]$Data.TenantName)))) + $blocks.Add((New-CippReportParagraph -Html '

Tools that have been explicitly approved are marked as company sanctioned and report the Informational risk level. Everything else represents shadow AI: tools adopted by employees without review or approval, whose handling of company data is unknown.

')) + $blocks.Add((New-CippReportStatRow -Title 'AI Usage Overview' -Stats @( + @{ value = (nz $summary.aiToolsDetected); label = 'AI Tools' } + @{ value = (nz $summary.deviceInstalls); label = 'Device Installs' } + @{ value = (nz $summary.consentedAiApps); label = 'Entra AI Apps' } + @{ value = (nz $summary.highRiskTools); label = 'High Risk'; colour = (RiskColour 'high') } + @{ value = (nz $summary.sanctionedTools); label = 'Sanctioned'; colour = (RiskColour 'informational') } + ))) + if ($topTools.Count -gt 0) { + $blocks.Add((New-CippReportTable -Title 'Most Used AI Tools' -Limit $topTools.Count -Columns @( + @{ header = 'Tool'; key = 'tool'; width = 3; bold = $true }, @{ header = 'Category'; key = 'category'; width = 3 } + @{ header = 'Status'; key = 'status'; width = 2 }, @{ header = 'Devices'; key = 'devices'; width = 1.5 }, @{ header = 'Users (7d)'; key = 'users'; width = 1.5 } + ) -Rows @($topTools | ForEach-Object { @{ tool = $_.tool; category = $_.category; status = ($_.status ?? 'Unsanctioned'); devices = "$($_.devices)"; users = "$($_.users)" } }))) + } + } + + if (on 'infographics') { $blocks.Add((New-CippReportHero -Image '/reportImages/laptop.jpg' -Highlight '75%' -SubText "of knowledge workers already`nuse generative AI at work -`nmost without their employer knowing" -FooterText "Visibility is the first step`nto control")) } + + # -- Understanding Shadow AI -- + if (on 'background') { + $blocks.Add((New-CippReportPage -Title 'Understanding Shadow AI' -Subtitle 'What unmanaged AI usage means for your organization')) + $blocks.Add((New-CippReportParagraph -Text 'Shadow AI is the use of artificial intelligence tools by employees without the knowledge or approval of the organization - the AI-era equivalent of shadow IT. Because most AI tools are free, browser-based and immediately useful, adoption happens quietly and quickly: an employee pastes a customer email into a chatbot to draft a reply, uploads a spreadsheet for analysis, or installs an AI notetaker that joins every meeting.')) + $blocks.Add((New-CippReportParagraph -Text 'The goal of a shadow AI program is not zero AI usage, but zero unsanctioned usage. Every tool in this report should end up either approved and managed, or replaced and blocked. The four risk areas below explain why unmanaged usage deserves attention.')) + $blocks.Add((New-CippReportHeading -Title 'Key Risk Areas')) + foreach ($area in $riskAreas) { $blocks.Add((New-CippReportInfoBox -Title $area.title -Colour $area.colour -Content $area.text)) } + } + + # -- Risk Levels & Distribution -- + if (on 'riskLevels') { + $blocks.Add((New-CippReportPage -Title 'AI Tool Risk Levels' -Subtitle 'How risk is assigned and how it is distributed in this tenant')) + $blocks.Add((New-CippReportParagraph -Text 'Detected tools are matched against a curated catalog of known AI services, each carrying a risk classification based on its data handling practices, account model and enterprise controls. Marking a tool as company sanctioned overrides its catalog risk with the Informational level, so the figures below reflect only unapproved use.')) + $blocks.Add((New-CippReportChart -Kind donut -Title 'AI Tool Risk Distribution' -CentreLabel 'Tools' -Data @($byRisk | ForEach-Object { @{ label = $_.risk; value = $_.tools; colour = (RiskColour $_.risk) } }))) + $blocks.Add((New-CippReportInfoBoxColumns -Columns 2 -Items @($riskLevels | ForEach-Object { @{ title = $_.name; content = $_.text; colour = (RiskColour $_.name); tintTitle = $true } }))) + } + + # -- Sanctioned Tools -- + if ((on 'sanctionedTools') -and $sanctionedList.Count -gt 0) { + $blocks.Add((New-CippReportPage -Title 'Company Sanctioned AI Tools' -Subtitle 'AI tools that are approved for use in this organization')) + $blocks.Add((New-CippReportParagraph -Text 'The tools listed below are permitted in this environment. They are allowed either because a business justification exists for their use, or because the system administrator has explicitly approved these tools for deployment. Sanctioned tools report the Informational risk level and are excluded from the shadow AI risk figures in this report; they remain listed for visibility into where AI is used across the organization.')) + $blocks.Add((New-CippReportParagraph -Text "Approval is not permanent: sanctioned tools should be reviewed periodically to confirm that the plan in use, the vendor's data handling terms and the business justification still hold.")) + $blocks.Add((New-CippReportTable -Limit $sanctionedList.Count -Columns @( + @{ header = 'Tool'; key = 'tool'; width = 3; bold = $true }, @{ header = 'Vendor'; key = 'vendor'; width = 3 }, @{ header = 'Category'; key = 'category'; width = 3 } + @{ header = 'Devices'; key = 'devices'; width = 2 }, @{ header = 'Users (7d)'; key = 'users'; width = 2 } + ) -Rows @($sanctionedList | ForEach-Object { @{ tool = $_.tool; vendor = $_.vendor; category = $_.category; devices = "$($_.devices)"; users = "$($_.users)" } }))) + } + + # -- Detected Software -- + if (on 'detectedSoftware') { + $blocks.Add((New-CippReportPage -Title 'AI Software on Managed Devices' -Subtitle 'AI applications found in the Intune software inventory')) + $detNote = if ($detected.Count -gt $detectedRows.Count) { ", showing the top $($detectedRows.Count) of $($detected.Count) entries" } else { '' } + $blocks.Add((New-CippReportParagraph -Text "The following AI applications were detected in the software inventory of managed devices$detNote. Device counts indicate how widely each application has spread through the environment.")) + $blocks.Add((New-CippReportTable -Limit $detectedRows.Count -Columns @( + @{ header = 'Application'; key = 'application'; width = 4; bold = $true }, @{ header = 'AI Tool'; key = 'aiTool'; width = 3 }, @{ header = 'Category'; key = 'category'; width = 3 } + @{ header = 'Risk'; key = 'risk'; width = 2; colourField = 'riskColour' }, @{ header = 'Status'; key = 'status'; width = 2.5 }, @{ header = 'Devices'; key = 'deviceCount'; width = 1.5 } + ) -Rows @($detectedRows | ForEach-Object { @{ application = $_.application; aiTool = $_.aiTool; category = $_.category; risk = $_.risk; riskColour = (RiskColour $_.risk); status = $_.status; deviceCount = "$($_.deviceCount)" } }))) + } + + # -- Entra Applications -- + if (on 'entraApplications') { + $blocks.Add((New-CippReportPage -Title 'AI Applications in Entra ID' -Subtitle 'AI services with a footprint in your identity platform')) + $conNote = if ($consented.Count -gt $consentedRows.Count) { ", showing the top $($consentedRows.Count) of $($consented.Count) entries" } else { '' } + $blocks.Add((New-CippReportParagraph -Text "The following AI services are registered as applications in the tenant, including any permissions users have consented to$conNote. The consent date shows when each service first gained a foothold in the environment.")) + $blocks.Add((New-CippReportTable -Limit $consentedRows.Count -Columns @( + @{ header = 'Application'; key = 'application'; width = 4; bold = $true }, @{ header = 'AI Tool'; key = 'aiTool'; width = 3 } + @{ header = 'Risk'; key = 'risk'; width = 2; colourField = 'riskColour' }, @{ header = 'Status'; key = 'status'; width = 2.5 }, @{ header = 'Users (7d)'; key = 'activeUsersLast7Days'; width = 2 }, @{ header = 'First Consented'; key = 'firstConsented'; width = 2.5 } + ) -Rows @($consentedRows | ForEach-Object { @{ application = $_.application; aiTool = $_.aiTool; risk = $_.risk; riskColour = (RiskColour $_.risk); status = $_.status; activeUsersLast7Days = "$($_.activeUsersLast7Days)"; firstConsented = $(if ($_.firstConsentedDateTime) { ([datetime]$_.firstConsentedDateTime).ToString('M/d/yyyy') } else { 'Unknown' }) } }))) + } + + if (on 'infographics') { $blocks.Add((New-CippReportHero -Image '/reportImages/working.jpg' -Highlight '1 in 3' -SubText "employees shares sensitive work data`nwith AI tools without approval" -FooterText "Sanctioned alternatives keep`nyour data under contract")) } + + # -- Recommendations -- + if (on 'recommendations') { + $blocks.Add((New-CippReportPage -Title 'Recommendations' -Subtitle 'A structured response to shadow AI in your environment')) + $blocks.Add((New-CippReportParagraph -Text 'A structured response to shadow AI combines approval of useful tools with controls on the rest. The following actions are recommended based on the findings in this report:')) + $blocks.Add((New-CippReportBullets -Title 'Action Plan' -Items @( + @{ label = 'Review & Decide:'; text = 'Evaluate each detected tool with stakeholders and decide whether it should be sanctioned, replaced with an approved alternative, or blocked.' } + @{ label = 'Sanction Approved Tools:'; text = 'Maintain a list of company sanctioned tools so future reports separate approved AI use from true shadow AI.' } + @{ label = 'Offer an Alternative:'; text = 'Provide a sanctioned option such as Microsoft 365 Copilot before blocking popular tools - blocking without an alternative drives usage to personal devices.' } + @{ label = 'Restrict Consent:'; text = 'Require admin approval for unverified applications in Entra ID so new AI services cannot access company data through user consent.' } + @{ label = 'Block & Monitor:'; text = 'Deploy Conditional Access and Defender for Cloud Apps policies to block or monitor unsanctioned AI web services.' } + @{ label = 'Extend DLP:'; text = 'Cover generative AI endpoints with data loss prevention policies to stop sensitive data from being pasted into chat prompts.' } + @{ label = 'Train Users:'; text = 'Publish an acceptable AI use policy and train users on what data may never be shared with AI tools.' } + @{ label = 'Review Monthly:'; text = 'Re-run this report on a regular cadence - new AI tools appear in tenants within days of release.' } + ))) + $blocks.Add((New-CippReportInfoBox -Title 'Next Review' -Content 'The AI tool landscape changes quickly and new tools appear in tenants within days of release. We recommend re-running this assessment monthly and reviewing newly detected tools against your acceptable AI use policy.')) + } + + @{ + Blocks = @($blocks) + Variables = @{ + coverlabel = 'AI Risk Assessment' + coversubtitle = 'Discovery and risk assessment of AI tools in use across managed devices and cloud applications.' + coverfooternote = 'Confidential - For Internal Use Only' + coverfallbackimage = '/reportImages/city.jpg' + footerlabel = "$($Data.TenantName) - Shadow AI Report" + } + } +} diff --git a/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippSharingReportTree.ps1 b/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippSharingReportTree.ps1 new file mode 100644 index 0000000000..1d2516a86f --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Tools/Reporting/Build-CippSharingReportTree.ps1 @@ -0,0 +1,138 @@ +function Build-CippSharingReportTree { + <# + .SYNOPSIS + Compose the SharePoint/OneDrive Sharing report as a component tree (server port of + SharingReportButton.jsx). + .DESCRIPTION + Pure composition from already-gathered sharing data. Returns @{ Blocks; Variables }: the + component nodes for ConvertTo-CippReportPdf and the cover/footer report variables. + .PARAMETER Data + Sharing data: summary (counts), links[], topRecipients[], topLibraries[]. + #> + [CmdletBinding()] + param([Parameter(Mandatory)][hashtable]$Data) + + $summary = if ($Data.summary) { $Data.summary } else { @{} } + # `?? @()` so a missing list is empty rather than @($null), which would render as one blank row. + $links = @($Data.links ?? @()) + $topRecipients = @($Data.topRecipients ?? @()) + $topLibraries = @($Data.topLibraries ?? @()) + function nz($v) { if ($null -eq $v) { 0 } else { [int]$v } } + function plural($c, $s, $p) { "$c $(if ($c -eq 1) { $s } else { if ($p) { $p } else { "${s}s" } })" } + function joinList($v) { if ($v -is [array]) { $v -join ', ' } else { [string]$v } } + + # Exposure grade (client assessExposure). + $score = (@( + if ((nz $summary.anonymousEditLinks) -gt 0) { 5 } + if ((nz $summary.neverExpiringAnonymous) -gt 0) { 3 } + if ((nz $summary.anonymousLinks) -gt 0) { 2 } + if ((nz $summary.folderShares) -gt 0) { 2 } + if ((nz $summary.externalLinks) -gt 0) { 1 } + ) | Measure-Object -Sum).Sum + $exposure = if ($score -ge 7) { 'High' } elseif ($score -ge 3) { 'Medium' } else { 'Low' } + $dangerC = '#742A2A'; $warnC = '#744210' + $sevColour = @{ High = $dangerC; Medium = $warnC; Low = '#22543D' }[$exposure] + + $canEdit = { param($r) (joinList $r.roles) -match 'write|owner' } + $anonEdit = @($links | Where-Object { $_.classification -eq 'Anonymous' -and (& $canEdit $_) }) + $neverExp = @($links | Where-Object { $_.classification -eq 'Anonymous' -and -not $_.expirationDateTime }) + $folderShares = @($links | Where-Object { $_.itemType -eq 'Folder' -and @('Anonymous', 'External') -contains $_.classification }) + $externalRows = @($links | Where-Object { $_.classification -eq 'External' }) + $locationOf = { param($r) "$(if ($r.siteName) { $r.siteName } elseif ($r.siteUrl) { $r.siteUrl } else { 'Unknown site' })$(if ($r.driveName) { " / $($r.driveName)" })" } + $expiryOf = { param($r) if ($r.expirationDateTime) { ([datetime]$r.expirationDateTime).ToString('M/d/yyyy') } else { 'Never' } } + + $blocks = [System.Collections.Generic.List[object]]::new() + + # -- Executive Summary -- + $blocks.Add((New-CippReportPage -Title 'Executive Summary' -Subtitle 'What has been shared, and how far it reaches')) + $blocks.Add((New-CippReportParagraph -Html ('

Sharing links are created by users on individual files and folders. They hand out access outside the permission structure an administrator sets on a site or library, they accumulate quietly as people work, and nothing prompts anyone to review them. This report covers what exists today across SharePoint and OneDrive in {0}.

' -f [System.Net.WebUtility]::HtmlEncode([string]$Data.TenantName)))) + $blocks.Add((New-CippReportStatRow -Stats @( + @{ value = (nz $summary.anonymousEditLinks); label = 'Anonymous & Editable'; colour = $(if ((nz $summary.anonymousEditLinks) -gt 0) { $dangerC }) } + @{ value = (nz $summary.neverExpiringAnonymous); label = 'Anonymous, No Expiry'; colour = $(if ((nz $summary.neverExpiringAnonymous) -gt 0) { $dangerC }) } + @{ value = (nz $summary.folderShares); label = 'Shared Folders'; colour = $(if ((nz $summary.folderShares) -gt 0) { $warnC }) } + @{ value = (nz $summary.externalRecipients); label = 'External Recipients'; colour = $(if ((nz $summary.externalRecipients) -gt 0) { $warnC }) } + ))) + $expText = switch ($exposure) { + 'High' { 'Content is reachable by people who cannot be identified. Anonymous links work for anyone holding them, with no sign-in and no record of use - and where those links also allow editing, changes are attributed to nobody. Treat the findings below as immediate remediation work.' } + 'Medium' { 'Sharing extends beyond the intended audience in places. Each finding below is individually manageable, but every open link widens what a single forwarded message can expose.' } + default { 'No high-risk sharing was found. Links are scoped and time-bounded. Continue reviewing periodically, since sharing accumulates as projects come and go.' } + } + $blocks.Add((New-CippReportAlertBox -Title "Sharing Exposure: $exposure" -Colour $sevColour -Content $expText)) + $blocks.Add((New-CippReportInfoBox -Title 'What was examined' -Content ("{0} sharing links and external shares across {1} SharePoint sites, {2} Teams-connected sites and {3} OneDrive accounts, covering {4} distinct shared items. Data is taken from the last completed sync, not read live." -f (nz $summary.totalLinks), (nz $summary.sharePointSites), (nz $summary.teamsSites), (nz $summary.oneDriveAccounts), (nz $summary.itemsShared)))) + $blocks.Add((New-CippReportInfoBox -Title 'What is not covered' -Content 'This report covers sharing links only. Permissions granted on a site or document library are a separate access path, governed differently, and are covered by the Permissions Report. A clean result here does not mean access is restricted - it means nothing has been shared out by link.')) + + # -- Findings -- + $blocks.Add((New-CippReportPage -Title 'Findings' -Subtitle 'Shares worth reviewing, most urgent first')) + $blocks.Add((New-CippReportHeading -Title 'Finding 1: Anonymous Links That Allow Editing')) + $blocks.Add((New-CippReportInfoBox -Title 'Why this matters' -Content 'An anonymous link works for anyone who holds it - no sign-in, no record of who used it. When that link also grants editing, anyone it has been forwarded to can change or delete the content, and the change is attributed to nobody. This is the only combination that allows untraceable modification.')) + if ($anonEdit.Count -gt 0) { + $blocks.Add((New-CippReportAlertBox -Title (plural $anonEdit.Count 'anonymous editable link') -Colour $dangerC -Content 'Revoke these, or downgrade them to view-only where the sharing is still needed.')) + $blocks.Add((New-CippReportTable -Columns @(@{ header = 'Item'; key = 'item'; width = 2.4 }, @{ header = 'Location'; key = 'location'; width = 2 }, @{ header = 'Expires'; key = 'expires'; width = 1 }) -Rows @($anonEdit | ForEach-Object { @{ item = $_.fileName; location = (& $locationOf $_); expires = (& $expiryOf $_) } }))) + } else { + $blocks.Add((New-CippReportClearBox -Title 'No anonymous editable links' -Content 'No anonymous link grants write access.')) + } + $blocks.Add((New-CippReportHeading -Title 'Finding 2: Anonymous Links That Never Expire')) + $blocks.Add((New-CippReportInfoBox -Title 'Why this matters' -Content 'A link with no expiry date stays live indefinitely, long after the reason for sharing has passed. Expiry is the only control that withdraws this access without somebody remembering to do it.')) + if ($neverExp.Count -gt 0) { + $blocks.Add((New-CippReportAlertBox -Title ("$(plural $neverExp.Count 'anonymous link') with no expiry") -Colour $warnC -Content 'Set a tenant-level default expiry so this cannot recur, then revoke the existing links that are no longer needed.')) + $blocks.Add((New-CippReportTable -Columns @(@{ header = 'Item'; key = 'item'; width = 2.4 }, @{ header = 'Location'; key = 'location'; width = 2 }, @{ header = 'Permission'; key = 'roles'; width = 1 }) -Rows @($neverExp | ForEach-Object { @{ item = $_.fileName; location = (& $locationOf $_); roles = (joinList $_.roles) } }))) + } else { + $blocks.Add((New-CippReportClearBox -Title 'All anonymous links expire' -Content 'Every anonymous link has an expiry date set.')) + } + + # -- Findings continued -- + $blocks.Add((New-CippReportPage -Title 'Findings (continued)' -Subtitle 'Reach and recipients')) + $blocks.Add((New-CippReportHeading -Title 'Finding 3: Shared Folders')) + $blocks.Add((New-CippReportInfoBox -Title 'Why this matters' -Content "Sharing a folder shares everything inside it, including anything added later. The recipient's access grows over time without anyone re-approving it, which is the main way a small share quietly becomes a large one.")) + if ($folderShares.Count -gt 0) { + $blocks.Add((New-CippReportAlertBox -Title ("$(plural $folderShares.Count 'folder') shared externally or anonymously") -Colour $warnC -Content 'Check what each folder holds now, not what it held when it was shared.')) + $blocks.Add((New-CippReportTable -Columns @(@{ header = 'Folder'; key = 'item'; width = 2.2 }, @{ header = 'Location'; key = 'location'; width = 2 }, @{ header = 'Audience'; key = 'audience'; width = 1.2 }) -Rows @($folderShares | ForEach-Object { @{ item = $_.fileName; location = (& $locationOf $_); audience = $_.classification } }))) + } else { + $blocks.Add((New-CippReportClearBox -Title 'No externally shared folders' -Content 'External and anonymous shares point at individual files rather than folders.')) + } + $blocks.Add((New-CippReportHeading -Title 'Finding 4: External Recipients')) + $blocks.Add((New-CippReportInfoBox -Title 'Why this matters' -Content 'Every external recipient is a person outside the organisation holding access that was granted individually, usually for a specific piece of work. Nothing withdraws it when that work ends.')) + if ($topRecipients.Count -gt 0) { + $blocks.Add((New-CippReportParagraph -Text ("{0} hold shared content, across {1}. The most frequent are listed below." -f (plural (nz $summary.externalRecipients) 'external identity' 'external identities'), (plural $externalRows.Count 'share')))) + $blocks.Add((New-CippReportTable -Limit 15 -Columns @(@{ header = 'Recipient'; key = 'recipient'; width = 3 }, @{ header = 'Shares'; key = 'shares'; width = 1 }) -Rows @($topRecipients | ForEach-Object { @{ recipient = $_.recipient; shares = "$($_.links)" } }))) + } else { + $blocks.Add((New-CippReportClearBox -Title 'No external recipients' -Content 'Nothing has been shared with an identity outside the organisation.')) + } + if ($topLibraries.Count -gt 0) { + $blocks.Add((New-CippReportHeading -Title 'Where Sharing Concentrates')) + $blocks.Add((New-CippReportParagraph -Text 'The libraries below account for the most sharing links. Concentration is not a problem in itself, but it shows where a review will have the most effect.')) + $blocks.Add((New-CippReportTable -Limit 10 -Columns @(@{ header = 'Library'; key = 'library'; width = 3 }, @{ header = 'Links'; key = 'links'; width = 1 }) -Rows @($topLibraries | ForEach-Object { @{ library = $_.library; links = "$($_.links)" } }))) + } + + # -- Recommendations -- + $blocks.Add((New-CippReportPage -Title 'Recommendations' -Subtitle 'What to do about the findings')) + $blocks.Add((New-CippReportHeading -Title 'Priority Actions')) + $blocks.Add((New-CippReportParagraph -Text 'Ordered by how much exposure each removes relative to the effort involved.')) + $blocks.Add((New-CippReportBullets -Items @( + @{ marker = '1.'; label = 'Revoke or downgrade anonymous editable links.'; text = 'Anonymous plus editable is the only combination allowing untracked changes. Switching to view-only keeps the sharing working while removing the ability to alter content.' } + @{ marker = '2.'; label = 'Set a default expiry for anonymous links.'; text = 'A tenant-level expiry policy stops never-expiring links being created again. This fixes the cause rather than the instances, so the problem does not rebuild.' } + @{ marker = '3.'; label = 'Review folder-level external shares.'; text = 'Share specific files where practical. A folder share keeps granting access to content that did not exist when it was approved.' } + @{ marker = '4.'; label = 'Review long-standing external recipients.'; text = 'Revoke shares belonging to finished engagements. External access has no natural end unless someone gives it one.' } + @{ marker = '5.'; label = 'Require sign-in where the audience is known.'; text = 'A link scoped to specific people records who opened it. Anonymous links cannot be attributed to anyone.' } + ))) + $blocks.Add((New-CippReportHeading -Title 'Keeping It That Way')) + $blocks.Add((New-CippReportBullets -Items @( + @{ label = 'Re-run this review regularly.'; text = 'Sharing accumulates continuously. A periodic review catches it while the list is still short.' } + @{ label = 'Set sharing defaults at tenant and site level.'; text = 'Default link type, expiry and permitted domains stop the riskiest shares being created at all, which is far cheaper than finding them later.' } + @{ label = 'Restrict anonymous links to view-only.'; text = 'If anonymous sharing is needed at all, removing the edit option eliminates untraceable changes without blocking the sharing itself.' } + @{ label = 'Password-protect sensitive shares.'; text = 'A password meaningfully narrows who can use a link that has been forwarded on.' } + ))) + + @{ + Blocks = @($blocks) + Variables = @{ + coverlabel = 'Data Sharing Review' + coversubtitle = "What has been shared out of SharePoint and OneDrive at $($Data.TenantName), who it reaches, and which of those shares are worth acting on." + covermeta = ('{0} sharing links / {1} items / {2} external recipients' -f (nz $summary.totalLinks), (nz $summary.itemsShared), (nz $summary.externalRecipients)) + covermetanote = "Sharing exposure: $exposure" + coverfooternote = 'Confidential - For Internal Use Only' + coverfallbackimage = '/reportImages/glasses.jpg' + footerlabel = "$($Data.TenantName) - SharePoint & OneDrive Sharing" + } + } +} diff --git a/backend/Modules/CIPPCore/Public/Tools/Reporting/Convert-CippExecStandardsToControls.ps1 b/backend/Modules/CIPPCore/Public/Tools/Reporting/Convert-CippExecStandardsToControls.ps1 new file mode 100644 index 0000000000..0cc80cad73 --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Tools/Reporting/Convert-CippExecStandardsToControls.ps1 @@ -0,0 +1,136 @@ +function Convert-CippExecStandardsToControls { + <# + .SYNOPSIS + Resolve the standards-comparison result into the Executive report's SecurityControls rows. + .DESCRIPTION + Server port of processStandardsData() from ExecutiveReportButton.jsx. Walks the first tenant's + standards.* entries in a ListStandardsCompare result, decides Compliant vs Review the same way the + client does (CurrentValue/ExpectedValue deep-equal with top-level keys sorted, else Value -eq $true), + and resolves each standard's display name / description / tags from the standards catalog + (Config\standards.json). Standards with no catalog entry fall back to a template display-name lookup + or a formatted key, exactly as the client does. Pure transform - no data gathering - so it is + unit-testable with sample data. + .PARAMETER Compare + The ListStandardsCompare Body: an array whose first element is the tenant's standards object. + .PARAMETER Templates + The listStandardTemplates Body, used only to resolve Intune/CA template GUIDs to display names. + .PARAMETER Catalog + The parsed Config\standards.json catalog (array of standard definitions). + #> + [CmdletBinding()] + param( + $Compare, + $Templates = @(), + $Catalog = @() + ) + + # JS parity: JSON.stringify of an object whose top-level keys are sorted. Nested objects keep their + # order, which ConvertTo-Json also does, so structurally-equal values serialize identically on both + # sides of the comparison. + function ConvertTo-CanonicalJson($Value) { + if ($null -eq $Value) { return 'null' } + if ($Value -is [hashtable] -or $Value -is [System.Collections.Specialized.OrderedDictionary]) { + $o = [ordered]@{} + foreach ($k in ($Value.Keys | Sort-Object)) { $o[$k] = $Value[$k] } + return ($o | ConvertTo-Json -Depth 20 -Compress) + } + if ($Value -is [System.Management.Automation.PSCustomObject]) { + $o = [ordered]@{} + foreach ($n in ($Value.PSObject.Properties.Name | Sort-Object)) { $o[$n] = $Value.$n } + return ($o | ConvertTo-Json -Depth 20 -Compress) + } + return ($Value | ConvertTo-Json -Depth 20 -Compress) + } + + $CompareArr = @($Compare) + if ($CompareArr.Count -eq 0 -or $null -eq $CompareArr[0]) { return @() } + $TenantData = $CompareArr[0] + + # Template GUID -> display name (Intune + Conditional Access templates, incl. Tags expansion). + $TemplateMap = @{} + foreach ($Template in @($Templates)) { + $Std = $Template.standards + if (-not $Std) { continue } + foreach ($ListName in @('IntuneTemplate', 'ConditionalAccessTemplate')) { + $Items = $Std.$ListName + if (-not $Items) { continue } + foreach ($Item in @($Items)) { + $Tl = $Item.TemplateList + if ($Tl -and $Tl.value -and $Tl.label) { $TemplateMap[([string]$Tl.value).ToLower()] = [string]$Tl.label } + $Tags = $Item.'TemplateList-Tags' + $TagTemplates = $null + if ($Tags) { + $TagTemplates = $Tags.addedFields.templates + if (-not $TagTemplates) { $TagTemplates = $Tags.rawData.templates } + } + foreach ($Et in @($TagTemplates)) { + if ($Et.GUID -and ($Et.displayName -or $Et.name)) { + $TemplateMap[([string]$Et.GUID).ToLower()] = [string]($Et.displayName ?? $Et.name) + } + } + } + } + } + + # Catalog by full standard name (e.g. 'standards.CopilotSettings'). + $CatalogMap = @{} + foreach ($C in @($Catalog)) { if ($C.name) { $CatalogMap[[string]$C.name] = $C } } + + $PropNames = if ($TenantData -is [hashtable]) { @($TenantData.Keys) } else { @($TenantData.PSObject.Properties.Name) } + $Out = [System.Collections.Generic.List[object]]::new() + + foreach ($Key in $PropNames) { + $KeyStr = [string]$Key + if (-not $KeyStr.StartsWith('standards.') -or $KeyStr -eq 'tenantFilter') { continue } + $Val = if ($TenantData -is [hashtable]) { $TenantData[$Key] } else { $TenantData.$Key } + + # Compliance: CurrentValue/ExpectedValue deep-equal (defined -> compared, null counts as defined), + # else Value -eq $true. + $HasCV = $false; $HasEV = $false; $CV = $null; $EV = $null; $ValueTrue = $false + if ($Val -is [hashtable]) { + $HasCV = $Val.ContainsKey('CurrentValue'); if ($HasCV) { $CV = $Val['CurrentValue'] } + $HasEV = $Val.ContainsKey('ExpectedValue'); if ($HasEV) { $EV = $Val['ExpectedValue'] } + if ($Val.ContainsKey('Value')) { $ValueTrue = ($Val['Value'] -eq $true) } + } elseif ($Val -is [System.Management.Automation.PSCustomObject]) { + $Props = $Val.PSObject.Properties.Name + $HasCV = $Props -contains 'CurrentValue'; if ($HasCV) { $CV = $Val.CurrentValue } + $HasEV = $Props -contains 'ExpectedValue'; if ($HasEV) { $EV = $Val.ExpectedValue } + if ($Props -contains 'Value') { $ValueTrue = ($Val.Value -eq $true) } + } + + $IsCompliant = $false + if ($HasCV -and $HasEV) { + $IsCompliant = ((ConvertTo-CanonicalJson $CV) -eq (ConvertTo-CanonicalJson $EV)) + } elseif ($ValueTrue) { + $IsCompliant = $true + } + $Status = if ($IsCompliant) { 'Compliant' } else { 'Review' } + + $Def = $CatalogMap[$KeyStr] + if ($Def) { + $Tags = if ($Def.tag -and @($Def.tag).Count -gt 0) { (@($Def.tag) | Select-Object -First 2) -join ', ' } else { 'No tags' } + $Name = [string]$Def.label + $Desc = if (-not [string]::IsNullOrWhiteSpace([string]$Def.executiveText)) { [string]$Def.executiveText } + elseif (-not [string]::IsNullOrWhiteSpace([string]$Def.helpText)) { [string]$Def.helpText } + else { 'No description available' } + } else { + $Tags = 'No tags' + $Desc = 'Security standard implementation' + if ($KeyStr -match '^standards\.IntuneTemplate\.([0-9a-fA-F-]+)') { + $Guid = $Matches[1] + $Name = $TemplateMap[$Guid.ToLower()] ?? ('Intune Template - ' + $Guid.Substring(0, [Math]::Min(8, $Guid.Length))) + } elseif ($KeyStr -match '^standards\.ConditionalAccessTemplate\.([0-9a-fA-F-]+)') { + $Guid = $Matches[1] + $Name = $TemplateMap[$Guid.ToLower()] ?? ('CA Template - ' + $Guid.Substring(0, [Math]::Min(8, $Guid.Length))) + } else { + $N = $KeyStr -replace '^standards\.', '' + $N = ([regex]::Replace($N, '([A-Z])', ' $1')).Trim() + $Name = if ($N.Length -gt 0) { $N.Substring(0, 1).ToUpper() + $N.Substring(1) } else { $N } + } + } + + $Out.Add(@{ name = $Name; description = $Desc; status = $Status; tags = $Tags }) + } + + return @($Out) +} diff --git a/backend/Modules/CIPPCore/Public/Tools/Reporting/Get-CippReportSankeyData.ps1 b/backend/Modules/CIPPCore/Public/Tools/Reporting/Get-CippReportSankeyData.ps1 new file mode 100644 index 0000000000..6de5a9a5d3 --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Tools/Reporting/Get-CippReportSankeyData.ps1 @@ -0,0 +1,194 @@ +function Get-CippReportSankeyData { + <# + .SYNOPSIS + Build the { nodes, links } for a dashboard-style sankey from its source collection rows. + .DESCRIPTION + Faithful server-side ports of the dashboard cards' sankey computations so a report renders the + SAME sankey a user sees on the dashboard. Each preset reads the raw collection rows and returns + @{ nodes = @(@{ id; label; nodeColor }, ...); links = @(@{ source; target; value }, ...) }. + + Ported one-to-one from the frontend cards (keep them in step): + mfaCoverage <- MFACard.jsx (MFAState) enabled -> registered/not -> enforcement + authMethods <- AuthMethodCard.jsx (MFAState) users -> factor class -> method breakdown + licenseAllocation <- LicenseCard.jsx (LicenseOverview) top-5 licence -> assigned/available + deviceCompliance <- managed devices (ManagedDevices) devices -> OS -> compliance state + + Nodes with no link are dropped by the renderer, so declaring the full node set is safe. + .PARAMETER Preset + Which dashboard sankey to build. + .PARAMETER Rows + The raw collection rows for that sankey's source (unfiltered; each builder filters as the card does). + .FUNCTIONALITY + Internal + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)][string]$Preset, + [AllowEmptyCollection()][object[]]$Rows = @() + ) + + $Nodes = [System.Collections.Generic.List[object]]::new() + $Links = [System.Collections.Generic.List[object]]::new() + $AddNode = { param($Id, $Label, $Colour) $Nodes.Add([ordered]@{ id = $Id; label = $Label; nodeColor = $Colour }) } + $AddLink = { param($Source, $Target, $Value) if ($Value -gt 0) { $Links.Add([ordered]@{ source = $Source; target = $Target; value = $Value }) } } + + switch ($Preset) { + 'mfaCoverage' { + # MFACard.jsx: enabled users -> MFA registered / Not registered -> how it is enforced. + $Enabled = @($Rows | Where-Object { $_.AccountEnabled -eq $true }) + $RegisteredUsers = 0; $NotRegisteredUsers = 0 + $RegCA = 0; $RegSD = 0; $RegPerUser = 0; $RegNone = 0 + $NotCA = 0; $NotSD = 0; $NotNone = 0 + foreach ($User in $Enabled) { + $HasRegistered = $User.MFARegistration -eq $true + $CoveredByCA = "$($User.CoveredByCA)".StartsWith('Enforced') + $CoveredBySD = $User.CoveredBySD -eq $true + $PerUser = "$($User.PerUser)" + $PerUserOn = $PerUser -eq 'enforced' -or $PerUser -eq 'enabled' + if ($HasRegistered -or $PerUserOn) { + $RegisteredUsers++ + if ($PerUserOn) { $RegPerUser++ } + elseif ($CoveredByCA) { $RegCA++ } + elseif ($CoveredBySD) { $RegSD++ } + else { $RegNone++ } + } else { + $NotRegisteredUsers++ + if ($CoveredByCA) { $NotCA++ } + elseif ($CoveredBySD) { $NotSD++ } + else { $NotNone++ } + } + } + & $AddNode 'Enabled users' 'Enabled users' 'hsl(28, 100%, 53%)' + & $AddNode 'MFA registered' 'MFA registered' 'hsl(99, 70%, 50%)' + & $AddNode 'Not registered' 'Not registered' 'hsl(39, 100%, 50%)' + & $AddNode 'CA policy' 'CA policy' 'hsl(99, 70%, 50%)' + & $AddNode 'Security defaults' 'Security defaults' 'hsl(140, 70%, 50%)' + & $AddNode 'Per-user MFA' 'Per-user MFA' 'hsl(200, 70%, 50%)' + & $AddNode 'No enforcement' 'No enforcement' 'hsl(0, 100%, 50%)' + & $AddLink 'Enabled users' 'MFA registered' $RegisteredUsers + & $AddLink 'Enabled users' 'Not registered' $NotRegisteredUsers + & $AddLink 'MFA registered' 'CA policy' $RegCA + & $AddLink 'MFA registered' 'Security defaults' $RegSD + & $AddLink 'MFA registered' 'Per-user MFA' $RegPerUser + & $AddLink 'MFA registered' 'No enforcement' $RegNone + & $AddLink 'Not registered' 'CA policy' $NotCA + & $AddLink 'Not registered' 'Security defaults' $NotSD + & $AddLink 'Not registered' 'No enforcement' $NotNone + } + 'authMethods' { + # AuthMethodCard.jsx: users -> single/multi factor, phishable vs phish-resistant, + breakdown. + $Enabled = @($Rows | Where-Object { $_.AccountEnabled -eq $true }) + $Phishable = @('mobilePhone', 'alternateMobilePhone', 'officePhone', 'email', 'microsoftAuthenticatorPush', 'softwareOneTimePasscode', 'hardwareOneTimePasscode') + $Passkey = @('fido2SecurityKey', 'passKeyDeviceBound', 'passKeyDeviceBoundAuthenticator', 'passKeyDeviceBoundWindowsHello', 'x509Certificate') + $PhishResistant = @($Passkey + 'windowsHelloForBusiness') + $SingleFactor = 0; $PhishableCount = 0; $PhishResistantCount = 0; $PerUserMFA = 0 + $PhoneCount = 0; $AuthenticatorCount = 0; $PasskeyCount = 0; $WhfbCount = 0 + foreach ($User in $Enabled) { + $Methods = @($User.MFAMethods | Where-Object { $null -ne $_ -and "$_" -ne '' }) + $PerUser = "$($User.PerUser)" + $PerUserOn = $PerUser -eq 'enforced' -or $PerUser -eq 'enabled' + $HasRegistered = $User.MFARegistration -eq $true + if ($PerUserOn -and -not $HasRegistered -and $Methods.Count -eq 0) { $PerUserMFA++; continue } + if (-not $HasRegistered -or $Methods.Count -eq 0) { $SingleFactor++; continue } + $HasPR = @($Methods | Where-Object { $PhishResistant -contains $_ }).Count -gt 0 + $HasPh = @($Methods | Where-Object { $Phishable -contains $_ }).Count -gt 0 + if ($HasPR) { + $PhishResistantCount++ + if (@($Methods | Where-Object { $Passkey -contains $_ }).Count -gt 0) { $PasskeyCount++ } + if ($Methods -contains 'windowsHelloForBusiness') { $WhfbCount++ } + } elseif ($HasPh) { + $PhishableCount++ + if (($Methods -contains 'mobilePhone') -or ($Methods -contains 'alternateMobilePhone') -or ($Methods -contains 'officePhone') -or ($Methods -contains 'email')) { $PhoneCount++ } + if (($Methods -contains 'microsoftAuthenticatorPush') -or ($Methods -contains 'softwareOneTimePasscode') -or ($Methods -contains 'hardwareOneTimePasscode')) { $AuthenticatorCount++ } + } else { + $PhishableCount++; $AuthenticatorCount++ + } + } + & $AddNode 'Users' 'Users' 'hsl(28, 100%, 53%)' + & $AddNode 'Single factor' 'Single factor' 'hsl(0, 100%, 50%)' + & $AddNode 'Multi factor' 'Multi factor' 'hsl(200, 70%, 50%)' + & $AddNode 'Phishable' 'Phishable' 'hsl(39, 100%, 50%)' + & $AddNode 'Phone' 'Phone' 'hsl(39, 100%, 45%)' + & $AddNode 'Authenticator' 'Authenticator' 'hsl(39, 100%, 55%)' + & $AddNode 'Phish resistant' 'Phish resistant' 'hsl(99, 70%, 50%)' + & $AddNode 'Passkey' 'Passkey' 'hsl(140, 70%, 50%)' + & $AddNode 'WHfB' 'WHfB' 'hsl(160, 70%, 50%)' + & $AddLink 'Users' 'Single factor' $SingleFactor + & $AddLink 'Users' 'Multi factor' $PerUserMFA + & $AddLink 'Users' 'Phishable' $PhishableCount + & $AddLink 'Users' 'Phish resistant' $PhishResistantCount + & $AddLink 'Phishable' 'Phone' $PhoneCount + & $AddLink 'Phishable' 'Authenticator' $AuthenticatorCount + & $AddLink 'Phish resistant' 'Passkey' $PasskeyCount + & $AddLink 'Phish resistant' 'WHfB' $WhfbCount + } + 'licenseAllocation' { + # LicenseCard.jsx: the top-5 licences by total, each fanning out to its own assigned/available. + $Top = @($Rows | Where-Object { ($_.TotalLicenses -as [int]) -gt 0 } | + Sort-Object -Property @{ Expression = { $_.TotalLicenses -as [int] }; Descending = $true } | + Select-Object -First 5) + $Index = 0 + foreach ($Lic in $Top) { + $Name = if ($Lic.License) { "$($Lic.License)" } elseif ($Lic.skuPartNumber) { "$($Lic.skuPartNumber)" } elseif ($Lic.SkuPartNumber) { "$($Lic.SkuPartNumber)" } else { 'Unknown License' } + $Short = if ($Name.Length -gt 30) { $Name.Substring(0, 27) + '...' } else { $Name } + $Assigned = ($Lic.CountUsed -as [int]); if ($null -eq $Assigned) { $Assigned = 0 } + $Available = ($Lic.CountAvailable -as [int]); if ($null -eq $Available) { $Available = 0 } + $NodeId = "$Index-$Short" + & $AddNode $NodeId $Short ('hsl({0}, 70%, 50%)' -f (210 + $Index * 30)) + if ($Assigned -gt 0) { + & $AddNode "$NodeId - Assigned" "$Short - Assigned" 'hsl(99, 70%, 50%)' + & $AddLink $NodeId "$NodeId - Assigned" $Assigned + } + if ($Available -gt 0) { + & $AddNode "$NodeId - Available" "$Short - Available" 'hsl(28, 100%, 53%)' + & $AddLink $NodeId "$NodeId - Available" $Available + } + $Index++ + } + } + 'deviceCompliance' { + # Managed devices -> operating system -> compliance state. Compliant green, non-compliant red, + # in-grace amber, anything else (unknown/error/conflict) grey. + $StateColour = { + param($State) + switch -Regex ("$State".ToLowerInvariant()) { + '^compliant$' { 'hsl(99, 70%, 50%)' } + '^noncompliant$' { 'hsl(0, 100%, 50%)' } + '^ingraceperiod$' { 'hsl(39, 100%, 50%)' } + default { 'hsl(220, 10%, 60%)' } + } + } + $OsOrder = [System.Collections.Generic.List[string]]::new() + $StateSeen = @{} + $OsTotals = @{} + $PairCounts = [ordered]@{} + foreach ($Device in $Rows) { + $Os = "$($Device.operatingSystem)"; if (-not $Os) { $Os = 'Unknown' } + $State = "$($Device.complianceState)"; if (-not $State) { $State = 'unknown' } + if (-not $OsOrder.Contains($Os)) { $OsOrder.Add($Os) } + $OsTotals[$Os] = ([int]$OsTotals[$Os]) + 1 + $Key = "$Os`n$State" + $PairCounts[$Key] = ([int]$PairCounts[$Key]) + 1 + } + if ($OsOrder.Count -gt 0) { + & $AddNode 'Managed devices' 'Managed devices' 'hsl(28, 100%, 53%)' + $OsIndex = 0 + foreach ($Os in $OsOrder) { + & $AddNode "os:$Os" $Os ('hsl({0}, 70%, 50%)' -f ((200 + $OsIndex * 35) % 360)) + & $AddLink 'Managed devices' "os:$Os" ([int]$OsTotals[$Os]) + $OsIndex++ + } + foreach ($Key in $PairCounts.Keys) { + $Parts = $Key -split "`n", 2 + $Os = $Parts[0]; $State = $Parts[1] + $StateId = "state:$State" + if (-not $StateSeen.ContainsKey($StateId)) { & $AddNode $StateId $State (& $StateColour $State); $StateSeen[$StateId] = $true } + & $AddLink "os:$Os" $StateId ([int]$PairCounts[$Key]) + } + } + } + default { } + } + + return @{ nodes = @($Nodes); links = @($Links) } +} diff --git a/backend/Modules/CIPPCore/Public/Tools/Reporting/Get-CippReportTableData.ps1 b/backend/Modules/CIPPCore/Public/Tools/Reporting/Get-CippReportTableData.ps1 new file mode 100644 index 0000000000..623e4cdec1 --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Tools/Reporting/Get-CippReportTableData.ps1 @@ -0,0 +1,69 @@ +function Get-CippReportTableData { + <# + .SYNOPSIS + Build flat table rows for a pre-built report table whose data lives in a nested shape a generic + single-collection dataSource cannot read. + .DESCRIPTION + Some report tables need data that is not one flat row per record - for example the Secure Score + controls, which live in a nested controlScores array inside a handful of daily snapshot rows. A + preset here reads the raw collection rows and returns an ordered list of flat rows (hashtables), + each key a field a table column can name. The report resolver feeds these rows through the same + column mapping every hand-built table uses, so the columns read their fields exactly as normal. + + Presets: + secureScoreFailing <- SecureScore the latest snapshot's controls that are not fully achieved + (scoreInPercentage < 100), worst first. + .PARAMETER Preset + Which table to build. + .PARAMETER Rows + The raw collection rows for that table's source. + .FUNCTIONALITY + Internal + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)][string]$Preset, + [AllowEmptyCollection()][object[]]$Rows = @() + ) + + $AsDouble = { param($Value) $n = "$Value" -as [double]; if ($null -ne $n) { $n } else { $null } } + $Pct = { param($Value) $n = & $AsDouble $Value; if ($null -eq $n) { '' } elseif ([math]::Round($n) -eq $n) { "$([int]$n)%" } else { "$([math]::Round($n, 1))%" } } + # Some control statuses arrive as HTML (implementationStatus); flatten to a short line of plain text. + $Plain = { + param([string]$Text) + if ([string]::IsNullOrWhiteSpace($Text)) { return '' } + $Clean = [regex]::Replace($Text, '<[^>]+>', ' ') + $Clean = [System.Net.WebUtility]::HtmlDecode($Clean) + $Clean = [regex]::Replace($Clean, '\s+', ' ').Trim() + if ($Clean.Length -gt 160) { $Clean = $Clean.Substring(0, 159).TrimEnd() + [char]0x2026 } + $Clean + } + + switch ($Preset) { + 'secureScoreFailing' { + # SecureScore is a series of daily snapshots; take the most recent one and read its per-control + # scores. A control that is not fully achieved (below 100%) is one worth listing. + if (@($Rows).Count -eq 0) { return @() } + $Latest = @($Rows | Sort-Object -Property @{ Expression = { [datetime]("$($_.createdDateTime)" -as [datetime]) } } -Descending | Select-Object -First 1) + if (-not $Latest) { $Latest = @($Rows)[-1] } + $Controls = @($Latest.controlScores) + if (@($Controls).Count -eq 0) { return @() } + $Failing = @($Controls | Where-Object { + $p = & $AsDouble $_.scoreInPercentage + $null -ne $p -and $p -lt 100 + }) + $Ordered = @($Failing | Sort-Object -Property @{ Expression = { [double](& $AsDouble $_.scoreInPercentage) } }, @{ Expression = { "$($_.controlName)" } }) + return @($Ordered | ForEach-Object { + [ordered]@{ + control = "$($_.controlName)" + category = "$($_.controlCategory)" + status = $(if ($_.implementationStatus) { (& $Plain "$($_.implementationStatus)") } else { "$($_.on)" }) + percent = (& $Pct $_.scoreInPercentage) + } + }) + } + default { + throw "Unknown report table preset '$Preset'." + } + } +} diff --git a/backend/Modules/CIPPCore/Public/Tools/Reporting/New-CippReportComponents.ps1 b/backend/Modules/CIPPCore/Public/Tools/Reporting/New-CippReportComponents.ps1 new file mode 100644 index 0000000000..9e9f84f6d7 --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Tools/Reporting/New-CippReportComponents.ps1 @@ -0,0 +1,159 @@ +# +# Report component builders - the PowerShell authoring layer for server-side reports. +# +# Each function returns one declarative component node (a hashtable) that the CIPPSharp component kit +# renders (see ConvertTo-CippReportPdf / [CIPP.Reporting.ReportPdf]). A report is composed of these +# and never hand-writes OfficeIMO or block JSON: gather data, map it to components, hand the array to +# ConvertTo-CippReportPdf. The node shapes mirror the block types the kit understands +# (page/blank/scorecard/richtable/richbullets/chart/infobox/alertbox/clearbox/hero/pagebreak). +# + +function New-CippReportPage { + # Opens a titled content page (a fixed report's ContentPage): its own header title + subtitle. + param([Parameter(Mandatory)][string]$Title, [string]$Subtitle) + $n = [ordered]@{ type = 'page'; title = $Title } + if ($Subtitle) { $n.subtitle = $Subtitle } + $n +} + +function New-CippReportPageBreak { + @{ type = 'pagebreak' } +} + +function New-CippReportHeading { + # A section heading on its own (renders the section title, no body). + param([Parameter(Mandatory)][string]$Title) + [ordered]@{ type = 'blank'; title = $Title; content = '' } +} + +function New-CippReportParagraph { + # Body copy. -Html passes raw HTML (bold/links); -Text wraps plain text. + # -Title adds a section heading above the paragraph. -Indent steps plain -Text in under a heading. + param([string]$Text, [string]$Html, [string]$Title, [switch]$Indent) + if ($Indent) { + $n = [ordered]@{ type = 'paragraphindent'; content = [string]$Text } + if ($Title) { $n.title = $Title } + return $n + } + if ($Html) { + $n = [ordered]@{ type = 'blank'; content = $Html } + } else { + $n = [ordered]@{ type = 'blank'; content = ('

{0}

' -f [System.Net.WebUtility]::HtmlEncode([string]$Text)) } + } + if ($Title) { $n.title = $Title } + $n +} + +function New-CippReportStatRow { + # A row of stat cards. -Stats: @( @{ value; label; caption; colour }, ... ). An empty row renders nothing. + param([string]$Title, [Parameter(Mandatory)][AllowEmptyCollection()][object[]]$Stats) + $n = [ordered]@{ type = 'scorecard'; stats = @($Stats) } + if ($Title) { $n.title = $Title } + $n +} + +function New-CippReportTable { + # A data table. -Columns: @( @{ header; key; width; bold; align; toneField }, ... ). -Rows: row objects. + param([string]$Title, [Parameter(Mandatory)][object[]]$Columns, [object[]]$Rows = @(), [int]$Limit = 25) + $n = [ordered]@{ type = 'richtable'; columns = @($Columns); rows = @($Rows); limit = $Limit } + if ($Title) { $n.title = $Title } + $n +} + +function New-CippReportBullets { + # Rich bullets. -Items: @( @{ label; text }, ... ) (label is optional bold prefix). + param([string]$Title, [Parameter(Mandatory)][object[]]$Items) + $n = [ordered]@{ type = 'richbullets'; items = @($Items) } + if ($Title) { $n.title = $Title } + $n +} + +function New-CippReportNote { + # A small italic aside (client Note / truncation line): "... and N more". + param([Parameter(Mandatory)][string]$Text) + [ordered]@{ type = 'note'; content = $Text } +} + +function New-CippReportChart { + # A chart: -Kind bar|donut|trend, -Data @( @{ label; value; colour }, ... ). Title shows in the frame. + # Empty data is allowed: the kit draws a "No data available" frame (a tenant with no mail, no risks...). + param([string]$Title, [ValidateSet('bar', 'donut', 'trend')][string]$Kind = 'bar', [Parameter(Mandatory)][AllowEmptyCollection()][object[]]$Data, [double]$Max, [string]$Caption, [string]$CentreLabel) + $n = [ordered]@{ type = 'chart'; chartKind = $Kind; chartData = @($Data) } + if ($Title) { $n.title = $Title } + if ($PSBoundParameters.ContainsKey('Max')) { $n.max = $Max } + if ($Caption) { $n.caption = $Caption } + if ($CentreLabel) { $n.centreLabel = $CentreLabel } + $n +} + +function New-CippReportProgress { + # A list of labelled progress bars (the client ProgressList). -Items: @( @{ label; value; max; display; colour }, ... ). + # Each renders a value/max data bar over a grey track - zero-safe (a 0 value draws an empty track), unlike a + # bar chart whose rounded bars collapse at zero. -Display overrides the "N%" label (e.g. a raw count). + param([string]$Title, [Parameter(Mandatory)][AllowEmptyCollection()][object[]]$Items) + $n = [ordered]@{ type = 'progress'; items = @($Items) } + if ($Title) { $n.title = $Title } + $n +} + +function New-CippReportInfoBox { + # A callout with a left accent stripe. -Tone ok|warn tints it; -Content is markdown, unless -Lines is + # set, in which case each '\n' line is kept as a tight line break (label/value detail lists). + param([Parameter(Mandatory)][string]$Title, [string]$Content, [ValidateSet('', 'ok', 'warn')][string]$Tone = '', [string]$Colour, [switch]$TintTitle, [switch]$Lines) + $n = [ordered]@{ type = 'infobox'; title = $Title; content = $Content } + if ($Tone) { $n.tone = $Tone } + if ($Colour) { $n.colour = $Colour } + if ($TintTitle) { $n.tintTitle = $true } + if ($Lines) { $n.lines = $true } + $n +} + +function New-CippReportInfoBoxColumns { + # A grid of callouts laid out -Columns per row (client Columns of InfoBoxes). -Items: + # @( @{ title; content; colour; tone; tintTitle }, ... ). Content is plain prose. + param([Parameter(Mandatory)][object[]]$Items, [int]$Columns = 2) + [ordered]@{ type = 'infoboxcolumns'; items = @($Items); columns = $Columns } +} + +function New-CippReportAlertBox { + # A warning callout. -Content is markdown, unless -Lines keeps each '\n' line as a tight line break. + param([Parameter(Mandatory)][string]$Title, [string]$Content, [string]$Colour, [switch]$Lines) + $n = [ordered]@{ type = 'alertbox'; title = $Title; content = $Content } + if ($Colour) { $n.colour = $Colour } + if ($Lines) { $n.lines = $true } + $n +} + +function New-CippReportClearBox { + param([Parameter(Mandatory)][string]$Title, [string]$Content, [switch]$Lines) + $n = [ordered]@{ type = 'clearbox'; title = $Title; content = $Content } + if ($Lines) { $n.lines = $true } + $n +} + +function New-CippReportHero { + # A full-bleed chapter divider. -Image is a data-URL cover photo; the big -Highlight sits over it. + param([string]$Image, [string]$Overtitle, [string]$Highlight, [string]$Headline, [string]$SubText, [string]$FooterText) + $n = [ordered]@{ type = 'hero' } + if ($Image) { $n.heroImage = $Image } + if ($Overtitle) { $n.overtitle = $Overtitle } + if ($Highlight) { $n.highlight = $Highlight } + if ($Headline) { $n.headline = $Headline } + if ($SubText) { $n.subText = $SubText } + if ($FooterText) { $n.footerText = $FooterText } + $n +} + +function New-CippReportSankey { + # A flow diagram (the dashboard CippSankey). -Nodes: @( @{ id; nodeColor; label }, ... ) - nodeColor + # accepts hex or hsl(); -Links: @( @{ source; target; value }, ... ) referencing node ids. Node columns + # and heights are derived from the link flow. -Height sets the plot height (default 240pt). Empty data + # draws a "No data available" frame. + param([string]$Title, [Parameter(Mandatory)][AllowEmptyCollection()][object[]]$Nodes, + [Parameter(Mandatory)][AllowEmptyCollection()][object[]]$Links, [string]$Caption, [double]$Height) + $n = [ordered]@{ type = 'sankey'; nodes = @($Nodes); links = @($Links) } + if ($Title) { $n.title = $Title } + if ($Caption) { $n.caption = $Caption } + if ($PSBoundParameters.ContainsKey('Height')) { $n.height = $Height } + $n +} diff --git a/backend/Modules/CIPPCore/Public/Tools/Resolve-CippReportDataToken.ps1 b/backend/Modules/CIPPCore/Public/Tools/Resolve-CippReportDataToken.ps1 new file mode 100644 index 0000000000..0f04fbaca2 --- /dev/null +++ b/backend/Modules/CIPPCore/Public/Tools/Resolve-CippReportDataToken.ps1 @@ -0,0 +1,447 @@ +function Resolve-CippReportDataToken { + <# + .SYNOPSIS + Resolve &data tokens& in report builder blocks against the reporting database. + .DESCRIPTION + A block's text can name reporting-database data with a token, and the value is read here on + the server when the report renders, so a scheduled run and a preview read the same data: + + &Users& the number of rows in that collection + &Users.displayName& the field's distinct values, comma-separated (the first 25) + &Devices.complianceState=compliant& the number of rows whose field has that value (* wildcards; != for the rest) + &Mailboxes.TotalItemSize:sum& a numeric field's sum, avg, min, max or count of rows carrying it + + Collection names are the reporting database's types, the same names the Database Data block + offers as sources; fields are case-insensitive and may reach into nested objects with dots. + A chart with a chartSource of &Devices.operatingSystem& gets one slice per value of that field; + a table with a dataSource of &Mailboxes& (a filter token works too) gets the rows, each column + reading the field it names (its `field`, else its header). A score card block with a statsSource + gets one card per value of the field (a count, or an aggregate of a numeric field) and a progress + block with an itemsSource one bar each, filled by its share of the total. A token that names + nothing is left as written, so the mistake shows in the report instead of silently blanking. + .PARAMETER Blocks + The enriched blocks, as objects or hashtables. Returned with the tokens replaced in place. + .PARAMETER TenantFilter + The tenant whose reporting database answers. + .FUNCTIONALITY + Internal + #> + [CmdletBinding()] + param( + [AllowEmptyCollection()][object[]]$Blocks = @(), + [Parameter(Mandatory = $true)][string]$TenantFilter + ) + + $Pattern = '(?:&|&)(?[A-Za-z0-9_-]+)(?:\.(?[A-Za-z0-9_.-]+))?(?:(?!=|=)(?[^&]*?))?(?::(?sum|avg|min|max|count))?(?:&|&)' + $MaxListed = 25 + $MaxSlices = 8 + $MaxPoints = 30 + $MaxRows = 200 + + # One read per collection per render; a collection the database does not hold reads as $null. The + # reserved collection 'TestResults' reads the in-app compliance test results (CippTestResults) instead + # of the reporting database, so a chart/table/flow can be driven by test data (count by Status, + # Category, Risk...) the same way it is driven by reporting collections. + $Cache = @{} + $RowsOf = { + param([string]$Type) + $Key = $Type.ToLowerInvariant() + if (-not $Cache.ContainsKey($Key)) { + $Rows = try { + if ($Key -eq 'testresults') { + @((Get-CIPPTestResults -TenantFilter $TenantFilter).TestResults) | Where-Object { $null -ne $_ } + } else { + @(New-CIPPDbRequest -TenantFilter $TenantFilter -Type $Type) | Where-Object { $null -ne $_ -and $_ -ne $false } + } + } catch { $null } + $Cache[$Key] = if ($null -eq $Rows) { $null } else { @($Rows) } + } + $Cache[$Key] + } + + # A field's values in one row, following a dotted path and flattening arrays along the way. + $ValueOf = { + param($Row, [string]$Path) + $Current = @($Row) + foreach ($Segment in $Path.Split('.')) { + $Current = @(foreach ($Item in $Current) { + if ($null -eq $Item) { continue } + if ($Item -is [System.Collections.IDictionary]) { + $Name = @($Item.Keys) | Where-Object { "$_" -ieq $Segment } | Select-Object -First 1 + if ($null -ne $Name) { $Item[$Name] } + } else { + $Property = $Item.PSObject.Properties | Where-Object { $_.Name -ieq $Segment } | Select-Object -First 1 + if ($Property) { $Property.Value } + } + }) + } + @($Current | Where-Object { $null -ne $_ -and "$_" -ne '' }) + } + + $RowMatches = { + param($Row, [string]$Field, [string]$Op, [string]$Wanted) + $Values = @(& $ValueOf $Row $Field | ForEach-Object { "$_" }) + $Hit = if ($Wanted.Contains('*')) { @($Values | Where-Object { $_ -like $Wanted }).Count -gt 0 } else { @($Values | Where-Object { $_ -ieq $Wanted }).Count -gt 0 } + if ($Op -eq '!=') { -not $Hit } else { $Hit } + } + + $FormatNumber = { param([double]$n) if ([math]::Round($n) -eq $n) { "$([long]$n)" } else { "$([math]::Round($n, 2))" } } + + # The text a token stands for; $null when its collection is unknown, so the token stays as written. + $Evaluate = { + param($Match) + $Type = $Match.Groups['type'].Value + $Field = $Match.Groups['field'].Value + $Op = $Match.Groups['op'].Value + $Wanted = $Match.Groups['value'].Value + $Agg = $Match.Groups['agg'].Value + $Rows = & $RowsOf $Type + if ($null -eq $Rows) { return $null } + if (-not $Field) { return "$($Rows.Count)" } + if ($Op) { return "$(@($Rows | Where-Object { & $RowMatches $_ $Field $Op $Wanted }).Count)" } + $Values = @(foreach ($Row in $Rows) { & $ValueOf $Row $Field }) + if ($Agg) { + if ($Agg -eq 'count') { return "$($Values.Count)" } + $Numbers = @($Values | ForEach-Object { $_ -as [double] } | Where-Object { $null -ne $_ }) + if ($Numbers.Count -eq 0) { return '0' } + $Measured = $Numbers | Measure-Object -Sum -Average -Minimum -Maximum + $Aggregate = switch ($Agg) { 'sum' { $Measured.Sum } 'avg' { $Measured.Average } 'min' { $Measured.Minimum } default { $Measured.Maximum } } + return (& $FormatNumber $Aggregate) + } + $Distinct = @($Values | ForEach-Object { "$_" } | Sort-Object -Unique) + if ($Distinct.Count -le $MaxListed) { return ($Distinct -join ', ') } + return (($Distinct | Select-Object -First $MaxListed) -join ', ') + " and $($Distinct.Count - $MaxListed) more" + } + + # Replace every token in a string. A string that is one token and resolves to a number becomes a + # number, so a progress bar's value or a chart point can be a token too. + $ReplaceIn = { + param([string]$Text) + if ($Text -notmatch '&') { return $Text } + $Whole = [regex]::Match($Text.Trim(), "^$Pattern$") + $Result = [regex]::Replace($Text, $Pattern, [System.Text.RegularExpressions.MatchEvaluator] { + param($m) + $Value = & $Evaluate $m + if ($null -eq $Value) { $m.Value } else { $Value } + }) + if ($Whole.Success -and $Result.Trim() -match '^-?\d+(\.\d+)?$') { return [double]$Result.Trim() } + $Result + } + + $SetProperty = { + param($Target, [string]$Name, $Value) + if ($Target -is [System.Collections.IDictionary]) { $Target[$Name] = $Value } else { $Target | Add-Member -NotePropertyName $Name -NotePropertyValue $Value -Force } + } + + # Walk a value: strings are resolved, lists and objects walked, everything else kept. + $Walk = $null + $Walk = { + param($Value) + if ($Value -is [string]) { return (& $ReplaceIn $Value) } + if ($Value -is [System.Collections.IDictionary]) { + foreach ($Name in @($Value.Keys)) { $Value[$Name] = & $Walk $Value[$Name] } + return $Value + } + if ($Value -is [array] -or $Value -is [System.Collections.IList]) { + return , @(foreach ($Item in $Value) { & $Walk $Item }) + } + if ($Value -is [System.Management.Automation.PSCustomObject]) { + foreach ($Property in @($Value.PSObject.Properties)) { $Value.($Property.Name) = & $Walk $Property.Value } + return $Value + } + $Value + } + + $ParseToken = { param([string]$Text) $m = [regex]::Match("$Text".Trim(), "^$Pattern$"); if ($m.Success) { $m } } + + # A chart or table source as the builder's picker saves it - { type; field; filter = { field; op; + # value } } - or as a token; either way @{ type; field; filter }, with filter $null when there is none. + $SourceOf = { + param($Source) + if ($null -eq $Source) { return $null } + if ($Source -is [string]) { + $m = & $ParseToken $Source + if (-not $m) { return $null } + $Field = $m.Groups['field'].Value + if ($m.Groups['op'].Value) { + return @{ type = $m.Groups['type'].Value; field = $null; filter = @{ field = $Field; op = $m.Groups['op'].Value; value = $m.Groups['value'].Value } } + } + return @{ type = $m.Groups['type'].Value; field = $(if ($Field) { $Field }); filter = $null } + } + if (-not $Source.type) { return $null } + $Filter = $Source.filter + $Spec = if ($Filter -and $Filter.field -and $Filter.op) { @{ field = "$($Filter.field)"; op = "$($Filter.op)"; value = "$($Filter.value)" } } + @{ + type = "$($Source.type)" + field = $(if ($Source.field) { "$($Source.field)" }) + # a numeric field to plot instead of counting rows; aggregate combines rows sharing a label + valueField = $(if ($Source.valueField -and "$($Source.valueField)" -ne '__count') { "$($Source.valueField)" }) + aggregate = $(if ($Source.aggregate -and @('sum', 'avg', 'max', 'min') -contains "$($Source.aggregate)") { "$($Source.aggregate)" }) + # divide plotted values by this before drawing, so bytes can be shown as GB and so on + scale = $(if (($Source.scale -as [double]) -gt 0) { [double]$Source.scale }) + filter = $Spec + } + } + $RowsFor = { + param($Spec) + $Rows = & $RowsOf $Spec.type + if ($null -eq $Rows) { return $null } + if ($Spec.filter) { $Rows = @($Rows | Where-Object { & $RowMatches $_ $Spec.filter.field $Spec.filter.op $Spec.filter.value }) } + , @($Rows) + } + + # Label/value pairs from a source, for score cards and progress bars: one entry per distinct value + # of the field (a count of rows, or an aggregate of a numeric field), or a single figure when no + # field is named. Sorted by value and capped so a row of cards or bars stays readable. + $CardsFrom = { + param($Spec, $Rows, [string]$SingleLabel) + $Field = $Spec.field + $Aggregate = { param($Numbers) if (@($Numbers).Count -eq 0) { return 0 } + $Nums = if ($Spec.scale) { @($Numbers | ForEach-Object { $_ / $Spec.scale }) } else { @($Numbers) } + $Measured = $Nums | Measure-Object -Sum -Average -Minimum -Maximum + switch ($Spec.aggregate) { 'avg' { [math]::Round($Measured.Average, 2) } 'min' { $Measured.Minimum } 'max' { $Measured.Maximum } default { $Measured.Sum } } } + $Pairs = if ($Spec.valueField) { + if ($Field) { + @($Rows | Group-Object { @(& $ValueOf $_ $Field | ForEach-Object { "$_" }) | Select-Object -First 1 } | ForEach-Object { + $Label = "$($_.Name)" + if (-not $Label) { return } + $Numbers = @($_.Group | ForEach-Object { @(& $ValueOf $_ $Spec.valueField | ForEach-Object { $_ -as [double] } | Where-Object { $null -ne $_ }) | Select-Object -First 1 } | Where-Object { $null -ne $_ }) + if ($Numbers.Count -eq 0) { return } + @{ label = $Label; value = [double](& $Aggregate $Numbers) } + }) + } else { + $Numbers = @($Rows | ForEach-Object { @(& $ValueOf $_ $Spec.valueField | ForEach-Object { $_ -as [double] } | Where-Object { $null -ne $_ }) | Select-Object -First 1 } | Where-Object { $null -ne $_ }) + @(@{ label = $(if ($SingleLabel) { $SingleLabel } else { $Spec.type }); value = [double](& $Aggregate $Numbers) }) + } + } elseif ($Field) { + $Groups = @(foreach ($Row in $Rows) { @(& $ValueOf $Row $Field | ForEach-Object { "$_" }) }) | Group-Object { $_.ToLowerInvariant() } + @($Groups | ForEach-Object { @{ label = "$($_.Group[0])"; value = [double]$_.Count } }) + } else { + @(@{ label = $(if ($SingleLabel) { $SingleLabel } else { $Spec.type }); value = [double]$Rows.Count }) + } + @($Pairs | Sort-Object -Property @{ Expression = { $_.value }; Descending = $true }, @{ Expression = { $_.label } } | Select-Object -First $MaxSlices) + } + + foreach ($Block in @($Blocks)) { + if ($null -eq $Block) { continue } + $Type = "$($Block.type)" + + # A chart drawn from the data. Counting rows: one slice per distinct value of the field, the long + # tail as Other, or a single counted slice when no field was picked. Plotting a field's value: + # one point per row labelled by the field - chronological when the labels are dates, which is + # how a Secure Score trend reads - or, with an aggregate, one point per label. + if ($Type -eq 'chart' -and $Block.chartSource) { + $Spec = & $SourceOf $Block.chartSource + $Rows = if ($Spec) { & $RowsFor $Spec } + if ($Spec -and $null -ne $Rows) { + $Field = $Spec.field + $Points = if ($Spec.valueField) { + $Series = @(foreach ($Row in $Rows) { + $Number = @(& $ValueOf $Row $Spec.valueField | ForEach-Object { $_ -as [double] } | Where-Object { $null -ne $_ }) | Select-Object -First 1 + if ($null -eq $Number) { continue } + if ($Spec.scale) { $Number = [double]$Number / $Spec.scale } + $Raw = if ($Field) { @(& $ValueOf $Row $Field | ForEach-Object { "$_" }) | Select-Object -First 1 } else { $null } + $Date = [datetime]::MinValue + $IsDate = $null -ne $Raw -and [datetime]::TryParse("$Raw", [cultureinfo]::InvariantCulture, [System.Globalization.DateTimeStyles]::AssumeUniversal, [ref]$Date) + @{ raw = $Raw; value = [double]$Number; date = $(if ($IsDate) { $Date }) } + }) + if ($Spec.aggregate) { + $Combined = @($Series | Group-Object { "$($_.raw)".ToLowerInvariant() } | ForEach-Object { + $Measured = @($_.Group | ForEach-Object { $_.value }) | Measure-Object -Sum -Average -Maximum -Minimum + $Figure = switch ($Spec.aggregate) { 'avg' { [math]::Round($Measured.Average, 2) } 'max' { $Measured.Maximum } 'min' { $Measured.Minimum } default { $Measured.Sum } } + @{ label = "$($_.Group[0].raw)"; value = [double]$Figure } + }) + @($Combined | Sort-Object -Property @{ Expression = { $_.value }; Descending = $true }, @{ Expression = { $_.label } } | Select-Object -First $MaxSlices) + } elseif ($Series.Count -gt 0 -and @($Series | Where-Object { $null -ne $_.date }).Count -eq $Series.Count) { + $Ordered = @($Series | Sort-Object -Property { $_.date } | Select-Object -Last $MaxPoints) + $Format = if (@($Ordered | ForEach-Object { $_.date.Year } | Select-Object -Unique).Count -gt 1) { 'MMM d yyyy' } else { 'MMM d' } + @($Ordered | ForEach-Object { @{ label = $_.date.ToString($Format, [cultureinfo]::InvariantCulture); value = $_.value } }) + } else { + @($Series | Select-Object -Last $MaxPoints | ForEach-Object { @{ label = $(if ($null -ne $_.raw) { "$($_.raw)" } else { '' }); value = $_.value } }) + } + } elseif ($Field) { + $Groups = @(foreach ($Row in $Rows) { @(& $ValueOf $Row $Field | ForEach-Object { "$_" }) }) | Group-Object { $_.ToLowerInvariant() } | Sort-Object -Property @{ Expression = 'Count'; Descending = $true }, @{ Expression = 'Name'; Descending = $false } + $Blank = @($Rows | Where-Object { @(& $ValueOf $_ $Field).Count -eq 0 }).Count + $Top = @($Groups | Select-Object -First $MaxSlices | ForEach-Object { @{ label = $_.Group[0]; value = $_.Count } }) + $Rest = @($Groups | Select-Object -Skip $MaxSlices | Measure-Object -Property Count -Sum).Sum + @($Top; if ($Rest -gt 0) { @{ label = 'Other'; value = [int]$Rest } }; if ($Blank -gt 0) { @{ label = '(blank)'; value = $Blank } }) + } else { + @(@{ label = $(if ($Block.title) { "$($Block.title)" } else { $Spec.type }); value = $Rows.Count }) + } + & $SetProperty $Block 'chartData' @($Points) + } + } + + # A table filled from the data: the rows (the ones the condition keeps), each column reading + # the field it names. A dataSource with a preset instead calls a derived builder for data that a + # flat single-collection read cannot express (e.g. the Secure Score controls, nested per snapshot); + # either way the same column mapping fills the table. + if ($Type -eq 'richtable' -and $Block.dataSource) { + $TablePreset = "$($Block.dataSource.preset)" + $Rows = if ($TablePreset) { + $Source = & $RowsOf "$($Block.dataSource.type)" + if ($null -ne $Source) { try { @(Get-CippReportTableData -Preset $TablePreset -Rows @($Source)) } catch { $null } } + } else { + $Spec = & $SourceOf $Block.dataSource + if ($Spec) { & $RowsFor $Spec } + } + if ($null -ne $Rows) { + $Columns = @($Block.columns) + $TableRows = @(foreach ($Row in (@($Rows) | Select-Object -First $MaxRows)) { + $Cells = [ordered]@{} + foreach ($Column in $Columns) { + $Key = "$($Column.key)" + $From = if ($Column.field) { "$($Column.field)" } else { "$($Column.header)" } + $Cells[$Key] = (@(& $ValueOf $Row $From | ForEach-Object { "$_" }) -join ', ') + } + $Cells + }) + & $SetProperty $Block 'rows' @($TableRows) + if (-not $Block.limit) { & $SetProperty $Block 'limit' $MaxRows } + } + } + + # Score cards drawn from the data: one card per distinct value of the field, the figure a count + # of rows (or an aggregate of a numeric field). Manual stats stay hand-typed and can use tokens. + if ($Type -eq 'scorecard' -and $Block.statsSource) { + $Spec = & $SourceOf $Block.statsSource + $Rows = if ($Spec) { & $RowsFor $Spec } + if ($Spec -and $null -ne $Rows) { + $Cards = @(& $CardsFrom $Spec @($Rows) "$($Block.title)" | ForEach-Object { @{ value = (& $FormatNumber $_.value); label = $_.label } }) + if ($Cards.Count -gt 0) { & $SetProperty $Block 'stats' @($Cards) } + } + } + + # Progress bars drawn from the data: one bar per distinct value of the field, filled by its share + # of the total (counting rows) or of the largest bar (aggregating a numeric field). + if ($Type -eq 'progress' -and $Block.itemsSource) { + $Spec = & $SourceOf $Block.itemsSource + $Rows = if ($Spec) { & $RowsFor $Spec } + if ($Spec -and $null -ne $Rows) { + $Bars = @(& $CardsFrom $Spec @($Rows) "$($Block.title)") + if ($Bars.Count -gt 0) { + $Max = if ($Spec.valueField) { [double](@($Bars | ForEach-Object { $_.value }) | Measure-Object -Maximum).Maximum } else { [double]@($Rows).Count } + if ($Max -le 0) { $Max = [double](@($Bars | ForEach-Object { $_.value }) | Measure-Object -Maximum).Maximum } + $Items = @($Bars | ForEach-Object { @{ label = $_.label; value = $_.value; max = $Max } }) + & $SetProperty $Block 'items' @($Items) + } + } + } + + # A sankey (flow diagram) drawn from the data. Three shapes: + # preset - a faithful server-side port of a dashboard sankey (MFA coverage, auth methods, + # licence allocation, device compliance); Get-CippReportSankeyData does the exact + # per-sankey computation the dashboard card does, so the report matches the dashboard. + # measures - one category field on the left, plus numeric measure fields, each a right-hand + # node; every row adds category -> measureLabel weighted by that field. + # flow - an ordered list of categorical fields; each adjacent pair (a, b) becomes a link + # a -> b weighted by the number of rows (or the sum of valueField). Node ids are + # namespaced by stage so a value shared between two columns does not fold/cycle. + if ($Type -eq 'sankey' -and $Block.sankeySource) { + $Sankey = $Block.sankeySource + $CollectionType = "$($Sankey.type)" + $Rows = if ($CollectionType) { & $RowsOf $CollectionType } + if ($null -ne $Rows -and $Sankey.preset) { + # Faithful dashboard sankey - the ported card logic owns the whole {nodes, links}. + $Built = try { Get-CippReportSankeyData -Preset "$($Sankey.preset)" -Rows @($Rows) } catch { $null } + if ($Built -and @($Built.links).Count -gt 0) { + & $SetProperty $Block 'nodes' @($Built.nodes) + & $SetProperty $Block 'links' @($Built.links) + } + } elseif ($null -ne $Rows) { + $SankeyFilter = $Sankey.filter + if ($SankeyFilter -and $SankeyFilter.field -and $SankeyFilter.op) { + $Rows = @($Rows | Where-Object { & $RowMatches $_ "$($SankeyFilter.field)" "$($SankeyFilter.op)" "$($SankeyFilter.value)" }) + } + $NodeOrder = [System.Collections.Generic.List[string]]::new() + $NodeLabel = @{} + $NodeColour = @{} + $LinkValue = [ordered]@{} + $Palette = { param([int]$Index) 'hsl({0}, 70%, 50%)' -f ((205 + $Index * 37) % 360) } + $AddNode = { + param([string]$Id, [string]$Label, [string]$Colour) + if (-not $NodeLabel.ContainsKey($Id)) { + $NodeOrder.Add($Id) + $NodeLabel[$Id] = $Label + $NodeColour[$Id] = if ($Colour) { $Colour } else { & $Palette ($NodeOrder.Count - 1) } + } + } + $AddLink = { + param([string]$Source, [string]$Target, [double]$Value) + $Key = "$Source`n$Target" + $LinkValue[$Key] = ([double]($LinkValue[$Key]) + $Value) + } + + if ("$($Sankey.mode)" -eq 'measures') { + $CategoryField = "$($Sankey.field)" + $Measures = @($Sankey.measures) + $Limit = if (($Sankey.limit -as [int]) -gt 0) { [int]$Sankey.limit } else { 8 } + # Rank categories by their total across all measures, keep the top N. + $CategoryTotals = @{} + foreach ($Row in $Rows) { + $Category = @(& $ValueOf $Row $CategoryField | ForEach-Object { "$_" }) | Select-Object -First 1 + if (-not $Category) { continue } + foreach ($Measure in $Measures) { + $Number = @(& $ValueOf $Row "$($Measure.field)" | ForEach-Object { $_ -as [double] } | Where-Object { $null -ne $_ }) | Select-Object -First 1 + if ($null -ne $Number) { $CategoryTotals[$Category] = ([double]($CategoryTotals[$Category]) + [double]$Number) } + } + } + $KeepCategories = @($CategoryTotals.GetEnumerator() | Sort-Object -Property Value -Descending | Select-Object -First $Limit -ExpandProperty Key) + foreach ($Row in $Rows) { + $Category = @(& $ValueOf $Row $CategoryField | ForEach-Object { "$_" }) | Select-Object -First 1 + if (-not $Category -or $Category -notin $KeepCategories) { continue } + & $AddNode $Category $Category $null + foreach ($Measure in $Measures) { + $Label = if ($Measure.label) { "$($Measure.label)" } else { "$($Measure.field)" } + $Number = @(& $ValueOf $Row "$($Measure.field)" | ForEach-Object { $_ -as [double] } | Where-Object { $null -ne $_ }) | Select-Object -First 1 + if ($null -eq $Number -or $Number -le 0) { continue } + & $AddNode "measure:$Label" $Label "$($Measure.colour)" + & $AddLink $Category "measure:$Label" ([double]$Number) + } + } + } else { + # flow: an ordered list of categorical fields; adjacent pairs become links. Node ids are + # namespaced by stage index so the same value in two columns stays two nodes. + $Fields = @($Sankey.fields | Where-Object { $_ } | ForEach-Object { "$_" }) + $ValueField = if ($Sankey.valueField) { "$($Sankey.valueField)" } + if ($Fields.Count -ge 2) { + foreach ($Row in $Rows) { + $Weight = if ($ValueField) { + @(& $ValueOf $Row $ValueField | ForEach-Object { $_ -as [double] } | Where-Object { $null -ne $_ }) | Select-Object -First 1 + } else { 1 } + if ($null -eq $Weight -or $Weight -le 0) { continue } + $Stages = @(for ($si = 0; $si -lt $Fields.Count; $si++) { + $Value = @(& $ValueOf $Row $Fields[$si] | ForEach-Object { "$_" }) | Select-Object -First 1 + if (-not $Value) { $Value = '(blank)' } + [pscustomobject]@{ Id = ('{0}:{1}' -f $si, $Value); Label = "$Value" } + }) + foreach ($Stage in $Stages) { & $AddNode $Stage.Id $Stage.Label $null } + for ($i = 0; $i -lt $Stages.Count - 1; $i++) { & $AddLink $Stages[$i].Id $Stages[$i + 1].Id ([double]$Weight) } + } + } + } + + if ($NodeOrder.Count -gt 0 -and $LinkValue.Count -gt 0) { + $Nodes = @(foreach ($Id in $NodeOrder) { @{ id = $Id; label = $NodeLabel[$Id]; nodeColor = $NodeColour[$Id] } }) + $Links = @(foreach ($Key in $LinkValue.Keys) { + $Parts = $Key -split "`n", 2 + @{ source = $Parts[0]; target = $Parts[1]; value = $LinkValue[$Key] } + }) + & $SetProperty $Block 'nodes' @($Nodes) + & $SetProperty $Block 'links' @($Links) + } + } + } + + # Every other string on the block, its rows and its items. + if ($Block -is [System.Collections.IDictionary]) { + foreach ($Name in @($Block.Keys)) { if ($Name -notin 'chartSource', 'dataSource', 'sankeySource', 'statsSource', 'itemsSource') { $Block[$Name] = & $Walk $Block[$Name] } } + } else { + foreach ($Property in @($Block.PSObject.Properties)) { if ($Property.Name -notin 'chartSource', 'dataSource', 'sankeySource', 'statsSource', 'itemsSource') { $Block.($Property.Name) = & $Walk $Property.Value } } + } + } + + # Unrolled, not wrapped: callers collect with @(), and a wrapped array would reach them as one + # element holding every block - which the renderer then draws as nothing at all. + return $Blocks +} diff --git a/backend/Modules/CIPPDB/Public/DBCache/Set-CIPPDBCacheMailTrafficSummary.ps1 b/backend/Modules/CIPPDB/Public/DBCache/Set-CIPPDBCacheMailTrafficSummary.ps1 new file mode 100644 index 0000000000..1fd59d8c71 --- /dev/null +++ b/backend/Modules/CIPPDB/Public/DBCache/Set-CIPPDBCacheMailTrafficSummary.ps1 @@ -0,0 +1,60 @@ +function Set-CIPPDBCacheMailTrafficSummary { + <# + .SYNOPSIS + Caches Exchange Online mail traffic summary (top senders and recipients by message volume) + + .DESCRIPTION + Runs Get-MailTrafficSummaryReport for the TopMailSender and TopMailRecipient categories over a + 30-day window and stores one row per address as { category, name, count, windowDays } under the + MailTrafficSummary type. This is the same data the live Mail Flow Statistics page reads, cached so + a scheduled or pre-built report (mailboxes by messaging volume) can use it without a live EXO call. + + .PARAMETER TenantFilter + The tenant to cache mail traffic summary for + + .PARAMETER QueueId + The queue ID to update with total tasks (optional) + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$TenantFilter, + [string]$QueueId + ) + + try { + Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Caching mail traffic summary' -sev Debug + + $WindowDays = 30 + $StartDate = (Get-Date).AddDays(-$WindowDays).ToUniversalTime().ToString('s') + $EndDate = (Get-Date).ToUniversalTime().ToString('s') + + # Both categories in one EXO batch; OperationGuid tags each returned row with the category it + # came from (the report rows do not otherwise say). C1 is the address/name, C2 the message count. + $Batch = @('TopMailSender', 'TopMailRecipient') | ForEach-Object { + @{ + CmdletInput = @{ CmdletName = 'Get-MailTrafficSummaryReport'; Parameters = @{ Category = $_; StartDate = $StartDate; EndDate = $EndDate } } + OperationGuid = $_ + } + } + $Summary = New-ExoBulkRequest -tenantid $TenantFilter -cmdletArray @($Batch) -useSystemMailbox $true + + $Rows = @(foreach ($Entry in @($Summary)) { + if ($Entry.error -or [string]::IsNullOrWhiteSpace("$($Entry.C1)")) { continue } + [PSCustomObject]@{ + category = "$($Entry.OperationGuid)" + name = "$($Entry.C1)" + count = [int]($Entry.C2) + windowDays = $WindowDays + } + }) + + if ($Rows.Count -gt 0) { + $Rows | Add-CIPPDbItem -TenantFilter $TenantFilter -Type 'MailTrafficSummary' -AddCount + } + Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message "Cached mail traffic summary successfully ($($Rows.Count) rows)" -sev Debug + + } catch { + Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message "Failed to cache mail traffic summary: $($_.Exception.Message)" -sev Error + } +} diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDBCache.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDBCache.ps1 index 5e9407e172..2d6191661c 100644 --- a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDBCache.ps1 +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDBCache.ps1 @@ -22,6 +22,7 @@ function Invoke-ListDBCache { get an estate-wide inventory and per-tenant cache freshness in one call. Pass a type alongside it to restrict the result to a single collection. + Use type=_shape for every collection's row count and fields (recorded when the cache was written). Use type=_availableTypes to discover which cache collections exist for a given tenant. Omitting the type parameter also returns the available types. @@ -120,6 +121,27 @@ function Invoke-ListDBCache { }) } + # type=_shape: every collection with its row count and the fields (and types) its rows were seen + # to carry, as recorded when the cache was written. What the report builder offers to pick from. + if ($Type -eq '_shape') { + $ShapeRows = @(Get-CIPPDbItem -CountsOnly -IncludeShape -TenantFilter $Tenant) + if ($null -ne $AllowedDomains) { + $ShapeRows = @($ShapeRows | Where-Object { $AllowedDomains.Contains([string]$_.PartitionKey) }) + } + $Shapes = @($ShapeRows | Sort-Object -Property RowKey | ForEach-Object { + $Fields = try { @((ConvertFrom-Json -InputObject "$($_.Shape)" -ErrorAction Stop).fields) } catch { @() } + [PSCustomObject]@{ + Type = $_.RowKey -replace '-Count$', '' + Count = $_.DataCount + Fields = @($Fields | Where-Object { $_.name } | ForEach-Object { [PSCustomObject]@{ name = [string]$_.name; type = [string]$_.type } }) + } + }) + return ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{ Results = $Shapes } + }) + } + if (-not $Type -or $Type -eq '_availableTypes') { $TypeRows = @(Get-CIPPDbItem -CountsOnly -TenantFilter $Tenant) if ($null -ne $AllowedDomains) { diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecBrandingSettings.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecBrandingSettings.ps1 index 886a547bcd..fb53d8ef32 100644 --- a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecBrandingSettings.ps1 +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecBrandingSettings.ps1 @@ -240,6 +240,19 @@ Function Invoke-ExecBrandingSettings { } } + # Which of the tenant's names a report prints: alias (the name CIPP shows), name (the + # Microsoft 365 organisation name) or domain (the default domain). + if (-not $ErrorMessage -and $Request.Body.PSObject.Properties.Name -contains 'tenantLabel') { + $TenantLabel = "$($Request.Body.tenantLabel)" + if (@('alias', 'name', 'domain') -notcontains $TenantLabel) { + $StatusCode = [HttpStatusCode]::BadRequest + $ErrorMessage = 'Error: tenantLabel must be alias, name or domain.' + } else { + $BrandingConfig | Add-Member -MemberType NoteProperty -Name 'tenantLabel' -Value $TenantLabel -Force + $Updated = $true + } + } + # Show the branded footer text on report pages. if (-not $ErrorMessage -and $Request.Body.PSObject.Properties.Name -contains 'showFooter') { $BrandingConfig | Add-Member -MemberType NoteProperty -Name 'showFooter' -Value ([bool]$Request.Body.showFooter) -Force @@ -438,6 +451,32 @@ Function Invoke-ExecBrandingSettings { Write-LogMessage -API $APIName -tenant 'Global' -headers $Request.Headers -message 'Reset branding settings to defaults' -Sev 'Info' 'Successfully reset branding settings to defaults' } + 'RenameImage' { + # A name for a gallery image, so it can be picked by name where the image is offered + # elsewhere: the report builder lists uploaded covers as Infographic page backgrounds. + $Kind = "$($Request.Body.kind)".ToLowerInvariant() + $ImageId = "$($Request.Body.id)".Trim() + $ImageName = "$($Request.Body.name)".Trim() + if (-not $ImageId) { + $StatusCode = [HttpStatusCode]::BadRequest + 'Error: id is required.' + break + } + if ($ImageName.Length -gt 64) { + $StatusCode = [HttpStatusCode]::BadRequest + 'Error: Image name must be 64 characters or fewer.' + break + } + $PartitionKey = switch ($Kind) { 'logo' { 'logo' } 'cover' { 'brandingCover' } default { $null } } + if (-not $PartitionKey) { + $StatusCode = [HttpStatusCode]::BadRequest + 'Error: kind must be logo or cover.' + break + } + Set-CIPPImageName -PartitionKey $PartitionKey -Id $ImageId -Name $ImageName + Write-LogMessage -API $APIName -tenant 'Global' -headers $Request.Headers -message "Named branding $Kind image $ImageId '$ImageName'" -Sev 'Info' + 'Successfully named image' + } 'ListPresets' { Get-CIPPBrandingPreset } @@ -513,6 +552,14 @@ Function Invoke-ExecBrandingSettings { break } + # Which of the tenant's names the preset's reports print; see the Set action. + $PresetTenantLabel = if ($Request.Body.tenantLabel) { "$($Request.Body.tenantLabel)" } else { 'alias' } + if (@('alias', 'name', 'domain') -notcontains $PresetTenantLabel) { + $StatusCode = [HttpStatusCode]::BadRequest + 'Error: tenantLabel must be alias, name or domain.' + break + } + $PresetId = if ($Request.Body.id) { "$($Request.Body.id)" } else { (New-Guid).Guid } Add-CIPPAzDataTableEntity @Table -Force -Entity @{ @@ -530,6 +577,7 @@ Function Invoke-ExecBrandingSettings { showPageNumbers = [bool]$Request.Body.showPageNumbers watermarkText = $PresetWatermark watermarkEnabled = [bool]$Request.Body.watermarkEnabled + tenantLabel = $PresetTenantLabel } | Out-Null Write-LogMessage -API $APIName -tenant 'Global' -headers $Request.Headers -message "Saved branding preset '$PresetName'" -Sev 'Info' diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecPreviewBrandingReportPdf.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecPreviewBrandingReportPdf.ps1 new file mode 100644 index 0000000000..10ab3e55e7 --- /dev/null +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecPreviewBrandingReportPdf.ps1 @@ -0,0 +1,68 @@ +function Invoke-ExecPreviewBrandingReportPdf { + <# + .FUNCTIONALITY + Entrypoint,AnyTenant + .ROLE + CIPP.AppSettings.Read + .DESCRIPTION + Renders a sample report against a branding configuration - normally the unsaved state of the + branding editor - and returns it as application/pdf bytes, so a colour, logo, cover, footer or + watermark can be judged on every page of a real report before it is saved. Each report type has + a fixed sample of the data its builder needs (Config/ReportSamples/.json); nothing is read + from a tenant. + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -Headers $Request.Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' + + $ReportNames = @{ + executive = 'Executive Summary'; reportBuilder = 'Quarterly Security Review'; shadowAI = 'Shadow AI Report' + bec = 'BEC Analysis Report'; sharing = 'Sharing Report'; permissions = 'Permissions Report'; mailFlow = 'Mail Flow Report' + } + + try { + # Which report to preview: executive, reportBuilder, shadowAI, bec, sharing, permissions or mailFlow. + $ReportType = [string]($Request.Body.reportType ?? 'executive') + if (-not $ReportNames.ContainsKey($ReportType)) { + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::BadRequest; Body = "Unknown report type '$ReportType'. Use one of: $($ReportNames.Keys -join ', ')." }) + } + $Sample = Get-Content (Join-Path $env:CIPPRootPath "Config\ReportSamples\$ReportType.json") -Raw | ConvertFrom-Json -AsHashtable + # The branding to render against, in the shape Get-CIPPBrandingSettings returns: colours (flat or + # under roleColours), a data-URL logo and cover or a coverStock path, footer, watermark and + # tenantLabel. Omitted -> the saved branding settings. + $Branding = $Request.Body.branding + # The sample tenant, named the way the branding under edit would name a real one. + $TenantName = switch ([string]$Branding.tenantLabel) { + 'domain' { 'contoso.onmicrosoft.com' } + 'name' { 'Contoso Ltd (sample data)' } + default { 'Contoso (sample data)' } + } + $Data = @{ TenantName = $TenantName } + $Sample + $Report = switch ($ReportType) { + 'reportBuilder' { @{ Blocks = @($Sample.blocks); Variables = @{} } } + 'shadowAI' { Build-CippShadowAIReportTree -Data $Data } + 'bec' { Build-CippBecReportTree -UserData $Sample.userData -BecData $Sample.becData -TenantName $TenantName } + 'sharing' { Build-CippSharingReportTree -Data $Data } + 'permissions' { Build-CippPermissionsReportTree -Data $Data } + 'mailFlow' { Build-CippMailFlowReportTree -Data $Data } + default { Build-CippExecutiveReportTree -Data $Data } + } + + # Optional: the tenant whose %variables% (%cippurl%, custom variables) resolve in the footer and cover. + $TenantFilter = [string]$Request.Body.tenantFilter + $Bytes = ConvertTo-CippReportPdf -Blocks $Report.Blocks -Variables $Report.Variables -Branding $Branding -TenantName $TenantName -TenantFilter $TenantFilter -ReportName $ReportNames[$ReportType] + + return ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + ContentType = 'application/pdf' + Headers = @{ 'Content-Disposition' = 'inline; filename="Branding_Preview.pdf"' } + Body = $Bytes + }) + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -Headers $Request.Headers -API $APIName -message "Failed to render the branding preview: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::InternalServerError; Body = "Error: $($ErrorMessage.NormalizedError)" }) + } +} diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ExecGetMailFlowReportPdf.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ExecGetMailFlowReportPdf.ps1 new file mode 100644 index 0000000000..b94e26a6ee --- /dev/null +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ExecGetMailFlowReportPdf.ps1 @@ -0,0 +1,74 @@ +function Invoke-ExecGetMailFlowReportPdf { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Exchange.Mailbox.Read + .DESCRIPTION + Server-renders the Exchange mail flow report as application/pdf bytes. Reads the same three Exchange + reports the Mail Flow page uses (Get-MailFlowStatusReport plus the TopMailSender and + TopSpamRecipient traffic summaries), aggregates the daily disposition rows the way the page does, + and composes them through the shared CIPPSharp kit (Build-CippMailFlowReportTree) - the server-side + replacement for the client react-pdf MailFlowReportButton. + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -Headers $Request.Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' + + try { + $TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter + if ([string]::IsNullOrWhiteSpace($TenantFilter)) { + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::BadRequest; Body = 'A tenantFilter is required' }) + } + # Reporting window in days (1-90). + $Days = [Math]::Min([Math]::Max([int]($Request.Query.days ?? 14), 1), 90) + $TenantName = Get-CippReportTenantName -TenantFilter $TenantFilter + + $Window = @{ + StartDate = (Get-Date).AddDays(-$Days).ToUniversalTime().ToString('s') + EndDate = (Get-Date).ToUniversalTime().ToString('s') + } + $FlowRows = @(New-ExoRequest -TenantId $TenantFilter -Cmdlet 'Get-MailFlowStatusReport' -CmdParams $Window) + # For the Top* traffic summary categories C1 is the address and C2 the message count. + function Get-TopList($Category) { + @(New-ExoRequest -TenantId $TenantFilter -Cmdlet 'Get-MailTrafficSummaryReport' -CmdParams ($Window + @{ Category = $Category }) | + ForEach-Object { @{ name = $_.C1; count = $_.C2 } }) + } + + # Message counts summed per event type, per direction and per day (the page's useMemo aggregation). + $Totals = @{} + foreach ($g in ($FlowRows | Group-Object EventType)) { $Totals[$g.Name] = ($g.Group | Measure-Object -Property MessageCount -Sum).Sum } + $DirectionTotals = @{} + foreach ($g in ($FlowRows | Group-Object Direction)) { $DirectionTotals[$g.Name] = ($g.Group | Measure-Object -Property MessageCount -Sum).Sum } + $Daily = @($FlowRows | Group-Object { ([datetime]$_.Date).ToString('yyyy-MM-dd') } | Sort-Object Name | ForEach-Object { + $Day = @{ date = $_.Name } + foreach ($t in ($_.Group | Group-Object EventType)) { $Day[$t.Name] = ($t.Group | Measure-Object -Property MessageCount -Sum).Sum } + $Day + }) + + $Report = Build-CippMailFlowReportTree -Data @{ + TenantName = $TenantName + days = $Days + totals = $Totals + directionTotals = $DirectionTotals + daily = $Daily + topSenders = Get-TopList 'TopMailSender' + topSpamRecipients = Get-TopList 'TopSpamRecipient' + } + + $Bytes = ConvertTo-CippReportPdf -Blocks $Report.Blocks -Variables $Report.Variables -TenantName $TenantName -TenantFilter $TenantFilter -ReportName 'Mail Flow Report' + $FileName = ("Mail_Flow_Report_$TenantFilter" -replace '[^a-zA-Z0-9_\-]', '_') + '.pdf' + return ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + ContentType = 'application/pdf' + Headers = @{ 'Content-Disposition' = "inline; filename=`"$FileName`"" } + Body = $Bytes + }) + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -Headers $Request.Headers -API $APIName -message "Failed to render Mail Flow report: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::InternalServerError; Body = "Error: $($ErrorMessage.NormalizedError)" }) + } +} diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-RemoveAssignmentFilterTemplate.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-RemoveAssignmentFilterTemplate.ps1 index e8973960f7..6855623067 100644 --- a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-RemoveAssignmentFilterTemplate.ps1 +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-RemoveAssignmentFilterTemplate.ps1 @@ -15,11 +15,9 @@ Function Invoke-RemoveAssignmentFilterTemplate { $ID = $request.Query.ID ?? $Request.Body.ID try { $Table = Get-CippTable -tablename 'templates' - Write-Host $ID $SafeID = ConvertTo-CIPPODataFilterValue -Value $ID -Type Guid $Filter = "PartitionKey eq 'AssignmentFilterTemplate' and RowKey eq '$SafeID'" - Write-Host $Filter $ClearRow = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey Remove-CIPPAzDataTableEntity -Force @Table -Entity $ClearRow $Result = "Removed Assignment Filter Template with ID $ID" diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecBECEvidenceExport.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecBECEvidenceExport.ps1 index 011c25569f..0c387af612 100644 --- a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecBECEvidenceExport.ps1 +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecBECEvidenceExport.ps1 @@ -7,7 +7,7 @@ function Invoke-ExecBECEvidenceExport { .SYNOPSIS Builds the evidence package for a Business Email Compromise run and returns it. .DESCRIPTION - Collates the run's stored results, one CSV per finding set, the score, the containment history, every logbook entry stamped with the case id and the browser-rendered PDF reports (pdfBase64, pdfSummaryBase64) into a ZIP. Nothing is stored: the ZIP is returned base64-encoded (ZipBase64) for the browser to save. Metadata only - nothing in the package is message content. + Collates the run's stored results, one CSV per finding set, the score, the containment history, every logbook entry stamped with the case id and the full and C-suite-summary report PDFs (rendered server-side) into a ZIP. Nothing is stored: the ZIP is returned base64-encoded (ZipBase64) for the browser to save. Metadata only - nothing in the package is message content. #> [CmdletBinding()] param($Request, $TriggerMetadata) @@ -16,15 +16,12 @@ function Invoke-ExecBECEvidenceExport { $Headers = $Request.Headers $TenantFilter = $Request.Body.tenantFilter $CaseId = [string]$Request.Body.caseId - # optional: the report PDFs rendered in the browser, base64-encoded (full report + C-suite summary) - $PdfBase64 = [string]$Request.Body.pdfBase64 - $PdfSummaryBase64 = [string]$Request.Body.pdfSummaryBase64 Set-CippBecCaseContext -CaseId $CaseId try { if (-not $TenantFilter) { throw 'tenantFilter is required' } if (-not $CaseId) { throw 'caseId is required' } - $Package = New-CIPPBecEvidencePackage -TenantFilter $TenantFilter -CaseId $CaseId -PdfBase64 $PdfBase64 -PdfSummaryBase64 $PdfSummaryBase64 -Headers $Headers -APIName $APIName + $Package = New-CIPPBecEvidencePackage -TenantFilter $TenantFilter -CaseId $CaseId -Headers $Headers -APIName $APIName $Body = @{ Results = "Evidence package for case $CaseId created: $($Package.FileCount) files, $([math]::Round($Package.Bytes / 1KB)) KB" Evidence = [pscustomobject]@{ diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecGetBecReportPdf.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecGetBecReportPdf.ps1 new file mode 100644 index 0000000000..d12000d3d1 --- /dev/null +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecGetBecReportPdf.ps1 @@ -0,0 +1,87 @@ +function Invoke-ExecGetBecReportPdf { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Identity.User.Read + .DESCRIPTION + Server-renders a stored Business Email Compromise (BEC) run as application/pdf bytes. Reads the + run through Get-CIPPBecReport (the BecReports metadata row plus its BecResults payload) and + composes it through the shared CIPPSharp component kit (Build-CippBecReportTree) - the server-side + replacement for the client react-pdf BECRemediationReportButton. A run is named by its caseId; + pass userId instead to render the user's newest completed run. The run must have completed - the + report reads its stored result, it does not trigger a new investigation. + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -Headers $Request.Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' + + try { + $TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter + # The stored run to render. A caseId names it directly; a userId picks the user's newest completed run. + $CaseId = $Request.Query.caseId ?? $Request.Body.caseId + $UserId = $Request.Query.userId ?? $Request.Body.userId + if ([string]::IsNullOrWhiteSpace($TenantFilter) -or ([string]::IsNullOrWhiteSpace($CaseId) -and [string]::IsNullOrWhiteSpace($UserId))) { + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::BadRequest; Body = 'A tenantFilter and either a caseId or a userId are required' }) + } + + # Without a caseId, fall back to the user's most recent completed run (the list is newest-first). + if ([string]::IsNullOrWhiteSpace($CaseId)) { + $Runs = @(Get-CIPPBecReport -TenantFilter $TenantFilter -UserId $UserId) + $CaseId = ($Runs | Where-Object { $_.Status -eq 'Completed' } | Select-Object -First 1).CaseId + if ([string]::IsNullOrWhiteSpace($CaseId)) { + return ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::NotFound + Body = 'No completed BEC analysis is stored for this user. Run the BEC check first, then generate the report.' + }) + } + } + + $Run = Get-CIPPBecReport -TenantFilter $TenantFilter -CaseId $CaseId -IncludeResults + if (-not $Run -or $Run.Status -ne 'Completed' -or -not $Run.Results) { + return ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::NotFound + Body = "No completed BEC analysis is stored for case '$CaseId'. Run the BEC check first, then generate the report." + }) + } + + # The results payload is what the check pages render. The containment history and the run's own + # identifiers live on the metadata row, and the client renderer reads them off becData.Run - so + # attach the same .Run block here, and the builder reads it exactly as the client does. + $BecData = $Run.Results + $RunBlock = [pscustomobject]@{ + CaseId = $Run.CaseId + Status = $Run.Status + ExtractedAt = $Run.ExtractedAt + RequestedAt = $Run.RequestedAt + RequestedBy = $Run.RequestedBy + Containment = $Run.Containment + } + $BecData | Add-Member -NotePropertyName 'Run' -NotePropertyValue $RunBlock -Force + + $TenantName = Get-CippReportTenantName -TenantFilter $TenantFilter + # The investigated user labels the cover and footer; the run stored who it was for. + $DisplayName = @($Run.DisplayName, $Run.UserPrincipalName, $Request.Query.userDisplayName, $Request.Body.userDisplayName, $Run.UserId) | + Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -First 1 + $UserName = @($Run.UserPrincipalName, $Request.Query.userName, $Request.Body.userName) | + Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -First 1 + $UserData = [pscustomobject]@{ displayName = $DisplayName; userPrincipalName = $UserName; id = $Run.UserId } + + $Report = Build-CippBecReportTree -UserData $UserData -BecData $BecData -TenantName $TenantName + + $Bytes = ConvertTo-CippReportPdf -Blocks $Report.Blocks -Variables $Report.Variables -TenantName $TenantName -TenantFilter $TenantFilter -ReportName 'BEC Analysis Report' + $FileName = ("BEC_Report_$DisplayName" -replace '[^a-zA-Z0-9_\-]', '_') + '.pdf' + return ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + ContentType = 'application/pdf' + Headers = @{ 'Content-Disposition' = "inline; filename=`"$FileName`"" } + Body = $Bytes + }) + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -Headers $Request.Headers -API $APIName -message "Failed to render BEC report: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::InternalServerError; Body = "Error: $($ErrorMessage.NormalizedError)" }) + } +} diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ExecGetPermissionsReportPdf.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ExecGetPermissionsReportPdf.ps1 new file mode 100644 index 0000000000..af1c283c1e --- /dev/null +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ExecGetPermissionsReportPdf.ps1 @@ -0,0 +1,48 @@ +function Invoke-ExecGetPermissionsReportPdf { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Sharepoint.Site.Read + .DESCRIPTION + Server-renders the SharePoint Permissions report as application/pdf bytes. Gathers the same shaped + data the Permissions page uses (ListSharePointPermissions, from the CIPP reporting cache), composes + it through the shared CIPPSharp component kit (Build-CippPermissionsReportTree) and returns the + finished PDF - the server-side replacement for the client react-pdf PermissionsReportButton. + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -Headers $Request.Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' + + try { + $TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter + if ([string]::IsNullOrWhiteSpace($TenantFilter)) { + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::BadRequest; Body = 'A tenantFilter is required' }) + } + $TenantName = Get-CippReportTenantName -TenantFilter $TenantFilter + + # The same shaped data the Permissions page reads (from the reporting cache). + $Raw = Get-CIPPSharePointPermissionsReport -TenantFilter $TenantFilter + $Report = Build-CippPermissionsReportTree -Data @{ + TenantName = $TenantName + summary = $Raw.summary + assignments = $Raw.assignments + skippedSites = $Raw.skippedSites + } + + $Bytes = ConvertTo-CippReportPdf -Blocks $Report.Blocks -Variables $Report.Variables -TenantName $TenantName -TenantFilter $TenantFilter -ReportName 'Permissions Report' + $FileName = ("Permissions_Report_$TenantFilter" -replace '[^a-zA-Z0-9_\-]', '_') + '.pdf' + return ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + ContentType = 'application/pdf' + Headers = @{ 'Content-Disposition' = "inline; filename=`"$FileName`"" } + Body = $Bytes + }) + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -Headers $Request.Headers -API $APIName -message "Failed to render Permissions report: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::InternalServerError; Body = "Error: $($ErrorMessage.NormalizedError)" }) + } +} diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ExecGetSharingReportPdf.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ExecGetSharingReportPdf.ps1 new file mode 100644 index 0000000000..892954f9b6 --- /dev/null +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ExecGetSharingReportPdf.ps1 @@ -0,0 +1,49 @@ +function Invoke-ExecGetSharingReportPdf { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Sharepoint.Site.Read + .DESCRIPTION + Server-renders the SharePoint & OneDrive Sharing report as application/pdf bytes. Gathers the same + shaped data the Sharing page uses (ListSharePointSharing, from the CIPP reporting cache), composes + it through the shared CIPPSharp component kit (Build-CippSharingReportTree) and returns the finished + PDF - the server-side replacement for the client react-pdf SharingReportButton. + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -Headers $Request.Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' + + try { + $TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter + if ([string]::IsNullOrWhiteSpace($TenantFilter)) { + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::BadRequest; Body = 'A tenantFilter is required' }) + } + $TenantName = Get-CippReportTenantName -TenantFilter $TenantFilter + + # The same shaped data the Sharing page reads (from the reporting cache; no live Graph enumeration). + $Raw = Get-CIPPSharePointSharingReport -TenantFilter $TenantFilter + $Report = Build-CippSharingReportTree -Data @{ + TenantName = $TenantName + summary = $Raw.summary + links = $Raw.links + topRecipients = $Raw.topRecipients + topLibraries = $Raw.topLibraries + } + + $Bytes = ConvertTo-CippReportPdf -Blocks $Report.Blocks -Variables $Report.Variables -TenantName $TenantName -TenantFilter $TenantFilter -ReportName 'Sharing Report' + $FileName = ("Sharing_Report_$TenantFilter" -replace '[^a-zA-Z0-9_\-]', '_') + '.pdf' + return ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + ContentType = 'application/pdf' + Headers = @{ 'Content-Disposition' = "inline; filename=`"$FileName`"" } + Body = $Bytes + }) + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -Headers $Request.Headers -API $APIName -message "Failed to render Sharing report: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::InternalServerError; Body = "Error: $($ErrorMessage.NormalizedError)" }) + } +} diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSharePointPermissions.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSharePointPermissions.ps1 index a6b7f59a4a..938c32b37f 100644 --- a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSharePointPermissions.ps1 +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSharePointPermissions.ps1 @@ -27,135 +27,7 @@ function Invoke-ListSharePointPermissions { param($Request, $TriggerMetadata) $TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter - - # A readable label for a site that has no display name, taken from the last path segment of - # its URL: '.../sites/AllCompany' becomes 'AllCompany', '.../search' becomes 'search'. - function Get-CIPPSiteLabel { - param([string]$SiteUrl) - if ([string]::IsNullOrWhiteSpace($SiteUrl)) { return 'Unnamed site' } - try { - $Path = ([System.Uri]$SiteUrl).AbsolutePath.Trim('/') - if ($Path) { return ($Path -split '/')[-1] } - return 'Root site' - } catch { - return 'Unnamed site' - } - } - - # --- Cached dataset from the CIPP reporting database --- - try { - $CacheRows = @(New-CIPPDbRequest -TenantFilter $TenantFilter -Type 'SharePointPermissions') - } catch { - $CacheRows = @() - } - - $PermissionsSynced = $false - $LastDataRefresh = $null - try { - $CountRow = Get-CIPPDbItem -TenantFilter $TenantFilter -Type 'SharePointPermissions' -CountsOnly | Select-Object -First 1 - if ($CountRow) { $PermissionsSynced = $true } - if ($CountRow.Timestamp) { $LastDataRefresh = $CountRow.Timestamp } - } catch {} - - $SiteRows = @($CacheRows | Where-Object { $_.rowType -eq 'Site' }) - $Assignments = @($CacheRows | Where-Object { $_.rowType -eq 'Assignment' }) - - # --- Scan coverage --- - $SitesScanned = 0; $LibrariesScanned = 0; $UniquePermissionLibraries = 0 - $SkippedSites = [System.Collections.Generic.List[object]]::new() - foreach ($Site in $SiteRows) { - $SitesScanned++ - $LibrariesScanned += [int]($Site.librariesScanned ?? 0) - $UniquePermissionLibraries += [int]($Site.librariesWithUniquePermissions ?? 0) - if ($Site.collectionStatus -eq 'Skipped') { - $SkippedSites.Add([PSCustomObject]@{ - siteName = $Site.siteName - siteUrl = $Site.siteUrl - error = $Site.collectionError - }) - } - } - - # --- Assignment rollups --- - $BroadClaimGrants = 0; $ExternalGrants = 0; $DirectFullControlGrants = 0 - $ByPermissionLevel = @{} - $ByPrincipalType = @{} - $ByBroadClaim = @{} - $BySiteUnique = @{} - $RealAssignments = 0 - foreach ($Assignment in $Assignments) { - # Placeholder rows for a unique-permission library with nothing granted carry no principal. - if (-not $Assignment.principalId) { continue } - # SharePoint maintains Limited Access itself; it grants nothing on its own. - if ($Assignment.isSystemManaged -eq $true) { continue } - $RealAssignments++ - - $Level = [string]($Assignment.permissionLevel ?? 'Unknown') - $ByPermissionLevel[$Level] = [int]($ByPermissionLevel[$Level] ?? 0) + 1 - $Type = [string]($Assignment.principalType ?? 'Other') - $ByPrincipalType[$Type] = [int]($ByPrincipalType[$Type] ?? 0) + 1 - - if ($Assignment.broadClaim) { - $BroadClaimGrants++ - $Claim = [string]$Assignment.broadClaim - $ByBroadClaim[$Claim] = [int]($ByBroadClaim[$Claim] ?? 0) + 1 - } - if ($Assignment.isGuest -eq $true) { $ExternalGrants++ } - # Full Control held by anything other than a SharePoint group means it was granted - # directly rather than through the site's Owners group, which every site has by default. - if ($Level -eq 'Full Control' -and $Assignment.principalType -ne 'SharePoint Group') { - $DirectFullControlGrants++ - } - } - - # Libraries that no longer inherit, counted per site for the chart. - foreach ($Site in $SiteRows) { - $Unique = [int]($Site.librariesWithUniquePermissions ?? 0) - if ($Unique -gt 0) { - $SiteName = [string]($Site.siteName ?? $Site.siteUrl) - if ($SiteName) { $BySiteUnique[$SiteName] = [int]($BySiteUnique[$SiteName] ?? 0) + $Unique } - } - } - - $Body = [PSCustomObject]@{ - summary = [PSCustomObject]@{ - sitesScanned = $SitesScanned - sitesSkipped = $SkippedSites.Count - librariesScanned = $LibrariesScanned - uniquePermissionLibraries = $UniquePermissionLibraries - totalAssignments = $RealAssignments - broadClaimGrants = $BroadClaimGrants - externalGrants = $ExternalGrants - directFullControlGrants = $DirectFullControlGrants - permissionsSynced = $PermissionsSynced - lastDataRefresh = $LastDataRefresh - } - byPermissionLevel = @($ByPermissionLevel.GetEnumerator() | Sort-Object -Property Value -Descending | ForEach-Object { [PSCustomObject]@{ level = $_.Key; grants = $_.Value } }) - byPrincipalType = @($ByPrincipalType.GetEnumerator() | Sort-Object -Property Value -Descending | ForEach-Object { [PSCustomObject]@{ type = $_.Key; grants = $_.Value } }) - byBroadClaim = @($ByBroadClaim.GetEnumerator() | Sort-Object -Property Value -Descending | ForEach-Object { [PSCustomObject]@{ claim = $_.Key; grants = $_.Value } }) - topSitesByUniqueLibraries = @($BySiteUnique.GetEnumerator() | Sort-Object -Property Value -Descending | Select-Object -First 10 | ForEach-Object { [PSCustomObject]@{ site = $_.Key; libraries = $_.Value } }) - skippedSites = @($SkippedSites) - # Display fields are derived here rather than stored, so existing cached data gains them - # without waiting for a re-scan. - # - # appliesTo spells out what scope means for a reader scanning the table. Every Library row - # is by definition a library that stopped inheriting - libraries that still inherit are not - # collected, because their permissions are the site's repeated. - # - # siteName falls back to a label built from the URL for the handful of system sites that - # have no name. The URL itself is not used: the tables render any value starting with http - # as a link, and a column of links where names should be is worse than a plain label. - assignments = @($Assignments | ForEach-Object { - $AppliesTo = if ($_.scope -eq 'Library') { 'This library only' } else { 'Whole site' } - $_ | Add-Member -NotePropertyName 'appliesTo' -NotePropertyValue $AppliesTo -Force - - $SiteName = [string]$_.siteName - if ([string]::IsNullOrWhiteSpace($SiteName) -or $SiteName -like 'http*') { - $_ | Add-Member -NotePropertyName 'siteName' -NotePropertyValue (Get-CIPPSiteLabel -SiteUrl $_.siteUrl) -Force - } - $_ - }) - } + $Body = Get-CIPPSharePointPermissionsReport -TenantFilter $TenantFilter return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSharePointSharing.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSharePointSharing.ps1 index 7faa7e9172..220821ebb9 100644 --- a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSharePointSharing.ps1 +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Teams-Sharepoint/Invoke-ListSharePointSharing.ps1 @@ -19,137 +19,7 @@ function Invoke-ListSharePointSharing { param($Request, $TriggerMetadata) $TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter - - # Usage report values can arrive as numbers, strings or empty strings depending on the tenant. - function ConvertTo-SafeDouble { - param($Value) - $Parsed = [double]0 - if ($null -ne $Value -and [double]::TryParse("$Value", [ref]$Parsed)) { return $Parsed } - return [double]0 - } - - # --- Cached datasets from the CIPP reporting database --- - $CacheTypes = @('SharePointSharingLinks', 'SharePointSiteUsage', 'OneDriveUsage') - $CacheData = @{} - $CacheSynced = @{} - $CacheTimestamps = [System.Collections.Generic.List[object]]::new() - foreach ($Type in $CacheTypes) { - try { - $CacheData[$Type] = @(New-CIPPDbRequest -TenantFilter $TenantFilter -Type $Type) - } catch { - $CacheData[$Type] = @() - } - $CacheSynced[$Type] = $false - try { - $CountRow = Get-CIPPDbItem -TenantFilter $TenantFilter -Type $Type -CountsOnly | Select-Object -First 1 - if ($CountRow) { $CacheSynced[$Type] = $true } - if ($CountRow.Timestamp) { $CacheTimestamps.Add($CountRow.Timestamp) } - } catch {} - } - $LastDataRefresh = $CacheTimestamps | Sort-Object | Select-Object -First 1 - - # --- Environment summaries per workload. Teams-connected sites (rootWebTemplate 'Group') - # are reported separately from the remaining SharePoint sites; OneDrive is per account. --- - $SharePointSites = 0; $SharePointFiles = [int64]0; $SharePointStorage = [double]0 - $TeamsSites = 0; $TeamsFiles = [int64]0; $TeamsStorage = [double]0 - foreach ($Site in $CacheData['SharePointSiteUsage']) { - $Files = [int64](ConvertTo-SafeDouble -Value $Site.fileCount) - $Storage = ConvertTo-SafeDouble -Value $Site.storageUsedInBytes - if ($Site.rootWebTemplate -eq 'Group') { - $TeamsSites++; $TeamsFiles += $Files; $TeamsStorage += $Storage - } else { - $SharePointSites++; $SharePointFiles += $Files; $SharePointStorage += $Storage - } - } - - $OneDriveAccounts = 0; $OneDriveFiles = [int64]0; $OneDriveStorage = [double]0 - foreach ($Account in $CacheData['OneDriveUsage']) { - $OneDriveAccounts++ - $OneDriveFiles += [int64](ConvertTo-SafeDouble -Value $Account.fileCount) - $OneDriveStorage += ConvertTo-SafeDouble -Value $Account.storageUsedInBytes - } - - # --- Sharing link rollups --- - $Links = $CacheData['SharePointSharingLinks'] - $AnonymousLinks = 0; $ExternalLinks = 0; $InternalLinks = 0 - $ByScope = @{} - $ByLinkType = @{} - $BySite = @{} - $ByLibrary = @{} - $ByRecipient = @{} - $SharedItemIds = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) - # Sprawl risk signals, derived from the same cached rows. - $AnonymousEditLinks = 0; $NeverExpiringAnonymousLinks = 0; $FolderShares = 0; $PasswordProtectedLinks = 0 - foreach ($Link in $Links) { - switch ($Link.classification) { - 'Anonymous' { $AnonymousLinks++ } - 'External' { $ExternalLinks++ } - default { $InternalLinks++ } - } - $Scope = [string]($Link.classification ?? 'Internal') - $ByScope[$Scope] = [int]($ByScope[$Scope] ?? 0) + 1 - $Type = [string]($Link.linkType ?? 'link') - $ByLinkType[$Type] = [int]($ByLinkType[$Type] ?? 0) + 1 - $SiteName = [string]($Link.siteName ?? $Link.siteUrl) - if ($SiteName) { $BySite[$SiteName] = [int]($BySite[$SiteName] ?? 0) + 1 } - if ($Link.driveName) { - $LibraryLabel = if ($SiteName) { "$SiteName / $($Link.driveName)" } else { [string]$Link.driveName } - $ByLibrary[$LibraryLabel] = [int]($ByLibrary[$LibraryLabel] ?? 0) + 1 - } - if ($Link.driveId -and $Link.itemId) { [void]$SharedItemIds.Add("$($Link.driveId)|$($Link.itemId)") } - - # 'write' and 'owner' both mean the recipient can change the content. - $CanEdit = @($Link.roles) -contains 'write' -or @($Link.roles) -contains 'owner' - if ($Link.classification -eq 'Anonymous') { - if ($CanEdit) { $AnonymousEditLinks++ } - if (-not $Link.expirationDateTime) { $NeverExpiringAnonymousLinks++ } - } - # A share on a folder exposes everything below it, so it counts differently to a file share. - if ($Link.itemType -eq 'Folder' -and $Link.classification -in @('Anonymous', 'External')) { $FolderShares++ } - if ($Link.hasPassword -eq $true) { $PasswordProtectedLinks++ } - - # Who the tenant is sharing with, counted from named external recipients only: - # anonymous links have no recipient and internal ones are not sprawl. - if ($Link.classification -eq 'External') { - foreach ($Recipient in @($Link.sharedWith)) { - if ([string]::IsNullOrWhiteSpace($Recipient)) { continue } - $ByRecipient[[string]$Recipient] = [int]($ByRecipient[[string]$Recipient] ?? 0) + 1 - } - } - } - - $Body = [PSCustomObject]@{ - summary = [PSCustomObject]@{ - sharePointSites = $SharePointSites - sharePointFiles = $SharePointFiles - sharePointStorageUsedGB = [math]::Round($SharePointStorage / 1GB, 2) - teamsSites = $TeamsSites - teamsFiles = $TeamsFiles - teamsStorageUsedGB = [math]::Round($TeamsStorage / 1GB, 2) - oneDriveAccounts = $OneDriveAccounts - oneDriveFiles = $OneDriveFiles - oneDriveStorageUsedGB = [math]::Round($OneDriveStorage / 1GB, 2) - totalLinks = @($Links).Count - anonymousLinks = $AnonymousLinks - externalLinks = $ExternalLinks - internalLinks = $InternalLinks - itemsShared = $SharedItemIds.Count - anonymousEditLinks = $AnonymousEditLinks - neverExpiringAnonymous = $NeverExpiringAnonymousLinks - folderShares = $FolderShares - passwordProtectedLinks = $PasswordProtectedLinks - externalRecipients = $ByRecipient.Count - linksSynced = $CacheSynced['SharePointSharingLinks'] - usageSynced = ($CacheSynced['SharePointSiteUsage'] -or $CacheSynced['OneDriveUsage']) - lastDataRefresh = $LastDataRefresh - } - byScope = @($ByScope.GetEnumerator() | Sort-Object -Property Value -Descending | ForEach-Object { [PSCustomObject]@{ scope = $_.Key; links = $_.Value } }) - byLinkType = @($ByLinkType.GetEnumerator() | Sort-Object -Property Value -Descending | ForEach-Object { [PSCustomObject]@{ type = $_.Key; links = $_.Value } }) - topSites = @($BySite.GetEnumerator() | Sort-Object -Property Value -Descending | Select-Object -First 10 | ForEach-Object { [PSCustomObject]@{ site = $_.Key; links = $_.Value } }) - topLibraries = @($ByLibrary.GetEnumerator() | Sort-Object -Property Value -Descending | Select-Object -First 10 | ForEach-Object { [PSCustomObject]@{ library = $_.Key; links = $_.Value } }) - topRecipients = @($ByRecipient.GetEnumerator() | Sort-Object -Property Value -Descending | Select-Object -First 10 | ForEach-Object { [PSCustomObject]@{ recipient = $_.Key; links = $_.Value } }) - links = @($Links) - } + $Body = Get-CIPPSharePointSharingReport -TenantFilter $TenantFilter return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecGetBaselineWhatIfReportPdf.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecGetBaselineWhatIfReportPdf.ps1 new file mode 100644 index 0000000000..1b1ed9bdba --- /dev/null +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecGetBaselineWhatIfReportPdf.ps1 @@ -0,0 +1,55 @@ +function Invoke-ExecGetBaselineWhatIfReportPdf { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Tenant.Baselines.Read + .DESCRIPTION + Server-renders the Baseline What-If report as application/pdf bytes: what applying the + configured standards would change for a tenant today, what each planned stage will change, + optionally what assigning one more baseline would roll out, and the agreed exceptions. Reads + the same alignment payload the Baselines page shows (Get-CIPPBaselineAlignment), composes it + through the shared CIPPSharp component kit (Build-CippBaselineWhatIfReportTree) and returns + the finished PDF. Nothing is changed by producing it. + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -Headers $Request.Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' + + try { + $TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter + if ([string]::IsNullOrWhiteSpace($TenantFilter)) { + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::BadRequest; Body = 'A tenantFilter is required' }) + } + # Optional: the GUID of a baseline not assigned to the tenant, to preview what assigning it would roll out stage by stage. + $SimulatedTemplateId = [string]($Request.Query.simulatedTemplateId ?? $Request.Body.simulatedTemplateId) + $TenantName = Get-CippReportTenantName -TenantFilter $TenantFilter + + $Alignment = Get-CIPPBaselineAlignment -TenantFilter $TenantFilter + $Simulated = if ($SimulatedTemplateId) { @(Get-CIPPBaseline) | Where-Object { $_.GUID -eq $SimulatedTemplateId } | Select-Object -First 1 } + $Report = Build-CippBaselineWhatIfReportTree -Data @{ + TenantName = $TenantName + TenantFilter = $TenantFilter + summary = $Alignment.summary + rows = @($Alignment.rows) + stageStates = @($Alignment.stageStates) + simulatedTemplate = $Simulated + catalog = @(Get-CIPPBaselineDefinition) + } + + $Bytes = ConvertTo-CippReportPdf -Blocks $Report.Blocks -Variables $Report.Variables -TenantName $TenantName -TenantFilter $TenantFilter -ReportName 'Baseline What-If Report' + $FileName = ("Baseline_WhatIf_Report_$TenantFilter" -replace '[^a-zA-Z0-9_\-]', '_') + '.pdf' + return ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + ContentType = 'application/pdf' + Headers = @{ 'Content-Disposition' = "inline; filename=`"$FileName`"" } + Body = $Bytes + }) + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -Headers $Request.Headers -API $APIName -message "Failed to render the Baseline What-If report: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::InternalServerError; Body = "Error: $($ErrorMessage.NormalizedError)" }) + } +} diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecGetExecutiveReportPdf.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecGetExecutiveReportPdf.ps1 new file mode 100644 index 0000000000..e5eb3bfb09 --- /dev/null +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecGetExecutiveReportPdf.ps1 @@ -0,0 +1,198 @@ +function Invoke-ExecGetExecutiveReportPdf { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Tenant.Standards.Read + .DESCRIPTION + Server-renders the Executive Summary report as application/pdf bytes. Every figure is read from the + nightly Reporting DB cache via New-CIPPDbRequest (Users/Guests/Roles for the environment overview, + LicenseOverview, ManagedDevices, ConditionalAccessPolicies and SecureScore) plus the standards + comparison from the CippStandardsReports table resolved against the standards catalog - the same + cached data the rest of CIPP reports from, with no live Graph or cross-endpoint HTTP calls. The + shaped data is composed through the shared CIPPSharp component kit (Build-CippExecutiveReportTree), + the server-side replacement for the client react-pdf ExecutiveReportButton. Each source is gathered + defensively: a source with no cached data simply drops its section. + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -Headers $Request.Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' + + try { + $TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter + if ([string]::IsNullOrWhiteSpace($TenantFilter)) { + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::BadRequest; Body = 'A tenantFilter is required' }) + } + + # The branding preset a caller picked for this render, else the global branding settings. + $BrandingPresetId = [string]($Request.Body.brandingPresetId ?? $Request.Query.brandingPresetId) + $TenantName = Get-CippReportTenantName -TenantFilter $TenantFilter -BrandingPresetId $BrandingPresetId + + # GA directory role template id, used to pull the Global Administrator count from the Roles cache. + $GaTemplateId = '62e90394-69f5-4237-9190-012177145e10' + + # -- User stats (Users / Guests cache + the cached privileged-role getter for GAs) -- + $UserStats = @{ licensedUsers = 0; unlicensedUsers = 0; guests = 0; globalAdmins = 0; permanentGlobalAdmins = 0; eligibleGlobalAdmins = 0; pimCapable = $false } + try { + $Users = @(New-CIPPDbRequest -TenantFilter $TenantFilter -Type 'Users' -Fields 'assignedLicenses') + $LicUsers = @($Users | Where-Object { @($_.assignedLicenses).Count -gt 0 }).Count + $GuestCount = @(New-CIPPDbRequest -TenantFilter $TenantFilter -Type 'Guests' -Fields 'id').Count + # Get-CippDbRoleMembers merges cached PIM (Active/Eligible) + direct role membership for the role. + $GaMembers = @(Get-CippDbRoleMembers -TenantFilter $TenantFilter -RoleTemplateId $GaTemplateId) + $EligibleGas = @($GaMembers | Where-Object { $_.AssignmentType -eq 'Eligible' }).Count + $PimActive = @($GaMembers | Where-Object { $_.AssignmentType -in @('Active', 'Eligible') }).Count + $UserStats = @{ + licensedUsers = $LicUsers + unlicensedUsers = [Math]::Max(0, $Users.Count - $LicUsers) + guests = $GuestCount + globalAdmins = $GaMembers.Count + permanentGlobalAdmins = ($GaMembers.Count - $EligibleGas) + eligibleGlobalAdmins = $EligibleGas + # PIM is in use for GA when any assignment is a PIM Active/Eligible one (not plain Direct). + pimCapable = ($PimActive -gt 0) + } + } catch { Write-Information "Executive report: user stats unavailable - $($_.Exception.Message)" } + + # -- Licences (LicenseOverview cache) -- + $Licenses = @() + try { + $Licenses = @(New-CIPPDbRequest -TenantFilter $TenantFilter -Type 'LicenseOverview' | ForEach-Object { + @{ name = ($_.License ?? 'N/A'); used = "$($_.CountUsed ?? 0)"; available = "$($_.CountAvailable ?? 0)"; total = "$($_.TotalLicenses ?? 0)" } + }) + } catch { Write-Information "Executive report: licences unavailable - $($_.Exception.Message)" } + + # -- Managed devices (ManagedDevices cache) -- + $Devices = @() + try { + $Devices = @(New-CIPPDbRequest -TenantFilter $TenantFilter -Type 'ManagedDevices' | ForEach-Object { + $d = $_ + $model = [string]$d.model + $mfr = [string]$d.manufacturer + $isCloudPc = ($d.isCloudPC -eq $true) -or ($d.deviceType -eq 'cloudPC') -or ($d.chassisType -eq 'cloudPC') -or ($model.ToLower().StartsWith('cloud pc') -and $mfr.ToLower() -eq 'microsoft corporation') + $compState = [string]$d.complianceState + @{ + name = ($d.deviceName ?? 'N/A') + os = ($d.operatingSystem ?? 'N/A') + compliance = $(if ([string]::IsNullOrWhiteSpace($compState)) { 'Unknown' } else { $compState }) + compliant = ($compState.ToLower() -eq 'compliant') + lastSync = $(if ($d.lastSyncDateTime) { try { ([datetime]$d.lastSyncDateTime).ToString('MMM d, yyyy') } catch { [string]$d.lastSyncDateTime } } else { 'N/A' }) + encrypted = (($d.isEncrypted -eq $true) -or $isCloudPc) + } + }) + } catch { Write-Information "Executive report: managed devices unavailable - $($_.Exception.Message)" } + + # -- Conditional Access policies (ConditionalAccessPolicies cache, raw Graph shape) -- + # 'controls' is the builtInControls array (drives the tree's MFA count via -contains 'mfa'); + # 'controlsText' is the human label; 'applications' summarises includeApplications. + $CAPolicies = @() + try { + $ControlLabels = [ordered]@{ mfa = 'MFA'; block = 'Block'; compliantDevice = 'Compliant Device' } + $CAPolicies = @(New-CIPPDbRequest -TenantFilter $TenantFilter -Type 'ConditionalAccessPolicies' | Where-Object { $_.displayName } | ForEach-Object { + $p = $_ + $bic = @($p.grantControls.builtInControls) + $apps = @($p.conditions.applications.includeApplications) + $labels = @(foreach ($k in $ControlLabels.Keys) { if ($bic -contains $k) { $ControlLabels[$k] } }) + @{ + name = ($p.displayName ?? 'N/A') + state = ([string]$p.state) + controls = $bic + controlsText = $(if ($labels.Count -gt 0) { $labels -join ', ' } else { 'Custom' }) + applications = $(if ($apps -contains 'All') { 'All' } elseif ($apps.Count -gt 0) { "$($apps.Count) app$(if ($apps.Count -ne 1) { 's' })" } else { 'None' }) + } + }) + } catch { Write-Information "Executive report: conditional access unavailable - $($_.Exception.Message)" } + + # -- Microsoft Secure Score (SecureScore cache, one record per day) -- + $SecureScore = $null + try { + $Scores = @(New-CIPPDbRequest -TenantFilter $TenantFilter -Type 'SecureScore' | Sort-Object -Property @{ Expression = { [datetime]$_.createdDateTime } } -Descending) + if ($Scores.Count -gt 0) { + $Latest = $Scores[0] + $Cur = [double]($Latest.currentScore ?? 0) + $Max = [double]($Latest.maxScore ?? 0) + $Acs = @($Latest.averageComparativeScores) + $VsAll = ($Acs | Where-Object { $_.basis -eq 'AllTenants' } | Select-Object -First 1).averageScore + $VsSim = ($Acs | Where-Object { $_.basis -eq 'TotalSeats' } | Select-Object -First 1).averageScore + # Trend: the 7 most recent points, drawn oldest-first. + $Trend = @($Scores | Select-Object -First 7 | Sort-Object -Property @{ Expression = { [datetime]$_.createdDateTime } } | ForEach-Object { + @{ label = ([datetime]$_.createdDateTime).ToString('MMM d'); value = [math]::Round([double]($_.currentScore ?? 0), 2) } + }) + $SecureScore = @{ + currentScore = [math]::Round($Cur, 2) + maxScore = $Max + percentageCurrent = $(if ($Max -gt 0) { [math]::Round(($Cur / $Max) * 100) } else { 0 }) + percentageVsSimilar = $(if ($null -ne $VsSim) { [math]::Round([double]$VsSim) } else { $null }) + percentageVsAllTenants = $(if ($null -ne $VsAll) { [math]::Round([double]$VsAll) } else { $null }) + trend = $Trend + } + } + } catch { Write-Information "Executive report: secure score unavailable - $($_.Exception.Message)" } + + # -- Security standards (CippStandardsReports table + the standards catalog) -- + $SecurityControls = @() + try { + $StdTable = Get-CIPPTable -TableName 'CippStandardsReports' + $StdRows = @(Get-CIPPAzDataTableEntity @StdTable -Filter "PartitionKey eq '$TenantFilter'") + if ($StdRows.Count -gt 0) { + $TenantStd = [ordered]@{ tenantFilter = $TenantFilter } + foreach ($Row in $StdRows) { + $FieldName = [string]$Row.RowKey + # Quarantine template rows hex-encode the display name in the key; decode it as the page does. + if ($FieldName -match '^(standards\.QuarantineTemplate\.)([0-9a-fA-F]+)$') { + $Prefix = $Matches[1]; $Hex = $Matches[2] + $Chars = for ($i = 0; $i -lt $Hex.Length; $i += 2) { [char][Convert]::ToInt32($Hex.Substring($i, 2), 16) } + $FieldName = "$Prefix$(-join $Chars)" + } + $Cv = if (-not [string]::IsNullOrWhiteSpace([string]$Row.CurrentValue) -and (Test-Json -Json ([string]$Row.CurrentValue) -ErrorAction SilentlyContinue)) { $Row.CurrentValue | ConvertFrom-Json -ErrorAction SilentlyContinue } else { $Row.CurrentValue } + $Ev = if (-not [string]::IsNullOrWhiteSpace([string]$Row.ExpectedValue) -and (Test-Json -Json ([string]$Row.ExpectedValue) -ErrorAction SilentlyContinue)) { $Row.ExpectedValue | ConvertFrom-Json -ErrorAction SilentlyContinue } else { $Row.ExpectedValue } + $Val = $Row.Value + if ($Val -is [string] -and -not [string]::IsNullOrWhiteSpace($Val) -and (Test-Json -Json $Val -ErrorAction SilentlyContinue)) { $Val = $Val | ConvertFrom-Json -ErrorAction SilentlyContinue } + $TenantStd[$FieldName] = @{ Value = $Val; CurrentValue = $Cv; ExpectedValue = $Ev } + } + $Catalog = @() + try { + $CatPath = Join-Path $env:CIPPRootPath 'Config\standards.json' + if (Test-Path $CatPath) { $Catalog = @(Get-Content $CatPath -Raw | ConvertFrom-Json -Depth 20) } + } catch { $Catalog = @() } + $SecurityControls = @(Convert-CippExecStandardsToControls -Compare @([pscustomobject]$TenantStd) -Catalog $Catalog) + } + } catch { Write-Information "Executive report: standards unavailable - $($_.Exception.Message)" } + + $Data = @{ + TenantName = $TenantName + UserStats = $UserStats + SecureScore = $SecureScore + Licenses = $Licenses + Devices = $Devices + CAPolicies = $CAPolicies + SecurityControls = $SecurityControls + } + + # Optional per-section toggles from the client's section panel (POST body). Absent -> full report. + $SectionConfig = @{} + $RawCfg = $Request.Body.sectionConfig + if ($RawCfg -is [hashtable]) { $SectionConfig = $RawCfg } + elseif ($RawCfg) { foreach ($p in $RawCfg.PSObject.Properties) { $SectionConfig[$p.Name] = [bool]$p.Value } } + + $Report = Build-CippExecutiveReportTree -Data $Data -SectionConfig $SectionConfig + + # Branding: a named preset if the client selected one (the report's Branding dropdown), else + # the tenant/global default. + $Bytes = ConvertTo-CippReportPdf -Blocks $Report.Blocks -Variables $Report.Variables -TenantName $TenantName -TenantFilter $TenantFilter ` + -ReportName 'Executive Summary' -BrandingPresetId $BrandingPresetId + + $FileName = ("Executive_Report_$TenantFilter" -replace '[^a-zA-Z0-9_\-]', '_') + '.pdf' + return ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + ContentType = 'application/pdf' + Headers = @{ 'Content-Disposition' = "inline; filename=`"$FileName`"" } + Body = $Bytes + }) + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -Headers $Request.Headers -API $APIName -message "Failed to render Executive report: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::InternalServerError; Body = "Error: $($ErrorMessage.NormalizedError)" }) + } +} diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecGetShadowAIReportPdf.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecGetShadowAIReportPdf.ps1 new file mode 100644 index 0000000000..a88ca43c51 --- /dev/null +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecGetShadowAIReportPdf.ps1 @@ -0,0 +1,56 @@ +function Invoke-ExecGetShadowAIReportPdf { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Tenant.Standards.Read + .DESCRIPTION + Server-renders the Shadow AI report as application/pdf bytes. Gathers the same shaped data the + Shadow AI page uses (ListShadowAI) and composes it through the shared CIPPSharp component kit + (Build-CippShadowAIReportTree) - the server-side replacement for the client react-pdf + ShadowAIReportButton. + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -Headers $Request.Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' + + try { + $TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter + if ([string]::IsNullOrWhiteSpace($TenantFilter)) { + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::BadRequest; Body = 'A tenantFilter is required' }) + } + $TenantName = Get-CippReportTenantName -TenantFilter $TenantFilter + + # Optional per-section toggles from the client's section panel (POST body). Absent -> full report. + $SectionConfig = @{} + $RawCfg = $Request.Body.sectionConfig + if ($RawCfg -is [hashtable]) { $SectionConfig = $RawCfg } + elseif ($RawCfg) { foreach ($p in $RawCfg.PSObject.Properties) { $SectionConfig[$p.Name] = [bool]$p.Value } } + + # The same shaped data the Shadow AI page reads (from the reporting cache). + $Raw = Get-CIPPShadowAIReport -TenantFilter $TenantFilter + $Report = Build-CippShadowAIReportTree -SectionConfig $SectionConfig -Data @{ + TenantName = $TenantName + summary = $Raw.summary + detectedApps = $Raw.detectedApps + consentedApps = $Raw.consentedApps + topTools = $Raw.topTools + byRisk = $Raw.byRisk + } + + $Bytes = ConvertTo-CippReportPdf -Blocks $Report.Blocks -Variables $Report.Variables -TenantName $TenantName -TenantFilter $TenantFilter -ReportName 'Shadow AI Report' + $FileName = ("Shadow_AI_Report_$TenantFilter" -replace '[^a-zA-Z0-9_\-]', '_') + '.pdf' + return ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + ContentType = 'application/pdf' + Headers = @{ 'Content-Disposition' = "inline; filename=`"$FileName`"" } + Body = $Bytes + }) + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -Headers $Request.Headers -API $APIName -message "Failed to render Shadow AI report: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::InternalServerError; Body = "Error: $($ErrorMessage.NormalizedError)" }) + } +} diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListShadowAI.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListShadowAI.ps1 index ff806f1c5a..405ea85f30 100644 --- a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListShadowAI.ps1 +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListShadowAI.ps1 @@ -15,247 +15,7 @@ function Invoke-ListShadowAI { param($Request, $TriggerMetadata) $TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter - - # Curated, PR-editable catalog of known AI tools/apps. - try { - $Catalog = @(Get-Content (Join-Path $env:CIPPRootPath 'Config\ShadowAI.json') -ErrorAction Stop | ConvertFrom-Json) - } catch { - Write-LogMessage -API 'ShadowAI' -tenant $TenantFilter -message "Could not load Shadow AI catalog. Error: $($_.Exception.Message)" -Sev 'Error' - $Catalog = @() - } - - # Returns the first catalog entry whose matchNames appear (case-insensitive substring) in $Text. - function Get-AiMatch { - param($Text, $Catalog) - if ([string]::IsNullOrWhiteSpace($Text)) { return $null } - $Haystack = $Text.ToLower() - foreach ($Entry in $Catalog) { - foreach ($Match in $Entry.matchNames) { - if ($Match -and $Haystack.Contains($Match.ToLower())) { return $Entry } - } - } - return $null - } - - $SanctionedTools = @{} - try { - $SanctionTable = Get-CIPPTable -TableName 'ShadowAIConfig' - $EscapedTenant = $TenantFilter -replace "'", "''" - foreach ($Row in @(Get-CIPPAzDataTableEntity @SanctionTable -Filter "PartitionKey eq '$EscapedTenant'")) { - $ToolName = if ($Row.Tool) { $Row.Tool } else { $Row.RowKey } - if ($ToolName) { $SanctionedTools[$ToolName.ToLower()] = $true } - } - } catch { - Write-LogMessage -API 'ShadowAI' -tenant $TenantFilter -message "Could not load sanctioned AI tools: $($_.Exception.Message)" -Sev 'Warning' - } - - # --- Cached datasets from the CIPP reporting database (no live Graph enumeration) --- - $CacheTypes = @('DetectedApps', 'ServicePrincipals', 'OAuth2PermissionGrants') - $CacheData = @{} - $CacheTimestamps = [System.Collections.Generic.List[object]]::new() - foreach ($Type in $CacheTypes) { - try { - $CacheData[$Type] = @(New-CIPPDbRequest -TenantFilter $TenantFilter -Type $Type) - } catch { - $CacheData[$Type] = @() - } - try { - $CountRow = Get-CIPPDbItem -TenantFilter $TenantFilter -Type $Type -CountsOnly | Select-Object -First 1 - if ($CountRow.Timestamp) { $CacheTimestamps.Add($CountRow.Timestamp) } - } catch {} - } - $IntuneSynced = $CacheData['DetectedApps'].Count -gt 0 - $EntraSynced = $CacheData['ServicePrincipals'].Count -gt 0 - $LastDataRefresh = $CacheTimestamps | Sort-Object | Select-Object -First 1 - - # 1) Installed AI tools from the cached Intune detected apps. The inventory reports a separate - # application entry per version (and per install flavor, e.g. 'Copilot' vs 'Microsoft.Copilot'), - # so merge everything that matches the same catalog tool into ONE row: distinct devices only, - # with the observed application names, versions and platforms combined. - $DetectedAppMap = [ordered]@{} - foreach ($App in $CacheData['DetectedApps']) { - $Match = Get-AiMatch -Text "$($App.displayName) $($App.publisher)" -Catalog $Catalog - if (-not $Match) { continue } - if (-not $DetectedAppMap.Contains($Match.name)) { - $DetectedAppMap[$Match.name] = [PSCustomObject]@{ - Match = $Match - Sanctioned = $SanctionedTools.ContainsKey($Match.name.ToLower()) - Applications = [System.Collections.Generic.List[string]]::new() - Publishers = [System.Collections.Generic.List[string]]::new() - Versions = [System.Collections.Generic.List[string]]::new() - Platforms = [System.Collections.Generic.List[string]]::new() - Devices = [ordered]@{} - } - } - $Entry = $DetectedAppMap[$Match.name] - if ($App.displayName -and $Entry.Applications -notcontains [string]$App.displayName) { $Entry.Applications.Add([string]$App.displayName) } - if ($App.publisher -and $Entry.Publishers -notcontains [string]$App.publisher) { $Entry.Publishers.Add([string]$App.publisher) } - if ($App.version -and $Entry.Versions -notcontains [string]$App.version) { $Entry.Versions.Add([string]$App.version) } - $Platform = if ([string]::IsNullOrWhiteSpace($App.platform)) { 'Unknown' } else { [string]$App.platform } - if ($Entry.Platforms -notcontains $Platform) { $Entry.Platforms.Add($Platform) } - foreach ($Device in @($App.managedDevices ?? @())) { - $DeviceKey = if ($Device.id) { [string]$Device.id } else { [string]$Device.deviceName } - if ($DeviceKey -and -not $Entry.Devices.Contains($DeviceKey)) { $Entry.Devices[$DeviceKey] = $Device } - } - } - - $DetectedApps = [System.Collections.Generic.List[object]]::new() - foreach ($Entry in $DetectedAppMap.Values) { - $Match = $Entry.Match - # Inventory rows mix clean publisher names with full certificate subjects - show the shortest - $Publisher = $Entry.Publishers | Sort-Object -Property Length | Select-Object -First 1 - $DetectedApps.Add([PSCustomObject]@{ - application = ($Entry.Applications | Sort-Object) -join ', ' - aiTool = $Match.name - vendor = $Match.vendor - category = $Match.category - risk = if ($Entry.Sanctioned) { 'Informational' } else { $Match.risk } - catalogRisk = $Match.risk - status = if ($Entry.Sanctioned) { 'Sanctioned' } else { 'Unsanctioned' } - toolDescription = $Match.description - riskReason = $Match.riskReason - publisher = $Publisher - version = ($Entry.Versions | Sort-Object) -join ', ' - platform = ($Entry.Platforms | Sort-Object) -join ', ' - deviceCount = $Entry.Devices.Count - managedDevices = @($Entry.Devices.Values) - }) - } - - # 2) AI applications in Entra: match ALL cached service principals (not only those with - # delegated grants), then attach any granted permissions. First consented = when the - # service principal was created in the tenant (the oauth2 grant startTime is unreliable). - $GrantsBySp = @{} - foreach ($Grant in $CacheData['OAuth2PermissionGrants']) { - if (-not $Grant.clientId) { continue } - if (-not $GrantsBySp.ContainsKey($Grant.clientId)) { - $GrantsBySp[$Grant.clientId] = [System.Collections.Generic.List[object]]::new() - } - $GrantsBySp[$Grant.clientId].Add($Grant) - } - - $ConsentedApps = [System.Collections.Generic.List[object]]::new() - $SeenApps = @{} - foreach ($Sp in $CacheData['ServicePrincipals']) { - $Match = Get-AiMatch -Text $Sp.displayName -Catalog $Catalog - if (-not $Match) { continue } - $Key = [string]($Sp.appId ?? $Sp.id) - if ($SeenApps.ContainsKey($Key)) { continue } - # Individual scopes as a string array so the frontend renders them as chips. - $Permissions = if ($GrantsBySp.ContainsKey($Sp.id)) { - @((@($GrantsBySp[$Sp.id].scope) -join ' ') -split '\s+' | Where-Object { $_ } | Sort-Object -Unique) - } else { - @() - } - $IsSanctioned = $SanctionedTools.ContainsKey($Match.name.ToLower()) - $Consent = [PSCustomObject]@{ - application = $Sp.displayName - aiTool = $Match.name - vendor = $Match.vendor - category = $Match.category - risk = if ($IsSanctioned) { 'Informational' } else { $Match.risk } - catalogRisk = $Match.risk - status = if ($IsSanctioned) { 'Sanctioned' } else { 'Unsanctioned' } - toolDescription = $Match.description - riskReason = $Match.riskReason - applicationId = $Sp.appId - approvedPermissions = @($Permissions) - firstConsentedDateTime = $Sp.createdDateTime - signInsLast7Days = 0 - activeUsersLast7Days = 0 - applicationUsers = @() - } - $SeenApps[$Key] = $Consent - $ConsentedApps.Add($Consent) - } - - # 2b) Best-effort: recent sign-in usage (last 7 days) for the matched AI apps. This is the only - # live Graph call: a single bounded query, skipped gracefully when unavailable (needs P1). - $AiAppIds = @($ConsentedApps.applicationId | Where-Object { $_ } | Select-Object -Unique -First 15) - if ($AiAppIds.Count -gt 0) { - try { - $StartDate = (Get-Date).AddDays(-7).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') - $AppFilter = ($AiAppIds | ForEach-Object { "appId eq '$_'" }) -join ' or ' - $SignInFilter = "createdDateTime ge $StartDate and ($AppFilter)" - $SignIns = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/auditLogs/signIns?`$filter=$SignInFilter" -tenantid $TenantFilter - $SignInGroups = $SignIns | Group-Object appId - foreach ($Consent in $ConsentedApps) { - $Group = $SignInGroups | Where-Object { $_.Name -eq $Consent.applicationId } - if ($Group) { - $Consent.signInsLast7Days = $Group.Count - $Consent.activeUsersLast7Days = @($Group.Group.userId | Select-Object -Unique).Count - $Consent.applicationUsers = @($Group.Group | Group-Object userPrincipalName | ForEach-Object { - [PSCustomObject]@{ - userPrincipalName = $_.Name - userDisplayName = ($_.Group | Select-Object -First 1).userDisplayName - signIns = $_.Count - lastSignInDateTime = ($_.Group.createdDateTime | Sort-Object -Descending | Select-Object -First 1) - } - }) - } - } - } catch { - Write-LogMessage -API 'ShadowAI' -tenant $TenantFilter -message "Sign-in usage enrichment skipped (requires Entra ID P1). Error: $($_.Exception.Message)" -Sev 'Info' - } - } - - # --- Roll up distinct AI tools across BOTH sources for the summary and charts --- - $ToolMap = @{} - foreach ($App in $DetectedApps) { - if (-not $ToolMap.ContainsKey($App.aiTool)) { - $ToolMap[$App.aiTool] = [PSCustomObject]@{ Tool = $App.aiTool; Category = $App.category; Risk = $App.risk; Status = $App.status; Devices = 0; Users = 0 } - } - $ToolMap[$App.aiTool].Devices += $App.deviceCount - } - foreach ($App in $ConsentedApps) { - if (-not $ToolMap.ContainsKey($App.aiTool)) { - $ToolMap[$App.aiTool] = [PSCustomObject]@{ Tool = $App.aiTool; Category = $App.category; Risk = $App.risk; Status = $App.status; Devices = 0; Users = 0 } - } - $ToolMap[$App.aiTool].Users += [int]$App.activeUsersLast7Days - } - - $ByCategory = foreach ($Group in ($ToolMap.Values | Group-Object Category)) { - [PSCustomObject]@{ - category = $Group.Name - tools = $Group.Count - devices = [int](($Group.Group | Measure-Object -Property Devices -Sum).Sum) - } - } - $ByRisk = foreach ($Group in ($ToolMap.Values | Group-Object Risk)) { - [PSCustomObject]@{ - risk = $Group.Name - tools = $Group.Count - } - } - # Top tools across BOTH sources: device installs (Intune) + active users (Entra, last 7 days). - $TopTools = $ToolMap.Values | Sort-Object -Property { $_.Devices + $_.Users } -Descending | Select-Object -First 8 | ForEach-Object { - [PSCustomObject]@{ - tool = $_.Tool - devices = $_.Devices - users = $_.Users - footprint = $_.Devices + $_.Users - category = $_.Category - status = $_.Status - } - } - - $Body = [PSCustomObject]@{ - summary = [PSCustomObject]@{ - aiToolsDetected = $ToolMap.Count - deviceInstalls = [int](($DetectedApps | Measure-Object -Property deviceCount -Sum).Sum) - consentedAiApps = $ConsentedApps.Count - highRiskTools = @($ToolMap.Values | Where-Object { $_.Risk -eq 'High' }).Count - sanctionedTools = @($ToolMap.Values | Where-Object { $_.Status -eq 'Sanctioned' }).Count - intuneSynced = $IntuneSynced - entraSynced = $EntraSynced - lastDataRefresh = $LastDataRefresh - } - byCategory = @($ByCategory) - byRisk = @($ByRisk) - topTools = @($TopTools) - detectedApps = @($DetectedApps) - consentedApps = @($ConsentedApps) - } + $Body = Get-CIPPShadowAIReport -TenantFilter $TenantFilter return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tools/Report-Builder/Invoke-ExecGenerateReportBuilderReport.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tools/Report-Builder/Invoke-ExecGenerateReportBuilderReport.ps1 index 50a73242f5..890b2ceb8f 100644 --- a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tools/Report-Builder/Invoke-ExecGenerateReportBuilderReport.ps1 +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tools/Report-Builder/Invoke-ExecGenerateReportBuilderReport.ps1 @@ -23,6 +23,11 @@ function Invoke-ExecGenerateReportBuilderReport { $ExistingEntity = Get-CIPPAzDataTableEntity @ReportTable -Filter "RowKey eq '$($Body.ReportGUID)'" if ($ExistingEntity) { Remove-CIPPAzDataTableEntity @ReportTable -Entity $ExistingEntity + # The rendered PDF sits in its own table; a large one is split across part rows, so fetch + # the raw head + part rows (keys only, no split markers) and hand them all to the remover. + $PdfTable = Get-CippTable -tablename 'ReportBuilderPdfs' + $PdfRows = @(Get-CIPPAzDataTableEntity @PdfTable -Filter "RowKey eq '$($Body.ReportGUID)' or OriginalEntityId eq '$($Body.ReportGUID)'" -Property PartitionKey, RowKey) + if ($PdfRows.Count -gt 0) { Remove-CIPPAzDataTableEntity @PdfTable -Entity $PdfRows } Write-LogMessage -headers $Headers -API $APIName -message "Deleted generated report '$($Body.ReportGUID)'" -Sev 'Info' $Result = @{ Results = 'Successfully deleted generated report' } } else { @@ -53,8 +58,12 @@ function Invoke-ExecGenerateReportBuilderReport { $GenerateResult = Push-ExecGenerateReportBuilderReport @GenerateParams Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message "Generated report builder report '$TemplateName'" -Sev 'Info' + # Push-* returns either the plain result string, or an envelope carrying base64 email + # attachments for the scheduled path. The interactive HTTP response only needs the message - + # the finished PDF is fetched from ExecGetReportBuilderPdf, not echoed here as base64. + $ResultText = if ($GenerateResult -is [System.Collections.IDictionary] -and $GenerateResult['Results']) { $GenerateResult['Results'] } else { $GenerateResult } $Result = @{ - Results = $GenerateResult + Results = $ResultText } $StatusCode = [HttpStatusCode]::OK @@ -68,6 +77,6 @@ function Invoke-ExecGenerateReportBuilderReport { return ([HttpResponseContext]@{ StatusCode = $StatusCode - Body = ConvertTo-Json -InputObject $Result -Depth 20 + Body = $Result }) } diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tools/Report-Builder/Invoke-ExecGetReportBuilderPdf.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tools/Report-Builder/Invoke-ExecGetReportBuilderPdf.ps1 new file mode 100644 index 0000000000..c3a7b1502b --- /dev/null +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tools/Report-Builder/Invoke-ExecGetReportBuilderPdf.ps1 @@ -0,0 +1,46 @@ +function Invoke-ExecGetReportBuilderPdf { + <# + .FUNCTIONALITY + Entrypoint,AnyTenant + .ROLE + CIPP.Core.Read + .DESCRIPTION + Returns the server-rendered PDF for a generated Report Builder report as application/pdf bytes. + Backs both the in-app preview (shown in an iframe) and the download button on the view page. + 404 when the report has no rendered PDF (generated before server-side rendering, or its render + failed). + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -Headers $Request.Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' + + try { + # The generated report's GUID. + $ReportGUID = $Request.Query.id ?? $Request.Query.ReportGUID + if ([string]::IsNullOrEmpty($ReportGUID)) { + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::BadRequest; Body = 'A report id is required' }) + } + $ReportGUID = ConvertTo-CIPPODataFilterValue -Value $ReportGUID -Type 'Guid' + + # The PDF lives in its own table (keyed by the report GUID) so listing reports never pulls the + # base64. No -Property projection: the merge-aware read reassembles a PDF split across part rows. + $Table = Get-CippTable -tablename 'ReportBuilderPdfs' + $Row = Get-CIPPAzDataTableEntity @Table -Filter "RowKey eq '$ReportGUID'" | Select-Object -First 1 + if ([string]::IsNullOrEmpty($Row.Pdf)) { + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::NotFound; Body = 'This report has no rendered PDF. Regenerate it to produce one.' }) + } + + return ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + ContentType = 'application/pdf' + Headers = @{ 'Content-Disposition' = "inline; filename=`"$($Row.FileName ?? "Report_$ReportGUID.pdf")`"" } + Body = [Convert]::FromBase64String($Row.Pdf) + }) + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -Headers $Request.Headers -API $APIName -message "Failed to fetch report PDF: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage + return ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::InternalServerError; Body = "Error: $($ErrorMessage.NormalizedError)" }) + } +} diff --git a/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tools/Report-Builder/Invoke-ExecPreviewReportBuilderPdf.ps1 b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tools/Report-Builder/Invoke-ExecPreviewReportBuilderPdf.ps1 new file mode 100644 index 0000000000..7cabd8e1e9 --- /dev/null +++ b/backend/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tools/Report-Builder/Invoke-ExecPreviewReportBuilderPdf.ps1 @@ -0,0 +1,57 @@ +function Invoke-ExecPreviewReportBuilderPdf { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + CIPP.Core.ReadWrite + .DESCRIPTION + Renders the current (unsaved) Report Builder state to a PDF and returns it as application/pdf + bytes without persisting a generated-report row. Powers the builder's live preview and download. + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + $Headers = $Request.Headers + + try { + $Body = $Request.Body + $TenantFilter = $Body.TenantFilter ?? $Request.Query.TenantFilter + if ([string]::IsNullOrEmpty($TenantFilter)) { throw 'TenantFilter is required' } + + $GenerateParams = @{ + TenantFilter = $TenantFilter + TemplateName = $Body.TemplateName ?? 'Report' + PreviewOnly = $true + } + if ($Body.Blocks) { + $GenerateParams.Blocks = if ($Body.Blocks -is [string]) { $Body.Blocks } else { ConvertTo-Json -InputObject @($Body.Blocks) -Depth 20 -Compress } + } + if ($Body.TemplateGUID) { $GenerateParams.TemplateGUID = $Body.TemplateGUID } + if ($Body.Settings) { + $GenerateParams.Settings = if ($Body.Settings -is [string]) { $Body.Settings } else { ConvertTo-Json -InputObject $Body.Settings -Depth 10 -Compress } + } + + $Preview = Push-ExecGenerateReportBuilderReport @GenerateParams + + if (-not $Preview.PdfBytes) { + return ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::InternalServerError + Body = 'Failed to render the report preview.' + }) + } + + return ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + ContentType = 'application/pdf' + Body = $Preview.PdfBytes + }) + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -headers $Headers -API $APIName -message "Report preview error: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage + return ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::InternalServerError + Body = "Error: $($ErrorMessage.NormalizedError)" + }) + } +} diff --git a/backend/Shared/CIPPSharp/CIPPSharp.csproj b/backend/Shared/CIPPSharp/CIPPSharp.csproj index 6c7a3f192b..d2b375fcb0 100644 --- a/backend/Shared/CIPPSharp/CIPPSharp.csproj +++ b/backend/Shared/CIPPSharp/CIPPSharp.csproj @@ -13,8 +13,17 @@ false bin\ false + + true - + + + + diff --git a/backend/Shared/CIPPSharp/Reporting/FontCmap.cs b/backend/Shared/CIPPSharp/Reporting/FontCmap.cs new file mode 100644 index 0000000000..b322e49711 --- /dev/null +++ b/backend/Shared/CIPPSharp/Reporting/FontCmap.cs @@ -0,0 +1,98 @@ +using System.Collections.Generic; +using System.Text; + +namespace CIPP.Reporting +{ + /// + /// A minimal read-only TrueType/OpenType cmap reader: enough to enumerate the Unicode code + /// points a font can draw, so the bundled emoji fallback is the single source of truth for both what + /// keeps and which ranges OfficeIMO routes to it. Reads the + /// Windows Unicode BMP subtable (format 4) and the full-repertoire subtable (format 12, which carries + /// the astral emoji); all multi-byte fields are big-endian per the sfnt spec. + /// + internal static class FontCmap + { + public static IEnumerable ReadCodepoints(byte[] f) + { + var result = new List(); + if (f.Length < 12) return result; + + var numTables = U16(f, 4); + var cmapOffset = -1; + for (var i = 0; i < numTables; i++) + { + var rec = 12 + i * 16; + if (rec + 16 > f.Length) break; + if (Encoding.ASCII.GetString(f, rec, 4) == "cmap") { cmapOffset = (int)U32(f, rec + 8); break; } + } + if (cmapOffset < 0 || cmapOffset + 4 > f.Length) return result; + + var subCount = U16(f, cmapOffset + 2); + var best4 = -1; + var best12 = -1; + for (var i = 0; i < subCount; i++) + { + var p = cmapOffset + 4 + i * 8; + if (p + 8 > f.Length) break; + var platform = U16(f, p); + var encoding = U16(f, p + 2); + var subOffset = cmapOffset + (int)U32(f, p + 4); + if (subOffset + 2 > f.Length) continue; + var format = U16(f, subOffset); + if (format == 12 && (platform == 3 && encoding == 10 || platform == 0)) best12 = subOffset; + else if (format == 4 && best4 < 0 && (platform == 3 && encoding == 1 || platform == 0)) best4 = subOffset; + } + + if (best12 >= 0) ReadFormat12(f, best12, result); + if (best4 >= 0) ReadFormat4(f, best4, result); + return result; + } + + private static void ReadFormat4(byte[] f, int o, List outp) + { + var segX2 = U16(f, o + 6); + var segCount = segX2 / 2; + var endO = o + 14; + var startO = endO + segX2 + 2; + var deltaO = startO + segX2; + var rangeO = deltaO + segX2; + for (var s = 0; s < segCount; s++) + { + var end = U16(f, endO + s * 2); + var start = U16(f, startO + s * 2); + if (start == 0xFFFF) continue; + var delta = U16(f, deltaO + s * 2); + var ro = U16(f, rangeO + s * 2); + for (var c = start; c <= end; c++) + { + int g; + if (ro == 0) g = (c + delta) & 0xFFFF; + else + { + var gi = rangeO + s * 2 + ro + (c - start) * 2; + if (gi + 2 > f.Length) continue; + g = U16(f, gi); + if (g != 0) g = (g + delta) & 0xFFFF; + } + if (g != 0) outp.Add(c); + } + } + } + + private static void ReadFormat12(byte[] f, int o, List outp) + { + var nGroups = U32(f, o + 12); + for (long i = 0; i < nGroups; i++) + { + var p = o + 16 + (int)i * 12; + if (p + 12 > f.Length) break; + var startChar = U32(f, p); + var endChar = U32(f, p + 4); + for (var c = startChar; c <= endChar && c <= 0x10FFFF; c++) outp.Add((int)c); + } + } + + private static int U16(byte[] f, int o) => (f[o] << 8) | f[o + 1]; + private static long U32(byte[] f, int o) => ((long)f[o] << 24) | ((long)f[o + 1] << 16) | ((long)f[o + 2] << 8) | f[o + 3]; + } +} diff --git a/backend/Shared/CIPPSharp/Reporting/ReportComponents.cs b/backend/Shared/CIPPSharp/Reporting/ReportComponents.cs new file mode 100644 index 0000000000..e422cc67d5 --- /dev/null +++ b/backend/Shared/CIPPSharp/Reporting/ReportComponents.cs @@ -0,0 +1,1710 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using OfficeIMO.Drawing; +using OfficeIMO.Pdf; + +namespace CIPP.Reporting +{ + /// + /// The reusable component kit - the server port of reportPdfPrimitives.jsx. Every component takes the + /// (theme/styles/variables) and the current OfficeIMO , + /// and encapsulates all OfficeIMO calls. Reports compose these; no report inlines a raw OfficeIMO call. + /// + public static class ReportComponents + { + private const double CodeParagraphSize = ReportStyles.CodeBlock; + + // Make any raw string safe for the PDF standard fonts (strips/maps emoji etc.). + private static string San(string? s) => ReportMarkdown.Sanitize(s); + + // -- colour bridge -- + public static PdfColor Pdf(string hex) + { + var (r, g, b) = ColourMath.ToRgb(hex); + return PdfColor.FromRgb((byte)r, (byte)g, (byte)b); + } + + // -- images -- + /// Decode a data-URL/base64 image (or resolve a bundled /reportImages/ path) into bytes OfficeIMO can draw anywhere, or null. + public static byte[]? DecodeImage(string? dataUrl) + { + if (string.IsNullOrWhiteSpace(dataUrl)) return null; + var s = dataUrl.Trim(); + + // A bundled stock image referenced by the frontend path it has always used, e.g. + // "/reportImages/board.jpg". The browser used to fetch these from public/reportImages; the + // server ships the same files beside the assembly under reportImages/, so resolve the path to + // those bytes. Only the file name is honoured (no traversal), and only files that ship return. + var stock = ReportImagesPathPattern.Match(s); + if (stock.Success) return ReportImage(stock.Groups[1].Value); + + var comma = s.IndexOf("base64,", StringComparison.OrdinalIgnoreCase); + if (comma >= 0) s = s.Substring(comma + "base64,".Length); + byte[] bytes; + try { bytes = Convert.FromBase64String(s.Trim()); } catch { return null; } + return NormaliseImage(bytes); + } + + // Every raster OfficeIMO decodes (PNG, JPEG, GIF, BMP, TIFF, WebP) is handed to it as-is. An SVG + // is only accepted by the drawing API, not by flow images or page backgrounds, so it is rasterised + // once here through OfficeIMO's own SVG reader - transparent background, ~1200px on the long side, + // crisp at logo and cover sizes and a few KB for a typical logo - and then behaves like a PNG. + // Anything OfficeIMO cannot identify is dropped here rather than left to fail at serialisation. + // Add-CIPPImage enforces the same list at upload time. + private const double SvgRasterSize = 1200; + private static byte[]? NormaliseImage(byte[] bytes) + { + try + { + if (!OfficeImageReader.TryIdentifyByContent(bytes, null, out var info)) return null; + if (info.Format != OfficeImageFormat.Svg) return bytes; + if (!OfficeSvgDrawingReader.TryRead(bytes, out var drawing) || drawing.Width <= 0 || drawing.Height <= 0) return null; + return OfficeDrawingRasterRenderer.ToPng(drawing, SvgRasterSize / Math.Max(drawing.Width, drawing.Height), null); + } + catch { return null; } + } + + private static readonly Regex ReportImagesPathPattern = + new(@"(?:^|/)reportImages/([A-Za-z0-9_.\-]+\.(?:jpg|jpeg|png|gif|webp))$", RegexOptions.IgnoreCase | RegexOptions.Compiled); + + // Stock report images (cover/hero photos) ship beside the assembly under reportImages/, mirroring + // how the twemoji PNG set and the fallback font are placed. Loaded once per file and cached. + private static readonly Lazy ReportImageDir = new(() => + { + try + { + var d = Path.GetDirectoryName(typeof(ReportComponents).Assembly.Location); + if (string.IsNullOrEmpty(d)) return null; + var dir = Path.Combine(d, "reportImages"); + return Directory.Exists(dir) ? dir : null; + } + catch { return null; } + }); + + private static readonly ConcurrentDictionary ReportImageCache = new(StringComparer.OrdinalIgnoreCase); + + /// Load a bundled stock report image by file name (no path segments), or null if it is not shipped. + public static byte[]? ReportImage(string? fileName) + { + if (string.IsNullOrWhiteSpace(fileName)) return null; + var key = Path.GetFileName(fileName); // defence in depth: strip any path + return ReportImageCache.GetOrAdd(key, k => + { + var dir = ReportImageDir.Value; + if (dir is null) return null; + try + { + var path = Path.Combine(dir, k); + return File.Exists(path) ? File.ReadAllBytes(path) : null; + } + catch { return null; } + }); + } + + /// + /// The box a branding logo is drawn in: the client sets a fixed height (cover 100pt, page header + /// 30pt) and lets the width follow the image's aspect ratio; the width cap keeps a banner-shaped + /// logo off the cover date and out of the page-header title column. + /// + public static (double w, double h) LogoBox(byte[] bytes, double height, double maxWidth) + { + var (pxW, pxH) = ImageSize(bytes); + var w = pxW * (height / pxH); + if (w > maxWidth) { height *= maxWidth / w; w = maxWidth; } + return (Math.Round(w), Math.Round(height)); + } + + /// The MIME type a drawing needs beside the bytes, from the content itself; null when OfficeIMO cannot identify it (skipped rather than failing). + public static string? ImageContentType(byte[] b) + { + try { return OfficeImageReader.TryIdentifyByContent(b, null, out var info) ? info.MimeType : null; } + catch { return null; } + } + + /// Pixel size of any image OfficeIMO identifies (a 4:3 guess for anything it cannot). + public static (int w, int h) ImageSize(byte[] b) + { + try + { + if (OfficeImageReader.TryIdentifyByContent(b, null, out var info) && info.Width > 0 && info.Height > 0) + return (info.Width, info.Height); + } + catch { /* fall through to the guess */ } + return (800, 600); + } + + // -- inline runs -- + // `size` is the base font size for the paragraph's runs (markdown has no size marks, so every run + // in a paragraph shares it). Without an explicit size OfficeIMO falls back to its ~12pt default, + // which is why body copy rendered far larger than the client's 9pt. + private static void ApplyRuns(PdfParagraphBuilder b, IReadOnlyList runs, string bodyColour, double size) + { + if (runs.Count == 0) { b.Text(" "); return; } + b.Color(Pdf(bodyColour)).FontSize(size); + foreach (var r in runs) + { + // Emoji split out to inline colour images (or a monochrome glyph) so the surrounding copy + // keeps its font/weight and an emoji never lands in a bold/italic run the fallback can't draw. + foreach (var seg in SegmentEmoji(r.Text)) + { + switch (seg.Kind) + { + case EmojiSegKind.Image: + b.InlineImage(seg.Image!, size, size, seg.Text, OfficeImageFit.Contain, EmojiOffset(size)); + break; + case EmojiSegKind.Mono: + b.Bold(false).Italic(false).Underline(false).Strike(false).Font(PdfStandardFont.Helvetica).Color(Pdf(seg.Tint ?? bodyColour)).Text(seg.Text); + break; + default: + b.Bold(r.Bold).Italic(r.Italic).Underline(r.Underline).Strike(r.Strike) + .Font(r.Code ? PdfStandardFont.Courier : PdfStandardFont.Helvetica).Color(Pdf(bodyColour)).Text(seg.Text); + break; + } + } + } + } + + // -- primitives -- + public static void Heading(ReportContext ctx, PdfContentBuilder item, int level, IReadOnlyList runs) + { + var text = RunsToPlain(runs); + var colour = level switch { 1 => ReportColours.Ink, 2 => ctx.Theme.Palette["heading"], _ => ReportColours.Body }; + var size = level switch { 1 => ReportStyles.Heading1, 2 => ReportStyles.Heading2, _ => ReportStyles.Heading3 }; + // A markdown heading is drawn as a bold paragraph (rather than item.H1/H2/H3) so an emoji in the + // heading renders as an inline colour image instead of a monochrome font glyph. + item.Paragraph(b => { b.FontSize(size); EmitInline(b, text, colour, size, bold: true); }, + PdfAlign.Left, null, new PdfParagraphStyle { LineHeight = 1.15, SpacingAfter = 6 }); + } + + // The text style a run of copy is drawn with. Threaded from the enclosing component so body copy, + // callout text and captions each render at their own size/colour/alignment - the server mirror of + // the client's context-driven styles. + public readonly struct TextStyle + { + public double Size { get; init; } + public string Colour { get; init; } + public PdfAlign Align { get; init; } + public double LineHeight { get; init; } + public double SpacingAfter { get; init; } + } + + // Body copy: 9pt, justified. The client uses lineHeight 1.5 / 8pt after, but OfficeIMO's leading + // renders looser at the same numbers, so a tighter 1.35 / 6pt gives the client's visual density. + public static TextStyle BodyStyle(ReportContext ctx) => new() + { + Size = ReportStyles.Body, Colour = ctx.Theme.Palette["body"], Align = PdfAlign.Justify, LineHeight = 1.35, SpacingAfter = 6, + }; + + public static void Paragraph(ReportContext ctx, PdfContentBuilder item, IReadOnlyList runs, TextStyle? style = null) + { + var st = style ?? BodyStyle(ctx); + item.Paragraph(b => ApplyRuns(b, runs, st.Colour, st.Size), st.Align, null, + new PdfParagraphStyle { LineHeight = st.LineHeight, SpacingAfter = st.SpacingAfter }); + } + + // Section title: 14pt bold heading colour (client styles.sectionTitle marginBottom 8, tightened to + // 5 to offset OfficeIMO's looser leading around the heading). + public static void SectionTitle(ReportContext ctx, PdfContentBuilder item, string title) + => item.Paragraph(b => { b.FontSize(ReportStyles.SectionTitle); EmitInline(b, title, ctx.Theme.Palette["heading"], ReportStyles.SectionTitle, bold: true); }, + PdfAlign.Left, null, new PdfParagraphStyle { LineHeight = 1.1, SpacingAfter = 5 }); + + // A callout's list rendered as one paragraph, a line break between items (a panel ignores list + // styling, so this matches the client's single-text-block callout bullets). `marker(i)` prefixes + // each line (a bullet dot, or "N. " for numbered). + private static void BulletLines(ReportContext ctx, PdfContentBuilder item, IReadOnlyList items, TextStyle ts, Func marker) + { + if (items.Count == 0) return; + item.Paragraph(b => + { + b.FontSize(ts.Size).Color(Pdf(ts.Colour)); + for (var i = 0; i < items.Count; i++) + { + if (i > 0) b.LineBreak(); + b.Color(Pdf(ts.Colour)).Text(marker(i)); + EmitToBuilder(b, San(items[i]), ts.Colour, ts.Size); + } + }, PdfAlign.Left, null, new PdfParagraphStyle { LeftIndent = 12, SpacingAfter = ts.SpacingAfter, LineHeight = ts.LineHeight }); + } + + public static void Bullets(ReportContext ctx, PdfContentBuilder item, IEnumerable items, double? size = null) + => BulletParagraphs(ctx, item, new List(items), size ?? ReportStyles.BulletText, _ => "• "); + + // A bullet/numbered list drawn as one paragraph per item (marker + emoji-aware text) rather than + // item.Bullets, so an emoji in a list item renders as an inline colour image. Marker in the heading + // colour; matches the old ListStyle (indent 12, 4pt item spacing, 1.3 line height). + private static void BulletParagraphs(ReportContext ctx, PdfContentBuilder item, IReadOnlyList items, double size, Func marker) + { + var body = ctx.Theme.Palette["body"]; + var markerColour = ctx.Theme.Palette["heading"]; + for (var i = 0; i < items.Count; i++) + { + var idx = i; + item.Paragraph(b => + { + b.FontSize(size); + b.Bold(true).Color(Pdf(markerColour)).Text(marker(idx)); + EmitToBuilder(b, San(items[idx]), body, size); + }, PdfAlign.Left, null, new PdfParagraphStyle { LeftIndent = 12, SpacingAfter = 4, LineHeight = 1.3 }); + } + } + + public static void Numbered(ReportContext ctx, PdfContentBuilder item, IEnumerable items, int start, double? size = null) + => BulletParagraphs(ctx, item, new List(items), size ?? ReportStyles.BulletText, i => (start + i) + ". "); + + // The branded cover, drawn as a page-sized OfficeDrawing over an optional full-bleed cover photo + // (client CoverPage): date top-right, a rounded pill label chip, the two-tone title, the subtitle + // and tenant vertically placed, and the confidential note at the bottom. A drawing lets the chip + // be a real rounded pill and the confidential note sit at the page foot - neither is possible in + // the plain content flow. + public static void RenderCoverDrawing(ReportContext ctx, PdfContentBuilder item) + { + var w = ctx.ContentWidth - 2; + // A drawing exactly the content height is rejected as too tall (as with width): landscape trips + // this because the A4 long edge is a hair over the page height, so shave 2pt off both here. + var h = ctx.ContentHeight - 2; + const double leftPad = 28; + var coverText = ctx.Theme.Palette["coverText"]; + var subtitleC = ctx.Theme.Palette["subtitle"]; + var primary = ctx.Theme.Primary; + var dw = new OfficeDrawing(w, h); + + // Vertical anchors mirror the client cover's fixed paddings (page pad 60, a header row of the + // logo and date with a 40pt margin under it, hero paddingTop 24) rather than a proportion of + // page height, so the block lands where the react-pdf cover puts it. The drawing origin already + // sits at the page margin, so each client "from page top" figure is offset by PagePadding here. + const double coverPad = 60, headerGap = 40, heroPad = 24, dateLine = 11, logoHeight = 100; + var headerTop = coverPad - ReportStyles.PagePadding; + + // Client coverHeader: the branding logo on the left at 100pt (width by aspect ratio, capped so + // a banner never reaches the date), the date on the right, the two vertically centred on each + // other. Without a logo the row is just the date line, which is where the hero's 135pt top came + // from (60 + 11 + 40 + 24). + var logoType = ctx.Logo is { Length: > 0 } ? ImageContentType(ctx.Logo) : null; + var logoBox = logoType is null ? (w: 0.0, h: 0.0) : LogoBox(ctx.Logo!, logoHeight, 260); + var headerH = Math.Max(dateLine, logoBox.h); + if (logoType is not null) + dw.AddImage(ctx.Logo!, logoType, new OfficeImageProjection( + new OfficeImagePlacement(leftPad, headerTop, logoBox.w, logoBox.h), new OfficeImageSourceCrop(0, 0, 0, 0), 0, null, null, false, false)); + if (!string.IsNullOrEmpty(ctx.GeneratedOn)) + AddT(dw, San(ctx.GeneratedOn).ToUpperInvariant(), 0, headerTop + Math.Max(0, (headerH - 14) / 2), w, 14, 9, subtitleC, OfficeTextAlignment.Right); + + var coverLabel = (ctx.Variables.TryGetValue("coverlabel", out var cl) && !string.IsNullOrWhiteSpace(cl)) ? cl : "ASSESSMENT REPORT"; + coverLabel = San(coverLabel).ToUpperInvariant(); + var chipW = Math.Min(w - leftPad * 2, 26 + coverLabel.Length * 6.4); + var y = coverPad + headerH + headerGap + heroPad - ReportStyles.PagePadding; // client coverHero content top (chip) + var chip = OfficeShape.RoundedRectangle(chipW, 26, 13); chip.FillColor = OC(primary); + dw.AddShape(chip, leftPad, y); + // AddText seats the glyph near the top of its box, so a box that merely matches the pill leaves + // the label riding high; drop it so the 10pt label sits centred in the 26pt pill. + AddT(dw, coverLabel, leftPad, y + 9, chipW, 12, ReportStyles.CoverLabel, ctx.Theme.OnPrimary, OfficeTextAlignment.Center, true); + y += 58; // chip height (~28) + client marginBottom 30 + + // Cover title: an explicit covertitle/coveraccent override (client coverTitle/coverAccent, e.g. + // the BEC report's "BEC Compromise" / "Analysis") else the report name split on its last word. + string lead, accent; + if (ctx.Variables.TryGetValue("covertitle", out var ctv) && !string.IsNullOrWhiteSpace(ctv)) + { + lead = San(ctv).ToUpperInvariant(); + accent = (ctx.Variables.TryGetValue("coveraccent", out var cav) && !string.IsNullOrWhiteSpace(cav)) ? San(cav).ToUpperInvariant() : string.Empty; + } + else + { + var words = San(ctx.ReportName).ToUpperInvariant().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + lead = words.Length > 1 ? string.Join(" ", words[..^1]) : (words.Length == 1 ? words[0] : string.Empty); + accent = words.Length > 1 ? words[^1] : string.Empty; + } + // A long line shrinks to fit rather than running off the page ("QUARTERLY SECURITY" did): + // Helvetica Bold capitals average about 0.7em, so the size that fits is the box over that. + double FitTitle(string s) => Math.Min(ReportStyles.CoverTitle, (w - leftPad) / Math.Max(1, s.Length * 0.7)); + var titleSize = Math.Min(FitTitle(lead), FitTitle(accent)); + if (!string.IsNullOrEmpty(lead)) { AddT(dw, lead, leftPad, y, w - leftPad, 56, titleSize, coverText, OfficeTextAlignment.Left, true); y += 53; } + if (!string.IsNullOrEmpty(accent)) { AddT(dw, accent, leftPad, y, w - leftPad, 56, titleSize, primary, OfficeTextAlignment.Left, true); y += 53; } + if (!string.IsNullOrEmpty(lead) || !string.IsNullOrEmpty(accent)) y += 20; // client title marginBottom 20 + + if (ctx.Variables.TryGetValue("coversubtitle", out var cs) && !string.IsNullOrWhiteSpace(cs)) + { + // Wraps within a fixed-width box like the client subtitle (maxWidth 400); advance by the + // wrapped height (14pt at lineHeight 1.5 ~= 21/line) plus the client subtitle marginBottom 40. + const double subW = 400, subLine = 21; + var subLines = Math.Max(1, Math.Ceiling(San(cs).Length / 57.0)); + AddT(dw, San(cs), leftPad, y, subW, subLine * subLines + 4, ReportStyles.CoverSubtitle, subtitleC, OfficeTextAlignment.Left, false, true); + y += subLine * subLines + 40; + } + + // The subject line under the title: usually the tenant, but a covertenant override names a + // different subject (the BEC report puts the compromised user here instead of the tenant). + var coverTenant = (ctx.Variables.TryGetValue("covertenant", out var cvt) && !string.IsNullOrWhiteSpace(cvt)) ? cvt : ctx.TenantName; + if (!string.IsNullOrEmpty(coverTenant)) + { + AddT(dw, San(coverTenant), leftPad, y, w - leftPad, 24, 18, coverText, OfficeTextAlignment.Left, true); + y += 26; + } + // Optional cover meta (client CoverMeta): extra detail lines under the tenant, then a note. + if (ctx.Variables.TryGetValue("covermeta", out var cm) && !string.IsNullOrWhiteSpace(cm)) + foreach (var line in cm.Replace("\r", "").Split('\n')) + { AddT(dw, San(line), leftPad, y, w - leftPad * 2, 16, 12, subtitleC, OfficeTextAlignment.Left); y += 16; } + if (ctx.Variables.TryGetValue("covermetanote", out var cmn) && !string.IsNullOrWhiteSpace(cmn)) + AddT(dw, San(cmn), leftPad, y + 6, w - leftPad * 2, 16, 11, subtitleC, OfficeTextAlignment.Left); + + var note = "CONFIDENTIAL & PROPRIETARY"; + if (ctx.Variables.TryGetValue("coverfooternote", out var cfn) && !string.IsNullOrWhiteSpace(cfn)) note = cfn; + else if (!string.IsNullOrEmpty(ctx.Theme.CoverFooterText)) note = ctx.Theme.CoverFooterText; + note = San(ReportTheme.ApplyVariables(note, ctx.Variables)).ToUpperInvariant(); + AddT(dw, note, 0, h - 16, w, 14, 9, ctx.Theme.Palette["footer"], OfficeTextAlignment.Center); + + item.Drawing(dw, PdfAlign.Left); + } + + // Full-bleed hero content (client HeroPage overlay): the big highlight figure plus overtitle/ + // headline/subtext block, vertically centred and left-aligned, with the footer note bottom-right. + // Drawn as one page-sized OfficeDrawing (transparent) over the section's background image, because + // flow layout can neither vertically centre nor pin the footer to the bottom-right corner. + public static void RenderHeroDrawing(ReportContext ctx, PdfContentBuilder item, ReportNode block) + { + var w = ctx.ContentWidth - 2; + // See RenderCoverDrawing: a page-tall drawing is rejected in landscape without this 2pt shave. + var h = ctx.ContentHeight - 2; + var highlightColour = ctx.Theme.Palette["infographic"]; + var onDark = ctx.Theme.OnInfographic; + var overtitle = block.Str("overtitle") ?? block.Str("heroOvertitle"); + var highlight = block.Str("highlight") ?? block.Str("heroHighlight"); + var headline = block.Str("headline") ?? block.Str("heroHeadline") ?? block.Str("title"); + var subText = block.Str("subText") ?? block.Str("heroSubText"); + var footerText = block.Str("footerText") ?? block.Str("heroFooterText"); + const double leftPad = 28, textW = 440; + var subLines = string.IsNullOrEmpty(subText) ? Array.Empty() : subText.Replace("\r", "").Split('\n'); + + var blockH = (string.IsNullOrEmpty(overtitle) ? 0 : 24) + (string.IsNullOrEmpty(highlight) ? 0 : 84) + + (string.IsNullOrEmpty(headline) ? 0 : 24) + subLines.Length * 19; + var y = Math.Max(40, (h - blockH) / 2); + + var dw = new OfficeDrawing(w, h); + if (!string.IsNullOrEmpty(overtitle)) { AddT(dw, San(overtitle), leftPad, y, textW, 22, 18, onDark, OfficeTextAlignment.Left, true); y += 24; } + if (!string.IsNullOrEmpty(highlight)) { AddT(dw, San(highlight), leftPad, y, textW, 82, 72, highlightColour, OfficeTextAlignment.Left, true); y += 84; } + if (!string.IsNullOrEmpty(headline)) { AddT(dw, San(headline), leftPad, y, textW, 22, 18, onDark, OfficeTextAlignment.Left, true); y += 24; } + foreach (var line in subLines) { AddT(dw, San(line), leftPad, y, textW, 18, 14, onDark, OfficeTextAlignment.Left, true); y += 19; } + + if (!string.IsNullOrEmpty(footerText)) + { + var fLines = footerText.Replace("\r", "").Split('\n'); + var fy = h - 40 - (fLines.Length - 1) * 16; + foreach (var line in fLines) { AddT(dw, San(line), 0, fy, w - leftPad, 16, 12, onDark, OfficeTextAlignment.Right, true); fy += 16; } + } + item.Drawing(dw, PdfAlign.Left); + } + + /// Rich bullets (client BulletList with {label, text}): an orange marker, a bold label, then + /// body text - each an item.Paragraph with per-run colour so the marker and label differ from the text. + public static void RichBullets(ReportContext ctx, PdfContentBuilder item, List items) + { + foreach (var it in items) + { + var label = ReportNode.RowStr(it, "label"); + var text = ReportNode.RowStr(it, "text") ?? string.Empty; + var marker = ReportNode.RowStr(it, "marker"); // custom marker (e.g. "1.") else a bullet dot + item.Paragraph(b => + { + b.FontSize(ReportStyles.BulletText); + b.Bold(true).Color(Pdf(ctx.Theme.Palette["heading"])).Text((string.IsNullOrEmpty(marker) ? "•" : San(marker)) + " "); + var body = ctx.Theme.Palette["body"]; + if (!string.IsNullOrEmpty(label)) EmitToBuilder(b, San(label) + " ", body, ReportStyles.BulletText, bold: true); + EmitToBuilder(b, San(text), body, ReportStyles.BulletText, bold: false); + }, PdfAlign.Left, null, new PdfParagraphStyle { LeftIndent = 12, SpacingAfter = 4, LineHeight = 1.3 }); + } + } + + /// Brand-coloured table: header band in the tenant's table colour, striped rows, repeating header. + public static PdfTableStyle BrandedTableStyle(ReportContext ctx) => new() + { + HeaderFill = Pdf(ctx.Theme.Palette["table"]), + HeaderTextColor = Pdf(ctx.Theme.OnTable), + HeaderBold = true, + HeaderFontSize = ReportStyles.TableHeaderCell, + RowStripeFill = Pdf(ReportColours.Panel), + BorderColor = Pdf(ReportColours.Line), + BorderWidth = 0.5, + RowSeparatorColor = Pdf(ReportColours.Line), + RowSeparatorWidth = 0.5, + TextColor = Pdf(ReportColours.Body), + FontSize = ReportStyles.TableCell, + RepeatHeaderRowCount = 1, + CellPaddingX = 12, // client TABLE_ROW_PADDING (horizontal cell inset) + CellPaddingY = 6, // client tableRow paddingVertical + }; + + // The client renders header cells uppercase (styles.tableHeaderCell textTransform), so the header + // row (row 0) is upper-cased before it goes to OfficeIMO. + private static List UpperHeader(IEnumerable rows) + { + var list = rows.ToList(); + if (list.Count > 0) + list[0] = list[0].Select(c => (c ?? string.Empty).ToUpperInvariant()).ToArray(); + return list; + } + + public static void Table(ReportContext ctx, PdfContentBuilder item, IEnumerable rows) + => item.Table(UpperHeader(rows), PdfAlign.Left, BrandedTableStyle(ctx)); + + // Shared status vocabulary (client STATUS_TONES): a status word coloured by tone. + private static string ToneColour(string? tone) => tone switch + { + "pass" => ReportColours.Success, + "warn" => ReportColours.Warning, + "fail" => ReportColours.Danger, + "muted" => ReportColours.Faint, + _ => ReportColours.Body, + }; + + private static bool RowBool(object? row, string key) + => row is Dictionary d && d.TryGetValue(key, out var v) && v is bool b && b; + + /// + /// The client DataTable: column specs (header/key/width weight/bold/align) with per-cell rendering. + /// A column with toneField draws its value as italic status text coloured by the row's tone + /// field (Compliant=green, Review=red, ...); a bold column draws its value bold. Header band + /// in the brand table colour, uppercase; striped body rows; rows beyond limit drop to a note. + /// + public static void RichTable(ReportContext ctx, PdfContentBuilder item, List columns, List rows, int limit) + { + if (columns.Count == 0) return; + var widths = new List(); + var aligns = new List(); + var header = new List(); + foreach (var c in columns) + { + var w = ReportNode.RowNum(c, "width"); if (w <= 0) w = 1; + widths.Add(w); + aligns.Add((ReportNode.RowStr(c, "align")) switch { "center" => PdfColumnAlign.Center, "right" => PdfColumnAlign.Right, _ => PdfColumnAlign.Left }); + // Header runs (not a plain string) so an emoji in a column header renders as a colour image; + // the brand header fill/uppercase come from the table style, the text colour is OnTable bold. + header.Add(CellRuns((ReportNode.RowStr(c, "header") ?? string.Empty).ToUpperInvariant(), ctx.Theme.OnTable, bold: true, size: ReportStyles.TableHeaderCell)); + } + + var shown = limit > 0 ? rows.Take(limit).ToList() : rows; + var hidden = rows.Count - (limit > 0 ? Math.Min(limit, rows.Count) : rows.Count); + var cells = new List { header.ToArray() }; + foreach (var r in shown) + { + var rowCells = new PdfTableCell[columns.Count]; + for (var ci = 0; ci < columns.Count; ci++) + { + var col = columns[ci]; + var key = ReportNode.RowStr(col, "key") ?? string.Empty; + var text = San(ReportNode.RowStr(r, key) ?? string.Empty); + var toneField = ReportNode.RowStr(col, "toneField"); + var colourField = ReportNode.RowStr(col, "colourField"); + if (!string.IsNullOrEmpty(toneField)) + { + var colour = ToneColour(ReportNode.RowStr(r, toneField)); + rowCells[ci] = CellRuns(text, colour, bold: false, italic: true); + } + else if (!string.IsNullOrEmpty(colourField)) + { + // Bold text in an arbitrary per-row colour (client column.colour(row), e.g. risk bands). + var colour = ReportNode.RowStr(r, colourField); + rowCells[ci] = CellRuns(text, string.IsNullOrEmpty(colour) ? ReportColours.Body : colour, bold: true); + } + else if (RowBool(col, "bold")) + { + rowCells[ci] = CellRuns(text, ReportColours.Body, bold: true); + } + else + { + rowCells[ci] = CellRuns(text, ReportColours.Body); + } + } + cells.Add(rowCells); + } + + var style = BrandedTableStyle(ctx); + style.ColumnWidthWeights = widths; + style.Alignments = aligns; + item.Table(cells, PdfAlign.Left, style); + if (hidden > 0) + Note(ctx, item, $"... and {hidden} more. Export the table from the report page for the full list."); + } + + public static void Code(ReportContext ctx, PdfContentBuilder item, string text) + => item.Paragraph(b => + { + b.Font(PdfStandardFont.Courier).FontSize(CodeParagraphSize).Color(Pdf(ReportColours.Body)); + var lines = San(text).Replace("\r\n", "\n").Split('\n'); + for (var i = 0; i < lines.Length; i++) { if (i > 0) b.LineBreak(); b.Text(lines[i]); } + }); + + public static void Hr(ReportContext ctx, PdfContentBuilder item) => item.HR(); + + // A small italic aside after a truncated list (client styles.truncationNote): 8pt faint italic, + // indented to the table's inner padding. + public static void Note(ReportContext ctx, PdfContentBuilder item, string text) + => item.Paragraph(b => { b.Italic(true).FontSize(ReportStyles.TableCell); EmitInline(b, text, ReportColours.Faint, ReportStyles.TableCell); }, + PdfAlign.Left, null, new PdfParagraphStyle { LeftIndent = 12, SpacingAfter = 4 }); + + // -- callouts (InfoBox / AlertBox / ClearBox) -- + // Callouts are drawn as a single-cell bordered TABLE, not a Panel: OfficeIMO panels apply a fixed + // internal paragraph leading and ignore per-call line-height/spacing, which opens a loose gap + // between the title and body and between body lines. A table cell honours run sizing and cell + // padding exactly (the same reason StatCard is a cell), so the callout matches the client's tight + // spacing. The title is a bold run, then a small line-break run sets the title/body gap, then the + // body runs; a left accent stripe is a per-cell LeftBorder over the cell's full border. + private const double CalloutPadX = 12; // client infoBox/alertBox padding (horizontal) + private const double CalloutPadY = 12; // fallback vertical cell padding + private const double CalloutPadTop = 11; // top padding (client padding 12, less the cell's own top leading) + private const double CalloutPadBottom = 10; // bottom padding (client 12; the cell's line descent already adds space) + private const double CardCornerRadius = 6; // rounded corners on callout boxes and stat cards (client border-radius) + private const double CalloutGap = 12; // space after an InfoBox/ClearBox (client infoBox marginBottom 12) + private const double AlertGap = 16; // space after an AlertBox (client alertBox marginBottom 16) + private const double SectionGap = 12; // space before a new section's heading (client section marginBottom 12) + + private static PdfTextRun Run(string text, string colour, double size, bool bold = false, bool italic = false, bool underline = false, bool strike = false) + => new(San(text), bold, underline, Pdf(colour), italic, strike, size); + + // The brand tint for the three report emoji when they fall back to the monochrome font (no colour + // image bundled); every other emoji renders untinted. Null for anything that is not a report emoji. + public static string? EmojiTint(int cp) => + cp == ReportMarkdown.EmojiWarning ? "#DD9B26" : // amber + cp == ReportMarkdown.EmojiCheck ? "#2F9E44" : // green + cp == ReportMarkdown.EmojiInfo ? "#3182CE" : // blue + null; + + // An emoji renders at the run's font size, dropped a touch below the baseline so it sits like a glyph. + private const double EmojiBaselineFactor = -0.12; + private static double EmojiOffset(double size) => size * EmojiBaselineFactor; + + private enum EmojiSegKind { Text, Image, Mono } + private readonly struct EmojiSegment + { + public EmojiSegKind Kind { get; init; } + public string Text { get; init; } // Text: the copy; Image: alt text; Mono: the glyph + public byte[]? Image { get; init; } // Image: the colour PNG bytes + public string? Tint { get; init; } // Mono: brand tint hex, or null + } + + // Split (already-sanitized) text into text / colour-image / monochrome-glyph segments. An emoji + // grapheme cluster (including a multi-code-point ZWJ sequence, flag or skin-tone) routes to a bundled + // Twemoji colour image when one exists, else to the monochrome fallback font (tinted for the three + // report emoji); everything else stays as text, so ordinary copy is untouched. + private static IEnumerable SegmentEmoji(string text) + { + var list = new List(); + var sb = new System.Text.StringBuilder(); + void FlushText() { if (sb.Length > 0) { list.Add(new EmojiSegment { Kind = EmojiSegKind.Text, Text = sb.ToString() }); sb.Clear(); } } + var e = System.Globalization.StringInfo.GetTextElementEnumerator(text); + while (e.MoveNext()) + { + var cluster = (string)e.Current; + if (cluster.Length == 1 && cluster[0] <= 'ÿ') { sb.Append(cluster); continue; } + var bytes = TwemojiAssets.Enabled ? TwemojiAssets.Bytes(cluster) : null; + if (bytes is not null) { FlushText(); list.Add(new EmojiSegment { Kind = EmojiSegKind.Image, Image = bytes, Text = cluster }); continue; } + if (cluster.Length <= 2 && ReportMarkdown.RenderEmojiGlyphs) + { + var cp = char.ConvertToUtf32(cluster, 0); + if (ReportMarkdown.EmojiCoverage.Contains(cp) || EmojiTint(cp) is not null) + { FlushText(); list.Add(new EmojiSegment { Kind = EmojiSegKind.Mono, Text = cluster, Tint = EmojiTint(cp) }); continue; } + } + sb.Append(cluster); + } + FlushText(); + return list; + } + + // Emit text as OfficeIMO runs for table-cell content (callouts): each emoji becomes an inline colour- + // image run (or a monochrome glyph run when no image is bundled), the rest keeps the given colour and + // marks. A monochrome glyph carries no weight or decoration of its own, so it is emitted plain. + private static void EmitRuns(List dest, string text, string colour, double size, + bool bold = false, bool italic = false, bool underline = false, bool strike = false, + double? emojiSize = null, double? emojiOffset = null) + { + // An emoji image defaults to the run's font size, but a caller can shrink it and set its baseline + // offset (the stat card's big number sizes the emoji to ~the digit height and drops it onto the + // number's cap box, so it doesn't raise the line's ascent and push the number below a sibling card + // whose number has no emoji). + var es = emojiSize ?? size; + var eo = emojiOffset ?? EmojiOffset(es); + foreach (var seg in SegmentEmoji(San(text))) + { + switch (seg.Kind) + { + case EmojiSegKind.Image: + dest.Add(PdfTextRun.Inline(new PdfInlineImage(seg.Image!, es, es, seg.Text, OfficeImageFit.Contain, eo))); + break; + case EmojiSegKind.Mono: + dest.Add(new PdfTextRun(seg.Text, false, false, Pdf(seg.Tint ?? colour), false, false, size)); + break; + default: + dest.Add(new PdfTextRun(seg.Text, bold, underline, Pdf(colour), italic, strike, size)); + break; + } + } + } + + // A table cell whose text renders emoji as inline colour images (via EmitRuns) at the given colour + // and weight, so a body cell like "ok ✅" shows a colour glyph rather than a monochrome one. + private static PdfTableCell CellRuns(string text, string colour, bool bold = false, bool italic = false, double? size = null) + { + var runs = new List(); + EmitRuns(runs, text, colour, size ?? ReportStyles.TableCell, bold, italic); + return new PdfTableCell(runs); + } + + // Emit inline text (with colour-image emoji) into a paragraph builder. For callers that compose on a + // builder outside the component kit - the page header title/subtitle in ReportPdf. + public static void EmitInline(PdfParagraphBuilder b, string text, string colour, double size, bool bold = false) + => EmitToBuilder(b, San(text), colour, size, bold); + + // Convert parsed inline runs to OfficeIMO runs at a callout's size/colour, marks carried through, + // with kept emoji split into their own tinted runs. + private static IEnumerable ToPdfRuns(IEnumerable runs, string colour, double size) + { + var dest = new List(); + foreach (var r in runs) EmitRuns(dest, r.Text, colour, size, r.Bold, r.Italic, r.Underline, r.Strike); + return dest; + } + + // A callout body flattened to one run list: each logical line separated by a line-break run, so the + // cell renders it as tight consecutive lines. `lines` keeps the source '\n' splits (label/value + // detail lists); otherwise the markdown blocks are flattened (paragraphs, and bullets as "- " lines). + private static List CalloutBodyRuns(ReportContext ctx, string content, bool lines, double size, string colour) + { + var outRuns = new List(); + void AddLine(IEnumerable lineRuns) + { + if (outRuns.Count > 0) outRuns.Add(Run("\n", colour, size)); + outRuns.AddRange(lineRuns); + } + if (lines) + { + foreach (var ln in San(content).Replace("\r\n", "\n").Split('\n')) + AddLine(ToPdfRuns(ReportMarkdown.MarkdownRuns(ln), colour, size)); + } + else + { + foreach (var node in ReportMarkdown.MarkdownToNodes(content)) + { + switch (node.Type) + { + case "paragraph": + AddLine(ToPdfRuns(node.Get>("runs") ?? new List(), colour, size)); + break; + case "bullets": + foreach (var s in StringItems(node)) AddLine(new[] { Run("• " + s, colour, size) }); + break; + case "numbered": + var n = (int)(node.Num("start") ?? 1); + foreach (var s in StringItems(node)) AddLine(new[] { Run($"{n++}. " + s, colour, size) }); + break; + default: + var t = node.Str("content") ?? node.Str("text"); + if (!string.IsNullOrEmpty(t)) AddLine(ToPdfRuns(ReportMarkdown.MarkdownRuns(t), colour, size)); + break; + } + } + } + if (outRuns.Count == 0) outRuns.Add(Run(" ", colour, size)); + return outRuns; + } + + // The callout as stacked table rows: a title row (with its own bottom margin) over a body row (with + // a tight body line height), or a single body row when untitled. One bordered cell stack lets the + // title/body gap and the body line spacing each be set exactly - a single cell forces one uniform + // line advance (the title can't get its own margin), and a panel's leading can't be set at all. + private static List CalloutRows(string? title, string titleColour, double titleSize, List bodyRuns) + { + // One cell, one row: a multi-row table always draws a divider between rows (OfficeIMO borders are + // a grid), so title and body live in the same cell. The title is a bold run then a body-size line + // break; the cell's natural leading (no LineHeight override) then gives the taller title line its + // own margin above the body - matching the client's infoTitle marginBottom - while the body lines + // sit at the client's ~1.4 leading. This is why the callout uses no LineHeight. + var runs = new List(); + if (!string.IsNullOrEmpty(title)) + { + EmitRuns(runs, title!, titleColour, titleSize, bold: true); + runs.Add(Run("\n", titleColour, ReportStyles.InfoText)); + } + runs.AddRange(bodyRuns); + return new List { new[] { new PdfTableCell(runs) } }; + } + + private static PdfTableStyle CalloutStyle(string bgHex, string? stripeHex, double stripeWidth, string borderHex, double borderWidth) + { + // Single cell -> the table's BorderWidth draws just the perimeter box (no interior grid). The left + // edge is overridden with the accent stripe via a per-cell LeftBorder when the callout has one. + var style = new PdfTableStyle + { + HeaderRowCount = 0, + BorderColor = Pdf(borderHex), + BorderWidth = borderWidth, + CornerRadius = CardCornerRadius, // softly rounded box; stripe/border are clipped to the rounded corners + RowSeparatorWidth = 0, + CellPaddingX = CalloutPadX, + CellPaddingY = CalloutPadY, + CellPaddings = new Dictionary<(int, int), PdfCellPadding> + { + [(0, 0)] = new PdfCellPadding { Left = CalloutPadX, Right = CalloutPadX, Top = CalloutPadTop, Bottom = CalloutPadBottom }, + }, + SpacingAfter = 0, // the gap after a callout is an explicit Spacer, not the table's + KeepTogether = true, // a callout never splits across a page (client keeps each whole) + CellFills = new Dictionary<(int, int), PdfColor> { [(0, 0)] = Pdf(bgHex) }, + }; + if (stripeHex is not null) + style.CellBorders = new Dictionary<(int, int), PdfCellBorder> + { + // only the left stripe; disable the class-default grey sides so 3.4.x's general + // cell-border renderer doesn't lay grey over the accent at the rounded corners. + [(0, 0)] = new PdfCellBorder { LeftBorder = new PdfCellBorderSide { Color = Pdf(stripeHex), Width = stripeWidth }, Top = false, Right = false, Bottom = false }, + }; + return style; + } + + /// + /// A titled note with an accent stripe down its left edge (client InfoBox). `tone` (ok/warn) tints + /// the background and title; `colour` recolours the stripe (and the title when tintTitle). `content` + /// is markdown, or line-broken label/value text when `lines`. + /// + public static void InfoBox(ReportContext ctx, PdfContentBuilder item, string? title, string? tone, + string? colour, bool tintTitle, string content, bool lines = false) + { + var (accent, bg, titleColour) = InfoBoxColours(ctx, tone, colour, tintTitle); + var body = CalloutBodyRuns(ctx, content, lines, ReportStyles.InfoText, ctx.Theme.Palette["subtitle"]); + item.Table(CalloutRows(title, titleColour, ReportStyles.InfoTitle, body), PdfAlign.Left, CalloutStyle(bg, accent, 4, ReportColours.Line, 1)); + item.Spacer(CalloutGap); + } + + // An InfoBox's stripe accent, background tint and title colour: `tone` (ok/warn) picks the tint, + // `colour` overrides the stripe (and the title when tintTitle). + private static (string accent, string bg, string title) InfoBoxColours(ReportContext ctx, string? tone, string? colour, bool tintTitle) + { + var accent = string.IsNullOrEmpty(colour) ? ctx.Theme.Palette["card"] : colour!; + var (bg, title) = tone switch + { + "ok" => (ReportColours.OkBg, ReportColours.Success), + "warn" => (ReportColours.WarnBg, ReportColours.Warning), + _ => (ReportColours.Panel, ctx.Theme.Palette["body"]), + }; + if (tintTitle && !string.IsNullOrEmpty(colour)) title = colour!; + return (accent, bg, title); + } + + /// A warning callout: red-tinted background with a full accent-coloured border (client AlertBox). + public static void AlertBox(ReportContext ctx, PdfContentBuilder item, string? title, string? colour, string content, bool lines = false) + { + var accent = string.IsNullOrEmpty(colour) ? ctx.Theme.Palette["card"] : colour!; + var body = CalloutBodyRuns(ctx, content, lines, ReportStyles.AlertText, ctx.Theme.Palette["body"]); + item.Table(CalloutRows(title, accent, ReportStyles.AlertTitle, body), PdfAlign.Left, CalloutStyle(ReportColours.AlertBg, null, 0, accent, 2)); + item.Spacer(AlertGap); + } + + /// The all-clear counterpart to AlertBox - a green InfoBox for a check that found nothing. + public static void ClearBox(ReportContext ctx, PdfContentBuilder item, string? title, string content, bool lines = false) + => InfoBox(ctx, item, title, "ok", null, false, content, lines); + + // Body copy stepped in under a heading (client Paragraph indent: marginLeft 12, marginTop 8). Used + // for the BEC summary lines that introduce a check's detail callouts. + public static void IndentedParagraph(ReportContext ctx, PdfContentBuilder item, string text) + { + item.Spacer(4); + var body = ctx.Theme.Palette["body"]; + item.Paragraph(b => { b.FontSize(ReportStyles.Body); EmitToBuilder(b, San(text), body, ReportStyles.Body); }, + PdfAlign.Left, null, new PdfParagraphStyle { LeftIndent = 12, LineHeight = 1.3, SpacingAfter = 0 }); + } + + // Write text into a paragraph builder: emoji as inline colour images (or a monochrome glyph), the + // rest as coloured text - the paragraph equivalent of EmitRuns, for contexts that compose directly + // on a PdfParagraphBuilder. + private static void EmitToBuilder(PdfParagraphBuilder b, string text, string colour, double size, bool bold = false) + { + foreach (var seg in SegmentEmoji(text)) + { + switch (seg.Kind) + { + case EmojiSegKind.Image: + b.InlineImage(seg.Image!, size, size, seg.Text, OfficeImageFit.Contain, EmojiOffset(size)); + break; + case EmojiSegKind.Mono: + b.Bold(false).Color(Pdf(seg.Tint ?? colour)).Text(seg.Text); + break; + default: + b.Bold(bold).Color(Pdf(colour)).Text(seg.Text); + break; + } + } + } + + /// + /// A grid of InfoBoxes laid out `cols` per row (client `Columns` of callouts, e.g. the Shadow AI + /// risk-level pairs). Each item is a node with title/content/tone/colour/tintTitle. Short final rows + /// are padded so columns keep their width. + /// + public static void InfoBoxColumns(ReportContext ctx, PdfContentBuilder item, List items, int cols) + { + if (items.Count == 0) return; + if (cols < 1) cols = 1; + var width = 100.0 / cols; + for (var start = 0; start < items.Count; start += cols) + { + var slice = items.Skip(start).Take(cols).ToList(); + item.Row(r => + { + r.Gap(10); + foreach (var node in slice) + r.PercentColumn(width, col => InfoBoxCol(ctx, col, + ReportNode.RowStr(node, "title"), ReportNode.RowStr(node, "tone"), + ReportNode.RowStr(node, "colour"), RowBool(node, "tintTitle"), + ReportNode.RowStr(node, "content") ?? string.Empty)); + for (var k = slice.Count; k < cols; k++) r.PercentColumn(width, _ => { }); + }); + item.Spacer(CalloutGap); + } + } + + // One InfoBox rendered inside a row column, as the same single-cell table used full width. + private static void InfoBoxCol(ReportContext ctx, PdfContentBuilder col, string? title, string? tone, + string? colour, bool tintTitle, string content) + { + var (accent, bg, titleColour) = InfoBoxColours(ctx, tone, colour, tintTitle); + var body = CalloutBodyRuns(ctx, content, false, ReportStyles.InfoText, ctx.Theme.Palette["subtitle"]); + col.Table(CalloutRows(title, titleColour, ReportStyles.InfoTitle, body), PdfAlign.Left, CalloutStyle(bg, accent, 4, ReportColours.Line, 1)); + } + + // Series colour for a chart entry: its own colour, else the theme series cycled by index. + private static string SeriesColour(ReportContext ctx, string? own, int index) + { + if (!string.IsNullOrEmpty(own)) return own; + var s = ctx.Theme.Series; + return s.Count > 0 ? s[index % s.Count] : ctx.Theme.Palette["chart"]; + } + + // A row of stat cards. Each card is a single-column table inside its own row column, because a + // panel inside a row column left-aligns its text regardless of alignment, whereas a table cell + // honours Alignments.Center - so the number and label sit centred like the client statCard. The + // gaps between cards come from the row gap; the brand accent is the card's top border. + public static void StatRow(ReportContext ctx, PdfContentBuilder item, List stats) + { + if (stats.Count == 0) return; + var accent = ctx.Theme.Palette["card"]; + var width = 100.0 / stats.Count; // column widths are percentages and must sum to ~100 + // Each card is its own single-cell table, so a caption on one card makes only that card taller + // and its border box no longer lines up with the rest. When any card in the row has a caption, + // every other card reserves a blank caption line so all the boxes stay the same height. + var reserveCaption = stats.Any(s => !string.IsNullOrEmpty(ReportNode.RowStr(s, "caption"))); + item.Row(r => + { + r.Gap(10); + foreach (var s in stats) + { + var value = ReportNode.RowStr(s, "value") ?? string.Empty; + var label = (ReportNode.RowStr(s, "label") ?? string.Empty).ToUpperInvariant(); + var caption = ReportNode.RowStr(s, "caption"); + var colour = ReportNode.RowStr(s, "colour") ?? accent; + r.PercentColumn(width, col => StatCard(ctx, col, value, label, caption, colour, accent, reserveCaption)); + } + }); + item.Spacer(8); + } + + private static void StatCard(ReportContext ctx, PdfContentBuilder col, string value, string label, string? caption, string colour, string accent, bool reserveCaption = false) + { + // One cell, the number over the label as separate runs split by a line break, so there is no + // internal row divider - just the outer card border and its brand top accent. + var runs = new List(); + // Size a number-adjacent emoji to ~the digit height and seat it on the digit cap box, so it does + // not raise the line's ascent - otherwise the number rides lower than a sibling card with no emoji + // (proportions measured for alignment: emoji ~0.55x the number, dropped ~0.18x onto the baseline). + EmitRuns(runs, value, colour, ReportStyles.StatNumber, bold: true, + emojiSize: ReportStyles.StatNumber * 0.55, emojiOffset: ReportStyles.StatNumber * -0.18); + runs.Add(Run("\n", colour, ReportStyles.StatNumber)); + EmitRuns(runs, label, ReportColours.Muted, ReportStyles.StatLabel, bold: true); + if (!string.IsNullOrEmpty(caption)) + { + runs.Add(Run("\n", ctx.Theme.Palette["subtitle"], ReportStyles.StatCaption)); + EmitRuns(runs, caption!, ctx.Theme.Palette["subtitle"], ReportStyles.StatCaption); + } + else if (reserveCaption) + { + // A blank caption line so this card matches the height of captioned cards in the same row. + runs.Add(Run("\n", ctx.Theme.Palette["subtitle"], ReportStyles.StatCaption)); + runs.Add(Run(" ", ctx.Theme.Palette["subtitle"], ReportStyles.StatCaption)); + } + var rows = new List { new[] { new PdfTableCell(runs) } }; + var style = new PdfTableStyle + { + HeaderRowCount = 0, + BorderColor = Pdf(ReportColours.Line), + BorderWidth = 1, + CornerRadius = CardCornerRadius, // softly rounded card; top accent bar is clipped to the rounded corners + RowSeparatorWidth = 0, + CellPaddingX = 6, + CellPaddingY = 8, + // The big number needs clear space under the top accent (a plain number otherwise sits tight + // against it), balanced by a comfortable bottom pad under the caption. + CellPaddings = new Dictionary<(int, int), PdfCellPadding> + { + [(0, 0)] = new PdfCellPadding { Left = 6, Right = 6, Top = 22, Bottom = 12 }, + }, + Alignments = new List { PdfColumnAlign.Center }, + CellFills = new Dictionary<(int, int), PdfColor> { [(0, 0)] = Pdf(ReportColours.White) }, + CellBorders = new Dictionary<(int, int), PdfCellBorder> + { + // only the top accent bar; disable the class-default grey sides so 3.4.x's general + // cell-border renderer doesn't lay grey over the accent at the rounded corners. + [(0, 0)] = new PdfCellBorder { TopBorder = new PdfCellBorderSide { Color = Pdf(accent), Width = 3 }, Left = false, Right = false, Bottom = false }, + }, + }; + col.Table(rows, PdfAlign.Left, style); + } + + // Labelled progress bars (client ProgressList): each item is its own bordered row box holding a + // bold label, a data bar over a grey track, and a bold value. One 1-row table per item gives the + // per-row border and lets the label/value be bold via cell runs; the row gap comes from a spacer. + public static void Progress(ReportContext ctx, PdfContentBuilder item, List items) + { + if (items.Count == 0) return; + for (var i = 0; i < items.Count; i++) + { + var it = items[i]; + var label = ReportNode.RowStr(it, "label") ?? string.Empty; + var value = ReportNode.RowNum(it, "value"); + var max = ReportNode.RowNum(it, "max"); if (max <= 0) max = 100; + var pct = Math.Max(0, Math.Min(1, value / max)); + var display = ReportNode.RowStr(it, "display"); + if (string.IsNullOrEmpty(display)) display = Math.Round(pct * 100) + "%"; + var colour = ReportNode.RowStr(it, "colour") ?? SeriesColour(ctx, null, i); + var rows = new List + { + new[] + { + CellRuns(label, ctx.Theme.Palette["body"], bold: true), + new PdfTableCell(string.Empty), + CellRuns(display!, ReportColours.Body, bold: true), + }, + }; + var style = new PdfTableStyle + { + HeaderRowCount = 0, + BorderColor = Pdf(ReportColours.Line), + BorderWidth = 1, + RowSeparatorWidth = 0, + CellPaddingX = 10, + CellPaddingY = 7, + ColumnWidthWeights = new List { 28, 58, 14 }, + Alignments = new List { PdfColumnAlign.Left, PdfColumnAlign.Left, PdfColumnAlign.Right }, + VerticalAlignments = new List { PdfCellVerticalAlign.Middle, PdfCellVerticalAlign.Middle, PdfCellVerticalAlign.Middle }, + CellFills = new Dictionary<(int, int), PdfColor> { [(0, 1)] = Pdf(ReportColours.Line) }, + CellDataBars = new Dictionary<(int, int), PdfCellDataBar> { [(0, 1)] = new PdfCellDataBar { Color = Pdf(colour), Ratio = pct, StartRatio = 0 } }, + }; + item.Table(rows, PdfAlign.Left, style); + item.Spacer(6); + } + } + + // -- charts (vector) -- + // Real vector charts, ported 1:1 from the client charts.jsx which draws SVG into a 400x200 viewBox. + // Built as an OfficeDrawing (a fixed-size composite that flows as one block and reserves its + // height - unlike item.Canvas, which paints page-absolute and lets neighbours overlap it). The + // drawing shares the SVG coordinate system (top-left origin, y down), so the geometry is copied + // verbatim: the 400x200 plot is centred in a bordered white frame, title above, caption below. + private const double ChartViewW = 400, ChartViewH = 200; + private const double ChartTitleSize = 10, ChartLabelSize = 7; + + private static OfficeColor OC(string hex) + { + var (r, g, b) = ColourMath.ToRgb(hex); + return OfficeColor.FromRgb((byte)r, (byte)g, (byte)b); + } + + private static string FmtNum(double v) + => v == Math.Floor(v) ? ((long)v).ToString(CultureInfo.InvariantCulture) : v.ToString("0.##", CultureInfo.InvariantCulture); + + // Positioned text into an OfficeDrawing at an explicit size/colour/alignment (AddText takes the + // size via the font, so every label supplies a Helvetica OfficeFontInfo). + private static void AddT(OfficeDrawing dw, string text, double x, double y, double w, double h, + double size, string colourHex, OfficeTextAlignment align, bool bold = false, bool wrap = false) + // The extended overload's wrapText makes a long block (the cover subtitle) fold inside its box + // instead of overrunning the page; the short overload leaves WrapText off (read-only afterwards). + => dw.AddText(text, x, y, w, h, + new OfficeFontInfo("Helvetica", size, bold ? OfficeFontStyle.Bold : OfficeFontStyle.Regular), + OC(colourHex), align, lineHeight: null, wrapText: wrap); + + public static void Chart(ReportContext ctx, PdfContentBuilder item, string? kind, List data, + string? title = null, string? caption = null, double? max = null, string? centreLabel = null, + double? availableWidth = null) + { + var k = (kind ?? "bar").ToLowerInvariant(); + // A drawing exactly the content width is rejected as too wide; availableWidth lets a chart draw + // inside a narrower column (half-width side-by-side charts) instead of the full page width. + var w = (availableWidth ?? ctx.ContentWidth) - 2; + const double pad = 16, titleH = 14, titleGap = 12, captionGap = 8, captionH = 10; + var hasTitle = !string.IsNullOrEmpty(title); + var hasCaption = !string.IsNullOrEmpty(caption); + var plotTop = pad + (hasTitle ? titleH + titleGap : 0); + // The plot's own coordinate width: the 400pt design width, or the drawing if it is narrower + // (a half-width chart), so the geometry scales down to fit rather than overflowing the frame. + var vw = Math.Min(ChartViewW, w); + var leftPad = Math.Max(0, (w - vw) / 2); + + var entries = data.Select((d, i) => ( + label: ReportNode.RowStr(d, "label") ?? string.Empty, + value: ReportNode.RowNum(d, "value"), + colour: SeriesColour(ctx, ReportNode.RowStr(d, "colour"), i))).ToList(); + + // A donut is drawn to its own height - the ring and the legend rows it needs - rather than + // the 400x200 box the bar and trend charts fill, so the frame closes up under the legend + // instead of leaving a band of white there and above the ring. + var viewH = k == "donut" && entries.Count > 0 ? DonutViewHeight(entries, vw) : ChartViewH; + var totalH = plotTop + viewH + pad + (hasCaption ? captionGap + captionH : 0); + + var dw = new OfficeDrawing(w, totalH); + var frame = OfficeShape.RoundedRectangle(w, totalH, 6); + frame.FillColor = OC(ReportColours.White); frame.StrokeColor = OC(ReportColours.Line); frame.StrokeWidth = 1; + dw.AddShape(frame, 0, 0); + if (hasTitle) + AddT(dw, San(title!), 0, pad, w, titleH, ChartTitleSize, ctx.Theme.Palette["body"], OfficeTextAlignment.Center, true); + + if (entries.Count == 0) + AddT(dw, "No data available for this chart.", 0, plotTop + viewH / 2 - 6, w, 12, ReportStyles.Body, ReportColours.Faint, OfficeTextAlignment.Center); + else if (k == "donut") DrawDonut(ctx, dw, entries, leftPad, plotTop, centreLabel, vw); + else if (k == "trend") DrawTrend(ctx, dw, entries, leftPad, plotTop, max, vw); + else DrawBar(ctx, dw, entries, leftPad, plotTop, vw); + + if (hasCaption) + AddT(dw, San(caption!), 0, plotTop + viewH + captionGap, w, captionH, ChartLabelSize, ctx.Theme.Palette["chart"], OfficeTextAlignment.Center); + + item.Drawing(dw, PdfAlign.Left); + item.Spacer(12); + } + + /// A chart block flagged to render at half the page width (so two can sit side by side). + public static bool IsHalfWidthChart(ReportNode block) + => block.Type == "chart" && string.Equals(block.Str("width"), "half", StringComparison.OrdinalIgnoreCase); + + // Render a chart block, optionally into a given width (a half-width column). Shared by the block + // dispatch (a lone half-width chart) and the side-by-side pair renderer. + private static void ChartFromBlock(ReportContext ctx, PdfContentBuilder item, ReportNode block, double? availableWidth) + => Chart(ctx, item, block.Str("chartKind"), block.ListOf("chartData") ?? new List(), + block.Str("title"), block.Str("caption") ?? block.Str("chartCaption"), + block.Num("max") ?? ParseNumber(block.Str("chartMax")), block.Str("centreLabel") ?? block.Str("chartCentreLabel"), + availableWidth); + + /// Two half-width charts side by side in one row (the caller has checked both qualify). + public static void RenderChartPair(ReportContext ctx, PdfContentBuilder item, ReportNode a, ReportNode b, bool firstOnPage) + { + const double gap = 16; + var colWidth = (ctx.ContentWidth - gap) / 2; + item.Row(r => + { + r.Gap(gap); + r.PercentColumn(50, col => ChartFromBlock(ctx, col, a, colWidth)); + r.PercentColumn(50, col => ChartFromBlock(ctx, col, b, colWidth)); + }); + } + + private static (double x, double y) Polar(double cx, double cy, double r, double deg) + => (cx + r * Math.Cos(deg * Math.PI / 180), cy + r * Math.Sin(deg * Math.PI / 180)); + + // Append cubic-bezier segments approximating a circular arc from a0 to a1 (radians) at radius r. + // Assumes the current path point is already at a0. Handles either sweep direction via the sign of + // (a1 - a0), splitting into <=90 degree segments (the standard 4/3*tan(dθ/4) control-point method). + private static void ArcBeziers(List cmds, double cx, double cy, double r, double a0, double a1) + { + var segs = Math.Max(1, (int)Math.Ceiling(Math.Abs(a1 - a0) / (Math.PI / 2))); + var d = (a1 - a0) / segs; + var t = a0; + for (var s = 0; s < segs; s++) + { + var t2 = t + d; + var k = 4.0 / 3.0 * Math.Tan(d / 4); + var x0 = cx + r * Math.Cos(t); var y0 = cy + r * Math.Sin(t); + var x3 = cx + r * Math.Cos(t2); var y3 = cy + r * Math.Sin(t2); + var c1x = x0 - k * r * Math.Sin(t); var c1y = y0 + k * r * Math.Cos(t); + var c2x = x3 + k * r * Math.Sin(t2); var c2y = y3 - k * r * Math.Cos(t2); + cmds.Add(OfficePathCommand.CubicBezierTo(c1x, c1y, c2x, c2y, x3, y3)); + t = t2; + } + } + + private static void DrawBar(ReportContext ctx, OfficeDrawing dw, + List<(string label, double value, string colour)> entries, double ox, double oy, double viewW = ChartViewW) + { + const double plotLeft = 40, plotTop = 20, plotHeight = 130; + var plotWidth = viewW - plotLeft - 20; // 340 at the full 400 design width + const double plotBottom = plotTop + plotHeight; + var maxValue = Math.Max(entries.Max(e => e.value), 0); if (maxValue <= 0) maxValue = 1; + var slot = plotWidth / entries.Count; + var barWidth = Math.Min(slot * 0.6, 46); + + // Lines are placed at their start point with endpoints relative to it (a shape's coordinates + // are normalised to its own box, so absolute endpoints would collapse to the origin). + var axis = OfficeShape.Line(0, 0, plotWidth, 0); + axis.StrokeColor = OC(ReportColours.Line); axis.StrokeWidth = 1; dw.AddShape(axis, ox + plotLeft, oy + plotBottom); + + for (var i = 0; i < entries.Count; i++) + { + var e = entries[i]; + var height = Math.Max(e.value / maxValue * plotHeight, e.value > 0 ? 2 : 1); + var x = plotLeft + i * slot + (slot - barWidth) / 2; + var y = plotBottom - height; + var barRadius = Math.Min(2, Math.Min(barWidth, height) / 2); + var bar = OfficeShape.RoundedRectangle(barWidth, height, barRadius); + bar.FillColor = OC(e.colour); dw.AddShape(bar, ox + x, oy + y); + AddT(dw, FmtNum(e.value), ox + plotLeft + i * slot, oy + y - 11, slot, 9, ChartLabelSize, ctx.Theme.Palette["body"], OfficeTextAlignment.Center); + var label = e.label.Length > 14 ? e.label.Substring(0, 13) + "…" : e.label; + AddT(dw, San(label), ox + plotLeft + i * slot, oy + plotBottom + 5, slot, 9, ChartLabelSize, ReportColours.Muted, OfficeTextAlignment.Center); + } + } + + // Donut geometry in chart coords: the ring's centre and radius, and where the legend starts. + private const double DonutCy = 68, DonutOuterR = 60, DonutInnerR = 25, DonutLegendY = 158, LegendRowH = 14; + private const double LegendSwatch = 8, LegendSwatchGap = 4, LegendEntryGap = 18, LegendCharW = 3.6; + + // Legend entries packed into rows that fit the given width: they flow left to right and wrap when + // the next entry would overrun, so a narrow (half-width) donut stacks its legend rather than + // running its labels off the edge (which OfficeIMO rejects). Shared by the height reservation and + // the drawing so both agree on the row count. + private static List> PackLegend( + List<(string label, double value, string colour)> entries, double viewW) + { + var budget = Math.Max(60, viewW - 20); + var rows = new List>(); + var current = new List<(string label, double value, string colour)>(); + double used = 0; + foreach (var e in entries) + { + var text = San($"{e.label} ({FmtNum(e.value)})"); + var entryW = LegendSwatch + LegendSwatchGap + text.Length * LegendCharW; + var add = (current.Count == 0 ? 0 : LegendEntryGap) + entryW; + if (current.Count > 0 && used + add > budget) { rows.Add(current); current = new List<(string, double, string)>(); used = 0; add = entryW; } + current.Add(e); used += add; + } + if (current.Count > 0) rows.Add(current); + return rows; + } + + /// The height a donut needs: the ring, then however many legend rows fit the width. + private static double DonutViewHeight(List<(string label, double value, string colour)> entries, double viewW = ChartViewW) + { + var rows = Math.Max(1, PackLegend(entries.Where(e => e.value > 0).ToList(), viewW).Count); + return DonutLegendY + rows * LegendRowH - 2; + } + + private static void DrawDonut(ReportContext ctx, OfficeDrawing dw, + List<(string label, double value, string colour)> entries, double ox, double oy, string? centreLabel = null, + double viewW = ChartViewW) + { + var visible = entries.Where(e => e.value > 0).ToList(); + var total = visible.Sum(e => e.value); + var viewH = DonutViewHeight(entries, viewW); + if (total <= 0) + { + AddT(dw, "No data available for this chart.", ox, oy + viewH / 2 - 6, viewW, 12, ReportStyles.Body, ReportColours.Faint, OfficeTextAlignment.Center); + return; + } + // Local chart coords (0..viewW, 0..viewH); all slices share one path box placed at the chart + // offset, so they align (a bare Path() normalises each to its own box and misplaces them). + double cx = viewW / 2, cy = DonutCy, outerR = DonutOuterR, innerR = DonutInnerR; + double preceding = 0; + foreach (var e in visible) + { + var startAngle = -90 + preceding / total * 360; + var angle = Math.Min(e.value / total * 360, 359.99); + var endAngle = startAngle + angle; + var os = Polar(cx, cy, outerR, startAngle); + var ie = Polar(cx, cy, innerR, endAngle); + var cmds = new List { OfficePathCommand.MoveTo(os.x, os.y) }; + ArcBeziers(cmds, cx, cy, outerR, startAngle * Math.PI / 180, endAngle * Math.PI / 180); + cmds.Add(OfficePathCommand.LineTo(ie.x, ie.y)); + ArcBeziers(cmds, cx, cy, innerR, endAngle * Math.PI / 180, startAngle * Math.PI / 180); + cmds.Add(OfficePathCommand.Close()); + var slice = OfficeShape.Path(viewW, viewH, cmds); + slice.FillColor = OC(e.colour); slice.StrokeColor = OC(ReportColours.White); slice.StrokeWidth = 1; + dw.AddShape(slice, ox, oy); + preceding += e.value; + } + // Total in the middle, with an optional caption under it (client centreLabel). + var centred = string.IsNullOrEmpty(centreLabel); + AddT(dw, FmtNum(total), ox + viewW / 2 - 40, oy + cy - (centred ? 8 : 13), 80, 16, 14, ReportColours.Ink, OfficeTextAlignment.Center); + if (!centred) + AddT(dw, San(centreLabel!), ox + viewW / 2 - 40, oy + cy + 8, 80, 10, 7, ReportColours.Muted, OfficeTextAlignment.Center); + DrawLegend(ctx, dw, visible, ox, oy, DonutLegendY, viewW); + } + + private static void DrawLegend(ReportContext ctx, OfficeDrawing dw, + List<(string label, double value, string colour)> entries, double ox, double oy, double baseY, double viewW = ChartViewW) + { + // Rows are packed to the width (see PackLegend), each centred; a text box is clamped to what + // is left inside the drawing so a long label never runs past the edge. + var packed = PackLegend(entries, viewW); + for (var row = 0; row < packed.Count; row++) + { + var rowEntries = packed[row]; + var texts = rowEntries.Select(e => San($"{e.label} ({FmtNum(e.value)})")).ToList(); + var widths = texts.Select(t => LegendSwatch + LegendSwatchGap + t.Length * LegendCharW).ToList(); + var x = Math.Max(6, (viewW - (widths.Sum() + LegendEntryGap * (rowEntries.Count - 1))) / 2); + var rowY = baseY + row * LegendRowH; + for (var i = 0; i < rowEntries.Count; i++) + { + var sw = OfficeShape.Rectangle(LegendSwatch, LegendSwatch); sw.FillColor = OC(rowEntries[i].colour); dw.AddShape(sw, ox + x, oy + rowY - 6); + var textX = x + LegendSwatch + LegendSwatchGap; + var textW = Math.Max(4, Math.Min(texts[i].Length * LegendCharW + 8, viewW - textX - 2)); + AddT(dw, texts[i], ox + textX, oy + rowY - 6, textW, 10, ChartLabelSize, ctx.Theme.Palette["body"], OfficeTextAlignment.Left); + x += widths[i] + LegendEntryGap; + } + } + } + + private static void DrawTrend(ReportContext ctx, OfficeDrawing dw, + List<(string label, double value, string colour)> entries, double ox, double oy, double? max, double viewW = ChartViewW) + { + const double plotLeft = 40, plotTop = 20, plotHeight = 140; + var plotWidth = viewW - plotLeft - 40; // 320 at the full 400 design width + const double plotBottom = plotTop + plotHeight; + var colour = ctx.Theme.Primary; + var dataMax = Math.Max(entries.Max(e => e.value), 0); + var scaleMax = max is > 0 ? max!.Value : dataMax > 0 ? dataMax : 1; + var spacing = plotWidth / Math.Max(entries.Count - 1, 1); + var pts = entries.Select((e, i) => ( + x: plotLeft + i * spacing, + y: plotBottom - Math.Min(e.value / scaleMax, 1) * plotHeight)).ToList(); + + var rect = OfficeShape.Rectangle(plotWidth, plotHeight); + rect.FillColor = OC(ReportColours.Panel); rect.StrokeColor = OC(ReportColours.Line); rect.StrokeWidth = 1; + dw.AddShape(rect, ox + plotLeft, oy + plotTop); + for (var g = 0; g <= 4; g++) + { + var gy = plotTop + g * (plotHeight / 4); + var gl = OfficeShape.Line(0, 0, plotWidth, 0); + gl.StrokeColor = OC(ReportColours.Line); gl.StrokeWidth = 0.5; dw.AddShape(gl, ox + plotLeft, oy + gy); + } + if (pts.Count > 1) + { + // Local coords in a 400x200 box placed at the chart offset (see donut note). + var area = new List { OfficePathCommand.MoveTo(pts[0].x, pts[0].y) }; + for (var i = 1; i < pts.Count; i++) area.Add(OfficePathCommand.LineTo(pts[i].x, pts[i].y)); + area.Add(OfficePathCommand.LineTo(pts[^1].x, plotBottom)); + area.Add(OfficePathCommand.LineTo(pts[0].x, plotBottom)); + area.Add(OfficePathCommand.Close()); + var areaShape = OfficeShape.Path(viewW, ChartViewH, area); areaShape.FillColor = OC(colour); areaShape.FillOpacity = 0.3; dw.AddShape(areaShape, ox, oy); + + var line = new List { OfficePathCommand.MoveTo(pts[0].x, pts[0].y) }; + for (var i = 1; i < pts.Count; i++) line.Add(OfficePathCommand.LineTo(pts[i].x, pts[i].y)); + var lineShape = OfficeShape.Path(viewW, ChartViewH, line); lineShape.StrokeColor = OC(colour); lineShape.StrokeWidth = 2; dw.AddShape(lineShape, ox, oy); + } + foreach (var p in pts) + { + var dot = OfficeShape.Ellipse(6, 6); dot.FillColor = OC(colour); dw.AddShape(dot, ox + p.x - 3, oy + p.y - 3); + } + var stride = (int)Math.Ceiling(entries.Count / 7.0); + for (var i = 0; i < entries.Count; i++) + if (i % stride == 0) + AddT(dw, San(entries[i].label), ox + pts[i].x - 20, oy + plotBottom + 8, 40, 9, ChartLabelSize, ReportColours.Muted, OfficeTextAlignment.Center); + foreach (var frac in new[] { 0, 0.25, 0.5, 0.75, 1.0 }) + AddT(dw, FmtNum(Math.Round(scaleMax * frac)), ox, oy + plotBottom - frac * plotHeight - 4, plotLeft - 5, 9, ChartLabelSize, ReportColours.Muted, OfficeTextAlignment.Right); + } + + // -- sankey (the dashboard CippSankey / nivo flow diagram) -- + // A sankey is a layered DAG: nodes fall into columns by the longest path from a source (a node is + // one column right of everything that flows into it), a node's height is proportional to the flow + // through it, and links are ribbons whose thickness carries the value. Same OfficeDrawing canvas as + // the other charts (top-left origin, y down) so the geometry mirrors d3-sankey directly. + private sealed class SankeyNode + { + public string Id = string.Empty; + public string Label = string.Empty; + public string Colour = string.Empty; + public double Value; // max(in, out) + public int Depth; + public double X, Y, H; // laid-out position/size in plot coords + public readonly List Out = new(); + public readonly List In = new(); + } + + private sealed class SankeyLink + { + public SankeyNode Src = null!; + public SankeyNode Tgt = null!; + public double Value, Width, Sy, Ty; // Sy/Ty = top edge of the ribbon at the source/target node + } + + private const double SankeyNodeThickness = 12, SankeyNodeSpacing = 12, SankeyLabelGutter = 88; + + public static void Sankey(ReportContext ctx, PdfContentBuilder item, List nodes, List links, + string? title = null, string? caption = null, double? height = null) + { + var w = ctx.ContentWidth - 2; // a drawing exactly the content width is rejected as too wide + const double pad = 16, titleH = 14, titleGap = 12, captionGap = 8, captionH = 10; + var hasTitle = !string.IsNullOrEmpty(title); + var hasCaption = !string.IsNullOrEmpty(caption); + var plotTop = pad + (hasTitle ? titleH + titleGap : 0); + var plotH = height is > 0 ? height!.Value : 240; + var totalH = plotTop + plotH + pad + (hasCaption ? captionGap + captionH : 0); + + var dw = new OfficeDrawing(w, totalH); + var frame = OfficeShape.RoundedRectangle(w, totalH, 6); + frame.FillColor = OC(ReportColours.White); frame.StrokeColor = OC(ReportColours.Line); frame.StrokeWidth = 1; + dw.AddShape(frame, 0, 0); + if (hasTitle) + AddT(dw, San(title!), 0, pad, w, titleH, ChartTitleSize, ctx.Theme.Palette["body"], OfficeTextAlignment.Center, true); + + var model = BuildSankey(ctx, nodes, links, w - 2 * SankeyLabelGutter, plotH); + if (model.Count == 0) + AddT(dw, "No data available for this chart.", 0, plotTop + plotH / 2 - 6, w, 12, ReportStyles.Body, ReportColours.Faint, OfficeTextAlignment.Center); + else + DrawSankey(dw, model, SankeyLabelGutter, plotTop, w - 2 * SankeyLabelGutter, w, totalH); + + if (hasCaption) + AddT(dw, San(caption!), 0, plotTop + plotH + captionGap, w, captionH, ChartLabelSize, ctx.Theme.Palette["chart"], OfficeTextAlignment.Center); + + item.Drawing(dw, PdfAlign.Left); + item.Spacer(12); + } + + // Read the nodes/links payload, assign columns, scale node heights to the plot, and relax the vertical + // positions so linked nodes line up (fewer ribbon crossings). Returns the laid-out nodes, or empty when + // there is nothing to draw. + private static List BuildSankey(ReportContext ctx, List nodes, List links, + double plotW, double plotH) + { + var byId = new Dictionary(StringComparer.Ordinal); + SankeyNode NodeFor(string id) => byId.TryGetValue(id, out var n) ? n + : byId[id] = new SankeyNode { Id = id, Label = id, Colour = ctx.Theme.Palette["chart"] }; + + var order = 0; + var sequence = new Dictionary(StringComparer.Ordinal); + foreach (var raw in nodes) + { + var id = ReportNode.RowStr(raw, "id"); + if (string.IsNullOrEmpty(id)) continue; + var n = NodeFor(id!); + n.Label = ReportNode.RowStr(raw, "label") ?? id!; + var colour = ReportNode.RowStr(raw, "nodeColor") ?? ReportNode.RowStr(raw, "colour"); + if (!string.IsNullOrEmpty(colour)) n.Colour = colour!; + if (!sequence.ContainsKey(id!)) sequence[id!] = order++; + } + foreach (var raw in links) + { + var s = ReportNode.RowStr(raw, "source"); var t = ReportNode.RowStr(raw, "target"); + var v = ReportNode.RowNum(raw, "value"); + if (string.IsNullOrEmpty(s) || string.IsNullOrEmpty(t) || v <= 0) continue; + var src = NodeFor(s!); var tgt = NodeFor(t!); + if (!sequence.ContainsKey(s!)) sequence[s!] = order++; + if (!sequence.ContainsKey(t!)) sequence[t!] = order++; + var link = new SankeyLink { Src = src, Tgt = tgt, Value = v }; + src.Out.Add(link); tgt.In.Add(link); + } + + var all = byId.Values.Where(n => n.Out.Count > 0 || n.In.Count > 0).ToList(); + if (all.Count == 0) return all; + foreach (var n in all) n.Value = Math.Max(n.Out.Sum(l => l.Value), n.In.Sum(l => l.Value)); + + // Column (depth): longest path from a source, then leaf nodes (no outgoing link) are pushed to + // the LAST column - this is nivo align="justify", which the dashboard uses. Terminal buckets + // such as the auth-methods "Single factor" therefore land in the output column exactly as on + // the dashboard; input-order stacking (below, nivo sort="input") keeps them at the top so their + // ribbons run cleanly across rather than cutting through the middle stage. + for (var i = 0; i < all.Count; i++) + foreach (var n in all) + foreach (var l in n.Out) + if (l.Tgt.Depth < n.Depth + 1) l.Tgt.Depth = n.Depth + 1; + var columnCount = all.Max(n => n.Depth) + 1; + foreach (var n in all) if (n.Out.Count == 0) n.Depth = columnCount - 1; + + var columns = Enumerable.Range(0, columnCount) + .Select(d => all.Where(n => n.Depth == d).OrderBy(n => sequence[n.Id]).ToList()) + .ToList(); + + // One vertical scale across every column - the tightest column (most flow / most nodes) sets it, so + // nothing overflows the plot. ky converts a value to points of height. + var ky = double.PositiveInfinity; + foreach (var col in columns) + { + var sum = col.Sum(n => n.Value); + if (sum <= 0) continue; + var available = plotH - (col.Count - 1) * SankeyNodeSpacing; + ky = Math.Min(ky, Math.Max(available, 1) / sum); + } + if (double.IsInfinity(ky) || ky <= 0) ky = 1; + + foreach (var n in all) + { + n.H = Math.Max(n.Value * ky, 2); + n.X = columnCount == 1 ? 0 : (double)n.Depth / (columnCount - 1) * Math.Max(plotW - SankeyNodeThickness, 1); + } + // Initial y: stack each column and centre the stack in the plot. + foreach (var col in columns) + { + var stack = col.Sum(n => n.H) + (col.Count - 1) * SankeyNodeSpacing; + var y = Math.Max(0, (plotH - stack) / 2); + foreach (var n in col) { n.Y = y; y += n.H + SankeyNodeSpacing; } + } + // A few relaxation passes align a node with the weighted centre of what flows into/out of it, the + // way d3-sankey does; collisions are then resolved IN THE FIXED INPUT ORDER (never re-sorted by + // Y), so the vertical order stays the data order - matching nivo sort="input" on the dashboard. + for (var iter = 0; iter < 6; iter++) + { + var alpha = 0.9 * Math.Pow(0.99, iter); + foreach (var col in columns) + foreach (var n in col) + { + var linked = n.In.Concat(n.Out).ToList(); + var weight = linked.Sum(l => l.Value); + if (weight <= 0) continue; + var target = linked.Sum(l => ((l.Src == n ? l.Tgt : l.Src).Y + (l.Src == n ? l.Tgt : l.Src).H / 2) * l.Value) / weight; + n.Y += (target - (n.Y + n.H / 2)) * alpha; + } + foreach (var col in columns) ResolveSankeyCollisions(col, plotH); + } + + // Stack the ribbon endpoints on each node face, ordered by the counterpart's position so ribbons + // fan out without crossing at the node. + foreach (var n in all) + { + var oy = n.Y; + foreach (var l in n.Out.OrderBy(l => l.Tgt.Y)) { l.Width = l.Value * ky; l.Sy = oy; oy += l.Width; } + var iy = n.Y; + foreach (var l in n.In.OrderBy(l => l.Src.Y)) { l.Ty = iy; iy += l.Width; } + } + return all; + } + + private static void ResolveSankeyCollisions(List col, double plotH) + { + if (col.Count == 0) return; + // Resolve overlaps in the column's GIVEN order (input/sequence order), not by Y - re-sorting by + // Y would scramble the nivo sort="input" ordering the dashboard uses. Push down, then if the + // stack overruns the plot, push back up from the bottom. + var y = 0.0; + foreach (var n in col) { if (n.Y < y) n.Y = y; y = n.Y + n.H + SankeyNodeSpacing; } + var overflow = y - SankeyNodeSpacing - plotH; + if (overflow > 0) + { + y = plotH; + for (var i = col.Count - 1; i >= 0; i--) + { + var n = col[i]; + if (n.Y + n.H > y) n.Y = y - n.H; + y = n.Y - SankeyNodeSpacing; + } + } + foreach (var n in col) n.Y = Math.Max(0, Math.Min(n.Y, plotH - n.H)); + } + + private static void DrawSankey(OfficeDrawing dw, List nodes, double ox, double oy, double plotW, + double boxW, double boxH) + { + // Ribbons first (under the node bars): a horizontal cubic band from the source's right face to the + // target's left face, tinted the source colour so a node's outflow reads as one family. + foreach (var n in nodes) + foreach (var l in n.Out) + { + double x0 = ox + l.Src.X + SankeyNodeThickness, x1 = ox + l.Tgt.X, xm = (x0 + x1) / 2; + double t0 = oy + l.Sy, t1 = oy + l.Ty, b0 = t0 + l.Width, b1 = t1 + l.Width; + var cmds = new List + { + OfficePathCommand.MoveTo(x0, t0), + OfficePathCommand.CubicBezierTo(xm, t0, xm, t1, x1, t1), + OfficePathCommand.LineTo(x1, b1), + OfficePathCommand.CubicBezierTo(xm, b1, xm, b0, x0, b0), + OfficePathCommand.Close(), + }; + var ribbon = OfficeShape.Path(boxW, boxH, cmds); + ribbon.FillColor = OC(l.Src.Colour); ribbon.FillOpacity = 0.45; + dw.AddShape(ribbon, 0, 0); + } + // Node bars + labels: end columns label outward into the gutter, inner columns label to the right. + foreach (var n in nodes) + { + // A thin node (tiny flow) is shorter than twice the corner radius, which RoundedRectangle + // rejects - clamp the radius to what fits. + var radius = Math.Min(3, Math.Min(SankeyNodeThickness, n.H) / 2); + var bar = OfficeShape.RoundedRectangle(SankeyNodeThickness, n.H, radius); + bar.FillColor = OC(n.Colour); dw.AddShape(bar, ox + n.X, oy + n.Y); + + var label = n.Label.Length > 22 ? n.Label.Substring(0, 21) + "…" : n.Label; + var midY = oy + n.Y + n.H / 2 - 4; + if (n.X <= 0.01) + AddT(dw, San(label), ox - SankeyLabelGutter, midY, SankeyLabelGutter - 5, 12, ChartLabelSize, ReportColours.Body, OfficeTextAlignment.Right); + else if (n.X >= plotW - SankeyNodeThickness - 0.01) + AddT(dw, San(label), ox + n.X + SankeyNodeThickness + 5, midY, SankeyLabelGutter - 5, 12, ChartLabelSize, ReportColours.Body, OfficeTextAlignment.Left); + else + AddT(dw, San(label), ox + n.X + SankeyNodeThickness + 4, midY, 100, 12, ChartLabelSize, ReportColours.Muted, OfficeTextAlignment.Left); + } + } + + private static string RunsToPlain(IReadOnlyList runs) + { + var sb = new System.Text.StringBuilder(); + foreach (var r in runs) sb.Append(r.Text); + return sb.ToString(); + } + + // -- dispatch -- + /// Render a list of component/primitive nodes into the current item flow. An optional text + /// style flows into paragraph nodes so a callout renders its body at the callout size, not body copy. + public static void RenderNodes(ReportContext ctx, PdfContentBuilder item, IEnumerable nodes, TextStyle? textStyle = null) + { + foreach (var node in nodes) RenderNode(ctx, item, node, textStyle); + } + + private static void RenderNode(ReportContext ctx, PdfContentBuilder item, ReportNode node, TextStyle? textStyle = null) + { + switch (node.Type) + { + case "heading": + Heading(ctx, item, (int)(node.Num("level") ?? 2), node.Get>("runs") ?? new List()); + break; + case "paragraph": + Paragraph(ctx, item, node.Get>("runs") ?? new List(), textStyle); + break; + case "bullets": + // Inside a callout (textStyle set) OfficeIMO panels ignore a per-call list style, so + // the list is drawn as one styled paragraph with a marker per line - matching the + // client, which renders callout bullets as a single text block. Elsewhere the real + // list renderer is used. + if (textStyle is { } bts) BulletLines(ctx, item, StringItems(node), bts, _ => "• "); + else Bullets(ctx, item, StringItems(node)); + break; + case "numbered": + if (textStyle is { } nts) + { + var start = (int)(node.Num("start") ?? 1); + BulletLines(ctx, item, StringItems(node), nts, i => $"{start + i}. "); + } + else Numbered(ctx, item, StringItems(node), (int)(node.Num("start") ?? 1)); + break; + case "table": + RenderTableNode(ctx, item, node); + break; + case "code": + Code(ctx, item, node.Str("text") ?? node.Str("content") ?? string.Empty); + break; + case "hr": + Hr(ctx, item); + break; + default: + // Unknown primitive: fall back to its text content as a paragraph. + var text = node.Str("content") ?? node.Str("text"); + if (!string.IsNullOrEmpty(text)) Paragraph(ctx, item, ReportMarkdown.MarkdownRuns(text)); + break; + } + } + + private static void RenderTableNode(ReportContext ctx, PdfContentBuilder item, ReportNode node) + { + var rows = node.Get>("rows"); + if (rows is { Count: > 0 }) item.Table(UpperHeader(rows), PdfAlign.Left, BrandedTableStyle(ctx)); + } + + private static List StringItems(ReportNode node) + { + var result = new List(); + if (node.Get>("items") is { } typed) return typed; + // Items may be plain strings (markdown) or objects with a text/label/content field (the report + // builder's row editor saves objects), so read the field when an item is a dictionary. + if (node.ListOf("items") is { } raw) + foreach (var o in raw) + result.Add(o is Dictionary + ? (ReportNode.RowStr(o, "text") ?? ReportNode.RowStr(o, "label") ?? ReportNode.RowStr(o, "content") ?? string.Empty) + : o?.ToString() ?? string.Empty); + return result; + } + + /// + /// Block -> component adapter for the Report Builder. Each top-level block renders its title as a + /// section heading, then its body composed from the component kit. Hero/pagebreak are handled by + /// the document scaffold, not here. + /// + private static double? ParseNumber(string? text) + => double.TryParse(text, NumberStyles.Any, CultureInfo.InvariantCulture, out var value) ? value : null; + + public static void RenderBlock(ReportContext ctx, PdfContentBuilder item, ReportNode block, bool firstOnPage = false) + { + var content = block.Str("content") ?? string.Empty; + + // Callouts carry their own title inside the box, so they are handled before the generic + // section-title emit below - the title must not also appear as a heading above the panel. + switch (block.Type) + { + case "infobox": + InfoBox(ctx, item, block.Str("title"), block.Str("tone"), block.Str("colour"), block.Bool("tintTitle"), content, block.Bool("lines")); + return; + case "infoboxcolumns": + InfoBoxColumns(ctx, item, block.ListOf("items") ?? new List(), (int)(block.Num("columns") ?? 2)); + return; + case "alertbox": + AlertBox(ctx, item, block.Str("title"), block.Str("colour"), content, block.Bool("lines")); + return; + case "clearbox": + ClearBox(ctx, item, block.Str("title"), content, block.Bool("lines")); + return; + case "paragraphindent": + IndentedParagraph(ctx, item, content); + return; + case "note": + Note(ctx, item, content); + return; + case "chart": + // The fixed reports name these caption/max/centreLabel; the report builder saves them + // with a chart prefix. Both reach the page. A half-width chart with no adjacent partner + // renders at half width on its own line (the pair renderer handles two together). + ChartFromBlock(ctx, item, block, IsHalfWidthChart(block) ? (ctx.ContentWidth - 16) / 2 : (double?)null); + return; + case "sankey": + Sankey(ctx, item, block.ListOf("nodes") ?? new List(), block.ListOf("links") ?? new List(), + block.Str("title"), block.Str("caption") ?? block.Str("chartCaption"), + block.Num("height") ?? ParseNumber(block.Str("height"))); + return; + } + + // A titled block opens a new section (client
). Every section but the first on a page + // carries the client's section marginBottom 12 above its heading, on top of the previous + // block's own trailing space - so sections sit apart the way they do in the react-pdf reports. + var title = block.Str("title"); + if (!string.IsNullOrEmpty(title)) + { + if (!firstOnPage) item.Spacer(SectionGap); + SectionTitle(ctx, item, title); + } + + // Test-block status line (Passed/Failed/Investigate/Skipped). + if (block.Type == "test" && !string.IsNullOrEmpty(block.Str("status"))) + Note(ctx, item, "Status: " + block.Str("status")); + + switch (block.Type) + { + case "scorecard": + StatRow(ctx, item, block.ListOf("stats") ?? new List()); + break; + case "richtable": + RichTable(ctx, item, block.ListOf("columns") ?? new List(), block.ListOf("rows") ?? new List(), (int)(block.Num("limit") ?? ParseNumber(block.Str("limit")) ?? 0)); + break; + case "richbullets": + RichBullets(ctx, item, block.ListOf("items") ?? new List()); + break; + case "progress": + Progress(ctx, item, block.ListOf("items") ?? new List()); + break; + case "database": + var format = block.Str("format"); + if (!string.IsNullOrEmpty(format) && format != "text") Code(ctx, item, content); + else RenderNodes(ctx, item, ReportMarkdown.MarkdownToNodes(content)); + break; + case "blank": + RenderNodes(ctx, item, ReportMarkdown.HtmlToNodes(content)); + break; + case "test": + RenderNodes(ctx, item, block.Bool("static") + ? ReportMarkdown.HtmlToNodes(content) + : ReportMarkdown.MarkdownToNodes(content)); + break; + default: + // A raw primitive node passed straight through (e.g. from a fixed report composing the kit). + RenderNode(ctx, item, block); + break; + } + } + } +} diff --git a/backend/Shared/CIPPSharp/Reporting/ReportContext.cs b/backend/Shared/CIPPSharp/Reporting/ReportContext.cs new file mode 100644 index 0000000000..08547ce810 --- /dev/null +++ b/backend/Shared/CIPPSharp/Reporting/ReportContext.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; + +namespace CIPP.Reporting +{ + /// + /// Branding as saved by Get-CIPPBrandingSettings (or a preset), parsed from JSON. Mirrors the shape + /// the client theme consumes: brand colours, per-role colour overrides, footer/watermark config, and + /// the logo/cover as data-URLs. Missing toggles stay null so the theme applies its "!== false" defaults. + /// + public sealed class BrandingInput + { + public string? Colour { get; init; } + public string? SecondaryColour { get; init; } + public string? FooterText { get; init; } + public string? CoverFooterText { get; init; } + public bool? ShowFooter { get; init; } + public bool? ShowPageNumbers { get; init; } + public string? WatermarkText { get; init; } + public bool? WatermarkEnabled { get; init; } + public string? Logo { get; init; } // data-URL + public string? CoverImage { get; init; } // data-URL (an uploaded cover) + public string? CoverStock { get; init; } // a bundled /reportImages/ path, or "none" + private Dictionary RoleColours { get; init; } = new(); + private Dictionary FlatColours { get; init; } = new(); + + /// roleColours[setting] first, then a flat property of the same name (preset/template case). + public string? RoleColour(string setting) + { + if (RoleColours.TryGetValue(setting, out var v)) return v; + return FlatColours.TryGetValue(setting, out var f) ? f : null; + } + + public static BrandingInput FromJson(string? json) + { + if (string.IsNullOrWhiteSpace(json)) return new BrandingInput(); + JsonElement root; + try { root = JsonDocument.Parse(json).RootElement; } + catch { return new BrandingInput(); } + if (root.ValueKind != JsonValueKind.Object) return new BrandingInput(); + + string? Str(string name) => root.TryGetProperty(name, out var e) && e.ValueKind == JsonValueKind.String ? e.GetString() : null; + bool? Bool(string name) => root.TryGetProperty(name, out var e) && (e.ValueKind == JsonValueKind.True || e.ValueKind == JsonValueKind.False) ? e.GetBoolean() : (bool?)null; + + var roles = new Dictionary(); + if (root.TryGetProperty("roleColours", out var rc) && rc.ValueKind == JsonValueKind.Object) + { + foreach (var p in rc.EnumerateObject()) + if (p.Value.ValueKind == JsonValueKind.String) roles[p.Name] = p.Value.GetString()!; + } + // Flat colour overrides (a preset/template can set e.g. "headingColour" directly). + var flat = new Dictionary(); + foreach (var p in root.EnumerateObject()) + if (p.Name.EndsWith("Colour", StringComparison.Ordinal) && p.Value.ValueKind == JsonValueKind.String) + flat[p.Name] = p.Value.GetString()!; + + return new BrandingInput + { + Colour = Str("colour"), + SecondaryColour = Str("secondaryColour"), + FooterText = Str("footerText"), + CoverFooterText = Str("coverFooterText"), + ShowFooter = Bool("showFooter"), + ShowPageNumbers = Bool("showPageNumbers"), + WatermarkText = Str("watermarkText"), + WatermarkEnabled = Bool("watermarkEnabled"), + Logo = Str("logo"), + CoverImage = Str("coverImage"), + CoverStock = Str("coverStock"), + RoleColours = roles, + FlatColours = flat, + }; + } + } + + /// + /// Everything a component needs while rendering - the ReportProvider equivalent. Passed to every + /// component so none of them reach for global state or inline a colour. + /// + public sealed class ReportContext + { + public required ReportTheme Theme { get; init; } + public required IReadOnlyDictionary Variables { get; init; } + public string PageSize { get; init; } = "A4"; + public bool Landscape { get; init; } + public string TenantName { get; init; } = "Organization"; + public string ReportName { get; init; } = "Report"; + public string GeneratedOn { get; init; } = ""; + public byte[]? Logo { get; init; } + public byte[]? CoverImage { get; init; } + + /// Usable content width in points (paper - page padding both sides). Mirrors contentWidth(). + public double ContentWidth => ReportStyles.ContentWidth(PageSize, Landscape); + + /// Usable content height in points (paper - page padding both sides). + public double ContentHeight => ReportStyles.ContentHeight(PageSize, Landscape); + } +} diff --git a/backend/Shared/CIPPSharp/Reporting/ReportMarkdown.cs b/backend/Shared/CIPPSharp/Reporting/ReportMarkdown.cs new file mode 100644 index 0000000000..bae4c95a2b --- /dev/null +++ b/backend/Shared/CIPPSharp/Reporting/ReportMarkdown.cs @@ -0,0 +1,627 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +namespace CIPP.Reporting +{ + /// + /// Faithful C# port of the frontend fidelity contract: + /// markdown-inline.js, html-inline.js, markdown-table.js and the markdown/HTML block converters + /// in ReportBuilderPDF.jsx. + /// Inline parsing yields a shared nested tree that flattens into s; block + /// parsing yields s the component kit renders. Both markdown and HTML feed + /// the same run emitter - ported once, exactly as the JS does. + /// + public static class ReportMarkdown + { + private sealed class InlineNode + { + public string Type = "text"; // text|strong|em|strongEm|code|strike|underline + public string Value = string.Empty; // for text + public List Children = new(); + } + + // -- Inline markdown -- + private static readonly (string marker, string type)[] EmphasisMarkers = + { + ("***", "strongEm"), ("___", "strongEm"), ("**", "strong"), ("__", "strong"), + ("~~", "strike"), ("*", "em"), ("_", "em"), + }; + private const string Escapable = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; + + private static bool IsBoundary(char? c) + => c is null || char.IsWhiteSpace(c.Value) || char.IsPunctuation(c.Value) || char.IsSymbol(c.Value); + + private static List ParseInlineMarkdown(string? text) + { + var source = text ?? string.Empty; + var nodes = new List(); + var buffer = new StringBuilder(); + var i = 0; + + void Flush() + { + if (buffer.Length > 0) { nodes.Add(new InlineNode { Type = "text", Value = buffer.ToString() }); buffer.Clear(); } + } + + while (i < source.Length) + { + var ch = source[i]; + + if (ch == '\\' && i + 1 < source.Length && Escapable.IndexOf(source[i + 1]) >= 0) + { + buffer.Append(source[i + 1]); i += 2; continue; + } + if (ch == '`') + { + var end = source.IndexOf('`', i + 1); + if (end > i + 1) + { + Flush(); + nodes.Add(new InlineNode { Type = "code", Children = { new InlineNode { Type = "text", Value = source.Substring(i + 1, end - i - 1) } } }); + i = end + 1; continue; + } + } + if (ch == '[') + { + var link = Regex.Match(source.Substring(i), "^\\[([^\\]]*)\\]\\([^)\\s]*(?:\\s+\"[^\"]*\")?\\)"); + if (link.Success) + { + Flush(); + nodes.AddRange(ParseInlineMarkdown(link.Groups[1].Value)); + i += link.Value.Length; continue; + } + } + if (ch == '*' || ch == '_' || ch == '~') + { + var span = MatchEmphasis(source, i); + if (span is not null) + { + Flush(); + nodes.Add(new InlineNode { Type = span.Value.type, Children = ParseInlineMarkdown(span.Value.content) }); + i = span.Value.end; continue; + } + } + buffer.Append(ch); i += 1; + } + Flush(); + return nodes; + } + + private static (string type, string content, int end)? MatchEmphasis(string source, int start) + { + foreach (var (marker, type) in EmphasisMarkers) + { + if (string.CompareOrdinal(source, start, marker, 0, marker.Length) != 0) continue; + if (marker[0] == '_' && !IsBoundary(start - 1 >= 0 ? source[start - 1] : (char?)null)) continue; + + var contentStart = start + marker.Length; + if (contentStart >= source.Length || char.IsWhiteSpace(source[contentStart])) continue; + + var search = contentStart; + while (search < source.Length) + { + var close = source.IndexOf(marker, search, StringComparison.Ordinal); + if (close == -1) break; + var before = source[close - 1]; + var after = close + marker.Length < source.Length ? source[close + marker.Length] : (char?)null; + var closes = !char.IsWhiteSpace(before) + && (marker[0] != '_' || IsBoundary(after)) + && close > contentStart; + if (closes) + return (type, source.Substring(contentStart, close - contentStart), close + marker.Length); + search = close + 1; + } + } + return null; + } + + // -- Inline HTML -- + private static readonly Dictionary MarkTags = new(StringComparer.OrdinalIgnoreCase) + { + ["strong"] = "strong", ["b"] = "strong", ["em"] = "em", ["i"] = "em", + ["s"] = "strike", ["del"] = "strike", ["strike"] = "strike", ["u"] = "underline", ["code"] = "code", + }; + private static readonly HashSet VoidContentTags = new(StringComparer.OrdinalIgnoreCase) { "script", "style" }; + private static readonly Dictionary NamedEntities = new(StringComparer.OrdinalIgnoreCase) + { + ["amp"] = "&", ["lt"] = "<", ["gt"] = ">", ["quot"] = "\"", ["apos"] = "'", ["nbsp"] = " ", + ["ndash"] = "–", ["mdash"] = "—", ["hellip"] = "…", + ["lsquo"] = "‘", ["rsquo"] = "’", ["ldquo"] = "“", ["rdquo"] = "”", + }; + + public static string DecodeHtmlEntities(string? text) => Regex.Replace(text ?? string.Empty, + "&(#x?[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]*);", m => + { + var entity = m.Groups[1].Value; + if (entity[0] == '#') + { + var isHex = entity.Length > 1 && (entity[1] == 'x' || entity[1] == 'X'); + var ok = int.TryParse(isHex ? entity.Substring(2) : entity.Substring(1), + isHex ? NumberStyles.HexNumber : NumberStyles.Integer, CultureInfo.InvariantCulture, out var cp); + return ok && cp > 0 ? char.ConvertFromUtf32(cp) : m.Value; + } + return NamedEntities.TryGetValue(entity, out var named) ? named : m.Value; + }); + + private static readonly Regex TagPattern = new("<(/?)([a-zA-Z][a-zA-Z0-9]*)\\b[^>]*>"); + + private static List ParseInlineHtml(string? html) + { + var source = html ?? string.Empty; + var root = new InlineNode(); + var stack = new List { root }; + var buffer = new StringBuilder(); + string? skipUntil = null; + + void Flush() + { + if (buffer.Length == 0) return; + stack[^1].Children.Add(new InlineNode { Type = "text", Value = DecodeHtmlEntities(buffer.ToString()) }); + buffer.Clear(); + } + + var cursor = 0; + foreach (Match match in TagPattern.Matches(source)) + { + var isClosing = match.Groups[1].Value == "/"; + var name = match.Groups[2].Value.ToLowerInvariant(); + var tag = match.Value; + + if (skipUntil is not null) + { + cursor = match.Index + match.Length; + if (isClosing && name == skipUntil) skipUntil = null; + continue; + } + + buffer.Append(source.Substring(cursor, match.Index - cursor)); + cursor = match.Index + match.Length; + + if (VoidContentTags.Contains(name)) + { + if (!isClosing && !tag.EndsWith("/>", StringComparison.Ordinal)) skipUntil = name; + continue; + } + if (name == "br") + { + Flush(); + stack[^1].Children.Add(new InlineNode { Type = "text", Value = "\n" }); + continue; + } + if (!MarkTags.TryGetValue(name, out var type)) continue; + + if (isClosing) + { + for (var i = stack.Count - 1; i > 0; i--) + { + if (stack[i].Type == type) { Flush(); stack.RemoveRange(i, stack.Count - i); break; } + } + continue; + } + if (tag.EndsWith("/>", StringComparison.Ordinal)) continue; + + Flush(); + var node = new InlineNode { Type = type }; + stack[^1].Children.Add(node); + stack.Add(node); + } + if (skipUntil is null) buffer.Append(source.Substring(cursor)); + Flush(); + return root.Children; + } + + // The light check/cross marks (✓ ✗) have no colour Twemoji asset, so they are mapped to the same + // [Pass]/[Fail] tokens the fixed reports use, which then promote to a colour glyph. Every other + // status emoji (✅ ❌ 🔴 🟠 🟡 🟢 ...) has a Twemoji image and is left for Sanitize/the renderer to + // draw in colour. Also strips the VS16 emoji-style variation selector. + private static readonly (string from, string to)[] GlyphMap = + { + ("✓", "[Pass]"), ("✗", "[Fail]"), + ("️", ""), + }; + + // The three report emoji: warning ⚠, the green white-heavy-check ✅ (the client's pass indicator), + // and info ℹ. They render as colour Twemoji images, or fall back to the monochrome font tinted to + // their brand colour. Kept verbatim rather than mapped to a token, and are the tokens' promotion + // targets. (The plain heavy check ✔ (U+2714) is Twemoji's dark check, so ✅ is used for "pass".) + public const char EmojiWarning = '⚠'; + public const char EmojiCheck = '✅'; + public const char EmojiInfo = 'ℹ'; + private static readonly HashSet EmojiKept = new() { EmojiWarning, EmojiCheck, EmojiInfo }; + + // The monochrome fallback font ships next to the assembly as emoji-fallback.ttf (like the OfficeIMO + // DLLs). Loaded once; absence just means emoji degrade to the ASCII tokens rather than throwing. + internal static readonly Lazy EmojiFontBytes = new(() => + { + try + { + var dir = System.IO.Path.GetDirectoryName(typeof(ReportMarkdown).Assembly.Location); + if (string.IsNullOrEmpty(dir)) return null; + var path = System.IO.Path.Combine(dir, "emoji-fallback.ttf"); + return System.IO.File.Exists(path) ? System.IO.File.ReadAllBytes(path) : null; + } + catch { return null; } + }); + + // Exactly the code points the bundled font can draw above U+00FF, minus the CP1252 specials the + // standard fonts render: the single source of truth for what Sanitize keeps (keeping one the font + // can't draw would make OfficeIMO's encoding preflight throw) and which ranges ReportPdf declares + // for the fallback. Read once from the font's own cmap so the two never drift; empty without a font. + private static readonly Lazy> EmojiCoverageSet = new(() => + { + var set = new HashSet(); + var font = EmojiFontBytes.Value; + if (font is not { Length: > 0 }) return set; + try + { + foreach (var cp in FontCmap.ReadCodepoints(font)) + if (cp > 0xFF && !IsWinAnsiSpecial(cp)) set.Add(cp); + } + catch { set.Clear(); } + return set; + }); + public static HashSet EmojiCoverage => EmojiCoverageSet.Value; + + // With the fallback font bundled, the reports' portable ASCII status tokens are promoted to the + // matching glyph (rendered downstream); without it they stay as tokens, so the report degrades. + public static bool RenderEmojiGlyphs => EmojiFontBytes.Value is { Length: > 0 }; + + // True when the bundled Twemoji colour PNG set is present, so an emoji grapheme cluster (including + // multi-code-point ZWJ sequences, flags and skin tones) is kept whole for the renderer to place as + // an inline colour image; the monochrome font remains the fallback for anything not bundled. + public static bool RenderEmojiImages => TwemojiAssets.Enabled; + private static readonly (string token, string glyph)[] EmojiTokens = + { + ("[!]", "⚠"), ("[Pass]", "✅"), ("[Fail]", "❌"), ("[i]", "ℹ"), + }; + + // CP1252 / WinAnsi code points above U+00FF that the PDF standard fonts CAN encode - kept as-is. + private static readonly HashSet WinAnsiSpecials = new(new[] + { + '€','‚','ƒ','„','…','†','‡','ˆ','‰','Š', + '‹','Œ','Ž','‘','’','“','”','•','–','—', + '˜','™','š','›','œ','ž','Ÿ', + }); + + // A CP1252 special (rendered by the standard fonts) rather than an emoji - excluded from the + // fallback coverage so normal punctuation like — or ™ never routes to the emoji font. + public static bool IsWinAnsiSpecial(int cp) => cp >= 0 && cp <= 0xFFFF && WinAnsiSpecials.Contains((char)cp); + + /// + /// Make text safe for the PDF standard fonts (WinAnsi): map the colour-coded status emoji to ASCII + /// tokens (a monochrome filled circle can't tell red from green), then keep anything the fonts can + /// draw - at or below U+00FF, the CP1252 punctuation set, or, when a fallback font is bundled, an + /// emoji that font actually carries (BMP or astral). Any remaining non-encodable character becomes + /// '?' so OfficeIMO's encoding preflight never throws. + /// + public static string Sanitize(string? s) + { + if (string.IsNullOrEmpty(s)) return s ?? string.Empty; + foreach (var g in GlyphMap) s = s.Replace(g.from, g.to); + if (RenderEmojiGlyphs) foreach (var e in EmojiTokens) s = s.Replace(e.token, e.glyph); + var needsFilter = false; + foreach (var c in s) { if (c > 'ÿ' && !WinAnsiSpecials.Contains(c) && !EmojiKept.Contains(c)) { needsFilter = true; break; } } + if (!needsFilter) return s; + var sb = new StringBuilder(s.Length); + // Walk by grapheme cluster so a multi-code-point emoji (ZWJ sequence, flag, skin-tone) is judged + // and kept as one unit for the colour-image renderer; plain text falls through to the per-code- + // point keep rule (WinAnsi, CP1252 specials, or a code point the monochrome fallback font draws). + var elements = System.Globalization.StringInfo.GetTextElementEnumerator(s); + while (elements.MoveNext()) + { + var cluster = (string)elements.Current; + if (cluster.Length == 1 && cluster[0] <= 'ÿ') { sb.Append(cluster); continue; } + if (RenderEmojiImages && TwemojiAssets.Has(cluster)) { sb.Append(cluster); continue; } + for (var i = 0; i < cluster.Length; i++) + { + var c = cluster[i]; + if (char.IsHighSurrogate(c) && i + 1 < cluster.Length && char.IsLowSurrogate(cluster[i + 1])) + { + var cp = char.ConvertToUtf32(c, cluster[i + 1]); + if (RenderEmojiGlyphs && EmojiCoverage.Contains(cp)) { sb.Append(c); sb.Append(cluster[i + 1]); } + i++; // the low surrogate was consumed with its high half + continue; + } + if (c <= 'ÿ' || WinAnsiSpecials.Contains(c) || EmojiKept.Contains(c) || (RenderEmojiGlyphs && EmojiCoverage.Contains(c))) + sb.Append(c); + else if (!char.IsHighSurrogate(c) && !char.IsLowSurrogate(c)) + sb.Append('?'); + // a lone surrogate (no matching half) is dropped + } + } + return sb.ToString(); + } + + // -- Flatten to runs -- + private static List Flatten(List nodes) + { + var runs = new List(); + void Walk(List ns, TextRun marks) + { + foreach (var n in ns) + { + if (n.Type == "text") + { + if (n.Value.Length == 0) continue; + var r = marks.Clone(); r.Text = Sanitize(n.Value); runs.Add(r); + continue; + } + var next = marks.Clone(); + switch (n.Type) + { + case "strong": next.Bold = true; break; + case "em": next.Italic = true; break; + case "strongEm": next.Bold = true; next.Italic = true; break; + case "code": next.Code = true; break; + case "strike": next.Strike = true; break; + case "underline": next.Underline = true; break; + } + Walk(n.Children, next); + } + } + Walk(nodes, new TextRun()); + return runs; + } + + private static string PlainText(List nodes) + { + var sb = new StringBuilder(); + void Walk(List ns) { foreach (var n in ns) { if (n.Type == "text") sb.Append(Sanitize(n.Value)); else Walk(n.Children); } } + Walk(nodes); + return sb.ToString(); + } + + public static List MarkdownRuns(string? text) => Flatten(ParseInlineMarkdown(text)); + public static List HtmlRuns(string? html) => Flatten(ParseInlineHtml(html)); + public static string MarkdownPlain(string? text) => PlainText(ParseInlineMarkdown(text)); + public static string HtmlPlain(string? html) => PlainText(ParseInlineHtml(html)); + + // -- Table rows (markdown-table.js) -- + private static readonly Regex SeparatorRow = new("^\\|?[\\s:|-]*-[\\s:|-]*\\|?$"); + + public static bool IsTableSeparatorRow(string? line) + { + var t = (line ?? string.Empty).Trim(); + if (!t.Contains('|') || !t.Contains('-')) return false; + return SeparatorRow.IsMatch(t); + } + + public static List ParseTableRow(string? line) + { + var raw = (line ?? string.Empty).Trim(); + var cells = new List(); + if (raw.Length == 0) return cells; + var current = new StringBuilder(); + var leading = false; var trailing = false; + for (var i = 0; i < raw.Length; i++) + { + var ch = raw[i]; + var next = i + 1 < raw.Length ? raw[i + 1] : '\0'; + if (ch == '\\' && (next == '|' || next == '\\')) { current.Append(next); i++; continue; } + if (ch == '|') + { + if (i == 0) leading = true; + if (i == raw.Length - 1) trailing = true; + cells.Add(current.ToString()); current.Clear(); continue; + } + current.Append(ch); + } + cells.Add(current.ToString()); + if (leading && cells.Count > 0) cells.RemoveAt(0); + if (trailing && cells.Count > 0) cells.RemoveAt(cells.Count - 1); + for (var i = 0; i < cells.Count; i++) cells[i] = Sanitize(cells[i].Trim()); + return cells; + } + + public static string[] NormaliseTableRow(List cells, int columnCount) + { + if (columnCount <= 0) return Array.Empty(); + if (cells.Count == columnCount) return cells.ToArray(); + if (cells.Count < columnCount) + { + var padded = new string[columnCount]; + for (var i = 0; i < columnCount; i++) padded[i] = i < cells.Count ? cells[i] : string.Empty; + return padded; + } + var head = cells.GetRange(0, columnCount - 1); + head.Add(string.Join(" | ", cells.GetRange(columnCount - 1, cells.Count - (columnCount - 1)))); + return head.ToArray(); + } + + // -- Block markdown -> nodes (markdownToElements) -- + public static List MarkdownToNodes(string? markdown) + { + var elements = new List(); + if (string.IsNullOrEmpty(markdown)) return elements; + var lines = markdown.Replace("\r\n", "\n").Split('\n'); + + var inCode = false; var code = new StringBuilder(); + var inTable = false; var tableRows = new List>(); + + void FlushTable() + { + if (tableRows.Count > 0) + { + var cols = tableRows[0].Count; + var rows = new List(); + foreach (var r in tableRows) rows.Add(NormaliseTableRow(r, cols)); + var node = new ReportNode("table"); node.Set("rows", rows); node.Set("hasHeader", true); + elements.Add(node); + } + inTable = false; tableRows = new List>(); + } + + // Consecutive list items are grouped into one bullets/numbered node. + List? listItems = null; var listOrdered = false; var listStart = 1; + void FlushList() + { + if (listItems is { Count: > 0 }) + { + var node = new ReportNode(listOrdered ? "numbered" : "bullets"); + node.Set("items", new List(listItems)); + if (listOrdered) node.Set("start", (double)listStart); + elements.Add(node); + } + listItems = null; + } + + foreach (var raw in lines) + { + var line = raw; + var trimmed = line.Trim(); + + if (trimmed.StartsWith("```", StringComparison.Ordinal)) + { + if (inCode) + { + FlushList(); + elements.Add(new ReportNode("code").Set("text", code.ToString().TrimEnd())); + code.Clear(); inCode = false; + } + else { FlushList(); inCode = true; } + continue; + } + if (inCode) { code.Append(line).Append('\n'); continue; } + + if (trimmed.StartsWith("|", StringComparison.Ordinal)) + { + FlushList(); + if (!inTable) { inTable = true; tableRows = new List>(); } + if (IsTableSeparatorRow(line)) continue; + tableRows.Add(ParseTableRow(line)); + continue; + } + if (inTable) FlushTable(); + + if (trimmed.Length == 0) { FlushList(); continue; } + + if (line.StartsWith("### ", StringComparison.Ordinal)) { FlushList(); elements.Add(Heading(3, line.Substring(4), false)); } + else if (line.StartsWith("## ", StringComparison.Ordinal)) { FlushList(); elements.Add(Heading(2, line.Substring(3), false)); } + else if (line.StartsWith("# ", StringComparison.Ordinal)) { FlushList(); elements.Add(Heading(1, line.Substring(2), false)); } + else if (Regex.IsMatch(trimmed, "^[-*_]{3,}$")) { FlushList(); elements.Add(new ReportNode("hr")); } + else if (Regex.IsMatch(trimmed, "^[-*+]\\s")) + { + if (listItems is null || listOrdered) { FlushList(); listItems = new List(); listOrdered = false; } + listItems.Add(MarkdownPlain(Regex.Replace(trimmed, "^[-*+]\\s", string.Empty))); + } + else if (Regex.IsMatch(trimmed, "^\\d+\\.\\s")) + { + var num = int.Parse(Regex.Match(trimmed, "^(\\d+)\\.").Groups[1].Value, CultureInfo.InvariantCulture); + if (listItems is null || !listOrdered) { FlushList(); listItems = new List(); listOrdered = true; listStart = num; } + listItems.Add(MarkdownPlain(Regex.Replace(trimmed, "^\\d+\\.\\s", string.Empty))); + } + else { FlushList(); elements.Add(Paragraph(MarkdownRuns(line))); } + } + if (inCode && code.Length > 0) elements.Add(new ReportNode("code").Set("text", code.ToString().TrimEnd())); + if (inTable) FlushTable(); + FlushList(); + return elements; + } + + // -- Block HTML -> nodes (htmlToElements) -- + public static List HtmlToNodes(string? html) + { + var elements = new List(); + if (string.IsNullOrEmpty(html)) return elements; + + // Extract tables first, replacing each with a placeholder paragraph. + var tables = new List(); + var remaining = Regex.Replace(html, "]*>([\\s\\S]*?)", m => + { + var placeholder = $"__TABLE_{tables.Count}__"; tables.Add(m.Value); return $"

{placeholder}

"; + }, RegexOptions.IgnoreCase); + + var blocks = Regex.Split(remaining, "

|||||"); + var inOrdered = false; var orderedIndex = 0; + + // Group consecutive
  • into one list node. + List? listItems = null; var listOrdered = false; var listStart = 1; + void FlushList() + { + if (listItems is { Count: > 0 }) + { + var node = new ReportNode(listOrdered ? "numbered" : "bullets"); + node.Set("items", new List(listItems)); + if (listOrdered) node.Set("start", (double)listStart); + elements.Add(node); + } + listItems = null; + } + + foreach (var block in blocks) + { + var cleaned = block.Trim(); + if (cleaned.Length == 0) continue; + + if (Regex.IsMatch(cleaned, "]", RegexOptions.IgnoreCase)) { inOrdered = true; orderedIndex = 0; } + else if (Regex.IsMatch(cleaned, "]", RegexOptions.IgnoreCase)) inOrdered = false; + + var tableMatch = Regex.Match(cleaned, "__TABLE_(\\d+)__"); + if (tableMatch.Success) + { + FlushList(); + var idx = int.Parse(tableMatch.Groups[1].Value, CultureInfo.InvariantCulture); + if (idx < tables.Count) elements.Add(HtmlTable(tables[idx])); + continue; + } + + if (Regex.IsMatch(cleaned, "]*>", RegexOptions.IgnoreCase)) { FlushList(); elements.Add(Heading(1, Regex.Replace(cleaned, "]*>", string.Empty, RegexOptions.IgnoreCase), true)); } + else if (Regex.IsMatch(cleaned, "]*>", RegexOptions.IgnoreCase)) { FlushList(); elements.Add(Heading(2, Regex.Replace(cleaned, "]*>", string.Empty, RegexOptions.IgnoreCase), true)); } + else if (Regex.IsMatch(cleaned, "]*>", RegexOptions.IgnoreCase)) { FlushList(); elements.Add(Heading(3, Regex.Replace(cleaned, "]*>", string.Empty, RegexOptions.IgnoreCase), true)); } + else if (Regex.IsMatch(cleaned, "]*>", RegexOptions.IgnoreCase)) + { + if (inOrdered) orderedIndex++; + if (listItems is null || listOrdered != inOrdered) { FlushList(); listItems = new List(); listOrdered = inOrdered; listStart = inOrdered ? orderedIndex : 1; } + listItems.Add(HtmlPlain(Regex.Replace(cleaned, "]*>", string.Empty, RegexOptions.IgnoreCase))); + } + else if (Regex.IsMatch(cleaned, "]*>", RegexOptions.IgnoreCase)) + { + FlushList(); + var inner = Regex.Replace(Regex.Replace(cleaned, "]*>", string.Empty, RegexOptions.IgnoreCase), "]*>", string.Empty, RegexOptions.IgnoreCase); + elements.Add(new ReportNode("code").Set("text", HtmlPlain(inner))); + } + else + { + var inner = Regex.Replace(cleaned, "]*>", string.Empty, RegexOptions.IgnoreCase); + if (HtmlPlain(inner).Trim().Length > 0) { FlushList(); elements.Add(Paragraph(HtmlRuns(inner))); } + } + + if (Regex.IsMatch(cleaned, "", RegexOptions.IgnoreCase)) { FlushList(); inOrdered = false; } + } + FlushList(); + return elements; + } + + private static ReportNode HtmlTable(string tableHtml) + { + var rows = new List(); + var rowHeaderFlags = new List(); + foreach (Match rowMatch in Regex.Matches(tableHtml, "]*>([\\s\\S]*?)", RegexOptions.IgnoreCase)) + { + var cells = new List(); + var allHeader = true; + foreach (Match cellMatch in Regex.Matches(rowMatch.Groups[1].Value, "<(td|th)[^>]*>([\\s\\S]*?)", RegexOptions.IgnoreCase)) + { + if (!cellMatch.Groups[1].Value.Equals("th", StringComparison.OrdinalIgnoreCase)) allHeader = false; + cells.Add(HtmlPlain(cellMatch.Groups[2].Value.Trim())); + } + if (cells.Count > 0) { rows.Add(cells.ToArray()); rowHeaderFlags.Add(allHeader); } + } + var hasHeader = Regex.IsMatch(tableHtml, " 0 && rowHeaderFlags[0]); + var cols = rows.Count > 0 ? rows[0].Length : 0; + var normalised = new List(); + foreach (var r in rows) normalised.Add(NormaliseTableRow(new List(r), cols)); + var node = new ReportNode("table"); node.Set("rows", normalised); node.Set("hasHeader", hasHeader); + return node; + } + + private static ReportNode Heading(int level, string text, bool html) + => new ReportNode("heading").Set("level", (double)level) + .Set("runs", html ? HtmlRuns(text) : MarkdownRuns(text)); + + private static ReportNode Paragraph(List runs) => new ReportNode("paragraph").Set("runs", runs); + } +} diff --git a/backend/Shared/CIPPSharp/Reporting/ReportModel.cs b/backend/Shared/CIPPSharp/Reporting/ReportModel.cs new file mode 100644 index 0000000000..c7ff63f0af --- /dev/null +++ b/backend/Shared/CIPPSharp/Reporting/ReportModel.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text.Json; + +namespace CIPP.Reporting +{ + /// An inline run of text with its marks - the shared node the markdown and HTML parsers both emit. + public sealed class TextRun + { + public string Text { get; set; } = string.Empty; + public bool Bold { get; set; } + public bool Italic { get; set; } + public bool Code { get; set; } + public bool Strike { get; set; } + public bool Underline { get; set; } + + public TextRun Clone() => new() + { + Text = Text, Bold = Bold, Italic = Italic, Code = Code, Strike = Strike, Underline = Underline, + }; + } + + /// + /// One component in a report. The declarative unit every report composes and never bypasses: + /// a type, its props, and optional children. Built either from the JSON component tree (Report + /// Builder blocks / fixed-report output) or synthesised by the markdown/HTML parsers. + /// + public sealed class ReportNode + { + public string Type { get; set; } = string.Empty; + public Dictionary Props { get; } = new(StringComparer.OrdinalIgnoreCase); + public List Children { get; } = new(); + + public ReportNode() { } + public ReportNode(string type) { Type = type; } + + public ReportNode Set(string key, object? value) { Props[key] = value; return this; } + + public string? Str(string key) => Props.TryGetValue(key, out var v) ? v as string : null; + + public bool Bool(string key, bool @default = false) + => Props.TryGetValue(key, out var v) && v is bool b ? b : @default; + + public double? Num(string key) + => Props.TryGetValue(key, out var v) && v is double d ? d : (double?)null; + + public T? Get(string key) where T : class + => Props.TryGetValue(key, out var v) ? v as T : null; + + public List? ListOf(string key) + => Props.TryGetValue(key, out var v) ? v as List : null; + + // -- JSON parsing -- + public static List ParseTree(string? json) + { + var result = new List(); + if (string.IsNullOrWhiteSpace(json)) return result; + JsonElement root; + try { root = JsonDocument.Parse(json).RootElement; } + catch { return result; } + if (root.ValueKind == JsonValueKind.Array) + { + foreach (var el in root.EnumerateArray()) + if (el.ValueKind == JsonValueKind.Object) result.Add(FromElement(el)); + } + else if (root.ValueKind == JsonValueKind.Object) + { + result.Add(FromElement(root)); + } + return result; + } + + private static ReportNode FromElement(JsonElement el) + { + var node = new ReportNode + { + Type = el.TryGetProperty("type", out var t) && t.ValueKind == JsonValueKind.String + ? t.GetString()!.ToLowerInvariant() + : string.Empty, + }; + foreach (var prop in el.EnumerateObject()) + { + if (prop.NameEquals("type")) continue; + if (prop.NameEquals("children") && prop.Value.ValueKind == JsonValueKind.Array) + { + foreach (var child in prop.Value.EnumerateArray()) + if (child.ValueKind == JsonValueKind.Object) node.Children.Add(FromElement(child)); + continue; + } + node.Props[prop.Name] = Convert(prop.Value); + } + return node; + } + + private static object? Convert(JsonElement el) => el.ValueKind switch + { + JsonValueKind.String => el.GetString(), + JsonValueKind.True => true, + JsonValueKind.False => false, + JsonValueKind.Number => el.TryGetDouble(out var d) ? d : 0d, + JsonValueKind.Array => ConvertArray(el), + JsonValueKind.Object => ConvertObject(el), + _ => null, + }; + + private static List ConvertArray(JsonElement el) + { + var list = new List(); + foreach (var item in el.EnumerateArray()) list.Add(Convert(item)); + return list; + } + + private static Dictionary ConvertObject(JsonElement el) + { + var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var p in el.EnumerateObject()) dict[p.Name] = Convert(p.Value); + return dict; + } + + /// Read a nested object's string field (for stats/chart/progress data rows). + public static string? RowStr(object? row, string key) + => row is Dictionary d && d.TryGetValue(key, out var v) ? v?.ToString() : null; + + public static double RowNum(object? row, string key) + { + if (row is Dictionary d && d.TryGetValue(key, out var v)) + { + if (v is double dd) return dd; + if (v is string s && double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var p)) return p; + } + return 0; + } + } +} diff --git a/backend/Shared/CIPPSharp/Reporting/ReportPdf.cs b/backend/Shared/CIPPSharp/Reporting/ReportPdf.cs new file mode 100644 index 0000000000..a201fe6f4c --- /dev/null +++ b/backend/Shared/CIPPSharp/Reporting/ReportPdf.cs @@ -0,0 +1,434 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using OfficeIMO; +using OfficeIMO.Pdf; + +namespace CIPP.Reporting +{ + /// + /// Entry point for server-side report rendering. Called from PowerShell as + /// [CIPP.Reporting.ReportPdf]::Render(...) (wrapped by ConvertTo-CippReportPdf.ps1). + /// Takes the declarative component tree + branding + variables and returns the finished PDF bytes, + /// composing the OfficeIMO document from the shared component kit - cover page, then content + /// sections carrying header / footer / page numbers / watermark, with hero pages and page breaks + /// splitting the flow (buildPageGroups equivalent). + /// + public static class ReportPdf + { + public static byte[] Render( + string blocksJson, + string brandingJson, + string variablesJson, + string tenantName, + string reportName, + string generatedOn, + string pageSize = "A4", + bool landscape = false, + bool chrome = true) + { + var blocks = ReportNode.ParseTree(blocksJson); + var branding = BrandingInput.FromJson(brandingJson); + var theme = ReportTheme.Create(branding); + + var variables = ParseVariables(variablesJson); + variables["tenantname"] = tenantName ?? "Organization"; + variables["reportname"] = reportName ?? "Report"; + variables["reportdate"] = generatedOn ?? string.Empty; + + // A report builder cover block: its fields become the cover variables a fixed report's tree + // builder supplies, and the block itself draws nothing. + var cover = blocks.Find(b => b.Type == "cover"); + if (cover != null) + { + foreach (var (variable, field) in new[] { ("covertitle", "title"), ("coveraccent", "coverAccent"), ("coversubtitle", "subtitle"), ("coverlabel", "coverLabel") }) + { + var value = cover.Str(field); + if (!string.IsNullOrWhiteSpace(value)) variables[variable] = value; + } + blocks.RemoveAll(b => b.Type == "cover"); + } + + var logo = ReportComponents.DecodeImage(branding.Logo); + // The cover photo, resolved the way the client's resolveCoverImage did: an uploaded cover wins, + // then the stock photo the branding picked ("none" means no cover at all), then the report's + // own %coverfallbackimage% (e.g. "/reportImages/soc.jpg"), so a report without configured + // branding still gets a cover. + var coverImage = ReportComponents.DecodeImage(branding.CoverImage) + ?? (branding.CoverStock == "none" ? null + : ReportComponents.DecodeImage(branding.CoverStock) + ?? (variables.TryGetValue("coverfallbackimage", out var coverFallback) ? ReportComponents.DecodeImage(coverFallback) : null)); + ReportContext Context(byte[]? logoBytes) => new() + { + Theme = theme, + Variables = variables, + PageSize = string.IsNullOrWhiteSpace(pageSize) ? "A4" : pageSize, + Landscape = landscape, + TenantName = tenantName ?? "Organization", + ReportName = reportName ?? "Report", + GeneratedOn = generatedOn ?? string.Empty, + Logo = logoBytes, + CoverImage = coverImage, + }; + + var groups = BuildPageGroups(blocks); + // A branding logo the engine cannot embed (a PNG with a bad chunk CRC, an unsupported format) + // only surfaces when the document is serialised, not where the logo is placed, so it must not + // sink the report: render once more without it. Any other failure still propagates. + var watermark = theme.WatermarkEnabled ? ReportTheme.ApplyWatermark(theme.WatermarkText, variables) : string.Empty; + byte[] Finish(PdfDocument doc) => chrome ? LiftWatermark(doc.ToBytes(), watermark) : doc.ToBytes(); + try { return Finish(Compose(Context(logo), groups, chrome, watermark)); } + catch when (logo is not null) { return Finish(Compose(Context(null), groups, chrome, watermark)); } + } + + private static PdfDocument Compose(ReportContext ctx, List groups, bool chrome, string watermark) + { + var theme = ctx.Theme; + var variables = ctx.Variables; + // Branding's configured footer wins; a report's own %footerlabel% is the fallback (client + // PageFooter's `label`), so a fixed report still identifies itself when no branding footer is set. + var footerText = theme.FooterEnabled + ? ReportTheme.ApplyFooter(theme.FooterTemplate, variables) + : (variables.TryGetValue("footerlabel", out var fl) ? ReportTheme.ApplyFooter(fl, variables) : string.Empty); + + return PdfDocument.Create(compose => + { + // OfficeIMO 3.4 dropped the document-wide Defaults() hook, so the page size, margin and + // orientation are applied to every page/section here instead. + void ApplyPageDefaults(PdfPageBuilder p) + { + p.Size(ResolveSize(ctx.PageSize)); + if (ctx.Landscape) p.Landscape(); + p.Margin(ReportStyles.PagePadding); + } + + // Cover - its own page, no header/footer/watermark. Skipped in chrome-less mode (used by + // the component A/B harness and any embedded/preview render that wants only the content). + // A cover photo (branding) fills the page behind the drawn cover content. + if (chrome) + compose.Page(p => + { + ApplyPageDefaults(p); + p.Background(ReportComponents.Pdf(ReportColours.White)); + if (ctx.CoverImage is { Length: > 0 }) + { + try { p.BackgroundImage(ctx.CoverImage, OfficeIMO.Drawing.OfficeImageFit.Cover, 0.5); } + catch { /* an unusable cover photo leaves the plain white cover */ } + } + p.Content(cc => cc.Item(i => ReportComponents.RenderCoverDrawing(ctx, i))); + }); + + foreach (var group in groups) + { + if (group.Kind == "hero") + { + // Full-bleed divider: a dark page with an optional cover photo behind the big figure, + // no header/footer chrome. The photo is a page background image (edge to edge); the + // text is drawn over it, vertically centred, with the footer note bottom-right. + var heroImage = ReportComponents.DecodeImage(group.Block!.Str("heroImage") ?? group.Block!.Str("backgroundImage")); + compose.Section(p => + { + ApplyPageDefaults(p); + p.Background(ReportComponents.Pdf(ctx.Theme.Palette["infographicBackground"])); + if (heroImage is { Length: > 0 }) + { + try { p.BackgroundImage(heroImage, OfficeIMO.Drawing.OfficeImageFit.Cover, 0.28); } + catch { /* an unusable cover photo leaves the plain dark page */ } + } + // The client's watermarkTextOnDark: the same mark in the divider's text colour and a + // little stronger, since an 8% brand-colour mark disappears on a dark page. + if (!string.IsNullOrEmpty(watermark)) + p.Watermark(watermark.ToUpperInvariant(), fontSize: 72, color: ReportComponents.Pdf(ctx.Theme.OnInfographic), opacity: 0.12, rotationAngle: -45, bold: true); + p.Content(cc => cc.Item(i => ReportComponents.RenderHeroDrawing(ctx, i, group.Block!))); + }); + continue; + } + compose.Section(p => + { + ApplyPageDefaults(p); + if (chrome) ApplyContentChrome(p, ctx, footerText, watermark); + p.Content(cc => cc.Item(i => + { + // A titled group (a fixed report's ContentPage) heads with its own title and + // subtitle; the Report Builder path has neither, so it falls back to the report + // name and the generated-on date. + if (chrome) RenderPageHeader(ctx, i, group.Title ?? ctx.ReportName, group.Subtitle ?? ctx.GeneratedOn); + var firstBlock = true; + // Two adjacent half-width charts render side by side in one row; anything else + // (including a lone half-width chart) renders on its own line. + for (var bi = 0; bi < group.Blocks.Count; bi++) + { + var block = group.Blocks[bi]; + if (ReportComponents.IsHalfWidthChart(block) && bi + 1 < group.Blocks.Count && ReportComponents.IsHalfWidthChart(group.Blocks[bi + 1])) + { + ReportComponents.RenderChartPair(ctx, i, block, group.Blocks[bi + 1], firstBlock); + bi++; + } + else + { + ReportComponents.RenderBlock(ctx, i, block, firstBlock); + } + firstBlock = false; + } + })); + }); + } + }, BuildOptions()); + } + + // PDF options carrying the emoji fallback font: the standard fonts have no glyph for any emoji, so a + // bundled monochrome symbol/emoji font is registered as a Unicode fallback and OfficeIMO routes each + // emoji code point through it (per character - the surrounding text stays in the standard font), in + // table cells and paragraphs alike. The report's three status emoji are tinted per run downstream; + // everything else renders in the surrounding text colour. + private static PdfOptions BuildOptions() + { + var options = new PdfOptions(); + var font = ReportMarkdown.EmojiFontBytes.Value; + var coverage = ReportMarkdown.EmojiCoverage; + if (font is { Length: > 0 } && coverage.Count > 0) + { + // OfficeIMO embeds only the glyphs a given report actually uses, compressed, so a report + // gains a few KB, not the whole ~800 KB font. The fallback is scoped to exactly the code + // points the font carries above U+00FF (the Latin-1 glyphs it also holds exist only so + // OfficeIMO's greedy neighbour-of-an-emoji fallback never lands on an uncovered character), + // so ordinary text always stays in the standard font. + var ranges = new OfficeIMO.Drawing.OfficeFontUnicodeRangeSet(CoverageRanges(coverage)); + options.CompressEmbeddedFonts = true; + options.EmbeddedFontFallbacks = new PdfEmbeddedFontFallbackSet( + new[] { new PdfEmbeddedFontFallbackCandidate("CippReportEmoji", font, ranges) }); + } + return options; + } + + // The coverage set compressed into [start,end] ranges for the fallback scope. The emoji blocks are + // scattered, so this coalesces across the smallest gaps until within OfficeIMO's 1..128-range limit. + // Widening a range is safe: it only ever includes unassigned/uncovered code points, which Sanitize + // (gated on the exact coverage set) never keeps, so no uncovered character is routed to the fallback. + private const int MaxFallbackRanges = 128; + private static OfficeIMO.Drawing.OfficeFontUnicodeRange[] CoverageRanges(HashSet coverage) + { + var sorted = new List(coverage); + sorted.Sort(); + var ranges = new List<(int start, int end)>(); + var start = sorted[0]; + var prev = sorted[0]; + for (var k = 1; k < sorted.Count; k++) + { + if (sorted[k] == prev + 1) { prev = sorted[k]; continue; } + ranges.Add((start, prev)); + start = prev = sorted[k]; + } + ranges.Add((start, prev)); + + while (ranges.Count > MaxFallbackRanges) + { + var mergeAt = 0; + var smallestGap = int.MaxValue; + for (var k = 0; k < ranges.Count - 1; k++) + { + var gap = ranges[k + 1].start - ranges[k].end; + if (gap < smallestGap) { smallestGap = gap; mergeAt = k; } + } + ranges[mergeAt] = (ranges[mergeAt].start, ranges[mergeAt + 1].end); + ranges.RemoveAt(mergeAt + 1); + } + return ranges.ConvertAll(r => new OfficeIMO.Drawing.OfficeFontUnicodeRange(r.start, r.end)).ToArray(); + } + + // The styled page header (big title + subtitle + brand rule) that opens each content group, + // matching the client's ContentPage header. Rendered as content rather than a running header so + // it can carry the brand-coloured rule the running-header API can't draw. + private static void RenderPageHeader(ReportContext ctx, PdfContentBuilder item, string? title, string? subtitle) + { + // Title and subtitle read as one unit (client pageTitle marginBottom 8), so the title line box + // is kept tight - the default paragraph line height otherwise balloons the gap between them - + // then the client paddingBottom 8 before the brand rule, and marginBottom 14 after it. + var titleColour = ctx.Theme.Palette["title"]; + var subtitleColour = ctx.Theme.Palette["subtitle"]; + Action titleRun = b => { b.FontSize(ReportStyles.PageTitle); ReportComponents.EmitInline(b, title ?? ctx.ReportName, titleColour, ReportStyles.PageTitle, bold: true); }; + var titleStyle = new PdfParagraphStyle { LineHeight = 1.0, SpacingAfter = 3 }; + Action? subtitleRun = string.IsNullOrEmpty(subtitle) ? null + : b => { b.FontSize(ReportStyles.PageSubtitle); ReportComponents.EmitInline(b, subtitle!, subtitleColour, ReportStyles.PageSubtitle); }; + var subtitleStyle = new PdfParagraphStyle { LineHeight = 1.05, SpacingAfter = 3 }; + + // Client pageHeader: the title block takes the width and the branding logo sits at the right + // edge, 30pt tall and top-aligned with the title. Without a logo the paragraphs flow directly. + var logoType = ctx.Logo is { Length: > 0 } ? ReportComponents.ImageContentType(ctx.Logo) : null; + if (logoType is null) + { + item.Paragraph(titleRun, PdfAlign.Left, null, titleStyle); + if (subtitleRun is not null) item.Paragraph(subtitleRun, PdfAlign.Left, null, subtitleStyle); + } + else + { + var box = ReportComponents.LogoBox(ctx.Logo!, 30, 120); + item.Row(r => + { + r.Gap(12); + r.PercentColumn(75, c => + { + c.Paragraph(titleRun, PdfAlign.Left, null, titleStyle); + if (subtitleRun is not null) c.Paragraph(subtitleRun, PdfAlign.Left, null, subtitleStyle); + }); + r.PercentColumn(25, c => c.Image(ctx.Logo!, box.w, box.h, PdfAlign.Right)); + }); + } + // Full-width brand rule under the header (HR auto-fits the content width; a fixed-width + // rectangle risks exceeding it). + item.HR(2, ReportComponents.Pdf(ctx.Theme.Palette["heading"])); + item.Spacer(8); + } + + private static void ApplyContentChrome(PdfPageBuilder p, ReportContext ctx, string footerText, string watermark) + { + if (ctx.Theme.FooterShow || ctx.Theme.ShowPageNumbers) + { + p.Footer(f => + { + f.Color(ReportComponents.Pdf(ctx.Theme.Palette["footer"])).FontSize(ReportStyles.FooterText); + var showText = ctx.Theme.FooterShow && !string.IsNullOrEmpty(footerText); + var safeFooter = ReportMarkdown.Sanitize(footerText); + if (ctx.Theme.ShowPageNumbers) + f.Text(b => + { + if (showText) b.Text(safeFooter + " - "); + b.Text("Page ").CurrentPage().Text(" of ").TotalPages(); + }); + else if (showText) + f.Text(safeFooter); + }); + } + // Named on purpose: OfficeIMO's positional order is (text, fontSize, colour, opacity, angle), so + // Watermark(text, 0.08, colour) asks for a 0.08pt watermark - drawn, but invisible. The values + // mirror the client's watermarkText style: 72pt bold, uppercase, 8% opacity, rotated -45deg. + if (!string.IsNullOrEmpty(watermark)) + p.Watermark(watermark.ToUpperInvariant(), fontSize: 72, color: ReportComponents.Pdf(ctx.Theme.Palette["watermark"]), opacity: 0.08, rotationAngle: -45, bold: true); + } + + /// + /// OfficeIMO paints a page watermark before anything else on the page, so every card, panel and + /// image covers it, where the client reports drew it over the content. The PDF it writes is + /// classic - no object or cross-reference streams - with uncompressed content streams, so the + /// watermark's operator block (a self-contained q ... Q group, recognised by its text and its + /// -45 degree text matrix) is moved to the end of each page's content stream in place: the same + /// bytes in a different order, so no length or offset in the file changes. A stream the block + /// cannot be found in is left as written. + /// + internal static byte[] LiftWatermark(byte[] pdf, string watermark) + { + if (string.IsNullOrEmpty(watermark)) return pdf; + var marker = Encoding.ASCII.GetBytes("<" + Convert.ToHexString(Encoding.Latin1.GetBytes(watermark.ToUpperInvariant())) + "> Tj"); + var rotation = Encoding.ASCII.GetBytes("0.707 -0.707 0.707 0.707"); + var streamTag = Encoding.ASCII.GetBytes("stream\n"); + var lengthTag = Encoding.ASCII.GetBytes("/Length "); + var open = Encoding.ASCII.GetBytes("q\n"); + var close = Encoding.ASCII.GetBytes("\nQ\n"); + + var at = 0; + while ((at = IndexOf(pdf, marker, at, pdf.Length)) >= 0) + { + var streamAt = LastIndexOf(pdf, streamTag, at); + var lengthAt = streamAt < 0 ? -1 : LastIndexOf(pdf, lengthTag, streamAt); + if (streamAt < 0 || lengthAt < 0 || streamAt - lengthAt > 64) { at += marker.Length; continue; } + var dataStart = streamAt + streamTag.Length; + var length = 0; + for (var d = lengthAt + lengthTag.Length; d < pdf.Length && pdf[d] >= '0' && pdf[d] <= '9'; d++) length = length * 10 + (pdf[d] - '0'); + var dataEnd = Math.Min(dataStart + length, pdf.Length); + + // Back to the "q" that opens the watermark's group (at a line start), forward to its "Q". + var blockStart = LastIndexOf(pdf, open, at); + while (blockStart > dataStart && pdf[blockStart - 1] != '\n') blockStart = LastIndexOf(pdf, open, blockStart); + var closeAt = IndexOf(pdf, close, at, dataEnd); + if (blockStart < dataStart || closeAt < 0) { at = dataEnd; continue; } + var blockEnd = closeAt + close.Length; + if (IndexOf(pdf, rotation, blockStart, blockEnd) < 0) { at = dataEnd; continue; } + + var block = pdf[blockStart..blockEnd]; + var tail = pdf[blockEnd..dataEnd]; + // Same length: the block's trailing newline becomes the separator in front of it. + Buffer.BlockCopy(tail, 0, pdf, blockStart, tail.Length); + var moved = blockStart + tail.Length; + pdf[moved] = (byte)'\n'; + Buffer.BlockCopy(block, 0, pdf, moved + 1, block.Length - 1); + at = dataEnd; + } + return pdf; + } + + private static int IndexOf(byte[] hay, byte[] needle, int from, int to) + { + for (var i = Math.Max(0, from); i <= to - needle.Length; i++) + { + var j = 0; + while (j < needle.Length && hay[i + j] == needle[j]) j++; + if (j == needle.Length) return i; + } + return -1; + } + + private static int LastIndexOf(byte[] hay, byte[] needle, int before) + { + for (var i = Math.Min(before, hay.Length) - needle.Length; i >= 0; i--) + { + var j = 0; + while (j < needle.Length && hay[i + j] == needle[j]) j++; + if (j == needle.Length) return i; + } + return -1; + } + + private sealed class PageGroup + { + public string Kind = "content"; // content|hero + public List Blocks = new(); + public ReportNode? Block; + public string? Title; // a fixed report's ContentPage title (null -> report name) + public string? Subtitle; // its descriptive subtitle (null -> generated-on date) + } + + private static List BuildPageGroups(List blocks) + { + var groups = new List(); + var current = new PageGroup(); + void Flush() { if (current.Blocks.Count > 0) { groups.Add(current); current = new PageGroup { Title = null, Subtitle = null }; } } + foreach (var block in blocks) + { + if (block.Type == "pagebreak") { Flush(); continue; } + if (block.Type == "hero") { Flush(); groups.Add(new PageGroup { Kind = "hero", Block = block }); current = new PageGroup(); continue; } + // A 'page' block opens a new titled content page (a fixed report's ContentPage). + if (block.Type == "page") { Flush(); current.Title = block.Str("title"); current.Subtitle = block.Str("subtitle"); continue; } + current.Blocks.Add(block); + } + Flush(); + if (groups.Count == 0) groups.Add(new PageGroup()); + return groups; + } + + private static PageSize ResolveSize(string size) => (size ?? "A4").ToUpperInvariant() switch + { + "LETTER" => PageSizes.Letter, + "LEGAL" => PageSizes.Legal, + "A3" => PageSizes.A3, + "A5" => PageSizes.A5, + _ => PageSizes.A4, + }; + + private static Dictionary ParseVariables(string? json) + { + var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (string.IsNullOrWhiteSpace(json)) return dict; + try + { + var root = JsonDocument.Parse(json).RootElement; + if (root.ValueKind == JsonValueKind.Object) + foreach (var p in root.EnumerateObject()) + if (p.Value.ValueKind == JsonValueKind.String) dict[p.Name] = p.Value.GetString()!; + else if (p.Value.ValueKind is JsonValueKind.Number or JsonValueKind.True or JsonValueKind.False) dict[p.Name] = p.Value.ToString(); + } + catch { /* leave empty */ } + return dict; + } + } +} diff --git a/backend/Shared/CIPPSharp/Reporting/ReportStyles.cs b/backend/Shared/CIPPSharp/Reporting/ReportStyles.cs new file mode 100644 index 0000000000..bb4005e1e1 --- /dev/null +++ b/backend/Shared/CIPPSharp/Reporting/ReportStyles.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; + +namespace CIPP.Reporting +{ + /// + /// Layout constants ported from frontend/src/components/CippPdf/reportPdfStyles.js - page metrics, + /// paddings and the font sizes the components draw at, so server output matches the client's spacing. + /// + public static class ReportStyles + { + public const double PagePadding = 32; // margin down each side of a content page + + // Font sizes (points). + public const double PageTitle = 20; + public const double PageSubtitle = 11; + public const double SectionTitle = 14; + public const double Body = 9; + public const double Heading1 = 16; + public const double Heading2 = 14; + public const double Heading3 = 12; + public const double TableCell = 8; + public const double TableHeaderCell = 7; + public const double BulletText = 9; + public const double CodeBlock = 8; + public const double FooterText = 7; + public const double StatNumber = 20; + public const double StatLabel = 7; + public const double StatCaption = 7; + public const double InfoTitle = 10; + public const double InfoText = 8; + public const double AlertTitle = 11; + public const double AlertText = 9; + public const double CoverTitle = 48; + public const double CoverSubtitle = 14; + public const double CoverLabel = 10; + + private static readonly Dictionary PageWidths = new(StringComparer.OrdinalIgnoreCase) + { + ["A4"] = 595.28, ["LETTER"] = 612, ["LEGAL"] = 612, ["A3"] = 841.89, ["A5"] = 419.53, + }; + private static readonly Dictionary PageHeights = new(StringComparer.OrdinalIgnoreCase) + { + ["A4"] = 841.89, ["LETTER"] = 792, ["LEGAL"] = 1008, ["A3"] = 1190.55, ["A5"] = 595.28, + }; + + /// Usable table/content width: paper (long edge if landscape) minus page padding both sides. + public static double ContentWidth(string size, bool landscape) + { + var key = (size ?? "A4").ToUpperInvariant(); + var w = PageWidths.TryGetValue(key, out var pw) ? pw : PageWidths["A4"]; + var h = PageHeights.TryGetValue(key, out var ph) ? ph : PageHeights["A4"]; + var paper = landscape ? h : w; + return paper - PagePadding * 2; + } + + /// Usable content height: paper (short edge if landscape) minus page padding both sides. + public static double ContentHeight(string size, bool landscape) + { + var key = (size ?? "A4").ToUpperInvariant(); + var w = PageWidths.TryGetValue(key, out var pw) ? pw : PageWidths["A4"]; + var h = PageHeights.TryGetValue(key, out var ph) ? ph : PageHeights["A4"]; + var paper = landscape ? w : h; + return paper - PagePadding * 2; + } + } +} diff --git a/backend/Shared/CIPPSharp/Reporting/ReportTheme.cs b/backend/Shared/CIPPSharp/Reporting/ReportTheme.cs new file mode 100644 index 0000000000..86ec060a5d --- /dev/null +++ b/backend/Shared/CIPPSharp/Reporting/ReportTheme.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text.RegularExpressions; + +namespace CIPP.Reporting +{ + /// + /// Server-side port of frontend/src/components/CippPdf/reportTheme.js. The single place a report + /// works out what it looks like: two brand colours, readable text on each, a chart series, and the + /// footer/watermark configuration. Kept as a faithful 1:1 port so a server-rendered report matches + /// the client preview colour-for-colour. Pure (no OfficeIMO dependency) so it is unit-testable + /// against the JS values. + /// + public static class ReportColours + { + public const string Danger = "#742A2A"; + public const string Warning = "#744210"; + public const string Success = "#22543D"; + public const string Info = "#2C5282"; + public const string Ink = "#1A202C"; + public const string Body = "#2D3748"; + public const string Muted = "#4A5568"; + public const string Faint = "#718096"; + public const string Line = "#E2E8F0"; + public const string Panel = "#F7FAFC"; + public const string White = "#FFFFFF"; + // Callout background tints (match the client InfoBox/AlertBox/ClearBox). + public const string AlertBg = "#FFF5F5"; + public const string OkBg = "#F0FDF4"; + public const string WarnBg = "#FEF5E7"; + } + + /// Colour maths ported from reportTheme.js - hex normalisation, WCAG luminance, mixing. + public static class ColourMath + { + public const string DefaultBrandColour = "#F77F00"; + private const double WhiteTextMinContrast = 2.5; + private static readonly Regex HexPattern = new("^#?([0-9a-f]{3}|[0-9a-f]{6})$", RegexOptions.IgnoreCase); + + /// #RGB / #RRGGBB / bare digits -> canonical #RRGGBB, or null if unparseable. + public static string? NormaliseHex(string? value) + { + if (value is null) return null; + var trimmed = value.Trim(); + if (!HexPattern.IsMatch(trimmed)) return null; + var digits = trimmed.Replace("#", string.Empty); + if (digits.Length == 3) + { + var chars = new char[6]; + for (var i = 0; i < 3; i++) { chars[i * 2] = digits[i]; chars[i * 2 + 1] = digits[i]; } + digits = new string(chars); + } + return "#" + digits.ToUpperInvariant(); + } + + // hsl(H, S%, L%) - the palette form the dashboard sankeys emit (e.g. "hsl(210, 70%, 50%)"). Commas + // or spaces between the three parts; the % on S/L is optional. Anything else returns null. + private static readonly Regex HslPattern = new( + @"^hsl\(\s*(-?\d+(?:\.\d+)?)\s*[, ]\s*(\d+(?:\.\d+)?)%?\s*[, ]\s*(\d+(?:\.\d+)?)%?\s*\)$", + RegexOptions.IgnoreCase); + + private static (int r, int g, int b)? TryHslToRgb(string? value) + { + if (value is null) return null; + var m = HslPattern.Match(value.Trim()); + if (!m.Success) return null; + var h = double.Parse(m.Groups[1].Value, CultureInfo.InvariantCulture); + var s = double.Parse(m.Groups[2].Value, CultureInfo.InvariantCulture) / 100.0; + var l = double.Parse(m.Groups[3].Value, CultureInfo.InvariantCulture) / 100.0; + h = ((h % 360) + 360) % 360 / 360.0; + s = Math.Max(0, Math.Min(1, s)); + l = Math.Max(0, Math.Min(1, l)); + if (s == 0) + { + var g = (int)Math.Round(l * 255); + return (g, g, g); + } + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + static double Hue(double p, double q, double t) + { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1.0 / 6) return p + (q - p) * 6 * t; + if (t < 1.0 / 2) return q; + if (t < 2.0 / 3) return p + (q - p) * (2.0 / 3 - t) * 6; + return p; + } + return ( + (int)Math.Round(Hue(p, q, h + 1.0 / 3) * 255), + (int)Math.Round(Hue(p, q, h) * 255), + (int)Math.Round(Hue(p, q, h - 1.0 / 3) * 255)); + } + + /// Hex, hsl() (or the default brand colour when unparseable) as 0-255 channels. + public static (int r, int g, int b) ToRgb(string? hex) + { + var hsl = TryHslToRgb(hex); + if (hsl.HasValue) return hsl.Value; + var normalised = NormaliseHex(hex) ?? DefaultBrandColour; + var d = normalised.Substring(1); + return ( + int.Parse(d.Substring(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture), + int.Parse(d.Substring(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture), + int.Parse(d.Substring(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture)); + } + + private static string ToHex(double r, double g, double b) + { + static string Ch(double v) => ((int)Math.Max(0, Math.Min(255, Math.Round(v)))).ToString("X2", CultureInfo.InvariantCulture); + return "#" + Ch(r) + Ch(g) + Ch(b); + } + + private static double RelativeLuminance(string hex) + { + var (r, g, b) = ToRgb(hex); + static double Ch(int value) + { + var s = value / 255.0; + return s <= 0.03928 ? s / 12.92 : Math.Pow((s + 0.055) / 1.055, 2.4); + } + return 0.2126 * Ch(r) + 0.7152 * Ch(g) + 0.0722 * Ch(b); + } + + private static double ContrastRatio(string a, string b) + { + var la = RelativeLuminance(a); + var lb = RelativeLuminance(b); + var lighter = Math.Max(la, lb); + var darker = Math.Min(la, lb); + return (lighter + 0.05) / (darker + 0.05); + } + + /// White on the brand colour where legible, else near-black - matches readableTextOn. + public static string ReadableTextOn(string? background) + { + var hex = NormaliseHex(background) ?? DefaultBrandColour; + return ContrastRatio(hex, ReportColours.White) >= WhiteTextMinContrast ? ReportColours.White : ReportColours.Ink; + } + + public static string Mix(string hex, string target, double amount) + { + var (fr, fg, fb) = ToRgb(hex); + var (tr, tg, tb) = ToRgb(target); + var ratio = Math.Max(0, Math.Min(1, amount)); + return ToHex(fr + (tr - fr) * ratio, fg + (tg - fg) * ratio, fb + (tb - fb) * ratio); + } + + public static string Lighten(string hex, double amount) => Mix(hex, "#FFFFFF", amount); + public static string Darken(string hex, double amount) => Mix(hex, "#000000", amount); + } + + /// One colour role, resolved from branding with a fallback derived from the brand colours. + internal sealed record ColourRole(string Key, string Setting, Func From); + + public sealed class ReportTheme + { + public string Primary { get; } + public string Secondary { get; } + public string OnPrimary { get; } + public IReadOnlyDictionary Palette { get; } + public string OnTable { get; } + public string OnInfographic { get; } + public IReadOnlyList Series { get; } + // Footer + public bool FooterShow { get; } + public bool FooterEnabled { get; } + public string FooterTemplate { get; } + public bool ShowPageNumbers { get; } + // Watermark + public bool WatermarkEnabled { get; } + public string WatermarkText { get; } + public string CoverFooterText { get; } + + public const int FooterMaxLength = 200; + public const int WatermarkMaxLength = 40; + + // Mirrors REPORT_COLOUR_ROLES. `from(primary, secondary)` is the fallback derivation. + private static readonly ColourRole[] Roles = + { + new("chart", "chartColour", (p, _) => p), + new("chartAccent", "chartAccentColour", (_, s) => s), + new("title", "titleColour", (_, _) => ReportColours.Ink), + new("coverText", "coverTextColour", (_, _) => ReportColours.Ink), + new("subtitle", "subtitleColour", (_, _) => ReportColours.Muted), + new("heading", "headingColour", (p, _) => p), + new("body", "bodyColour", (_, _) => ReportColours.Body), + new("footer", "footerColour", (_, _) => ReportColours.Faint), + new("card", "cardColour", (_, s) => s), + new("table", "tableColour", (p, _) => p), + new("infographic", "infographicColour", (p, _) => p), + new("infographicBackground", "infographicBackgroundColour", (_, _) => "#000000"), + new("watermark", "watermarkColour", (p, _) => p), + }; + + private ReportTheme(BrandingInput b) + { + Primary = ColourMath.NormaliseHex(b.Colour) ?? ColourMath.DefaultBrandColour; + Secondary = ColourMath.NormaliseHex(b.SecondaryColour) ?? Primary; + OnPrimary = ColourMath.ReadableTextOn(Primary); + + var palette = new Dictionary(); + foreach (var role in Roles) + { + var configured = b.RoleColour(role.Setting); + palette[role.Key] = ColourMath.NormaliseHex(configured) ?? role.From(Primary, Secondary); + } + Palette = palette; + + OnTable = ColourMath.ReadableTextOn(palette["table"]); + OnInfographic = ColourMath.ReadableTextOn(palette["infographicBackground"]); + Series = BuildSeries(palette["chart"], palette["chartAccent"]); + + FooterShow = b.ShowFooter != false; + FooterEnabled = b.ShowFooter != false && !string.IsNullOrEmpty(b.FooterText); + FooterTemplate = b.FooterText ?? string.Empty; + ShowPageNumbers = b.ShowPageNumbers != false; + WatermarkEnabled = b.WatermarkEnabled != false && !string.IsNullOrEmpty(b.WatermarkText); + WatermarkText = b.WatermarkText ?? string.Empty; + CoverFooterText = b.CoverFooterText ?? string.Empty; + } + + public static ReportTheme Create(BrandingInput branding) => new(branding); + + private static List BuildSeries(string primary, string secondary) + { + if (primary == secondary) + { + return new List + { + primary, ColourMath.Lighten(primary, 0.35), ReportColours.Info, + ColourMath.Darken(primary, 0.3), ColourMath.Lighten(primary, 0.65) + }; + } + return new List + { + primary, secondary, ColourMath.Lighten(primary, 0.4), + ColourMath.Lighten(secondary, 0.4), ColourMath.Darken(primary, 0.25) + }; + } + + /// %variable% substitution - case-insensitive, unknown tokens left as written. + public static string ApplyVariables(string? template, IReadOnlyDictionary variables) + { + if (string.IsNullOrEmpty(template)) return string.Empty; + var lookup = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var kv in variables) lookup[kv.Key.Trim('%').ToLowerInvariant()] = kv.Value; + return Regex.Replace(template, "%([\\w()]+)%", m => + lookup.TryGetValue(m.Groups[1].Value.ToLowerInvariant(), out var v) ? v : m.Value); + } + + public static string ApplyFooter(string? template, IReadOnlyDictionary variables) + { + var s = ApplyVariables(template, variables); + return s.Length > FooterMaxLength ? s.Substring(0, FooterMaxLength) : s; + } + + public static string ApplyWatermark(string? template, IReadOnlyDictionary variables) + { + var s = ApplyVariables(template, variables); + return s.Length > WatermarkMaxLength ? s.Substring(0, WatermarkMaxLength) : s; + } + } +} diff --git a/backend/Shared/CIPPSharp/Reporting/TwemojiAssets.cs b/backend/Shared/CIPPSharp/Reporting/TwemojiAssets.cs new file mode 100644 index 0000000000..2246917fb3 --- /dev/null +++ b/backend/Shared/CIPPSharp/Reporting/TwemojiAssets.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; + +namespace CIPP.Reporting +{ + /// + /// Resolves an emoji grapheme cluster to its bundled Twemoji colour PNG. The full 72x72 Twemoji set + /// ships as individual files under twemoji/ beside the assembly (one small file per emoji, so a + /// render only touches the handful it embeds). The renderer places each as an inline colour image, the + /// way the client's react-pdf reports did - the standard PDF fonts and OfficeIMO's embedding are + /// monochrome-only, so this is the only route to colour emoji. Absent, emoji fall back to the bundled + /// monochrome font. + /// + internal static class TwemojiAssets + { + private static readonly Lazy Dir = new(() => + { + try + { + var d = Path.GetDirectoryName(typeof(TwemojiAssets).Assembly.Location); + if (string.IsNullOrEmpty(d)) return null; + var t = Path.Combine(d, "twemoji"); + return Directory.Exists(t) ? t : null; + } + catch { return null; } + }); + + // The set of available basenames (e.g. "26a0", "1f1fa-1f1f8"), read once, so a lookup is O(1) and + // never hits the disk for an emoji we don't ship. + private static readonly Lazy> Available = new(() => + { + var set = new HashSet(StringComparer.Ordinal); + var dir = Dir.Value; + if (dir is null) return set; + try { foreach (var f in Directory.EnumerateFiles(dir, "*.png")) set.Add(Path.GetFileNameWithoutExtension(f)); } + catch { } + return set; + }); + + // Bytes read on first use for an emoji and kept (a used emoji is at most ~5 KB; the whole set is + // ~4 MB), so a report referencing the same emoji many times reads the file once. + private static readonly ConcurrentDictionary Cache = new(StringComparer.Ordinal); + + public static bool Enabled => Dir.Value is not null && Available.Value.Count > 0; + + /// + /// The Twemoji basename for a grapheme cluster (its toCodePoint rule): drop the VS16 emoji-style + /// selector unless the cluster is a ZWJ sequence, then join the code points as lowercase hex with + /// '-'. Null if the cluster has no drawable code point. + /// + public static string? Key(string cluster) + { + if (string.IsNullOrEmpty(cluster)) return null; + var hasZwj = cluster.IndexOf('‍') >= 0; + var sb = new StringBuilder(); + var any = false; + for (var i = 0; i < cluster.Length;) + { + int cp; + if (char.IsHighSurrogate(cluster[i]) && i + 1 < cluster.Length && char.IsLowSurrogate(cluster[i + 1])) + { cp = char.ConvertToUtf32(cluster[i], cluster[i + 1]); i += 2; } + else { cp = cluster[i]; i++; } + if (!hasZwj && cp == 0xFE0F) continue; + if (any) sb.Append('-'); + sb.Append(cp.ToString("x", CultureInfo.InvariantCulture)); + any = true; + } + return any ? sb.ToString() : null; + } + + /// True when a bundled Twemoji PNG exists for this grapheme cluster. + public static bool Has(string cluster) + { + if (!Enabled) return false; + var k = Key(cluster); + return k is not null && Available.Value.Contains(k); + } + + /// The PNG bytes for a grapheme cluster, or null if none is bundled. + public static byte[]? Bytes(string cluster) + { + var k = Key(cluster); + if (k is null || !Available.Value.Contains(k)) return null; + return Cache.GetOrAdd(k, key => + { + try { return File.ReadAllBytes(Path.Combine(Dir.Value!, key + ".png")); } + catch { return null; } + }); + } + } +} diff --git a/backend/Shared/CIPPSharp/Reporting/emoji-fallback.build.md b/backend/Shared/CIPPSharp/Reporting/emoji-fallback.build.md new file mode 100644 index 0000000000..d1fa0f1cd3 --- /dev/null +++ b/backend/Shared/CIPPSharp/Reporting/emoji-fallback.build.md @@ -0,0 +1,80 @@ +# Emoji rendering: colour Twemoji images + a monochrome fallback font + +Emoji render in two tiers. Both are **committed assets, not build outputs** — the Dockerfile ships +them via `COPY backend/Shared` and never rebuilds them. + +1. **Colour (primary): the Twemoji PNG set in `bin/twemoji/`** (~4,000 individual 72x72 PNGs, ~4 MB, + named by lowercase-hex code point, e.g. `26a0.png`, `1f1fa-1f1f8.png`). [`TwemojiAssets`](TwemojiAssets.cs) + resolves an emoji grapheme cluster (incl. ZWJ sequences, flags, skin tones) to its PNG, and the + component kit places it as an inline colour image (`PdfParagraphBuilder.InlineImage` in flow text, + `PdfTextRun.Inline` in table cells). A render only reads the handful of files it embeds; OfficeIMO + dedupes so each distinct emoji embeds once (~1-1.5 KB in the PDF). This is how the old react-pdf + reports rendered emoji (Twemoji via `Font.registerEmojiSource`). Attribution: `bin/twemoji/ATTRIBUTION.txt` + (CC-BY 4.0). Refresh from https://github.com/jdecked/twemoji `assets/72x72/`. + + **Vector-drawing contexts render emoji monochrome, not colour**: the cover, hero and chart + title/labels are `OfficeDrawing`s (text drawn via a font, not inline images), and the running + footer's builder has no image API - so emoji there fall back to the font below. Colour in a drawing + would need manual per-glyph image layout (text measurement + positioning). Emoji are uncommon in + those branded/chrome contexts, so they are left monochrome. + +2. **Monochrome fallback: `bin/emoji-fallback.ttf`** (internal family **CippReportEmoji**, ~800 KB). + Used for any emoji the Twemoji set lacks, and in the drawing/footer contexts above. The standard PDF + fonts have no emoji glyphs, so [`ReportMarkdown.Sanitize`](ReportMarkdown.cs) keeps any emoji this + font can draw and OfficeIMO routes each such code point through it per character, while the + surrounding text stays in the standard font. The three report emoji (⚠ ✅ ℹ) are tinted to their + brand colour on this path; everything else renders in the surrounding text colour. Regenerate it only + to widen coverage or refresh the upstreams (below). + +## What it is + +A subset+merge of two redistributable upstreams (see `bin/emoji-fallback.LICENSE.txt`): + +| Glyphs | Source | Licence | +| --- | --- | --- | +| Latin-1 (`U+0020-00FF`) + CP1252 specials | DejaVu Sans | Bitstream Vera / Arev | +| Symbols & emoji, BMP + astral | Noto Emoji (monochrome) | SIL OFL 1.1 | + +The Latin-1 glyphs are there only so OfficeIMO's greedy "neighbour of an emoji" fallback never +lands on an uncovered character; ordinary text never routes to this font because those code points +are excluded from the declared fallback ranges (below). + +## Recipe (fonttools + Python) + +```bash +# Sources +# DejaVu Sans: https://dejavu-fonts.github.io/ (DejaVuSans.ttf, 2048 upem) +# Noto Emoji: https://github.com/google/fonts/raw/main/ofl/notoemoji/NotoEmoji%5Bwght%5D.ttf + +# 1. Instance the Noto Emoji variable font to a static regular weight (also 2048 upem) +python -c "from fontTools.ttLib import TTFont; from fontTools.varLib.instancer import instantiateVariableFont; \ + f=TTFont('NotoEmoji[wght].ttf'); instantiateVariableFont(f,{'wght':400},inplace=True); f.save('NotoEmoji-Static.ttf')" + +# 2. Subset each. DejaVu = Latin-1 + CP1252 specials; Noto = symbol/emoji ranges, EXCLUDING U+2122 (TM, +# which DejaVu supplies) so the two cmaps do not collide on merge. +python -m fontTools.subset DejaVuSans.ttf --output-file=dejavu-latin.ttf --no-hinting --desubroutinize \ + --unicodes="0020-00FF,20AC,201A,0192,201E,2026,2020,2021,02C6,2030,0160,2039,0152,017D,2018,2019,201C,201D,2022,2013,2014,02DC,2122,0161,203A,0153,017E,0178" +python -m fontTools.subset NotoEmoji-Static.ttf --output-file=noto-sub.ttf --no-hinting --desubroutinize \ + --unicodes="2100-2121,2123-27BF,2B00-2BFF,1F004,1F0CF,1F170-1F251,1F300-1F6FF,1F7E0-1F7EB,1F900-1F9FF,1FA70-1FAFF" + +# 3. Strip tables fontTools.merge cannot combine (Noto ships a MATH table), keeping only the essentials. +python -c "from fontTools.ttLib import TTFont; keep={'glyf','loca','cmap','head','hhea','hmtx','maxp','name','OS/2','post','gasp'}; \ + [ ( (lambda f:[ [f.__delitem__(t) for t in list(f.keys()) if t not in keep], f.save(fn)])(TTFont(fn)) ) for fn in ('dejavu-latin.ttf','noto-sub.ttf') ]" + +# 4. Merge and rename the family so no upstream reserved font name survives. +python -c "from fontTools import merge; f=merge.Merger().merge(['dejavu-latin.ttf','noto-sub.ttf']); \ + n=f['name']; [n.setName('CippReportEmoji',i,3,1,0x409) for i in (1,16)]; [n.setName('CippReportEmoji-Regular',i,3,1,0x409) for i in (4,6)]; \ + f.save('emoji-fallback.ttf')" +``` + +Both upstreams must share `unitsPerEm` (2048) or the merged glyphs mis-scale. Verify the result has a +Windows BMP `cmap` (platform 3, encoding 1, format 4) **and** a full `cmap` (3/10, format 12 — this is +what carries the astral emoji). + +## How the code stays in sync with the font + +Nothing hard-codes the code points. At render time [`FontCmap`](FontCmap.cs) reads this font's own +`cmap`; `ReportPdf` keeps the code points above `U+00FF` (minus the CP1252 specials) as both the +`Sanitize` keep-set and the OfficeIMO fallback ranges (coalesced to OfficeIMO's 128-range limit). So +widening coverage is just a re-subset here — no C# change. If you drop this file, emoji degrade to +ASCII tokens rather than throwing. diff --git a/backend/Shared/CIPPSharp/bin/CIPPSharp.dll b/backend/Shared/CIPPSharp/bin/CIPPSharp.dll index 4b71bbc8b7..8fddcf26e6 100644 Binary files a/backend/Shared/CIPPSharp/bin/CIPPSharp.dll and b/backend/Shared/CIPPSharp/bin/CIPPSharp.dll differ diff --git a/backend/Shared/CIPPSharp/bin/OfficeIMO.Core.dll b/backend/Shared/CIPPSharp/bin/OfficeIMO.Core.dll new file mode 100644 index 0000000000..305cf431fb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/OfficeIMO.Core.dll differ diff --git a/backend/Shared/CIPPSharp/bin/OfficeIMO.Pdf.dll b/backend/Shared/CIPPSharp/bin/OfficeIMO.Pdf.dll new file mode 100644 index 0000000000..6d414c3c54 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/OfficeIMO.Pdf.dll differ diff --git a/backend/Shared/CIPPSharp/bin/emoji-fallback.LICENSE.txt b/backend/Shared/CIPPSharp/bin/emoji-fallback.LICENSE.txt new file mode 100644 index 0000000000..4df1087b4f --- /dev/null +++ b/backend/Shared/CIPPSharp/bin/emoji-fallback.LICENSE.txt @@ -0,0 +1,305 @@ +emoji-fallback.ttf - third-party font notices +============================================= + +emoji-fallback.ttf (internal family name "CippReportEmoji") is a subsetted, merged +font used only as a monochrome symbol/emoji fallback by the CIPP server-side PDF +renderer (backend/Shared/CIPPSharp). It is built from two upstream fonts: + + * Latin-1 and CP1252 punctuation glyphs .... DejaVu Sans (Bitstream Vera / Arev) + * Symbol and emoji glyphs (BMP + astral) ... Noto Emoji (SIL Open Font License 1.1) + +Only the glyphs the reports may use are included; the family was renamed so it does +not carry any reserved font name from the upstream projects. The full upstream +licences follow. + + +================================================================================ +1. DejaVu Sans (Bitstream Vera Fonts Copyright / Arev Fonts Copyright) +================================================================================ + +Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. +Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) + + +Bitstream Vera Fonts Copyright +------------------------------ + +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is +a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the fonts accompanying this license ("Fonts") and associated +documentation files (the "Font Software"), to reproduce and distribute the +Font Software, including without limitation the rights to use, copy, merge, +publish, distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to the +following conditions: + +The above copyright and trademark notices and this permission notice shall +be included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular +the designs of glyphs or characters in the Fonts may be modified and +additional glyphs or characters may be added to the Fonts, only if the fonts +are renamed to names not containing either the words "Bitstream" or the word +"Vera". + +This License becomes null and void to the extent applicable to Fonts or Font +Software that has been modified and is distributed under the "Bitstream +Vera" names. + +The Font Software may be sold as part of a larger software package but no +copy of one or more of the Font Software typefaces may be sold by itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, +TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME +FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING +ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE +FONT SOFTWARE. + +Except as contained in this notice, the names of Gnome, the Gnome +Foundation, and Bitstream Inc., shall not be used in advertising or +otherwise to promote the sale, use or other dealings in this Font Software +without prior written authorization from the Gnome Foundation or Bitstream +Inc., respectively. For further information, contact: fonts at gnome dot +org. + +Arev Fonts Copyright +------------------------------ + +Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the fonts accompanying this license ("Fonts") and +associated documentation files (the "Font Software"), to reproduce +and distribute the modifications to the Bitstream Vera Font Software, +including without limitation the rights to use, copy, merge, publish, +distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to +the following conditions: + +The above copyright and trademark notices and this permission notice +shall be included in all copies of one or more of the Font Software +typefaces. + +The Font Software may be modified, altered, or added to, and in +particular the designs of glyphs or characters in the Fonts may be +modified and additional glyphs or characters may be added to the +Fonts, only if the fonts are renamed to names not containing either +the words "Tavmjong Bah" or the word "Arev". + +This License becomes null and void to the extent applicable to Fonts +or Font Software that has been modified and is distributed under the +"Tavmjong Bah Arev" names. + +The Font Software may be sold as part of a larger software package but +no copy of one or more of the Font Software typefaces may be sold by +itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL +TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +Except as contained in this notice, the name of Tavmjong Bah shall not +be used in advertising or otherwise to promote the sale, use or other +dealings in this Font Software without prior written authorization +from Tavmjong Bah. For further information, contact: tavmjong @ free +. fr. + +TeX Gyre DJV Math +----------------- +Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. + +Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski +(on behalf of TeX users groups) are in public domain. + +Letters imported from Euler Fraktur from AMSfonts are (c) American +Mathematical Society (see below). +Bitstream Vera Fonts Copyright +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera +is a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the fonts accompanying this license (“Fonts”) and associated +documentation +files (the “Font Software”), to reproduce and distribute the Font Software, +including without limitation the rights to use, copy, merge, publish, +distribute, +and/or sell copies of the Font Software, and to permit persons to whom +the Font Software is furnished to do so, subject to the following +conditions: + +The above copyright and trademark notices and this permission notice +shall be +included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular +the designs of glyphs or characters in the Fonts may be modified and +additional +glyphs or characters may be added to the Fonts, only if the fonts are +renamed +to names not containing either the words “Bitstream” or the word “Vera”. + +This License becomes null and void to the extent applicable to Fonts or +Font Software +that has been modified and is distributed under the “Bitstream Vera” +names. + +The Font Software may be sold as part of a larger software package but +no copy +of one or more of the Font Software typefaces may be sold by itself. + +THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, +TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME +FOUNDATION +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, +SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN +ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR +INABILITY TO USE +THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. +Except as contained in this notice, the names of GNOME, the GNOME +Foundation, +and Bitstream Inc., shall not be used in advertising or otherwise to promote +the sale, use or other dealings in this Font Software without prior written +authorization from the GNOME Foundation or Bitstream Inc., respectively. +For further information, contact: fonts at gnome dot org. + +AMSFonts (v. 2.2) copyright + +The PostScript Type 1 implementation of the AMSFonts produced by and +previously distributed by Blue Sky Research and Y&Y, Inc. are now freely +available for general use. This has been accomplished through the +cooperation +of a consortium of scientific publishers with Blue Sky Research and Y&Y. +Members of this consortium include: + +Elsevier Science IBM Corporation Society for Industrial and Applied +Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS) + +In order to assure the authenticity of these fonts, copyright will be +held by +the American Mathematical Society. This is not meant to restrict in any way +the legitimate use of the fonts, such as (but not limited to) electronic +distribution of documents containing these fonts, inclusion of these fonts +into other public domain or commercial font collections or computer +applications, use of the outline data to create derivative fonts and/or +faces, etc. However, the AMS does require that the AMS copyright notice be +removed from any derivative versions of the fonts which have been altered in +any way. In addition, to ensure the fidelity of TeX documents using Computer +Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces, +has requested that any alterations which yield different font metrics be +given a different name. + +$Id$ + + +================================================================================ +2. Noto Emoji (SIL Open Font License, Version 1.1) +================================================================================ + +Copyright 2013 Google LLC + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/backend/Shared/CIPPSharp/bin/emoji-fallback.ttf b/backend/Shared/CIPPSharp/bin/emoji-fallback.ttf new file mode 100644 index 0000000000..6df5404593 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/emoji-fallback.ttf differ diff --git a/backend/Shared/CIPPSharp/bin/reportImages/board.jpg b/backend/Shared/CIPPSharp/bin/reportImages/board.jpg new file mode 100644 index 0000000000..7acaef3b6c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/reportImages/board.jpg differ diff --git a/backend/Shared/CIPPSharp/bin/reportImages/city.jpg b/backend/Shared/CIPPSharp/bin/reportImages/city.jpg new file mode 100644 index 0000000000..dd3ba7434c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/reportImages/city.jpg differ diff --git a/backend/Shared/CIPPSharp/bin/reportImages/glasses.jpg b/backend/Shared/CIPPSharp/bin/reportImages/glasses.jpg new file mode 100644 index 0000000000..4da25c27e6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/reportImages/glasses.jpg differ diff --git a/backend/Shared/CIPPSharp/bin/reportImages/laptop.jpg b/backend/Shared/CIPPSharp/bin/reportImages/laptop.jpg new file mode 100644 index 0000000000..5d8579553a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/reportImages/laptop.jpg differ diff --git a/backend/Shared/CIPPSharp/bin/reportImages/soc.jpg b/backend/Shared/CIPPSharp/bin/reportImages/soc.jpg new file mode 100644 index 0000000000..f3e83f78e1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/reportImages/soc.jpg differ diff --git a/backend/Shared/CIPPSharp/bin/reportImages/working.jpg b/backend/Shared/CIPPSharp/bin/reportImages/working.jpg new file mode 100644 index 0000000000..fdc1903dfc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/reportImages/working.jpg differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f004.png b/backend/Shared/CIPPSharp/bin/twemoji/1f004.png new file mode 100644 index 0000000000..93c0fa92ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f004.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f0cf.png b/backend/Shared/CIPPSharp/bin/twemoji/1f0cf.png new file mode 100644 index 0000000000..5530110afe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f0cf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f170.png b/backend/Shared/CIPPSharp/bin/twemoji/1f170.png new file mode 100644 index 0000000000..8944eac4ff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f170.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f171.png b/backend/Shared/CIPPSharp/bin/twemoji/1f171.png new file mode 100644 index 0000000000..6662ffefc4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f171.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f17e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f17e.png new file mode 100644 index 0000000000..7d38be20cc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f17e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f17f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f17f.png new file mode 100644 index 0000000000..afa850c900 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f17f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f18e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f18e.png new file mode 100644 index 0000000000..0c6879c699 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f18e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f191.png b/backend/Shared/CIPPSharp/bin/twemoji/1f191.png new file mode 100644 index 0000000000..d4cb3afc63 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f191.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f192.png b/backend/Shared/CIPPSharp/bin/twemoji/1f192.png new file mode 100644 index 0000000000..d1920bcc46 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f192.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f193.png b/backend/Shared/CIPPSharp/bin/twemoji/1f193.png new file mode 100644 index 0000000000..20c351632c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f193.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f194.png b/backend/Shared/CIPPSharp/bin/twemoji/1f194.png new file mode 100644 index 0000000000..d2a9c0ac69 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f194.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f195.png b/backend/Shared/CIPPSharp/bin/twemoji/1f195.png new file mode 100644 index 0000000000..9c0693701c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f195.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f196.png b/backend/Shared/CIPPSharp/bin/twemoji/1f196.png new file mode 100644 index 0000000000..1676a2b190 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f196.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f197.png b/backend/Shared/CIPPSharp/bin/twemoji/1f197.png new file mode 100644 index 0000000000..90763be704 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f197.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f198.png b/backend/Shared/CIPPSharp/bin/twemoji/1f198.png new file mode 100644 index 0000000000..2dd0eb0440 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f198.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f199.png b/backend/Shared/CIPPSharp/bin/twemoji/1f199.png new file mode 100644 index 0000000000..177ee60634 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f199.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f19a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f19a.png new file mode 100644 index 0000000000..9f26ffe7f7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f19a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1e8.png new file mode 100644 index 0000000000..8e7a630b92 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1e9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1e9.png new file mode 100644 index 0000000000..69972dfe8c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1ea.png new file mode 100644 index 0000000000..11d2bb6e9f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1eb.png new file mode 100644 index 0000000000..7a37ec27e7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1ec.png new file mode 100644 index 0000000000..8602fff488 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1ee.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1ee.png new file mode 100644 index 0000000000..73f0c20398 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1ee.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f1.png new file mode 100644 index 0000000000..a6d45735f3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f2.png new file mode 100644 index 0000000000..16ef38ae5f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f4.png new file mode 100644 index 0000000000..934952555e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f6.png new file mode 100644 index 0000000000..d21e4bafd2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f7.png new file mode 100644 index 0000000000..a7d8a31dd1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f8.png new file mode 100644 index 0000000000..a5cdfccbcd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f9.png new file mode 100644 index 0000000000..3937efe840 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1fa.png new file mode 100644 index 0000000000..84a51806bf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1fc.png new file mode 100644 index 0000000000..e250be924b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1fd.png new file mode 100644 index 0000000000..a5444fc65e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1ff.png new file mode 100644 index 0000000000..4f0c1c8678 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6-1f1ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6.png new file mode 100644 index 0000000000..a030394274 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1e6.png new file mode 100644 index 0000000000..3ffe114d94 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1e7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1e7.png new file mode 100644 index 0000000000..e6fb3edddb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1e7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1e9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1e9.png new file mode 100644 index 0000000000..c1c933e092 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ea.png new file mode 100644 index 0000000000..93eaaff692 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1eb.png new file mode 100644 index 0000000000..29064ff376 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ec.png new file mode 100644 index 0000000000..0519f963c5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ed.png new file mode 100644 index 0000000000..28c3ec6f69 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ee.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ee.png new file mode 100644 index 0000000000..7133116525 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ee.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ef.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ef.png new file mode 100644 index 0000000000..3ac1d995d1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ef.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f1.png new file mode 100644 index 0000000000..aa9bb0689f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f2.png new file mode 100644 index 0000000000..13531297f2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f3.png new file mode 100644 index 0000000000..f861221064 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f4.png new file mode 100644 index 0000000000..21c555bdd6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f6.png new file mode 100644 index 0000000000..be922b0c12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f7.png new file mode 100644 index 0000000000..642f3a520d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f8.png new file mode 100644 index 0000000000..f959101b26 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f9.png new file mode 100644 index 0000000000..ee668fb7da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1fb.png new file mode 100644 index 0000000000..d3a19fdf81 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1fc.png new file mode 100644 index 0000000000..4aeeb130f2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1fe.png new file mode 100644 index 0000000000..fc2051bf7e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ff.png new file mode 100644 index 0000000000..0c23f8075a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7-1f1ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7.png new file mode 100644 index 0000000000..2322e67044 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1e6.png new file mode 100644 index 0000000000..fe777da3f5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1e8.png new file mode 100644 index 0000000000..f232d71578 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1e9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1e9.png new file mode 100644 index 0000000000..f0855b2738 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1eb.png new file mode 100644 index 0000000000..18a6891661 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1ec.png new file mode 100644 index 0000000000..113face155 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1ed.png new file mode 100644 index 0000000000..bc50b99e54 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1ee.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1ee.png new file mode 100644 index 0000000000..e19e84b00d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1ee.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f0.png new file mode 100644 index 0000000000..e5ca391482 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f1.png new file mode 100644 index 0000000000..d4d91d0655 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f2.png new file mode 100644 index 0000000000..457a061631 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f3.png new file mode 100644 index 0000000000..dcc0fb1aea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f4.png new file mode 100644 index 0000000000..0d2cf35564 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f5.png new file mode 100644 index 0000000000..662adc7b32 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f6.png new file mode 100644 index 0000000000..5626a9fa37 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f7.png new file mode 100644 index 0000000000..69b41acd29 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1fa.png new file mode 100644 index 0000000000..00e3299d6d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1fb.png new file mode 100644 index 0000000000..88ac363003 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1fc.png new file mode 100644 index 0000000000..83a5a7d226 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1fd.png new file mode 100644 index 0000000000..b6f3608554 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1fe.png new file mode 100644 index 0000000000..5c333b0781 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1ff.png new file mode 100644 index 0000000000..ecad9fdbb5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8-1f1ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8.png new file mode 100644 index 0000000000..1683e3206e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1ea.png new file mode 100644 index 0000000000..2933ab89e3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1ec.png new file mode 100644 index 0000000000..aee82bade8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1ef.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1ef.png new file mode 100644 index 0000000000..24602c11d1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1ef.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1f0.png new file mode 100644 index 0000000000..095ff86cae Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1f2.png new file mode 100644 index 0000000000..05f78c9647 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1f4.png new file mode 100644 index 0000000000..4cb2f4ad23 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1ff.png new file mode 100644 index 0000000000..8bdb882bd9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9-1f1ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1e9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9.png new file mode 100644 index 0000000000..641d1641ee Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1e6.png new file mode 100644 index 0000000000..fddf21a1a3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1e8.png new file mode 100644 index 0000000000..05b0a2ace0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1ea.png new file mode 100644 index 0000000000..94d8d46443 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1ec.png new file mode 100644 index 0000000000..29dfd5bad8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1ed.png new file mode 100644 index 0000000000..29d07917d0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1f7.png new file mode 100644 index 0000000000..fd25d0e349 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1f8.png new file mode 100644 index 0000000000..5c449da194 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1f9.png new file mode 100644 index 0000000000..4f4e7fc249 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1fa.png new file mode 100644 index 0000000000..4f0b5d7f4f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea-1f1fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea.png new file mode 100644 index 0000000000..cb6286fcc4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1ee.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1ee.png new file mode 100644 index 0000000000..34096f2ca1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1ee.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1ef.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1ef.png new file mode 100644 index 0000000000..3f4c0e0624 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1ef.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1f0.png new file mode 100644 index 0000000000..5b2ecff4e4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1f2.png new file mode 100644 index 0000000000..0fc25e88f9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1f4.png new file mode 100644 index 0000000000..7f8e443512 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1f7.png new file mode 100644 index 0000000000..662adc7b32 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1eb-1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1eb.png new file mode 100644 index 0000000000..6d033081a5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1e6.png new file mode 100644 index 0000000000..6f73a3793b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1e7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1e7.png new file mode 100644 index 0000000000..af56765726 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1e7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1e9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1e9.png new file mode 100644 index 0000000000..66f160e0e7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1ea.png new file mode 100644 index 0000000000..31af12832b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1eb.png new file mode 100644 index 0000000000..99ff31b2f4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1ec.png new file mode 100644 index 0000000000..72ac329c84 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1ed.png new file mode 100644 index 0000000000..2b19dbdad4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1ee.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1ee.png new file mode 100644 index 0000000000..26d4ef755e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1ee.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f1.png new file mode 100644 index 0000000000..5df66b33ec Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f2.png new file mode 100644 index 0000000000..4d1a3b4c12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f3.png new file mode 100644 index 0000000000..813c3caa1f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f5.png new file mode 100644 index 0000000000..97a3dd9be7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f6.png new file mode 100644 index 0000000000..02b7aec5fd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f7.png new file mode 100644 index 0000000000..0478442a41 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f8.png new file mode 100644 index 0000000000..da4bbff9c6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f9.png new file mode 100644 index 0000000000..23cec33928 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1fa.png new file mode 100644 index 0000000000..75c0945cfe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1fc.png new file mode 100644 index 0000000000..cf8c9416c1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1fe.png new file mode 100644 index 0000000000..2e102fdc8e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec-1f1fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec.png new file mode 100644 index 0000000000..f9f35b9928 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1f0.png new file mode 100644 index 0000000000..412b6841f8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1f2.png new file mode 100644 index 0000000000..9972434a17 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1f3.png new file mode 100644 index 0000000000..e1617778cb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1f7.png new file mode 100644 index 0000000000..2dafc9a092 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1f9.png new file mode 100644 index 0000000000..afbf0287b6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1fa.png new file mode 100644 index 0000000000..b2f43f77d8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ed-1f1fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ed.png new file mode 100644 index 0000000000..519a4136d3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1e8.png new file mode 100644 index 0000000000..44cb511f1b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1e9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1e9.png new file mode 100644 index 0000000000..4e8df64737 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1ea.png new file mode 100644 index 0000000000..bbbeada96f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f1.png new file mode 100644 index 0000000000..6c390c3fe6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f2.png new file mode 100644 index 0000000000..99304fca91 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f3.png new file mode 100644 index 0000000000..22d2bdfded Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f4.png new file mode 100644 index 0000000000..7ef54ae2ad Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f6.png new file mode 100644 index 0000000000..81de697331 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f7.png new file mode 100644 index 0000000000..5147905ec9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f8.png new file mode 100644 index 0000000000..349e9efab5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f9.png new file mode 100644 index 0000000000..2cd1da785e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee-1f1f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ee.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee.png new file mode 100644 index 0000000000..4a50ebe907 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ee.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ef-1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ef-1f1ea.png new file mode 100644 index 0000000000..bc974c1272 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ef-1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ef-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ef-1f1f2.png new file mode 100644 index 0000000000..2a48a73a5e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ef-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ef-1f1f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ef-1f1f4.png new file mode 100644 index 0000000000..5161701b24 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ef-1f1f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ef-1f1f5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ef-1f1f5.png new file mode 100644 index 0000000000..dd515aa063 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ef-1f1f5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ef.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ef.png new file mode 100644 index 0000000000..f52052fadb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ef.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1ea.png new file mode 100644 index 0000000000..468ab957ac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1ec.png new file mode 100644 index 0000000000..58f07a7ba6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1ed.png new file mode 100644 index 0000000000..cf9c1a6649 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1ee.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1ee.png new file mode 100644 index 0000000000..ee10d01349 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1ee.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1f2.png new file mode 100644 index 0000000000..1035f149a6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1f3.png new file mode 100644 index 0000000000..e021417b87 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1f5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1f5.png new file mode 100644 index 0000000000..7e05f75fc6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1f5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1f7.png new file mode 100644 index 0000000000..8ff7ccd3ff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1fc.png new file mode 100644 index 0000000000..76f53c2f7f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1fe.png new file mode 100644 index 0000000000..158d5899b0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1ff.png new file mode 100644 index 0000000000..d7e3401def Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0-1f1ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0.png new file mode 100644 index 0000000000..00a31cbaed Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1e6.png new file mode 100644 index 0000000000..44f5e8da70 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1e7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1e7.png new file mode 100644 index 0000000000..f506228dd6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1e7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1e8.png new file mode 100644 index 0000000000..1b7e53a859 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1ee.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1ee.png new file mode 100644 index 0000000000..9a69e9a048 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1ee.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1f0.png new file mode 100644 index 0000000000..8f8d0b3aea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1f7.png new file mode 100644 index 0000000000..8ed6a5a877 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1f8.png new file mode 100644 index 0000000000..051b53ab12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1f9.png new file mode 100644 index 0000000000..69de64e8b1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1fa.png new file mode 100644 index 0000000000..d7a507d075 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1fb.png new file mode 100644 index 0000000000..5505550928 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1fe.png new file mode 100644 index 0000000000..426e9319db Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1-1f1fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1.png new file mode 100644 index 0000000000..50ba60466f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1e6.png new file mode 100644 index 0000000000..49d5f2f712 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1e8.png new file mode 100644 index 0000000000..cc71565d44 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1e9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1e9.png new file mode 100644 index 0000000000..72ab452b4c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1ea.png new file mode 100644 index 0000000000..ed5d0c4198 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1eb.png new file mode 100644 index 0000000000..662adc7b32 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1ec.png new file mode 100644 index 0000000000..460babaa5d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1ed.png new file mode 100644 index 0000000000..6b69a8d551 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f0.png new file mode 100644 index 0000000000..9db397c936 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f1.png new file mode 100644 index 0000000000..d7db7397c1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f2.png new file mode 100644 index 0000000000..d4b642c327 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f3.png new file mode 100644 index 0000000000..7f2225c80b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f4.png new file mode 100644 index 0000000000..2fe5c8cb83 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f5.png new file mode 100644 index 0000000000..c9d6d57f64 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f6.png new file mode 100644 index 0000000000..aa46f114c2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f7.png new file mode 100644 index 0000000000..e9767976cf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f8.png new file mode 100644 index 0000000000..fd6b759a34 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f9.png new file mode 100644 index 0000000000..9265b06e1a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1fa.png new file mode 100644 index 0000000000..b48e01b4bb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1fb.png new file mode 100644 index 0000000000..c666d7ae6d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1fc.png new file mode 100644 index 0000000000..e58fe389c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1fd.png new file mode 100644 index 0000000000..61775bf729 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1fe.png new file mode 100644 index 0000000000..ee84ec4d6c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1ff.png new file mode 100644 index 0000000000..1a33e0c69e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2-1f1ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2.png new file mode 100644 index 0000000000..2539dab475 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1e6.png new file mode 100644 index 0000000000..860139dede Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1e8.png new file mode 100644 index 0000000000..81fa763884 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1ea.png new file mode 100644 index 0000000000..1f1dc36221 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1eb.png new file mode 100644 index 0000000000..599da68e6b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1ec.png new file mode 100644 index 0000000000..6cbb424d3d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1ee.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1ee.png new file mode 100644 index 0000000000..3f14038212 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1ee.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1f1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1f1.png new file mode 100644 index 0000000000..619bf8ce11 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1f1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1f4.png new file mode 100644 index 0000000000..d75873518c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1f5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1f5.png new file mode 100644 index 0000000000..48acf5f2b7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1f5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1f7.png new file mode 100644 index 0000000000..1748da9cab Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1fa.png new file mode 100644 index 0000000000..9bd5ec1ada Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1ff.png new file mode 100644 index 0000000000..0d193b3269 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3-1f1ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3.png new file mode 100644 index 0000000000..11a883c397 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f4-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f4-1f1f2.png new file mode 100644 index 0000000000..41176aa0de Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f4-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f4.png new file mode 100644 index 0000000000..71ff5a54b2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1e6.png new file mode 100644 index 0000000000..617422984c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1ea.png new file mode 100644 index 0000000000..f7e54091a9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1eb.png new file mode 100644 index 0000000000..f89b755744 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1ec.png new file mode 100644 index 0000000000..d75762c30a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1ed.png new file mode 100644 index 0000000000..a0adbef2cf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f0.png new file mode 100644 index 0000000000..311ca9a14a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f1.png new file mode 100644 index 0000000000..8d6921db8d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f2.png new file mode 100644 index 0000000000..357a863146 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f3.png new file mode 100644 index 0000000000..a7ced0f12c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f7.png new file mode 100644 index 0000000000..867df29d20 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f8.png new file mode 100644 index 0000000000..9756c0427d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f9.png new file mode 100644 index 0000000000..c359291bd2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1fc.png new file mode 100644 index 0000000000..8a7aea6a9d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1fe.png new file mode 100644 index 0000000000..8bc9da32bd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5-1f1fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5.png new file mode 100644 index 0000000000..70f7afe0a6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f6-1f1e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f6-1f1e6.png new file mode 100644 index 0000000000..363099051c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f6-1f1e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f6.png new file mode 100644 index 0000000000..2628efd225 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f7-1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f7-1f1ea.png new file mode 100644 index 0000000000..8010d88dd7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f7-1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f7-1f1f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f7-1f1f4.png new file mode 100644 index 0000000000..ce8f8e86bc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f7-1f1f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f7-1f1f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f7-1f1f8.png new file mode 100644 index 0000000000..0485b8639a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f7-1f1f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f7-1f1fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f7-1f1fa.png new file mode 100644 index 0000000000..9af1cb845e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f7-1f1fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f7-1f1fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f7-1f1fc.png new file mode 100644 index 0000000000..2802d1a2d7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f7-1f1fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f7.png new file mode 100644 index 0000000000..ecb868c0a1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1e6.png new file mode 100644 index 0000000000..5f90f43d4b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1e7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1e7.png new file mode 100644 index 0000000000..385dbaae36 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1e7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1e8.png new file mode 100644 index 0000000000..908f6b88e6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1e9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1e9.png new file mode 100644 index 0000000000..5ef2fd9a6e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ea.png new file mode 100644 index 0000000000..2cf2258713 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ec.png new file mode 100644 index 0000000000..454b82a079 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ed.png new file mode 100644 index 0000000000..2d0df5102e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ee.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ee.png new file mode 100644 index 0000000000..86bffaf313 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ee.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ef.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ef.png new file mode 100644 index 0000000000..d75873518c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ef.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f0.png new file mode 100644 index 0000000000..276c387e03 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f1.png new file mode 100644 index 0000000000..de88763d0d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f2.png new file mode 100644 index 0000000000..ddecba8b9e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f3.png new file mode 100644 index 0000000000..843bda4187 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f4.png new file mode 100644 index 0000000000..a4c573da18 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f7.png new file mode 100644 index 0000000000..77cd1d3bec Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f8.png new file mode 100644 index 0000000000..9cbf721edb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f9.png new file mode 100644 index 0000000000..f209bcde4d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1fb.png new file mode 100644 index 0000000000..333063f39e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1fd.png new file mode 100644 index 0000000000..72401f5fcd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1fe.png new file mode 100644 index 0000000000..877225169f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ff.png new file mode 100644 index 0000000000..0cf3fb1d73 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8-1f1ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8.png new file mode 100644 index 0000000000..7a38fb9fc0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1e6.png new file mode 100644 index 0000000000..15e4c3e159 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1e8.png new file mode 100644 index 0000000000..d3da08b9c7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1e9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1e9.png new file mode 100644 index 0000000000..77184f4d06 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1eb.png new file mode 100644 index 0000000000..2c17f5e70a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1ec.png new file mode 100644 index 0000000000..d9b238aa18 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1ed.png new file mode 100644 index 0000000000..902f55caba Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1ef.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1ef.png new file mode 100644 index 0000000000..2fc5e4e9b5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1ef.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f0.png new file mode 100644 index 0000000000..122903dfe2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f1.png new file mode 100644 index 0000000000..2f69d8d3f7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f2.png new file mode 100644 index 0000000000..8e6fbb11c5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f3.png new file mode 100644 index 0000000000..0838b9330d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f4.png new file mode 100644 index 0000000000..6a680b1171 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f7.png new file mode 100644 index 0000000000..a80ec23d64 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f9.png new file mode 100644 index 0000000000..7052a392b7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1fb.png new file mode 100644 index 0000000000..44880afa7b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1fc.png new file mode 100644 index 0000000000..d5d14aed27 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1ff.png new file mode 100644 index 0000000000..87c990d625 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9-1f1ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9.png new file mode 100644 index 0000000000..a3faa0db6d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1e6.png new file mode 100644 index 0000000000..81db6c222e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1ec.png new file mode 100644 index 0000000000..f560449896 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1f2.png new file mode 100644 index 0000000000..6d18cb7088 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1f3.png new file mode 100644 index 0000000000..ea7fc4c7d7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1f8.png new file mode 100644 index 0000000000..0ba064250e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1fe.png new file mode 100644 index 0000000000..aa981ca381 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1ff.png new file mode 100644 index 0000000000..ae196dd459 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa-1f1ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa.png new file mode 100644 index 0000000000..95651c65e4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1e6.png new file mode 100644 index 0000000000..f0fe36f9ad Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1e8.png new file mode 100644 index 0000000000..234bf65ada Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1ea.png new file mode 100644 index 0000000000..70a249fec6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1ec.png new file mode 100644 index 0000000000..72017181c9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1ee.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1ee.png new file mode 100644 index 0000000000..0feb6aebed Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1ee.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1f3.png new file mode 100644 index 0000000000..5282bd4343 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1fa.png new file mode 100644 index 0000000000..6047a7bf46 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb-1f1fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb.png new file mode 100644 index 0000000000..f431931f67 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fc-1f1eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fc-1f1eb.png new file mode 100644 index 0000000000..15746dad01 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fc-1f1eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fc-1f1f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fc-1f1f8.png new file mode 100644 index 0000000000..5356f6d468 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fc-1f1f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fc.png new file mode 100644 index 0000000000..b9c56ae2ef Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fd-1f1f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fd-1f1f0.png new file mode 100644 index 0000000000..043e9b514d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fd-1f1f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fd.png new file mode 100644 index 0000000000..328a53365f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fe-1f1ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fe-1f1ea.png new file mode 100644 index 0000000000..e858bb2c19 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fe-1f1ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fe-1f1f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fe-1f1f9.png new file mode 100644 index 0000000000..642b133c08 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fe-1f1f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1fe.png new file mode 100644 index 0000000000..a1518ab3fa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ff-1f1e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ff-1f1e6.png new file mode 100644 index 0000000000..4535007787 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ff-1f1e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ff-1f1f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ff-1f1f2.png new file mode 100644 index 0000000000..6283325081 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ff-1f1f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ff-1f1fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ff-1f1fc.png new file mode 100644 index 0000000000..d87fd6ac15 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ff-1f1fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f1ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f1ff.png new file mode 100644 index 0000000000..090d1bc408 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f1ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f201.png b/backend/Shared/CIPPSharp/bin/twemoji/1f201.png new file mode 100644 index 0000000000..31952eaf9e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f201.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f202.png b/backend/Shared/CIPPSharp/bin/twemoji/1f202.png new file mode 100644 index 0000000000..d12881eda2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f202.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f21a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f21a.png new file mode 100644 index 0000000000..671699d6fe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f21a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f22f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f22f.png new file mode 100644 index 0000000000..b07ad0826a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f22f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f232.png b/backend/Shared/CIPPSharp/bin/twemoji/1f232.png new file mode 100644 index 0000000000..37ad941cf4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f232.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f233.png b/backend/Shared/CIPPSharp/bin/twemoji/1f233.png new file mode 100644 index 0000000000..9dc7c35f14 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f233.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f234.png b/backend/Shared/CIPPSharp/bin/twemoji/1f234.png new file mode 100644 index 0000000000..5217963f44 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f234.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f235.png b/backend/Shared/CIPPSharp/bin/twemoji/1f235.png new file mode 100644 index 0000000000..2ef4e02c81 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f235.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f236.png b/backend/Shared/CIPPSharp/bin/twemoji/1f236.png new file mode 100644 index 0000000000..503ca0f311 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f236.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f237.png b/backend/Shared/CIPPSharp/bin/twemoji/1f237.png new file mode 100644 index 0000000000..a8a13729c4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f237.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f238.png b/backend/Shared/CIPPSharp/bin/twemoji/1f238.png new file mode 100644 index 0000000000..c85c702091 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f238.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f239.png b/backend/Shared/CIPPSharp/bin/twemoji/1f239.png new file mode 100644 index 0000000000..338aded4e9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f239.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f23a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f23a.png new file mode 100644 index 0000000000..de3f1aa908 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f23a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f250.png b/backend/Shared/CIPPSharp/bin/twemoji/1f250.png new file mode 100644 index 0000000000..be414e9e1c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f250.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f251.png b/backend/Shared/CIPPSharp/bin/twemoji/1f251.png new file mode 100644 index 0000000000..bc0802fcc4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f251.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f300.png b/backend/Shared/CIPPSharp/bin/twemoji/1f300.png new file mode 100644 index 0000000000..12726261ee Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f300.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f301.png b/backend/Shared/CIPPSharp/bin/twemoji/1f301.png new file mode 100644 index 0000000000..aef27ad589 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f301.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f302.png b/backend/Shared/CIPPSharp/bin/twemoji/1f302.png new file mode 100644 index 0000000000..803de0a38c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f302.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f303.png b/backend/Shared/CIPPSharp/bin/twemoji/1f303.png new file mode 100644 index 0000000000..8eb0c12b66 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f303.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f304.png b/backend/Shared/CIPPSharp/bin/twemoji/1f304.png new file mode 100644 index 0000000000..ab061858b8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f304.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f305.png b/backend/Shared/CIPPSharp/bin/twemoji/1f305.png new file mode 100644 index 0000000000..eda7cbee49 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f305.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f306.png b/backend/Shared/CIPPSharp/bin/twemoji/1f306.png new file mode 100644 index 0000000000..768e2d22d9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f306.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f307.png b/backend/Shared/CIPPSharp/bin/twemoji/1f307.png new file mode 100644 index 0000000000..f0cc2745d8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f307.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f308.png b/backend/Shared/CIPPSharp/bin/twemoji/1f308.png new file mode 100644 index 0000000000..9952a393eb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f308.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f309.png b/backend/Shared/CIPPSharp/bin/twemoji/1f309.png new file mode 100644 index 0000000000..8e1d3a50c4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f309.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f30a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f30a.png new file mode 100644 index 0000000000..f29f044ed5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f30a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f30b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f30b.png new file mode 100644 index 0000000000..4c220af1d8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f30b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f30c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f30c.png new file mode 100644 index 0000000000..9c3f134ad6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f30c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f30d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f30d.png new file mode 100644 index 0000000000..b5f62c3310 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f30d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f30e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f30e.png new file mode 100644 index 0000000000..d74ff449d5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f30e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f30f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f30f.png new file mode 100644 index 0000000000..66c4bed3c3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f30f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f310.png b/backend/Shared/CIPPSharp/bin/twemoji/1f310.png new file mode 100644 index 0000000000..faf027d8d1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f310.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f311.png b/backend/Shared/CIPPSharp/bin/twemoji/1f311.png new file mode 100644 index 0000000000..b4be1870e0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f311.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f312.png b/backend/Shared/CIPPSharp/bin/twemoji/1f312.png new file mode 100644 index 0000000000..1f2d7a1df1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f312.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f313.png b/backend/Shared/CIPPSharp/bin/twemoji/1f313.png new file mode 100644 index 0000000000..2e4ee20bb3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f313.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f314.png b/backend/Shared/CIPPSharp/bin/twemoji/1f314.png new file mode 100644 index 0000000000..63dda718ae Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f314.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f315.png b/backend/Shared/CIPPSharp/bin/twemoji/1f315.png new file mode 100644 index 0000000000..b7aea1a030 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f315.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f316.png b/backend/Shared/CIPPSharp/bin/twemoji/1f316.png new file mode 100644 index 0000000000..3d08d8dba7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f316.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f317.png b/backend/Shared/CIPPSharp/bin/twemoji/1f317.png new file mode 100644 index 0000000000..fb799d0353 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f317.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f318.png b/backend/Shared/CIPPSharp/bin/twemoji/1f318.png new file mode 100644 index 0000000000..d16ee12ac2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f318.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f319.png b/backend/Shared/CIPPSharp/bin/twemoji/1f319.png new file mode 100644 index 0000000000..51c101ff96 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f319.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f31a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f31a.png new file mode 100644 index 0000000000..e5857351e5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f31a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f31b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f31b.png new file mode 100644 index 0000000000..2d974fda64 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f31b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f31c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f31c.png new file mode 100644 index 0000000000..9538a024c2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f31c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f31d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f31d.png new file mode 100644 index 0000000000..02ceca4380 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f31d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f31e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f31e.png new file mode 100644 index 0000000000..4a928c085e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f31e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f31f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f31f.png new file mode 100644 index 0000000000..3d5fce4a43 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f31f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f320.png b/backend/Shared/CIPPSharp/bin/twemoji/1f320.png new file mode 100644 index 0000000000..7a97429ff7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f320.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f321.png b/backend/Shared/CIPPSharp/bin/twemoji/1f321.png new file mode 100644 index 0000000000..ea557a7fd0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f321.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f324.png b/backend/Shared/CIPPSharp/bin/twemoji/1f324.png new file mode 100644 index 0000000000..61c53ff910 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f324.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f325.png b/backend/Shared/CIPPSharp/bin/twemoji/1f325.png new file mode 100644 index 0000000000..93a39d5083 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f325.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f326.png b/backend/Shared/CIPPSharp/bin/twemoji/1f326.png new file mode 100644 index 0000000000..e0ebf7437d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f326.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f327.png b/backend/Shared/CIPPSharp/bin/twemoji/1f327.png new file mode 100644 index 0000000000..950061b359 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f327.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f328.png b/backend/Shared/CIPPSharp/bin/twemoji/1f328.png new file mode 100644 index 0000000000..51f7b80cc7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f328.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f329.png b/backend/Shared/CIPPSharp/bin/twemoji/1f329.png new file mode 100644 index 0000000000..0d6a16b344 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f329.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f32a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f32a.png new file mode 100644 index 0000000000..1f304d4d11 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f32a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f32b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f32b.png new file mode 100644 index 0000000000..a51e486b33 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f32b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f32c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f32c.png new file mode 100644 index 0000000000..89a0f5c875 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f32c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f32d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f32d.png new file mode 100644 index 0000000000..ef0758877e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f32d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f32e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f32e.png new file mode 100644 index 0000000000..4080bd1014 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f32e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f32f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f32f.png new file mode 100644 index 0000000000..6e2327063a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f32f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f330.png b/backend/Shared/CIPPSharp/bin/twemoji/1f330.png new file mode 100644 index 0000000000..06ce776154 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f330.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f331.png b/backend/Shared/CIPPSharp/bin/twemoji/1f331.png new file mode 100644 index 0000000000..57aaddb55c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f331.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f332.png b/backend/Shared/CIPPSharp/bin/twemoji/1f332.png new file mode 100644 index 0000000000..f9ca66ff0a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f332.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f333.png b/backend/Shared/CIPPSharp/bin/twemoji/1f333.png new file mode 100644 index 0000000000..0d51f232e3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f333.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f334.png b/backend/Shared/CIPPSharp/bin/twemoji/1f334.png new file mode 100644 index 0000000000..325ed94d16 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f334.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f335.png b/backend/Shared/CIPPSharp/bin/twemoji/1f335.png new file mode 100644 index 0000000000..76c5096d17 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f335.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f336.png b/backend/Shared/CIPPSharp/bin/twemoji/1f336.png new file mode 100644 index 0000000000..b0e51a9d28 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f336.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f337.png b/backend/Shared/CIPPSharp/bin/twemoji/1f337.png new file mode 100644 index 0000000000..a0b89f7986 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f337.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f338.png b/backend/Shared/CIPPSharp/bin/twemoji/1f338.png new file mode 100644 index 0000000000..3983b94fb2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f338.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f339.png b/backend/Shared/CIPPSharp/bin/twemoji/1f339.png new file mode 100644 index 0000000000..27608f3e61 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f339.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f33a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f33a.png new file mode 100644 index 0000000000..955d1f1c68 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f33a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f33b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f33b.png new file mode 100644 index 0000000000..7770c79ae2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f33b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f33c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f33c.png new file mode 100644 index 0000000000..12f5ce461f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f33c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f33d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f33d.png new file mode 100644 index 0000000000..5f1d62b10a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f33d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f33e.png new file mode 100644 index 0000000000..5899de351b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f33f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f33f.png new file mode 100644 index 0000000000..72a53fe94d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f33f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f340.png b/backend/Shared/CIPPSharp/bin/twemoji/1f340.png new file mode 100644 index 0000000000..265e245a96 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f340.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f341.png b/backend/Shared/CIPPSharp/bin/twemoji/1f341.png new file mode 100644 index 0000000000..3eadf4bcfa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f341.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f342.png b/backend/Shared/CIPPSharp/bin/twemoji/1f342.png new file mode 100644 index 0000000000..dcdc17b80d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f342.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f343.png b/backend/Shared/CIPPSharp/bin/twemoji/1f343.png new file mode 100644 index 0000000000..51abe6c6ab Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f343.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f344-200d-1f7eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f344-200d-1f7eb.png new file mode 100644 index 0000000000..686fc42bb0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f344-200d-1f7eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f344.png b/backend/Shared/CIPPSharp/bin/twemoji/1f344.png new file mode 100644 index 0000000000..80402a0cc1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f344.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f345.png b/backend/Shared/CIPPSharp/bin/twemoji/1f345.png new file mode 100644 index 0000000000..93eaaae6ef Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f345.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f346.png b/backend/Shared/CIPPSharp/bin/twemoji/1f346.png new file mode 100644 index 0000000000..83f1ffabd3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f346.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f347.png b/backend/Shared/CIPPSharp/bin/twemoji/1f347.png new file mode 100644 index 0000000000..c5f99b4d8f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f347.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f348.png b/backend/Shared/CIPPSharp/bin/twemoji/1f348.png new file mode 100644 index 0000000000..8072ee1eb7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f348.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f349.png b/backend/Shared/CIPPSharp/bin/twemoji/1f349.png new file mode 100644 index 0000000000..9c23902148 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f349.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f34a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f34a.png new file mode 100644 index 0000000000..bb250acc92 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f34a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f34b-200d-1f7e9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f34b-200d-1f7e9.png new file mode 100644 index 0000000000..9274d36cea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f34b-200d-1f7e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f34b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f34b.png new file mode 100644 index 0000000000..78b6f953d0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f34b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f34c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f34c.png new file mode 100644 index 0000000000..9a0f2c5731 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f34c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f34d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f34d.png new file mode 100644 index 0000000000..949edeab1f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f34d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f34e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f34e.png new file mode 100644 index 0000000000..a5c4892329 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f34e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f34f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f34f.png new file mode 100644 index 0000000000..ac7be3e39f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f34f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f350.png b/backend/Shared/CIPPSharp/bin/twemoji/1f350.png new file mode 100644 index 0000000000..0f578599be Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f350.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f351.png b/backend/Shared/CIPPSharp/bin/twemoji/1f351.png new file mode 100644 index 0000000000..32e026cb1a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f351.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f352.png b/backend/Shared/CIPPSharp/bin/twemoji/1f352.png new file mode 100644 index 0000000000..ecd2e564af Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f352.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f353.png b/backend/Shared/CIPPSharp/bin/twemoji/1f353.png new file mode 100644 index 0000000000..ac2454a4c6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f353.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f354.png b/backend/Shared/CIPPSharp/bin/twemoji/1f354.png new file mode 100644 index 0000000000..c6e7e93b56 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f354.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f355.png b/backend/Shared/CIPPSharp/bin/twemoji/1f355.png new file mode 100644 index 0000000000..eaefc58a9f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f355.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f356.png b/backend/Shared/CIPPSharp/bin/twemoji/1f356.png new file mode 100644 index 0000000000..7fd839b48a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f356.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f357.png b/backend/Shared/CIPPSharp/bin/twemoji/1f357.png new file mode 100644 index 0000000000..a32c8a6af4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f357.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f358.png b/backend/Shared/CIPPSharp/bin/twemoji/1f358.png new file mode 100644 index 0000000000..e4cd7588a2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f358.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f359.png b/backend/Shared/CIPPSharp/bin/twemoji/1f359.png new file mode 100644 index 0000000000..d294d11755 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f359.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f35a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f35a.png new file mode 100644 index 0000000000..a0ff523cec Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f35a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f35b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f35b.png new file mode 100644 index 0000000000..0826dca835 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f35b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f35c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f35c.png new file mode 100644 index 0000000000..cc6233e336 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f35c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f35d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f35d.png new file mode 100644 index 0000000000..d03a14d2a4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f35d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f35e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f35e.png new file mode 100644 index 0000000000..04dd14e8ad Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f35e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f35f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f35f.png new file mode 100644 index 0000000000..b61e6bc73a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f35f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f360.png b/backend/Shared/CIPPSharp/bin/twemoji/1f360.png new file mode 100644 index 0000000000..9efde9879b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f360.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f361.png b/backend/Shared/CIPPSharp/bin/twemoji/1f361.png new file mode 100644 index 0000000000..90c6fa1d0e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f361.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f362.png b/backend/Shared/CIPPSharp/bin/twemoji/1f362.png new file mode 100644 index 0000000000..db1343958b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f362.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f363.png b/backend/Shared/CIPPSharp/bin/twemoji/1f363.png new file mode 100644 index 0000000000..5c703ff7c7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f363.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f364.png b/backend/Shared/CIPPSharp/bin/twemoji/1f364.png new file mode 100644 index 0000000000..b1797f11f5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f364.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f365.png b/backend/Shared/CIPPSharp/bin/twemoji/1f365.png new file mode 100644 index 0000000000..b98a7419ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f365.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f366.png b/backend/Shared/CIPPSharp/bin/twemoji/1f366.png new file mode 100644 index 0000000000..b1ac286333 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f366.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f367.png b/backend/Shared/CIPPSharp/bin/twemoji/1f367.png new file mode 100644 index 0000000000..70860c53a8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f367.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f368.png b/backend/Shared/CIPPSharp/bin/twemoji/1f368.png new file mode 100644 index 0000000000..f142b35f99 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f368.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f369.png b/backend/Shared/CIPPSharp/bin/twemoji/1f369.png new file mode 100644 index 0000000000..647f504a69 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f369.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f36a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f36a.png new file mode 100644 index 0000000000..074da61308 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f36a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f36b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f36b.png new file mode 100644 index 0000000000..2f7e59bbbd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f36b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f36c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f36c.png new file mode 100644 index 0000000000..de50bdd580 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f36c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f36d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f36d.png new file mode 100644 index 0000000000..d87f8e1f4f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f36d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f36e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f36e.png new file mode 100644 index 0000000000..50a3ef8bfc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f36e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f36f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f36f.png new file mode 100644 index 0000000000..8b283f880b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f36f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f370.png b/backend/Shared/CIPPSharp/bin/twemoji/1f370.png new file mode 100644 index 0000000000..f9b848bec0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f370.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f371.png b/backend/Shared/CIPPSharp/bin/twemoji/1f371.png new file mode 100644 index 0000000000..fde795df59 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f371.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f372.png b/backend/Shared/CIPPSharp/bin/twemoji/1f372.png new file mode 100644 index 0000000000..22775a2c2f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f372.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f373.png new file mode 100644 index 0000000000..6fba00ca3e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f374.png b/backend/Shared/CIPPSharp/bin/twemoji/1f374.png new file mode 100644 index 0000000000..1e8b70d304 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f374.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f375.png b/backend/Shared/CIPPSharp/bin/twemoji/1f375.png new file mode 100644 index 0000000000..3cd74cd95d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f375.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f376.png b/backend/Shared/CIPPSharp/bin/twemoji/1f376.png new file mode 100644 index 0000000000..db30dd2201 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f376.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f377.png b/backend/Shared/CIPPSharp/bin/twemoji/1f377.png new file mode 100644 index 0000000000..94d9830518 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f377.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f378.png b/backend/Shared/CIPPSharp/bin/twemoji/1f378.png new file mode 100644 index 0000000000..d6e4c8da5f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f378.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f379.png b/backend/Shared/CIPPSharp/bin/twemoji/1f379.png new file mode 100644 index 0000000000..40f93365e2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f379.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f37a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f37a.png new file mode 100644 index 0000000000..80de88052b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f37a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f37b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f37b.png new file mode 100644 index 0000000000..ad7800d64b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f37b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f37c.png new file mode 100644 index 0000000000..4493e2b56b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f37d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f37d.png new file mode 100644 index 0000000000..191c4e41a5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f37d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f37e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f37e.png new file mode 100644 index 0000000000..2342f1bdec Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f37e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f37f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f37f.png new file mode 100644 index 0000000000..7708b1f109 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f37f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f380.png b/backend/Shared/CIPPSharp/bin/twemoji/1f380.png new file mode 100644 index 0000000000..3b6663d918 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f380.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f381.png b/backend/Shared/CIPPSharp/bin/twemoji/1f381.png new file mode 100644 index 0000000000..af0b07e48b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f381.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f382.png b/backend/Shared/CIPPSharp/bin/twemoji/1f382.png new file mode 100644 index 0000000000..54a6232020 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f382.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f383.png b/backend/Shared/CIPPSharp/bin/twemoji/1f383.png new file mode 100644 index 0000000000..3e68cb5290 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f383.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f384.png new file mode 100644 index 0000000000..9e3e83a853 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f385-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f385-1f3fb.png new file mode 100644 index 0000000000..ae26f1ffc0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f385-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f385-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f385-1f3fc.png new file mode 100644 index 0000000000..ae1e9a8eef Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f385-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f385-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f385-1f3fd.png new file mode 100644 index 0000000000..3cd9a51351 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f385-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f385-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f385-1f3fe.png new file mode 100644 index 0000000000..bbb8796fdb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f385-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f385-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f385-1f3ff.png new file mode 100644 index 0000000000..1432544fc2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f385-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f385.png b/backend/Shared/CIPPSharp/bin/twemoji/1f385.png new file mode 100644 index 0000000000..44e75755f2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f385.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f386.png b/backend/Shared/CIPPSharp/bin/twemoji/1f386.png new file mode 100644 index 0000000000..68d7bf45cb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f386.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f387.png b/backend/Shared/CIPPSharp/bin/twemoji/1f387.png new file mode 100644 index 0000000000..bfc84671ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f387.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f388.png b/backend/Shared/CIPPSharp/bin/twemoji/1f388.png new file mode 100644 index 0000000000..f003fbea2e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f388.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f389.png b/backend/Shared/CIPPSharp/bin/twemoji/1f389.png new file mode 100644 index 0000000000..863661cd75 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f389.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f38a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f38a.png new file mode 100644 index 0000000000..234935a7ad Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f38a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f38b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f38b.png new file mode 100644 index 0000000000..8fe41cf130 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f38b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f38c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f38c.png new file mode 100644 index 0000000000..e65ed9a0f6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f38c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f38d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f38d.png new file mode 100644 index 0000000000..df1fec8f64 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f38d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f38e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f38e.png new file mode 100644 index 0000000000..d026c99989 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f38e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f38f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f38f.png new file mode 100644 index 0000000000..4371ba79e9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f38f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f390.png b/backend/Shared/CIPPSharp/bin/twemoji/1f390.png new file mode 100644 index 0000000000..78b2db1878 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f390.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f391.png b/backend/Shared/CIPPSharp/bin/twemoji/1f391.png new file mode 100644 index 0000000000..a2dfd88db1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f391.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f392.png b/backend/Shared/CIPPSharp/bin/twemoji/1f392.png new file mode 100644 index 0000000000..c064023730 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f392.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f393.png new file mode 100644 index 0000000000..09d4757afc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f396.png b/backend/Shared/CIPPSharp/bin/twemoji/1f396.png new file mode 100644 index 0000000000..42a8e1f92f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f396.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f397.png b/backend/Shared/CIPPSharp/bin/twemoji/1f397.png new file mode 100644 index 0000000000..b40fb3fd3b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f397.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f399.png b/backend/Shared/CIPPSharp/bin/twemoji/1f399.png new file mode 100644 index 0000000000..1af0e3a7a5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f399.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f39a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f39a.png new file mode 100644 index 0000000000..9b013eed6a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f39a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f39b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f39b.png new file mode 100644 index 0000000000..deeeec9307 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f39b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f39e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f39e.png new file mode 100644 index 0000000000..431b759e64 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f39e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f39f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f39f.png new file mode 100644 index 0000000000..d79c8e5d47 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f39f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3a0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3a0.png new file mode 100644 index 0000000000..41fb66baaa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3a0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3a1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3a1.png new file mode 100644 index 0000000000..ea4a06a580 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3a1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3a2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3a2.png new file mode 100644 index 0000000000..a881d3ee24 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3a2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3a3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3a3.png new file mode 100644 index 0000000000..aa7b3e9b74 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3a3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3a4.png new file mode 100644 index 0000000000..51e8d9063e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3a5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3a5.png new file mode 100644 index 0000000000..5c990159ff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3a5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3a6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3a6.png new file mode 100644 index 0000000000..7d22c83f7b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3a6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3a7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3a7.png new file mode 100644 index 0000000000..847afe56c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3a7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3a8.png new file mode 100644 index 0000000000..1dda3e0c6e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3a9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3a9.png new file mode 100644 index 0000000000..6698169056 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3a9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3aa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3aa.png new file mode 100644 index 0000000000..685c18bfb0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3aa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ab.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ab.png new file mode 100644 index 0000000000..072235f16e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ab.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ac.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ac.png new file mode 100644 index 0000000000..3a45826402 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ac.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ad.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ad.png new file mode 100644 index 0000000000..a84cd97a9c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ad.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ae.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ae.png new file mode 100644 index 0000000000..10b366c9fe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ae.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3af.png new file mode 100644 index 0000000000..d96b850d3a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3b0.png new file mode 100644 index 0000000000..0e7ef1bea1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3b1.png new file mode 100644 index 0000000000..4905297389 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3b2.png new file mode 100644 index 0000000000..c70ee4822c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3b3.png new file mode 100644 index 0000000000..d8705bf55c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3b4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3b4.png new file mode 100644 index 0000000000..d6935e19be Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3b4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3b5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3b5.png new file mode 100644 index 0000000000..dde36d4f98 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3b5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3b6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3b6.png new file mode 100644 index 0000000000..75b6f53721 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3b6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3b7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3b7.png new file mode 100644 index 0000000000..a87468ef84 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3b7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3b8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3b8.png new file mode 100644 index 0000000000..9e8daae5de Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3b8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3b9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3b9.png new file mode 100644 index 0000000000..39d91fa106 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3b9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ba.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ba.png new file mode 100644 index 0000000000..493a9aa33b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ba.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3bb.png new file mode 100644 index 0000000000..d14c4272ac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3bc.png new file mode 100644 index 0000000000..9362582b99 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3bd.png new file mode 100644 index 0000000000..970d4167f4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3be.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3be.png new file mode 100644 index 0000000000..6eb3a19dc9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3be.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3bf.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3bf.png new file mode 100644 index 0000000000..c5f5037702 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3bf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c0.png new file mode 100644 index 0000000000..144a5d6acc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c1.png new file mode 100644 index 0000000000..31d2215af1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c2-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c2-1f3fb.png new file mode 100644 index 0000000000..c5f299d313 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c2-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c2-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c2-1f3fc.png new file mode 100644 index 0000000000..33a3c31770 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c2-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c2-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c2-1f3fd.png new file mode 100644 index 0000000000..bc172a919f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c2-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c2-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c2-1f3fe.png new file mode 100644 index 0000000000..c8800a85d8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c2-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c2-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c2-1f3ff.png new file mode 100644 index 0000000000..f7c766fc07 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c2-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c2.png new file mode 100644 index 0000000000..4b1baa0376 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..5adbc60cb6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..1f2be9ceaa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..f67e35b532 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..5490f5eb8b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb-200d-27a1-fe0f.png new file mode 100644 index 0000000000..0cd3ec5a7a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb.png new file mode 100644 index 0000000000..d412329c35 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..a661d7093f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..88151f6e4a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..6639fe6d7f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..21b944bcd4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..7b9c0defd0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc.png new file mode 100644 index 0000000000..1e4be4df88 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..717287b7b3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..df2f77de89 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..8821eaf0b0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..200677994c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..5e355999db Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd.png new file mode 100644 index 0000000000..949de3d358 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..69d7ad6c59 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..565f91af86 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..7df41b4e66 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..4965c7bfd0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe-200d-27a1-fe0f.png new file mode 100644 index 0000000000..ad3a74c2b2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe.png new file mode 100644 index 0000000000..c4eedcb9e1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..fc2e409c56 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..1fe6d0da10 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..361ae58052 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..eead2a8106 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff-200d-27a1-fe0f.png new file mode 100644 index 0000000000..2127e12c07 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff.png new file mode 100644 index 0000000000..9cfc207a2a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..fbb55ba641 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-200d-2640-fe0f.png new file mode 100644 index 0000000000..03bfd1f6f7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..eeef753526 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-200d-2642-fe0f.png new file mode 100644 index 0000000000..9f4092dc07 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-200d-27a1-fe0f.png new file mode 100644 index 0000000000..ba7ddf603d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3.png new file mode 100644 index 0000000000..0657112541 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..c9877834d2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..818f97682d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fb.png new file mode 100644 index 0000000000..483a0737f4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..7f60894535 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..dfa13101f5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fc.png new file mode 100644 index 0000000000..ad55c3b4b8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..6044602f24 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..d0c5918e95 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fd.png new file mode 100644 index 0000000000..84672394fd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..db8f4ee613 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..db6ce1687d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fe.png new file mode 100644 index 0000000000..736316e609 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..45a3620fc0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..f863def125 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3ff.png new file mode 100644 index 0000000000..4f3f5b216c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-200d-2640-fe0f.png new file mode 100644 index 0000000000..c11a60b08b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-200d-2642-fe0f.png new file mode 100644 index 0000000000..82ffd6cb5a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4.png new file mode 100644 index 0000000000..5f0626a59b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c5.png new file mode 100644 index 0000000000..b19a9ac56d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c6.png new file mode 100644 index 0000000000..cd4438f90e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c7-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c7-1f3fb.png new file mode 100644 index 0000000000..67053c98fd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c7-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c7-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c7-1f3fc.png new file mode 100644 index 0000000000..83eed6ee1a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c7-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c7-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c7-1f3fd.png new file mode 100644 index 0000000000..79d25a9411 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c7-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c7-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c7-1f3fe.png new file mode 100644 index 0000000000..70a05d74b4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c7-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c7-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c7-1f3ff.png new file mode 100644 index 0000000000..be6aa30fcd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c7-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c7.png new file mode 100644 index 0000000000..904d7ea386 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c8.png new file mode 100644 index 0000000000..4ff4d5e43a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3c9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3c9.png new file mode 100644 index 0000000000..1d533f99d6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3c9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..3c424f3fc7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..d303f285b8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fb.png new file mode 100644 index 0000000000..6d020a4d05 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..1b3c39293e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..4da544a7b6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fc.png new file mode 100644 index 0000000000..b14a6b19a4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..ed8eab1682 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..487bf48507 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fd.png new file mode 100644 index 0000000000..76026bed75 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..bd1d02a4a6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..1e60588161 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fe.png new file mode 100644 index 0000000000..d40f61b1b7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..c7bd85610a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..a9dd7d3dc8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3ff.png new file mode 100644 index 0000000000..95eac61bcc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-200d-2640-fe0f.png new file mode 100644 index 0000000000..5f3f0483fc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-200d-2642-fe0f.png new file mode 100644 index 0000000000..f2c1433306 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ca.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca.png new file mode 100644 index 0000000000..c2896f43cc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ca.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..df2b83f1e3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..660634c312 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fb.png new file mode 100644 index 0000000000..2955cf95e6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..647848baaa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..4ed8a9b2f6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fc.png new file mode 100644 index 0000000000..2a9af4a4c0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..c6e0276786 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..18216413f2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fd.png new file mode 100644 index 0000000000..4be445c6d7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..94a62125fa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..8ceeb5ef3e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fe.png new file mode 100644 index 0000000000..3c05ac275a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..4e0d6d8df5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..6e500d5cdb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3ff.png new file mode 100644 index 0000000000..3b4b183bea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-fe0f-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-fe0f-200d-2640-fe0f.png new file mode 100644 index 0000000000..1478cc88ba Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-fe0f-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-fe0f-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-fe0f-200d-2642-fe0f.png new file mode 100644 index 0000000000..876096a509 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb-fe0f-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb.png new file mode 100644 index 0000000000..dcdf5fd270 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..1c176850f4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..a6d86bbe18 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fb.png new file mode 100644 index 0000000000..ada62af9ce Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..e1be1ec254 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..d480b6bce1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fc.png new file mode 100644 index 0000000000..7aba672350 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..a31793fcd3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..29bae8f98a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fd.png new file mode 100644 index 0000000000..cd31ce995d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..f8eb7fabd5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..f7dcada7af Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fe.png new file mode 100644 index 0000000000..811b8bf83d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..1381b5fea4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..5d005621e1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3ff.png new file mode 100644 index 0000000000..77024416bc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-fe0f-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-fe0f-200d-2640-fe0f.png new file mode 100644 index 0000000000..92f4457bda Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-fe0f-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-fe0f-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-fe0f-200d-2642-fe0f.png new file mode 100644 index 0000000000..cda196e7eb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc-fe0f-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc.png new file mode 100644 index 0000000000..5816219995 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cd.png new file mode 100644 index 0000000000..ac675cf4a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ce.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ce.png new file mode 100644 index 0000000000..732ab9ff2f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ce.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3cf.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3cf.png new file mode 100644 index 0000000000..dd330cb80c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3cf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3d0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3d0.png new file mode 100644 index 0000000000..5836514c03 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3d0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3d1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3d1.png new file mode 100644 index 0000000000..a663a0ca6e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3d1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3d2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3d2.png new file mode 100644 index 0000000000..3f7b86896c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3d2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3d3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3d3.png new file mode 100644 index 0000000000..6d5a36dbf3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3d3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3d4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3d4.png new file mode 100644 index 0000000000..a177f1bad2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3d4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3d5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3d5.png new file mode 100644 index 0000000000..73d9df795c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3d5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3d6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3d6.png new file mode 100644 index 0000000000..19cf9ce916 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3d6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3d7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3d7.png new file mode 100644 index 0000000000..0c6e9dffe5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3d7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3d8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3d8.png new file mode 100644 index 0000000000..b4b3bf2eb3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3d8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3d9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3d9.png new file mode 100644 index 0000000000..048591245f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3d9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3da.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3da.png new file mode 100644 index 0000000000..feb0a21465 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3da.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3db.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3db.png new file mode 100644 index 0000000000..e49f54e847 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3db.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3dc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3dc.png new file mode 100644 index 0000000000..7775db168d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3dc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3dd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3dd.png new file mode 100644 index 0000000000..c8a6edaab0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3dd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3de.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3de.png new file mode 100644 index 0000000000..478bacc9c3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3de.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3df.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3df.png new file mode 100644 index 0000000000..cef6dead0c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3df.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3e0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3e0.png new file mode 100644 index 0000000000..a804a62786 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3e0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3e1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3e1.png new file mode 100644 index 0000000000..e8eb761a95 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3e1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3e2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3e2.png new file mode 100644 index 0000000000..0ad23d8b05 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3e2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3e3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3e3.png new file mode 100644 index 0000000000..da0982b011 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3e4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3e4.png new file mode 100644 index 0000000000..e1ff2da636 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3e4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3e5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3e5.png new file mode 100644 index 0000000000..b038bb58eb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3e5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3e6.png new file mode 100644 index 0000000000..011412337e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3e7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3e7.png new file mode 100644 index 0000000000..1e36fefdbd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3e7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3e8.png new file mode 100644 index 0000000000..646fe6e42e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3e9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3e9.png new file mode 100644 index 0000000000..6c789fc22a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ea.png new file mode 100644 index 0000000000..bb84d3e665 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3eb.png new file mode 100644 index 0000000000..ae15884cc2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ec.png new file mode 100644 index 0000000000..5c94977ebd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ed.png new file mode 100644 index 0000000000..555c8a7ed5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ee.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ee.png new file mode 100644 index 0000000000..a6e883285d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ee.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ef.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ef.png new file mode 100644 index 0000000000..3819a53812 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ef.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3f0.png new file mode 100644 index 0000000000..78bc6200ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3f3-fe0f-200d-1f308.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3f3-fe0f-200d-1f308.png new file mode 100644 index 0000000000..a79b58c8c1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3f3-fe0f-200d-1f308.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3f3-fe0f-200d-26a7-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3f3-fe0f-200d-26a7-fe0f.png new file mode 100644 index 0000000000..8994b2d055 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3f3-fe0f-200d-26a7-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3f3.png new file mode 100644 index 0000000000..49317273b4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3f4-200d-2620-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3f4-200d-2620-fe0f.png new file mode 100644 index 0000000000..f5fc239943 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3f4-200d-2620-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.png new file mode 100644 index 0000000000..fcdd94aa9d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.png new file mode 100644 index 0000000000..05a827ffd0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.png new file mode 100644 index 0000000000..0370dbf432 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3f4.png new file mode 100644 index 0000000000..71a7af8304 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3f5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3f5.png new file mode 100644 index 0000000000..0ba33a9051 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3f5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3f7.png new file mode 100644 index 0000000000..355b9e375d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3f8.png new file mode 100644 index 0000000000..1e0a95a180 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3f9.png new file mode 100644 index 0000000000..87dea7a594 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3fa.png new file mode 100644 index 0000000000..83caa8d108 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3fb.png new file mode 100644 index 0000000000..84f81dfcfb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3fc.png new file mode 100644 index 0000000000..e9e39e53fd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3fd.png new file mode 100644 index 0000000000..661cfc2e58 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3fe.png new file mode 100644 index 0000000000..5001b8017d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f3ff.png new file mode 100644 index 0000000000..bc2c6e58fb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f400.png b/backend/Shared/CIPPSharp/bin/twemoji/1f400.png new file mode 100644 index 0000000000..a2150f2d3b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f400.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f401.png b/backend/Shared/CIPPSharp/bin/twemoji/1f401.png new file mode 100644 index 0000000000..e2509b0729 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f401.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f402.png b/backend/Shared/CIPPSharp/bin/twemoji/1f402.png new file mode 100644 index 0000000000..e20bd7d42b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f402.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f403.png b/backend/Shared/CIPPSharp/bin/twemoji/1f403.png new file mode 100644 index 0000000000..ced075f6af Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f403.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f404.png b/backend/Shared/CIPPSharp/bin/twemoji/1f404.png new file mode 100644 index 0000000000..bb0e16c3c3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f404.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f405.png b/backend/Shared/CIPPSharp/bin/twemoji/1f405.png new file mode 100644 index 0000000000..eb20e615c5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f405.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f406.png b/backend/Shared/CIPPSharp/bin/twemoji/1f406.png new file mode 100644 index 0000000000..2f019b86e5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f406.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f407.png b/backend/Shared/CIPPSharp/bin/twemoji/1f407.png new file mode 100644 index 0000000000..afbdaf3b0d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f407.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f408-200d-2b1b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f408-200d-2b1b.png new file mode 100644 index 0000000000..b8fe4db9e2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f408-200d-2b1b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f408.png b/backend/Shared/CIPPSharp/bin/twemoji/1f408.png new file mode 100644 index 0000000000..f92e1aad84 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f408.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f409.png b/backend/Shared/CIPPSharp/bin/twemoji/1f409.png new file mode 100644 index 0000000000..824e37d940 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f409.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f40a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f40a.png new file mode 100644 index 0000000000..8e15d44959 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f40a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f40b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f40b.png new file mode 100644 index 0000000000..c103c6ef38 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f40b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f40c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f40c.png new file mode 100644 index 0000000000..898bdd1b19 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f40c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f40d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f40d.png new file mode 100644 index 0000000000..920bc69382 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f40d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f40e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f40e.png new file mode 100644 index 0000000000..dc91a0d91f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f40e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f40f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f40f.png new file mode 100644 index 0000000000..96380f1497 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f40f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f410.png b/backend/Shared/CIPPSharp/bin/twemoji/1f410.png new file mode 100644 index 0000000000..696e91c93a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f410.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f411.png b/backend/Shared/CIPPSharp/bin/twemoji/1f411.png new file mode 100644 index 0000000000..f803dd80b8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f411.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f412.png b/backend/Shared/CIPPSharp/bin/twemoji/1f412.png new file mode 100644 index 0000000000..cf52ab1518 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f412.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f413.png b/backend/Shared/CIPPSharp/bin/twemoji/1f413.png new file mode 100644 index 0000000000..61a94d4729 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f413.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f414.png b/backend/Shared/CIPPSharp/bin/twemoji/1f414.png new file mode 100644 index 0000000000..b6cfed1f66 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f414.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f415-200d-1f9ba.png b/backend/Shared/CIPPSharp/bin/twemoji/1f415-200d-1f9ba.png new file mode 100644 index 0000000000..f09349e64e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f415-200d-1f9ba.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f415.png b/backend/Shared/CIPPSharp/bin/twemoji/1f415.png new file mode 100644 index 0000000000..7c869c487a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f415.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f416.png b/backend/Shared/CIPPSharp/bin/twemoji/1f416.png new file mode 100644 index 0000000000..1bb3b6e5bc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f416.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f417.png b/backend/Shared/CIPPSharp/bin/twemoji/1f417.png new file mode 100644 index 0000000000..049f5bc9d1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f417.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f418.png b/backend/Shared/CIPPSharp/bin/twemoji/1f418.png new file mode 100644 index 0000000000..ebc666dce3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f418.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f419.png b/backend/Shared/CIPPSharp/bin/twemoji/1f419.png new file mode 100644 index 0000000000..c90c5f4d23 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f419.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f41a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f41a.png new file mode 100644 index 0000000000..496e7a8146 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f41a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f41b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f41b.png new file mode 100644 index 0000000000..6010258f4a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f41b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f41c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f41c.png new file mode 100644 index 0000000000..268571bb07 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f41c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f41d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f41d.png new file mode 100644 index 0000000000..2440295b91 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f41d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f41e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f41e.png new file mode 100644 index 0000000000..f605bd5d22 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f41e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f41f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f41f.png new file mode 100644 index 0000000000..1d6e90fa69 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f41f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f420.png b/backend/Shared/CIPPSharp/bin/twemoji/1f420.png new file mode 100644 index 0000000000..5f1e2e5e8e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f420.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f421.png b/backend/Shared/CIPPSharp/bin/twemoji/1f421.png new file mode 100644 index 0000000000..1dc9bd9a0e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f421.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f422.png b/backend/Shared/CIPPSharp/bin/twemoji/1f422.png new file mode 100644 index 0000000000..5253638fea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f422.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f423.png b/backend/Shared/CIPPSharp/bin/twemoji/1f423.png new file mode 100644 index 0000000000..23b0dd1bf4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f423.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f424.png b/backend/Shared/CIPPSharp/bin/twemoji/1f424.png new file mode 100644 index 0000000000..bd87c6fac3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f424.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f425.png b/backend/Shared/CIPPSharp/bin/twemoji/1f425.png new file mode 100644 index 0000000000..66e7b973b1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f425.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f426-200d-1f525.png b/backend/Shared/CIPPSharp/bin/twemoji/1f426-200d-1f525.png new file mode 100644 index 0000000000..28d17bc160 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f426-200d-1f525.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f426-200d-2b1b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f426-200d-2b1b.png new file mode 100644 index 0000000000..c3e1865175 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f426-200d-2b1b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f426.png b/backend/Shared/CIPPSharp/bin/twemoji/1f426.png new file mode 100644 index 0000000000..d129ac778f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f426.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f427.png b/backend/Shared/CIPPSharp/bin/twemoji/1f427.png new file mode 100644 index 0000000000..2ee0dbc6ae Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f427.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f428.png b/backend/Shared/CIPPSharp/bin/twemoji/1f428.png new file mode 100644 index 0000000000..0c2ae778a7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f428.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f429.png b/backend/Shared/CIPPSharp/bin/twemoji/1f429.png new file mode 100644 index 0000000000..ebc9aef972 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f429.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f42a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f42a.png new file mode 100644 index 0000000000..4ede95a2aa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f42a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f42b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f42b.png new file mode 100644 index 0000000000..af4a976b32 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f42b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f42c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f42c.png new file mode 100644 index 0000000000..86b97d8a3c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f42c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f42d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f42d.png new file mode 100644 index 0000000000..fa31afc1b0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f42d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f42e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f42e.png new file mode 100644 index 0000000000..c872a5fe67 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f42e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f42f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f42f.png new file mode 100644 index 0000000000..cb9915252d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f42f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f430.png b/backend/Shared/CIPPSharp/bin/twemoji/1f430.png new file mode 100644 index 0000000000..2397ddd337 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f430.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f431.png b/backend/Shared/CIPPSharp/bin/twemoji/1f431.png new file mode 100644 index 0000000000..292aff7841 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f431.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f432.png b/backend/Shared/CIPPSharp/bin/twemoji/1f432.png new file mode 100644 index 0000000000..88a42e2fba Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f432.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f433.png b/backend/Shared/CIPPSharp/bin/twemoji/1f433.png new file mode 100644 index 0000000000..1bae0fc3a2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f433.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f434.png b/backend/Shared/CIPPSharp/bin/twemoji/1f434.png new file mode 100644 index 0000000000..79d0ba1ee5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f434.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f435.png b/backend/Shared/CIPPSharp/bin/twemoji/1f435.png new file mode 100644 index 0000000000..780034d553 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f435.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f436.png b/backend/Shared/CIPPSharp/bin/twemoji/1f436.png new file mode 100644 index 0000000000..a4c07a081f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f436.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f437.png b/backend/Shared/CIPPSharp/bin/twemoji/1f437.png new file mode 100644 index 0000000000..1c9215aae2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f437.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f438.png b/backend/Shared/CIPPSharp/bin/twemoji/1f438.png new file mode 100644 index 0000000000..9f61856983 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f438.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f439.png b/backend/Shared/CIPPSharp/bin/twemoji/1f439.png new file mode 100644 index 0000000000..7081211244 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f439.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f43a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f43a.png new file mode 100644 index 0000000000..8f676db32d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f43a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f43b-200d-2744-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f43b-200d-2744-fe0f.png new file mode 100644 index 0000000000..ac27142489 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f43b-200d-2744-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f43b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f43b.png new file mode 100644 index 0000000000..726115f19e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f43b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f43c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f43c.png new file mode 100644 index 0000000000..e3995ee938 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f43c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f43d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f43d.png new file mode 100644 index 0000000000..1aee7931c0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f43d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f43e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f43e.png new file mode 100644 index 0000000000..2c6266d248 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f43e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f43f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f43f.png new file mode 100644 index 0000000000..4e284b89bb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f43f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f440.png b/backend/Shared/CIPPSharp/bin/twemoji/1f440.png new file mode 100644 index 0000000000..1b3d0daf01 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f440.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f441-200d-1f5e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f441-200d-1f5e8.png new file mode 100644 index 0000000000..515c73a477 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f441-200d-1f5e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f441.png b/backend/Shared/CIPPSharp/bin/twemoji/1f441.png new file mode 100644 index 0000000000..9a59d28b09 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f441.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f442-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f442-1f3fb.png new file mode 100644 index 0000000000..80566ef86f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f442-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f442-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f442-1f3fc.png new file mode 100644 index 0000000000..a926069af7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f442-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f442-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f442-1f3fd.png new file mode 100644 index 0000000000..89f758f3bf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f442-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f442-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f442-1f3fe.png new file mode 100644 index 0000000000..997fbb346e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f442-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f442-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f442-1f3ff.png new file mode 100644 index 0000000000..1b6842c5d8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f442-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f442.png b/backend/Shared/CIPPSharp/bin/twemoji/1f442.png new file mode 100644 index 0000000000..d8e2ad76e2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f442.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f443-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f443-1f3fb.png new file mode 100644 index 0000000000..6aea7ccc76 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f443-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f443-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f443-1f3fc.png new file mode 100644 index 0000000000..76dcb3d7b6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f443-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f443-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f443-1f3fd.png new file mode 100644 index 0000000000..c3a48df072 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f443-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f443-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f443-1f3fe.png new file mode 100644 index 0000000000..946fd2b330 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f443-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f443-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f443-1f3ff.png new file mode 100644 index 0000000000..492fdc35d5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f443-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f443.png b/backend/Shared/CIPPSharp/bin/twemoji/1f443.png new file mode 100644 index 0000000000..ece7bb8dd8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f443.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f444.png b/backend/Shared/CIPPSharp/bin/twemoji/1f444.png new file mode 100644 index 0000000000..16dbe112fe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f444.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f445.png b/backend/Shared/CIPPSharp/bin/twemoji/1f445.png new file mode 100644 index 0000000000..441866f0a7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f445.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f446-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f446-1f3fb.png new file mode 100644 index 0000000000..024353211d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f446-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f446-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f446-1f3fc.png new file mode 100644 index 0000000000..8fe07a76f1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f446-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f446-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f446-1f3fd.png new file mode 100644 index 0000000000..7e795d6695 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f446-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f446-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f446-1f3fe.png new file mode 100644 index 0000000000..c3b6373178 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f446-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f446-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f446-1f3ff.png new file mode 100644 index 0000000000..9df30a7730 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f446-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f446.png b/backend/Shared/CIPPSharp/bin/twemoji/1f446.png new file mode 100644 index 0000000000..8a5dfb10b0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f446.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f447-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f447-1f3fb.png new file mode 100644 index 0000000000..9b87944e1c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f447-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f447-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f447-1f3fc.png new file mode 100644 index 0000000000..05da2e0ef6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f447-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f447-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f447-1f3fd.png new file mode 100644 index 0000000000..cf8393f057 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f447-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f447-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f447-1f3fe.png new file mode 100644 index 0000000000..3daf8e0c06 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f447-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f447-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f447-1f3ff.png new file mode 100644 index 0000000000..54ee9d9be2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f447-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f447.png b/backend/Shared/CIPPSharp/bin/twemoji/1f447.png new file mode 100644 index 0000000000..b5d06ca53b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f447.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f448-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f448-1f3fb.png new file mode 100644 index 0000000000..576a0f3f21 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f448-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f448-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f448-1f3fc.png new file mode 100644 index 0000000000..08ecf7e6d5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f448-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f448-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f448-1f3fd.png new file mode 100644 index 0000000000..c5b5a71402 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f448-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f448-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f448-1f3fe.png new file mode 100644 index 0000000000..f1ed6f7a21 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f448-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f448-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f448-1f3ff.png new file mode 100644 index 0000000000..89821cd395 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f448-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f448.png b/backend/Shared/CIPPSharp/bin/twemoji/1f448.png new file mode 100644 index 0000000000..441c2f819a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f448.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f449-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f449-1f3fb.png new file mode 100644 index 0000000000..82ea57957b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f449-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f449-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f449-1f3fc.png new file mode 100644 index 0000000000..31c5fa365e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f449-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f449-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f449-1f3fd.png new file mode 100644 index 0000000000..ee0081eb16 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f449-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f449-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f449-1f3fe.png new file mode 100644 index 0000000000..a8aa2f3439 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f449-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f449-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f449-1f3ff.png new file mode 100644 index 0000000000..2b72da33b1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f449-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f449.png b/backend/Shared/CIPPSharp/bin/twemoji/1f449.png new file mode 100644 index 0000000000..85c300aaa8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f449.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44a-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44a-1f3fb.png new file mode 100644 index 0000000000..90bffd8278 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44a-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44a-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44a-1f3fc.png new file mode 100644 index 0000000000..f800eabcea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44a-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44a-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44a-1f3fd.png new file mode 100644 index 0000000000..d9de1ad26d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44a-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44a-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44a-1f3fe.png new file mode 100644 index 0000000000..029c1b3bc3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44a-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44a-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44a-1f3ff.png new file mode 100644 index 0000000000..4ad7a4a646 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44a-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44a.png new file mode 100644 index 0000000000..264880fd86 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44b-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44b-1f3fb.png new file mode 100644 index 0000000000..8dd2c95e36 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44b-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44b-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44b-1f3fc.png new file mode 100644 index 0000000000..4d983cd4d8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44b-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44b-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44b-1f3fd.png new file mode 100644 index 0000000000..478ee389fb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44b-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44b-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44b-1f3fe.png new file mode 100644 index 0000000000..cba0b2d9a4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44b-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44b-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44b-1f3ff.png new file mode 100644 index 0000000000..34c61982dc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44b-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44b.png new file mode 100644 index 0000000000..250e857fb7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44c-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44c-1f3fb.png new file mode 100644 index 0000000000..f93153c582 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44c-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44c-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44c-1f3fc.png new file mode 100644 index 0000000000..68e450716d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44c-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44c-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44c-1f3fd.png new file mode 100644 index 0000000000..869d982cb0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44c-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44c-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44c-1f3fe.png new file mode 100644 index 0000000000..b3d2e82631 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44c-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44c-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44c-1f3ff.png new file mode 100644 index 0000000000..f0c6a8cf2b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44c-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44c.png new file mode 100644 index 0000000000..6a7b897693 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44d-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44d-1f3fb.png new file mode 100644 index 0000000000..ceab0da2e2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44d-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44d-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44d-1f3fc.png new file mode 100644 index 0000000000..e78a835459 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44d-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44d-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44d-1f3fd.png new file mode 100644 index 0000000000..56a60600de Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44d-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44d-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44d-1f3fe.png new file mode 100644 index 0000000000..14a8eb91ad Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44d-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44d-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44d-1f3ff.png new file mode 100644 index 0000000000..6fddfe1058 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44d-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44d.png new file mode 100644 index 0000000000..51b8c75bb9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44e-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44e-1f3fb.png new file mode 100644 index 0000000000..48e16a253b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44e-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44e-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44e-1f3fc.png new file mode 100644 index 0000000000..8fdb6a1279 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44e-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44e-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44e-1f3fd.png new file mode 100644 index 0000000000..1f7ff2b13a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44e-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44e-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44e-1f3fe.png new file mode 100644 index 0000000000..f971e494a3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44e-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44e-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44e-1f3ff.png new file mode 100644 index 0000000000..62f23f40d8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44e-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44e.png new file mode 100644 index 0000000000..3801fe8f3e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44f-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44f-1f3fb.png new file mode 100644 index 0000000000..4194338799 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44f-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44f-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44f-1f3fc.png new file mode 100644 index 0000000000..28f2ab9716 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44f-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44f-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44f-1f3fd.png new file mode 100644 index 0000000000..aef3c3398f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44f-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44f-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44f-1f3fe.png new file mode 100644 index 0000000000..7d64a24db5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44f-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44f-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44f-1f3ff.png new file mode 100644 index 0000000000..389765736a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44f-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f44f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f44f.png new file mode 100644 index 0000000000..8512b44bfe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f44f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f450-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f450-1f3fb.png new file mode 100644 index 0000000000..d049c49418 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f450-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f450-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f450-1f3fc.png new file mode 100644 index 0000000000..95155ca5d5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f450-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f450-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f450-1f3fd.png new file mode 100644 index 0000000000..44a91e5408 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f450-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f450-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f450-1f3fe.png new file mode 100644 index 0000000000..cb7c763be0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f450-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f450-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f450-1f3ff.png new file mode 100644 index 0000000000..8fe0efb743 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f450-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f450.png b/backend/Shared/CIPPSharp/bin/twemoji/1f450.png new file mode 100644 index 0000000000..43b0021baf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f450.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f451.png b/backend/Shared/CIPPSharp/bin/twemoji/1f451.png new file mode 100644 index 0000000000..ee8792481e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f451.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f452.png b/backend/Shared/CIPPSharp/bin/twemoji/1f452.png new file mode 100644 index 0000000000..ae6e88c2bc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f452.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f453.png b/backend/Shared/CIPPSharp/bin/twemoji/1f453.png new file mode 100644 index 0000000000..2808766313 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f453.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f454.png b/backend/Shared/CIPPSharp/bin/twemoji/1f454.png new file mode 100644 index 0000000000..08d920fa12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f454.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f455.png b/backend/Shared/CIPPSharp/bin/twemoji/1f455.png new file mode 100644 index 0000000000..1dc1d91d40 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f455.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f456.png b/backend/Shared/CIPPSharp/bin/twemoji/1f456.png new file mode 100644 index 0000000000..44713902ee Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f456.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f457.png b/backend/Shared/CIPPSharp/bin/twemoji/1f457.png new file mode 100644 index 0000000000..40ee78e104 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f457.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f458.png b/backend/Shared/CIPPSharp/bin/twemoji/1f458.png new file mode 100644 index 0000000000..b4fd087b12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f458.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f459.png b/backend/Shared/CIPPSharp/bin/twemoji/1f459.png new file mode 100644 index 0000000000..3892a92940 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f459.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f45a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f45a.png new file mode 100644 index 0000000000..721cfdc238 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f45a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f45b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f45b.png new file mode 100644 index 0000000000..13037d8f33 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f45b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f45c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f45c.png new file mode 100644 index 0000000000..61e490910b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f45c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f45d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f45d.png new file mode 100644 index 0000000000..1e7e714619 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f45d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f45e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f45e.png new file mode 100644 index 0000000000..87cbf8a1f8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f45e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f45f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f45f.png new file mode 100644 index 0000000000..fe7db6fb74 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f45f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f460.png b/backend/Shared/CIPPSharp/bin/twemoji/1f460.png new file mode 100644 index 0000000000..02325941a2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f460.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f461.png b/backend/Shared/CIPPSharp/bin/twemoji/1f461.png new file mode 100644 index 0000000000..806fd1cf1e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f461.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f462.png b/backend/Shared/CIPPSharp/bin/twemoji/1f462.png new file mode 100644 index 0000000000..91d298e0f7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f462.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f463.png b/backend/Shared/CIPPSharp/bin/twemoji/1f463.png new file mode 100644 index 0000000000..0e2b87e791 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f463.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f464.png b/backend/Shared/CIPPSharp/bin/twemoji/1f464.png new file mode 100644 index 0000000000..df64d95cc0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f464.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f465.png b/backend/Shared/CIPPSharp/bin/twemoji/1f465.png new file mode 100644 index 0000000000..b907898cf4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f465.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f466-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f466-1f3fb.png new file mode 100644 index 0000000000..30ee0fb998 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f466-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f466-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f466-1f3fc.png new file mode 100644 index 0000000000..219c8a3ede Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f466-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f466-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f466-1f3fd.png new file mode 100644 index 0000000000..fb2d105c54 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f466-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f466-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f466-1f3fe.png new file mode 100644 index 0000000000..a0a1895f09 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f466-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f466-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f466-1f3ff.png new file mode 100644 index 0000000000..89edbdaadf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f466-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f466.png new file mode 100644 index 0000000000..aca7839400 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f467-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f467-1f3fb.png new file mode 100644 index 0000000000..959d55e664 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f467-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f467-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f467-1f3fc.png new file mode 100644 index 0000000000..a6276570ce Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f467-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f467-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f467-1f3fd.png new file mode 100644 index 0000000000..c5bba79773 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f467-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f467-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f467-1f3fe.png new file mode 100644 index 0000000000..0509f75b12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f467-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f467-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f467-1f3ff.png new file mode 100644 index 0000000000..c97e91ac4d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f467-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f467.png b/backend/Shared/CIPPSharp/bin/twemoji/1f467.png new file mode 100644 index 0000000000..c7199f6d46 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f467.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f33e.png new file mode 100644 index 0000000000..b28cdb8332 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f373.png new file mode 100644 index 0000000000..23bace9784 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f37c.png new file mode 100644 index 0000000000..808c0f5e6c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f384.png new file mode 100644 index 0000000000..ae26f1ffc0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f393.png new file mode 100644 index 0000000000..3e1eb365c2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f3a4.png new file mode 100644 index 0000000000..95948a7fc7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f3a8.png new file mode 100644 index 0000000000..d0da93939a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f3eb.png new file mode 100644 index 0000000000..68789803d7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f3ed.png new file mode 100644 index 0000000000..7df79d4d23 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f430-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f430-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..d5723b4680 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f430-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f430-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f430-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..ae72011792 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f430-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f430-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f430-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..856cd76bba Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f430-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f430-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f430-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..0b8849b320 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f430-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f4bb.png new file mode 100644 index 0000000000..e027f55c48 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f4bc.png new file mode 100644 index 0000000000..df0c1a7d37 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f527.png new file mode 100644 index 0000000000..285e331669 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f52c.png new file mode 100644 index 0000000000..b5eaf05037 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f680.png new file mode 100644 index 0000000000..4d448a6483 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f692.png new file mode 100644 index 0000000000..a8f2467ea1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f91d-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f91d-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..ee0e2ec974 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f91d-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f91d-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f91d-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..4a1df9db54 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f91d-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f91d-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f91d-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..59d86c8ed7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f91d-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f91d-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f91d-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..0c87327c47 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f91d-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..7df47a7f0e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9af.png new file mode 100644 index 0000000000..8c67c0be0f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9b0.png new file mode 100644 index 0000000000..dac8c82948 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9b1.png new file mode 100644 index 0000000000..04735fd5c9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9b2.png new file mode 100644 index 0000000000..f3f3aeee9e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9b3.png new file mode 100644 index 0000000000..95b8518cd5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..d33d0e1d31 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9bc.png new file mode 100644 index 0000000000..ebde69b480 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..d14c99f404 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9bd.png new file mode 100644 index 0000000000..51320cf7c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1faef-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1faef-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..e2b3936875 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1faef-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1faef-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1faef-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..6683d6d100 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1faef-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1faef-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1faef-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..cf0c689b1a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1faef-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1faef-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1faef-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..2da0f40f9f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-1faef-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2695-fe0f.png new file mode 100644 index 0000000000..bd6e8f55d4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2696-fe0f.png new file mode 100644 index 0000000000..f2f4294516 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2708-fe0f.png new file mode 100644 index 0000000000..6dc6627855 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..8290495a65 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..b9262d870f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..0531846ca3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..fa219506ff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..c91e948cce Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..b12316d2e3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..4ff97b2f4d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..bc4bbde38c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..843df4d0b0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..c25225e4fc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb.png new file mode 100644 index 0000000000..2c36824e37 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f33e.png new file mode 100644 index 0000000000..5361fb1e76 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f373.png new file mode 100644 index 0000000000..43a434563a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f37c.png new file mode 100644 index 0000000000..17fcdd4524 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f384.png new file mode 100644 index 0000000000..ae1e9a8eef Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f393.png new file mode 100644 index 0000000000..77f968f1c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f3a4.png new file mode 100644 index 0000000000..52980df4eb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f3a8.png new file mode 100644 index 0000000000..2aee77b79a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f3eb.png new file mode 100644 index 0000000000..b88314d2f1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f3ed.png new file mode 100644 index 0000000000..3105701468 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f430-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f430-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..44b8547308 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f430-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f430-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f430-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..daab9e8443 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f430-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f430-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f430-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..f6ad5be908 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f430-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f430-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f430-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..6acafda3fe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f430-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f4bb.png new file mode 100644 index 0000000000..4cfa537593 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f4bc.png new file mode 100644 index 0000000000..65123e0f06 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f527.png new file mode 100644 index 0000000000..415644a413 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f52c.png new file mode 100644 index 0000000000..56f0e8bf79 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f680.png new file mode 100644 index 0000000000..3d45cf40fd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f692.png new file mode 100644 index 0000000000..b9322dd0ee Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f91d-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f91d-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..f72fc9e1d4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f91d-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f91d-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f91d-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..95c78cf0f1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f91d-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f91d-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f91d-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..0775407fd1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f91d-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f91d-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f91d-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..23be878bb3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f91d-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..cb061b5a49 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9af.png new file mode 100644 index 0000000000..41b92ea7fc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9b0.png new file mode 100644 index 0000000000..5344a5eb83 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9b1.png new file mode 100644 index 0000000000..555c452173 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9b2.png new file mode 100644 index 0000000000..dcd774d5f3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9b3.png new file mode 100644 index 0000000000..3ac9f2a521 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..77e5cba3b0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9bc.png new file mode 100644 index 0000000000..b25df4a150 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..c0e847b4b7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9bd.png new file mode 100644 index 0000000000..969888b99d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1faef-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1faef-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..77cb9f0762 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1faef-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1faef-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1faef-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..aa2a2b5278 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1faef-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1faef-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1faef-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..ad859d9171 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1faef-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1faef-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1faef-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..61df548a3d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-1faef-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2695-fe0f.png new file mode 100644 index 0000000000..ee4b0b2276 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2696-fe0f.png new file mode 100644 index 0000000000..df34a740ac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2708-fe0f.png new file mode 100644 index 0000000000..a6b365c661 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..d268d0385a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..9c3cbdc838 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..2398368367 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..7c247fb4a3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..24e5d43fd5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..bca840789e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..649b70d190 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..7649e7a3e3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..563216eb4b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..752afb8588 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc.png new file mode 100644 index 0000000000..a5197a24d5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f33e.png new file mode 100644 index 0000000000..433b19cf99 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f373.png new file mode 100644 index 0000000000..a04904eeea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f37c.png new file mode 100644 index 0000000000..798c17a2c7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f384.png new file mode 100644 index 0000000000..3cd9a51351 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f393.png new file mode 100644 index 0000000000..d48c7cf19a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f3a4.png new file mode 100644 index 0000000000..22ed9b589a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f3a8.png new file mode 100644 index 0000000000..cb83a98819 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f3eb.png new file mode 100644 index 0000000000..8980a62fec Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f3ed.png new file mode 100644 index 0000000000..2244d972a8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f430-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f430-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..c7d268e958 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f430-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f430-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f430-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..1483cf28ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f430-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f430-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f430-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..292841f536 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f430-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f430-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f430-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..027ff75a51 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f430-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f4bb.png new file mode 100644 index 0000000000..36c1040876 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f4bc.png new file mode 100644 index 0000000000..65475b2de6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f527.png new file mode 100644 index 0000000000..09e4853df7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f52c.png new file mode 100644 index 0000000000..9c670b2c4c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f680.png new file mode 100644 index 0000000000..9e911eca58 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f692.png new file mode 100644 index 0000000000..c7cef26f4c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f91d-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f91d-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..3a85784d20 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f91d-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f91d-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f91d-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..eeca65ba76 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f91d-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f91d-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f91d-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..6e802288da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f91d-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f91d-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f91d-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..f71b54bae5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f91d-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..2b5be13773 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9af.png new file mode 100644 index 0000000000..542dde3a9e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9b0.png new file mode 100644 index 0000000000..d30e0010a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9b1.png new file mode 100644 index 0000000000..a50bcdb773 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9b2.png new file mode 100644 index 0000000000..7f8c9c0343 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9b3.png new file mode 100644 index 0000000000..d9ab3d622c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..2bc64e0fd2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9bc.png new file mode 100644 index 0000000000..cd6f3ee38b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..7dc045a999 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9bd.png new file mode 100644 index 0000000000..bea6fd5f8f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1faef-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1faef-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..f699015030 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1faef-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1faef-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1faef-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..1fdcbb1346 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1faef-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1faef-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1faef-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..628d12bdfc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1faef-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1faef-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1faef-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..564efcef26 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-1faef-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2695-fe0f.png new file mode 100644 index 0000000000..43c4697839 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2696-fe0f.png new file mode 100644 index 0000000000..5ba5b8635f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2708-fe0f.png new file mode 100644 index 0000000000..6ed640ab1e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..b6c8a1110f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..92f4a52f4c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..651837977e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..6b72c3a5e5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..546203a463 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..b101259ad4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..bef3788ae0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..529c3e6041 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..59617840ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..8cb73c6954 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd.png new file mode 100644 index 0000000000..e4aea99247 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f33e.png new file mode 100644 index 0000000000..4288e466d1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f373.png new file mode 100644 index 0000000000..cbccc305ef Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f37c.png new file mode 100644 index 0000000000..daa4a1bd54 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f384.png new file mode 100644 index 0000000000..bbb8796fdb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f393.png new file mode 100644 index 0000000000..b6ea714bc6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f3a4.png new file mode 100644 index 0000000000..c354254bd6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f3a8.png new file mode 100644 index 0000000000..6aeda931f8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f3eb.png new file mode 100644 index 0000000000..ab2aa4994d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f3ed.png new file mode 100644 index 0000000000..421a07faa9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f430-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f430-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..1013358251 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f430-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f430-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f430-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..b4c1656bba Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f430-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f430-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f430-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..f5c53c78b6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f430-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f430-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f430-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..7a4967afcd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f430-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f4bb.png new file mode 100644 index 0000000000..a9e8269bd4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f4bc.png new file mode 100644 index 0000000000..767ad37fdb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f527.png new file mode 100644 index 0000000000..f1793d24f4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f52c.png new file mode 100644 index 0000000000..39830d4078 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f680.png new file mode 100644 index 0000000000..a19a44cb6e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f692.png new file mode 100644 index 0000000000..d6eb28613d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f91d-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f91d-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..7e58b7fb33 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f91d-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f91d-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f91d-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..c4c0bb7c27 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f91d-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f91d-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f91d-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..1c87ac4795 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f91d-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f91d-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f91d-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..9acfab532a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f91d-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..26cef5b0ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9af.png new file mode 100644 index 0000000000..ea907f31fb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9b0.png new file mode 100644 index 0000000000..e1e44448e9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9b1.png new file mode 100644 index 0000000000..1cd00e7767 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9b2.png new file mode 100644 index 0000000000..686089b88a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9b3.png new file mode 100644 index 0000000000..3820783ba8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..018067b5e1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9bc.png new file mode 100644 index 0000000000..4c01fea875 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..755178050b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9bd.png new file mode 100644 index 0000000000..4d89035705 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1faef-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1faef-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..5a387dc6d2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1faef-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1faef-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1faef-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..30ffaceeb6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1faef-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1faef-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1faef-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..9c681ae0ae Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1faef-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1faef-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1faef-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..640b009979 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-1faef-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2695-fe0f.png new file mode 100644 index 0000000000..7bbd95a815 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2696-fe0f.png new file mode 100644 index 0000000000..46dfbd0f1e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2708-fe0f.png new file mode 100644 index 0000000000..f51a7bb099 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..da5c2ec02b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..619e0c9dc2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..ee52f49cf5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..50d9c522f8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..da404b4182 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..9b1088d2dc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..4bafa7449d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..4ad28a205b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..e2e60de7f0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..06844e2d32 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe.png new file mode 100644 index 0000000000..98fd811ee3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f33e.png new file mode 100644 index 0000000000..017942bcd6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f373.png new file mode 100644 index 0000000000..8bd2733732 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f37c.png new file mode 100644 index 0000000000..8e30073f4f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f384.png new file mode 100644 index 0000000000..1432544fc2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f393.png new file mode 100644 index 0000000000..ef973d59b6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f3a4.png new file mode 100644 index 0000000000..839463e530 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f3a8.png new file mode 100644 index 0000000000..494c966bd7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f3eb.png new file mode 100644 index 0000000000..6687d31166 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f3ed.png new file mode 100644 index 0000000000..581f0ac8d5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f430-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f430-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..224ae5ac20 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f430-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f430-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f430-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..c664371559 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f430-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f430-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f430-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..eebad7bd4f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f430-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f430-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f430-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..aed1e1d21d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f430-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f4bb.png new file mode 100644 index 0000000000..475e95e11b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f4bc.png new file mode 100644 index 0000000000..ce338cf323 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f527.png new file mode 100644 index 0000000000..89f704df67 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f52c.png new file mode 100644 index 0000000000..2b592b89da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f680.png new file mode 100644 index 0000000000..c307fce21c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f692.png new file mode 100644 index 0000000000..fa5f4e772c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..99feb8229b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..2ec63cdce3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..6d7f9a24df Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..d7faf9293b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..e0745cb858 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9af.png new file mode 100644 index 0000000000..fd675d90c3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9b0.png new file mode 100644 index 0000000000..59c95dcd28 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9b1.png new file mode 100644 index 0000000000..d3f13d3979 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9b2.png new file mode 100644 index 0000000000..4948d26030 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9b3.png new file mode 100644 index 0000000000..b66b396f69 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..26c5f1a763 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9bc.png new file mode 100644 index 0000000000..bdcdcd1cde Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..f00b8bfc8c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9bd.png new file mode 100644 index 0000000000..041fd9f5b9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1faef-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1faef-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..33eb81a295 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1faef-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1faef-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1faef-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..d399b89ac4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1faef-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1faef-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1faef-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..6fba156c78 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1faef-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1faef-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1faef-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..521e770d2e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-1faef-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2695-fe0f.png new file mode 100644 index 0000000000..98c9f1b28e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2696-fe0f.png new file mode 100644 index 0000000000..ec57bb7631 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2708-fe0f.png new file mode 100644 index 0000000000..76e27a6cfa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..0c837fe51f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..e53c5b5ab0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..8217fadc48 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..7f6849e4be Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..62bce01c72 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..534a9fd574 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..64d4c1c1f7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..7e4cf9dff4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..7afaeaf87c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..741c39ca24 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff.png new file mode 100644 index 0000000000..94443e7b54 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f33e.png new file mode 100644 index 0000000000..7d7bc93834 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f373.png new file mode 100644 index 0000000000..370414efa8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f37c.png new file mode 100644 index 0000000000..38c86b6590 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f384.png new file mode 100644 index 0000000000..44e75755f2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f393.png new file mode 100644 index 0000000000..9cd13fdc72 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f3a4.png new file mode 100644 index 0000000000..e45c0323b4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f3a8.png new file mode 100644 index 0000000000..00af96eb89 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f3eb.png new file mode 100644 index 0000000000..fd1fd2a4f5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f3ed.png new file mode 100644 index 0000000000..e934a5ada3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f466-200d-1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f466-200d-1f466.png new file mode 100644 index 0000000000..ee87fb5f67 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f466-200d-1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f466.png new file mode 100644 index 0000000000..4b0224ae1c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f467-200d-1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f467-200d-1f466.png new file mode 100644 index 0000000000..ddcd333322 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f467-200d-1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f467-200d-1f467.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f467-200d-1f467.png new file mode 100644 index 0000000000..879886f688 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f467-200d-1f467.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f467.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f467.png new file mode 100644 index 0000000000..0a6f96ba17 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f467.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f468-200d-1f466-200d-1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f468-200d-1f466-200d-1f466.png new file mode 100644 index 0000000000..1b698206b0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f468-200d-1f466-200d-1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f468-200d-1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f468-200d-1f466.png new file mode 100644 index 0000000000..d4b9231acc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f468-200d-1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f468-200d-1f467-200d-1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f468-200d-1f467-200d-1f466.png new file mode 100644 index 0000000000..e5bea54b6a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f468-200d-1f467-200d-1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f468-200d-1f467-200d-1f467.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f468-200d-1f467-200d-1f467.png new file mode 100644 index 0000000000..b7a8e9a30a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f468-200d-1f467-200d-1f467.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f468-200d-1f467.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f468-200d-1f467.png new file mode 100644 index 0000000000..d2cbee8fe2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f468-200d-1f467.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f469-200d-1f466-200d-1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f469-200d-1f466-200d-1f466.png new file mode 100644 index 0000000000..dffdd9cc80 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f469-200d-1f466-200d-1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f469-200d-1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f469-200d-1f466.png new file mode 100644 index 0000000000..40d006d2ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f469-200d-1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f469-200d-1f467-200d-1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f469-200d-1f467-200d-1f466.png new file mode 100644 index 0000000000..7eb04555c3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f469-200d-1f467-200d-1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f469-200d-1f467-200d-1f467.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f469-200d-1f467-200d-1f467.png new file mode 100644 index 0000000000..d3571fa447 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f469-200d-1f467-200d-1f467.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f469-200d-1f467.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f469-200d-1f467.png new file mode 100644 index 0000000000..73fc6e578e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f469-200d-1f467.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f4bb.png new file mode 100644 index 0000000000..b55ddcb360 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f4bc.png new file mode 100644 index 0000000000..9b1e7dd167 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f527.png new file mode 100644 index 0000000000..d731a12c7c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f52c.png new file mode 100644 index 0000000000..10d2db918c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f680.png new file mode 100644 index 0000000000..698e909a12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f692.png new file mode 100644 index 0000000000..07fa090b4f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..2ea3def598 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9af.png new file mode 100644 index 0000000000..3672517803 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9b0.png new file mode 100644 index 0000000000..4d5ef0e840 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9b1.png new file mode 100644 index 0000000000..36afa4fe95 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9b2.png new file mode 100644 index 0000000000..8e3a838506 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9b3.png new file mode 100644 index 0000000000..8886449b99 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..57aa99a13e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9bc.png new file mode 100644 index 0000000000..d7110f072a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..62c0228551 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9bd.png new file mode 100644 index 0000000000..ce9c939e7c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-2695-fe0f.png new file mode 100644 index 0000000000..0155f25b7b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-2696-fe0f.png new file mode 100644 index 0000000000..ffba6a5cc3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-2708-fe0f.png new file mode 100644 index 0000000000..9f7bbb145f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-2764-fe0f-200d-1f468.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-2764-fe0f-200d-1f468.png new file mode 100644 index 0000000000..c332da0d55 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-2764-fe0f-200d-1f468.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-2764-fe0f-200d-1f48b-200d-1f468.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-2764-fe0f-200d-1f48b-200d-1f468.png new file mode 100644 index 0000000000..619116ee22 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468-200d-2764-fe0f-200d-1f48b-200d-1f468.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f468.png b/backend/Shared/CIPPSharp/bin/twemoji/1f468.png new file mode 100644 index 0000000000..8ebc6be4ae Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f468.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f33e.png new file mode 100644 index 0000000000..01336cf2b6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f373.png new file mode 100644 index 0000000000..f2772df0d6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f37c.png new file mode 100644 index 0000000000..f14e525bd5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f384.png new file mode 100644 index 0000000000..6d3c340692 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f393.png new file mode 100644 index 0000000000..b8dda6f8c9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f3a4.png new file mode 100644 index 0000000000..ebeac884bd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f3a8.png new file mode 100644 index 0000000000..a8130c1f2e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f3eb.png new file mode 100644 index 0000000000..ec83c4c826 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f3ed.png new file mode 100644 index 0000000000..8f8f9062a3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f430-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f430-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..b67c2aa3e3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f430-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f430-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f430-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..01c5669455 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f430-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f430-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f430-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..ec00b0c12a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f430-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f430-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f430-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..0af5779799 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f430-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f4bb.png new file mode 100644 index 0000000000..1b4de1c454 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f4bc.png new file mode 100644 index 0000000000..c48aabe00b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f527.png new file mode 100644 index 0000000000..0bdb1bb66b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f52c.png new file mode 100644 index 0000000000..3af32eeddb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f680.png new file mode 100644 index 0000000000..3279782af1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f692.png new file mode 100644 index 0000000000..81148034d9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..04d54b11f6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..634db3cf5f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..89c6c6dfe2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..a93dbebd06 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..5e77313428 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..41834dd9ff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..72a9f81285 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..68bf8a6891 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f91d-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..508c94ee33 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9af.png new file mode 100644 index 0000000000..3e532dbcdd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9b0.png new file mode 100644 index 0000000000..189c8458f5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9b1.png new file mode 100644 index 0000000000..3a9bee7f4e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9b2.png new file mode 100644 index 0000000000..9c6334028b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9b3.png new file mode 100644 index 0000000000..d7ba9dd493 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..fa28f7e63f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9bc.png new file mode 100644 index 0000000000..840f5d1d37 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..181b8a343e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9bd.png new file mode 100644 index 0000000000..5d3b083b4c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1faef-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1faef-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..c05962ed3d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1faef-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1faef-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1faef-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..905963c415 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1faef-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1faef-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1faef-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..76c7111bc8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1faef-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1faef-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1faef-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..130de56844 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-1faef-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2695-fe0f.png new file mode 100644 index 0000000000..57c7ce71e1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2696-fe0f.png new file mode 100644 index 0000000000..0b91b7b5da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2708-fe0f.png new file mode 100644 index 0000000000..52f3f5ac25 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..6abeec5e11 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..68839e3153 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..d7b5854e88 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..4df2de094c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..ea30ee7159 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..b75ca49c72 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..684f958f46 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..f7c8955059 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..d1d44f460f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..2b42948594 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..eeeb5612db Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..cc49c8299c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..248c9382f5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..fb9b86989e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..96a9871423 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..dcd0954ff5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..91df6227b3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..fbafff818e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..98e2a42003 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..fc113be7c2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb.png new file mode 100644 index 0000000000..6fe70fb42a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f33e.png new file mode 100644 index 0000000000..fdd7276829 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f373.png new file mode 100644 index 0000000000..b49e212976 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f37c.png new file mode 100644 index 0000000000..be4de07d79 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f384.png new file mode 100644 index 0000000000..52c9ba810d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f393.png new file mode 100644 index 0000000000..83fcaee79d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f3a4.png new file mode 100644 index 0000000000..376945c0f4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f3a8.png new file mode 100644 index 0000000000..c60866bbc3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f3eb.png new file mode 100644 index 0000000000..77f9248f4f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f3ed.png new file mode 100644 index 0000000000..9c1e189d53 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f430-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f430-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..e53f02c435 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f430-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f430-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f430-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..d52df03f2d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f430-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f430-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f430-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..5f9421ea16 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f430-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f430-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f430-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..272c6a4cc8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f430-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f4bb.png new file mode 100644 index 0000000000..8d5d19e692 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f4bc.png new file mode 100644 index 0000000000..464266db06 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f527.png new file mode 100644 index 0000000000..a7227fa905 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f52c.png new file mode 100644 index 0000000000..98c2b0578b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f680.png new file mode 100644 index 0000000000..ac9ae0e67d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f692.png new file mode 100644 index 0000000000..b68993e525 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..abb332bb87 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..5f6a93568f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..a00d6d7da8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..94502bbc28 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..f1580c23ed Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..d73b98c433 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..a356909601 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..377891fd05 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f91d-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..24307e3447 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9af.png new file mode 100644 index 0000000000..598edf8d97 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9b0.png new file mode 100644 index 0000000000..d4453666b3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9b1.png new file mode 100644 index 0000000000..00e006de0f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9b2.png new file mode 100644 index 0000000000..47ef56b31a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9b3.png new file mode 100644 index 0000000000..30aece9450 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..9015d7aa92 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9bc.png new file mode 100644 index 0000000000..1522085332 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..c69fa8d6fa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9bd.png new file mode 100644 index 0000000000..3b739dacfc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1faef-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1faef-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..06abad9fb6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1faef-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1faef-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1faef-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..20bd3a1f7d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1faef-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1faef-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1faef-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..dd85f78378 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1faef-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1faef-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1faef-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..91c559c2dc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-1faef-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2695-fe0f.png new file mode 100644 index 0000000000..256743387e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2696-fe0f.png new file mode 100644 index 0000000000..44a0942517 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2708-fe0f.png new file mode 100644 index 0000000000..afe2b71edd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..31285f92fe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..521cde1def Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..3feae07a6b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..49c1c5d66d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..c2a3bc4a71 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..349fcbde31 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..f8ad5951da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..453036d730 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..c1db221991 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..2355698dc5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..e4364650ef Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..dc588d402d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..6fcaba1672 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..9947bd5865 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..389c1ff7bd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..6c5c51161b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..5dca5078ec Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..d94389a658 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..37d3eedb0e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..a5b00492ce Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc.png new file mode 100644 index 0000000000..ef30b5b50f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f33e.png new file mode 100644 index 0000000000..ab863a38dd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f373.png new file mode 100644 index 0000000000..d8cb716253 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f37c.png new file mode 100644 index 0000000000..f6380c01cf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f384.png new file mode 100644 index 0000000000..9651385708 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f393.png new file mode 100644 index 0000000000..1d75113f5c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f3a4.png new file mode 100644 index 0000000000..aeee09c435 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f3a8.png new file mode 100644 index 0000000000..dc3d89a35e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f3eb.png new file mode 100644 index 0000000000..5d78b83432 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f3ed.png new file mode 100644 index 0000000000..1b5be523ba Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f430-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f430-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..8a6e9357a5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f430-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f430-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f430-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..a3552fc015 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f430-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f430-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f430-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..5e366f800a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f430-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f430-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f430-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..1d3e424862 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f430-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f4bb.png new file mode 100644 index 0000000000..4ee8b25a58 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f4bc.png new file mode 100644 index 0000000000..24b42f4d30 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f527.png new file mode 100644 index 0000000000..30a610ea22 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f52c.png new file mode 100644 index 0000000000..240e843f7c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f680.png new file mode 100644 index 0000000000..e8bc3b12e6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f692.png new file mode 100644 index 0000000000..4ca5e00308 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..7db467b58a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..59bf913230 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..9cf5710191 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..ad588fcd75 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..64a48c6aa5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..9068a99a0e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..d705ac79ce Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..792b28361d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f91d-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..cf27ce73fc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9af.png new file mode 100644 index 0000000000..71a6adab9c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9b0.png new file mode 100644 index 0000000000..36b5bbd70f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9b1.png new file mode 100644 index 0000000000..82049eb799 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9b2.png new file mode 100644 index 0000000000..c66fd3fdd1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9b3.png new file mode 100644 index 0000000000..6feda96ac2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..d4512140e9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9bc.png new file mode 100644 index 0000000000..9ff345026e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..53f022c093 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9bd.png new file mode 100644 index 0000000000..432260e9bb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1faef-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1faef-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..d1aa282fc0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1faef-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1faef-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1faef-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..3bc22f22d4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1faef-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1faef-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1faef-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..e98018a5df Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1faef-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1faef-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1faef-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..2b8fe5916d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-1faef-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2695-fe0f.png new file mode 100644 index 0000000000..ccf237ccb2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2696-fe0f.png new file mode 100644 index 0000000000..6f56b22ae2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2708-fe0f.png new file mode 100644 index 0000000000..45c0a24913 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..206e1a5623 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..02bd684d58 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..d54a5b4f5f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..c6cdea3693 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..0d822f8189 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..f606a06bd2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..7fcb5a963b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..c113409044 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..e6966af61a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..33337f36c0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..0ee1621a52 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..11982d12a5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..ea319f30e3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..80a370409f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..510571ddce Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..3126c6c1af Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..98fab6f963 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..99f90fb6f4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..b25ef75e8a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..d735157eb7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd.png new file mode 100644 index 0000000000..c5f68bae0f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f33e.png new file mode 100644 index 0000000000..520a84bdb8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f373.png new file mode 100644 index 0000000000..00860e85ee Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f37c.png new file mode 100644 index 0000000000..2941a44c5e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f384.png new file mode 100644 index 0000000000..3ec0c5686e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f393.png new file mode 100644 index 0000000000..5740309845 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f3a4.png new file mode 100644 index 0000000000..3897fa2d2a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f3a8.png new file mode 100644 index 0000000000..5521daed8a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f3eb.png new file mode 100644 index 0000000000..362323f42c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f3ed.png new file mode 100644 index 0000000000..0dc2e09e25 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f430-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f430-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..f592eeab62 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f430-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f430-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f430-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..b4ef338e68 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f430-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f430-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f430-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..acf8a2ca52 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f430-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f430-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f430-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..575912acac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f430-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f4bb.png new file mode 100644 index 0000000000..6107c8305f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f4bc.png new file mode 100644 index 0000000000..b4a3d21965 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f527.png new file mode 100644 index 0000000000..45ef04294d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f52c.png new file mode 100644 index 0000000000..d808b83cdf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f680.png new file mode 100644 index 0000000000..b81db8a6a9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f692.png new file mode 100644 index 0000000000..e8ebea98ad Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..96bab42fe3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..ec63a8daf9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..e28c33145b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..4a44a039d2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..97a1b798ae Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..88eb58af4b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..09992c848d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..2865b2d0cc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f91d-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..1865c5c0fd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9af.png new file mode 100644 index 0000000000..1a868a9429 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9b0.png new file mode 100644 index 0000000000..ea95260345 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9b1.png new file mode 100644 index 0000000000..fcfa7dd7a3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9b2.png new file mode 100644 index 0000000000..7b8d469877 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9b3.png new file mode 100644 index 0000000000..2adad8ef1c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..1c2053756d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9bc.png new file mode 100644 index 0000000000..b2f38a4619 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..11d7be87cd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9bd.png new file mode 100644 index 0000000000..31f9f77e3a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1faef-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1faef-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..77f75ac711 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1faef-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1faef-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1faef-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..237b9c0b7f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1faef-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1faef-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1faef-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..fb48616f58 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1faef-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1faef-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1faef-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..5f54ba5545 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-1faef-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2695-fe0f.png new file mode 100644 index 0000000000..1e54df038d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2696-fe0f.png new file mode 100644 index 0000000000..7172918434 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2708-fe0f.png new file mode 100644 index 0000000000..c07612d8a7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..39ad0efe96 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..8d9e5ee9a2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..7aebfc0948 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..e1a35a7716 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..ef0e889fe6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..13955c5281 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..673dff4199 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..8e965d3e40 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..dfaeda2a7d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..4137f157b9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..be6712935f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..d12011600d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..3457dd389f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..34c45bcee9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..74e69f5ad2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..3cdcd5d25d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..a2e182d230 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..3a0cbc1561 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..c919434ea4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..60acfb5941 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe.png new file mode 100644 index 0000000000..294369061f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f33e.png new file mode 100644 index 0000000000..7e6a26f7d3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f373.png new file mode 100644 index 0000000000..6bc9ea8a92 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f37c.png new file mode 100644 index 0000000000..519d8e43ac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f384.png new file mode 100644 index 0000000000..f86966be83 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f393.png new file mode 100644 index 0000000000..60292568c1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f3a4.png new file mode 100644 index 0000000000..91976880ed Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f3a8.png new file mode 100644 index 0000000000..1217f191a7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f3eb.png new file mode 100644 index 0000000000..b8cae5d44e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f3ed.png new file mode 100644 index 0000000000..e93b848a3c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f430-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f430-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..4f633e5974 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f430-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f430-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f430-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..98cf5a1172 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f430-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f430-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f430-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..2ea21e9292 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f430-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f430-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f430-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..6f0aaa14a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f430-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f4bb.png new file mode 100644 index 0000000000..0bfedd67cd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f4bc.png new file mode 100644 index 0000000000..81b90ae238 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f527.png new file mode 100644 index 0000000000..a03fdb7470 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f52c.png new file mode 100644 index 0000000000..bddc3c88f2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f680.png new file mode 100644 index 0000000000..e2609e7f21 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f692.png new file mode 100644 index 0000000000..a41c8c3f18 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..b09e52c1df Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..52b1a7faf3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..06fb17dbce Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..a55b2981f5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..0354f5130e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..a0be380ae8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..4328159c2e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..80d7e7fa78 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..7d09962175 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9af.png new file mode 100644 index 0000000000..cd03b2dd09 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9b0.png new file mode 100644 index 0000000000..805814296d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9b1.png new file mode 100644 index 0000000000..7ae6c1ff7c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9b2.png new file mode 100644 index 0000000000..ced00feafd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9b3.png new file mode 100644 index 0000000000..fdc58db65e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..8fd25c2b63 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9bc.png new file mode 100644 index 0000000000..0b0320ccc2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..1b821c0540 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9bd.png new file mode 100644 index 0000000000..9af884f32e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1faef-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1faef-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..99fbcb06a4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1faef-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1faef-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1faef-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..a5e4fd21ac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1faef-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1faef-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1faef-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..a324ec8c9a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1faef-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1faef-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1faef-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..56779c0a96 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-1faef-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2695-fe0f.png new file mode 100644 index 0000000000..16435b24c1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2696-fe0f.png new file mode 100644 index 0000000000..b3651d1e90 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2708-fe0f.png new file mode 100644 index 0000000000..798cd2a8be Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..3012fc53ba Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..c2d672ca47 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..63b6d1058f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..7fb5c124d7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..e796b055f2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..a95a138db5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..8a6ae7b04d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..eba466bf62 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..db682f62eb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..4890f46687 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png new file mode 100644 index 0000000000..0468d7bf37 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png new file mode 100644 index 0000000000..0bead8d1bc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png new file mode 100644 index 0000000000..187f23270a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png new file mode 100644 index 0000000000..9f920bdd86 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png new file mode 100644 index 0000000000..de2699dc8f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png new file mode 100644 index 0000000000..48b888bca2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png new file mode 100644 index 0000000000..cb68cc6a4c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png new file mode 100644 index 0000000000..aa06a8bcb2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png new file mode 100644 index 0000000000..1f7a610e30 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png new file mode 100644 index 0000000000..663dc3885c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff.png new file mode 100644 index 0000000000..3d2c226201 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f33e.png new file mode 100644 index 0000000000..039da46e99 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f373.png new file mode 100644 index 0000000000..6a2f087204 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f37c.png new file mode 100644 index 0000000000..aadeca6076 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f384.png new file mode 100644 index 0000000000..56dc794884 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f393.png new file mode 100644 index 0000000000..86f0cefe9a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f3a4.png new file mode 100644 index 0000000000..06d534d439 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f3a8.png new file mode 100644 index 0000000000..7c50ca6ca4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f3eb.png new file mode 100644 index 0000000000..b4a6c798af Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f3ed.png new file mode 100644 index 0000000000..c3a0396c11 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f466-200d-1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f466-200d-1f466.png new file mode 100644 index 0000000000..a5dc61401a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f466-200d-1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f466.png new file mode 100644 index 0000000000..9dd4a0fb62 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f467-200d-1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f467-200d-1f466.png new file mode 100644 index 0000000000..b0b4723200 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f467-200d-1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f467-200d-1f467.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f467-200d-1f467.png new file mode 100644 index 0000000000..6f77e1fdfb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f467-200d-1f467.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f467.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f467.png new file mode 100644 index 0000000000..972cf4b4b7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f467.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f469-200d-1f466-200d-1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f469-200d-1f466-200d-1f466.png new file mode 100644 index 0000000000..00372fcf3c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f469-200d-1f466-200d-1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f469-200d-1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f469-200d-1f466.png new file mode 100644 index 0000000000..0cbbd88290 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f469-200d-1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f469-200d-1f467-200d-1f466.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f469-200d-1f467-200d-1f466.png new file mode 100644 index 0000000000..ea533a05a1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f469-200d-1f467-200d-1f466.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f469-200d-1f467-200d-1f467.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f469-200d-1f467-200d-1f467.png new file mode 100644 index 0000000000..d58eb387aa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f469-200d-1f467-200d-1f467.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f469-200d-1f467.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f469-200d-1f467.png new file mode 100644 index 0000000000..057cab2e44 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f469-200d-1f467.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f4bb.png new file mode 100644 index 0000000000..b9471c2dc6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f4bc.png new file mode 100644 index 0000000000..1646ce6498 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f527.png new file mode 100644 index 0000000000..95e2c6589b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f52c.png new file mode 100644 index 0000000000..af9c4fe71f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f680.png new file mode 100644 index 0000000000..d1a0dc9c64 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f692.png new file mode 100644 index 0000000000..282f50cb47 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..e020a5abd1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9af.png new file mode 100644 index 0000000000..4c2c23eb66 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9b0.png new file mode 100644 index 0000000000..373c1c537b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9b1.png new file mode 100644 index 0000000000..80ca4a5c3b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9b2.png new file mode 100644 index 0000000000..2fe69f9458 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9b3.png new file mode 100644 index 0000000000..98de2e9c3e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..f977c402fb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9bc.png new file mode 100644 index 0000000000..8f9a994642 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..5c3783fce9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9bd.png new file mode 100644 index 0000000000..23b365ddc0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2695-fe0f.png new file mode 100644 index 0000000000..c33e27829f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2696-fe0f.png new file mode 100644 index 0000000000..65e7b72694 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2708-fe0f.png new file mode 100644 index 0000000000..ccb503b8b2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2764-fe0f-200d-1f468.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2764-fe0f-200d-1f468.png new file mode 100644 index 0000000000..d559bec4b4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2764-fe0f-200d-1f468.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2764-fe0f-200d-1f469.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2764-fe0f-200d-1f469.png new file mode 100644 index 0000000000..42400b469b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2764-fe0f-200d-1f469.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2764-fe0f-200d-1f48b-200d-1f468.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2764-fe0f-200d-1f48b-200d-1f468.png new file mode 100644 index 0000000000..bced8de9f4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2764-fe0f-200d-1f48b-200d-1f468.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2764-fe0f-200d-1f48b-200d-1f469.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2764-fe0f-200d-1f48b-200d-1f469.png new file mode 100644 index 0000000000..8055e53c7b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469-200d-2764-fe0f-200d-1f48b-200d-1f469.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f469.png b/backend/Shared/CIPPSharp/bin/twemoji/1f469.png new file mode 100644 index 0000000000..918d94f8a4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f469.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46a.png new file mode 100644 index 0000000000..d11a22ec6e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46b-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46b-1f3fb.png new file mode 100644 index 0000000000..c82b331ae0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46b-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46b-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46b-1f3fc.png new file mode 100644 index 0000000000..d756e1a64e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46b-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46b-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46b-1f3fd.png new file mode 100644 index 0000000000..bd6315f21b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46b-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46b-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46b-1f3fe.png new file mode 100644 index 0000000000..75c3f3391c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46b-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46b-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46b-1f3ff.png new file mode 100644 index 0000000000..8d46caeacb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46b-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46b.png new file mode 100644 index 0000000000..dd41b77856 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46c-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46c-1f3fb.png new file mode 100644 index 0000000000..7a7105d5ce Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46c-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46c-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46c-1f3fc.png new file mode 100644 index 0000000000..f3f01894ec Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46c-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46c-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46c-1f3fd.png new file mode 100644 index 0000000000..5a3839e45d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46c-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46c-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46c-1f3fe.png new file mode 100644 index 0000000000..c24f08b025 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46c-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46c-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46c-1f3ff.png new file mode 100644 index 0000000000..2b04da70d3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46c-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46c.png new file mode 100644 index 0000000000..63649e2bc1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46d-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46d-1f3fb.png new file mode 100644 index 0000000000..9eb93c9b35 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46d-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46d-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46d-1f3fc.png new file mode 100644 index 0000000000..51a33eebf4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46d-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46d-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46d-1f3fd.png new file mode 100644 index 0000000000..47ade25069 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46d-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46d-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46d-1f3fe.png new file mode 100644 index 0000000000..1ac7b9e6ce Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46d-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46d-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46d-1f3ff.png new file mode 100644 index 0000000000..bc5432db84 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46d-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46d.png new file mode 100644 index 0000000000..5dbc59dacb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..3a6da267e6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..70042a6aa0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fb.png new file mode 100644 index 0000000000..0c8e6410c4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..6175b1d664 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..7a8fda8766 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fc.png new file mode 100644 index 0000000000..7b5c5252cd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..8e2c504b4d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..33f8980e71 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fd.png new file mode 100644 index 0000000000..f9e093e9f4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..df3dad6aac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..2baa54ab5a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fe.png new file mode 100644 index 0000000000..8275ab3b11 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..9098ad2850 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..e31a033ba3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3ff.png new file mode 100644 index 0000000000..5e0e1f1d9a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-200d-2640-fe0f.png new file mode 100644 index 0000000000..13ed498d63 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-200d-2642-fe0f.png new file mode 100644 index 0000000000..acd2111f61 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46e.png new file mode 100644 index 0000000000..7093bb3cd6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..3f3ca7501e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..c3e5b8dd33 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fb.png new file mode 100644 index 0000000000..7ff623d607 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..0c0b35c151 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..59121b11a1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fc.png new file mode 100644 index 0000000000..30442cbaf3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..1bd8515100 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..89980b0c13 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fd.png new file mode 100644 index 0000000000..4aa486889d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..db6c77f19f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..f2cd5c54ba Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fe.png new file mode 100644 index 0000000000..e006614e20 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..0925233ef7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..c2cbf77778 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3ff.png new file mode 100644 index 0000000000..12be93bb2e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-200d-2640-fe0f.png new file mode 100644 index 0000000000..e71b3cc1c0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-200d-2642-fe0f.png new file mode 100644 index 0000000000..13fb047fb5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f46f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f46f.png new file mode 100644 index 0000000000..5b84d39040 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f46f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..e3a5d3ff7c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..af472a42b3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fb.png new file mode 100644 index 0000000000..c28329b037 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..b054d60faf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..954b610ce3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fc.png new file mode 100644 index 0000000000..c231a438c5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..29159df5f2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..39a13a4b27 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fd.png new file mode 100644 index 0000000000..5dcec53626 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..bf45ade5f3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..f7691ee7c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fe.png new file mode 100644 index 0000000000..0f8a2fb28a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..86e4a38016 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..464cd6cc44 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3ff.png new file mode 100644 index 0000000000..9e7cff0276 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-200d-2640-fe0f.png new file mode 100644 index 0000000000..dafe0b4746 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470-200d-2642-fe0f.png new file mode 100644 index 0000000000..278ac8d861 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f470.png b/backend/Shared/CIPPSharp/bin/twemoji/1f470.png new file mode 100644 index 0000000000..3adfd123ed Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f470.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..06e54a10b7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..9da7e3f7fb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fb.png new file mode 100644 index 0000000000..9a9dbe92eb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..0f58537be8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..7ff9efd4da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fc.png new file mode 100644 index 0000000000..01319fe842 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..377a236450 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..5780e5eb27 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fd.png new file mode 100644 index 0000000000..e28275a14b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..e5856a3a90 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..60694c689f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fe.png new file mode 100644 index 0000000000..599b7ba9d9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..15a0886ea9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..91e94883a8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3ff.png new file mode 100644 index 0000000000..56bd2f0040 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-200d-2640-fe0f.png new file mode 100644 index 0000000000..59f40b86e3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471-200d-2642-fe0f.png new file mode 100644 index 0000000000..d4c3407b5c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f471.png b/backend/Shared/CIPPSharp/bin/twemoji/1f471.png new file mode 100644 index 0000000000..2825ca0ec1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f471.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f472-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f472-1f3fb.png new file mode 100644 index 0000000000..7330ddb392 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f472-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f472-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f472-1f3fc.png new file mode 100644 index 0000000000..46758f8e86 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f472-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f472-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f472-1f3fd.png new file mode 100644 index 0000000000..ff46804732 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f472-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f472-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f472-1f3fe.png new file mode 100644 index 0000000000..a8e65ba81d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f472-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f472-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f472-1f3ff.png new file mode 100644 index 0000000000..ade1565c18 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f472-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f472.png b/backend/Shared/CIPPSharp/bin/twemoji/1f472.png new file mode 100644 index 0000000000..818c6fac4b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f472.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..255fa6adba Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..42453c6fdb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fb.png new file mode 100644 index 0000000000..44096010ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..2c6fc0b44e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..5b6257f79d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fc.png new file mode 100644 index 0000000000..9287f432ac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..5114a3f61c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..5bb1f8422a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fd.png new file mode 100644 index 0000000000..64ec778f7c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..dc8229ff0b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..a7a297c795 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fe.png new file mode 100644 index 0000000000..bd69b7bfea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..455c763e58 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..4cb5d7dd52 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3ff.png new file mode 100644 index 0000000000..89e31c4bbb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-200d-2640-fe0f.png new file mode 100644 index 0000000000..102b138b6d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473-200d-2642-fe0f.png new file mode 100644 index 0000000000..81b794e3ed Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f473.png b/backend/Shared/CIPPSharp/bin/twemoji/1f473.png new file mode 100644 index 0000000000..793d62fa6c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f473.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f474-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f474-1f3fb.png new file mode 100644 index 0000000000..75647b8dbc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f474-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f474-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f474-1f3fc.png new file mode 100644 index 0000000000..35d32ce14e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f474-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f474-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f474-1f3fd.png new file mode 100644 index 0000000000..2bed5c6048 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f474-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f474-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f474-1f3fe.png new file mode 100644 index 0000000000..6b70028287 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f474-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f474-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f474-1f3ff.png new file mode 100644 index 0000000000..2fb3fb1ce1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f474-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f474.png b/backend/Shared/CIPPSharp/bin/twemoji/1f474.png new file mode 100644 index 0000000000..5650d2b0c2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f474.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f475-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f475-1f3fb.png new file mode 100644 index 0000000000..dda546263e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f475-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f475-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f475-1f3fc.png new file mode 100644 index 0000000000..a73b9f647f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f475-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f475-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f475-1f3fd.png new file mode 100644 index 0000000000..fe04b280e9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f475-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f475-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f475-1f3fe.png new file mode 100644 index 0000000000..f2ecc40556 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f475-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f475-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f475-1f3ff.png new file mode 100644 index 0000000000..d10982f399 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f475-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f475.png b/backend/Shared/CIPPSharp/bin/twemoji/1f475.png new file mode 100644 index 0000000000..e9d04f9e69 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f475.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f476-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f476-1f3fb.png new file mode 100644 index 0000000000..949fd809cb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f476-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f476-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f476-1f3fc.png new file mode 100644 index 0000000000..d06f285a2c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f476-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f476-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f476-1f3fd.png new file mode 100644 index 0000000000..4da9b776d7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f476-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f476-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f476-1f3fe.png new file mode 100644 index 0000000000..14f78b00e9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f476-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f476-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f476-1f3ff.png new file mode 100644 index 0000000000..2008c1726d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f476-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f476.png b/backend/Shared/CIPPSharp/bin/twemoji/1f476.png new file mode 100644 index 0000000000..f093e36bb9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f476.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..abb56fda88 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..25f1238cf0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fb.png new file mode 100644 index 0000000000..069befedd3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..f01d7faf54 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..03035e37c5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fc.png new file mode 100644 index 0000000000..aa57d01352 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..a9a6221911 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..1475fc9949 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fd.png new file mode 100644 index 0000000000..3c96be1b8d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..72172594ed Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..2083c8be7c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fe.png new file mode 100644 index 0000000000..72ea66cb29 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..c818e20b45 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..627ad31417 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3ff.png new file mode 100644 index 0000000000..3b934e5d11 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-200d-2640-fe0f.png new file mode 100644 index 0000000000..5a3ed34ad4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477-200d-2642-fe0f.png new file mode 100644 index 0000000000..fe6733ec0c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f477.png b/backend/Shared/CIPPSharp/bin/twemoji/1f477.png new file mode 100644 index 0000000000..49aef2f636 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f477.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f478-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f478-1f3fb.png new file mode 100644 index 0000000000..4caae554c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f478-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f478-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f478-1f3fc.png new file mode 100644 index 0000000000..ad23227906 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f478-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f478-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f478-1f3fd.png new file mode 100644 index 0000000000..61dcaca7b9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f478-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f478-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f478-1f3fe.png new file mode 100644 index 0000000000..71c963faed Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f478-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f478-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f478-1f3ff.png new file mode 100644 index 0000000000..7579ff1914 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f478-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f478.png b/backend/Shared/CIPPSharp/bin/twemoji/1f478.png new file mode 100644 index 0000000000..6542a8d9b0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f478.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f479.png b/backend/Shared/CIPPSharp/bin/twemoji/1f479.png new file mode 100644 index 0000000000..e9ca4c6f3a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f479.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f47a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f47a.png new file mode 100644 index 0000000000..a479705e10 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f47a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f47b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f47b.png new file mode 100644 index 0000000000..93eebb055c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f47b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f47c-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f47c-1f3fb.png new file mode 100644 index 0000000000..161df6e791 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f47c-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f47c-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f47c-1f3fc.png new file mode 100644 index 0000000000..e72aa44c89 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f47c-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f47c-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f47c-1f3fd.png new file mode 100644 index 0000000000..0cd969ba94 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f47c-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f47c-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f47c-1f3fe.png new file mode 100644 index 0000000000..e82ae008cb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f47c-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f47c-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f47c-1f3ff.png new file mode 100644 index 0000000000..ecf1950186 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f47c-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f47c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f47c.png new file mode 100644 index 0000000000..584cd15d4d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f47c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f47d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f47d.png new file mode 100644 index 0000000000..e722644aa2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f47d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f47e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f47e.png new file mode 100644 index 0000000000..043e4950ac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f47e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f47f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f47f.png new file mode 100644 index 0000000000..5bc58b3ddd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f47f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f480.png b/backend/Shared/CIPPSharp/bin/twemoji/1f480.png new file mode 100644 index 0000000000..e9f9e7fc45 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f480.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..6ff67d24b7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..9477058016 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fb.png new file mode 100644 index 0000000000..24d5128f90 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..2198601817 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..0242604a63 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fc.png new file mode 100644 index 0000000000..96fb262c30 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..22a5d00a30 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..ec70756513 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fd.png new file mode 100644 index 0000000000..115223a816 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..1bfc1fdb67 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..ace710f49d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fe.png new file mode 100644 index 0000000000..668de97a58 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..f475567604 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..92611e2014 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3ff.png new file mode 100644 index 0000000000..440c65c5eb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-200d-2640-fe0f.png new file mode 100644 index 0000000000..1669256349 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481-200d-2642-fe0f.png new file mode 100644 index 0000000000..0c1369ab73 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f481.png b/backend/Shared/CIPPSharp/bin/twemoji/1f481.png new file mode 100644 index 0000000000..eca519992a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f481.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..757e031bc4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..7769d53537 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fb.png new file mode 100644 index 0000000000..73e15cdfb1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..52bb7244e2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..1664fa8df5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fc.png new file mode 100644 index 0000000000..df444ad577 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..20ceadf907 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..5cf7987069 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fd.png new file mode 100644 index 0000000000..9cafd1242b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..78671ba2d3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..37e92ac881 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fe.png new file mode 100644 index 0000000000..0324eed397 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..f2e824f8af Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..6d658ba383 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3ff.png new file mode 100644 index 0000000000..23fcb9b4b9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-200d-2640-fe0f.png new file mode 100644 index 0000000000..db3181a9e1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482-200d-2642-fe0f.png new file mode 100644 index 0000000000..954eee8342 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f482.png b/backend/Shared/CIPPSharp/bin/twemoji/1f482.png new file mode 100644 index 0000000000..b5a7d423ac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f482.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f483-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f483-1f3fb.png new file mode 100644 index 0000000000..ec9955f575 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f483-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f483-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f483-1f3fc.png new file mode 100644 index 0000000000..e1768ae610 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f483-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f483-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f483-1f3fd.png new file mode 100644 index 0000000000..885fc7edb2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f483-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f483-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f483-1f3fe.png new file mode 100644 index 0000000000..824b1cdad4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f483-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f483-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f483-1f3ff.png new file mode 100644 index 0000000000..2288e55a25 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f483-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f483.png b/backend/Shared/CIPPSharp/bin/twemoji/1f483.png new file mode 100644 index 0000000000..758c9b077d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f483.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f484.png b/backend/Shared/CIPPSharp/bin/twemoji/1f484.png new file mode 100644 index 0000000000..6427cf477a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f484.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f485-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f485-1f3fb.png new file mode 100644 index 0000000000..94c08c55de Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f485-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f485-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f485-1f3fc.png new file mode 100644 index 0000000000..46c13f0da2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f485-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f485-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f485-1f3fd.png new file mode 100644 index 0000000000..dc23af7844 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f485-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f485-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f485-1f3fe.png new file mode 100644 index 0000000000..1b96440843 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f485-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f485-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f485-1f3ff.png new file mode 100644 index 0000000000..32d637ccf3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f485-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f485.png b/backend/Shared/CIPPSharp/bin/twemoji/1f485.png new file mode 100644 index 0000000000..cba5a05fcf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f485.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..9358a2510a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..3e1a5eafb6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fb.png new file mode 100644 index 0000000000..7efac7579f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..82f2f6a48d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..734dce274e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fc.png new file mode 100644 index 0000000000..a021bd2050 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..f95d2d1349 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..5a43d75899 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fd.png new file mode 100644 index 0000000000..e4ebf9678b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..efd42dbadb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..0225d35992 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fe.png new file mode 100644 index 0000000000..e64dde84c4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..dae49c589d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..31e00a02ef Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3ff.png new file mode 100644 index 0000000000..cb742c7246 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-200d-2640-fe0f.png new file mode 100644 index 0000000000..8c4b552418 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486-200d-2642-fe0f.png new file mode 100644 index 0000000000..428e9688d8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f486.png b/backend/Shared/CIPPSharp/bin/twemoji/1f486.png new file mode 100644 index 0000000000..20eac93bc8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f486.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..d43b5be286 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..28a8f35873 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fb.png new file mode 100644 index 0000000000..7d257dfd55 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..268ed6a121 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..99e8cf9544 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fc.png new file mode 100644 index 0000000000..c85ad00c52 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..7b8a067c08 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..ea5d33fa13 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fd.png new file mode 100644 index 0000000000..6210438abf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..5a6ce144d8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..b3c78959de Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fe.png new file mode 100644 index 0000000000..b02ae7a614 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..0a8e1f3b49 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..fa980f2467 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3ff.png new file mode 100644 index 0000000000..720901ada3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-200d-2640-fe0f.png new file mode 100644 index 0000000000..acd8162d19 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487-200d-2642-fe0f.png new file mode 100644 index 0000000000..8b290f6dc2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f487.png b/backend/Shared/CIPPSharp/bin/twemoji/1f487.png new file mode 100644 index 0000000000..029cb0b6d5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f487.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f488.png b/backend/Shared/CIPPSharp/bin/twemoji/1f488.png new file mode 100644 index 0000000000..5c19ed5067 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f488.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f489.png b/backend/Shared/CIPPSharp/bin/twemoji/1f489.png new file mode 100644 index 0000000000..5f2d305e7c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f489.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f48a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f48a.png new file mode 100644 index 0000000000..81b7fe1b57 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f48a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f48b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f48b.png new file mode 100644 index 0000000000..5feb59b18c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f48b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f48c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f48c.png new file mode 100644 index 0000000000..a79d3c1579 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f48c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f48d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f48d.png new file mode 100644 index 0000000000..f9a1fce4f1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f48d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f48e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f48e.png new file mode 100644 index 0000000000..7eca5b21c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f48e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f48f-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f48f-1f3fb.png new file mode 100644 index 0000000000..d2ee5c4d77 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f48f-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f48f-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f48f-1f3fc.png new file mode 100644 index 0000000000..ccee0497c4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f48f-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f48f-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f48f-1f3fd.png new file mode 100644 index 0000000000..15709e5651 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f48f-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f48f-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f48f-1f3fe.png new file mode 100644 index 0000000000..96ac2ef4f1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f48f-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f48f-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f48f-1f3ff.png new file mode 100644 index 0000000000..461bc79632 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f48f-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f48f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f48f.png new file mode 100644 index 0000000000..0774078e95 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f48f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f490.png b/backend/Shared/CIPPSharp/bin/twemoji/1f490.png new file mode 100644 index 0000000000..79bdf474e6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f490.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f491-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f491-1f3fb.png new file mode 100644 index 0000000000..1d6c1bfc67 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f491-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f491-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f491-1f3fc.png new file mode 100644 index 0000000000..f11a685fe8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f491-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f491-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f491-1f3fd.png new file mode 100644 index 0000000000..f2e3534bd3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f491-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f491-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f491-1f3fe.png new file mode 100644 index 0000000000..5cf2c2f148 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f491-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f491-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f491-1f3ff.png new file mode 100644 index 0000000000..b5231a51fe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f491-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f491.png b/backend/Shared/CIPPSharp/bin/twemoji/1f491.png new file mode 100644 index 0000000000..066d5dfcb9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f491.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f492.png b/backend/Shared/CIPPSharp/bin/twemoji/1f492.png new file mode 100644 index 0000000000..386d58d104 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f492.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f493.png b/backend/Shared/CIPPSharp/bin/twemoji/1f493.png new file mode 100644 index 0000000000..33b8aa93d4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f493.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f494.png b/backend/Shared/CIPPSharp/bin/twemoji/1f494.png new file mode 100644 index 0000000000..5af54e5f15 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f494.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f495.png b/backend/Shared/CIPPSharp/bin/twemoji/1f495.png new file mode 100644 index 0000000000..bffc24e976 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f495.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f496.png b/backend/Shared/CIPPSharp/bin/twemoji/1f496.png new file mode 100644 index 0000000000..48e0216c61 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f496.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f497.png b/backend/Shared/CIPPSharp/bin/twemoji/1f497.png new file mode 100644 index 0000000000..591c9bf244 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f497.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f498.png b/backend/Shared/CIPPSharp/bin/twemoji/1f498.png new file mode 100644 index 0000000000..ad41943b54 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f498.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f499.png b/backend/Shared/CIPPSharp/bin/twemoji/1f499.png new file mode 100644 index 0000000000..736bc85cae Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f499.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f49a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f49a.png new file mode 100644 index 0000000000..b8bf54c320 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f49a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f49b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f49b.png new file mode 100644 index 0000000000..215fb3c6e0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f49b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f49c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f49c.png new file mode 100644 index 0000000000..c68892cc28 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f49c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f49d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f49d.png new file mode 100644 index 0000000000..35c270742a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f49d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f49e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f49e.png new file mode 100644 index 0000000000..a85936255c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f49e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f49f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f49f.png new file mode 100644 index 0000000000..f036931498 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f49f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4a0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4a0.png new file mode 100644 index 0000000000..3e22818d4a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4a0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4a1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4a1.png new file mode 100644 index 0000000000..ae01adac42 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4a1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4a2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4a2.png new file mode 100644 index 0000000000..0630d37eca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4a2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4a3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4a3.png new file mode 100644 index 0000000000..af90401f42 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4a3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4a4.png new file mode 100644 index 0000000000..0b3671fb5e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4a5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4a5.png new file mode 100644 index 0000000000..8bf99d6434 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4a5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4a6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4a6.png new file mode 100644 index 0000000000..6bec97d0c0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4a6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4a7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4a7.png new file mode 100644 index 0000000000..c3398a2d09 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4a7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4a8.png new file mode 100644 index 0000000000..d9b0016b0e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4a9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4a9.png new file mode 100644 index 0000000000..bf8eac64da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4a9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4aa-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4aa-1f3fb.png new file mode 100644 index 0000000000..16769e9787 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4aa-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4aa-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4aa-1f3fc.png new file mode 100644 index 0000000000..3d70cd4ba9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4aa-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4aa-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4aa-1f3fd.png new file mode 100644 index 0000000000..8f734365eb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4aa-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4aa-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4aa-1f3fe.png new file mode 100644 index 0000000000..6fe4ff8caf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4aa-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4aa-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4aa-1f3ff.png new file mode 100644 index 0000000000..1ce1f149e8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4aa-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4aa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4aa.png new file mode 100644 index 0000000000..ff53dfc3dc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4aa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4ab.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4ab.png new file mode 100644 index 0000000000..87a7b7eab4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4ab.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4ac.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4ac.png new file mode 100644 index 0000000000..62a11b6dcb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4ac.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4ad.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4ad.png new file mode 100644 index 0000000000..f31b046294 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4ad.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4ae.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4ae.png new file mode 100644 index 0000000000..bbad8a9e34 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4ae.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4af.png new file mode 100644 index 0000000000..6a8fb87e7c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4b0.png new file mode 100644 index 0000000000..8b2e49b859 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4b1.png new file mode 100644 index 0000000000..967a03177e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4b2.png new file mode 100644 index 0000000000..e3bf117999 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4b3.png new file mode 100644 index 0000000000..4f5b6dcf83 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4b4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4b4.png new file mode 100644 index 0000000000..d9a37b4699 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4b4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4b5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4b5.png new file mode 100644 index 0000000000..4f29d43c52 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4b5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4b6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4b6.png new file mode 100644 index 0000000000..b393e772a3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4b6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4b7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4b7.png new file mode 100644 index 0000000000..d48d889d23 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4b7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4b8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4b8.png new file mode 100644 index 0000000000..4ede47ad1e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4b8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4b9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4b9.png new file mode 100644 index 0000000000..29ed4d39cc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4b9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4ba.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4ba.png new file mode 100644 index 0000000000..fd029b0f56 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4ba.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4bb.png new file mode 100644 index 0000000000..9d295ffe22 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4bc.png new file mode 100644 index 0000000000..08670c075a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4bd.png new file mode 100644 index 0000000000..460752a2dc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4be.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4be.png new file mode 100644 index 0000000000..fc9746b7bf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4be.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4bf.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4bf.png new file mode 100644 index 0000000000..319a0f68a4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4bf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4c0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4c0.png new file mode 100644 index 0000000000..64f792e404 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4c0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4c1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4c1.png new file mode 100644 index 0000000000..92b1b50bca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4c1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4c2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4c2.png new file mode 100644 index 0000000000..5a7f53bc8f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4c2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4c3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4c3.png new file mode 100644 index 0000000000..4c4d33a65b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4c3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4c4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4c4.png new file mode 100644 index 0000000000..c868e03bb6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4c4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4c5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4c5.png new file mode 100644 index 0000000000..6ad723c462 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4c5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4c6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4c6.png new file mode 100644 index 0000000000..32054b032a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4c6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4c7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4c7.png new file mode 100644 index 0000000000..cd17bac894 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4c7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4c8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4c8.png new file mode 100644 index 0000000000..8ff6e47cfa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4c8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4c9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4c9.png new file mode 100644 index 0000000000..aa190e8236 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4c9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4ca.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4ca.png new file mode 100644 index 0000000000..c449d4cc18 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4ca.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4cb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4cb.png new file mode 100644 index 0000000000..9a2a5559fd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4cb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4cc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4cc.png new file mode 100644 index 0000000000..f442f25924 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4cc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4cd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4cd.png new file mode 100644 index 0000000000..81a54135c3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4cd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4ce.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4ce.png new file mode 100644 index 0000000000..bab2f94fa8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4ce.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4cf.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4cf.png new file mode 100644 index 0000000000..aae172cb5b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4cf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4d0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4d0.png new file mode 100644 index 0000000000..ccc5b2cd7b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4d0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4d1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4d1.png new file mode 100644 index 0000000000..d3e85665fa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4d1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4d2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4d2.png new file mode 100644 index 0000000000..d4ba357a59 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4d2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4d3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4d3.png new file mode 100644 index 0000000000..3e4482daa9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4d3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4d4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4d4.png new file mode 100644 index 0000000000..fa299b384f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4d4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4d5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4d5.png new file mode 100644 index 0000000000..0acd2bbc50 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4d5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4d6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4d6.png new file mode 100644 index 0000000000..4a839711c0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4d6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4d7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4d7.png new file mode 100644 index 0000000000..dec65326de Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4d7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4d8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4d8.png new file mode 100644 index 0000000000..f4eecca91b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4d8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4d9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4d9.png new file mode 100644 index 0000000000..08f81464d4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4d9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4da.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4da.png new file mode 100644 index 0000000000..cdcbcb8efc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4da.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4db.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4db.png new file mode 100644 index 0000000000..fc4a0e50fe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4db.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4dc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4dc.png new file mode 100644 index 0000000000..9126fd0cd9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4dc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4dd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4dd.png new file mode 100644 index 0000000000..0f953e52a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4dd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4de.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4de.png new file mode 100644 index 0000000000..cb3e2d71a6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4de.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4df.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4df.png new file mode 100644 index 0000000000..ac6ae4ce9a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4df.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4e0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4e0.png new file mode 100644 index 0000000000..c9b62aca2f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4e0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4e1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4e1.png new file mode 100644 index 0000000000..c1b86f723f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4e1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4e2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4e2.png new file mode 100644 index 0000000000..7cdddf23e7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4e2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4e3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4e3.png new file mode 100644 index 0000000000..aa25335631 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4e4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4e4.png new file mode 100644 index 0000000000..246875f21d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4e4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4e5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4e5.png new file mode 100644 index 0000000000..7588c05d17 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4e5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4e6.png new file mode 100644 index 0000000000..7e2bab3d03 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4e7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4e7.png new file mode 100644 index 0000000000..9974eca3ba Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4e7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4e8.png new file mode 100644 index 0000000000..e710181235 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4e9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4e9.png new file mode 100644 index 0000000000..2a40f0ad75 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4ea.png new file mode 100644 index 0000000000..374256b336 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4eb.png new file mode 100644 index 0000000000..8896b0e280 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4ec.png new file mode 100644 index 0000000000..0e20c2ad00 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4ed.png new file mode 100644 index 0000000000..36d5b729e4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4ee.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4ee.png new file mode 100644 index 0000000000..ce53f3998b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4ee.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4ef.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4ef.png new file mode 100644 index 0000000000..d999826a78 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4ef.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4f0.png new file mode 100644 index 0000000000..644645b3db Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4f1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4f1.png new file mode 100644 index 0000000000..f44861b91b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4f1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4f2.png new file mode 100644 index 0000000000..6755677487 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4f3.png new file mode 100644 index 0000000000..c332ca7aa6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4f4.png new file mode 100644 index 0000000000..abc1b821be Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4f5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4f5.png new file mode 100644 index 0000000000..f4e538d8c6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4f5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4f6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4f6.png new file mode 100644 index 0000000000..f4f93cfdc7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4f6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4f7.png new file mode 100644 index 0000000000..246f692dac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4f8.png new file mode 100644 index 0000000000..591257f3ee Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4f9.png new file mode 100644 index 0000000000..8ee4f62766 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4fa.png new file mode 100644 index 0000000000..54d39eb439 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4fb.png new file mode 100644 index 0000000000..9609bbef83 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4fc.png new file mode 100644 index 0000000000..aca48416a7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4fd.png new file mode 100644 index 0000000000..f2dcfad6a5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f4ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f4ff.png new file mode 100644 index 0000000000..be750c49da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f4ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f500.png b/backend/Shared/CIPPSharp/bin/twemoji/1f500.png new file mode 100644 index 0000000000..fbe6dd1270 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f500.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f501.png b/backend/Shared/CIPPSharp/bin/twemoji/1f501.png new file mode 100644 index 0000000000..a2ae9a301f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f501.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f502.png b/backend/Shared/CIPPSharp/bin/twemoji/1f502.png new file mode 100644 index 0000000000..a8a0ce467b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f502.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f503.png b/backend/Shared/CIPPSharp/bin/twemoji/1f503.png new file mode 100644 index 0000000000..a955030f54 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f503.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f504.png b/backend/Shared/CIPPSharp/bin/twemoji/1f504.png new file mode 100644 index 0000000000..62af6feb54 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f504.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f505.png b/backend/Shared/CIPPSharp/bin/twemoji/1f505.png new file mode 100644 index 0000000000..5a2249c260 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f505.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f506.png b/backend/Shared/CIPPSharp/bin/twemoji/1f506.png new file mode 100644 index 0000000000..f84a19af53 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f506.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f507.png b/backend/Shared/CIPPSharp/bin/twemoji/1f507.png new file mode 100644 index 0000000000..425ea54f07 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f507.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f508.png b/backend/Shared/CIPPSharp/bin/twemoji/1f508.png new file mode 100644 index 0000000000..727fc474d0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f508.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f509.png b/backend/Shared/CIPPSharp/bin/twemoji/1f509.png new file mode 100644 index 0000000000..dd3b69384d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f509.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f50a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f50a.png new file mode 100644 index 0000000000..16a3b36e06 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f50a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f50b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f50b.png new file mode 100644 index 0000000000..ea11270323 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f50b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f50c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f50c.png new file mode 100644 index 0000000000..fa12e63965 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f50c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f50d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f50d.png new file mode 100644 index 0000000000..da93723dd7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f50d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f50e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f50e.png new file mode 100644 index 0000000000..af8a137dad Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f50e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f50f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f50f.png new file mode 100644 index 0000000000..9651c38488 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f50f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f510.png b/backend/Shared/CIPPSharp/bin/twemoji/1f510.png new file mode 100644 index 0000000000..e1366af45e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f510.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f511.png b/backend/Shared/CIPPSharp/bin/twemoji/1f511.png new file mode 100644 index 0000000000..7b1d7807dd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f511.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f512.png b/backend/Shared/CIPPSharp/bin/twemoji/1f512.png new file mode 100644 index 0000000000..b5d9e2334e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f512.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f513.png b/backend/Shared/CIPPSharp/bin/twemoji/1f513.png new file mode 100644 index 0000000000..2b69f8f943 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f513.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f514.png b/backend/Shared/CIPPSharp/bin/twemoji/1f514.png new file mode 100644 index 0000000000..6626232b61 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f514.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f515.png b/backend/Shared/CIPPSharp/bin/twemoji/1f515.png new file mode 100644 index 0000000000..d4ae2ec14f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f515.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f516.png b/backend/Shared/CIPPSharp/bin/twemoji/1f516.png new file mode 100644 index 0000000000..d11e540ad2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f516.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f517.png b/backend/Shared/CIPPSharp/bin/twemoji/1f517.png new file mode 100644 index 0000000000..76618cb5bc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f517.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f518.png b/backend/Shared/CIPPSharp/bin/twemoji/1f518.png new file mode 100644 index 0000000000..49d062b28a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f518.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f519.png b/backend/Shared/CIPPSharp/bin/twemoji/1f519.png new file mode 100644 index 0000000000..97707411bf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f519.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f51a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f51a.png new file mode 100644 index 0000000000..f399acb035 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f51a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f51b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f51b.png new file mode 100644 index 0000000000..6aaaa73b26 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f51b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f51c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f51c.png new file mode 100644 index 0000000000..35fddd633e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f51c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f51d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f51d.png new file mode 100644 index 0000000000..9a7fe01181 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f51d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f51e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f51e.png new file mode 100644 index 0000000000..ea100db7f2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f51e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f51f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f51f.png new file mode 100644 index 0000000000..bc4b0fd18b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f51f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f520.png b/backend/Shared/CIPPSharp/bin/twemoji/1f520.png new file mode 100644 index 0000000000..9da0bed608 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f520.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f521.png b/backend/Shared/CIPPSharp/bin/twemoji/1f521.png new file mode 100644 index 0000000000..e808e12e5e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f521.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f522.png b/backend/Shared/CIPPSharp/bin/twemoji/1f522.png new file mode 100644 index 0000000000..84c2082306 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f522.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f523.png b/backend/Shared/CIPPSharp/bin/twemoji/1f523.png new file mode 100644 index 0000000000..fa2d4ec815 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f523.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f524.png b/backend/Shared/CIPPSharp/bin/twemoji/1f524.png new file mode 100644 index 0000000000..2b187cadfd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f524.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f525.png b/backend/Shared/CIPPSharp/bin/twemoji/1f525.png new file mode 100644 index 0000000000..717523f17e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f525.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f526.png b/backend/Shared/CIPPSharp/bin/twemoji/1f526.png new file mode 100644 index 0000000000..d071d1c986 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f526.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f527.png new file mode 100644 index 0000000000..f16a78613d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f528.png b/backend/Shared/CIPPSharp/bin/twemoji/1f528.png new file mode 100644 index 0000000000..d76ec56de0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f528.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f529.png b/backend/Shared/CIPPSharp/bin/twemoji/1f529.png new file mode 100644 index 0000000000..346e985869 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f529.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f52a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f52a.png new file mode 100644 index 0000000000..79cfa347a5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f52a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f52b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f52b.png new file mode 100644 index 0000000000..e3f5d67061 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f52b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f52c.png new file mode 100644 index 0000000000..5136b7767f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f52d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f52d.png new file mode 100644 index 0000000000..f180454c6c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f52d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f52e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f52e.png new file mode 100644 index 0000000000..a8abfb842c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f52e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f52f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f52f.png new file mode 100644 index 0000000000..b6f1d4f896 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f52f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f530.png b/backend/Shared/CIPPSharp/bin/twemoji/1f530.png new file mode 100644 index 0000000000..f45bbca7ee Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f530.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f531.png b/backend/Shared/CIPPSharp/bin/twemoji/1f531.png new file mode 100644 index 0000000000..fea3585283 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f531.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f532.png b/backend/Shared/CIPPSharp/bin/twemoji/1f532.png new file mode 100644 index 0000000000..87392a4ea9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f532.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f533.png b/backend/Shared/CIPPSharp/bin/twemoji/1f533.png new file mode 100644 index 0000000000..cff774c988 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f533.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f534.png b/backend/Shared/CIPPSharp/bin/twemoji/1f534.png new file mode 100644 index 0000000000..721075df18 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f534.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f535.png b/backend/Shared/CIPPSharp/bin/twemoji/1f535.png new file mode 100644 index 0000000000..de9927d965 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f535.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f536.png b/backend/Shared/CIPPSharp/bin/twemoji/1f536.png new file mode 100644 index 0000000000..d808f65499 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f536.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f537.png b/backend/Shared/CIPPSharp/bin/twemoji/1f537.png new file mode 100644 index 0000000000..9c2363d71b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f537.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f538.png b/backend/Shared/CIPPSharp/bin/twemoji/1f538.png new file mode 100644 index 0000000000..02eab512a7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f538.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f539.png b/backend/Shared/CIPPSharp/bin/twemoji/1f539.png new file mode 100644 index 0000000000..89ce677201 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f539.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f53a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f53a.png new file mode 100644 index 0000000000..470f3d3748 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f53a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f53b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f53b.png new file mode 100644 index 0000000000..ece2885060 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f53b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f53c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f53c.png new file mode 100644 index 0000000000..7c78843ecd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f53c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f53d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f53d.png new file mode 100644 index 0000000000..70b308b6ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f53d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f549.png b/backend/Shared/CIPPSharp/bin/twemoji/1f549.png new file mode 100644 index 0000000000..eb644f25ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f549.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f54a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f54a.png new file mode 100644 index 0000000000..0676697ba3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f54a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f54b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f54b.png new file mode 100644 index 0000000000..89899e709d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f54b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f54c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f54c.png new file mode 100644 index 0000000000..0ffb68aa44 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f54c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f54d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f54d.png new file mode 100644 index 0000000000..d5c15756f3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f54d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f54e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f54e.png new file mode 100644 index 0000000000..4e031b2e13 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f54e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f550.png b/backend/Shared/CIPPSharp/bin/twemoji/1f550.png new file mode 100644 index 0000000000..4fd13e3d50 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f550.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f551.png b/backend/Shared/CIPPSharp/bin/twemoji/1f551.png new file mode 100644 index 0000000000..14fa3f30f4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f551.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f552.png b/backend/Shared/CIPPSharp/bin/twemoji/1f552.png new file mode 100644 index 0000000000..354da1a398 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f552.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f553.png b/backend/Shared/CIPPSharp/bin/twemoji/1f553.png new file mode 100644 index 0000000000..4c6107313b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f553.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f554.png b/backend/Shared/CIPPSharp/bin/twemoji/1f554.png new file mode 100644 index 0000000000..07c7e8ffc9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f554.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f555.png b/backend/Shared/CIPPSharp/bin/twemoji/1f555.png new file mode 100644 index 0000000000..71a04ff4ba Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f555.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f556.png b/backend/Shared/CIPPSharp/bin/twemoji/1f556.png new file mode 100644 index 0000000000..72a0b9a48b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f556.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f557.png b/backend/Shared/CIPPSharp/bin/twemoji/1f557.png new file mode 100644 index 0000000000..bb9753cc8a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f557.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f558.png b/backend/Shared/CIPPSharp/bin/twemoji/1f558.png new file mode 100644 index 0000000000..70d5adbeee Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f558.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f559.png b/backend/Shared/CIPPSharp/bin/twemoji/1f559.png new file mode 100644 index 0000000000..25e6dabf0a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f559.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f55a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f55a.png new file mode 100644 index 0000000000..51c70f80c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f55a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f55b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f55b.png new file mode 100644 index 0000000000..1b58527cf5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f55b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f55c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f55c.png new file mode 100644 index 0000000000..741b630a75 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f55c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f55d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f55d.png new file mode 100644 index 0000000000..58a1af0d56 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f55d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f55e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f55e.png new file mode 100644 index 0000000000..112d5c452c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f55e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f55f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f55f.png new file mode 100644 index 0000000000..14ddea9f30 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f55f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f560.png b/backend/Shared/CIPPSharp/bin/twemoji/1f560.png new file mode 100644 index 0000000000..b30c623abd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f560.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f561.png b/backend/Shared/CIPPSharp/bin/twemoji/1f561.png new file mode 100644 index 0000000000..2753c64c54 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f561.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f562.png b/backend/Shared/CIPPSharp/bin/twemoji/1f562.png new file mode 100644 index 0000000000..a4e4cb5dbb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f562.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f563.png b/backend/Shared/CIPPSharp/bin/twemoji/1f563.png new file mode 100644 index 0000000000..ca7e45feab Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f563.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f564.png b/backend/Shared/CIPPSharp/bin/twemoji/1f564.png new file mode 100644 index 0000000000..9c3582dafe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f564.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f565.png b/backend/Shared/CIPPSharp/bin/twemoji/1f565.png new file mode 100644 index 0000000000..f8af392f87 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f565.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f566.png b/backend/Shared/CIPPSharp/bin/twemoji/1f566.png new file mode 100644 index 0000000000..c4134e8e5a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f566.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f567.png b/backend/Shared/CIPPSharp/bin/twemoji/1f567.png new file mode 100644 index 0000000000..9e6bb465ee Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f567.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f56f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f56f.png new file mode 100644 index 0000000000..5b48a36fa5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f56f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f570.png b/backend/Shared/CIPPSharp/bin/twemoji/1f570.png new file mode 100644 index 0000000000..583feb4ef4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f570.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f573.png b/backend/Shared/CIPPSharp/bin/twemoji/1f573.png new file mode 100644 index 0000000000..5ebed95d62 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f573.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..1eedd74c0a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..c4070ba337 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fb.png new file mode 100644 index 0000000000..ecce7bca8e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..806f043fc3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..525ff09c0a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fc.png new file mode 100644 index 0000000000..205c9a42cb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..15e65ab440 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..17b4804701 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fd.png new file mode 100644 index 0000000000..b0b9ccd2c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..98d11b84e7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..5814bfc6ef Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fe.png new file mode 100644 index 0000000000..18db02ee44 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..4359b4c44b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..5da1f05658 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3ff.png new file mode 100644 index 0000000000..01f734fe8d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-fe0f-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-fe0f-200d-2640-fe0f.png new file mode 100644 index 0000000000..ddfa42524c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-fe0f-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574-fe0f-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574-fe0f-200d-2642-fe0f.png new file mode 100644 index 0000000000..83de6d6446 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574-fe0f-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f574.png b/backend/Shared/CIPPSharp/bin/twemoji/1f574.png new file mode 100644 index 0000000000..d947a641ce Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f574.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..7b0a67f841 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..edbb1f1fdd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fb.png new file mode 100644 index 0000000000..28c11bef71 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..dc84904de2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..64d1e80687 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fc.png new file mode 100644 index 0000000000..f5163334f1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..c5d9838c2f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..1656142df6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fd.png new file mode 100644 index 0000000000..ed52180925 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..1c3f9edfa9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..f275f195c9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fe.png new file mode 100644 index 0000000000..20715fbb02 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..bcd5fbb887 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..0a9f8373aa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3ff.png new file mode 100644 index 0000000000..d625b611e1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-fe0f-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-fe0f-200d-2640-fe0f.png new file mode 100644 index 0000000000..dd3d5f0db0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-fe0f-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575-fe0f-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575-fe0f-200d-2642-fe0f.png new file mode 100644 index 0000000000..8ed60e2b51 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575-fe0f-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f575.png b/backend/Shared/CIPPSharp/bin/twemoji/1f575.png new file mode 100644 index 0000000000..26e5ed1afc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f575.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f576.png b/backend/Shared/CIPPSharp/bin/twemoji/1f576.png new file mode 100644 index 0000000000..70cd5c81de Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f576.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f577.png b/backend/Shared/CIPPSharp/bin/twemoji/1f577.png new file mode 100644 index 0000000000..475377b870 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f577.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f578.png b/backend/Shared/CIPPSharp/bin/twemoji/1f578.png new file mode 100644 index 0000000000..7b5fbf6015 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f578.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f579.png b/backend/Shared/CIPPSharp/bin/twemoji/1f579.png new file mode 100644 index 0000000000..6dbd77dc37 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f579.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f57a-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f57a-1f3fb.png new file mode 100644 index 0000000000..62de0e0741 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f57a-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f57a-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f57a-1f3fc.png new file mode 100644 index 0000000000..f3640871fc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f57a-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f57a-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f57a-1f3fd.png new file mode 100644 index 0000000000..f39461bd91 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f57a-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f57a-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f57a-1f3fe.png new file mode 100644 index 0000000000..f99b43ba5a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f57a-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f57a-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f57a-1f3ff.png new file mode 100644 index 0000000000..477ecad666 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f57a-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f57a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f57a.png new file mode 100644 index 0000000000..12499df34f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f57a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f587.png b/backend/Shared/CIPPSharp/bin/twemoji/1f587.png new file mode 100644 index 0000000000..048cde6fb8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f587.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f58a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f58a.png new file mode 100644 index 0000000000..178fd352f3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f58a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f58b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f58b.png new file mode 100644 index 0000000000..f996037a2f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f58b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f58c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f58c.png new file mode 100644 index 0000000000..dc9fbae60f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f58c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f58d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f58d.png new file mode 100644 index 0000000000..ffa2e36762 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f58d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f590-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f590-1f3fb.png new file mode 100644 index 0000000000..3d0a6dc228 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f590-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f590-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f590-1f3fc.png new file mode 100644 index 0000000000..ada57e3d89 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f590-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f590-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f590-1f3fd.png new file mode 100644 index 0000000000..297c12b450 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f590-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f590-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f590-1f3fe.png new file mode 100644 index 0000000000..043d7551e3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f590-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f590-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f590-1f3ff.png new file mode 100644 index 0000000000..09b7feaaa7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f590-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f590.png b/backend/Shared/CIPPSharp/bin/twemoji/1f590.png new file mode 100644 index 0000000000..6e03235070 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f590.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f595-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f595-1f3fb.png new file mode 100644 index 0000000000..396d2f521e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f595-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f595-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f595-1f3fc.png new file mode 100644 index 0000000000..c7e4024287 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f595-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f595-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f595-1f3fd.png new file mode 100644 index 0000000000..dacfe5cd79 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f595-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f595-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f595-1f3fe.png new file mode 100644 index 0000000000..8af84e4b73 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f595-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f595-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f595-1f3ff.png new file mode 100644 index 0000000000..036298512c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f595-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f595.png b/backend/Shared/CIPPSharp/bin/twemoji/1f595.png new file mode 100644 index 0000000000..9b5de31144 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f595.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f596-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f596-1f3fb.png new file mode 100644 index 0000000000..467aef7e49 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f596-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f596-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f596-1f3fc.png new file mode 100644 index 0000000000..60b9be3931 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f596-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f596-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f596-1f3fd.png new file mode 100644 index 0000000000..36ba32915c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f596-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f596-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f596-1f3fe.png new file mode 100644 index 0000000000..07db6dba3d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f596-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f596-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f596-1f3ff.png new file mode 100644 index 0000000000..59a79e6def Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f596-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f596.png b/backend/Shared/CIPPSharp/bin/twemoji/1f596.png new file mode 100644 index 0000000000..0778c7bc57 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f596.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5a4.png new file mode 100644 index 0000000000..39b91ddf20 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5a5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5a5.png new file mode 100644 index 0000000000..ac11f31e76 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5a5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5a8.png new file mode 100644 index 0000000000..094e0d57b4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5b1.png new file mode 100644 index 0000000000..a8c2b82f50 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5b2.png new file mode 100644 index 0000000000..62a462eda2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5bc.png new file mode 100644 index 0000000000..0f22c74fc4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5c2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5c2.png new file mode 100644 index 0000000000..9a75e28dfe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5c2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5c3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5c3.png new file mode 100644 index 0000000000..b4a283d934 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5c3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5c4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5c4.png new file mode 100644 index 0000000000..9b19e2e8a2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5c4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5d1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5d1.png new file mode 100644 index 0000000000..bdd43bc6e0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5d1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5d2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5d2.png new file mode 100644 index 0000000000..b77006699c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5d2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5d3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5d3.png new file mode 100644 index 0000000000..72e526929f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5d3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5dc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5dc.png new file mode 100644 index 0000000000..35702a97c5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5dc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5dd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5dd.png new file mode 100644 index 0000000000..2a6bb2d882 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5dd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5de.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5de.png new file mode 100644 index 0000000000..f32e9860ad Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5de.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5e1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5e1.png new file mode 100644 index 0000000000..19328e1e26 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5e1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5e3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5e3.png new file mode 100644 index 0000000000..b98f4fffeb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5e8.png new file mode 100644 index 0000000000..ee9daef15e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5ef.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5ef.png new file mode 100644 index 0000000000..ea4f27ae72 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5ef.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5f3.png new file mode 100644 index 0000000000..c9cabef7e2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5fa.png new file mode 100644 index 0000000000..c03959d257 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5fb.png new file mode 100644 index 0000000000..ee42aa786f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5fc.png new file mode 100644 index 0000000000..b33cacb900 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5fd.png new file mode 100644 index 0000000000..1d3dd5a3b5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5fe.png new file mode 100644 index 0000000000..b223c92c0b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f5ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f5ff.png new file mode 100644 index 0000000000..09b71d9ea2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f5ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f600.png b/backend/Shared/CIPPSharp/bin/twemoji/1f600.png new file mode 100644 index 0000000000..e1cca7d00c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f600.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f601.png b/backend/Shared/CIPPSharp/bin/twemoji/1f601.png new file mode 100644 index 0000000000..3a73f9d906 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f601.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f602.png b/backend/Shared/CIPPSharp/bin/twemoji/1f602.png new file mode 100644 index 0000000000..e1113e8e3f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f602.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f603.png b/backend/Shared/CIPPSharp/bin/twemoji/1f603.png new file mode 100644 index 0000000000..e24b685f61 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f603.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f604.png b/backend/Shared/CIPPSharp/bin/twemoji/1f604.png new file mode 100644 index 0000000000..e032945e68 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f604.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f605.png b/backend/Shared/CIPPSharp/bin/twemoji/1f605.png new file mode 100644 index 0000000000..bfc7c287f5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f605.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f606.png b/backend/Shared/CIPPSharp/bin/twemoji/1f606.png new file mode 100644 index 0000000000..ac4797ee40 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f606.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f607.png b/backend/Shared/CIPPSharp/bin/twemoji/1f607.png new file mode 100644 index 0000000000..def2c98d28 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f607.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f608.png b/backend/Shared/CIPPSharp/bin/twemoji/1f608.png new file mode 100644 index 0000000000..3e1a54cd10 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f608.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f609.png b/backend/Shared/CIPPSharp/bin/twemoji/1f609.png new file mode 100644 index 0000000000..703a50e098 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f609.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f60a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f60a.png new file mode 100644 index 0000000000..8a5b034e05 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f60a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f60b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f60b.png new file mode 100644 index 0000000000..4a71524d15 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f60b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f60c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f60c.png new file mode 100644 index 0000000000..4fb75cffec Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f60c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f60d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f60d.png new file mode 100644 index 0000000000..0d4f15ee1b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f60d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f60e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f60e.png new file mode 100644 index 0000000000..1a0560df6d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f60e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f60f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f60f.png new file mode 100644 index 0000000000..4fca129f12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f60f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f610.png b/backend/Shared/CIPPSharp/bin/twemoji/1f610.png new file mode 100644 index 0000000000..17628f9534 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f610.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f611.png b/backend/Shared/CIPPSharp/bin/twemoji/1f611.png new file mode 100644 index 0000000000..fda6326aff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f611.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f612.png b/backend/Shared/CIPPSharp/bin/twemoji/1f612.png new file mode 100644 index 0000000000..53c52ecd31 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f612.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f613.png b/backend/Shared/CIPPSharp/bin/twemoji/1f613.png new file mode 100644 index 0000000000..325ce9e7ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f613.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f614.png b/backend/Shared/CIPPSharp/bin/twemoji/1f614.png new file mode 100644 index 0000000000..1357dd9c7a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f614.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f615.png b/backend/Shared/CIPPSharp/bin/twemoji/1f615.png new file mode 100644 index 0000000000..02cd1fe2d3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f615.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f616.png b/backend/Shared/CIPPSharp/bin/twemoji/1f616.png new file mode 100644 index 0000000000..37221bd16c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f616.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f617.png b/backend/Shared/CIPPSharp/bin/twemoji/1f617.png new file mode 100644 index 0000000000..22c9bd93b5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f617.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f618.png b/backend/Shared/CIPPSharp/bin/twemoji/1f618.png new file mode 100644 index 0000000000..7aa39d71db Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f618.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f619.png b/backend/Shared/CIPPSharp/bin/twemoji/1f619.png new file mode 100644 index 0000000000..0118638138 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f619.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f61a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f61a.png new file mode 100644 index 0000000000..3ee01547c3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f61a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f61b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f61b.png new file mode 100644 index 0000000000..79806b1272 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f61b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f61c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f61c.png new file mode 100644 index 0000000000..8aeab47c62 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f61c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f61d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f61d.png new file mode 100644 index 0000000000..abd9e150ce Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f61d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f61e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f61e.png new file mode 100644 index 0000000000..f23431b81b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f61e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f61f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f61f.png new file mode 100644 index 0000000000..b0f9051901 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f61f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f620.png b/backend/Shared/CIPPSharp/bin/twemoji/1f620.png new file mode 100644 index 0000000000..31ecddc815 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f620.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f621.png b/backend/Shared/CIPPSharp/bin/twemoji/1f621.png new file mode 100644 index 0000000000..3921022f59 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f621.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f622.png b/backend/Shared/CIPPSharp/bin/twemoji/1f622.png new file mode 100644 index 0000000000..4b8f0e943d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f622.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f623.png b/backend/Shared/CIPPSharp/bin/twemoji/1f623.png new file mode 100644 index 0000000000..007139dc3a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f623.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f624.png b/backend/Shared/CIPPSharp/bin/twemoji/1f624.png new file mode 100644 index 0000000000..6847184123 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f624.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f625.png b/backend/Shared/CIPPSharp/bin/twemoji/1f625.png new file mode 100644 index 0000000000..c4c4a2a367 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f625.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f626.png b/backend/Shared/CIPPSharp/bin/twemoji/1f626.png new file mode 100644 index 0000000000..1e97bcc4ac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f626.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f627.png b/backend/Shared/CIPPSharp/bin/twemoji/1f627.png new file mode 100644 index 0000000000..28ce280730 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f627.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f628.png b/backend/Shared/CIPPSharp/bin/twemoji/1f628.png new file mode 100644 index 0000000000..f32b0a4ef2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f628.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f629.png b/backend/Shared/CIPPSharp/bin/twemoji/1f629.png new file mode 100644 index 0000000000..2ceecc05a8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f629.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f62a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f62a.png new file mode 100644 index 0000000000..1a0d7d5767 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f62a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f62b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f62b.png new file mode 100644 index 0000000000..db9edc1eb7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f62b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f62c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f62c.png new file mode 100644 index 0000000000..24619b8a60 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f62c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f62d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f62d.png new file mode 100644 index 0000000000..554edf044f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f62d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f62e-200d-1f4a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f62e-200d-1f4a8.png new file mode 100644 index 0000000000..37db70540a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f62e-200d-1f4a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f62e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f62e.png new file mode 100644 index 0000000000..45820be674 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f62e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f62f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f62f.png new file mode 100644 index 0000000000..11433a2c64 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f62f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f630.png b/backend/Shared/CIPPSharp/bin/twemoji/1f630.png new file mode 100644 index 0000000000..248ec17673 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f630.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f631.png b/backend/Shared/CIPPSharp/bin/twemoji/1f631.png new file mode 100644 index 0000000000..fa3debccac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f631.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f632.png b/backend/Shared/CIPPSharp/bin/twemoji/1f632.png new file mode 100644 index 0000000000..dd4fa0c000 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f632.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f633.png b/backend/Shared/CIPPSharp/bin/twemoji/1f633.png new file mode 100644 index 0000000000..47093b1bfe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f633.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f634.png b/backend/Shared/CIPPSharp/bin/twemoji/1f634.png new file mode 100644 index 0000000000..4c3806dea5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f634.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f635-200d-1f4ab.png b/backend/Shared/CIPPSharp/bin/twemoji/1f635-200d-1f4ab.png new file mode 100644 index 0000000000..f2ad9dc1f9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f635-200d-1f4ab.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f635.png b/backend/Shared/CIPPSharp/bin/twemoji/1f635.png new file mode 100644 index 0000000000..3db251c49c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f635.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f636-200d-1f32b-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f636-200d-1f32b-fe0f.png new file mode 100644 index 0000000000..74bf63d50a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f636-200d-1f32b-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f636.png b/backend/Shared/CIPPSharp/bin/twemoji/1f636.png new file mode 100644 index 0000000000..4d7d903a2b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f636.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f637.png b/backend/Shared/CIPPSharp/bin/twemoji/1f637.png new file mode 100644 index 0000000000..5197ce37cd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f637.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f638.png b/backend/Shared/CIPPSharp/bin/twemoji/1f638.png new file mode 100644 index 0000000000..538ff46e5b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f638.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f639.png b/backend/Shared/CIPPSharp/bin/twemoji/1f639.png new file mode 100644 index 0000000000..7ff22eae37 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f639.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f63a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f63a.png new file mode 100644 index 0000000000..3ca61cd602 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f63a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f63b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f63b.png new file mode 100644 index 0000000000..13e14f016e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f63b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f63c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f63c.png new file mode 100644 index 0000000000..9d4ceee4af Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f63c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f63d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f63d.png new file mode 100644 index 0000000000..966fed00b5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f63d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f63e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f63e.png new file mode 100644 index 0000000000..d702c14440 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f63e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f63f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f63f.png new file mode 100644 index 0000000000..84ade17f33 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f63f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f640.png b/backend/Shared/CIPPSharp/bin/twemoji/1f640.png new file mode 100644 index 0000000000..410cf816c7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f640.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f641.png b/backend/Shared/CIPPSharp/bin/twemoji/1f641.png new file mode 100644 index 0000000000..68cf4d4a4f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f641.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f642-200d-2194-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f642-200d-2194-fe0f.png new file mode 100644 index 0000000000..da7be18911 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f642-200d-2194-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f642-200d-2195-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f642-200d-2195-fe0f.png new file mode 100644 index 0000000000..88a87de226 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f642-200d-2195-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f642.png b/backend/Shared/CIPPSharp/bin/twemoji/1f642.png new file mode 100644 index 0000000000..d6ece82228 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f642.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f643.png b/backend/Shared/CIPPSharp/bin/twemoji/1f643.png new file mode 100644 index 0000000000..6c5f72b5ff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f643.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f644.png b/backend/Shared/CIPPSharp/bin/twemoji/1f644.png new file mode 100644 index 0000000000..1bafe2a637 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f644.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..4b0d4a0c62 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..afe0d607d1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fb.png new file mode 100644 index 0000000000..240633eef6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..218e57ff69 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..3d6161b286 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fc.png new file mode 100644 index 0000000000..fc2b550d70 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..9bd6857bfb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..8168441f3d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fd.png new file mode 100644 index 0000000000..6daf870413 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..da0411f8c2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..efc545851a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fe.png new file mode 100644 index 0000000000..7b0fc547fa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..3ae6ff4e2f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..91768e526c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3ff.png new file mode 100644 index 0000000000..6fd9efc5a5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-200d-2640-fe0f.png new file mode 100644 index 0000000000..a38f17ea8e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645-200d-2642-fe0f.png new file mode 100644 index 0000000000..0edacb01b0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f645.png b/backend/Shared/CIPPSharp/bin/twemoji/1f645.png new file mode 100644 index 0000000000..fe42663a8b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f645.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..1376c0ec36 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..1bb39d024b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fb.png new file mode 100644 index 0000000000..334ad0555c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..3f04847ff3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..fc62d9984f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fc.png new file mode 100644 index 0000000000..017a2e7fd0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..29d4d5b260 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..6d5ca67efb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fd.png new file mode 100644 index 0000000000..f7bdeaca23 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..e4352cee82 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..0cd923a9bf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fe.png new file mode 100644 index 0000000000..7c92b35ecc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..7bc47ebdc7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..4f885b0746 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3ff.png new file mode 100644 index 0000000000..9193addc3a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-200d-2640-fe0f.png new file mode 100644 index 0000000000..5b18034ac1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646-200d-2642-fe0f.png new file mode 100644 index 0000000000..94d87d8c28 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f646.png b/backend/Shared/CIPPSharp/bin/twemoji/1f646.png new file mode 100644 index 0000000000..1ece1c7f28 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f646.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..bc5c50a3c1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..f42ce72a94 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fb.png new file mode 100644 index 0000000000..8519432cf9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..d0461b3447 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..50015401cb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fc.png new file mode 100644 index 0000000000..5038aeaaf2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..24b8cfb810 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..eeebeda840 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fd.png new file mode 100644 index 0000000000..38389ed31d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..21d7688242 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..90f1d16f6c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fe.png new file mode 100644 index 0000000000..9b5f7135b6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..2c192cb641 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..f97b23935f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3ff.png new file mode 100644 index 0000000000..561024107a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-200d-2640-fe0f.png new file mode 100644 index 0000000000..bee0cd77fa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647-200d-2642-fe0f.png new file mode 100644 index 0000000000..1c7a05baa5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f647.png b/backend/Shared/CIPPSharp/bin/twemoji/1f647.png new file mode 100644 index 0000000000..7f92695560 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f647.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f648.png b/backend/Shared/CIPPSharp/bin/twemoji/1f648.png new file mode 100644 index 0000000000..d9866c9042 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f648.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f649.png b/backend/Shared/CIPPSharp/bin/twemoji/1f649.png new file mode 100644 index 0000000000..284cf9b4b5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f649.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64a.png new file mode 100644 index 0000000000..ab35f405a9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..bfadf8f909 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..c3eaf18d18 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fb.png new file mode 100644 index 0000000000..97439b7bb5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..c12537b102 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..dffc95b685 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fc.png new file mode 100644 index 0000000000..b2ec6e1f4a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..431daae49a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..ff577874e7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fd.png new file mode 100644 index 0000000000..92c0e793b6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..f21a1cbbd2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..8423ab2f7d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fe.png new file mode 100644 index 0000000000..59c34dba7b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..328e2c5adb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..be3fe243b6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3ff.png new file mode 100644 index 0000000000..113a5d95db Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-200d-2640-fe0f.png new file mode 100644 index 0000000000..d66bfa3f12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-200d-2642-fe0f.png new file mode 100644 index 0000000000..a7ac68aeed Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64b.png new file mode 100644 index 0000000000..ca3aef07e0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64c-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64c-1f3fb.png new file mode 100644 index 0000000000..6933625595 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64c-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64c-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64c-1f3fc.png new file mode 100644 index 0000000000..d1499a4f3a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64c-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64c-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64c-1f3fd.png new file mode 100644 index 0000000000..aff7790bcd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64c-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64c-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64c-1f3fe.png new file mode 100644 index 0000000000..1b91f8b02f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64c-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64c-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64c-1f3ff.png new file mode 100644 index 0000000000..bbec83c35a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64c-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64c.png new file mode 100644 index 0000000000..e634507a0b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..aae352d056 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..418bca4b98 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fb.png new file mode 100644 index 0000000000..840ddde7d2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..1ddc7df17d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..aa2ce5703c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fc.png new file mode 100644 index 0000000000..820ebabf80 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..611f5abc71 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..6de2188406 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fd.png new file mode 100644 index 0000000000..196cb444d1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..f9be0f0f02 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..7e7117d633 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fe.png new file mode 100644 index 0000000000..eb07daabbe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..a79fe542dc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..b20da5f07d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3ff.png new file mode 100644 index 0000000000..d9ef12d0d1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-200d-2640-fe0f.png new file mode 100644 index 0000000000..8ecf1966be Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-200d-2642-fe0f.png new file mode 100644 index 0000000000..f4cc4acc15 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64d.png new file mode 100644 index 0000000000..1d58cfe929 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..86dd895a35 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..f13a8093a9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fb.png new file mode 100644 index 0000000000..fb498d23d2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..75f03ce985 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..87b6e6ce87 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fc.png new file mode 100644 index 0000000000..30189f4383 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..3d1528dece Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..ed75c900a3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fd.png new file mode 100644 index 0000000000..e515a71283 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..a8b0388daa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..75dfe96016 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fe.png new file mode 100644 index 0000000000..a60b07cc80 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..c293681fb6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..fd5365cad3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3ff.png new file mode 100644 index 0000000000..1a746f3f77 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-200d-2640-fe0f.png new file mode 100644 index 0000000000..72da6530d0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-200d-2642-fe0f.png new file mode 100644 index 0000000000..4c6b4298da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64e.png new file mode 100644 index 0000000000..92a455969c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64f-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64f-1f3fb.png new file mode 100644 index 0000000000..25dfbd62ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64f-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64f-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64f-1f3fc.png new file mode 100644 index 0000000000..c7025799ad Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64f-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64f-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64f-1f3fd.png new file mode 100644 index 0000000000..4f8982c459 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64f-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64f-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64f-1f3fe.png new file mode 100644 index 0000000000..ab440bb60b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64f-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64f-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64f-1f3ff.png new file mode 100644 index 0000000000..6274a14cc0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64f-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f64f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f64f.png new file mode 100644 index 0000000000..b18f35a7f6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f64f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f680.png new file mode 100644 index 0000000000..ee6a88ac73 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f681.png b/backend/Shared/CIPPSharp/bin/twemoji/1f681.png new file mode 100644 index 0000000000..10796dfb77 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f681.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f682.png b/backend/Shared/CIPPSharp/bin/twemoji/1f682.png new file mode 100644 index 0000000000..00d6b1c86c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f682.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f683.png b/backend/Shared/CIPPSharp/bin/twemoji/1f683.png new file mode 100644 index 0000000000..d563d35b64 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f683.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f684.png b/backend/Shared/CIPPSharp/bin/twemoji/1f684.png new file mode 100644 index 0000000000..826875f7b4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f684.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f685.png b/backend/Shared/CIPPSharp/bin/twemoji/1f685.png new file mode 100644 index 0000000000..ae356c6d62 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f685.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f686.png b/backend/Shared/CIPPSharp/bin/twemoji/1f686.png new file mode 100644 index 0000000000..c74aa912a7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f686.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f687.png b/backend/Shared/CIPPSharp/bin/twemoji/1f687.png new file mode 100644 index 0000000000..d4614c1451 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f687.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f688.png b/backend/Shared/CIPPSharp/bin/twemoji/1f688.png new file mode 100644 index 0000000000..26aa866540 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f688.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f689.png b/backend/Shared/CIPPSharp/bin/twemoji/1f689.png new file mode 100644 index 0000000000..596d8d6142 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f689.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f68a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f68a.png new file mode 100644 index 0000000000..9d51921680 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f68a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f68b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f68b.png new file mode 100644 index 0000000000..f497e46674 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f68b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f68c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f68c.png new file mode 100644 index 0000000000..4a900b4da0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f68c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f68d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f68d.png new file mode 100644 index 0000000000..078be9ba71 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f68d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f68e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f68e.png new file mode 100644 index 0000000000..cc5a24037f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f68e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f68f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f68f.png new file mode 100644 index 0000000000..06a7892894 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f68f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f690.png b/backend/Shared/CIPPSharp/bin/twemoji/1f690.png new file mode 100644 index 0000000000..ee8318b525 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f690.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f691.png b/backend/Shared/CIPPSharp/bin/twemoji/1f691.png new file mode 100644 index 0000000000..154fafae93 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f691.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f692.png new file mode 100644 index 0000000000..c8bef7b865 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f693.png b/backend/Shared/CIPPSharp/bin/twemoji/1f693.png new file mode 100644 index 0000000000..97428b4083 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f693.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f694.png b/backend/Shared/CIPPSharp/bin/twemoji/1f694.png new file mode 100644 index 0000000000..53e8067028 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f694.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f695.png b/backend/Shared/CIPPSharp/bin/twemoji/1f695.png new file mode 100644 index 0000000000..118ac470c3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f695.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f696.png b/backend/Shared/CIPPSharp/bin/twemoji/1f696.png new file mode 100644 index 0000000000..116b2b4364 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f696.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f697.png b/backend/Shared/CIPPSharp/bin/twemoji/1f697.png new file mode 100644 index 0000000000..fb0f6c86b6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f697.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f698.png b/backend/Shared/CIPPSharp/bin/twemoji/1f698.png new file mode 100644 index 0000000000..666ed7e013 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f698.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f699.png b/backend/Shared/CIPPSharp/bin/twemoji/1f699.png new file mode 100644 index 0000000000..4563409213 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f699.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f69a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f69a.png new file mode 100644 index 0000000000..2fadb0a186 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f69a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f69b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f69b.png new file mode 100644 index 0000000000..883f792ec1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f69b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f69c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f69c.png new file mode 100644 index 0000000000..715c1511d5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f69c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f69d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f69d.png new file mode 100644 index 0000000000..ce6a1c79b5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f69d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f69e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f69e.png new file mode 100644 index 0000000000..255b5fcb84 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f69e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f69f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f69f.png new file mode 100644 index 0000000000..5615e3b760 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f69f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a0.png new file mode 100644 index 0000000000..b0b508ab6b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a1.png new file mode 100644 index 0000000000..4c45163e0a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a2.png new file mode 100644 index 0000000000..aba0f76e00 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..fde490bb6e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..18b201b803 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fb.png new file mode 100644 index 0000000000..9bdd601069 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..16884b8eec Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..8e9d27becc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fc.png new file mode 100644 index 0000000000..ad8126e2e7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..c58930f590 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..e543a176de Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fd.png new file mode 100644 index 0000000000..7cb95e5eda Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..179b248bff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..fc992c215e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fe.png new file mode 100644 index 0000000000..44d884e57d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..3624440360 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..846573e3e2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3ff.png new file mode 100644 index 0000000000..270c275c45 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-200d-2640-fe0f.png new file mode 100644 index 0000000000..21e1468e9d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-200d-2642-fe0f.png new file mode 100644 index 0000000000..34cc521527 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3.png new file mode 100644 index 0000000000..405d114bfd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a4.png new file mode 100644 index 0000000000..2fea9d256f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a5.png new file mode 100644 index 0000000000..82e222ae62 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a6.png new file mode 100644 index 0000000000..0596ab3523 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a7.png new file mode 100644 index 0000000000..b4e63faff1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a8.png new file mode 100644 index 0000000000..170218b147 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6a9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6a9.png new file mode 100644 index 0000000000..aa650d6dcb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6a9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6aa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6aa.png new file mode 100644 index 0000000000..2ac4b18b0a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6aa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6ab.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6ab.png new file mode 100644 index 0000000000..43aba4d3fd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6ab.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6ac.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6ac.png new file mode 100644 index 0000000000..ec1b357237 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6ac.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6ad.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6ad.png new file mode 100644 index 0000000000..f1e1bc17c0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6ad.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6ae.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6ae.png new file mode 100644 index 0000000000..5636443371 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6ae.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6af.png new file mode 100644 index 0000000000..eb0b0ab56f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b0.png new file mode 100644 index 0000000000..e50afb54b4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b1.png new file mode 100644 index 0000000000..41ee54ab9e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b2.png new file mode 100644 index 0000000000..3c3b5f88bc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b3.png new file mode 100644 index 0000000000..9d911de7f8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..df15a18afe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..cd2a021e34 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fb.png new file mode 100644 index 0000000000..ed9418f8ea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..ab2991777e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..626267467e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fc.png new file mode 100644 index 0000000000..227b23c301 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..76f60cd6ff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..fa5a581e59 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fd.png new file mode 100644 index 0000000000..5dd31d7f10 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..6df286ef52 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..a5f0d7353f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fe.png new file mode 100644 index 0000000000..542172dd06 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..fdb0747c81 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..f6824bd060 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3ff.png new file mode 100644 index 0000000000..154ced30da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-200d-2640-fe0f.png new file mode 100644 index 0000000000..bf9729e40c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-200d-2642-fe0f.png new file mode 100644 index 0000000000..4e20da3472 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4.png new file mode 100644 index 0000000000..740460a5c4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..acf76d55ab Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..80193f2b18 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fb.png new file mode 100644 index 0000000000..f11d9b0e9f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..2015da4886 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..858838777c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fc.png new file mode 100644 index 0000000000..d64e9126c5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..781b6363f8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..0fa06e5e27 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fd.png new file mode 100644 index 0000000000..cdbec9bce3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..b016fd04f8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..35783c0b1d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fe.png new file mode 100644 index 0000000000..b47ec86843 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..8d48bcbad7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..99cd3437b2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3ff.png new file mode 100644 index 0000000000..77b5a67ebb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-200d-2640-fe0f.png new file mode 100644 index 0000000000..442571a93a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-200d-2642-fe0f.png new file mode 100644 index 0000000000..1a65cdaecb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5.png new file mode 100644 index 0000000000..a19fca05b9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..9242fe8b4d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..8adb675c40 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..648d6df1ff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..21de68e56d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb-200d-27a1-fe0f.png new file mode 100644 index 0000000000..44c8ebfba7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb.png new file mode 100644 index 0000000000..dedfe0def1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..dad007501a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..83393cbbb8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..648c7cea59 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..ad530e7e56 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..0e906c7847 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc.png new file mode 100644 index 0000000000..7e9f7f2742 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..423d9d5386 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..a02b5596e6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..67d234dfc7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..4ec1ab50c9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..4da7831d0e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd.png new file mode 100644 index 0000000000..e4cef4018c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..cecc609b1b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..1a7393e143 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..b001149131 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..8d60431ecd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe-200d-27a1-fe0f.png new file mode 100644 index 0000000000..5ca6ce8d9e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe.png new file mode 100644 index 0000000000..1713d959fb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..bc135e6c3c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..4c15c76936 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..cd564b41a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..4bc174b724 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff-200d-27a1-fe0f.png new file mode 100644 index 0000000000..fd81af89a2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff.png new file mode 100644 index 0000000000..f3719b6d06 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..ea8ac23cd7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-200d-2640-fe0f.png new file mode 100644 index 0000000000..33f0cf7414 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..61384241d1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-200d-2642-fe0f.png new file mode 100644 index 0000000000..59d7673e2b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-200d-27a1-fe0f.png new file mode 100644 index 0000000000..d83236e9be Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6.png new file mode 100644 index 0000000000..a45b3db78f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b7.png new file mode 100644 index 0000000000..92bfa62a99 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b8.png new file mode 100644 index 0000000000..72ff572c64 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6b9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6b9.png new file mode 100644 index 0000000000..ea42e7ff64 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6b9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6ba.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6ba.png new file mode 100644 index 0000000000..636d05b632 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6ba.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6bb.png new file mode 100644 index 0000000000..7bb19365e9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6bc.png new file mode 100644 index 0000000000..4195611226 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6bd.png new file mode 100644 index 0000000000..a85e96567c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6be.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6be.png new file mode 100644 index 0000000000..9583065733 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6be.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6bf.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6bf.png new file mode 100644 index 0000000000..59619c4bba Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6bf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6c0-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6c0-1f3fb.png new file mode 100644 index 0000000000..88d871a4f4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6c0-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6c0-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6c0-1f3fc.png new file mode 100644 index 0000000000..9f43d609db Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6c0-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6c0-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6c0-1f3fd.png new file mode 100644 index 0000000000..437bfe94b0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6c0-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6c0-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6c0-1f3fe.png new file mode 100644 index 0000000000..1ded3eccaa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6c0-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6c0-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6c0-1f3ff.png new file mode 100644 index 0000000000..4e030ad13d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6c0-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6c0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6c0.png new file mode 100644 index 0000000000..476a0c174b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6c0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6c1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6c1.png new file mode 100644 index 0000000000..cac11d9aa1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6c1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6c2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6c2.png new file mode 100644 index 0000000000..f3ded0a810 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6c2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6c3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6c3.png new file mode 100644 index 0000000000..b065c8d0da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6c3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6c4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6c4.png new file mode 100644 index 0000000000..6c15f41ed7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6c4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6c5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6c5.png new file mode 100644 index 0000000000..755c5ca8e3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6c5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6cb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6cb.png new file mode 100644 index 0000000000..2634ec310d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6cb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6cc-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6cc-1f3fb.png new file mode 100644 index 0000000000..1f9e71fc43 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6cc-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6cc-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6cc-1f3fc.png new file mode 100644 index 0000000000..bf38c8114a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6cc-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6cc-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6cc-1f3fd.png new file mode 100644 index 0000000000..c1e9c14341 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6cc-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6cc-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6cc-1f3fe.png new file mode 100644 index 0000000000..8764e991d0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6cc-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6cc-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6cc-1f3ff.png new file mode 100644 index 0000000000..3e02dc6ddb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6cc-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6cc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6cc.png new file mode 100644 index 0000000000..432262ecaf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6cc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6cd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6cd.png new file mode 100644 index 0000000000..6fd9e6bbe4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6cd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6ce.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6ce.png new file mode 100644 index 0000000000..82d49dab57 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6ce.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6cf.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6cf.png new file mode 100644 index 0000000000..5b2a2b641f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6cf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6d0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6d0.png new file mode 100644 index 0000000000..c064314c8b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6d0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6d1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6d1.png new file mode 100644 index 0000000000..583f00dfd6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6d1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6d2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6d2.png new file mode 100644 index 0000000000..1ca32e8330 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6d2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6d5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6d5.png new file mode 100644 index 0000000000..2c20004e8a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6d5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6d6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6d6.png new file mode 100644 index 0000000000..16e300625e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6d6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6d7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6d7.png new file mode 100644 index 0000000000..4670711dd6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6d7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6d8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6d8.png new file mode 100644 index 0000000000..cb1c69dc52 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6d8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6dc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6dc.png new file mode 100644 index 0000000000..3aad5d590d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6dc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6dd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6dd.png new file mode 100644 index 0000000000..a74dd95ae4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6dd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6de.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6de.png new file mode 100644 index 0000000000..13e845b4a7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6de.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6df.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6df.png new file mode 100644 index 0000000000..6bab85229c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6df.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6e0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6e0.png new file mode 100644 index 0000000000..6b64d3a11c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6e0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6e1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6e1.png new file mode 100644 index 0000000000..b29d246639 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6e1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6e2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6e2.png new file mode 100644 index 0000000000..32b10f1aa3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6e2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6e3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6e3.png new file mode 100644 index 0000000000..50d3dca2b1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6e4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6e4.png new file mode 100644 index 0000000000..fa1547075c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6e4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6e5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6e5.png new file mode 100644 index 0000000000..c573cb3d6c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6e5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6e9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6e9.png new file mode 100644 index 0000000000..ee9cfe1837 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6eb.png new file mode 100644 index 0000000000..34531e823c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6ec.png new file mode 100644 index 0000000000..f44f5c295e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6f0.png new file mode 100644 index 0000000000..d834f7b898 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6f3.png new file mode 100644 index 0000000000..8f0be1dedd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6f4.png new file mode 100644 index 0000000000..bbd247acba Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6f5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6f5.png new file mode 100644 index 0000000000..436efa3629 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6f5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6f6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6f6.png new file mode 100644 index 0000000000..99b6c1bbca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6f6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6f7.png new file mode 100644 index 0000000000..e0f5eb7ee9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6f8.png new file mode 100644 index 0000000000..ae7b217a2f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6f9.png new file mode 100644 index 0000000000..142b14d8d7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6fa.png new file mode 100644 index 0000000000..a0157fb735 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6fb.png new file mode 100644 index 0000000000..2bc271a7fd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f6fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f6fc.png new file mode 100644 index 0000000000..ba95b2eafa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f6fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f7e0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f7e0.png new file mode 100644 index 0000000000..006dfd73c1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f7e0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f7e1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f7e1.png new file mode 100644 index 0000000000..16b1609ced Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f7e1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f7e2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f7e2.png new file mode 100644 index 0000000000..8b267199d7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f7e2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f7e3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f7e3.png new file mode 100644 index 0000000000..194709defa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f7e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f7e4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f7e4.png new file mode 100644 index 0000000000..57d9f46b04 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f7e4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f7e5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f7e5.png new file mode 100644 index 0000000000..efd984d1b4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f7e5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f7e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f7e6.png new file mode 100644 index 0000000000..11304dd310 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f7e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f7e7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f7e7.png new file mode 100644 index 0000000000..5d987f7b4d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f7e7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f7e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f7e8.png new file mode 100644 index 0000000000..a55b13e717 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f7e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f7e9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f7e9.png new file mode 100644 index 0000000000..87fca3dd4b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f7e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f7ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f7ea.png new file mode 100644 index 0000000000..2d15d47907 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f7ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f7eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f7eb.png new file mode 100644 index 0000000000..859afd69ae Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f7eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f7f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f7f0.png new file mode 100644 index 0000000000..cc01767c2e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f7f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f90c-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f90c-1f3fb.png new file mode 100644 index 0000000000..1efddf99b9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f90c-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f90c-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f90c-1f3fc.png new file mode 100644 index 0000000000..884ed0dacf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f90c-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f90c-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f90c-1f3fd.png new file mode 100644 index 0000000000..00e0258211 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f90c-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f90c-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f90c-1f3fe.png new file mode 100644 index 0000000000..83de986a93 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f90c-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f90c-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f90c-1f3ff.png new file mode 100644 index 0000000000..fb4a3faa21 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f90c-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f90c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f90c.png new file mode 100644 index 0000000000..0fe8b356e2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f90c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f90d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f90d.png new file mode 100644 index 0000000000..38b3ed8eb4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f90d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f90e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f90e.png new file mode 100644 index 0000000000..f33e827de9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f90e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f90f-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f90f-1f3fb.png new file mode 100644 index 0000000000..23eb251ded Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f90f-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f90f-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f90f-1f3fc.png new file mode 100644 index 0000000000..bb45b10f51 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f90f-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f90f-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f90f-1f3fd.png new file mode 100644 index 0000000000..f733bfbaa5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f90f-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f90f-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f90f-1f3fe.png new file mode 100644 index 0000000000..156da313b3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f90f-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f90f-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f90f-1f3ff.png new file mode 100644 index 0000000000..883f5c79f8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f90f-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f90f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f90f.png new file mode 100644 index 0000000000..a463f0d583 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f90f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f910.png b/backend/Shared/CIPPSharp/bin/twemoji/1f910.png new file mode 100644 index 0000000000..8badd1278f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f910.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f911.png b/backend/Shared/CIPPSharp/bin/twemoji/1f911.png new file mode 100644 index 0000000000..e392e769dd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f911.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f912.png b/backend/Shared/CIPPSharp/bin/twemoji/1f912.png new file mode 100644 index 0000000000..79d49eb0a6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f912.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f913.png b/backend/Shared/CIPPSharp/bin/twemoji/1f913.png new file mode 100644 index 0000000000..2e231e4e36 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f913.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f914.png b/backend/Shared/CIPPSharp/bin/twemoji/1f914.png new file mode 100644 index 0000000000..354f5a14e2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f914.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f915.png b/backend/Shared/CIPPSharp/bin/twemoji/1f915.png new file mode 100644 index 0000000000..c8ff292665 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f915.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f916.png b/backend/Shared/CIPPSharp/bin/twemoji/1f916.png new file mode 100644 index 0000000000..3be87d30c7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f916.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f917.png b/backend/Shared/CIPPSharp/bin/twemoji/1f917.png new file mode 100644 index 0000000000..6d0f049fac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f917.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f918-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f918-1f3fb.png new file mode 100644 index 0000000000..e29d0ef48c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f918-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f918-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f918-1f3fc.png new file mode 100644 index 0000000000..1e4ffa6993 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f918-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f918-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f918-1f3fd.png new file mode 100644 index 0000000000..e672efd740 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f918-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f918-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f918-1f3fe.png new file mode 100644 index 0000000000..9203c6e771 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f918-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f918-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f918-1f3ff.png new file mode 100644 index 0000000000..a0803dbff4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f918-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f918.png b/backend/Shared/CIPPSharp/bin/twemoji/1f918.png new file mode 100644 index 0000000000..47144764c7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f918.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f919-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f919-1f3fb.png new file mode 100644 index 0000000000..2c7675b58c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f919-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f919-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f919-1f3fc.png new file mode 100644 index 0000000000..1389cf46ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f919-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f919-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f919-1f3fd.png new file mode 100644 index 0000000000..7a20f3039d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f919-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f919-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f919-1f3fe.png new file mode 100644 index 0000000000..eac1f18e77 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f919-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f919-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f919-1f3ff.png new file mode 100644 index 0000000000..7b82fed460 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f919-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f919.png b/backend/Shared/CIPPSharp/bin/twemoji/1f919.png new file mode 100644 index 0000000000..6a4dcb42de Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f919.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91a-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91a-1f3fb.png new file mode 100644 index 0000000000..e23d29d97c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91a-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91a-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91a-1f3fc.png new file mode 100644 index 0000000000..18afbc05ec Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91a-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91a-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91a-1f3fd.png new file mode 100644 index 0000000000..bce5db45dc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91a-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91a-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91a-1f3fe.png new file mode 100644 index 0000000000..b5c4c4aebe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91a-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91a-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91a-1f3ff.png new file mode 100644 index 0000000000..d1fe817445 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91a-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91a.png new file mode 100644 index 0000000000..6de21fb3f4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91b-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91b-1f3fb.png new file mode 100644 index 0000000000..ad304c95db Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91b-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91b-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91b-1f3fc.png new file mode 100644 index 0000000000..54c768aff7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91b-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91b-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91b-1f3fd.png new file mode 100644 index 0000000000..0abece218b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91b-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91b-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91b-1f3fe.png new file mode 100644 index 0000000000..b24da132a9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91b-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91b-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91b-1f3ff.png new file mode 100644 index 0000000000..465964a9d5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91b-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91b.png new file mode 100644 index 0000000000..0f44a9f4e8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91c-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91c-1f3fb.png new file mode 100644 index 0000000000..ac7cb248de Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91c-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91c-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91c-1f3fc.png new file mode 100644 index 0000000000..6227854be5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91c-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91c-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91c-1f3fd.png new file mode 100644 index 0000000000..d3c6ac0d0d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91c-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91c-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91c-1f3fe.png new file mode 100644 index 0000000000..84f3b032b3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91c-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91c-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91c-1f3ff.png new file mode 100644 index 0000000000..e8d3ee01d4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91c-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91c.png new file mode 100644 index 0000000000..13ac5dd272 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91d-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91d-1f3fb.png new file mode 100644 index 0000000000..0f3d86ba4e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91d-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91d-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91d-1f3fc.png new file mode 100644 index 0000000000..ef404f8d71 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91d-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91d-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91d-1f3fd.png new file mode 100644 index 0000000000..ea4b53b28d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91d-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91d-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91d-1f3fe.png new file mode 100644 index 0000000000..5e8363b6ea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91d-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91d-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91d-1f3ff.png new file mode 100644 index 0000000000..9e1eafb6eb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91d-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91d.png new file mode 100644 index 0000000000..c1cc9d069d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91e-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91e-1f3fb.png new file mode 100644 index 0000000000..e29a32222f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91e-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91e-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91e-1f3fc.png new file mode 100644 index 0000000000..17c89bdd22 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91e-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91e-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91e-1f3fd.png new file mode 100644 index 0000000000..0f279d8660 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91e-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91e-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91e-1f3fe.png new file mode 100644 index 0000000000..3a01379cb0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91e-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91e-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91e-1f3ff.png new file mode 100644 index 0000000000..282f833165 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91e-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91e.png new file mode 100644 index 0000000000..94d8e3a682 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91f-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91f-1f3fb.png new file mode 100644 index 0000000000..050f9e1a85 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91f-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91f-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91f-1f3fc.png new file mode 100644 index 0000000000..e1af8d68aa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91f-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91f-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91f-1f3fd.png new file mode 100644 index 0000000000..f8b4ec3eb5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91f-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91f-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91f-1f3fe.png new file mode 100644 index 0000000000..abd0c98873 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91f-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91f-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91f-1f3ff.png new file mode 100644 index 0000000000..fd5e2f4315 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91f-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f91f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f91f.png new file mode 100644 index 0000000000..c9dd359430 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f91f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f920.png b/backend/Shared/CIPPSharp/bin/twemoji/1f920.png new file mode 100644 index 0000000000..179af9fc12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f920.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f921.png b/backend/Shared/CIPPSharp/bin/twemoji/1f921.png new file mode 100644 index 0000000000..befcd928f3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f921.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f922.png b/backend/Shared/CIPPSharp/bin/twemoji/1f922.png new file mode 100644 index 0000000000..726e07a710 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f922.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f923.png b/backend/Shared/CIPPSharp/bin/twemoji/1f923.png new file mode 100644 index 0000000000..06419921d4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f923.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f924.png b/backend/Shared/CIPPSharp/bin/twemoji/1f924.png new file mode 100644 index 0000000000..47733cd9f2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f924.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f925.png b/backend/Shared/CIPPSharp/bin/twemoji/1f925.png new file mode 100644 index 0000000000..98fc089012 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f925.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..ff6832f23e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..9a880d0691 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fb.png new file mode 100644 index 0000000000..1d4cc4a41a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..fc54044bc9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..bc32acf9a1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fc.png new file mode 100644 index 0000000000..2628d2c6b5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..dd51cfd963 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..8b4e34a454 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fd.png new file mode 100644 index 0000000000..4ca0440921 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..756403023f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..ed371a1682 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fe.png new file mode 100644 index 0000000000..c3c67ec5fb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..622d19c8e8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..4ff960412d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3ff.png new file mode 100644 index 0000000000..8cb2827b70 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-200d-2640-fe0f.png new file mode 100644 index 0000000000..bd2226b024 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926-200d-2642-fe0f.png new file mode 100644 index 0000000000..1ca028b734 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f926.png b/backend/Shared/CIPPSharp/bin/twemoji/1f926.png new file mode 100644 index 0000000000..0e5111fa63 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f926.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f927.png b/backend/Shared/CIPPSharp/bin/twemoji/1f927.png new file mode 100644 index 0000000000..c5b524ec6c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f927.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f928.png b/backend/Shared/CIPPSharp/bin/twemoji/1f928.png new file mode 100644 index 0000000000..db22dd7d2f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f928.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f929.png b/backend/Shared/CIPPSharp/bin/twemoji/1f929.png new file mode 100644 index 0000000000..dc94d7d914 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f929.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f92a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f92a.png new file mode 100644 index 0000000000..bf2d10381a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f92a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f92b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f92b.png new file mode 100644 index 0000000000..3808dd3553 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f92b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f92c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f92c.png new file mode 100644 index 0000000000..5cde0c98da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f92c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f92d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f92d.png new file mode 100644 index 0000000000..6cc00cc793 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f92d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f92e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f92e.png new file mode 100644 index 0000000000..47ddebaa9b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f92e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f92f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f92f.png new file mode 100644 index 0000000000..a6ea47ee3f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f92f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f930-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f930-1f3fb.png new file mode 100644 index 0000000000..6caaf2e865 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f930-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f930-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f930-1f3fc.png new file mode 100644 index 0000000000..31a45c3ca4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f930-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f930-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f930-1f3fd.png new file mode 100644 index 0000000000..93f4a2a5a9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f930-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f930-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f930-1f3fe.png new file mode 100644 index 0000000000..11fc34f553 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f930-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f930-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f930-1f3ff.png new file mode 100644 index 0000000000..42cd09c285 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f930-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f930.png b/backend/Shared/CIPPSharp/bin/twemoji/1f930.png new file mode 100644 index 0000000000..685a2e7ec5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f930.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f931-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f931-1f3fb.png new file mode 100644 index 0000000000..b129c3fdab Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f931-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f931-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f931-1f3fc.png new file mode 100644 index 0000000000..8bdeb62553 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f931-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f931-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f931-1f3fd.png new file mode 100644 index 0000000000..c721900959 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f931-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f931-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f931-1f3fe.png new file mode 100644 index 0000000000..ec6060b74b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f931-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f931-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f931-1f3ff.png new file mode 100644 index 0000000000..eed8063463 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f931-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f931.png b/backend/Shared/CIPPSharp/bin/twemoji/1f931.png new file mode 100644 index 0000000000..dba766f9a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f931.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f932-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f932-1f3fb.png new file mode 100644 index 0000000000..535047e4ac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f932-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f932-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f932-1f3fc.png new file mode 100644 index 0000000000..205f7b7fb0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f932-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f932-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f932-1f3fd.png new file mode 100644 index 0000000000..92fbfe3b25 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f932-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f932-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f932-1f3fe.png new file mode 100644 index 0000000000..5b363bfa9c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f932-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f932-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f932-1f3ff.png new file mode 100644 index 0000000000..7cc2755df5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f932-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f932.png b/backend/Shared/CIPPSharp/bin/twemoji/1f932.png new file mode 100644 index 0000000000..7114175189 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f932.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f933-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f933-1f3fb.png new file mode 100644 index 0000000000..b117100ef6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f933-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f933-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f933-1f3fc.png new file mode 100644 index 0000000000..4b9373c584 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f933-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f933-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f933-1f3fd.png new file mode 100644 index 0000000000..929b6b942a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f933-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f933-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f933-1f3fe.png new file mode 100644 index 0000000000..1308327dd8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f933-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f933-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f933-1f3ff.png new file mode 100644 index 0000000000..549c6e4e9c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f933-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f933.png b/backend/Shared/CIPPSharp/bin/twemoji/1f933.png new file mode 100644 index 0000000000..f5eb251cbf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f933.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f934-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f934-1f3fb.png new file mode 100644 index 0000000000..ebc44218ad Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f934-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f934-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f934-1f3fc.png new file mode 100644 index 0000000000..fe8fe2230b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f934-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f934-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f934-1f3fd.png new file mode 100644 index 0000000000..d2c58410be Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f934-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f934-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f934-1f3fe.png new file mode 100644 index 0000000000..9079fe4876 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f934-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f934-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f934-1f3ff.png new file mode 100644 index 0000000000..1a781d825a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f934-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f934.png b/backend/Shared/CIPPSharp/bin/twemoji/1f934.png new file mode 100644 index 0000000000..9527136fa7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f934.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..981df10b9e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..22f79e5263 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fb.png new file mode 100644 index 0000000000..fb2c509156 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..5f7d09f6a9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..3f636db81d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fc.png new file mode 100644 index 0000000000..03f5023e4e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..6a7726db16 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..a631877785 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fd.png new file mode 100644 index 0000000000..a3675888b5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..95a815eab4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..2d3166fe0c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fe.png new file mode 100644 index 0000000000..5133d6d9d1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..693097e5c0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..340d98fc18 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3ff.png new file mode 100644 index 0000000000..17ff9f3f75 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-200d-2640-fe0f.png new file mode 100644 index 0000000000..fad0f83bce Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935-200d-2642-fe0f.png new file mode 100644 index 0000000000..f5e421bd3a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f935.png b/backend/Shared/CIPPSharp/bin/twemoji/1f935.png new file mode 100644 index 0000000000..cc67ef4246 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f935.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f936-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f936-1f3fb.png new file mode 100644 index 0000000000..6d3c340692 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f936-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f936-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f936-1f3fc.png new file mode 100644 index 0000000000..52c9ba810d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f936-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f936-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f936-1f3fd.png new file mode 100644 index 0000000000..9651385708 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f936-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f936-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f936-1f3fe.png new file mode 100644 index 0000000000..3ec0c5686e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f936-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f936-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f936-1f3ff.png new file mode 100644 index 0000000000..f86966be83 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f936-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f936.png b/backend/Shared/CIPPSharp/bin/twemoji/1f936.png new file mode 100644 index 0000000000..56dc794884 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f936.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..35d2519be0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..6f4807fb97 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fb.png new file mode 100644 index 0000000000..8f8b579da1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..d0f463f708 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..c882436eba Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fc.png new file mode 100644 index 0000000000..622a19295b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..d839134f89 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..b73d2c5e05 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fd.png new file mode 100644 index 0000000000..eed23a44b7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..b773e32d4c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..48b8959adc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fe.png new file mode 100644 index 0000000000..5490832285 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..4b1468dac2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..e9df52e5cb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3ff.png new file mode 100644 index 0000000000..4fd0d85d9c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-200d-2640-fe0f.png new file mode 100644 index 0000000000..00f36c6b2b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937-200d-2642-fe0f.png new file mode 100644 index 0000000000..4e4c51d015 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f937.png b/backend/Shared/CIPPSharp/bin/twemoji/1f937.png new file mode 100644 index 0000000000..92b5575c35 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f937.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..734e052c74 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..d1ac33c3e7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fb.png new file mode 100644 index 0000000000..e993855704 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..9dd74e26a6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..ccfa25496c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fc.png new file mode 100644 index 0000000000..a44b98416e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..71061064d5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..006782472b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fd.png new file mode 100644 index 0000000000..c2cb4e7918 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..b727f95c9c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..34c246667a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fe.png new file mode 100644 index 0000000000..6c02fd65d1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..4b1bf03787 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..fe77b725e4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3ff.png new file mode 100644 index 0000000000..8c5919fd80 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-200d-2640-fe0f.png new file mode 100644 index 0000000000..dfe3070597 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938-200d-2642-fe0f.png new file mode 100644 index 0000000000..e783fe7489 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f938.png b/backend/Shared/CIPPSharp/bin/twemoji/1f938.png new file mode 100644 index 0000000000..00dc8f4214 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f938.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..8840e5587f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..d4c8c6cdf9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fb.png new file mode 100644 index 0000000000..253b8b880c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..22770625fb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..f075c7d1c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fc.png new file mode 100644 index 0000000000..1056ad88ed Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..21b04a87ef Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..95bc0a8601 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fd.png new file mode 100644 index 0000000000..1498c77f4f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..aa8e325bd6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..c038dd8bff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fe.png new file mode 100644 index 0000000000..618dc1697b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..461600ba19 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..ae2401b480 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3ff.png new file mode 100644 index 0000000000..02222272ad Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-200d-2640-fe0f.png new file mode 100644 index 0000000000..482bf46dec Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939-200d-2642-fe0f.png new file mode 100644 index 0000000000..555d35ccd9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f939.png b/backend/Shared/CIPPSharp/bin/twemoji/1f939.png new file mode 100644 index 0000000000..04e2c586ee Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f939.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93a.png new file mode 100644 index 0000000000..0781fdab50 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..b11efe803d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..20270806fa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fb.png new file mode 100644 index 0000000000..6eec184f7d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..92974fd9b0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..76fecae1da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fc.png new file mode 100644 index 0000000000..9adfffd496 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..762e0b417b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..0a93155c4e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fd.png new file mode 100644 index 0000000000..e3bf239bbb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..310597da1b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..e456ab89bb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fe.png new file mode 100644 index 0000000000..1f9fc47dc1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..5a6f0991f5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..1a9879a927 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3ff.png new file mode 100644 index 0000000000..c795ec3cd8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-200d-2640-fe0f.png new file mode 100644 index 0000000000..8f51f00fc2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-200d-2642-fe0f.png new file mode 100644 index 0000000000..259c87e7c1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93c.png new file mode 100644 index 0000000000..3d50b96c0d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..3633cb03dc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..8a61156f8e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fb.png new file mode 100644 index 0000000000..5a2a755ef7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..1b7ec16a5e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..501bd5783f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fc.png new file mode 100644 index 0000000000..8f3ac54526 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..b905b42090 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..db5c1f7925 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fd.png new file mode 100644 index 0000000000..9b533b2b13 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..94d2f57414 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..0325893b3d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fe.png new file mode 100644 index 0000000000..41a3d6071f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..fb287729a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..b4e89e7530 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3ff.png new file mode 100644 index 0000000000..5e1d803049 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-200d-2640-fe0f.png new file mode 100644 index 0000000000..db9a6451a4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-200d-2642-fe0f.png new file mode 100644 index 0000000000..b0c8a9e364 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93d.png new file mode 100644 index 0000000000..86dfb8be12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..e08716d150 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..00466fd8db Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fb.png new file mode 100644 index 0000000000..b4705b5f69 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..a1d7c67c33 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..bb1ad889fd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fc.png new file mode 100644 index 0000000000..a2297db966 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..3340792ec2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..f3f33a03dc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fd.png new file mode 100644 index 0000000000..6e83120bd7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..33ce2ea00c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..bd4593bba3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fe.png new file mode 100644 index 0000000000..e9e6d6a787 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..ee6a7a8efa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..b7042235b0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3ff.png new file mode 100644 index 0000000000..f723f73a47 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-200d-2640-fe0f.png new file mode 100644 index 0000000000..daacbb2513 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-200d-2642-fe0f.png new file mode 100644 index 0000000000..a87ffc0128 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93e.png new file mode 100644 index 0000000000..f5db924d0b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f93f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f93f.png new file mode 100644 index 0000000000..8bf1de49a7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f93f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f940.png b/backend/Shared/CIPPSharp/bin/twemoji/1f940.png new file mode 100644 index 0000000000..9390ebfcbc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f940.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f941.png b/backend/Shared/CIPPSharp/bin/twemoji/1f941.png new file mode 100644 index 0000000000..19c427bdfd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f941.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f942.png b/backend/Shared/CIPPSharp/bin/twemoji/1f942.png new file mode 100644 index 0000000000..4801c68fd6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f942.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f943.png b/backend/Shared/CIPPSharp/bin/twemoji/1f943.png new file mode 100644 index 0000000000..a495095feb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f943.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f944.png b/backend/Shared/CIPPSharp/bin/twemoji/1f944.png new file mode 100644 index 0000000000..1c418ebd9f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f944.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f945.png b/backend/Shared/CIPPSharp/bin/twemoji/1f945.png new file mode 100644 index 0000000000..fb5aeffcfa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f945.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f947.png b/backend/Shared/CIPPSharp/bin/twemoji/1f947.png new file mode 100644 index 0000000000..7f1df40486 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f947.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f948.png b/backend/Shared/CIPPSharp/bin/twemoji/1f948.png new file mode 100644 index 0000000000..d451fd52d1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f948.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f949.png b/backend/Shared/CIPPSharp/bin/twemoji/1f949.png new file mode 100644 index 0000000000..f5c87e3cb0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f949.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f94a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f94a.png new file mode 100644 index 0000000000..4f5f0333bd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f94a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f94b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f94b.png new file mode 100644 index 0000000000..30fee1e368 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f94b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f94c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f94c.png new file mode 100644 index 0000000000..65228c2db8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f94c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f94d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f94d.png new file mode 100644 index 0000000000..8202c8b34b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f94d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f94e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f94e.png new file mode 100644 index 0000000000..b78c695486 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f94e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f94f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f94f.png new file mode 100644 index 0000000000..ef34259ead Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f94f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f950.png b/backend/Shared/CIPPSharp/bin/twemoji/1f950.png new file mode 100644 index 0000000000..3788e1bd43 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f950.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f951.png b/backend/Shared/CIPPSharp/bin/twemoji/1f951.png new file mode 100644 index 0000000000..2af5fc6bdf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f951.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f952.png b/backend/Shared/CIPPSharp/bin/twemoji/1f952.png new file mode 100644 index 0000000000..1bac31e5cd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f952.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f953.png b/backend/Shared/CIPPSharp/bin/twemoji/1f953.png new file mode 100644 index 0000000000..6b2418e79a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f953.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f954.png b/backend/Shared/CIPPSharp/bin/twemoji/1f954.png new file mode 100644 index 0000000000..bf3d9b9c47 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f954.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f955.png b/backend/Shared/CIPPSharp/bin/twemoji/1f955.png new file mode 100644 index 0000000000..831d63911c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f955.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f956.png b/backend/Shared/CIPPSharp/bin/twemoji/1f956.png new file mode 100644 index 0000000000..0b9cc251bc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f956.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f957.png b/backend/Shared/CIPPSharp/bin/twemoji/1f957.png new file mode 100644 index 0000000000..f255f4d6ec Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f957.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f958.png b/backend/Shared/CIPPSharp/bin/twemoji/1f958.png new file mode 100644 index 0000000000..39b64e069e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f958.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f959.png b/backend/Shared/CIPPSharp/bin/twemoji/1f959.png new file mode 100644 index 0000000000..f3377b9382 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f959.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f95a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f95a.png new file mode 100644 index 0000000000..6d03b79044 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f95a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f95b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f95b.png new file mode 100644 index 0000000000..234419e003 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f95b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f95c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f95c.png new file mode 100644 index 0000000000..e8f17a1a97 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f95c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f95d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f95d.png new file mode 100644 index 0000000000..3ba423b22f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f95d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f95e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f95e.png new file mode 100644 index 0000000000..6bcded7237 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f95e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f95f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f95f.png new file mode 100644 index 0000000000..82136b8ef4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f95f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f960.png b/backend/Shared/CIPPSharp/bin/twemoji/1f960.png new file mode 100644 index 0000000000..a1e8958af7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f960.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f961.png b/backend/Shared/CIPPSharp/bin/twemoji/1f961.png new file mode 100644 index 0000000000..20d8554969 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f961.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f962.png b/backend/Shared/CIPPSharp/bin/twemoji/1f962.png new file mode 100644 index 0000000000..8284c128fe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f962.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f963.png b/backend/Shared/CIPPSharp/bin/twemoji/1f963.png new file mode 100644 index 0000000000..2905488376 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f963.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f964.png b/backend/Shared/CIPPSharp/bin/twemoji/1f964.png new file mode 100644 index 0000000000..f65704ebfd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f964.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f965.png b/backend/Shared/CIPPSharp/bin/twemoji/1f965.png new file mode 100644 index 0000000000..e92e8d403d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f965.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f966.png b/backend/Shared/CIPPSharp/bin/twemoji/1f966.png new file mode 100644 index 0000000000..24b0d3d527 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f966.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f967.png b/backend/Shared/CIPPSharp/bin/twemoji/1f967.png new file mode 100644 index 0000000000..1d17609efb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f967.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f968.png b/backend/Shared/CIPPSharp/bin/twemoji/1f968.png new file mode 100644 index 0000000000..94fac17b41 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f968.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f969.png b/backend/Shared/CIPPSharp/bin/twemoji/1f969.png new file mode 100644 index 0000000000..5eab364621 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f969.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f96a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f96a.png new file mode 100644 index 0000000000..a0a6a6da27 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f96a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f96b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f96b.png new file mode 100644 index 0000000000..01d7649d2d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f96b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f96c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f96c.png new file mode 100644 index 0000000000..8487529df1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f96c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f96d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f96d.png new file mode 100644 index 0000000000..57e84dea3c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f96d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f96e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f96e.png new file mode 100644 index 0000000000..991c3b4f1c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f96e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f96f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f96f.png new file mode 100644 index 0000000000..99312be2fe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f96f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f970.png b/backend/Shared/CIPPSharp/bin/twemoji/1f970.png new file mode 100644 index 0000000000..0c9466ce77 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f970.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f971.png b/backend/Shared/CIPPSharp/bin/twemoji/1f971.png new file mode 100644 index 0000000000..8c12578bf6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f971.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f972.png b/backend/Shared/CIPPSharp/bin/twemoji/1f972.png new file mode 100644 index 0000000000..c799f4bbfa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f972.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f973.png b/backend/Shared/CIPPSharp/bin/twemoji/1f973.png new file mode 100644 index 0000000000..cf02521ccc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f973.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f974.png b/backend/Shared/CIPPSharp/bin/twemoji/1f974.png new file mode 100644 index 0000000000..3c4a112bec Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f974.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f975.png b/backend/Shared/CIPPSharp/bin/twemoji/1f975.png new file mode 100644 index 0000000000..2ed695a362 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f975.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f976.png b/backend/Shared/CIPPSharp/bin/twemoji/1f976.png new file mode 100644 index 0000000000..6d14820c56 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f976.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f977-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f977-1f3fb.png new file mode 100644 index 0000000000..1382f448f3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f977-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f977-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f977-1f3fc.png new file mode 100644 index 0000000000..f61b5a5ecc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f977-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f977-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f977-1f3fd.png new file mode 100644 index 0000000000..b1a6a3a221 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f977-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f977-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f977-1f3fe.png new file mode 100644 index 0000000000..820a3ee6ce Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f977-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f977-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f977-1f3ff.png new file mode 100644 index 0000000000..c67b540ab2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f977-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f977.png b/backend/Shared/CIPPSharp/bin/twemoji/1f977.png new file mode 100644 index 0000000000..63f3e16bb2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f977.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f978.png b/backend/Shared/CIPPSharp/bin/twemoji/1f978.png new file mode 100644 index 0000000000..390b7e4e16 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f978.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f979.png b/backend/Shared/CIPPSharp/bin/twemoji/1f979.png new file mode 100644 index 0000000000..d109b16453 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f979.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f97a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f97a.png new file mode 100644 index 0000000000..b1cc8fb71e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f97a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f97b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f97b.png new file mode 100644 index 0000000000..37e499fb76 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f97b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f97c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f97c.png new file mode 100644 index 0000000000..b6ed6b935f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f97c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f97d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f97d.png new file mode 100644 index 0000000000..b4d3c40abc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f97d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f97e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f97e.png new file mode 100644 index 0000000000..1e97c61b01 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f97e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f97f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f97f.png new file mode 100644 index 0000000000..fded27f574 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f97f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f980.png b/backend/Shared/CIPPSharp/bin/twemoji/1f980.png new file mode 100644 index 0000000000..915354ca2f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f980.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f981.png b/backend/Shared/CIPPSharp/bin/twemoji/1f981.png new file mode 100644 index 0000000000..07ca6147bd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f981.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f982.png b/backend/Shared/CIPPSharp/bin/twemoji/1f982.png new file mode 100644 index 0000000000..767cf550d0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f982.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f983.png b/backend/Shared/CIPPSharp/bin/twemoji/1f983.png new file mode 100644 index 0000000000..2113718e6b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f983.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f984.png b/backend/Shared/CIPPSharp/bin/twemoji/1f984.png new file mode 100644 index 0000000000..0e3279c7bd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f984.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f985.png b/backend/Shared/CIPPSharp/bin/twemoji/1f985.png new file mode 100644 index 0000000000..c8869604fb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f985.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f986.png b/backend/Shared/CIPPSharp/bin/twemoji/1f986.png new file mode 100644 index 0000000000..d4e995eedc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f986.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f987.png b/backend/Shared/CIPPSharp/bin/twemoji/1f987.png new file mode 100644 index 0000000000..e0375fb660 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f987.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f988.png b/backend/Shared/CIPPSharp/bin/twemoji/1f988.png new file mode 100644 index 0000000000..88ccc81e30 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f988.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f989.png b/backend/Shared/CIPPSharp/bin/twemoji/1f989.png new file mode 100644 index 0000000000..6bc9c7af42 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f989.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f98a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f98a.png new file mode 100644 index 0000000000..7f6e50a802 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f98a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f98b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f98b.png new file mode 100644 index 0000000000..3d17d98107 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f98b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f98c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f98c.png new file mode 100644 index 0000000000..06b32f6f4b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f98c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f98d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f98d.png new file mode 100644 index 0000000000..bd02ff4d36 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f98d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f98e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f98e.png new file mode 100644 index 0000000000..dc7b028272 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f98e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f98f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f98f.png new file mode 100644 index 0000000000..9b43bbf25b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f98f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f990.png b/backend/Shared/CIPPSharp/bin/twemoji/1f990.png new file mode 100644 index 0000000000..f48ec33e62 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f990.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f991.png b/backend/Shared/CIPPSharp/bin/twemoji/1f991.png new file mode 100644 index 0000000000..8e1aa1e27a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f991.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f992.png b/backend/Shared/CIPPSharp/bin/twemoji/1f992.png new file mode 100644 index 0000000000..731f72fac9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f992.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f993.png b/backend/Shared/CIPPSharp/bin/twemoji/1f993.png new file mode 100644 index 0000000000..4c080f3006 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f993.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f994.png b/backend/Shared/CIPPSharp/bin/twemoji/1f994.png new file mode 100644 index 0000000000..489827231b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f994.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f995.png b/backend/Shared/CIPPSharp/bin/twemoji/1f995.png new file mode 100644 index 0000000000..b58a3179e3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f995.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f996.png b/backend/Shared/CIPPSharp/bin/twemoji/1f996.png new file mode 100644 index 0000000000..ed7927dd3f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f996.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f997.png b/backend/Shared/CIPPSharp/bin/twemoji/1f997.png new file mode 100644 index 0000000000..481034afa2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f997.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f998.png b/backend/Shared/CIPPSharp/bin/twemoji/1f998.png new file mode 100644 index 0000000000..c14c89466d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f998.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f999.png b/backend/Shared/CIPPSharp/bin/twemoji/1f999.png new file mode 100644 index 0000000000..d43ae19ae1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f999.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f99a.png b/backend/Shared/CIPPSharp/bin/twemoji/1f99a.png new file mode 100644 index 0000000000..59602cdcf0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f99a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f99b.png b/backend/Shared/CIPPSharp/bin/twemoji/1f99b.png new file mode 100644 index 0000000000..747f4136f4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f99b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f99c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f99c.png new file mode 100644 index 0000000000..4630636a20 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f99c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f99d.png b/backend/Shared/CIPPSharp/bin/twemoji/1f99d.png new file mode 100644 index 0000000000..759c064422 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f99d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f99e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f99e.png new file mode 100644 index 0000000000..5312cade84 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f99e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f99f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f99f.png new file mode 100644 index 0000000000..fe18c2a6ec Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f99f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9a0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9a0.png new file mode 100644 index 0000000000..bdd887e1d5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9a0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9a1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9a1.png new file mode 100644 index 0000000000..858196e8f6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9a1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9a2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9a2.png new file mode 100644 index 0000000000..58af7f5086 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9a2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9a3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9a3.png new file mode 100644 index 0000000000..94c8998c79 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9a3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9a4.png new file mode 100644 index 0000000000..160cc563da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9a5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9a5.png new file mode 100644 index 0000000000..2bb0545286 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9a5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9a6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9a6.png new file mode 100644 index 0000000000..fb3e07085f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9a6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9a7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9a7.png new file mode 100644 index 0000000000..17d26498bb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9a7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9a8.png new file mode 100644 index 0000000000..d342d560c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9a9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9a9.png new file mode 100644 index 0000000000..0ee76f3240 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9a9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9aa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9aa.png new file mode 100644 index 0000000000..5649bbfad4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9aa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ab.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ab.png new file mode 100644 index 0000000000..11839911b1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ab.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ac.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ac.png new file mode 100644 index 0000000000..744a814ec6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ac.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ad.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ad.png new file mode 100644 index 0000000000..1dc20feef0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ad.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ae.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ae.png new file mode 100644 index 0000000000..0bf6ca9eb6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ae.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9af.png new file mode 100644 index 0000000000..d09b2846c7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b0.png new file mode 100644 index 0000000000..ae61441680 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b1.png new file mode 100644 index 0000000000..8b79646863 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b2.png new file mode 100644 index 0000000000..34022c6b98 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b3.png new file mode 100644 index 0000000000..e062ea9d52 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b4.png new file mode 100644 index 0000000000..92062ac853 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b5-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b5-1f3fb.png new file mode 100644 index 0000000000..3dd05b71fe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b5-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b5-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b5-1f3fc.png new file mode 100644 index 0000000000..3678ebea22 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b5-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b5-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b5-1f3fd.png new file mode 100644 index 0000000000..7a8be7e7ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b5-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b5-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b5-1f3fe.png new file mode 100644 index 0000000000..7488b0c298 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b5-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b5-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b5-1f3ff.png new file mode 100644 index 0000000000..498f404c67 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b5-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b5.png new file mode 100644 index 0000000000..b0087b4926 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b6-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b6-1f3fb.png new file mode 100644 index 0000000000..628899a81c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b6-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b6-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b6-1f3fc.png new file mode 100644 index 0000000000..fc3afc3a82 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b6-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b6-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b6-1f3fd.png new file mode 100644 index 0000000000..195ae0d0e8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b6-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b6-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b6-1f3fe.png new file mode 100644 index 0000000000..cabdb40e08 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b6-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b6-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b6-1f3ff.png new file mode 100644 index 0000000000..47fcee6b93 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b6-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b6.png new file mode 100644 index 0000000000..ed57c2bd9c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b7.png new file mode 100644 index 0000000000..f5d32ea713 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..7caf678b32 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..d5354946f1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fb.png new file mode 100644 index 0000000000..c9928c9e29 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..835b312056 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..9afb6294cf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fc.png new file mode 100644 index 0000000000..4db64c4f10 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..510ef57152 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..a5917aadc9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fd.png new file mode 100644 index 0000000000..55dad3c658 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..ba21b3c465 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..75fa8b4fc9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fe.png new file mode 100644 index 0000000000..01e4f882e9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..9a2cbb400b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..fc34c480bc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3ff.png new file mode 100644 index 0000000000..698ee383c0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-200d-2640-fe0f.png new file mode 100644 index 0000000000..5f91609e83 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-200d-2642-fe0f.png new file mode 100644 index 0000000000..5d825059fc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8.png new file mode 100644 index 0000000000..d79cfc97d8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..21759f8a77 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..e1553453f0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fb.png new file mode 100644 index 0000000000..f2ca77d3e6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..2baaab958b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..11c63297c0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fc.png new file mode 100644 index 0000000000..a96d25be0f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..50b0ff7364 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..3bf15398a2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fd.png new file mode 100644 index 0000000000..d8f77eaf9b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..80bd13c9be Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..08803cba0f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fe.png new file mode 100644 index 0000000000..3a6cbde1e7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..bbdcd028e8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..0f0b10435c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3ff.png new file mode 100644 index 0000000000..1397c3f592 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-200d-2640-fe0f.png new file mode 100644 index 0000000000..e28243484d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-200d-2642-fe0f.png new file mode 100644 index 0000000000..7b58aaba6d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9b9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9.png new file mode 100644 index 0000000000..f86bd6bd8c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9b9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ba.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ba.png new file mode 100644 index 0000000000..f1b86e164d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ba.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9bb-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9bb-1f3fb.png new file mode 100644 index 0000000000..a2bcf4355b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9bb-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9bb-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9bb-1f3fc.png new file mode 100644 index 0000000000..0b90533534 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9bb-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9bb-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9bb-1f3fd.png new file mode 100644 index 0000000000..ba898631df Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9bb-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9bb-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9bb-1f3fe.png new file mode 100644 index 0000000000..b019addc1e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9bb-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9bb-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9bb-1f3ff.png new file mode 100644 index 0000000000..8a37cc0f60 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9bb-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9bb.png new file mode 100644 index 0000000000..492632f175 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9bc.png new file mode 100644 index 0000000000..eea4289378 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9bd.png new file mode 100644 index 0000000000..c56f257680 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9be.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9be.png new file mode 100644 index 0000000000..d758cf5bb7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9be.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9bf.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9bf.png new file mode 100644 index 0000000000..9c0fed3c90 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9bf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9c0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9c0.png new file mode 100644 index 0000000000..4f88fa06df Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9c0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9c1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9c1.png new file mode 100644 index 0000000000..39ad36c043 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9c1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9c2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9c2.png new file mode 100644 index 0000000000..053367714a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9c2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9c3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9c3.png new file mode 100644 index 0000000000..fce80c8232 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9c3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9c4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9c4.png new file mode 100644 index 0000000000..65c307104e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9c4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9c5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9c5.png new file mode 100644 index 0000000000..73e1fea8ef Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9c5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9c6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9c6.png new file mode 100644 index 0000000000..98fafac5d4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9c6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9c7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9c7.png new file mode 100644 index 0000000000..8696bf4b90 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9c7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9c8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9c8.png new file mode 100644 index 0000000000..a5d5528001 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9c8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9c9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9c9.png new file mode 100644 index 0000000000..9523074ce0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9c9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ca.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ca.png new file mode 100644 index 0000000000..57b37162bb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ca.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cb.png new file mode 100644 index 0000000000..3fbdf46407 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cc.png new file mode 100644 index 0000000000..86a1a90f56 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..03a420f91d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..d1fe8a5154 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fb.png new file mode 100644 index 0000000000..ef9ca45c9b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..d9cc521065 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..d01f6d3ed7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fc.png new file mode 100644 index 0000000000..bc4fa44c30 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..8019d6c651 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..16c5908cda Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fd.png new file mode 100644 index 0000000000..6ec42816a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..334f947251 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..3ba70f7fbe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fe.png new file mode 100644 index 0000000000..24a5bc2094 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..15b740272d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..0c68629a7a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3ff.png new file mode 100644 index 0000000000..62f92a01a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-200d-2640-fe0f.png new file mode 100644 index 0000000000..fb6bbc852e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-200d-2642-fe0f.png new file mode 100644 index 0000000000..af90767fed Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd.png new file mode 100644 index 0000000000..092bc94cfa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..15435fa1e8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..84b1932a22 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..4c3951ab87 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..88ac6639ff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb-200d-27a1-fe0f.png new file mode 100644 index 0000000000..304c2e1c57 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb.png new file mode 100644 index 0000000000..e0709b9cc7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..45e11b9ee5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..f9703876f8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..bbf8fc067e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..c13fba6b9b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..88e52c6bff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc.png new file mode 100644 index 0000000000..a009cd3d02 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..04ddfe6d2f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..fff814de35 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..97c3cd6a66 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..1e94283e0b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..192d58f13c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd.png new file mode 100644 index 0000000000..bee23dd367 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..e192507d3c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..ea9b468cd5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..241c001316 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..37167a9517 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe-200d-27a1-fe0f.png new file mode 100644 index 0000000000..e4c2c62961 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe.png new file mode 100644 index 0000000000..38d173fbae Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..6d45e67e59 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..a08af4741e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..2290a39f7f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..e0b00f3c7f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff-200d-27a1-fe0f.png new file mode 100644 index 0000000000..933cd847cc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff.png new file mode 100644 index 0000000000..903927bf3e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-200d-2640-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-200d-2640-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..f663666173 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-200d-2640-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-200d-2640-fe0f.png new file mode 100644 index 0000000000..3fc023d8c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-200d-2642-fe0f-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-200d-2642-fe0f-200d-27a1-fe0f.png new file mode 100644 index 0000000000..7dc7c1eff5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-200d-2642-fe0f-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-200d-2642-fe0f.png new file mode 100644 index 0000000000..3fffc15dd7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-200d-27a1-fe0f.png new file mode 100644 index 0000000000..fe7d99ddcd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ce.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce.png new file mode 100644 index 0000000000..d59df78e5a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ce.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..add2f37e13 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..49c176d765 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fb.png new file mode 100644 index 0000000000..e323f4d602 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..04e20bfabc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..597c758ad8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fc.png new file mode 100644 index 0000000000..6c520175a1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..189eadc665 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..9201ed2c38 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fd.png new file mode 100644 index 0000000000..6819537224 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..6ad5401b20 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..ef77e24e5c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fe.png new file mode 100644 index 0000000000..b9f5a2e2b3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..7402f46bbf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..c603479f2c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3ff.png new file mode 100644 index 0000000000..9e0b55eee1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-200d-2640-fe0f.png new file mode 100644 index 0000000000..36c5770c17 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-200d-2642-fe0f.png new file mode 100644 index 0000000000..86d326cc5f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9cf.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf.png new file mode 100644 index 0000000000..0af9d1827d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9cf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d0.png new file mode 100644 index 0000000000..33b85f47a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f33e.png new file mode 100644 index 0000000000..e5a3b549bd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f373.png new file mode 100644 index 0000000000..eb9beba629 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f37c.png new file mode 100644 index 0000000000..4932c45b65 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f384.png new file mode 100644 index 0000000000..172f05ce7b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f393.png new file mode 100644 index 0000000000..04a6808924 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f3a4.png new file mode 100644 index 0000000000..0c7b34b6a2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f3a8.png new file mode 100644 index 0000000000..477eb7b768 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f3eb.png new file mode 100644 index 0000000000..f4af417b4a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f3ed.png new file mode 100644 index 0000000000..cd118e602f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f430-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f430-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..4425b12748 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f430-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f430-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f430-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..6302b329d2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f430-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f430-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f430-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..9cd9c1311f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f430-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f430-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f430-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..556bd0790b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f430-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f4bb.png new file mode 100644 index 0000000000..dd92bca709 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f4bc.png new file mode 100644 index 0000000000..b579f983e7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f527.png new file mode 100644 index 0000000000..98b9e863b1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f52c.png new file mode 100644 index 0000000000..c1124158a7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f680.png new file mode 100644 index 0000000000..78c64474ab Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f692.png new file mode 100644 index 0000000000..dde98a30dd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..68770f73c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..634f7a4466 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..50859462f4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..b231dd826d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..9e3ed2aeb2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..1eb7c3a6ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9af.png new file mode 100644 index 0000000000..503ee15605 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9b0.png new file mode 100644 index 0000000000..ced2e66c48 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9b1.png new file mode 100644 index 0000000000..2b50eecf35 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9b2.png new file mode 100644 index 0000000000..88e6ecb323 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9b3.png new file mode 100644 index 0000000000..1e07f773a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..10965c626f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9bc.png new file mode 100644 index 0000000000..503975f062 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..861868c27b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9bd.png new file mode 100644 index 0000000000..33b5c740c9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1fa70.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1fa70.png new file mode 100644 index 0000000000..1f3871ed25 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1fa70.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1faef-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1faef-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..f38875ae4b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1faef-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1faef-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1faef-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..4485e23da3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1faef-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1faef-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1faef-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..0ccf2d14e7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1faef-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1faef-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1faef-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..d0ca8151ff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-1faef-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2695-fe0f.png new file mode 100644 index 0000000000..11ed16166b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2696-fe0f.png new file mode 100644 index 0000000000..7831dd8e15 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2708-fe0f.png new file mode 100644 index 0000000000..ad7873dabe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..24471dfbf4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..69cb7fdab5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..4aaa6add7e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..347575b4ee Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..0ce8aca1fc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..33ecddcd69 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..ae01e0416e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..22ff8e141d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb.png new file mode 100644 index 0000000000..31093adbc4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f33e.png new file mode 100644 index 0000000000..905e04f98f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f373.png new file mode 100644 index 0000000000..d6042efa39 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f37c.png new file mode 100644 index 0000000000..46af1cc5f4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f384.png new file mode 100644 index 0000000000..a25be0e159 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f393.png new file mode 100644 index 0000000000..f1052bb585 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f3a4.png new file mode 100644 index 0000000000..03f3eec5d6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f3a8.png new file mode 100644 index 0000000000..37811e394e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f3eb.png new file mode 100644 index 0000000000..b43288fa3c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f3ed.png new file mode 100644 index 0000000000..c13227b4de Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f430-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f430-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..6b87b5b99a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f430-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f430-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f430-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..588d952f4b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f430-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f430-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f430-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..6e197a0920 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f430-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f430-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f430-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..64c1613253 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f430-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f4bb.png new file mode 100644 index 0000000000..17a3674698 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f4bc.png new file mode 100644 index 0000000000..8451d35046 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f527.png new file mode 100644 index 0000000000..63d8a78a93 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f52c.png new file mode 100644 index 0000000000..4c6a6f2669 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f680.png new file mode 100644 index 0000000000..162c4d9a48 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f692.png new file mode 100644 index 0000000000..fc28a8c169 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..ee80b1d083 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..b3d999a8c4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..c2573b7095 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..d641e2375f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..2a29c96ef4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..0e6e25d10e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9af.png new file mode 100644 index 0000000000..870d4453e5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9b0.png new file mode 100644 index 0000000000..0a951625f3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9b1.png new file mode 100644 index 0000000000..d1cea53bb0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9b2.png new file mode 100644 index 0000000000..1065c6bf4b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9b3.png new file mode 100644 index 0000000000..4c2b85a3fc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..21a7b2a277 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9bc.png new file mode 100644 index 0000000000..31a737e825 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..fb00706183 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9bd.png new file mode 100644 index 0000000000..18255458fb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1fa70.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1fa70.png new file mode 100644 index 0000000000..97a389864e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1fa70.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1faef-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1faef-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..39253f1058 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1faef-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1faef-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1faef-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..50ece6238a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1faef-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1faef-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1faef-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..6c3268bb52 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1faef-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1faef-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1faef-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..31833145fd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-1faef-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2695-fe0f.png new file mode 100644 index 0000000000..722e6e6b20 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2696-fe0f.png new file mode 100644 index 0000000000..21ec0e9c5b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2708-fe0f.png new file mode 100644 index 0000000000..1e1a9ffeea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..5ddf4016f7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..a38e6a28c3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..9908e3b893 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..b4f5862b11 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..e20d7ad764 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..49a1b8d104 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..12a7abc495 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..66e9e117b2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc.png new file mode 100644 index 0000000000..d3451581d1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f33e.png new file mode 100644 index 0000000000..444160826b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f373.png new file mode 100644 index 0000000000..5fb0463a0f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f37c.png new file mode 100644 index 0000000000..478a66ddc3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f384.png new file mode 100644 index 0000000000..9c7db219b5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f393.png new file mode 100644 index 0000000000..d7414dd375 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f3a4.png new file mode 100644 index 0000000000..d0f9741456 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f3a8.png new file mode 100644 index 0000000000..e8ef80d7f5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f3eb.png new file mode 100644 index 0000000000..027833aa9c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f3ed.png new file mode 100644 index 0000000000..010030d665 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f430-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f430-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..6bc78a1a1d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f430-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f430-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f430-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..456768e03d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f430-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f430-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f430-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..7e920f665b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f430-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f430-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f430-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..94255c30b5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f430-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f4bb.png new file mode 100644 index 0000000000..0852cc01d3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f4bc.png new file mode 100644 index 0000000000..95f6a939ac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f527.png new file mode 100644 index 0000000000..ca32388149 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f52c.png new file mode 100644 index 0000000000..5ae8cccb5d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f680.png new file mode 100644 index 0000000000..d50c650139 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f692.png new file mode 100644 index 0000000000..79f3dbaa0e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..5b2f725e1d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..229c18d1c5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..c6fcaea042 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..ac99a2c1fb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..2f80d05c55 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..eaa2fa2d28 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9af.png new file mode 100644 index 0000000000..260507ef51 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9b0.png new file mode 100644 index 0000000000..f11dff1190 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9b1.png new file mode 100644 index 0000000000..37587d5ffd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9b2.png new file mode 100644 index 0000000000..609b41b7b2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9b3.png new file mode 100644 index 0000000000..36b16487e2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..deadeed5eb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9bc.png new file mode 100644 index 0000000000..93d2a824bb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..336fe8e45b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9bd.png new file mode 100644 index 0000000000..f5b5e63864 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1fa70.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1fa70.png new file mode 100644 index 0000000000..a74c7d1c2a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1fa70.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1faef-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1faef-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..b8a6c0f9e7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1faef-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1faef-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1faef-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..adb93513e3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1faef-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1faef-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1faef-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..a07f0eb3af Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1faef-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1faef-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1faef-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..b3b4c2255b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-1faef-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2695-fe0f.png new file mode 100644 index 0000000000..ef513a7eae Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2696-fe0f.png new file mode 100644 index 0000000000..e708c2eac2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2708-fe0f.png new file mode 100644 index 0000000000..99c6821d34 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..34379c5b55 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..e3d1daff2f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..6ff7a61739 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..4f75a98d3c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..3267a3e3c6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..328e976a12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..8e18df2441 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..c0e4c46d88 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd.png new file mode 100644 index 0000000000..3c51e01439 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f33e.png new file mode 100644 index 0000000000..ce18ce0c80 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f373.png new file mode 100644 index 0000000000..f92bb8033f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f37c.png new file mode 100644 index 0000000000..f6e6621922 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f384.png new file mode 100644 index 0000000000..d7480e7254 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f393.png new file mode 100644 index 0000000000..c7393d59b2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f3a4.png new file mode 100644 index 0000000000..5b5ffcae3d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f3a8.png new file mode 100644 index 0000000000..f3501b34d8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f3eb.png new file mode 100644 index 0000000000..6767d97ff7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f3ed.png new file mode 100644 index 0000000000..1e83f20f03 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f430-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f430-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..7310bd7ac6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f430-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f430-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f430-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..4755128de4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f430-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f430-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f430-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..5da6d1cbdc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f430-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f430-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f430-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..d86870d9d6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f430-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f4bb.png new file mode 100644 index 0000000000..6f851f76f1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f4bc.png new file mode 100644 index 0000000000..1da65d87d9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f527.png new file mode 100644 index 0000000000..688a909eb8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f52c.png new file mode 100644 index 0000000000..4a78ea9b2d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f680.png new file mode 100644 index 0000000000..092fc992c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f692.png new file mode 100644 index 0000000000..cb0e4b2bea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..dd29f84c4b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..b538426680 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..794b1aab4d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..bdab9fc730 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..eae7472270 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..4272c29cdb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9af.png new file mode 100644 index 0000000000..b383c54427 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9b0.png new file mode 100644 index 0000000000..7781c29ecb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9b1.png new file mode 100644 index 0000000000..7de0d4e436 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9b2.png new file mode 100644 index 0000000000..25c78ed087 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9b3.png new file mode 100644 index 0000000000..2fb82858f1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..6c3940b3a5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9bc.png new file mode 100644 index 0000000000..51a7fcba73 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..0dbdac7e7f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9bd.png new file mode 100644 index 0000000000..72f5aa062e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1fa70.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1fa70.png new file mode 100644 index 0000000000..ef731916e8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1fa70.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1faef-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1faef-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..5d2ce4a6eb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1faef-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1faef-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1faef-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..6e9e5b9a8f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1faef-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1faef-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1faef-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..e5b21b6fe1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1faef-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1faef-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1faef-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..af6ea963f1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-1faef-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2695-fe0f.png new file mode 100644 index 0000000000..d54a125058 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2696-fe0f.png new file mode 100644 index 0000000000..05b2b8ee13 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2708-fe0f.png new file mode 100644 index 0000000000..97da172e2d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..c35b92e85a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..ddec3e7cf4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..465f60dd8a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..376180f70d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..8d4e9808af Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..3926831f61 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..4d9e7c989b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..ce3d818725 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe.png new file mode 100644 index 0000000000..f3f7e0c4a6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f33e.png new file mode 100644 index 0000000000..7ee88d8fe7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f373.png new file mode 100644 index 0000000000..1060299ccc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f37c.png new file mode 100644 index 0000000000..03ccea351b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f384.png new file mode 100644 index 0000000000..a3d6e659dd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f393.png new file mode 100644 index 0000000000..c906f18e88 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f3a4.png new file mode 100644 index 0000000000..6ab09a25f7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f3a8.png new file mode 100644 index 0000000000..0388084caa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f3eb.png new file mode 100644 index 0000000000..13df8d2cde Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f3ed.png new file mode 100644 index 0000000000..d4b3ee56e4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f430-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f430-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..060153e50a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f430-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f430-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f430-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..6a3a35f70f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f430-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f430-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f430-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..1071e4df9f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f430-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f430-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f430-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..7fb02d5e18 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f430-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f4bb.png new file mode 100644 index 0000000000..de2819894b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f4bc.png new file mode 100644 index 0000000000..73a8e53a20 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f527.png new file mode 100644 index 0000000000..5d098b9841 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f52c.png new file mode 100644 index 0000000000..db4b9d41c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f680.png new file mode 100644 index 0000000000..846be56c12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f692.png new file mode 100644 index 0000000000..82e051a7a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..476f100c7d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..887480f099 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..dbafdf9d3d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..6e80a36548 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3ff.png new file mode 100644 index 0000000000..c0350e92e8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..c6914bea22 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9af.png new file mode 100644 index 0000000000..f2fcce1e25 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9b0.png new file mode 100644 index 0000000000..81a7caa551 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9b1.png new file mode 100644 index 0000000000..1cb908c735 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9b2.png new file mode 100644 index 0000000000..6a5e8b0494 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9b3.png new file mode 100644 index 0000000000..a4772fea5a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..4eeddc4a1a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9bc.png new file mode 100644 index 0000000000..fc99dd988a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..5de8f6f988 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9bd.png new file mode 100644 index 0000000000..6c8e92b295 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1fa70.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1fa70.png new file mode 100644 index 0000000000..d3e3fbe9a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1fa70.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1faef-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1faef-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..e253c130a4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1faef-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1faef-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1faef-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..95d6d34e57 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1faef-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1faef-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1faef-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..fe5b5b6e0e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1faef-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1faef-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1faef-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..e9786cdd02 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-1faef-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2695-fe0f.png new file mode 100644 index 0000000000..8f95ec023c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2696-fe0f.png new file mode 100644 index 0000000000..1b2c484452 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2708-fe0f.png new file mode 100644 index 0000000000..a504160f7c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..14ced38a1f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..dc796c6d7b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..3403518fd8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..60702d908d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fb.png new file mode 100644 index 0000000000..a470e99e2a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fc.png new file mode 100644 index 0000000000..b2e7f1652c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fd.png new file mode 100644 index 0000000000..01644ff1f3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fe.png new file mode 100644 index 0000000000..0fd86e7846 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff.png new file mode 100644 index 0000000000..dac06fdee0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f33e.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f33e.png new file mode 100644 index 0000000000..62757b900b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f33e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f373.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f373.png new file mode 100644 index 0000000000..4d6f90161d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f373.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f37c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f37c.png new file mode 100644 index 0000000000..4e67b21b90 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f37c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f384.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f384.png new file mode 100644 index 0000000000..1c723c878f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f384.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f393.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f393.png new file mode 100644 index 0000000000..4b6421a8b2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f393.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f3a4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f3a4.png new file mode 100644 index 0000000000..b4b56c95b7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f3a4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f3a8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f3a8.png new file mode 100644 index 0000000000..890cc7f172 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f3a8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f3eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f3eb.png new file mode 100644 index 0000000000..e0dc2f5979 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f3eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f3ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f3ed.png new file mode 100644 index 0000000000..1a7e218139 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f3ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f4bb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f4bb.png new file mode 100644 index 0000000000..6dfefc0fdf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f4bb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f4bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f4bc.png new file mode 100644 index 0000000000..1570683639 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f4bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f527.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f527.png new file mode 100644 index 0000000000..bdaf652be2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f527.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f52c.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f52c.png new file mode 100644 index 0000000000..9d71dc2102 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f52c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f680.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f680.png new file mode 100644 index 0000000000..e9221ddb4d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f680.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f692.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f692.png new file mode 100644 index 0000000000..c84c813768 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f91d-200d-1f9d1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f91d-200d-1f9d1.png new file mode 100644 index 0000000000..38916c55b1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f91d-200d-1f9d1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9af-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9af-200d-27a1-fe0f.png new file mode 100644 index 0000000000..c02145edc7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9af-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9af.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9af.png new file mode 100644 index 0000000000..e160612831 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9af.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9b0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9b0.png new file mode 100644 index 0000000000..363fd9c8ab Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9b1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9b1.png new file mode 100644 index 0000000000..8c809f1436 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9b2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9b2.png new file mode 100644 index 0000000000..e939a1115b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9b2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9b3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9b3.png new file mode 100644 index 0000000000..241b5446cd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9b3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9bc-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9bc-200d-27a1-fe0f.png new file mode 100644 index 0000000000..831528469d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9bc-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9bc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9bc.png new file mode 100644 index 0000000000..a372a8e3d5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9bc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9bd-200d-27a1-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9bd-200d-27a1-fe0f.png new file mode 100644 index 0000000000..181a3e1646 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9bd-200d-27a1-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9bd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9bd.png new file mode 100644 index 0000000000..3fdadab5e9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9d1-200d-1f9d2-200d-1f9d2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9d1-200d-1f9d2-200d-1f9d2.png new file mode 100644 index 0000000000..983f56fe3c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9d1-200d-1f9d2-200d-1f9d2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9d1-200d-1f9d2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9d1-200d-1f9d2.png new file mode 100644 index 0000000000..07ea11eb2b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9d1-200d-1f9d2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9d2-200d-1f9d2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9d2-200d-1f9d2.png new file mode 100644 index 0000000000..badab11cb3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9d2-200d-1f9d2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9d2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9d2.png new file mode 100644 index 0000000000..e2d9cb6530 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1f9d2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1fa70.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1fa70.png new file mode 100644 index 0000000000..9c9b964123 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-1fa70.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-2695-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-2695-fe0f.png new file mode 100644 index 0000000000..354e2124a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-2695-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-2696-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-2696-fe0f.png new file mode 100644 index 0000000000..8d9409d37e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-2696-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-2708-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-2708-fe0f.png new file mode 100644 index 0000000000..78c4b3738a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1-200d-2708-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1.png new file mode 100644 index 0000000000..13ad8a69b9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d2-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d2-1f3fb.png new file mode 100644 index 0000000000..3176891d09 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d2-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d2-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d2-1f3fc.png new file mode 100644 index 0000000000..85c9853939 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d2-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d2-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d2-1f3fd.png new file mode 100644 index 0000000000..5eedce05af Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d2-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d2-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d2-1f3fe.png new file mode 100644 index 0000000000..0931b2dbca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d2-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d2-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d2-1f3ff.png new file mode 100644 index 0000000000..c02a87e427 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d2-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d2.png new file mode 100644 index 0000000000..d64876b9b0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d3-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d3-1f3fb.png new file mode 100644 index 0000000000..0d031a0818 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d3-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d3-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d3-1f3fc.png new file mode 100644 index 0000000000..c2213380b5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d3-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d3-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d3-1f3fd.png new file mode 100644 index 0000000000..0c56feac0f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d3-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d3-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d3-1f3fe.png new file mode 100644 index 0000000000..c98423b5ce Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d3-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d3-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d3-1f3ff.png new file mode 100644 index 0000000000..de458e297f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d3-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d3.png new file mode 100644 index 0000000000..627b9e7fac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..d5ae81abe2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..8e23bc93ea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fb.png new file mode 100644 index 0000000000..454efbfea2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..c39f5cf001 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..2d2c904ca4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fc.png new file mode 100644 index 0000000000..0930537c4e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..a26157d65a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..714fa37075 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fd.png new file mode 100644 index 0000000000..1eaf4e67ee Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..574a2aa6d3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..4fcd67ee86 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fe.png new file mode 100644 index 0000000000..0587ba6ac2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..38e64542f3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..16810f8a50 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3ff.png new file mode 100644 index 0000000000..92b8857f57 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-200d-2640-fe0f.png new file mode 100644 index 0000000000..78c10b31d5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-200d-2642-fe0f.png new file mode 100644 index 0000000000..c764ff644b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4.png new file mode 100644 index 0000000000..44e8c0d6c0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d5-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d5-1f3fb.png new file mode 100644 index 0000000000..1d643e0c3b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d5-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d5-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d5-1f3fc.png new file mode 100644 index 0000000000..852b0a92b3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d5-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d5-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d5-1f3fd.png new file mode 100644 index 0000000000..b48e2fc6a6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d5-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d5-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d5-1f3fe.png new file mode 100644 index 0000000000..3c9d4ff337 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d5-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d5-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d5-1f3ff.png new file mode 100644 index 0000000000..36415f4ccf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d5-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d5.png new file mode 100644 index 0000000000..b39adcda35 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..1ac4aaf56c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..c7651ed3e8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fb.png new file mode 100644 index 0000000000..01e7bad740 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..33b55698a3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..bb99429b95 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fc.png new file mode 100644 index 0000000000..190adb4568 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..44314fb495 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..79c0bc65ed Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fd.png new file mode 100644 index 0000000000..bb170269ea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..4b4add48cc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..1d1672d71f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fe.png new file mode 100644 index 0000000000..3e065c0af8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..7741499671 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..ed8c3944c6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3ff.png new file mode 100644 index 0000000000..d846549152 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-200d-2640-fe0f.png new file mode 100644 index 0000000000..3fa739ab64 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-200d-2642-fe0f.png new file mode 100644 index 0000000000..a9a4c5ea92 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6.png new file mode 100644 index 0000000000..3a2ec06ddf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..fecf90a787 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..c19285100b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fb.png new file mode 100644 index 0000000000..ff9d0fbbe8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..215e18e2d4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..9e8196c287 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fc.png new file mode 100644 index 0000000000..31bf0347f7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..7d9396ce12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..a0255f24b0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fd.png new file mode 100644 index 0000000000..67efea29f3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..5408afcd71 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..ed01df66b1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fe.png new file mode 100644 index 0000000000..79708dd24c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..ab88bd1d31 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..480fcf1ef2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3ff.png new file mode 100644 index 0000000000..c61785e2a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-200d-2640-fe0f.png new file mode 100644 index 0000000000..b336b14d56 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-200d-2642-fe0f.png new file mode 100644 index 0000000000..6ad10ec4c2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7.png new file mode 100644 index 0000000000..1303a54aaf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..996741f9e3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..002357d71a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fb.png new file mode 100644 index 0000000000..fea8e8ba70 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..dfa69c8ab6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..e1606adc4c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fc.png new file mode 100644 index 0000000000..0740402041 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..95dbb2d16d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..9cc179ecdb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fd.png new file mode 100644 index 0000000000..35f2252ba4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..ff022df7fa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..9460caacb9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fe.png new file mode 100644 index 0000000000..facdd8e06e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..714c8f1893 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..005bab6b5a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3ff.png new file mode 100644 index 0000000000..6781bf78dd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-200d-2640-fe0f.png new file mode 100644 index 0000000000..d9189351d8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-200d-2642-fe0f.png new file mode 100644 index 0000000000..0840aa1b53 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8.png new file mode 100644 index 0000000000..64cd7e2eb8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..a90901839e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..9bf271911c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fb.png new file mode 100644 index 0000000000..90d1a887de Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..53e07504dd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..4c787f5bea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fc.png new file mode 100644 index 0000000000..00e895ef0f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..2a7dcc21f2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..96d8c88219 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fd.png new file mode 100644 index 0000000000..b233884387 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..e640bf2c5b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..2b66defd30 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fe.png new file mode 100644 index 0000000000..f9c021873a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..84fc554366 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..3de4920721 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3ff.png new file mode 100644 index 0000000000..8abf6646a4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-200d-2640-fe0f.png new file mode 100644 index 0000000000..747397c39a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-200d-2642-fe0f.png new file mode 100644 index 0000000000..d12c1e6052 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9d9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9.png new file mode 100644 index 0000000000..4dd9aa577a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9d9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..3ebb190891 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..4aef2a3eb7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fb.png new file mode 100644 index 0000000000..71865d80d2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..3a3a4a4ec1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..2f3566584c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fc.png new file mode 100644 index 0000000000..696a9bfd9b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..be21165139 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..fa1b8242c7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fd.png new file mode 100644 index 0000000000..a6c48dd4ac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..6469a079f1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..e0990049f1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fe.png new file mode 100644 index 0000000000..05e51d2d79 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..47181d6fc3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..5fa6356ac5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3ff.png new file mode 100644 index 0000000000..b2a3b83da0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-200d-2640-fe0f.png new file mode 100644 index 0000000000..b4a1e9a4f9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-200d-2642-fe0f.png new file mode 100644 index 0000000000..9f79ec1635 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9da.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9da.png new file mode 100644 index 0000000000..2922c2e4cf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9da.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..3a43c52fe4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..76fe5ede4e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fb.png new file mode 100644 index 0000000000..e9d81f660d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..f958045cd7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..08af30c85d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fc.png new file mode 100644 index 0000000000..c3bdf52360 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..7a1bea782f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..fad2b7f9ef Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fd.png new file mode 100644 index 0000000000..1cdbc1e63e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..51d63cd18e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..755de90ce1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fe.png new file mode 100644 index 0000000000..8c7f62beb8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..3fc9f4dfcc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..39424839cd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3ff.png new file mode 100644 index 0000000000..a04d300a75 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-200d-2640-fe0f.png new file mode 100644 index 0000000000..8d43714750 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-200d-2642-fe0f.png new file mode 100644 index 0000000000..c1a60bc419 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9db.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9db.png new file mode 100644 index 0000000000..f9153f654a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9db.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..249ce2ba67 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..8f32dd45e8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fb.png new file mode 100644 index 0000000000..2d1664f6d0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..8bf8de385a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..14e0c8955c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fc.png new file mode 100644 index 0000000000..0cf2d6b73a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..c7278e3d3d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..d2dd2bb047 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fd.png new file mode 100644 index 0000000000..463649c3eb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..f702433ea4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..fa34d60d7e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fe.png new file mode 100644 index 0000000000..12010a147d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..7a67a9391a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..1f179ea145 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3ff.png new file mode 100644 index 0000000000..c2aa65ca26 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-200d-2640-fe0f.png new file mode 100644 index 0000000000..566588215c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-200d-2642-fe0f.png new file mode 100644 index 0000000000..c80e8cc2d7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc.png new file mode 100644 index 0000000000..ec92221234 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..bd6d562bc4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..ac43289511 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fb.png new file mode 100644 index 0000000000..0e6c18962f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..c88fe43de8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..1c9b4b55a2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fc.png new file mode 100644 index 0000000000..c5d5474092 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..73b25fb960 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..d368a17828 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fd.png new file mode 100644 index 0000000000..1a9ca7f33e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..f89356ef0f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..6d1e045571 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fe.png new file mode 100644 index 0000000000..dd598986ac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..0aa3e71dab Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..de1e6d0799 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3ff.png new file mode 100644 index 0000000000..d8dc2f0d6e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-200d-2640-fe0f.png new file mode 100644 index 0000000000..84e8ab03a2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-200d-2642-fe0f.png new file mode 100644 index 0000000000..f838ee82bb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9dd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd.png new file mode 100644 index 0000000000..c830d709e6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9dd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9de-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9de-200d-2640-fe0f.png new file mode 100644 index 0000000000..d1a5608de9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9de-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9de-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9de-200d-2642-fe0f.png new file mode 100644 index 0000000000..0e30daa256 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9de-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9de.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9de.png new file mode 100644 index 0000000000..34942f1807 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9de.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9df-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9df-200d-2640-fe0f.png new file mode 100644 index 0000000000..cdaa255dc5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9df-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9df-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9df-200d-2642-fe0f.png new file mode 100644 index 0000000000..6065e11af6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9df-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9df.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9df.png new file mode 100644 index 0000000000..fda1ab48b5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9df.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9e0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9e0.png new file mode 100644 index 0000000000..dd5cd23be8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9e0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9e1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9e1.png new file mode 100644 index 0000000000..a3ba1c2735 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9e1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9e2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9e2.png new file mode 100644 index 0000000000..1441df8c83 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9e2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9e3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9e3.png new file mode 100644 index 0000000000..0faf6627c9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9e4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9e4.png new file mode 100644 index 0000000000..ba0a2af68b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9e4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9e5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9e5.png new file mode 100644 index 0000000000..4219af9b0c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9e5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9e6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9e6.png new file mode 100644 index 0000000000..924c953098 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9e6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9e7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9e7.png new file mode 100644 index 0000000000..8b7941d1b5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9e7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9e8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9e8.png new file mode 100644 index 0000000000..6dc6b7e04c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9e8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9e9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9e9.png new file mode 100644 index 0000000000..9a3e7fa3bc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ea.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ea.png new file mode 100644 index 0000000000..d083b76ff6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9eb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9eb.png new file mode 100644 index 0000000000..f48fdb32d6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ec.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ec.png new file mode 100644 index 0000000000..1fa93207d6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ed.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ed.png new file mode 100644 index 0000000000..ff199d4822 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ee.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ee.png new file mode 100644 index 0000000000..e56e558529 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ee.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ef.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ef.png new file mode 100644 index 0000000000..f05d9276a1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ef.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9f0.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9f0.png new file mode 100644 index 0000000000..f66bd60df4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9f1.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9f1.png new file mode 100644 index 0000000000..764830aedd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9f1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9f2.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9f2.png new file mode 100644 index 0000000000..f6366085d3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9f3.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9f3.png new file mode 100644 index 0000000000..f907616056 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9f4.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9f4.png new file mode 100644 index 0000000000..2fb947682a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9f5.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9f5.png new file mode 100644 index 0000000000..0e26993b84 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9f5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9f6.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9f6.png new file mode 100644 index 0000000000..005e38061f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9f6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9f7.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9f7.png new file mode 100644 index 0000000000..b15166e2ab Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9f8.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9f8.png new file mode 100644 index 0000000000..4ba5b7bb59 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9f9.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9f9.png new file mode 100644 index 0000000000..13176f55f1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9fa.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9fa.png new file mode 100644 index 0000000000..696476ff6d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9fb.png new file mode 100644 index 0000000000..1a12083201 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9fc.png new file mode 100644 index 0000000000..3432ebceb1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9fd.png new file mode 100644 index 0000000000..8f810b2395 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9fe.png new file mode 100644 index 0000000000..15408be776 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1f9ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1f9ff.png new file mode 100644 index 0000000000..a5e55b990f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1f9ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa70.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa70.png new file mode 100644 index 0000000000..4d57413ddb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa70.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa71.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa71.png new file mode 100644 index 0000000000..af41879e3d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa71.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa72.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa72.png new file mode 100644 index 0000000000..0a36008476 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa72.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa73.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa73.png new file mode 100644 index 0000000000..f1adff3ee6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa73.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa74.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa74.png new file mode 100644 index 0000000000..f7b078b069 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa74.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa75.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa75.png new file mode 100644 index 0000000000..ce47cefb93 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa75.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa76.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa76.png new file mode 100644 index 0000000000..7a4d48186c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa76.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa77.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa77.png new file mode 100644 index 0000000000..094d80077d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa77.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa78.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa78.png new file mode 100644 index 0000000000..62aaa0f4cd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa78.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa79.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa79.png new file mode 100644 index 0000000000..edb23981a4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa79.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa7a.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa7a.png new file mode 100644 index 0000000000..edb930715c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa7a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa7b.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa7b.png new file mode 100644 index 0000000000..f25e24dcbe Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa7b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa7c.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa7c.png new file mode 100644 index 0000000000..2c6fb77e12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa7c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa80.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa80.png new file mode 100644 index 0000000000..5ae5a9d03a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa80.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa81.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa81.png new file mode 100644 index 0000000000..07bcb5503f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa81.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa82.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa82.png new file mode 100644 index 0000000000..246aab9b1b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa82.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa83.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa83.png new file mode 100644 index 0000000000..8cbd6b7cff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa83.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa84.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa84.png new file mode 100644 index 0000000000..73649cbd14 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa84.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa85.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa85.png new file mode 100644 index 0000000000..e97835b0c9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa85.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa86.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa86.png new file mode 100644 index 0000000000..5181a4ef2b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa86.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa87.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa87.png new file mode 100644 index 0000000000..23350ca48c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa87.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa88.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa88.png new file mode 100644 index 0000000000..b2f88ca8c8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa88.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa89.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa89.png new file mode 100644 index 0000000000..afa917b4d9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa89.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa8a.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa8a.png new file mode 100644 index 0000000000..4c8e3c1073 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa8a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa8e.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa8e.png new file mode 100644 index 0000000000..ba190bcde4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa8e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa8f.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa8f.png new file mode 100644 index 0000000000..a31c0536fc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa8f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa90.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa90.png new file mode 100644 index 0000000000..e0f5af2934 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa90.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa91.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa91.png new file mode 100644 index 0000000000..2ffe0ada06 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa91.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa92.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa92.png new file mode 100644 index 0000000000..02ec912205 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa92.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa93.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa93.png new file mode 100644 index 0000000000..eb96121dd6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa93.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa94.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa94.png new file mode 100644 index 0000000000..161befbaf5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa94.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa95.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa95.png new file mode 100644 index 0000000000..f5eb90921e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa95.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa96.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa96.png new file mode 100644 index 0000000000..47d54a833e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa96.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa97.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa97.png new file mode 100644 index 0000000000..d198407df7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa97.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa98.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa98.png new file mode 100644 index 0000000000..9e25d36418 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa98.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa99.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa99.png new file mode 100644 index 0000000000..8df742e5d7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa99.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa9a.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa9a.png new file mode 100644 index 0000000000..6c2b2e9e17 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa9a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa9b.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa9b.png new file mode 100644 index 0000000000..215ebb5273 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa9b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa9c.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa9c.png new file mode 100644 index 0000000000..7486beece7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa9c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa9d.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa9d.png new file mode 100644 index 0000000000..67499210a6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa9d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa9e.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa9e.png new file mode 100644 index 0000000000..843365b96f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa9e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fa9f.png b/backend/Shared/CIPPSharp/bin/twemoji/1fa9f.png new file mode 100644 index 0000000000..41627f39dd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fa9f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faa0.png b/backend/Shared/CIPPSharp/bin/twemoji/1faa0.png new file mode 100644 index 0000000000..51a1058063 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faa0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faa1.png b/backend/Shared/CIPPSharp/bin/twemoji/1faa1.png new file mode 100644 index 0000000000..92f82ce01d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faa1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faa2.png b/backend/Shared/CIPPSharp/bin/twemoji/1faa2.png new file mode 100644 index 0000000000..beaedaf0cb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faa2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faa3.png b/backend/Shared/CIPPSharp/bin/twemoji/1faa3.png new file mode 100644 index 0000000000..01c6a2476b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faa3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faa4.png b/backend/Shared/CIPPSharp/bin/twemoji/1faa4.png new file mode 100644 index 0000000000..8571b0f319 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faa4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faa5.png b/backend/Shared/CIPPSharp/bin/twemoji/1faa5.png new file mode 100644 index 0000000000..bed7fa028d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faa5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faa6.png b/backend/Shared/CIPPSharp/bin/twemoji/1faa6.png new file mode 100644 index 0000000000..30e67e7cbf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faa6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faa7.png b/backend/Shared/CIPPSharp/bin/twemoji/1faa7.png new file mode 100644 index 0000000000..2421eb6e52 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faa7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faa8.png b/backend/Shared/CIPPSharp/bin/twemoji/1faa8.png new file mode 100644 index 0000000000..2f56725b81 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faa8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faa9.png b/backend/Shared/CIPPSharp/bin/twemoji/1faa9.png new file mode 100644 index 0000000000..371dd3f59a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faa9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faaa.png b/backend/Shared/CIPPSharp/bin/twemoji/1faaa.png new file mode 100644 index 0000000000..28914d597d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faaa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faab.png b/backend/Shared/CIPPSharp/bin/twemoji/1faab.png new file mode 100644 index 0000000000..56248eae44 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faab.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faac.png b/backend/Shared/CIPPSharp/bin/twemoji/1faac.png new file mode 100644 index 0000000000..95b5e05072 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faac.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faad.png b/backend/Shared/CIPPSharp/bin/twemoji/1faad.png new file mode 100644 index 0000000000..c9973e0847 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faad.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faae.png b/backend/Shared/CIPPSharp/bin/twemoji/1faae.png new file mode 100644 index 0000000000..0469ee9d75 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faae.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faaf.png b/backend/Shared/CIPPSharp/bin/twemoji/1faaf.png new file mode 100644 index 0000000000..050384ee17 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faaf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fab0.png b/backend/Shared/CIPPSharp/bin/twemoji/1fab0.png new file mode 100644 index 0000000000..5183b5bb98 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fab0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fab1.png b/backend/Shared/CIPPSharp/bin/twemoji/1fab1.png new file mode 100644 index 0000000000..5ae8568f0b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fab1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fab2.png b/backend/Shared/CIPPSharp/bin/twemoji/1fab2.png new file mode 100644 index 0000000000..fcc1e46f75 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fab2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fab3.png b/backend/Shared/CIPPSharp/bin/twemoji/1fab3.png new file mode 100644 index 0000000000..a237252382 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fab3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fab4.png b/backend/Shared/CIPPSharp/bin/twemoji/1fab4.png new file mode 100644 index 0000000000..10c0fa57a5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fab4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fab5.png b/backend/Shared/CIPPSharp/bin/twemoji/1fab5.png new file mode 100644 index 0000000000..5d25b7a230 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fab5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fab6.png b/backend/Shared/CIPPSharp/bin/twemoji/1fab6.png new file mode 100644 index 0000000000..5ff7191cdb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fab6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fab7.png b/backend/Shared/CIPPSharp/bin/twemoji/1fab7.png new file mode 100644 index 0000000000..1cbdb98467 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fab7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fab8.png b/backend/Shared/CIPPSharp/bin/twemoji/1fab8.png new file mode 100644 index 0000000000..f312f47d56 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fab8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fab9.png b/backend/Shared/CIPPSharp/bin/twemoji/1fab9.png new file mode 100644 index 0000000000..505c9f891e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fab9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faba.png b/backend/Shared/CIPPSharp/bin/twemoji/1faba.png new file mode 100644 index 0000000000..d2a8494252 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faba.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fabb.png b/backend/Shared/CIPPSharp/bin/twemoji/1fabb.png new file mode 100644 index 0000000000..d6da522ceb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fabb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fabc.png b/backend/Shared/CIPPSharp/bin/twemoji/1fabc.png new file mode 100644 index 0000000000..4ffad41c6d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fabc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fabd.png b/backend/Shared/CIPPSharp/bin/twemoji/1fabd.png new file mode 100644 index 0000000000..2d9efc8bf0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fabd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fabe.png b/backend/Shared/CIPPSharp/bin/twemoji/1fabe.png new file mode 100644 index 0000000000..49a4c5abde Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fabe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fabf.png b/backend/Shared/CIPPSharp/bin/twemoji/1fabf.png new file mode 100644 index 0000000000..93688c24f2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fabf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac0.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac0.png new file mode 100644 index 0000000000..6d712a0652 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac1.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac1.png new file mode 100644 index 0000000000..3259df2647 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac2.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac2.png new file mode 100644 index 0000000000..8cc1c05858 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac3-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac3-1f3fb.png new file mode 100644 index 0000000000..2040952ba4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac3-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac3-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac3-1f3fc.png new file mode 100644 index 0000000000..1a4a8f2c87 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac3-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac3-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac3-1f3fd.png new file mode 100644 index 0000000000..fa2e94b551 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac3-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac3-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac3-1f3fe.png new file mode 100644 index 0000000000..6decc3e1aa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac3-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac3-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac3-1f3ff.png new file mode 100644 index 0000000000..960abc8c8f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac3-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac3.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac3.png new file mode 100644 index 0000000000..a1bb8560d8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac4-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac4-1f3fb.png new file mode 100644 index 0000000000..76cfb2e71d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac4-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac4-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac4-1f3fc.png new file mode 100644 index 0000000000..36d5f5401e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac4-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac4-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac4-1f3fd.png new file mode 100644 index 0000000000..4c7ba5a161 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac4-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac4-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac4-1f3fe.png new file mode 100644 index 0000000000..26963851ad Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac4-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac4-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac4-1f3ff.png new file mode 100644 index 0000000000..d3118efb96 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac4-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac4.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac4.png new file mode 100644 index 0000000000..2bc1837868 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac5-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac5-1f3fb.png new file mode 100644 index 0000000000..f9d535683d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac5-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac5-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac5-1f3fc.png new file mode 100644 index 0000000000..13e32b14b9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac5-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac5-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac5-1f3fd.png new file mode 100644 index 0000000000..09c284ceee Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac5-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac5-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac5-1f3fe.png new file mode 100644 index 0000000000..eb639141b1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac5-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac5-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac5-1f3ff.png new file mode 100644 index 0000000000..c3607584ab Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac5-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac5.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac5.png new file mode 100644 index 0000000000..22e3e9d501 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac6.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac6.png new file mode 100644 index 0000000000..8f045e03c0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fac8.png b/backend/Shared/CIPPSharp/bin/twemoji/1fac8.png new file mode 100644 index 0000000000..f5aad025dc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fac8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1facd.png b/backend/Shared/CIPPSharp/bin/twemoji/1facd.png new file mode 100644 index 0000000000..aced863706 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1facd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1face.png b/backend/Shared/CIPPSharp/bin/twemoji/1face.png new file mode 100644 index 0000000000..2fa60c3cfc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1face.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1facf.png b/backend/Shared/CIPPSharp/bin/twemoji/1facf.png new file mode 100644 index 0000000000..d9eec52bac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1facf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fad0.png b/backend/Shared/CIPPSharp/bin/twemoji/1fad0.png new file mode 100644 index 0000000000..3dbe93433c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fad0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fad1.png b/backend/Shared/CIPPSharp/bin/twemoji/1fad1.png new file mode 100644 index 0000000000..b4ef7e32a8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fad1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fad2.png b/backend/Shared/CIPPSharp/bin/twemoji/1fad2.png new file mode 100644 index 0000000000..3e660e6d0c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fad2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fad3.png b/backend/Shared/CIPPSharp/bin/twemoji/1fad3.png new file mode 100644 index 0000000000..30b2977b68 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fad3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fad4.png b/backend/Shared/CIPPSharp/bin/twemoji/1fad4.png new file mode 100644 index 0000000000..7dba90554e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fad4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fad5.png b/backend/Shared/CIPPSharp/bin/twemoji/1fad5.png new file mode 100644 index 0000000000..8b1f507337 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fad5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fad6.png b/backend/Shared/CIPPSharp/bin/twemoji/1fad6.png new file mode 100644 index 0000000000..37b47ed427 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fad6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fad7.png b/backend/Shared/CIPPSharp/bin/twemoji/1fad7.png new file mode 100644 index 0000000000..6f7e690458 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fad7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fad8.png b/backend/Shared/CIPPSharp/bin/twemoji/1fad8.png new file mode 100644 index 0000000000..ae12344e84 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fad8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fad9.png b/backend/Shared/CIPPSharp/bin/twemoji/1fad9.png new file mode 100644 index 0000000000..6988172c39 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fad9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fada.png b/backend/Shared/CIPPSharp/bin/twemoji/1fada.png new file mode 100644 index 0000000000..5cd8622186 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fada.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fadb.png b/backend/Shared/CIPPSharp/bin/twemoji/1fadb.png new file mode 100644 index 0000000000..3e56b7a9ff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fadb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fadc.png b/backend/Shared/CIPPSharp/bin/twemoji/1fadc.png new file mode 100644 index 0000000000..7395691443 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fadc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fadf.png b/backend/Shared/CIPPSharp/bin/twemoji/1fadf.png new file mode 100644 index 0000000000..89cf85425a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fadf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fae0.png b/backend/Shared/CIPPSharp/bin/twemoji/1fae0.png new file mode 100644 index 0000000000..b22f9f4ce1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fae0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fae1.png b/backend/Shared/CIPPSharp/bin/twemoji/1fae1.png new file mode 100644 index 0000000000..16d636271d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fae1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fae2.png b/backend/Shared/CIPPSharp/bin/twemoji/1fae2.png new file mode 100644 index 0000000000..9153a41d89 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fae2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fae3.png b/backend/Shared/CIPPSharp/bin/twemoji/1fae3.png new file mode 100644 index 0000000000..b4572380a8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fae3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fae4.png b/backend/Shared/CIPPSharp/bin/twemoji/1fae4.png new file mode 100644 index 0000000000..44c34dc71d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fae4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fae5.png b/backend/Shared/CIPPSharp/bin/twemoji/1fae5.png new file mode 100644 index 0000000000..e0444c480e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fae5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fae6.png b/backend/Shared/CIPPSharp/bin/twemoji/1fae6.png new file mode 100644 index 0000000000..755c94e5ae Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fae6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fae7.png b/backend/Shared/CIPPSharp/bin/twemoji/1fae7.png new file mode 100644 index 0000000000..8b402bdba3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fae7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fae8.png b/backend/Shared/CIPPSharp/bin/twemoji/1fae8.png new file mode 100644 index 0000000000..2720a2dfcd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fae8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1fae9.png b/backend/Shared/CIPPSharp/bin/twemoji/1fae9.png new file mode 100644 index 0000000000..f9c18a6a15 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1fae9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faea.png b/backend/Shared/CIPPSharp/bin/twemoji/1faea.png new file mode 100644 index 0000000000..06a237f5e4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faef.png b/backend/Shared/CIPPSharp/bin/twemoji/1faef.png new file mode 100644 index 0000000000..f03999f6b5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faef.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf0-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf0-1f3fb.png new file mode 100644 index 0000000000..64fe03d815 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf0-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf0-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf0-1f3fc.png new file mode 100644 index 0000000000..4c5610564e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf0-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf0-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf0-1f3fd.png new file mode 100644 index 0000000000..fa2b902e57 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf0-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf0-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf0-1f3fe.png new file mode 100644 index 0000000000..6a2c153ec3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf0-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf0-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf0-1f3ff.png new file mode 100644 index 0000000000..8e73cdf494 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf0-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf0.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf0.png new file mode 100644 index 0000000000..1a4ebedf2e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fb-200d-1faf2-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fb-200d-1faf2-1f3fc.png new file mode 100644 index 0000000000..2c911d7d70 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fb-200d-1faf2-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fb-200d-1faf2-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fb-200d-1faf2-1f3fd.png new file mode 100644 index 0000000000..5f81f9d886 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fb-200d-1faf2-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fb-200d-1faf2-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fb-200d-1faf2-1f3fe.png new file mode 100644 index 0000000000..b1f794731c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fb-200d-1faf2-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fb-200d-1faf2-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fb-200d-1faf2-1f3ff.png new file mode 100644 index 0000000000..270800ee51 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fb-200d-1faf2-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fb.png new file mode 100644 index 0000000000..350fd42a0b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fc-200d-1faf2-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fc-200d-1faf2-1f3fb.png new file mode 100644 index 0000000000..3d7487d619 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fc-200d-1faf2-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fc-200d-1faf2-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fc-200d-1faf2-1f3fd.png new file mode 100644 index 0000000000..681473f688 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fc-200d-1faf2-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fc-200d-1faf2-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fc-200d-1faf2-1f3fe.png new file mode 100644 index 0000000000..267dfa1f34 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fc-200d-1faf2-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fc-200d-1faf2-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fc-200d-1faf2-1f3ff.png new file mode 100644 index 0000000000..09312a04ad Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fc-200d-1faf2-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fc.png new file mode 100644 index 0000000000..7a8459690c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fd-200d-1faf2-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fd-200d-1faf2-1f3fb.png new file mode 100644 index 0000000000..8fff1125da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fd-200d-1faf2-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fd-200d-1faf2-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fd-200d-1faf2-1f3fc.png new file mode 100644 index 0000000000..b71098b814 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fd-200d-1faf2-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fd-200d-1faf2-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fd-200d-1faf2-1f3fe.png new file mode 100644 index 0000000000..7e76b6a849 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fd-200d-1faf2-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fd-200d-1faf2-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fd-200d-1faf2-1f3ff.png new file mode 100644 index 0000000000..df2600e84a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fd-200d-1faf2-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fd.png new file mode 100644 index 0000000000..62cdaa15fc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fe-200d-1faf2-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fe-200d-1faf2-1f3fb.png new file mode 100644 index 0000000000..7f8c42c1b9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fe-200d-1faf2-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fe-200d-1faf2-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fe-200d-1faf2-1f3fc.png new file mode 100644 index 0000000000..2982f4e0af Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fe-200d-1faf2-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fe-200d-1faf2-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fe-200d-1faf2-1f3fd.png new file mode 100644 index 0000000000..cea2e531f3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fe-200d-1faf2-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fe-200d-1faf2-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fe-200d-1faf2-1f3ff.png new file mode 100644 index 0000000000..353d0adcc4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fe-200d-1faf2-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fe.png new file mode 100644 index 0000000000..754a1fdc04 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3ff-200d-1faf2-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3ff-200d-1faf2-1f3fb.png new file mode 100644 index 0000000000..7fda714716 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3ff-200d-1faf2-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3ff-200d-1faf2-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3ff-200d-1faf2-1f3fc.png new file mode 100644 index 0000000000..3f09537065 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3ff-200d-1faf2-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3ff-200d-1faf2-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3ff-200d-1faf2-1f3fd.png new file mode 100644 index 0000000000..2759c41810 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3ff-200d-1faf2-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3ff-200d-1faf2-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3ff-200d-1faf2-1f3fe.png new file mode 100644 index 0000000000..c17022a84b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3ff-200d-1faf2-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3ff.png new file mode 100644 index 0000000000..22527d97e7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf1.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf1.png new file mode 100644 index 0000000000..c03e0401cc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf2-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf2-1f3fb.png new file mode 100644 index 0000000000..2d75e3048f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf2-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf2-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf2-1f3fc.png new file mode 100644 index 0000000000..cf9fb69a5e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf2-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf2-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf2-1f3fd.png new file mode 100644 index 0000000000..38b0cdc55d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf2-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf2-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf2-1f3fe.png new file mode 100644 index 0000000000..d10f7a0396 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf2-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf2-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf2-1f3ff.png new file mode 100644 index 0000000000..68b394f372 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf2-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf2.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf2.png new file mode 100644 index 0000000000..4997423659 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf3-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf3-1f3fb.png new file mode 100644 index 0000000000..d8168dce4d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf3-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf3-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf3-1f3fc.png new file mode 100644 index 0000000000..5a841e5023 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf3-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf3-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf3-1f3fd.png new file mode 100644 index 0000000000..c439588ed0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf3-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf3-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf3-1f3fe.png new file mode 100644 index 0000000000..3b63da73ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf3-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf3-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf3-1f3ff.png new file mode 100644 index 0000000000..a8ea30ce80 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf3-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf3.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf3.png new file mode 100644 index 0000000000..1e057115a4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf4-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf4-1f3fb.png new file mode 100644 index 0000000000..0695579d7b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf4-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf4-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf4-1f3fc.png new file mode 100644 index 0000000000..4251f775a3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf4-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf4-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf4-1f3fd.png new file mode 100644 index 0000000000..49208c249f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf4-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf4-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf4-1f3fe.png new file mode 100644 index 0000000000..ae14345638 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf4-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf4-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf4-1f3ff.png new file mode 100644 index 0000000000..fcc64e7f98 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf4-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf4.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf4.png new file mode 100644 index 0000000000..d9fdff839f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf5-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf5-1f3fb.png new file mode 100644 index 0000000000..e6ae0b9df1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf5-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf5-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf5-1f3fc.png new file mode 100644 index 0000000000..9dfd77c879 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf5-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf5-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf5-1f3fd.png new file mode 100644 index 0000000000..222201c99d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf5-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf5-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf5-1f3fe.png new file mode 100644 index 0000000000..5bba90bb65 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf5-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf5-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf5-1f3ff.png new file mode 100644 index 0000000000..3d922501fa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf5-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf5.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf5.png new file mode 100644 index 0000000000..9e3a1b3838 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf6-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf6-1f3fb.png new file mode 100644 index 0000000000..55bfa0f369 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf6-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf6-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf6-1f3fc.png new file mode 100644 index 0000000000..d79a55f31a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf6-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf6-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf6-1f3fd.png new file mode 100644 index 0000000000..b0eb0d00b1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf6-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf6-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf6-1f3fe.png new file mode 100644 index 0000000000..e6f52ba0a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf6-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf6-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf6-1f3ff.png new file mode 100644 index 0000000000..f85b0ca83f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf6-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf6.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf6.png new file mode 100644 index 0000000000..23cf0502f2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf7-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf7-1f3fb.png new file mode 100644 index 0000000000..9e4f338583 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf7-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf7-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf7-1f3fc.png new file mode 100644 index 0000000000..acba6237ef Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf7-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf7-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf7-1f3fd.png new file mode 100644 index 0000000000..50700f0aeb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf7-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf7-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf7-1f3fe.png new file mode 100644 index 0000000000..a4a8d1a168 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf7-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf7-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf7-1f3ff.png new file mode 100644 index 0000000000..0a57d9c5bb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf7-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf7.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf7.png new file mode 100644 index 0000000000..edc4feb402 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf8-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf8-1f3fb.png new file mode 100644 index 0000000000..3f6cf4af3d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf8-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf8-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf8-1f3fc.png new file mode 100644 index 0000000000..1148af5ba0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf8-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf8-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf8-1f3fd.png new file mode 100644 index 0000000000..07f293a53b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf8-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf8-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf8-1f3fe.png new file mode 100644 index 0000000000..3aed956347 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf8-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf8-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf8-1f3ff.png new file mode 100644 index 0000000000..d4589f053f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf8-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/1faf8.png b/backend/Shared/CIPPSharp/bin/twemoji/1faf8.png new file mode 100644 index 0000000000..dbdceaba81 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/1faf8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/203c.png b/backend/Shared/CIPPSharp/bin/twemoji/203c.png new file mode 100644 index 0000000000..2334a7f5c1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/203c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2049.png b/backend/Shared/CIPPSharp/bin/twemoji/2049.png new file mode 100644 index 0000000000..c5304bf141 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2049.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2122.png b/backend/Shared/CIPPSharp/bin/twemoji/2122.png new file mode 100644 index 0000000000..b9ffb56e3f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2122.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2139.png b/backend/Shared/CIPPSharp/bin/twemoji/2139.png new file mode 100644 index 0000000000..829f0bf085 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2139.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2194.png b/backend/Shared/CIPPSharp/bin/twemoji/2194.png new file mode 100644 index 0000000000..d0cb730c83 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2194.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2195.png b/backend/Shared/CIPPSharp/bin/twemoji/2195.png new file mode 100644 index 0000000000..cbbe269e4e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2195.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2196.png b/backend/Shared/CIPPSharp/bin/twemoji/2196.png new file mode 100644 index 0000000000..c3f9bdfce5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2196.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2197.png b/backend/Shared/CIPPSharp/bin/twemoji/2197.png new file mode 100644 index 0000000000..08eb63ceed Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2197.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2198.png b/backend/Shared/CIPPSharp/bin/twemoji/2198.png new file mode 100644 index 0000000000..5135705c6f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2198.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2199.png b/backend/Shared/CIPPSharp/bin/twemoji/2199.png new file mode 100644 index 0000000000..41b24fe673 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2199.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/21a9.png b/backend/Shared/CIPPSharp/bin/twemoji/21a9.png new file mode 100644 index 0000000000..1df93e6906 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/21a9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/21aa.png b/backend/Shared/CIPPSharp/bin/twemoji/21aa.png new file mode 100644 index 0000000000..7a0b709d25 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/21aa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23-20e3.png b/backend/Shared/CIPPSharp/bin/twemoji/23-20e3.png new file mode 100644 index 0000000000..f9129612ea Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23-20e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/231a.png b/backend/Shared/CIPPSharp/bin/twemoji/231a.png new file mode 100644 index 0000000000..0f0ec3c9e3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/231a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/231b.png b/backend/Shared/CIPPSharp/bin/twemoji/231b.png new file mode 100644 index 0000000000..8e9196f3cd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/231b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2328.png b/backend/Shared/CIPPSharp/bin/twemoji/2328.png new file mode 100644 index 0000000000..22a0a7dbce Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2328.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23cf.png b/backend/Shared/CIPPSharp/bin/twemoji/23cf.png new file mode 100644 index 0000000000..940831bdc1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23cf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23e9.png b/backend/Shared/CIPPSharp/bin/twemoji/23e9.png new file mode 100644 index 0000000000..a4277ef3f0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23ea.png b/backend/Shared/CIPPSharp/bin/twemoji/23ea.png new file mode 100644 index 0000000000..9018ecf7eb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23eb.png b/backend/Shared/CIPPSharp/bin/twemoji/23eb.png new file mode 100644 index 0000000000..3311acf024 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23eb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23ec.png b/backend/Shared/CIPPSharp/bin/twemoji/23ec.png new file mode 100644 index 0000000000..2e53b4fffa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23ec.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23ed.png b/backend/Shared/CIPPSharp/bin/twemoji/23ed.png new file mode 100644 index 0000000000..a69200295c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23ed.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23ee.png b/backend/Shared/CIPPSharp/bin/twemoji/23ee.png new file mode 100644 index 0000000000..56116b43d0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23ee.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23ef.png b/backend/Shared/CIPPSharp/bin/twemoji/23ef.png new file mode 100644 index 0000000000..4de7931bee Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23ef.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23f0.png b/backend/Shared/CIPPSharp/bin/twemoji/23f0.png new file mode 100644 index 0000000000..1e16c4f64f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23f1.png b/backend/Shared/CIPPSharp/bin/twemoji/23f1.png new file mode 100644 index 0000000000..34d7f141a0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23f1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23f2.png b/backend/Shared/CIPPSharp/bin/twemoji/23f2.png new file mode 100644 index 0000000000..e8b7dc4741 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23f3.png b/backend/Shared/CIPPSharp/bin/twemoji/23f3.png new file mode 100644 index 0000000000..b86946b324 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23f8.png b/backend/Shared/CIPPSharp/bin/twemoji/23f8.png new file mode 100644 index 0000000000..f2843e3af5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23f9.png b/backend/Shared/CIPPSharp/bin/twemoji/23f9.png new file mode 100644 index 0000000000..009bce06d6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/23fa.png b/backend/Shared/CIPPSharp/bin/twemoji/23fa.png new file mode 100644 index 0000000000..8f14600a24 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/23fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/24c2.png b/backend/Shared/CIPPSharp/bin/twemoji/24c2.png new file mode 100644 index 0000000000..64e209fd09 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/24c2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/25aa.png b/backend/Shared/CIPPSharp/bin/twemoji/25aa.png new file mode 100644 index 0000000000..ab6088aac9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/25aa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/25ab.png b/backend/Shared/CIPPSharp/bin/twemoji/25ab.png new file mode 100644 index 0000000000..250060020d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/25ab.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/25b6.png b/backend/Shared/CIPPSharp/bin/twemoji/25b6.png new file mode 100644 index 0000000000..b0bf29e5b4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/25b6.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/25c0.png b/backend/Shared/CIPPSharp/bin/twemoji/25c0.png new file mode 100644 index 0000000000..2721eabe55 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/25c0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/25fb.png b/backend/Shared/CIPPSharp/bin/twemoji/25fb.png new file mode 100644 index 0000000000..8696fed41d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/25fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/25fc.png b/backend/Shared/CIPPSharp/bin/twemoji/25fc.png new file mode 100644 index 0000000000..417cf85109 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/25fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/25fd.png b/backend/Shared/CIPPSharp/bin/twemoji/25fd.png new file mode 100644 index 0000000000..a7de8524b8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/25fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/25fe.png b/backend/Shared/CIPPSharp/bin/twemoji/25fe.png new file mode 100644 index 0000000000..bcfd21205d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/25fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2600.png b/backend/Shared/CIPPSharp/bin/twemoji/2600.png new file mode 100644 index 0000000000..718b31db31 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2600.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2601.png b/backend/Shared/CIPPSharp/bin/twemoji/2601.png new file mode 100644 index 0000000000..6b1fead8ac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2601.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2602.png b/backend/Shared/CIPPSharp/bin/twemoji/2602.png new file mode 100644 index 0000000000..ce9bb58d1e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2602.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2603.png b/backend/Shared/CIPPSharp/bin/twemoji/2603.png new file mode 100644 index 0000000000..dfff95c7f2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2603.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2604.png b/backend/Shared/CIPPSharp/bin/twemoji/2604.png new file mode 100644 index 0000000000..3b6021bcda Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2604.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/260e.png b/backend/Shared/CIPPSharp/bin/twemoji/260e.png new file mode 100644 index 0000000000..e3319af037 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/260e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2611.png b/backend/Shared/CIPPSharp/bin/twemoji/2611.png new file mode 100644 index 0000000000..4b530a8faa Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2611.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2614.png b/backend/Shared/CIPPSharp/bin/twemoji/2614.png new file mode 100644 index 0000000000..b8a96f9c33 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2614.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2615.png b/backend/Shared/CIPPSharp/bin/twemoji/2615.png new file mode 100644 index 0000000000..5ce8931e1d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2615.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2618.png b/backend/Shared/CIPPSharp/bin/twemoji/2618.png new file mode 100644 index 0000000000..f987099ff7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2618.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/261d-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/261d-1f3fb.png new file mode 100644 index 0000000000..18a9780639 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/261d-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/261d-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/261d-1f3fc.png new file mode 100644 index 0000000000..13dc0973c2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/261d-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/261d-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/261d-1f3fd.png new file mode 100644 index 0000000000..a042487226 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/261d-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/261d-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/261d-1f3fe.png new file mode 100644 index 0000000000..4365323610 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/261d-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/261d-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/261d-1f3ff.png new file mode 100644 index 0000000000..2e943bcb46 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/261d-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/261d.png b/backend/Shared/CIPPSharp/bin/twemoji/261d.png new file mode 100644 index 0000000000..23d7641871 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/261d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2620.png b/backend/Shared/CIPPSharp/bin/twemoji/2620.png new file mode 100644 index 0000000000..ba32dd234d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2620.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2622.png b/backend/Shared/CIPPSharp/bin/twemoji/2622.png new file mode 100644 index 0000000000..6b167878b1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2622.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2623.png b/backend/Shared/CIPPSharp/bin/twemoji/2623.png new file mode 100644 index 0000000000..5d8d42b02b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2623.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2626.png b/backend/Shared/CIPPSharp/bin/twemoji/2626.png new file mode 100644 index 0000000000..b9f0e3a866 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2626.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/262a.png b/backend/Shared/CIPPSharp/bin/twemoji/262a.png new file mode 100644 index 0000000000..c7538a524b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/262a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/262e.png b/backend/Shared/CIPPSharp/bin/twemoji/262e.png new file mode 100644 index 0000000000..8a8be5e463 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/262e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/262f.png b/backend/Shared/CIPPSharp/bin/twemoji/262f.png new file mode 100644 index 0000000000..82faf77a03 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/262f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2638.png b/backend/Shared/CIPPSharp/bin/twemoji/2638.png new file mode 100644 index 0000000000..e3a3e9210a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2638.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2639.png b/backend/Shared/CIPPSharp/bin/twemoji/2639.png new file mode 100644 index 0000000000..9d9698f6b7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2639.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/263a.png b/backend/Shared/CIPPSharp/bin/twemoji/263a.png new file mode 100644 index 0000000000..29c80a226f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/263a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2640.png b/backend/Shared/CIPPSharp/bin/twemoji/2640.png new file mode 100644 index 0000000000..f9b1760358 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2640.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2642.png b/backend/Shared/CIPPSharp/bin/twemoji/2642.png new file mode 100644 index 0000000000..2591c832ac Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2642.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2648.png b/backend/Shared/CIPPSharp/bin/twemoji/2648.png new file mode 100644 index 0000000000..0798e87b7e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2648.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2649.png b/backend/Shared/CIPPSharp/bin/twemoji/2649.png new file mode 100644 index 0000000000..5ca7494601 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2649.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/264a.png b/backend/Shared/CIPPSharp/bin/twemoji/264a.png new file mode 100644 index 0000000000..7bdc7b1002 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/264a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/264b.png b/backend/Shared/CIPPSharp/bin/twemoji/264b.png new file mode 100644 index 0000000000..9d14919ebf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/264b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/264c.png b/backend/Shared/CIPPSharp/bin/twemoji/264c.png new file mode 100644 index 0000000000..a1009e9a0d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/264c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/264d.png b/backend/Shared/CIPPSharp/bin/twemoji/264d.png new file mode 100644 index 0000000000..8bf788774b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/264d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/264e.png b/backend/Shared/CIPPSharp/bin/twemoji/264e.png new file mode 100644 index 0000000000..f06a78cf2e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/264e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/264f.png b/backend/Shared/CIPPSharp/bin/twemoji/264f.png new file mode 100644 index 0000000000..2ca7e0b9c2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/264f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2650.png b/backend/Shared/CIPPSharp/bin/twemoji/2650.png new file mode 100644 index 0000000000..57919345ec Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2650.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2651.png b/backend/Shared/CIPPSharp/bin/twemoji/2651.png new file mode 100644 index 0000000000..b89c8d2196 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2651.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2652.png b/backend/Shared/CIPPSharp/bin/twemoji/2652.png new file mode 100644 index 0000000000..de34060dd3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2652.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2653.png b/backend/Shared/CIPPSharp/bin/twemoji/2653.png new file mode 100644 index 0000000000..84d436f39a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2653.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/265f.png b/backend/Shared/CIPPSharp/bin/twemoji/265f.png new file mode 100644 index 0000000000..18f933cb0c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/265f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2660.png b/backend/Shared/CIPPSharp/bin/twemoji/2660.png new file mode 100644 index 0000000000..c9138c3963 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2660.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2663.png b/backend/Shared/CIPPSharp/bin/twemoji/2663.png new file mode 100644 index 0000000000..20af6f8d36 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2663.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2665.png b/backend/Shared/CIPPSharp/bin/twemoji/2665.png new file mode 100644 index 0000000000..f80860bb3a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2665.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2666.png b/backend/Shared/CIPPSharp/bin/twemoji/2666.png new file mode 100644 index 0000000000..574fadf609 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2666.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2668.png b/backend/Shared/CIPPSharp/bin/twemoji/2668.png new file mode 100644 index 0000000000..6ca91542f1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2668.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/267b.png b/backend/Shared/CIPPSharp/bin/twemoji/267b.png new file mode 100644 index 0000000000..97c09767f3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/267b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/267e.png b/backend/Shared/CIPPSharp/bin/twemoji/267e.png new file mode 100644 index 0000000000..778e87b2e4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/267e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/267f.png b/backend/Shared/CIPPSharp/bin/twemoji/267f.png new file mode 100644 index 0000000000..5d796cee1b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/267f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2692.png b/backend/Shared/CIPPSharp/bin/twemoji/2692.png new file mode 100644 index 0000000000..055a8e7aa0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2692.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2693.png b/backend/Shared/CIPPSharp/bin/twemoji/2693.png new file mode 100644 index 0000000000..277f85ec4d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2693.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2694.png b/backend/Shared/CIPPSharp/bin/twemoji/2694.png new file mode 100644 index 0000000000..61bf8b1bf0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2694.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2695.png b/backend/Shared/CIPPSharp/bin/twemoji/2695.png new file mode 100644 index 0000000000..d542f01bd6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2695.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2696.png b/backend/Shared/CIPPSharp/bin/twemoji/2696.png new file mode 100644 index 0000000000..cf179675d9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2696.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2697.png b/backend/Shared/CIPPSharp/bin/twemoji/2697.png new file mode 100644 index 0000000000..c4b62271c7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2697.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2699.png b/backend/Shared/CIPPSharp/bin/twemoji/2699.png new file mode 100644 index 0000000000..b78e56447a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2699.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/269b.png b/backend/Shared/CIPPSharp/bin/twemoji/269b.png new file mode 100644 index 0000000000..7daedd8e40 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/269b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/269c.png b/backend/Shared/CIPPSharp/bin/twemoji/269c.png new file mode 100644 index 0000000000..7ad56ad454 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/269c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26a0.png b/backend/Shared/CIPPSharp/bin/twemoji/26a0.png new file mode 100644 index 0000000000..a154653002 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26a0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26a1.png b/backend/Shared/CIPPSharp/bin/twemoji/26a1.png new file mode 100644 index 0000000000..797d3ca938 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26a1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26a7.png b/backend/Shared/CIPPSharp/bin/twemoji/26a7.png new file mode 100644 index 0000000000..fc84e19d14 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26a7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26aa.png b/backend/Shared/CIPPSharp/bin/twemoji/26aa.png new file mode 100644 index 0000000000..1d5ec9319f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26aa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26ab.png b/backend/Shared/CIPPSharp/bin/twemoji/26ab.png new file mode 100644 index 0000000000..038f93edd6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26ab.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26b0.png b/backend/Shared/CIPPSharp/bin/twemoji/26b0.png new file mode 100644 index 0000000000..184c59f60d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26b1.png b/backend/Shared/CIPPSharp/bin/twemoji/26b1.png new file mode 100644 index 0000000000..9c45483469 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26b1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26bd.png b/backend/Shared/CIPPSharp/bin/twemoji/26bd.png new file mode 100644 index 0000000000..5e11489594 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26bd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26be.png b/backend/Shared/CIPPSharp/bin/twemoji/26be.png new file mode 100644 index 0000000000..b69c9c765b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26be.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26c4.png b/backend/Shared/CIPPSharp/bin/twemoji/26c4.png new file mode 100644 index 0000000000..70518ad8ed Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26c4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26c5.png b/backend/Shared/CIPPSharp/bin/twemoji/26c5.png new file mode 100644 index 0000000000..13ff46049f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26c5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26c8.png b/backend/Shared/CIPPSharp/bin/twemoji/26c8.png new file mode 100644 index 0000000000..699b775ce6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26c8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26ce.png b/backend/Shared/CIPPSharp/bin/twemoji/26ce.png new file mode 100644 index 0000000000..24fbd8a748 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26ce.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26cf.png b/backend/Shared/CIPPSharp/bin/twemoji/26cf.png new file mode 100644 index 0000000000..d482a90622 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26cf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26d1.png b/backend/Shared/CIPPSharp/bin/twemoji/26d1.png new file mode 100644 index 0000000000..d99f6046f5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26d1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26d3-fe0f-200d-1f4a5.png b/backend/Shared/CIPPSharp/bin/twemoji/26d3-fe0f-200d-1f4a5.png new file mode 100644 index 0000000000..c92a434e39 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26d3-fe0f-200d-1f4a5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26d3.png b/backend/Shared/CIPPSharp/bin/twemoji/26d3.png new file mode 100644 index 0000000000..1fa9dfd006 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26d3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26d4.png b/backend/Shared/CIPPSharp/bin/twemoji/26d4.png new file mode 100644 index 0000000000..d4410559be Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26d4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26e9.png b/backend/Shared/CIPPSharp/bin/twemoji/26e9.png new file mode 100644 index 0000000000..0fb5d1286d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26e9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26ea.png b/backend/Shared/CIPPSharp/bin/twemoji/26ea.png new file mode 100644 index 0000000000..3d84412bb9 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26ea.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f0.png b/backend/Shared/CIPPSharp/bin/twemoji/26f0.png new file mode 100644 index 0000000000..26f6e5cfa1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f1.png b/backend/Shared/CIPPSharp/bin/twemoji/26f1.png new file mode 100644 index 0000000000..120de456f7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f2.png b/backend/Shared/CIPPSharp/bin/twemoji/26f2.png new file mode 100644 index 0000000000..545c3eb85e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f2.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f3.png b/backend/Shared/CIPPSharp/bin/twemoji/26f3.png new file mode 100644 index 0000000000..4dbfac6781 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f4.png b/backend/Shared/CIPPSharp/bin/twemoji/26f4.png new file mode 100644 index 0000000000..ce8759630b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f4.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f5.png b/backend/Shared/CIPPSharp/bin/twemoji/26f5.png new file mode 100644 index 0000000000..ed45fad838 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f5.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f7-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/26f7-1f3fb.png new file mode 100644 index 0000000000..9cd4e00aed Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f7-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f7-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/26f7-1f3fc.png new file mode 100644 index 0000000000..1ed165b678 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f7-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f7-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/26f7-1f3fd.png new file mode 100644 index 0000000000..427b3d027e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f7-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f7-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/26f7-1f3fe.png new file mode 100644 index 0000000000..5970ef6ecb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f7-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f7-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/26f7-1f3ff.png new file mode 100644 index 0000000000..390c4fb504 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f7-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f7.png b/backend/Shared/CIPPSharp/bin/twemoji/26f7.png new file mode 100644 index 0000000000..7de15aa6c7 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f7.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f8.png b/backend/Shared/CIPPSharp/bin/twemoji/26f8.png new file mode 100644 index 0000000000..aedf5af158 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f8.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fb-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fb-200d-2640-fe0f.png new file mode 100644 index 0000000000..83750a1515 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fb-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fb-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fb-200d-2642-fe0f.png new file mode 100644 index 0000000000..4d4d656f53 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fb-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fb.png new file mode 100644 index 0000000000..082b064721 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fc-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fc-200d-2640-fe0f.png new file mode 100644 index 0000000000..807a7fbcf4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fc-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fc-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fc-200d-2642-fe0f.png new file mode 100644 index 0000000000..a2427ae231 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fc-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fc.png new file mode 100644 index 0000000000..b8f1405132 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fd-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fd-200d-2640-fe0f.png new file mode 100644 index 0000000000..7cbed5efaf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fd-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fd-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fd-200d-2642-fe0f.png new file mode 100644 index 0000000000..10facd5e67 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fd-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fd.png new file mode 100644 index 0000000000..b2b315a683 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fe-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fe-200d-2640-fe0f.png new file mode 100644 index 0000000000..851ee25df4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fe-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fe-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fe-200d-2642-fe0f.png new file mode 100644 index 0000000000..fb6f377a8f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fe-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fe.png new file mode 100644 index 0000000000..b5a25be18c Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3ff-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3ff-200d-2640-fe0f.png new file mode 100644 index 0000000000..e3d4cec690 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3ff-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3ff-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3ff-200d-2642-fe0f.png new file mode 100644 index 0000000000..830fa1a72e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3ff-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3ff.png new file mode 100644 index 0000000000..74e0500e7f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-fe0f-200d-2640-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-fe0f-200d-2640-fe0f.png new file mode 100644 index 0000000000..42df033036 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-fe0f-200d-2640-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9-fe0f-200d-2642-fe0f.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9-fe0f-200d-2642-fe0f.png new file mode 100644 index 0000000000..122f330725 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9-fe0f-200d-2642-fe0f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26f9.png b/backend/Shared/CIPPSharp/bin/twemoji/26f9.png new file mode 100644 index 0000000000..b7f7856204 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26f9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26fa.png b/backend/Shared/CIPPSharp/bin/twemoji/26fa.png new file mode 100644 index 0000000000..d8a0dbd39e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26fa.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/26fd.png b/backend/Shared/CIPPSharp/bin/twemoji/26fd.png new file mode 100644 index 0000000000..8307de2e55 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/26fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2702.png b/backend/Shared/CIPPSharp/bin/twemoji/2702.png new file mode 100644 index 0000000000..ba94dc6418 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2702.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2705.png b/backend/Shared/CIPPSharp/bin/twemoji/2705.png new file mode 100644 index 0000000000..0683c7ecf1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2705.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2708.png b/backend/Shared/CIPPSharp/bin/twemoji/2708.png new file mode 100644 index 0000000000..6bffcd5a96 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2708.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2709.png b/backend/Shared/CIPPSharp/bin/twemoji/2709.png new file mode 100644 index 0000000000..90023eb685 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2709.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270a-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/270a-1f3fb.png new file mode 100644 index 0000000000..0b7aeb8042 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270a-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270a-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/270a-1f3fc.png new file mode 100644 index 0000000000..14b14468f2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270a-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270a-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/270a-1f3fd.png new file mode 100644 index 0000000000..73ff6c977f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270a-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270a-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/270a-1f3fe.png new file mode 100644 index 0000000000..f2ae4eb0df Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270a-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270a-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/270a-1f3ff.png new file mode 100644 index 0000000000..39e3a237da Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270a-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270a.png b/backend/Shared/CIPPSharp/bin/twemoji/270a.png new file mode 100644 index 0000000000..0e2291c261 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270a.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270b-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/270b-1f3fb.png new file mode 100644 index 0000000000..b8a3784618 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270b-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270b-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/270b-1f3fc.png new file mode 100644 index 0000000000..45c1a47426 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270b-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270b-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/270b-1f3fd.png new file mode 100644 index 0000000000..d1a826860b Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270b-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270b-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/270b-1f3fe.png new file mode 100644 index 0000000000..0a593055ee Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270b-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270b-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/270b-1f3ff.png new file mode 100644 index 0000000000..d556c2a316 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270b-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270b.png b/backend/Shared/CIPPSharp/bin/twemoji/270b.png new file mode 100644 index 0000000000..ce49d6aef1 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270c-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/270c-1f3fb.png new file mode 100644 index 0000000000..3a69852347 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270c-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270c-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/270c-1f3fc.png new file mode 100644 index 0000000000..488baf8bf6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270c-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270c-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/270c-1f3fd.png new file mode 100644 index 0000000000..10a5746601 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270c-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270c-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/270c-1f3fe.png new file mode 100644 index 0000000000..bf52c750e4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270c-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270c-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/270c-1f3ff.png new file mode 100644 index 0000000000..61f75f2d75 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270c-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270c.png b/backend/Shared/CIPPSharp/bin/twemoji/270c.png new file mode 100644 index 0000000000..5734f34918 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270d-1f3fb.png b/backend/Shared/CIPPSharp/bin/twemoji/270d-1f3fb.png new file mode 100644 index 0000000000..a2f9d932eb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270d-1f3fb.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270d-1f3fc.png b/backend/Shared/CIPPSharp/bin/twemoji/270d-1f3fc.png new file mode 100644 index 0000000000..f9f6739061 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270d-1f3fc.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270d-1f3fd.png b/backend/Shared/CIPPSharp/bin/twemoji/270d-1f3fd.png new file mode 100644 index 0000000000..0943238115 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270d-1f3fd.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270d-1f3fe.png b/backend/Shared/CIPPSharp/bin/twemoji/270d-1f3fe.png new file mode 100644 index 0000000000..8252421f51 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270d-1f3fe.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270d-1f3ff.png b/backend/Shared/CIPPSharp/bin/twemoji/270d-1f3ff.png new file mode 100644 index 0000000000..4214c3a152 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270d-1f3ff.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270d.png b/backend/Shared/CIPPSharp/bin/twemoji/270d.png new file mode 100644 index 0000000000..86f753c3d2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/270f.png b/backend/Shared/CIPPSharp/bin/twemoji/270f.png new file mode 100644 index 0000000000..ae8bdbc285 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/270f.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2712.png b/backend/Shared/CIPPSharp/bin/twemoji/2712.png new file mode 100644 index 0000000000..e5d30fd605 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2712.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2714.png b/backend/Shared/CIPPSharp/bin/twemoji/2714.png new file mode 100644 index 0000000000..b63dce1d12 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2714.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2716.png b/backend/Shared/CIPPSharp/bin/twemoji/2716.png new file mode 100644 index 0000000000..32c55aa9a2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2716.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/271d.png b/backend/Shared/CIPPSharp/bin/twemoji/271d.png new file mode 100644 index 0000000000..0755be4508 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/271d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2721.png b/backend/Shared/CIPPSharp/bin/twemoji/2721.png new file mode 100644 index 0000000000..149560071a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2721.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2728.png b/backend/Shared/CIPPSharp/bin/twemoji/2728.png new file mode 100644 index 0000000000..ebd2c77b1d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2728.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2733.png b/backend/Shared/CIPPSharp/bin/twemoji/2733.png new file mode 100644 index 0000000000..1f393dae53 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2733.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2734.png b/backend/Shared/CIPPSharp/bin/twemoji/2734.png new file mode 100644 index 0000000000..83d31fafd2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2734.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2744.png b/backend/Shared/CIPPSharp/bin/twemoji/2744.png new file mode 100644 index 0000000000..ee53c86873 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2744.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2747.png b/backend/Shared/CIPPSharp/bin/twemoji/2747.png new file mode 100644 index 0000000000..8a9731b97d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2747.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/274c.png b/backend/Shared/CIPPSharp/bin/twemoji/274c.png new file mode 100644 index 0000000000..8347c7bad5 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/274c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/274e.png b/backend/Shared/CIPPSharp/bin/twemoji/274e.png new file mode 100644 index 0000000000..a96a67c92d Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/274e.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2753.png b/backend/Shared/CIPPSharp/bin/twemoji/2753.png new file mode 100644 index 0000000000..1f337b2214 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2753.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2754.png b/backend/Shared/CIPPSharp/bin/twemoji/2754.png new file mode 100644 index 0000000000..a28b30475a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2754.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2755.png b/backend/Shared/CIPPSharp/bin/twemoji/2755.png new file mode 100644 index 0000000000..0b173031e2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2755.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2757.png b/backend/Shared/CIPPSharp/bin/twemoji/2757.png new file mode 100644 index 0000000000..9c48a72739 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2757.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2763.png b/backend/Shared/CIPPSharp/bin/twemoji/2763.png new file mode 100644 index 0000000000..1e421bc9db Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2763.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2764-fe0f-200d-1f525.png b/backend/Shared/CIPPSharp/bin/twemoji/2764-fe0f-200d-1f525.png new file mode 100644 index 0000000000..36be1b8077 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2764-fe0f-200d-1f525.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2764-fe0f-200d-1fa79.png b/backend/Shared/CIPPSharp/bin/twemoji/2764-fe0f-200d-1fa79.png new file mode 100644 index 0000000000..b834e62ccb Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2764-fe0f-200d-1fa79.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2764.png b/backend/Shared/CIPPSharp/bin/twemoji/2764.png new file mode 100644 index 0000000000..a7511e97ae Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2764.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2795.png b/backend/Shared/CIPPSharp/bin/twemoji/2795.png new file mode 100644 index 0000000000..21d5a886d2 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2795.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2796.png b/backend/Shared/CIPPSharp/bin/twemoji/2796.png new file mode 100644 index 0000000000..7779c9d3f0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2796.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2797.png b/backend/Shared/CIPPSharp/bin/twemoji/2797.png new file mode 100644 index 0000000000..1d2425eaff Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2797.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/27a1.png b/backend/Shared/CIPPSharp/bin/twemoji/27a1.png new file mode 100644 index 0000000000..1cf8b3d683 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/27a1.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/27b0.png b/backend/Shared/CIPPSharp/bin/twemoji/27b0.png new file mode 100644 index 0000000000..71def8e1bd Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/27b0.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/27bf.png b/backend/Shared/CIPPSharp/bin/twemoji/27bf.png new file mode 100644 index 0000000000..e01c0c2dd6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/27bf.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2934.png b/backend/Shared/CIPPSharp/bin/twemoji/2934.png new file mode 100644 index 0000000000..bafb601bb6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2934.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2935.png b/backend/Shared/CIPPSharp/bin/twemoji/2935.png new file mode 100644 index 0000000000..83466a0243 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2935.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2a-20e3.png b/backend/Shared/CIPPSharp/bin/twemoji/2a-20e3.png new file mode 100644 index 0000000000..8944081e47 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2a-20e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2b05.png b/backend/Shared/CIPPSharp/bin/twemoji/2b05.png new file mode 100644 index 0000000000..03c7d3af48 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2b05.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2b06.png b/backend/Shared/CIPPSharp/bin/twemoji/2b06.png new file mode 100644 index 0000000000..4f2a411021 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2b06.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2b07.png b/backend/Shared/CIPPSharp/bin/twemoji/2b07.png new file mode 100644 index 0000000000..cbb200dadc Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2b07.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2b1b.png b/backend/Shared/CIPPSharp/bin/twemoji/2b1b.png new file mode 100644 index 0000000000..1ea0e3ec7e Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2b1b.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2b1c.png b/backend/Shared/CIPPSharp/bin/twemoji/2b1c.png new file mode 100644 index 0000000000..5f76e52ee3 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2b1c.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2b50.png b/backend/Shared/CIPPSharp/bin/twemoji/2b50.png new file mode 100644 index 0000000000..0590867e07 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2b50.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/2b55.png b/backend/Shared/CIPPSharp/bin/twemoji/2b55.png new file mode 100644 index 0000000000..8106cde260 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/2b55.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/30-20e3.png b/backend/Shared/CIPPSharp/bin/twemoji/30-20e3.png new file mode 100644 index 0000000000..f0d5615acf Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/30-20e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/3030.png b/backend/Shared/CIPPSharp/bin/twemoji/3030.png new file mode 100644 index 0000000000..8e74f9b895 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/3030.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/303d.png b/backend/Shared/CIPPSharp/bin/twemoji/303d.png new file mode 100644 index 0000000000..1a47aff64f Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/303d.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/31-20e3.png b/backend/Shared/CIPPSharp/bin/twemoji/31-20e3.png new file mode 100644 index 0000000000..3836e2c3e4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/31-20e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/32-20e3.png b/backend/Shared/CIPPSharp/bin/twemoji/32-20e3.png new file mode 100644 index 0000000000..dbb85d1f99 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/32-20e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/3297.png b/backend/Shared/CIPPSharp/bin/twemoji/3297.png new file mode 100644 index 0000000000..ac83914e0a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/3297.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/3299.png b/backend/Shared/CIPPSharp/bin/twemoji/3299.png new file mode 100644 index 0000000000..c9e9e27f0a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/3299.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/33-20e3.png b/backend/Shared/CIPPSharp/bin/twemoji/33-20e3.png new file mode 100644 index 0000000000..4b77c2d792 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/33-20e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/34-20e3.png b/backend/Shared/CIPPSharp/bin/twemoji/34-20e3.png new file mode 100644 index 0000000000..ddbb5559ca Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/34-20e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/35-20e3.png b/backend/Shared/CIPPSharp/bin/twemoji/35-20e3.png new file mode 100644 index 0000000000..6bf88f6a59 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/35-20e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/36-20e3.png b/backend/Shared/CIPPSharp/bin/twemoji/36-20e3.png new file mode 100644 index 0000000000..cf16307eb4 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/36-20e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/37-20e3.png b/backend/Shared/CIPPSharp/bin/twemoji/37-20e3.png new file mode 100644 index 0000000000..308e89ca0a Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/37-20e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/38-20e3.png b/backend/Shared/CIPPSharp/bin/twemoji/38-20e3.png new file mode 100644 index 0000000000..34f098df01 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/38-20e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/39-20e3.png b/backend/Shared/CIPPSharp/bin/twemoji/39-20e3.png new file mode 100644 index 0000000000..df6e1161d6 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/39-20e3.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/ATTRIBUTION.txt b/backend/Shared/CIPPSharp/bin/twemoji/ATTRIBUTION.txt new file mode 100644 index 0000000000..a3584ef88a --- /dev/null +++ b/backend/Shared/CIPPSharp/bin/twemoji/ATTRIBUTION.txt @@ -0,0 +1,409 @@ +Twemoji colour emoji graphics +============================= + +The PNG files in this directory are Twemoji (https://github.com/jdecked/twemoji), +the maintained community fork of Twitter's open-source emoji set. The CIPP +server-side PDF renderer places them as inline colour images so reports show +colour emoji (the standard PDF fonts and font embedding are monochrome-only). + +Graphics licence: CC-BY 4.0. +Attribution: "Twemoji" by Twitter, Inc. and other contributors, licensed under +CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/). + +Only the 72x72 PNG assets are bundled; the Twemoji JavaScript (MIT) is not used. +The full CC-BY 4.0 licence text follows. + + +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public licenses. +Notwithstanding, Creative Commons may elect to apply one of its public +licenses to material it publishes and in those instances will be +considered the "Licensor." Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the public +licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/backend/Shared/CIPPSharp/bin/twemoji/a9.png b/backend/Shared/CIPPSharp/bin/twemoji/a9.png new file mode 100644 index 0000000000..db8ce8aed8 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/a9.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/ae.png b/backend/Shared/CIPPSharp/bin/twemoji/ae.png new file mode 100644 index 0000000000..76dec323b0 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/ae.png differ diff --git a/backend/Shared/CIPPSharp/bin/twemoji/e50a.png b/backend/Shared/CIPPSharp/bin/twemoji/e50a.png new file mode 100644 index 0000000000..898209d505 Binary files /dev/null and b/backend/Shared/CIPPSharp/bin/twemoji/e50a.png differ diff --git a/backend/Tests/Build/CIPPSharp-PdfDependencies.Tests.ps1 b/backend/Tests/Build/CIPPSharp-PdfDependencies.Tests.ps1 new file mode 100644 index 0000000000..b3fb61e6ed --- /dev/null +++ b/backend/Tests/Build/CIPPSharp-PdfDependencies.Tests.ps1 @@ -0,0 +1,43 @@ +# Guards the bundling of the server-side PDF renderer: the OfficeIMO DLLs must ship next to +# CIPPSharp.dll (loaded via RequiredAssemblies; .NET resolves them by same-directory probing, there +# is no deps.json), and the CIPPSharp assembly must expose the ReportPdf entry point. + +BeforeAll { + $RepoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSCommandPath)) + $script:Bin = Join-Path $RepoRoot 'Shared/CIPPSharp/bin' +} + +Describe 'CIPPSharp PDF dependencies' { + It 'ships CIPPSharp.dll' { + Test-Path (Join-Path $script:Bin 'CIPPSharp.dll') | Should -BeTrue + } + It 'ships OfficeIMO.Pdf.dll beside CIPPSharp.dll' { + Test-Path (Join-Path $script:Bin 'OfficeIMO.Pdf.dll') | Should -BeTrue + } + It 'ships OfficeIMO.Core.dll beside CIPPSharp.dll' { + Test-Path (Join-Path $script:Bin 'OfficeIMO.Core.dll') | Should -BeTrue + } + It 'does not ship the System.Management.Automation facade' { + Test-Path (Join-Path $script:Bin 'System.Management.Automation.dll') | Should -BeFalse + } + It 'ships the emoji fallback font beside CIPPSharp.dll' { + # The monochrome symbol/emoji fallback the renderer loads at runtime (ReportPdf.EmojiFontBytes); + # without it emoji degrade to ASCII tokens rather than glyphs. + Test-Path (Join-Path $script:Bin 'emoji-fallback.ttf') | Should -BeTrue + } + It 'ships the Twemoji colour emoji image set' { + # The colour emoji PNGs the renderer places inline (TwemojiAssets); a large individual-file set so a + # render only reads the handful it embeds. A few thousand files, keyed by lowercase hex code point. + $twemoji = Join-Path $script:Bin 'twemoji' + Test-Path $twemoji | Should -BeTrue + (Get-ChildItem -Path $twemoji -Filter '*.png' -File).Count | Should -BeGreaterThan 1000 + Test-Path (Join-Path $twemoji '26a0.png') | Should -BeTrue # warning + Test-Path (Join-Path $twemoji '2705.png') | Should -BeTrue # green check + } + It 'exposes the ReportPdf renderer type' { + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $script:Bin 'OfficeIMO.Core.dll')) + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $script:Bin 'OfficeIMO.Pdf.dll')) + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $script:Bin 'CIPPSharp.dll')) + ([type]'CIPP.Reporting.ReportPdf') | Should -Not -BeNullOrEmpty + } +} diff --git a/backend/Tests/DBCache/Add-CIPPDbItem.Shape.Tests.ps1 b/backend/Tests/DBCache/Add-CIPPDbItem.Shape.Tests.ps1 new file mode 100644 index 0000000000..19facf3039 --- /dev/null +++ b/backend/Tests/DBCache/Add-CIPPDbItem.Shape.Tests.ps1 @@ -0,0 +1,59 @@ +# Pester tests for the collection shape the cache writer records on the '-Count' row: the +# fields its rows carry, one nested level deep, with their types - what the report builder's data +# picker offers. + +BeforeAll { + $RepoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSCommandPath)) + . (Get-ChildItem -Path (Join-Path $RepoRoot 'Modules') -Recurse -Filter 'Add-CIPPDbItem.ps1' | Select-Object -First 1 -ExpandProperty FullName) + + $script:Written = @{} + $script:Existing = $null + function Get-CippTable { param($tablename) @{ Context = $tablename } } + function Add-CIPPAzDataTableEntity { param($Context, $Entity, [switch]$Force) foreach ($e in @($Entity)) { $script:Written[$e.RowKey] = $e } } + function Get-CIPPAzDataTableEntity { param($Context, $Filter, $Property) $script:Existing } + function Remove-CIPPAzDataTableEntity { param($Context, $Entity, [switch]$Force) } + function Write-LogMessage { param([Parameter(ValueFromRemainingArguments = $true)]$Rest) } + function Get-Tenants { param($TenantFilter, [switch]$IncludeErrors) @() } + + function Get-Shape { (ConvertFrom-Json -InputObject $script:Written['Devices-Count'].Shape).fields | ForEach-Object { "$($_.name):$($_.type)" } } +} + +Describe 'Add-CIPPDbItem shape' { + BeforeEach { $script:Written = @{}; $script:Existing = $null } + + It 'records each field with its type, one nested level deep, on the count row' { + @( + [PSCustomObject]@{ id = 'a'; deviceName = 'PC1'; isCompliant = $true; storage = 512; enrolledAt = '2026-09-01T10:00:00Z'; hardware = [PSCustomObject]@{ model = 'X1'; ram = 16 }; apps = @([PSCustomObject]@{ name = 'Edge' }) } + [PSCustomObject]@{ id = 'b'; deviceName = 'PC2'; isCompliant = $false; storage = $null; lastSeen = $null } + ) | Add-CIPPDbItem -TenantFilter 'contoso.onmicrosoft.com' -Type 'Devices' -AddCount + + $Shape = @(Get-Shape) + $Shape | Should -Contain 'deviceName:string' + $Shape | Should -Contain 'isCompliant:boolean' + $Shape | Should -Contain 'storage:number' + $Shape | Should -Contain 'enrolledAt:date' + $Shape | Should -Contain 'hardware:object' + $Shape | Should -Contain 'hardware.model:string' + $Shape | Should -Contain 'apps:array' + $Shape | Should -Contain 'apps.name:string' + $Shape | Should -Contain 'lastSeen:null' + $script:Written['Devices-Count'].DataCount | Should -Be 2 + } + + It 'keeps the recorded shape when a count-only call follows the rows' { + $script:Existing = @{ DataCount = 7; Shape = '{"fields":[{"name":"deviceName","type":"string"}]}' } + Add-CIPPDbItem -TenantFilter 'contoso.onmicrosoft.com' -Type 'Devices' -InputObject 7 -Count + + @(Get-Shape) | Should -Be @('deviceName:string') + } + + It 'merges into the recorded shape when appending' { + $script:Existing = @{ DataCount = 1; Shape = '{"fields":[{"name":"deviceName","type":"string"}]}' } + @([PSCustomObject]@{ id = 'c'; osVersion = '10.0' }) | Add-CIPPDbItem -TenantFilter 'contoso.onmicrosoft.com' -Type 'Devices' -AddCount -Append + + $Shape = @(Get-Shape) + $Shape | Should -Contain 'deviceName:string' + $Shape | Should -Contain 'osVersion:string' + $script:Written['Devices-Count'].DataCount | Should -Be 2 + } +} diff --git a/backend/Tests/Endpoint/Invoke-ExecBECEvidenceExport.Tests.ps1 b/backend/Tests/Endpoint/Invoke-ExecBECEvidenceExport.Tests.ps1 index b95ca8856a..d1cf021a44 100644 --- a/backend/Tests/Endpoint/Invoke-ExecBECEvidenceExport.Tests.ps1 +++ b/backend/Tests/Endpoint/Invoke-ExecBECEvidenceExport.Tests.ps1 @@ -5,7 +5,7 @@ BeforeAll { if (-not ([System.Management.Automation.PSTypeName]'HttpStatusCode').Type) { $TypeAccelerators::Add('HttpStatusCode', [System.Net.HttpStatusCode]) } - function New-CIPPBecEvidencePackage { param($TenantFilter, $CaseId, $PdfBase64, $PdfSummaryBase64, $Headers, $APIName) } + function New-CIPPBecEvidencePackage { param($TenantFilter, $CaseId, $Headers, $APIName) } function Set-CippBecCaseContext { param($CaseId) } function Write-LogMessage { param($message, $tenant, $API, $tenantId, $headers, $user, $sev, $LogData) } function Get-CippException { param($Exception) [pscustomobject]@{ NormalizedError = [string]$Exception.Exception.Message } } @@ -45,13 +45,6 @@ Describe 'Invoke-ExecBECEvidenceExport' { Should -Invoke Write-LogMessage -Times 0 -ParameterFilter { $sev -eq 'Error' } } - It 'forwards the browser-rendered PDFs to the package builder, and passes them empty when the browser sent none' { - $null = Invoke-ExecBECEvidenceExport -Request (New-Request @{ tenantFilter = 'contoso.com'; caseId = 'BEC-1'; pdfBase64 = 'JVBERi0x'; pdfSummaryBase64 = 'JVBERi0y' }) -TriggerMetadata $null - Should -Invoke New-CIPPBecEvidencePackage -Times 1 -ParameterFilter { $PdfBase64 -eq 'JVBERi0x' -and $PdfSummaryBase64 -eq 'JVBERi0y' } - $null = Invoke-ExecBECEvidenceExport -Request (New-Request @{ tenantFilter = 'contoso.com'; caseId = 'BEC-1' }) -TriggerMetadata $null - Should -Invoke New-CIPPBecEvidencePackage -Times 1 -ParameterFilter { $CaseId -eq 'BEC-1' -and $PdfBase64 -eq '' -and $PdfSummaryBase64 -eq '' } - } - It 'stamps the case id on the log context while it runs and clears it afterwards' { $null = Invoke-ExecBECEvidenceExport -Request (New-Request @{ tenantFilter = 'contoso.com'; caseId = 'BEC-1' }) -TriggerMetadata $null Should -Invoke Set-CippBecCaseContext -Times 2 -Exactly diff --git a/backend/Tests/Private/New-CIPPBecEvidencePackage.Tests.ps1 b/backend/Tests/Private/New-CIPPBecEvidencePackage.Tests.ps1 index 163460e23b..a02043172e 100644 --- a/backend/Tests/Private/New-CIPPBecEvidencePackage.Tests.ps1 +++ b/backend/Tests/Private/New-CIPPBecEvidencePackage.Tests.ps1 @@ -7,6 +7,11 @@ BeforeAll { # Nothing in the evidence path may touch blob storage function New-CIPPAzStorageRequest { throw 'blob storage must not be touched' } function Write-LogMessage { param($message, $tenant, $API, $tenantId, $headers, $user, $sev, $LogData) } + # The reports are rendered server-side through the report builder + kit; stub them so the unit test + # never loads OfficeIMO - the package's job here is collation, not the kit's PDF bytes. + function Get-CippReportTenantName { param($TenantFilter) } + function Build-CippBecReportTree { param($UserData, $BecData, $TenantName, $Variant) } + function ConvertTo-CippReportPdf { param($Blocks, $Variables, $TenantName, $TenantFilter, $ReportName, $Branding, $BrandingPresetId, $GeneratedOn, $PageSize, [switch]$Landscape) } . (Join-Path $RepoRoot 'Modules/CIPPCore/Public/BEC/New-CIPPBecEvidencePackage.ps1') $script:Run = [pscustomobject]@{ @@ -21,7 +26,7 @@ BeforeAll { Score = [pscustomobject]@{ Value = 12; Level = 'High' } } } - $script:PdfBase64 = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes('%PDF-1.4 fake')) + $script:FakePdf = [System.Text.Encoding]::ASCII.GetBytes('%PDF-1.4 fake') function Read-Zip { param([byte[]]$Bytes) @@ -52,10 +57,13 @@ Describe 'New-CIPPBecEvidencePackage' { ) } Mock Write-LogMessage { } + Mock Get-CippReportTenantName { 'Contoso' } + Mock Build-CippBecReportTree { @{ Blocks = @(@{ type = 'blank' }); Variables = @{ coverlabel = 'Security Incident Report' } } } + Mock ConvertTo-CippReportPdf { $script:FakePdf } } - It 'builds a ZIP with the results, per-finding CSVs, score, containment, logbook and both PDFs, and stores nothing' { - $Package = New-CIPPBecEvidencePackage -TenantFilter 'contoso.com' -CaseId 'BEC-20260820120000-ev0001' -PdfBase64 $script:PdfBase64 -PdfSummaryBase64 $script:PdfBase64 + It 'builds a ZIP with the results, per-finding CSVs, score, containment, logbook and both server-rendered PDFs, and stores nothing' { + $Package = New-CIPPBecEvidencePackage -TenantFilter 'contoso.com' -CaseId 'BEC-20260820120000-ev0001' $Package.Bytes | Should -BeGreaterThan 0 $Entries = Read-Zip -Bytes $Package.ZipBytes $Entries.Keys | Should -Contain 'results.json' @@ -69,15 +77,25 @@ Describe 'New-CIPPBecEvidencePackage' { $Entries.Keys | Should -Contain 'report-full.pdf' $Entries.Keys | Should -Contain 'report-summary.pdf' $Entries.Count | Should -Be $Package.FileCount + # rendered server-side: the full report and the summary variant, no browser round-trip + Should -Invoke ConvertTo-CippReportPdf -Times 2 + Should -Invoke Build-CippBecReportTree -Times 1 -ParameterFilter { $Variant -eq 'summary' } Should -Invoke Set-CIPPBecReport -Times 0 -Because 'an export leaves no record on the run' } - It 'flattens arrays and nested objects into CSV cells and builds without PDFs' { + It 'flattens arrays and nested objects into CSV cells' { $Package = New-CIPPBecEvidencePackage -TenantFilter 'contoso.com' -CaseId 'BEC-20260820120000-ev0001' $Entries = Read-Zip -Bytes $Package.ZipBytes $Csv = [System.Text.Encoding]::UTF8.GetString($Entries['findings/NewRules.csv']) $Csv | Should -Match 'Forwards or redirects messages; Deletes messages' $Csv | Should -Match '\{""a"":1\}' + } + + It 'still ships the rest of the package when the PDF render fails' { + Mock ConvertTo-CippReportPdf { throw 'render boom' } + $Package = New-CIPPBecEvidencePackage -TenantFilter 'contoso.com' -CaseId 'BEC-20260820120000-ev0001' + $Entries = Read-Zip -Bytes $Package.ZipBytes + $Entries.Keys | Should -Contain 'results.json' $Entries.Keys | Should -Not -Contain 'report-full.pdf' $Entries.Keys | Should -Not -Contain 'report-summary.pdf' } @@ -91,8 +109,7 @@ Describe 'New-CIPPBecEvidencePackage' { Should -Invoke Get-CIPPAzDataTableEntity -Times 1 -ParameterFilter { $Filter -like "BecCaseId eq 'BEC-20260820120000-ev0001'*" -and $Filter -like "*PartitionKey ge '20260819'*" } } - It 'refuses a non-PDF and an incomplete run' { - { New-CIPPBecEvidencePackage -TenantFilter 'contoso.com' -CaseId 'BEC-20260820120000-ev0001' -PdfBase64 ([Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(''))) } | Should -Throw '*not a PDF*' + It 'refuses an incomplete run' { Mock Get-CIPPBecReport { [pscustomobject]@{ Status = 'Waiting' } } { New-CIPPBecEvidencePackage -TenantFilter 'contoso.com' -CaseId 'BEC-x' } | Should -Throw '*only completed runs*' } diff --git a/backend/Tests/Reports/Add-CIPPImage.Tests.ps1 b/backend/Tests/Reports/Add-CIPPImage.Tests.ps1 index 49c6e83419..1ed519bf3f 100644 --- a/backend/Tests/Reports/Add-CIPPImage.Tests.ps1 +++ b/backend/Tests/Reports/Add-CIPPImage.Tests.ps1 @@ -46,6 +46,13 @@ Describe 'Add-CIPPImage' { Should -Be 'image/svg+xml' } + It 'accepts every format the report engine draws, whatever the case of the subtype' { + foreach ($Type in 'PNG', 'jpg', 'gif', 'bmp', 'tiff', 'webp') { + (Add-CIPPImage -PartitionKey 'logo' -Data (New-DataUrl -Type $Type)).contentType | + Should -Be "image/$($Type.ToLowerInvariant())" + } + } + It 'stores the original data URL, not just the decoded bytes' { $Url = New-DataUrl Add-CIPPImage -PartitionKey 'logo' -Data $Url | Out-Null @@ -88,6 +95,12 @@ Describe 'Add-CIPPImage' { Should -Throw '*Invalid image format*' } + It 'rejects an image format the report engine cannot draw' { + { Add-CIPPImage -PartitionKey 'logo' -Data (New-DataUrl -Type 'heic') } | + Should -Throw '*Unsupported image format*' + $script:SavedEntity | Should -BeNullOrEmpty + } + It 'rejects a bare URL' { { Add-CIPPImage -PartitionKey 'logo' -Data 'https://example.com/logo.png' } | Should -Throw '*Invalid image format*' diff --git a/backend/Tests/Reports/Build-CippBaselineWhatIfReportTree.Tests.ps1 b/backend/Tests/Reports/Build-CippBaselineWhatIfReportTree.Tests.ps1 new file mode 100644 index 0000000000..79ccb936fe --- /dev/null +++ b/backend/Tests/Reports/Build-CippBaselineWhatIfReportTree.Tests.ps1 @@ -0,0 +1,132 @@ +# Pester tests for the Baseline What-If report: the tree builder puts the right standards in the +# right sections with the wording the client document used, and the endpoint renders the tree from +# the alignment getters into PDF bytes. + +BeforeAll { + $RepoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSCommandPath)) + $Bin = Join-Path $RepoRoot 'Shared/CIPPSharp/bin' + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $Bin 'OfficeIMO.Core.dll')) + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $Bin 'OfficeIMO.Pdf.dll')) + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $Bin 'CIPPSharp.dll')) + + if (-not ('HttpResponseContext' -as [type])) { + Add-Type -TypeDefinition 'public class HttpResponseContext { public object StatusCode; public object Body; public string ContentType; public object Headers; }' + } + $null = [PowerShell].Assembly.GetType('System.Management.Automation.TypeAccelerators')::Add('HttpStatusCode', [System.Net.HttpStatusCode]) + + $Reporting = Get-ChildItem -Path (Join-Path $RepoRoot 'Modules') -Recurse -Directory -Filter 'Reporting' | Select-Object -First 1 + Get-ChildItem -Path $Reporting.FullName -Filter '*.ps1' | ForEach-Object { . $_.FullName } + foreach ($Name in 'ConvertTo-CippReportPdf.ps1', 'Get-CippReportTenantName.ps1', 'Invoke-ExecGetBaselineWhatIfReportPdf.ps1') { + . (Get-ChildItem -Path (Join-Path $RepoRoot 'Modules') -Recurse -Filter $Name | Select-Object -First 1 -ExpandProperty FullName) + } + function Get-CIPPBrandingSettings { @{ colour = '#F77F00' } } + function Get-CIPPBrandingPreset { param($Id, [switch]$SkipImageData) @() } + function Write-LogMessage { param([Parameter(ValueFromRemainingArguments = $true)]$Rest) } + function Get-CippException { param($Exception) @{ NormalizedError = "$($Exception)" } } + function Get-CIPPTextReplacement { param($TenantFilter, $Text, [switch]$EscapeForJson) $Text } + + $script:Catalog = @( + @{ name = 'MFA'; label = 'Require MFA'; executiveText = 'Everyone signs in with a second factor.' } + @{ name = 'Legacy'; label = 'Block legacy auth'; executiveText = 'Old sign-in protocols are switched off.' } + @{ name = 'CA'; label = 'Conditional Access'; executiveText = 'Access rules follow the device and location.' } + @{ name = 'Guests'; label = 'Disable stale guests'; executiveText = 'Guests who stop signing in are disabled.' } + ) + $script:Alignment = [PSCustomObject]@{ + summary = @{ alignedPercentage = 75; verifiedPercentage = 50 } + rows = @( + @{ standardName = 'Legacy'; standardLabel = 'Block legacy auth'; status = 'Drift'; impact = 'High Impact'; secureScoreImpact = 5; sourceTemplate = 'Core' } + @{ standardName = 'MFA'; standardLabel = 'Require MFA'; status = 'Compliant'; impact = 'Low Impact'; secureScoreImpact = 0; sourceTemplate = 'Core' } + @{ standardName = 'Guests'; standardLabel = 'Disable stale guests'; status = 'Accepted'; deviationReason = 'Guests are reviewed by HR quarterly.'; sourceTemplate = 'Core' } + @{ standardName = 'CA'; standardLabel = 'Conditional Access'; status = 'Denied - Remediate Pending'; impact = 'Medium Impact'; secureScoreImpact = 0; sourceTemplate = 'Core' } + ) + stageStates = @( + @{ + templateId = 'core'; templateName = 'Core'; currentStage = 1; totalStages = 2; stageName = 'Foundations' + enteredStageAt = '2026-09-01T00:00:00Z'; nextStageName = 'Hardening' + nextStage = @{ name = 'Hardening'; logic = 'and'; conditions = @(@{ type = 'time'; days = 2; unit = 'weeks' }, @{ type = 'success' }); standards = @('CA', 'CA#2', 'Unknown') } + } + ) + } + $script:Simulated = @{ + GUID = 'sim'; templateName = 'Security Plus'; description = 'Adds the extras' + stages = @( + @{ name = 'Kickoff'; standards = @('MFA#1', 'Legacy') } + @{ name = 'Later'; logic = 'or'; conditions = @(@{ type = 'manual' }, @{ type = 'variable'; variable = 'tier'; operator = 'eq'; value = 'gold' }); standards = @('Guests') } + ) + } + function Build-Fixture([switch]$WithSimulation) { + Build-CippBaselineWhatIfReportTree -Data @{ + TenantName = 'Contoso'; TenantFilter = 'contoso.onmicrosoft.com' + summary = $script:Alignment.summary; rows = $script:Alignment.rows; stageStates = $script:Alignment.stageStates + simulatedTemplate = $(if ($WithSimulation) { $script:Simulated }); catalog = $script:Catalog + } + } + function Get-BlockText($Report) { $Report.Blocks | ConvertTo-Json -Depth 10 -Compress } +} + +Describe 'Build-CippBaselineWhatIfReportTree' { + It 'counts the changes, planned stages and exceptions onto the cover' { + $r = Build-Fixture + $r.Variables.coverlabel | Should -Be 'Baseline What-If' + $r.Variables.covermeta | Should -Be '2 changes to make / 1 staged rollout / 1 agreed exception' + $r.Variables.covermetanote | Should -Be 'Compliant with baseline: 50%' + } + + It 'lists the drifted and denied standards as changes, with their impact notes' { + $text = Get-BlockText (Build-Fixture) + $text | Should -Match 'Changes we would make now \(2\)' + $text | Should -Match 'Old sign-in protocols are switched off\. \(High Impact - increases Secure Score by up to 5 points\)' + $text | Should -Match 'Access rules follow the device and location\. \(Medium Impact - deviation denied, fix pending\)' + $text | Should -Match 'Guests are reviewed by HR quarterly\.' + } + + It 'describes the next stage in words with an estimated date, listing only catalogued standards once' { + $text = Get-BlockText (Build-Fixture) + $text | Should -Match 'Currently in Stage 1 of 2 \(Foundations\)\. Next: Stage 2 \(Hardening\) - advances when 2 weeks in the previous stage AND all previous stage items applied successfully, estimated around September 15, 2026\.' + ([regex]::Matches($text, 'Access rules follow the device and location\. ')).Count | Should -Be 1 + $text | Should -Not -Match 'Unknown' + } + + It 'adds the what-if section only when a baseline is simulated, marking what is already aligned' { + (Get-BlockText (Build-Fixture)) | Should -Not -Match 'What-if: assigning' + $text = Get-BlockText (Build-Fixture -WithSimulation) + $text | Should -Match 'What-if: assigning the Security Plus baseline' + $text | Should -Match 'Stage 1: Kickoff - applies immediately' + $text | Should -Match 'Everyone signs in with a second factor\. No change - already aligned today \(configured by Core\)\.' + $text | Should -Match 'Old sign-in protocols are switched off\. Would change this tenant when the stage applies\.' + $text | Should -Match "Stage 2: Later - advances when manual approval by an operator OR tier equals 'gold'" + } + + It 'renders an empty tenant with the nothing-to-change notes' { + $r = Build-CippBaselineWhatIfReportTree -Data @{ TenantName = 'Contoso' } + $text = Get-BlockText $r + $text | Should -Match 'Nothing to change' + $text | Should -Match 'No further staged changes' + $Bytes = ConvertTo-CippReportPdf -Blocks $r.Blocks -Variables $r.Variables -TenantName 'Contoso' -ReportName 'T' + [System.Text.Encoding]::ASCII.GetString($Bytes[0..4]) | Should -Be '%PDF-' + } +} + +Describe 'Invoke-ExecGetBaselineWhatIfReportPdf' { + BeforeAll { + function Get-Tenants { param($TenantFilter) @{ displayName = 'Contoso' } } + function Get-CIPPBaselineAlignment { param($TenantFilter) $script:Alignment } + function Get-CIPPBaseline { @($script:Simulated, @{ GUID = 'core'; templateName = 'Core'; stages = @() }) } + function Get-CIPPBaselineDefinition { $script:Catalog } + function Invoke-Report($Body) { + Invoke-ExecGetBaselineWhatIfReportPdf -Request @{ Body = $Body; Query = @{}; Headers = @{} } -TriggerMetadata @{ FunctionName = 'ExecGetBaselineWhatIfReportPdf' } + } + } + + It 'renders the tenant alignment, with a simulated baseline, as a PDF' { + $Response = Invoke-Report @{ tenantFilter = 'contoso.onmicrosoft.com'; simulatedTemplateId = 'sim' } + $Response.StatusCode | Should -Be ([System.Net.HttpStatusCode]::OK) + $Response.ContentType | Should -Be 'application/pdf' + $Response.Headers.'Content-Disposition' | Should -Be 'inline; filename="Baseline_WhatIf_Report_contoso_onmicrosoft_com.pdf"' + [System.Text.Encoding]::ASCII.GetString($Response.Body[0..4]) | Should -Be '%PDF-' + } + + It 'requires a tenantFilter' { + (Invoke-Report @{}).StatusCode | Should -Be ([System.Net.HttpStatusCode]::BadRequest) + } +} diff --git a/backend/Tests/Reports/Build-CippReportTrees.Tests.ps1 b/backend/Tests/Reports/Build-CippReportTrees.Tests.ps1 new file mode 100644 index 0000000000..792333dae4 --- /dev/null +++ b/backend/Tests/Reports/Build-CippReportTrees.Tests.ps1 @@ -0,0 +1,109 @@ +# Pester tests for the fixed-report tree builders: each composes @{ Blocks; Variables } from shaped +# sample data, the grade on the cover matches the tree's own scoring, and the tree renders to a PDF. + +BeforeAll { + $RepoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSCommandPath)) + $Bin = Join-Path $RepoRoot 'Shared/CIPPSharp/bin' + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $Bin 'OfficeIMO.Core.dll')) + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $Bin 'OfficeIMO.Pdf.dll')) + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $Bin 'CIPPSharp.dll')) + + $Reporting = Get-ChildItem -Path (Join-Path $RepoRoot 'Modules') -Recurse -Directory -Filter 'Reporting' | Select-Object -First 1 + Get-ChildItem -Path $Reporting.FullName -Filter '*.ps1' | ForEach-Object { . $_.FullName } + . (Get-ChildItem -Path (Join-Path $RepoRoot 'Modules') -Recurse -Filter 'ConvertTo-CippReportPdf.ps1' | Select-Object -First 1 -ExpandProperty FullName) + function Get-CIPPBrandingSettings { @{ colour = '#F77F00' } } + + function Test-Report($Report, [string]$Label) { + $Report.Blocks.Count | Should -BeGreaterThan 0 + $Report.Variables.coverlabel | Should -Be $Label + $Bytes = ConvertTo-CippReportPdf -Blocks $Report.Blocks -Variables $Report.Variables -TenantName 'Contoso' -ReportName 'T' + [System.Text.Encoding]::ASCII.GetString($Bytes[0..4]) | Should -Be '%PDF-' + } +} + +Describe 'Report tree builders' { + It 'Sharing: grades exposure from the summary and renders' { + $r = Build-CippSharingReportTree -Data @{ + TenantName = 'Contoso' + summary = @{ totalLinks = 4; itemsShared = 3; externalRecipients = 1; anonymousEditLinks = 1; neverExpiringAnonymous = 1 } + links = @(@{ fileName = 'a.docx'; siteName = 'S'; classification = 'Anonymous'; roles = @('write'); itemType = 'File' }) + topRecipients = @(@{ recipient = 'x@example.com'; links = 1 }) + topLibraries = @() + } + $r.Variables.covermetanote | Should -Be 'Sharing exposure: High' + Test-Report $r 'Data Sharing Review' + } + + It 'Permissions: a lone detached library is Low exposure and renders' { + $r = Build-CippPermissionsReportTree -Data @{ + TenantName = 'Contoso' + summary = @{ uniquePermissionLibraries = 1; sitesScanned = 2; librariesScanned = 3; totalAssignments = 5 } + assignments = @(@{ principalId = 'p1'; scope = 'Library'; siteName = 'S'; libraryTitle = 'Docs'; title = 'Bob'; permissionLevel = 'Edit'; principalType = 'User' }) + } + $r.Variables.covermetanote | Should -Be 'Permission exposure: Low' + Test-Report $r 'Access Review' + } + + It 'MailFlow: sums the dispositions, grades hygiene and renders' { + $r = Build-CippMailFlowReportTree -Data @{ + TenantName = 'Contoso'; days = 7 + totals = @{ GoodMail = 90; EmailPhish = 10 } + daily = @(@{ date = '2026-09-01'; GoodMail = 90; EmailPhish = 10 }) + topSenders = @(@{ name = 'a@contoso.com'; count = 5 }) + } + $r.Variables.covermeta | Should -Be '100 messages / 90% delivered / 10 threats caught' + $r.Variables.covermetanote | Should -Be 'Mail hygiene: Attention Needed' + Test-Report $r 'Email Traffic Review' + } + + It 'MailFlow: an empty window (no rows at all) still renders, with empty charts' { + $r = Build-CippMailFlowReportTree -Data @{ TenantName = 'Contoso' } + $r.Variables.covermetanote | Should -Be 'Mail hygiene: Good' + Test-Report $r 'Email Traffic Review' + } + + It 'ShadowAI: merges sanctioned tools across both sources and renders' { + $r = Build-CippShadowAIReportTree -Data @{ + TenantName = 'Contoso' + summary = @{ aiToolsDetected = 2 } + detectedApps = @(@{ aiTool = 'ChatGPT'; vendor = 'OpenAI'; category = 'Chat'; status = 'Sanctioned'; deviceCount = 3; risk = 'High' }) + consentedApps = @(@{ aiTool = 'ChatGPT'; vendor = 'OpenAI'; category = 'Chat'; status = 'Sanctioned'; activeUsersLast7Days = 5; risk = 'High' }) + topTools = @(); byRisk = @(@{ risk = 'High'; tools = 1 }) + } + $Sanctioned = $r.Blocks | Where-Object { $_.type -eq 'richtable' -and $_.rows[0].tool -eq 'ChatGPT' -and $_.rows[0].users } | Select-Object -First 1 + $Sanctioned.rows[0].devices | Should -Be '3' + $Sanctioned.rows[0].users | Should -Be '5' + Test-Report $r 'AI Risk Assessment' + } + + It 'Executive: composes every section from shaped data and renders' { + $r = Build-CippExecutiveReportTree -Data @{ + TenantName = 'Contoso' + UserStats = @{ licensedUsers = 10; unlicensedUsers = 1; guests = 2; globalAdmins = 1; permanentGlobalAdmins = 1; eligibleGlobalAdmins = 0; pimCapable = $true } + SecureScore = @{ currentScore = 50; maxScore = 100; percentageCurrent = 50; percentageVsSimilar = 40; percentageVsAllTenants = 45; trend = @(@{ label = 'Sep 1'; value = 50 }) } + Licenses = @(@{ name = 'E3'; used = '5'; available = '1'; total = '6' }) + Devices = @(@{ name = 'PC1'; os = 'Windows'; compliance = 'compliant'; compliant = $true; lastSync = 'Sep 1, 2026'; encrypted = $true }) + CAPolicies = @(@{ name = 'Require MFA'; state = 'enabled'; controls = @('mfa'); controlsText = 'MFA'; applications = 'All' }) + SecurityControls = @(@{ name = 'MFA'; description = 'd'; tags = 't'; status = 'Compliant' }) + } + @($r.Blocks | Where-Object { $_.type -eq 'hero' }).Count | Should -Be 5 + $r.Variables.covertenant | Should -Be 'Contoso' + Test-Report $r 'SECURITY ASSESSMENT' + } + + It 'BEC: renders the server-computed threat level, names the user on the cover and renders' { + # The threat level is computed server-side (Get-CIPPBecScore) and stored on the run; the builder + # renders that stored Score rather than scoring the findings itself. + $r = Build-CippBecReportTree -TenantName 'Contoso' -UserData @{ displayName = 'Alice'; userPrincipalName = 'alice@contoso.com' } -BecData @{ + ExtractedAt = '2026-09-01T00:00:00Z' + Score = @{ Value = 19; Level = 'High' } + NewRules = @(@{ Name = 'Hide'; MoveToFolder = 'RSS Subscriptions' }) + SentMessageAnalysis = @{ Flagged = $true; Bursts = @(@{ MessageCount = 40; RecipientCount = 40; WindowStart = '2026-09-01T09:00:00Z'; TopSubject = 'Invoice' }) } + LocationAnalysis = @{ UsageLocation = 'AU' } + } + ($r.Blocks | Where-Object { $_.type -eq 'alertbox' -and $_.title -like 'Threat Assessment:*' }).title | Should -Be 'Threat Assessment: High (score 19)' + $r.Variables.covertenant | Should -Be 'Alice' + $r.Variables.footerlabel | Should -Be 'Contoso - BEC Analysis Report for Alice' + Test-Report $r 'Security Incident Report' + } +} diff --git a/backend/Tests/Reports/ConvertTo-CippReportPdf.Tests.ps1 b/backend/Tests/Reports/ConvertTo-CippReportPdf.Tests.ps1 new file mode 100644 index 0000000000..9bb34eb801 --- /dev/null +++ b/backend/Tests/Reports/ConvertTo-CippReportPdf.Tests.ps1 @@ -0,0 +1,226 @@ +# Pester tests for ConvertTo-CippReportPdf and the CIPPSharp component kit it wraps. +# Verifies every block type renders to a valid PDF, empty input still produces a page, branding is +# applied without throwing, and every image format the engine accepts decodes and renders. + +BeforeAll { + $RepoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSCommandPath)) + $Bin = Join-Path $RepoRoot 'Shared/CIPPSharp/bin' + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $Bin 'OfficeIMO.Core.dll')) + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $Bin 'OfficeIMO.Pdf.dll')) + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $Bin 'CIPPSharp.dll')) + + $HelperPath = Get-ChildItem -Path (Join-Path $RepoRoot 'Modules') -Recurse -Filter 'ConvertTo-CippReportPdf.ps1' -File -ErrorAction SilentlyContinue | + Select-Object -First 1 -ExpandProperty FullName + if (-not $HelperPath) { throw 'Could not locate ConvertTo-CippReportPdf.ps1 under Modules/' } + . $HelperPath + # The wrapper resolves branding itself when none is passed; keep that offline. + function Get-CIPPBrandingSettings { @{} } + function Get-CIPPBrandingPreset { param($Id, [switch]$SkipImageData) @() } + + function Test-IsPdf { + param($Bytes) + if ($Bytes -isnot [byte[]] -or $Bytes.Length -lt 100) { return $false } + return ([System.Text.Encoding]::ASCII.GetString($Bytes[0..4]) -eq '%PDF-') + } + + # 1x1 transparent PNG. + $script:TinyPng = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==' +} + +Describe 'ConvertTo-CippReportPdf' { + Context 'Block types render to a valid PDF' { + It 'renders a blank (HTML) block with marks and a list' { + $b = @(@{ type = 'blank'; title = 'Summary'; content = '

    Hello world and more

    • one
    • two
    ' }) + Test-IsPdf (ConvertTo-CippReportPdf -Blocks $b -TenantName 'Contoso' -ReportName 'T') | Should -BeTrue + } + It 'renders a markdown test block with a status and a table' { + $md = "## Details`n`nUsers without **MFA** are exposed. SKU ``SPE_E5`` stays literal.`n`n| Setting | State |`n|---|---|`n| MFA | Off |" + $b = @(@{ type = 'test'; title = 'MFA'; status = 'Failed'; static = $false; content = $md }) + Test-IsPdf (ConvertTo-CippReportPdf -Blocks $b) | Should -BeTrue + } + It 'renders a database markdown table block' { + $b = @(@{ type = 'database'; title = 'Users'; format = 'text'; content = "| Name | UPN |`n|---|---|`n| Bob | bob@x.com |" }) + Test-IsPdf (ConvertTo-CippReportPdf -Blocks $b) | Should -BeTrue + } + It 'renders a database csv/json block as a code block' { + $b = @(@{ type = 'database'; title = 'Raw'; format = 'json'; content = '[{"a":1}]' }) + Test-IsPdf (ConvertTo-CippReportPdf -Blocks $b) | Should -BeTrue + } + It 'renders a scorecard block' { + $b = @(@{ type = 'scorecard'; title = 'At a glance'; stats = @(@{ value = '3'; label = 'Anon' }, @{ value = '7'; label = 'No expiry' }) }) + Test-IsPdf (ConvertTo-CippReportPdf -Blocks $b) | Should -BeTrue + } + It 'renders a chart block' { + $b = @(@{ type = 'chart'; title = 'By risk'; chartData = @(@{ label = 'High'; value = 5 }, @{ label = 'Low'; value = 9 }) }) + Test-IsPdf (ConvertTo-CippReportPdf -Blocks $b) | Should -BeTrue + } + It 'renders a hero and a page break without throwing' { + $b = @(@{ type = 'hero'; title = 'Chapter'; heroHighlight = '39' }, @{ type = 'pagebreak' }, @{ type = 'blank'; content = '

    after

    ' }) + Test-IsPdf (ConvertTo-CippReportPdf -Blocks $b) | Should -BeTrue + } + } + + Context 'Edge cases' { + It 'renders an empty component tree as a valid one-page PDF' { + Test-IsPdf (ConvertTo-CippReportPdf -Blocks @()) | Should -BeTrue + } + It 'applies a branding colour without throwing' { + $b = @(@{ type = 'blank'; content = '

    x

    ' }) + $branding = @{ colour = '#0E4C92'; secondaryColour = '#F77F00'; watermarkText = 'DRAFT'; watermarkEnabled = $true; footerText = '%tenantname% report' } + Test-IsPdf (ConvertTo-CippReportPdf -Blocks $b -Branding $branding -TenantName 'Contoso') | Should -BeTrue + } + It 'still renders when the branding logo cannot be embedded (skips it gracefully)' { + $b = @(@{ type = 'blank'; content = '

    x

    ' }) + # An unusable logo (here a PNG OfficeIMO rejects) must not sink the whole report. + Test-IsPdf (ConvertTo-CippReportPdf -Blocks $b -Branding @{ logo = $script:TinyPng }) | Should -BeTrue + } + It 'accepts a pre-serialised JSON block string' { + $json = ConvertTo-Json -InputObject @(@{ type = 'blank'; content = '

    json

    ' }) -Depth 10 + Test-IsPdf (ConvertTo-CippReportPdf -Blocks $json) | Should -BeTrue + } + } + + Context 'Image formats' { + BeforeAll { + # 1x1 fixtures: the smallest valid GIF and WebP, a two-colour SVG, and a corrupt PNG. + $script:Gif = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' + $script:Webp = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA' + $script:Svg = 'data:image/svg+xml;base64,' + [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes('')) + $script:Block = @(@{ type = 'blank'; title = 'Formats'; content = '

    x

    ' }) + } + It 'identifies every raster OfficeIMO decodes, not just PNG and JPEG' { + [CIPP.Reporting.ReportComponents]::ImageContentType([CIPP.Reporting.ReportComponents]::DecodeImage($script:Gif)) | Should -Be 'image/gif' + [CIPP.Reporting.ReportComponents]::ImageContentType([CIPP.Reporting.ReportComponents]::DecodeImage($script:Webp)) | Should -Be 'image/webp' + } + It 'rasterises an SVG once at decode time so every placement sees a PNG' { + $bytes = [CIPP.Reporting.ReportComponents]::DecodeImage($script:Svg) + [CIPP.Reporting.ReportComponents]::ImageContentType($bytes) | Should -Be 'image/png' + # 40x20 source scaled to 1200px on the long side, aspect kept. + $size = [CIPP.Reporting.ReportComponents]::ImageSize($bytes) + $size.Item1 | Should -Be 1200 + $size.Item2 | Should -Be 600 + } + It 'drops what OfficeIMO cannot identify rather than failing the render' { + [CIPP.Reporting.ReportComponents]::DecodeImage($script:TinyPng) | Should -BeNullOrEmpty + [CIPP.Reporting.ReportComponents]::DecodeImage('data:image/png;base64,' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes('not an image'))) | Should -BeNullOrEmpty + } + It 'renders a GIF logo, an SVG logo and a WebP cover' { + Test-IsPdf (ConvertTo-CippReportPdf -Blocks $script:Block -Branding @{ logo = $script:Gif }) | Should -BeTrue + Test-IsPdf (ConvertTo-CippReportPdf -Blocks $script:Block -Branding @{ logo = $script:Svg }) | Should -BeTrue + Test-IsPdf (ConvertTo-CippReportPdf -Blocks $script:Block -Branding @{ coverImage = $script:Webp; logo = $script:Webp }) | Should -BeTrue + } + } + + Context 'Emoji rendering' { + BeforeAll { + # A render populates the emoji flags (font coverage from the cmap; Twemoji image assets present). + $null = ConvertTo-CippReportPdf -Blocks @(@{ type = 'blank'; content = '

    x

    ' }) + } + It 'renders arbitrary BMP and astral emoji (incl. a ZWJ sequence) alongside text without throwing' { + $party = [char]::ConvertFromUtf32(0x1F389) # astral (surrogate pair) + $rocket = [char]::ConvertFromUtf32(0x1F680) # astral + $star = [char]0x2B50 # BMP symbol + $dev = [char]::ConvertFromUtf32(0x1F469) + [char]0x200D + [char]::ConvertFromUtf32(0x1F4BB) # woman technologist (ZWJ) + $b = @(@{ type = 'blank'; content = "

    Great work $party a rocket $rocket a star $star a dev $dev and warning [!]

    " }) + Test-IsPdf (ConvertTo-CippReportPdf -Blocks $b) | Should -BeTrue + } + It 'renders emoji inside a callout (table cell) without throwing' { + $party = [char]::ConvertFromUtf32(0x1F389) + $b = @(@{ type = 'infobox'; title = "Alert $party"; tone = 'warn'; content = "Body [!] with a rocket $([char]::ConvertFromUtf32(0x1F680))." }) + Test-IsPdf (ConvertTo-CippReportPdf -Blocks $b) | Should -BeTrue + } + It 'loads the bundled monochrome font coverage and the Twemoji image assets' { + [CIPP.Reporting.ReportMarkdown]::RenderEmojiGlyphs | Should -BeTrue + [CIPP.Reporting.ReportMarkdown]::EmojiCoverage.Count | Should -BeGreaterThan 100 + [CIPP.Reporting.ReportMarkdown]::RenderEmojiImages | Should -BeTrue + } + It 'keeps a colour emoji (which the renderer draws as an image) rather than stripping it' { + # A red circle has a Twemoji asset, so Sanitize keeps it verbatim for the image renderer. + [CIPP.Reporting.ReportMarkdown]::Sanitize([char]::ConvertFromUtf32(0x1F534)) | Should -Be ([char]::ConvertFromUtf32(0x1F534)) + } + It 'promotes the status tokens to their colour glyphs' { + [CIPP.Reporting.ReportMarkdown]::Sanitize('[Pass]') | Should -Be '✅' + [CIPP.Reporting.ReportMarkdown]::Sanitize('[Fail]') | Should -Be '❌' + } + } +} + +Describe 'Watermark layering' { + # OfficeIMO paints the watermark first; the engine moves its operator block to the end of each page's + # content stream so it sits over the content, on content pages and dark divider pages alike. + It 'draws the watermark after everything else on every page that carries one' { + $Blocks = @( + @{ type = 'scorecard'; title = 'Figures'; stats = @(@{ value = '1'; label = 'One' }, @{ value = '2'; label = 'Two' }) } + @{ type = 'hero'; title = 'Divider'; heroHighlight = '83%'; heroSubText = 'of controls in place' } + @{ type = 'blank'; title = 'After the divider'; content = '

    Body text under the mark.

    ' } + ) + $Bytes = ConvertTo-CippReportPdf -Blocks $Blocks -Variables @{} -Branding @{ colour = '#0E4C92'; watermarkText = 'Preview'; watermarkEnabled = $true } -TenantName 'Contoso' -ReportName 'T' + $Pdf = [System.Text.Encoding]::Latin1.GetString($Bytes) + $Marker = '<50524556494557> Tj' + $Streams = [regex]::Matches($Pdf, '(?s)<< /Length (\d+) >>\s*stream\n(.*?)\nendstream') | ForEach-Object { $_.Groups[2].Value } + $Marked = @($Streams | Where-Object { $_.Contains($Marker) }) + # a content page and the divider at least; the cover never carries one + $Marked.Count | Should -BeGreaterOrEqual 2 + $Streams[0] | Should -Not -Match 'Tj\s*ET\s*Q\s*$' + foreach ($Data in $Marked) { + $Data.TrimEnd() | Should -Match "$([regex]::Escape($Marker))\s*ET\s*Q$" + ([regex]::Matches($Data, '> Tj|\) Tj') | Select-Object -Last 1).Value | Should -Be '> Tj' + $Data.LastIndexOf(' re') | Should -BeLessThan $Data.IndexOf($Marker) + } + } + + It 'leaves a document without a watermark untouched' { + $Bytes = ConvertTo-CippReportPdf -Blocks @(@{ type = 'blank'; title = 'T'; content = '

    x

    ' }) -Variables @{} -Branding @{ colour = '#0E4C92' } -TenantName 'Contoso' -ReportName 'T' + [System.Text.Encoding]::ASCII.GetString($Bytes[0..4]) | Should -Be '%PDF-' + [System.Text.Encoding]::Latin1.GetString($Bytes) | Should -Not -Match '0\.707 -0\.707 0\.707 0\.707' + } +} + +Describe 'Tenant name in the branding text' { + It 'substitutes %tenantname% with the name the report was given, before the cache-based replacement runs' { + $script:Seen = @() + function Get-CIPPTextReplacement { param($TenantFilter, $Text, [switch]$EscapeForJson) $script:Seen += $Text; $Text -replace '%tenantname%', 'CacheName' } + $Bytes = ConvertTo-CippReportPdf -Blocks @(@{ type = 'blank'; title = 'T'; content = '

    x

    ' }) -Variables @{} -Branding @{ colour = '#0E4C92'; footerText = 'Prepared for %TenantName%'; watermarkText = '%tenantname%' } -TenantName 'contoso.onmicrosoft.com' -TenantFilter 'contoso.onmicrosoft.com' -ReportName 'T' + [System.Text.Encoding]::ASCII.GetString($Bytes[0..4]) | Should -Be '%PDF-' + $Branding = $script:Seen | Where-Object { $_ -like '*footerText*' } | Select-Object -First 1 + $Branding | Should -Match 'Prepared for contoso\.onmicrosoft\.com' + $Branding | Should -Match '"watermarkText":"contoso\.onmicrosoft\.com"' + $Branding | Should -Not -Match '(?i)%tenantname%' + } +} + +Describe 'Report builder cover block' { + It 'puts a cover block on the cover and draws nothing else for it' { + $Bytes = ConvertTo-CippReportPdf -Blocks @( + @{ type = 'cover'; title = 'Custom Cover'; coverAccent = 'Words'; subtitle = 'A subtitle of my own'; coverLabel = 'My Label' } + @{ type = 'blank'; title = 'Body'; content = '

    x

    ' } + ) -Variables @{} -Branding @{ colour = '#0E4C92' } -TenantName 'Contoso' -ReportName 'Ignored Name' + $Pdf = [System.Text.Encoding]::Latin1.GetString($Bytes) + $Hex = { param($s) [Convert]::ToHexString([System.Text.Encoding]::ASCII.GetBytes($s)) } + # the drawing writes a word per string, so the words are what can be matched + $Pdf | Should -Match (& $Hex 'CUSTOM') + $Pdf | Should -Match (& $Hex 'COVER') + $Pdf | Should -Match (& $Hex 'WORDS') + $Pdf | Should -Match (& $Hex 'LABEL') + $Pdf | Should -Not -Match (& $Hex 'IGNORED') + ([regex]::Matches($Pdf, '/Type /Page[^s]')).Count | Should -Be 2 + } +} + +Describe 'Gallery covers on Infographic pages' { + It 'reads a gallery cover into the page and leaves a missing one as a plain background' { + function Get-CIPPImage { param($PartitionKey, $Id) if ($Id -eq 'g1') { @{ data = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==' } } } + $Blocks = @( + @{ type = 'hero'; title = 'With cover'; heroHighlight = '1'; heroImage = 'gallery:g1' } + @{ type = 'hero'; title = 'Without'; heroHighlight = '2'; heroImage = 'gallery:missing' } + ) + $Bytes = ConvertTo-CippReportPdf -Blocks $Blocks -Variables @{} -Branding @{ colour = '#0E4C92'; coverStock = 'none' } -TenantName 'Contoso' -ReportName 'T' + $Pdf = [System.Text.Encoding]::Latin1.GetString($Bytes) + # the PNG carries an alpha mask, so it lands as two image objects; none at all without a cover + ([regex]::Matches($Pdf, '/Subtype /Image')).Count | Should -BeGreaterOrEqual 1 + $Plain = ConvertTo-CippReportPdf -Blocks @(@{ type = 'hero'; title = 'Without'; heroHighlight = '2'; heroImage = 'gallery:missing' }) -Variables @{} -Branding @{ colour = '#0E4C92'; coverStock = 'none' } -TenantName 'Contoso' -ReportName 'T' + ([regex]::Matches([System.Text.Encoding]::Latin1.GetString($Plain), '/Subtype /Image')).Count | Should -Be 0 + $Blocks[0].heroImage | Should -BeLike 'data:image/png;base64,*' + $Blocks[1].heroImage | Should -Be '' + } +} diff --git a/backend/Tests/Reports/Get-CIPPBrandingSettings.Tests.ps1 b/backend/Tests/Reports/Get-CIPPBrandingSettings.Tests.ps1 index 97d341cc52..7fbccec333 100644 --- a/backend/Tests/Reports/Get-CIPPBrandingSettings.Tests.ps1 +++ b/backend/Tests/Reports/Get-CIPPBrandingSettings.Tests.ps1 @@ -26,6 +26,7 @@ BeforeAll { return $Map } function Add-CIPPImage { param($PartitionKey, $Data) @{ id = 'migrated-1' } } + function Get-CIPPImageNameMap { param($PartitionKey) if ($script:ImageNames) { $script:ImageNames } else { @{} } } function ConvertTo-CIPPCoverImageIdList { param($Value) if ($null -eq $Value -or $Value -eq '') { return , [string[]]@() } @@ -155,6 +156,21 @@ Describe 'Get-CIPPBrandingSettings' { } } + Context 'Named covers' { + It 'lists the gallery covers by id with the names they were given' { + $script:Rows = @(New-ConfigRow -Overrides @{ coverImageIds = '["c1","c2"]' }) + $script:Images = @{ c1 = @{ data = 'data:image/png;base64,AAAA' }; c2 = @{ data = 'data:image/png;base64,BBBB' } } + $script:ImageNames = @{ c1 = 'Board room' } + $Covers = @((Get-CIPPBrandingSettings).coverImages) + $Covers.Count | Should -Be 2 + $Covers[0].id | Should -Be 'c1' + $Covers[0].name | Should -Be 'Board room' + $Covers[1].name | Should -Be '' + $script:ImageNames = $null + $script:Images = @{} + } + } + Context 'Cover stock' { It 'falls back to the default stock when the stored value is not one we ship' { $script:Rows = @(New-ConfigRow -Overrides @{ coverStock = 'https://evil.example/x.jpg' }) diff --git a/backend/Tests/Reports/Get-CIPPShadowAIReport.Tests.ps1 b/backend/Tests/Reports/Get-CIPPShadowAIReport.Tests.ps1 new file mode 100644 index 0000000000..7bbcc2671f --- /dev/null +++ b/backend/Tests/Reports/Get-CIPPShadowAIReport.Tests.ps1 @@ -0,0 +1,69 @@ +# Pester tests for Get-CIPPShadowAIReport, the cache-plus-catalog rollup behind ListShadowAI and the +# Shadow AI PDF: Intune rows merge per tool, Entra apps pick up their grants, and both sources roll up +# into one tool list for the summary and charts. + +BeforeAll { + $RepoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSCommandPath)) + . (Join-Path $RepoRoot 'Modules/CIPPCore/Public/Get-CIPPShadowAIReport.ps1') + # The real catalog (Config/ShadowAI.json) resolves through CIPPRootPath. + $env:CIPPRootPath = $RepoRoot + + # Static stubs for the cache, table and Graph reads. No pass-through mocks. + function New-CIPPDbRequest { + param($TenantFilter, $Type, $Fields) + switch ($Type) { + 'DetectedApps' { + @( + [pscustomobject]@{ displayName = 'GitHub Copilot'; publisher = 'GitHub, Inc.'; version = '1.0'; platform = 'Windows'; managedDevices = @(@{ id = 'dev1' }, @{ id = 'dev2' }) } + [pscustomobject]@{ displayName = 'GitHub Copilot'; publisher = 'CN=GitHub, Inc., O=GitHub'; version = '1.1'; platform = 'Windows'; managedDevices = @(@{ id = 'dev2' }, @{ id = 'dev3' }) } + [pscustomobject]@{ displayName = 'Notepad'; publisher = 'Microsoft'; version = '11'; platform = 'Windows'; managedDevices = @(@{ id = 'dev1' }) } + ) + } + 'ServicePrincipals' { @([pscustomobject]@{ displayName = 'GitHub Copilot for Business'; appId = 'app-1'; id = 'sp-1'; createdDateTime = '2026-01-15T00:00:00Z' }) } + 'OAuth2PermissionGrants' { @([pscustomobject]@{ clientId = 'sp-1'; scope = 'User.Read Mail.Read User.Read' }) } + } + } + function Get-CIPPDbItem { param($TenantFilter, $Type, [switch]$CountsOnly) [pscustomobject]@{ Timestamp = [datetime]'2026-09-01' } } + function Get-CIPPTable { param($TableName) @{ Context = $TableName } } + function Get-CIPPAzDataTableEntity { param($Context, $Filter) @() } + function New-GraphGetRequest { param($uri, $tenantid) throw 'no P1' } + function Write-LogMessage { param([Parameter(ValueFromRemainingArguments = $true)]$Rest) } +} + +Describe 'Get-CIPPShadowAIReport' { + BeforeAll { $script:Report = Get-CIPPShadowAIReport -TenantFilter 'contoso.onmicrosoft.com' } + + It 'merges the Intune inventory rows of one tool into a single row with distinct devices' { + $apps = @($script:Report.detectedApps) + $apps.Count | Should -Be 1 + $apps[0].aiTool | Should -Be 'GitHub Copilot' + $apps[0].deviceCount | Should -Be 3 + $apps[0].version | Should -Be '1.0, 1.1' + # The shortest publisher string wins over the certificate subject. + $apps[0].publisher | Should -Be 'GitHub, Inc.' + $apps[0].status | Should -Be 'Unsanctioned' + } + + It 'matches Entra service principals and attaches their distinct granted scopes' { + $apps = @($script:Report.consentedApps) + $apps.Count | Should -Be 1 + $apps[0].applicationId | Should -Be 'app-1' + @($apps[0].approvedPermissions) | Should -Be @('Mail.Read', 'User.Read') + # The sign-in enrichment is best-effort: without P1 the counts stay at zero. + $apps[0].activeUsersLast7Days | Should -Be 0 + } + + It 'rolls both sources up into one tool for the summary and charts' { + $r = $script:Report + $r.summary.aiToolsDetected | Should -Be 1 + $r.summary.deviceInstalls | Should -Be 3 + $r.summary.consentedAiApps | Should -Be 1 + $r.summary.intuneSynced | Should -BeTrue + $r.topTools[0].tool | Should -Be 'GitHub Copilot' + $r.topTools[0].devices | Should -Be 3 + $r.topTools[0].footprint | Should -Be 3 + $r.byRisk[0].risk | Should -Be 'Medium' + $r.byRisk[0].tools | Should -Be 1 + $r.byCategory[0].category | Should -Be 'AI Coding' + } +} diff --git a/backend/Tests/Reports/Get-CIPPSharePointPermissionsReport.Tests.ps1 b/backend/Tests/Reports/Get-CIPPSharePointPermissionsReport.Tests.ps1 new file mode 100644 index 0000000000..c300282ddc --- /dev/null +++ b/backend/Tests/Reports/Get-CIPPSharePointPermissionsReport.Tests.ps1 @@ -0,0 +1,62 @@ +# Pester tests for Get-CIPPSharePointPermissionsReport, the cache rollup behind ListSharePointPermissions +# and the permissions PDF: scan coverage, the oversharing signals and the derived display fields. + +BeforeAll { + $RepoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSCommandPath)) + . (Join-Path $RepoRoot 'Modules/CIPPCore/Public/Get-CIPPSharePointPermissionsReport.ps1') + + # Static stubs for the cache reads. No pass-through mocks. + function New-CIPPDbRequest { + param($TenantFilter, $Type, $Fields) + @( + [pscustomobject]@{ rowType = 'Site'; siteName = 'Sales'; siteUrl = 'https://x/sites/Sales'; librariesScanned = 2; librariesWithUniquePermissions = 1; collectionStatus = 'Completed' } + [pscustomobject]@{ rowType = 'Site'; siteName = 'Broken'; siteUrl = 'https://x/sites/Broken'; librariesScanned = 0; librariesWithUniquePermissions = 0; collectionStatus = 'Skipped'; collectionError = 'boom' } + [pscustomobject]@{ rowType = 'Assignment'; principalId = 'p1'; permissionLevel = 'Full Control'; principalType = 'User'; scope = 'Site'; siteName = 'Sales'; siteUrl = 'https://x/sites/Sales' } + [pscustomobject]@{ rowType = 'Assignment'; principalId = 'p2'; permissionLevel = 'Edit'; principalType = 'User'; isGuest = $true; scope = 'Library'; siteName = 'https://x/sites/Sales'; siteUrl = 'https://x/sites/Sales'; libraryTitle = 'Docs' } + [pscustomobject]@{ rowType = 'Assignment'; principalId = 'p3'; permissionLevel = 'Read'; principalType = 'SharePoint Group'; broadClaim = 'Everyone'; scope = 'Site'; siteName = ''; siteUrl = 'https://x/sites/Sales' } + # Limited Access and placeholder rows grant nothing and stay out of every signal. + [pscustomobject]@{ rowType = 'Assignment'; principalId = 'p4'; permissionLevel = 'Full Control'; principalType = 'User'; isSystemManaged = $true; scope = 'Site' } + [pscustomobject]@{ rowType = 'Assignment'; principalId = $null; scope = 'Library' } + ) + } + function Get-CIPPDbItem { param($TenantFilter, $Type, [switch]$CountsOnly) [pscustomobject]@{ Timestamp = [datetime]'2026-09-01' } } +} + +Describe 'Get-CIPPSharePointPermissionsReport' { + BeforeAll { $script:Report = Get-CIPPSharePointPermissionsReport -TenantFilter 'contoso.onmicrosoft.com' } + + It 'summarises scan coverage and the oversharing signals' { + $s = $script:Report.summary + $s.sitesScanned | Should -Be 2 + $s.sitesSkipped | Should -Be 1 + $s.librariesScanned | Should -Be 2 + $s.uniquePermissionLibraries | Should -Be 1 + $s.totalAssignments | Should -Be 3 + $s.broadClaimGrants | Should -Be 1 + $s.externalGrants | Should -Be 1 + $s.directFullControlGrants | Should -Be 1 + $s.permissionsSynced | Should -BeTrue + $s.lastDataRefresh | Should -Be ([datetime]'2026-09-01') + } + + It 'builds the chart datasets from the real assignments only' { + $r = $script:Report + ($r.byPermissionLevel | Where-Object { $_.level -eq 'Full Control' }).grants | Should -Be 1 + ($r.byPrincipalType | Where-Object { $_.type -eq 'User' }).grants | Should -Be 2 + $r.byBroadClaim[0].claim | Should -Be 'Everyone' + $r.byBroadClaim[0].grants | Should -Be 1 + $r.topSitesByUniqueLibraries[0].site | Should -Be 'Sales' + $r.topSitesByUniqueLibraries[0].libraries | Should -Be 1 + $r.skippedSites[0].error | Should -Be 'boom' + } + + It 'derives the display fields on every assignment row' { + $rows = @($script:Report.assignments) + $rows.Count | Should -Be 5 + ($rows | Where-Object { $_.principalId -eq 'p2' }).appliesTo | Should -Be 'This library only' + ($rows | Where-Object { $_.principalId -eq 'p1' }).appliesTo | Should -Be 'Whole site' + # A URL or blank site name is replaced by a label taken from the URL. + ($rows | Where-Object { $_.principalId -eq 'p2' }).siteName | Should -Be 'Sales' + ($rows | Where-Object { $_.principalId -eq 'p3' }).siteName | Should -Be 'Sales' + } +} diff --git a/backend/Tests/Reports/Get-CIPPSharePointSharingReport.Tests.ps1 b/backend/Tests/Reports/Get-CIPPSharePointSharingReport.Tests.ps1 new file mode 100644 index 0000000000..82f7c33d0c --- /dev/null +++ b/backend/Tests/Reports/Get-CIPPSharePointSharingReport.Tests.ps1 @@ -0,0 +1,80 @@ +# Pester tests for Get-CIPPSharePointSharingReport, the cache rollup behind ListSharePointSharing and +# the sharing PDF: link counts by classification, the sprawl signals, workload summaries and rankings. + +BeforeAll { + $RepoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSCommandPath)) + . (Join-Path $RepoRoot 'Modules/CIPPCore/Public/Get-CIPPSharePointSharingReport.ps1') + + # Static stubs for the cache reads. No pass-through mocks. + function New-CIPPDbRequest { + param($TenantFilter, $Type, $Fields) + switch ($Type) { + 'SharePointSharingLinks' { + @( + [pscustomobject]@{ classification = 'Anonymous'; roles = @('write'); expirationDateTime = $null; itemType = 'Folder'; driveId = 'd1'; itemId = 'i1'; siteName = 'Sales'; driveName = 'Documents'; linkType = 'edit'; hasPassword = $false } + [pscustomobject]@{ classification = 'External'; roles = @('read'); expirationDateTime = '2026-12-31'; itemType = 'File'; driveId = 'd1'; itemId = 'i2'; siteName = 'Sales'; driveName = 'Documents'; linkType = 'view'; sharedWith = @('a@example.com', 'b@example.org', '') } + [pscustomobject]@{ classification = 'Internal'; roles = @('read'); itemType = 'File'; driveId = 'D1'; itemId = 'I1'; siteUrl = 'https://x/sites/Ops'; linkType = 'view'; hasPassword = $true } + ) + } + 'SharePointSiteUsage' { + @( + [pscustomobject]@{ rootWebTemplate = 'Group'; fileCount = '10'; storageUsedInBytes = 1GB } + [pscustomobject]@{ rootWebTemplate = 'SitePage'; fileCount = 5; storageUsedInBytes = 2GB } + ) + } + 'OneDriveUsage' { @([pscustomobject]@{ fileCount = 3; storageUsedInBytes = 512MB }) } + } + } + function Get-CIPPDbItem { + param($TenantFilter, $Type, [switch]$CountsOnly) + if ($Type -eq 'OneDriveUsage') { return $null } + [pscustomobject]@{ Timestamp = [datetime]'2026-09-01' } + } +} + +Describe 'Get-CIPPSharePointSharingReport' { + BeforeAll { $script:Report = Get-CIPPSharePointSharingReport -TenantFilter 'contoso.onmicrosoft.com' } + + It 'counts links by classification and the sprawl signals' { + $s = $script:Report.summary + $s.totalLinks | Should -Be 3 + $s.anonymousLinks | Should -Be 1 + $s.externalLinks | Should -Be 1 + $s.internalLinks | Should -Be 1 + # d1|i1 and D1|I1 are the same item. + $s.itemsShared | Should -Be 2 + $s.anonymousEditLinks | Should -Be 1 + $s.neverExpiringAnonymous | Should -Be 1 + $s.folderShares | Should -Be 1 + $s.passwordProtectedLinks | Should -Be 1 + $s.externalRecipients | Should -Be 2 + } + + It 'summarises each workload from the usage caches' { + $s = $script:Report.summary + $s.teamsSites | Should -Be 1 + $s.teamsFiles | Should -Be 10 + $s.teamsStorageUsedGB | Should -Be 1 + $s.sharePointSites | Should -Be 1 + $s.sharePointFiles | Should -Be 5 + $s.sharePointStorageUsedGB | Should -Be 2 + $s.oneDriveAccounts | Should -Be 1 + $s.oneDriveFiles | Should -Be 3 + $s.oneDriveStorageUsedGB | Should -Be 0.5 + $s.linksSynced | Should -BeTrue + $s.usageSynced | Should -BeTrue + $s.lastDataRefresh | Should -Be ([datetime]'2026-09-01') + } + + It 'ranks the busiest libraries, sites and recipients' { + $r = $script:Report + $r.topLibraries[0].library | Should -Be 'Sales / Documents' + $r.topLibraries[0].links | Should -Be 2 + $r.topSites[0].site | Should -Be 'Sales' + $r.topSites[0].links | Should -Be 2 + @($r.topRecipients).Count | Should -Be 2 + ($r.byScope | Where-Object { $_.scope -eq 'Anonymous' }).links | Should -Be 1 + ($r.byLinkType | Where-Object { $_.type -eq 'view' }).links | Should -Be 2 + @($r.links).Count | Should -Be 3 + } +} diff --git a/backend/Tests/Reports/Get-CippReportTenantName.Tests.ps1 b/backend/Tests/Reports/Get-CippReportTenantName.Tests.ps1 new file mode 100644 index 0000000000..634768b72a --- /dev/null +++ b/backend/Tests/Reports/Get-CippReportTenantName.Tests.ps1 @@ -0,0 +1,47 @@ +# Pester tests for the branding's tenantLabel: which of the tenant's names a report prints. + +BeforeAll { + $RepoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSCommandPath)) + . (Get-ChildItem -Path (Join-Path $RepoRoot 'Modules') -Recurse -Filter 'Get-CippReportTenantName.ps1' | Select-Object -First 1 -ExpandProperty FullName) + + $script:Tenant = [PSCustomObject]@{ displayName = 'Contoso (alias)'; defaultDomainName = 'contoso.onmicrosoft.com'; customerId = 'guid' } + $script:Settings = @{ tenantLabel = 'alias' } + $script:Presets = @{} + $script:GraphCalls = 0 + function Get-Tenants { param($TenantFilter) $script:Tenant } + function Get-CIPPBrandingSettings { $script:Settings } + function Get-CIPPBrandingPreset { param($Id, [switch]$SkipImageData) $script:Presets[$Id] } + function New-GraphGetRequest { param($uri, $tenantid, $AsApp) $script:GraphCalls++; [PSCustomObject]@{ displayName = 'Contoso Ltd' } } +} + +Describe 'Get-CippReportTenantName' { + BeforeEach { $script:GraphCalls = 0 } + + It 'prints the name CIPP shows by default, without touching Graph' { + Get-CippReportTenantName -TenantFilter 'contoso.onmicrosoft.com' | Should -Be 'Contoso (alias)' + $script:GraphCalls | Should -Be 0 + } + + It 'prints the default domain when the branding asks for it' { + Get-CippReportTenantName -TenantFilter 'contoso.onmicrosoft.com' -Branding @{ tenantLabel = 'domain' } | Should -Be 'contoso.onmicrosoft.com' + } + + It 'prints the organisation name from Graph when the branding asks for it, since the cache holds the alias' { + Get-CippReportTenantName -TenantFilter 'contoso.onmicrosoft.com' -Branding @{ tenantLabel = 'name' } | Should -Be 'Contoso Ltd' + $script:GraphCalls | Should -Be 1 + } + + It 'lets a preset decide over the global setting' { + $script:Presets['p1'] = @{ tenantLabel = 'domain' } + Get-CippReportTenantName -TenantFilter 'contoso.onmicrosoft.com' -BrandingPresetId 'p1' | Should -Be 'contoso.onmicrosoft.com' + Get-CippReportTenantName -TenantFilter 'contoso.onmicrosoft.com' -BrandingPresetId 'missing' | Should -Be 'Contoso (alias)' + } + + It 'falls back to the shown name when Graph cannot answer, and to the filter when nothing is cached' { + function New-GraphGetRequest { throw 'no access' } + Get-CippReportTenantName -TenantFilter 'contoso.onmicrosoft.com' -Branding @{ tenantLabel = 'name' } | Should -Be 'Contoso (alias)' + function Get-Tenants { param($TenantFilter) $null } + Get-CippReportTenantName -TenantFilter 'contoso.onmicrosoft.com' -Branding @{ tenantLabel = 'domain' } | Should -Be 'contoso.onmicrosoft.com' + Get-CippReportTenantName -TenantFilter 'contoso.onmicrosoft.com' -Branding @{ tenantLabel = 'alias' } | Should -Be 'contoso.onmicrosoft.com' + } +} diff --git a/backend/Tests/Reports/Invoke-ExecPreviewBrandingReportPdf.Tests.ps1 b/backend/Tests/Reports/Invoke-ExecPreviewBrandingReportPdf.Tests.ps1 new file mode 100644 index 0000000000..2948be07fb --- /dev/null +++ b/backend/Tests/Reports/Invoke-ExecPreviewBrandingReportPdf.Tests.ps1 @@ -0,0 +1,56 @@ +# Pester tests for the branding preview endpoint: every sample report type renders through its tree +# builder against a supplied branding, and an unknown type is a 400. This is also what keeps +# Config/ReportSamples/.json in step with the builders' input shapes. + +BeforeAll { + $RepoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSCommandPath)) + $Bin = Join-Path $RepoRoot 'Shared/CIPPSharp/bin' + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $Bin 'OfficeIMO.Core.dll')) + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $Bin 'OfficeIMO.Pdf.dll')) + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $Bin 'CIPPSharp.dll')) + $env:CIPPRootPath = $RepoRoot + + # Craft injects these types; shim them so the entrypoint's return statements bind. + if (-not ('HttpResponseContext' -as [type])) { + Add-Type -TypeDefinition 'public class HttpResponseContext { public object StatusCode; public object Body; public string ContentType; public object Headers; }' + } + $null = [PowerShell].Assembly.GetType('System.Management.Automation.TypeAccelerators')::Add('HttpStatusCode', [System.Net.HttpStatusCode]) + + $Reporting = Get-ChildItem -Path (Join-Path $RepoRoot 'Modules') -Recurse -Directory -Filter 'Reporting' | Select-Object -First 1 + Get-ChildItem -Path $Reporting.FullName -Filter '*.ps1' | ForEach-Object { . $_.FullName } + foreach ($Name in 'ConvertTo-CippReportPdf.ps1', 'Invoke-ExecPreviewBrandingReportPdf.ps1') { + . (Get-ChildItem -Path (Join-Path $RepoRoot 'Modules') -Recurse -Filter $Name | Select-Object -First 1 -ExpandProperty FullName) + } + function Get-CIPPBrandingSettings { @{ colour = '#F77F00' } } + function Get-CIPPBrandingPreset { param($Id, [switch]$SkipImageData) @() } + function Write-LogMessage { param([Parameter(ValueFromRemainingArguments = $true)]$Rest) } + function Get-CippException { param($Exception) @{ NormalizedError = "$($Exception)" } } + + function Invoke-Preview($Body) { + Invoke-ExecPreviewBrandingReportPdf -Request @{ Body = $Body; Headers = @{} } -TriggerMetadata @{ FunctionName = 'ExecPreviewBrandingReportPdf' } + } +} + +Describe 'Invoke-ExecPreviewBrandingReportPdf' { + It 'renders the sample against the supplied branding' -ForEach @( + @{ type = 'executive' }, @{ type = 'reportBuilder' }, @{ type = 'shadowAI' }, @{ type = 'bec' } + @{ type = 'sharing' }, @{ type = 'permissions' }, @{ type = 'mailFlow' } + ) { + $Response = Invoke-Preview @{ reportType = $type; branding = @{ colour = '#0E4C92'; coverStock = 'none'; footerText = '%tenantname% preview' } } + $Response.StatusCode | Should -Be ([System.Net.HttpStatusCode]::OK) + $Response.ContentType | Should -Be 'application/pdf' + [System.Text.Encoding]::ASCII.GetString($Response.Body[0..4]) | Should -Be '%PDF-' + } + + It 'falls back to the executive sample and the saved branding when the body is empty' { + $Response = Invoke-Preview @{} + $Response.StatusCode | Should -Be ([System.Net.HttpStatusCode]::OK) + [System.Text.Encoding]::ASCII.GetString($Response.Body[0..4]) | Should -Be '%PDF-' + } + + It 'rejects an unknown report type' { + $Response = Invoke-Preview @{ reportType = 'nope' } + $Response.StatusCode | Should -Be ([System.Net.HttpStatusCode]::BadRequest) + $Response.Body | Should -BeLike "Unknown report type 'nope'*" + } +} diff --git a/backend/Tests/Reports/Push-ExecGenerateReportBuilderReport.Pdf.Tests.ps1 b/backend/Tests/Reports/Push-ExecGenerateReportBuilderReport.Pdf.Tests.ps1 new file mode 100644 index 0000000000..af4e983df4 --- /dev/null +++ b/backend/Tests/Reports/Push-ExecGenerateReportBuilderReport.Pdf.Tests.ps1 @@ -0,0 +1,66 @@ +# Pester tests for the PDF path of Push-ExecGenerateReportBuilderReport: a rendered PDF is stored in +# its own table (never on the report row the list reads), attached to the scheduled-email envelope, +# and -PreviewOnly returns bytes without storing anything. + +BeforeAll { + $RepoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSCommandPath)) + $Bin = Join-Path $RepoRoot 'Shared/CIPPSharp/bin' + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $Bin 'OfficeIMO.Core.dll')) + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $Bin 'OfficeIMO.Pdf.dll')) + [void][System.Reflection.Assembly]::LoadFrom((Join-Path $Bin 'CIPPSharp.dll')) + + function Find-Module1([string]$Name) { + Get-ChildItem -Path (Join-Path $RepoRoot 'Modules') -Recurse -Filter $Name -File -ErrorAction SilentlyContinue | + Select-Object -First 1 -ExpandProperty FullName + } + . (Find-Module1 'ConvertTo-CippReportPdf.ps1') + . (Find-Module1 'Get-CippReportTenantName.ps1') + . (Find-Module1 'Resolve-CippReportDataToken.ps1') + . (Find-Module1 'Push-ExecGenerateReportBuilderReport.ps1') + + # Static stubs for the storage/data helpers the generator calls. The Add stub captures the stored + # entity per table so the test can assert on where the PDF went. No pass-through mocks. + $script:Stored = @{} + function Get-CippTable { param($tablename) @{ Context = $tablename } } + function Get-CIPPAzDataTableEntity { param($Context, $Filter, $Property) @() } + function Add-CIPPAzDataTableEntity { param($Context, $Entity, [switch]$Force) $script:Stored[$Context] = $Entity } + function Get-CIPPBrandingSettings { @{ colour = '#F77F00' } } + function Get-CIPPBrandingPreset { param($Id, [switch]$SkipImageData) @() } + function Get-Tenants { param($TenantFilter) @{ displayName = 'Contoso' } } + function New-CIPPDbRequest { param($TenantFilter, $Type, $Fields) @() } + function Get-CIPPTextReplacement { param($TenantFilter, $Text, [switch]$EscapeForJson) $Text } + function Write-LogMessage { param([Parameter(ValueFromRemainingArguments = $true)]$Rest) } + function Get-CippException { param($Exception) @{ NormalizedError = "$($Exception)" } } + + $script:Blocks = ConvertTo-Json -InputObject @(@{ type = 'blank'; title = 'Summary'; content = '

    Hello

    ' }) -Depth 10 +} + +Describe 'Push-ExecGenerateReportBuilderReport PDF path' { + It 'stores the base64 PDF in the ReportBuilderPdfs table, keyed by the report GUID, not on the report row' { + $script:Stored = @{} + $null = Push-ExecGenerateReportBuilderReport -TenantFilter 'contoso.onmicrosoft.com' -TemplateName 'Test Report' -Blocks $script:Blocks + $Report = $script:Stored['ReportBuilderReports'] + $Pdf = $script:Stored['ReportBuilderPdfs'] + $Report | Should -Not -BeNullOrEmpty + $Report.Pdf | Should -BeNullOrEmpty + $Pdf.RowKey | Should -Be $Report.RowKey + $Pdf.PartitionKey | Should -Be 'contoso.onmicrosoft.com' + $Pdf.FileName | Should -Be 'Test_Report_contoso_onmicrosoft_com.pdf' + # The stored value must be valid base64 of a real PDF. + $bytes = [Convert]::FromBase64String($Pdf.Pdf) + [System.Text.Encoding]::ASCII.GetString($bytes[0..4]) | Should -Be '%PDF-' + } + + It 'returns the rendered PDF as an application/pdf task attachment' { + $result = Push-ExecGenerateReportBuilderReport -TenantFilter 'contoso.onmicrosoft.com' -TemplateName 'Test Report' -Blocks $script:Blocks + $result.TaskAttachments | Should -Not -BeNullOrEmpty + @($result.TaskAttachments | Where-Object { $_.ContentType -eq 'application/pdf' }).Count | Should -Be 1 + } + + It 'PreviewOnly returns bytes without persisting anything' { + $script:Stored = @{} + $result = Push-ExecGenerateReportBuilderReport -TenantFilter 'contoso.onmicrosoft.com' -Blocks $script:Blocks -PreviewOnly + [System.Text.Encoding]::ASCII.GetString($result.PdfBytes[0..4]) | Should -Be '%PDF-' + $script:Stored.Count | Should -Be 0 + } +} diff --git a/backend/Tests/Reports/Resolve-CippReportDataToken.Tests.ps1 b/backend/Tests/Reports/Resolve-CippReportDataToken.Tests.ps1 new file mode 100644 index 0000000000..3eff8c29b4 --- /dev/null +++ b/backend/Tests/Reports/Resolve-CippReportDataToken.Tests.ps1 @@ -0,0 +1,161 @@ +# Pester tests for the report builder's data tokens: what &Users&, &Users.displayName&, +# &Devices.complianceState=compliant& and the chart/table sources resolve to, from a stubbed +# reporting database. + +BeforeAll { + $RepoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSCommandPath)) + . (Get-ChildItem -Path (Join-Path $RepoRoot 'Modules') -Recurse -Filter 'Resolve-CippReportDataToken.ps1' | Select-Object -First 1 -ExpandProperty FullName) + + $script:Db = @{ + Users = @( + [PSCustomObject]@{ displayName = 'Adele Vance'; accountEnabled = $true; assignedLicenses = @(@{ skuId = 'e3' }, @{ skuId = 'p1' }) } + [PSCustomObject]@{ displayName = 'Alex Wilber'; accountEnabled = $true; assignedLicenses = @(@{ skuId = 'e3' }) } + [PSCustomObject]@{ displayName = 'Guest User'; accountEnabled = $false; assignedLicenses = @() } + ) + SecureScore = @( + [PSCustomObject]@{ createdDateTime = '2026-08-24T00:00:00Z'; currentScore = 213.71; maxScore = 273 } + [PSCustomObject]@{ createdDateTime = '2026-08-22T00:00:00Z'; currentScore = 210.5; maxScore = 273 } + [PSCustomObject]@{ createdDateTime = '2026-08-23T00:00:00Z'; currentScore = 213.69; maxScore = 273 } + ) + Mailboxes = @( + [PSCustomObject]@{ UPN = 'a@x'; department = 'Sales'; TotalItemSize = 10 } + [PSCustomObject]@{ UPN = 'b@x'; department = 'sales'; TotalItemSize = 5 } + [PSCustomObject]@{ UPN = 'c@x'; department = 'IT'; TotalItemSize = 40 } + ) + Devices = @( + [PSCustomObject]@{ deviceName = 'PC1'; operatingSystem = 'Windows'; complianceState = 'compliant'; storageTotal = 512 } + [PSCustomObject]@{ deviceName = 'PC2'; operatingSystem = 'Windows'; complianceState = 'noncompliant'; storageTotal = 256 } + [PSCustomObject]@{ deviceName = 'MAC1'; operatingSystem = 'macOS'; complianceState = 'compliant'; storageTotal = 1024 } + [PSCustomObject]@{ deviceName = 'PHONE'; operatingSystem = $null; complianceState = 'Compliant'; storageTotal = 64 } + ) + } + $script:Reads = 0 + function New-CIPPDbRequest { + param($TenantFilter, $Type, $Fields) + $script:Reads++ + if ($script:Db.ContainsKey($Type)) { $script:Db[$Type] } else { throw "No cache for $Type" } + } + function Resolve-Blocks($Blocks) { + $Out = @(Resolve-CippReportDataToken -Blocks $Blocks -TenantFilter 'contoso.onmicrosoft.com') + # one element per block, never a wrapped array: that is what blanked every preview once + $Out.Count | Should -Be @($Blocks).Count + foreach ($b in $Out) { $b -is [array] | Should -BeFalse } + # kept an array for the tests' indexing; a lone block would otherwise unroll to itself + , $Out + } +} + +Describe 'Resolve-CippReportDataToken' { + BeforeEach { $script:Reads = 0 } + + It 'counts a collection, lists a field and counts the rows that match' { + $Blocks = Resolve-Blocks @(@{ type = 'blank'; content = '

    &Users& users: &Users.displayName&. Enabled: &Users.accountEnabled=true&, disabled: &Users.accountEnabled!=true&.

    ' }) + $Blocks[0].content | Should -Be '

    3 users: Adele Vance, Alex Wilber, Guest User. Enabled: 2, disabled: 1.

    ' + } + + It 'reaches into nested objects and arrays, and wildcards a filter' { + $Blocks = Resolve-Blocks @(@{ type = 'note'; content = 'Licences: &Users.assignedLicenses.skuId&; E3 holders: &Users.assignedLicenses.skuId=e3&; Windows-ish: &Devices.operatingSystem=Win*&' }) + $Blocks[0].content | Should -Be 'Licences: e3, p1; E3 holders: 2; Windows-ish: 2' + } + + It 'aggregates a numeric field' { + $Blocks = Resolve-Blocks @(@{ type = 'scorecard'; stats = @(@{ label = 'Storage'; value = '&Devices.storageTotal:sum& GB' }, @{ label = 'Average'; value = '&Devices.storageTotal:avg&' }, @{ label = 'Largest'; value = '&Devices.storageTotal:max&' }) }) + $Blocks[0].stats[0].value | Should -Be '1856 GB' + $Blocks[0].stats[1].value | Should -Be 464 + $Blocks[0].stats[2].value | Should -Be 1024 + } + + It 'turns a lone numeric token into a number, so bars and points can use one' { + $Blocks = Resolve-Blocks @(@{ type = 'progress'; items = @(@{ label = 'Compliant'; value = '&Devices.complianceState=compliant&'; max = '&Devices&' }) }) + $Blocks[0].items[0].value | Should -Be 3 + $Blocks[0].items[0].value | Should -BeOfType [double] + $Blocks[0].items[0].max | Should -Be 4 + } + + It 'leaves a token that names nothing as written, and reads each collection once' { + $Blocks = Resolve-Blocks @(@{ type = 'blank'; content = '&Nope& and &Nope.x& stay; &Users& and &Users& resolve' }, @{ type = 'note'; content = '&Users.displayName=Adele Vance&' }) + $Blocks[0].content | Should -Be '&Nope& and &Nope.x& stay; 3 and 3 resolve' + $Blocks[1].content | Should -Be '1' + $script:Reads | Should -Be 2 + } + + It 'fills a chart with one slice per value of the field, blanks counted apart' { + $Blocks = Resolve-Blocks @(@{ type = 'chart'; title = 'OS'; chartKind = 'donut'; chartSource = '&Devices.operatingSystem&'; chartData = @(@{ label = 'placeholder'; value = 0 }) }) + $Points = @($Blocks[0].chartData) + $Points.Count | Should -Be 3 + $Points[0].label | Should -Be 'Windows' + $Points[0].value | Should -Be 2 + $Points[1].label | Should -Be 'macOS' + $Points[2].label | Should -Be '(blank)' + $Blocks[0].chartSource | Should -Be '&Devices.operatingSystem&' + } + + It 'fills a chart with a single counted slice when the source has no field to group by' { + $Blocks = Resolve-Blocks @(@{ type = 'chart'; title = 'Compliant devices'; chartSource = '&Devices.complianceState=compliant&'; chartData = @() }) + @($Blocks[0].chartData).Count | Should -Be 1 + $Blocks[0].chartData[0].label | Should -Be 'Compliant devices' + $Blocks[0].chartData[0].value | Should -Be 3 + } + + It 'fills a table from the rows a filter keeps, each column reading its field or header' { + $Blocks = Resolve-Blocks @(@{ + type = 'richtable'; dataSource = '&Devices.complianceState=compliant&' + columns = @(@{ header = 'Device'; key = 'c1'; field = 'deviceName' }, @{ header = 'operatingSystem'; key = 'c2' }) + rows = @(@{ c1 = 'typed'; c2 = 'rows' }) + }) + $Rows = @($Blocks[0].rows) + $Rows.Count | Should -Be 3 + $Rows[0].c1 | Should -Be 'PC1' + $Rows[0].c2 | Should -Be 'Windows' + $Rows[2].c2 | Should -Be '' + $Blocks[0].limit | Should -Be 200 + } + + It 'fills a chart from a picked source, grouping the rows the condition keeps' { + $Blocks = Resolve-Blocks @(@{ type = 'chart'; title = 'Compliant by OS'; chartSource = @{ type = 'Devices'; field = 'operatingSystem'; filter = @{ field = 'complianceState'; op = '='; value = 'compliant' } }; chartData = @() }) + $Points = @($Blocks[0].chartData) + ($Points | ForEach-Object { "$($_.label)=$($_.value)" }) -join ';' | Should -Be 'macOS=1;Windows=1;(blank)=1' + } + + It 'plots a value per row in date order, as a Secure Score trend needs' { + $Blocks = Resolve-Blocks @(@{ type = 'chart'; chartKind = 'trend'; chartSource = @{ type = 'SecureScore'; field = 'createdDateTime'; valueField = 'currentScore'; filter = $null }; chartData = @() }) + ($Blocks[0].chartData | ForEach-Object { "$($_.label)=$($_.value)" }) -join ';' | Should -Be 'Aug 22=210.5;Aug 23=213.69;Aug 24=213.71' + } + + It 'plots a value per row in row order when the labels are not dates, and treats Count of rows as counting' { + $Blocks = Resolve-Blocks @( + @{ type = 'chart'; chartKind = 'bar'; chartSource = @{ type = 'Mailboxes'; field = 'UPN'; valueField = 'TotalItemSize'; filter = $null }; chartData = @() } + @{ type = 'chart'; chartKind = 'bar'; chartSource = @{ type = 'Mailboxes'; field = 'department'; valueField = '__count'; filter = $null }; chartData = @() } + ) + ($Blocks[0].chartData | ForEach-Object { "$($_.label)=$($_.value)" }) -join ';' | Should -Be 'a@x=10;b@x=5;c@x=40' + ($Blocks[1].chartData | ForEach-Object { "$($_.label)=$($_.value)" }) -join ';' | Should -Be 'Sales=2;IT=1' + } + + It 'combines the rows sharing a label with the chosen aggregate' { + $Blocks = Resolve-Blocks @(@{ type = 'chart'; chartKind = 'bar'; chartSource = @{ type = 'Mailboxes'; field = 'department'; valueField = 'TotalItemSize'; aggregate = 'sum'; filter = $null }; chartData = @() }) + ($Blocks[0].chartData | ForEach-Object { "$($_.label)=$($_.value)" }) -join ';' | Should -Be 'IT=40;Sales=15' + } + + It 'counts a picked source with no field as one slice' { + $Blocks = Resolve-Blocks @(@{ type = 'chart'; title = 'Devices'; chartSource = @{ type = 'Devices'; field = $null; filter = $null }; chartData = @() }) + $Blocks[0].chartData[0].value | Should -Be 4 + } + + It 'fills a table from a picked source' { + $Blocks = Resolve-Blocks @(@{ type = 'richtable'; dataSource = @{ type = 'Users'; filter = @{ field = 'accountEnabled'; op = '!='; value = 'true' } }; columns = @(@{ header = 'Name'; key = 'c1'; field = 'displayName' }); rows = @() }) + @($Blocks[0].rows).Count | Should -Be 1 + $Blocks[0].rows[0].c1 | Should -Be 'Guest User' + } + + It 'keeps typed rows when the table source names nothing' { + $Blocks = Resolve-Blocks @(@{ type = 'richtable'; dataSource = '&Nothing&'; columns = @(@{ header = 'A'; key = 'c1' }); rows = @(@{ c1 = 'typed' }) }) + $Blocks[0].rows[0].c1 | Should -Be 'typed' + } + + It 'walks objects parsed from JSON the way the generator hands them over' { + $Json = '[{"type":"scorecard","stats":[{"label":"Users","value":"&Users&"}]},{"type":"page","title":"&Devices& devices","subtitle":"x"}]' + $Blocks = Resolve-Blocks @(ConvertFrom-Json -InputObject $Json) + $Blocks[0].stats[0].value | Should -Be 3 + $Blocks[1].title | Should -Be '4 devices' + } +} diff --git a/build/Dockerfile b/build/Dockerfile index a48fad503c..ce97382dc4 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -196,9 +196,16 @@ COPY backend/Modules/PassPushPosh /app/API/Modules/PassPushPosh/ # at startup (non-root distroless) and CIPP modules read it back from the same path. COPY --chown=app:app backend/Config /app/API/Config/ COPY backend/Shared /app/API/Shared/ -# Overwrite the committed CIPPSharp.dll with a freshly built one targeting the -# runtime framework. Must run AFTER the Shared/ copy so it isn't clobbered. -COPY --from=build-cippsharp /out/CIPPSharp.dll /app/API/Shared/CIPPSharp/bin/CIPPSharp.dll +# Overwrite the committed CIPPSharp.dll (and its OfficeIMO PDF dependencies) with freshly built ones +# targeting the runtime framework. Must run AFTER the Shared/ copy so they aren't clobbered. The +# OfficeIMO DLLs must sit beside CIPPSharp.dll - CIPPSharp is loaded via RequiredAssemblies and .NET +# resolves its dependencies from the same directory (there is no deps.json). +COPY --from=build-cippsharp /out/CIPPSharp.dll /app/API/Shared/CIPPSharp/bin/CIPPSharp.dll +COPY --from=build-cippsharp /out/OfficeIMO.Pdf.dll /app/API/Shared/CIPPSharp/bin/OfficeIMO.Pdf.dll +COPY --from=build-cippsharp /out/OfficeIMO.Core.dll /app/API/Shared/CIPPSharp/bin/OfficeIMO.Core.dll +# emoji-fallback.ttf (monochrome fallback font) and the twemoji/ colour-emoji PNG set the renderer places +# inline are committed assets, not build outputs, so they ship via the `COPY backend/Shared` above and need +# no rebuild here. COPY backend/AddChocoApp /app/API/AddChocoApp/ COPY backend/AddMSPApp /app/API/AddMSPApp/ COPY backend/ExecMaintenanceScripts /app/API/ExecMaintenanceScripts/ diff --git a/frontend/src/components/BECRemediationReportButton.jsx b/frontend/src/components/BECRemediationReportButton.jsx index 44a501aaf1..267960f555 100644 --- a/frontend/src/components/BECRemediationReportButton.jsx +++ b/frontend/src/components/BECRemediationReportButton.jsx @@ -8,2310 +8,34 @@ import { DialogContent, DialogActions, Box, - Stack, - ToggleButton, - ToggleButtonGroup, Typography, IconButton, - CircularProgress, } from '@mui/material' -import { PDFDownloadLink } from '@react-pdf/renderer' -import { CippPdfPreview } from './CippPdf/CippPdfPreview' -import { useReportVariables } from './CippPdf/useReportVariables' -import { useBrandingSettings } from './CippPdf/useBrandingSettings' -import { - AlertBox, - Bold, - Bullet, - BulletList, - ClearBox, - ContentPage, - CoverMeta, - DataTable, - InfoBox, - Note, - Paragraph, - ProgressList, - ReportDocument, - Section, - StatRow, -} from './CippPdf' -import { - BEC_GROUPS, - becGroupFlagged, - becWindowStart, -} from '../utils/bec-objectives' -import { - BEC_OBJECTIVE_COLOR, - BEC_OBJECTIVE_LABEL, - buildBecTimeline, -} from '../utils/bec-timeline' - -// BEC Remediation PDF Document Component -// Exported so the branding preview can render this report against sample data, and so tests can -// render it to a real PDF. -export const BECRemediationReportDocument = ({ - userData, - becData, - brandingSettings, - tenantName, - variables, - // 'full' (default) = every page; 'summary' = the executive pages only, for a C-suite reader. - variant = 'full', -}) => { - const isSummary = variant === 'summary' - const currentDate = new Date().toLocaleDateString('en-US', { - year: 'numeric', - month: 'long', - day: 'numeric', - }) - - // Helper function to format dates - const formatDate = (dateString) => { - if (!dateString) return 'N/A' - try { - return new Date(dateString).toLocaleString('en-US', { - year: 'numeric', - month: 'short', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - }) - } catch { - return dateString - } - } - - const formatSafelistValue = (value) => { - if (!value) return 'unchanged' - return Array.isArray(value) - ? value.join(', ') || 'unchanged' - : String(value) - } - - // Calculate statistics - const stats = { - newRules: becData?.NewRules?.length || 0, - ruleChanges: becData?.InboxRuleChanges?.length || 0, - newUsers: becData?.NewUsers?.length || 0, - newApps: becData?.AddedApps?.length || 0, - permissionChanges: becData?.MailboxPermissionChanges?.length || 0, - permissionChangesTargetingUser: ( - becData?.MailboxPermissionChanges || [] - ).filter((change) => change?.TargetsSuspect === true).length, - mfaDevices: becData?.MFADevices?.length || 0, - passwordChanges: becData?.ChangedPasswords?.length || 0, - sentMessages: becData?.SentMessages?.length || 0, - trustedSenders: becData?.TrustedSenders?.length || 0, - blockedSenders: becData?.BlockedSenders?.length || 0, - safelistChanges: becData?.SafelistChanges?.length || 0, - sharingChanges: becData?.SharingChanges?.length || 0, - anonymousLinks: (becData?.SharingChanges || []).filter((c) => - c?.Operation?.startsWith('AnonymousLink') - ).length, - intuneDevices: becData?.IntuneDevices?.length || 0, - signIns: becData?.SuspectUserSignIns?.length || 0, - sentTotalMessages: becData?.SentMessageAnalysis?.TotalMessages ?? 0, - sentTotalRecipients: becData?.SentMessageAnalysis?.TotalRecipients ?? 0, - repeatedSubjects: becData?.SentMessageAnalysis?.FlaggedSubjectCount || 0, - sendBursts: becData?.SentMessageAnalysis?.Bursts?.length || 0, - massMailFlagged: becData?.SentMessageAnalysis?.Flagged === true, - maliciousApps: - (becData?.AddedApps || []).filter((app) => app?.MaliciousMatch).length + - (becData?.MaliciousSPs?.length || 0), - } - - const locationAnalysis = becData?.LocationAnalysis - stats.foreignSignIns = locationAnalysis?.ForeignSignInCount || 0 - stats.foreignSuccessfulSignIns = - locationAnalysis?.ForeignSuccessfulSignInCount || 0 - stats.foreignSentMessages = locationAnalysis?.ForeignSentMessageCount || 0 - stats.foreignActivity = - (locationAnalysis?.ForeignRuleChangeCount || 0) + - (locationAnalysis?.ForeignSafelistChangeCount || 0) + - (locationAnalysis?.ForeignSharingChangeCount || 0) + - (locationAnalysis?.ForeignSentMessageCount || 0) - - const analysisWindowStart = becWindowStart( - becData, - becData?.AnalysisWindowDays || 7 - ) - - const recentIntuneDevices = (becData?.IntuneDevices || []).filter( - (device) => { - if (!device?.enrolledDateTime) return false - const enrolled = new Date(device.enrolledDateTime) - if (Number.isNaN(enrolled.getTime())) return false - return enrolled >= analysisWindowStart - } - ) - stats.recentIntuneDevices = recentIntuneDevices.length - - const isRecentMfaDevice = (method) => { - if (!method?.createdDateTime) return false - const created = new Date(method.createdDateTime) - if (Number.isNaN(created.getTime())) return false - return created >= analysisWindowStart - } - stats.recentMfaDevices = (becData?.MFADevices || []).filter( - isRecentMfaDevice - ).length - - // successful foreign sign-ins first - they prove access, failed ones are mostly spray noise - const foreignSignIns = (becData?.SuspectUserSignIns || []) - .filter((signIn) => signIn?.ForeignLocation === true) - .sort((a, b) => (b?.Status === 'Success') - (a?.Status === 'Success')) - - const sortedIntuneDevices = [...(becData?.IntuneDevices || [])].sort( - (a, b) => { - const aTime = a?.enrolledDateTime - ? new Date(a.enrolledDateTime).getTime() - : 0 - const bTime = b?.enrolledDateTime - ? new Date(b.enrolledDateTime).getTime() - : 0 - return bTime - aTime - } - ) - - // The threat level is computed server-side (Get-CIPPBecScore) and stored on the run. - const threatLevel = { - level: becData?.Score?.Level || 'Low', - value: becData?.Score?.Value ?? 0, - color: - becData?.Score?.Level === 'High' - ? '#742A2A' - : becData?.Score?.Level === 'Medium' - ? '#744210' - : '#22543D', - } - const appliedSignals = (becData?.Score?.Breakdown || []).filter( - (signal) => signal.Applied - ) - const completenessEntries = Object.entries(becData?.Completeness || {}) - // A check that could not run for lack of a licence/permission/mailbox is "skipped" (not applicable), - // reported apart from a check that failed or was capped - a skipped check is never a clean pass. - const skippedCollectors = completenessEntries.filter( - ([, marker]) => marker && marker.Skipped - ) - const incompleteCollectors = completenessEntries.filter( - ([, marker]) => marker && marker.Complete === false && !marker.Skipped - ) - const windowDays = becData?.AnalysisWindowDays || 7 - const flaggedDelegations = (becData?.Delegations || []).filter( - (d) => d.Flagged - ) - const flaggedGrants = (becData?.UserGrants || []).filter((g) => g.Flagged) - const flaggedTransportChanges = (becData?.TransportRuleChanges || []).filter( - (c) => c.Flagged - ) - const flaggedTransportRules = becData?.TransportRulesFlagged || [] - const flaggedAddIns = (becData?.MailboxAddIns || []).filter((a) => a.Flagged) - const receivedFindings = becData?.ReceivedMailFindings || [] - const deliveredThreats = (becData?.DefenderDetections || []).filter( - (d) => d.Delivered - ) - const flaggedAudits = (becData?.DirectoryAudits || []).filter( - (a) => a.Flagged - ) - const recentRegisteredDevices = (becData?.RegisteredDevices || []).filter( - (d) => d.RegisteredInWindow - ) - const foreignNonInteractive = (becData?.NonInteractiveSignIns || []).filter( - (s) => s.ForeignLocation === true && s.Status === 'Success' - ) - const mailActivitySummary = becData?.MailActivitySummary - const riskState = becData?.RiskState - - // ============================================================================================ - // Executive intelligence. A results roll-up, a findings-by-objective breakdown, evidence-driven - // priority actions and a chronological timeline — all derived from the same becData the detailed - // check pages render. These lead the report so a reader who stops after the first pages still has - // the verdict, the shape of what was found, and what to do about it, before any deep detail. - // ============================================================================================ - const forwardingAddress = - becData?.MailboxState?.ForwardingSmtpAddress || - becData?.MailboxState?.ForwardingAddress || - null - const hasForwarding = !!( - becData?.MailboxState?.HasForwarding || forwardingAddress - ) - - // Every check as one row — flagged (with a high-risk sub-count) or clear — so the summary page - // carries the whole result set at a glance, not only the four headline stats. - const summaryData = [ - { - area: 'Inbox rules & changes', - count: stats.newRules + stats.ruleChanges, - }, - { area: 'Mailbox delegations', count: flaggedDelegations.length }, - { - area: 'Application consents', - count: flaggedGrants.length, - danger: flaggedGrants.length, - }, - { - area: 'New / rogue applications', - count: stats.newApps, - danger: stats.maliciousApps, - }, - { area: 'Mailbox permission changes', count: stats.permissionChanges }, - { - area: 'Transport rules', - count: flaggedTransportRules.length + flaggedTransportChanges.length, - }, - { area: 'Mailbox add-ins', count: flaggedAddIns.length }, - { area: 'Forwarding & auto-reply', count: hasForwarding ? 1 : 0 }, - { area: 'Trusted / blocked sender changes', count: stats.safelistChanges }, - { area: 'Sent mail / mass-mail', count: stats.massMailFlagged ? 1 : 0 }, - { - area: 'Received phishing & threats', - count: receivedFindings.length + deliveredThreats.length, - danger: deliveredThreats.length, - }, - { - area: 'Sharing links', - count: stats.sharingChanges, - danger: stats.anonymousLinks, - }, - { area: 'MFA methods (new in window)', count: stats.recentMfaDevices }, - { - area: 'Registered devices (new in window)', - count: recentRegisteredDevices.length, - }, - { - area: 'Intune devices (new in window)', - count: stats.recentIntuneDevices, - }, - { - area: 'Foreign successful sign-ins', - count: stats.foreignSuccessfulSignIns, - danger: stats.foreignSuccessfulSignIns, - }, - { area: 'Directory audit events', count: flaggedAudits.length }, - { area: 'Identity Protection risk', count: riskState?.Listed ? 1 : 0 }, - ].map((row) => { - const danger = row.danger || 0 - const flagged = row.count || 0 - return { - area: row.area, - result: - danger > 0 - ? `${flagged} flagged · ${danger} high-risk` - : flagged > 0 - ? `${flagged} flagged` - : 'Clear', - resultColour: - danger > 0 ? '#C53030' : flagged > 0 ? '#B7791F' : '#2F855A', - } - }) - const flaggedAreaCount = summaryData.filter( - (row) => row.result !== 'Clear' - ).length - - // The findings grouped by the attacker objective they serve — the same five-objective lens and - // the same flag predicates the case workspace uses, so the report and the page agree. - const objectiveFlagged = becGroupFlagged(becData, windowDays) - const objectiveBreakdown = BEC_GROUPS.map((group) => ({ - label: BEC_OBJECTIVE_LABEL[group.id], - value: objectiveFlagged[group.id] || 0, - colour: BEC_OBJECTIVE_COLOR[group.id], - })) - const objectiveMax = Math.max( - ...objectiveBreakdown.map((entry) => entry.value), - 1 - ) - const totalFindings = objectiveBreakdown.reduce( - (sum, objective) => sum + objective.value, - 0 - ) - - // Priority remediation actions, written from what was actually found. Each line names the count - // and, where it helps, the specific rule/app/address — so the report tells this user's story - // rather than repeating a generic checklist. - const isHighOrMed = - threatLevel.level === 'High' || threatLevel.level === 'Medium' - const ruleNames = (becData?.NewRules || []) - .map((rule) => rule.Name) - .filter(Boolean) - .slice(0, 3) - .join(', ') - const rogueAppNames = [ - ...(becData?.AddedApps || []) - .filter((app) => app.MaliciousMatch) - .map((app) => app.DisplayName || app.AppId), - ...(becData?.MaliciousSPs || []).map((sp) => sp.DisplayName || sp.AppId), - ] - .filter(Boolean) - .slice(0, 3) - .join(', ') - const consentNames = flaggedGrants - .map((grant) => grant.ClientDisplayName || grant.ClientAppId) - .filter(Boolean) - .slice(0, 3) - .join(', ') - - const tailoredActions = [ - isHighOrMed && { - tag: 'Critical', - text: `Reset ${userData?.userPrincipalName || 'the user'}'s password and revoke all active sessions to cut off any current attacker access.`, - }, - threatLevel.level === 'High' && { - tag: 'Critical', - text: 'Block sign-in for the account until the mailbox and identity are confirmed clean.', - }, - (flaggedGrants.length > 0 || stats.maliciousApps > 0) && { - tag: 'Critical', - text: `Revoke ${flaggedGrants.length + stats.maliciousApps} risky application consent(s)${consentNames || rogueAppNames ? ` (${consentNames || rogueAppNames})` : ''} — consent survives a password reset.`, - }, - stats.maliciousApps > 0 && { - tag: 'Critical', - text: `Disable the catalog-matched rogue application(s)${rogueAppNames ? ` (${rogueAppNames})` : ''} tenant-wide.`, - }, - (stats.newRules > 0 || stats.ruleChanges > 0) && { - tag: 'High', - text: `Disable the ${stats.newRules + stats.ruleChanges} suspicious inbox rule(s)/change(s)${ruleNames ? ` (${ruleNames})` : ''} that hide replies or auto-forward mail.`, - }, - hasForwarding && { - tag: 'High', - text: `Clear mailbox forwarding${forwardingAddress ? ` to ${forwardingAddress}` : ''}, which silently copies mail out of the tenant.`, - }, - flaggedDelegations.length > 0 && { - tag: 'High', - text: `Remove ${flaggedDelegations.length} flagged mailbox delegation(s) — a delegate keeps access after a reset.`, - }, - (stats.anonymousLinks > 0 || stats.sharingChanges > 0) && { - tag: 'High', - text: `Remove the ${stats.sharingChanges} sharing-link change(s)${stats.anonymousLinks ? `, including ${stats.anonymousLinks} "anyone" link(s)` : ''} and disable OneDrive sharing — anonymous links expose data past any reset.`, - }, - stats.massMailFlagged && { - tag: 'High', - text: `The mailbox sent a mass-mail campaign (${stats.sentTotalMessages} message(s) to ${stats.sentTotalRecipients} recipient(s)). Scope the wave and warn recipients before anything is purged.`, - }, - stats.foreignSuccessfulSignIns > 0 && { - tag: 'High', - text: `${stats.foreignSuccessfulSignIns} successful sign-in(s) from outside the assigned usage location confirm access — treat the account as compromised.`, - }, - flaggedTransportRules.length + flaggedTransportChanges.length > 0 && { - tag: 'High', - text: `Review and disable ${flaggedTransportRules.length + flaggedTransportChanges.length} tenant transport rule(s) changed in the window — these affect every mailbox.`, - }, - stats.recentMfaDevices > 0 && { - tag: 'Medium', - text: `Remove ${stats.recentMfaDevices} MFA method(s) registered during the window, then re-register trusted ones.`, - }, - recentRegisteredDevices.length > 0 && { - tag: 'Medium', - text: `Disable ${recentRegisteredDevices.length} device(s) registered during the window so they cannot satisfy device-based Conditional Access.`, - }, - stats.safelistChanges > 0 && { - tag: 'Medium', - text: `Review ${stats.safelistChanges} trusted-sender / safelist change(s) that would let an attacker's future mail skip filtering.`, - }, - flaggedAddIns.length > 0 && { - tag: 'Medium', - text: `Disable ${flaggedAddIns.length} flagged mailbox add-in(s).`, - }, - ].filter(Boolean) - const priorityActions = - tailoredActions.length > 0 - ? tailoredActions - : [ - { - tag: 'Monitor', - text: 'No specific indicators require remediation. Continue monitoring the account for 30 days and keep MFA enforced as a precaution.', - }, - ] - - // The outcome in plain business terms — what the intrusion actually achieved, phrased for a reader - // who wants the "so what", not which checks ran or how the data was gathered. Leads the summary. - const impactFindings = [ - (stats.foreignSuccessfulSignIns > 0 || foreignNonInteractive.length > 0) && - `Unauthorized access is confirmed — ${ - stats.foreignSuccessfulSignIns + foreignNonInteractive.length - } successful sign-in(s) came from outside the account's assigned location.`, - riskState?.Listed && - `Microsoft Identity Protection currently flags this account as at risk${ - riskState.RiskLevel ? ` (${riskState.RiskLevel} risk)` : '' - }.`, - hasForwarding && - `Incoming mail is being copied out of the organization${ - forwardingAddress ? ` to ${forwardingAddress}` : '' - }, so the attacker keeps reading it even after a reset.`, - (stats.newRules > 0 || stats.ruleChanges > 0) && - `${ - stats.newRules + stats.ruleChanges - } inbox rule(s) or change(s) hide, delete or redirect the user's mail.`, - stats.anonymousLinks > 0 && - `${stats.anonymousLinks} "anyone with the link" sharing link(s) expose files to anyone holding the URL, past any later reset.`, - stats.massMailFlagged && - `The mailbox sent a mass-mail wave — ${stats.sentTotalMessages} message(s) to ${stats.sentTotalRecipients} recipient(s) — so it is now being used to attack others.`, - (flaggedGrants.length > 0 || stats.maliciousApps > 0) && - `${ - flaggedGrants.length + stats.maliciousApps - } risky application consent(s) or app(s) retain access to data independently of the password.`, - (stats.recentMfaDevices > 0 || recentRegisteredDevices.length > 0) && - `New sign-in persistence was added — ${stats.recentMfaDevices} MFA method(s) and ${recentRegisteredDevices.length} device(s) registered during the window.`, - flaggedDelegations.length > 0 && - `${flaggedDelegations.length} mailbox delegation(s) let another account read this mailbox.`, - ].filter(Boolean) - - // A chronological "order of events" that correlates every timestamped signal — the same event - // stream the case page's timeline shows (buildBecTimeline) — so the report reads as the shape of - // the intrusion over time. Identical bursts collapse to one ×N line so a repeated audit event does - // not drown the narrative. - const { events: timelineRaw } = buildBecTimeline( - becData, - windowDays, - userData?.userPrincipalName - ) - - // Collapse a run of identical events in the same minute (same label and detail) into one ×N row. - const timelineCollapsed = [] - timelineRaw.forEach((event) => { - const minute = event.date.toISOString().slice(0, 16) - const previous = timelineCollapsed[timelineCollapsed.length - 1] - if ( - previous && - previous.minute === minute && - previous.label === event.label && - previous.detail === event.detail - ) { - previous.count += 1 - return - } - timelineCollapsed.push({ ...event, minute, count: 1 }) - }) - const timelineEvents = timelineCollapsed.map((event) => ({ - when: formatDate(event.date), - event: event.count > 1 ? `${event.label} (×${event.count})` : event.label, - detail: event.detail, - })) - - // Containment actions already run for this case (from the run's stored history), flattened to one - // row per action result, newest first — so the report records what was done, not only what to do. - const remediationRows = (becData?.Run?.Containment || []) - .slice() - .reverse() - .flatMap((entry) => - (Array.isArray(entry.Results) ? entry.Results : []).map((row) => ({ - when: formatDate(entry.At), - action: String(row.Action || '') - .replace(/([a-z0-9])([A-Z])/g, '$1 $2') - .trim(), - target: row.Target, - result: row.resultText, - state: row.state, - })) - ) - const remediationStateColour = (row) => - ({ - success: '#2F855A', - error: '#C53030', - warning: '#B7791F', - })[row.state] || '#4A5568' - - return ( - - } - > - {/* EXECUTIVE SUMMARY PAGE */} - -
    - - This report documents the findings of a Business Email Compromise - (BEC) investigation performed for the user account{' '} - {userData?.userPrincipalName} within{' '} - {tenantName}. The investigation analyzed suspicious - activity indicators including mailbox rules, permission changes, new - applications, authentication patterns, and sign-in locations over a - {windowDays}-day period. - - - - Business Email Compromise is a sophisticated scam targeting - organizations that regularly perform wire transfers or have - established relationships with foreign suppliers. Attackers - compromise legitimate email accounts through social engineering or - computer intrusion techniques to conduct unauthorized fund - transfers, steal sensitive information, or impersonate executives. - -
    - -
    - - - - {threatLevel.level === 'High' && - 'HIGH RISK: Multiple indicators of compromise detected. Immediate remediation actions are strongly recommended. This account shows patterns consistent with active Business Email Compromise attacks.'} - {threatLevel.level === 'Medium' && - 'MEDIUM RISK: Suspicious activity patterns detected. Review findings and consider implementing recommended security measures. Some indicators suggest potential unauthorized access.'} - {threatLevel.level === 'Low' && - 'LOW RISK: Minimal suspicious activity detected. The findings show standard user behavior with no significant indicators of compromise. Continue monitoring as a precautionary measure.'} - - {appliedSignals.length > 0 && ( - - {appliedSignals - .map( - (signal) => - `+${signal.Weight} ${signal.Description} (${signal.Count})` - ) - .join('\n')} - - )} -
    - -
    - {impactFindings.length > 0 ? ( - <> - - In plain terms, this is what the investigation established about{' '} - {userData?.userPrincipalName}: - - - {impactFindings.map((finding, index) => ( - {finding} - ))} - - - ) : ( - - None of the investigation's checks returned evidence that - this account was accessed, altered or misused during the analysis - window. - - )} -
    - -
    - - Every check in this investigation and its result.{' '} - {!isSummary && - 'Flagged rows are expanded in the detailed findings later in this report. '} - {flaggedAreaCount} of {summaryData.length} checks returned something - to review. - - {totalFindings > 0 && ( - <> - - Findings by attacker objective — grouped by what - each finding would let an attacker do: - - ({ - label: objective.label, - value: objective.value, - max: objectiveMax, - display: `${objective.value}`, - colour: objective.colour, - }))} - /> - - )} - row.resultColour, - }, - ]} - rows={summaryData} - limit={summaryData.length} - /> - {!isSummary && ( - - Checks that could not run (missing a licence, permission, mailbox - or service) are itemised under Data Source Information below — a - check that did not run is not a pass. - - )} -
    - -
    - - Actions specific to what this investigation found, most urgent - first. Your IT or security team should carry these out without delay - {!isSummary - ? '; the strategic and preventative measures follow later in this report.' - : '.'} - - - ({ - Critical: '#C53030', - High: '#DD6B20', - Medium: '#B7791F', - Monitor: '#2F855A', - })[row.tag] || '#4A5568', - }, - { header: 'Action', key: 'text', width: 5 }, - ]} - rows={priorityActions} - limit={priorityActions.length} - /> -
    - -
    - {timelineEvents.length > 0 ? ( - <> - - Every timestamped signal — sign-ins, directory and mailbox - changes, sharing, and the mail itself — in the order it - happened, with who and where where known. Read it as the shape - of the intrusion over time, not as isolated findings. - - - - ) : ( - - None of the checks returned a dated event inside the analysis - window. This usually means no changes were made to the account in - the period, not that data was missing. - - )} -
    - - {remediationRows.length > 0 && ( -
    - - The containment actions already run for this account during the - investigation, and their result for each target. - - -
    - )} - - {!isSummary && ( -
    - - {becData?.ExtractResult || 'Unknown'} - - - Last {windowDays} days ending{' '} - {becData?.ExtractedAt ? formatDate(becData.ExtractedAt) : 'N/A'} - - {becData?.CaseId && ( - - {becData.CaseId} - full investigation. Metadata only: audit - records, sign-ins, trace headers, permissions, rules and devices - were collected; no message content was read. - - )} - {skippedCollectors.length > 0 && ( - - {skippedCollectors - .map( - ([name, marker]) => - `${name}: ${marker.Requirement || marker.Error || 'not checked'}` - ) - .join('\n')} - - )} - {incompleteCollectors.length > 0 && ( - - {incompleteCollectors - .map( - ([name, marker]) => - `${name}: ${marker.Error || `capped at ${marker.Cap}`}` - ) - .join('\n')} - - )} - - {locationAnalysis?.UsageLocation || - 'Not assigned - sign-ins and activity could not be compared against an expected country'} - -
    - )} -
    - - {/* The educational, per-check detail, recommendations and compliance pages — everything past the - executive summary. The summary variant stops here so a C-suite reader gets the verdict, the - findings at a glance, the priority actions, the timeline and what was remediated, and no more. */} - {!isSummary && ( - <> - {/* UNDERSTANDING BEC PAGE */} - -
    - - Business Email Compromise (BEC) is a type of cyberattack where - criminals gain unauthorized access to a business email account. - Once inside, attackers can: - - - - - {' '} - Read sensitive emails to learn about business operations, - financial processes, and key relationships. - - - {' '} - Send fraudulent emails appearing to come from company - leadership requesting wire transfers or sensitive data. - - - {' '} - Intercept legitimate invoices and alter payment information to - redirect funds to attacker-controlled accounts. - - - {' '} - Create email rules to automatically delete or hide messages, - preventing detection. - - -
    - -
    - - Attackers typically gain access to email accounts through: - - - - - {' '} - Deceptive emails that trick users into providing their login - credentials on fake websites. - - - {' '} - Automated attempts to log in using common passwords across - many accounts. - - - {' '} - Using usernames and passwords leaked from other breached - websites. - - - {' '} - Software that captures keystrokes or steals stored passwords - from compromised devices. - - -
    - -
    - - This analysis was initiated because suspicious activity was - detected or reported for this user account. The investigation - examines multiple indicators that might suggest account - compromise, including unusual mailbox rules, unexpected - permission changes, new application authorizations, and abnormal - sign-in patterns. Early detection is critical to minimize - potential damage and prevent financial loss or data theft. - -
    -
    - - {/* DETAILED FINDINGS PAGE */} - - {/* Check 1: Mailbox Rules */} -
    - - Attackers often create email rules to automatically forward, - delete, or hide messages so victims never see evidence of - fraudulent activity. A rule is flagged when it forwards or - redirects mail (especially to an external address), deletes - messages, moves them to a low-visibility folder (RSS, Archive, - Deleted Items), stops processing other rules, targets financial - keywords, or takes any of these actions on all incoming mail - with no condition. - - - {stats.newRules > 0 && ( - <> - - The following mailbox rules were detected. Review each rule - carefully to determine if it was created by the user or by - an attacker. Rules that forward emails or move them to - unusual folders are particularly suspicious. - - - {becData.NewRules.slice(0, 10).map((rule, index) => ( - - {[ - rule.MoveToFolder && - `Moves mail to: ${rule.MoveToFolder}`, - rule.ForwardTo && `Forwards to: ${rule.ForwardTo}`, - rule.ForwardAsAttachmentTo && - `Forwards as attachment to: ${rule.ForwardAsAttachmentTo}`, - rule.RedirectTo && `Redirects to: ${rule.RedirectTo}`, - rule.DeleteMessage && 'Deletes messages', - rule.MarkAsRead && 'Marks messages read', - rule.StopProcessingRules && - 'Stops processing further rules', - rule.SubjectContainsWords && - `On subject words: ${ - Array.isArray(rule.SubjectContainsWords) - ? rule.SubjectContainsWords.join(', ') - : rule.SubjectContainsWords - }`, - rule.RecentlyChanged && - 'Created or changed in the window', - rule.Enabled === false && 'Currently disabled', - ] - .filter(Boolean) - .join('\n') || - rule.Description || - 'No actions recorded on this rule'} - - ))} - {becData.NewRules.length > 10 && ( - - ... and {becData.NewRules.length - 10} more rules (in the - retained investigation record) - - )} - - )} - {stats.ruleChanges > 0 && ( - <> - - The audit log recorded inbox rules being created, changed or - removed on this mailbox. Rules that were removed after use - are a common way for attackers to cover their tracks. - - - {becData.InboxRuleChanges.slice(0, 10).map( - (change, index) => ( - - Date: {change.Date || 'Unknown'} - {'\n'} - By: {change.UserKey || 'Unknown'} - {change.ClientIP && - `\nFrom: ${change.ClientIP}${change.Country ? ` (${change.Country})` : ''}`} - {change.ForeignLocation === true && - '\n⚠️ Originated outside the assigned usage location'} - {change.Parameters && - `\nParameters: ${change.Parameters}`} - - ) - )} - {becData.InboxRuleChanges.length > 10 && ( - - ... and {becData.InboxRuleChanges.length - 10} more - changes (see the retained investigation record for the - full list) - - )} - - )} - {stats.newRules === 0 && stats.ruleChanges === 0 && ( - - No mailbox rules were detected that match suspicious patterns. - This is a positive indicator. - - )} -
    -
    - - {/* CHECK 2: NEW USERS */} - -
    - - Attackers sometimes create new user accounts to maintain - persistent access or to use as staging accounts for fraudulent - activities. Reviewing recently created users helps identify - unauthorized account creation. - - - {stats.newUsers > 0 ? ( - <> - - The following users were created in the last {windowDays}{' '} - days. Verify that each account creation was authorized and - legitimate. - - - {becData.NewUsers.slice(0, 8).map((user, index) => ( - - Email: {user.userPrincipalName || 'N/A'} - {'\n'} - Created: {formatDate(user.createdDateTime)} - - ))} - {becData.NewUsers.length > 8 && ( - - ... and {becData.NewUsers.length - 8} more users (see JSON - export for full list) - - )} - - ) : ( - - No new user accounts were created during the analysis period. - - )} -
    - - {/* Check 3: New Applications */} -
    - - Attackers may authorize malicious or suspicious third-party - applications to access your email and data. These applications - can read emails, send messages, and access files without the - user's explicit knowledge. - - - {stats.maliciousApps > 0 && ( - - One or more applications in this tenant match a - known-malicious application catalog. Consent-based access - survives a password reset, so these applications should be - removed unless their presence is explained. - - )} - - {stats.newApps > 0 ? ( - <> - - New applications were granted access during the analysis - period. Review each application to ensure it was authorized - and is from a trusted publisher. - - - {becData.AddedApps.slice(0, 6).map((app, index) => ( - - Publisher: {app.publisher || 'Unknown'} - {'\n'} - App ID: {app.appId || 'N/A'} - {'\n'} - Created: {formatDate(app.createdDateTime)} - {app.MaliciousMatch && - `\n⚠️ Matches known-malicious catalog entry "${app.MaliciousMatch.Name}"${ - app.MaliciousMatch.Categories?.length - ? ` (${app.MaliciousMatch.Categories.join(', ')})` - : '' - }`} - - ))} - {becData.AddedApps.length > 6 && ( - - ... and {becData.AddedApps.length - 6} more apps (see JSON - export for full list) - - )} - - ) : ( - (becData?.MaliciousSPs?.length || 0) === 0 && ( - - No new applications were authorized during the analysis - period, and no known malicious applications are present in - the tenant. - - ) - )} - - {(becData?.MaliciousSPs?.length || 0) > 0 && ( - <> - {becData.MaliciousSPs.slice(0, 6).map((app, index) => ( - - Catalog entry: {app.CatalogName || 'Unknown'} - {'\n'} - App ID: {app.appId || 'N/A'} - {'\n'} - Categories:{' '} - {app.Categories?.length - ? app.Categories.join(', ') - : 'N/A'} - {'\n'} - Enabled: {String(app.accountEnabled ?? 'Unknown')} - {'\n'} - First seen: {formatDate(app.createdDateTime)} - - ))} - {becData.MaliciousSPs.length > 6 && ( - - ... and {becData.MaliciousSPs.length - 6} more (see JSON - export for full list) - - )} - - )} -
    -
    - - {/* CHECK 4, 5, 6, 7: PERMISSIONS, SENT MAIL, MFA, PASSWORDS */} - - {/* Check 4: Mailbox Permission Changes */} -
    - - Unauthorized changes to mailbox permissions can allow attackers - to grant themselves or accomplices access to read, send, or - manage emails. This is a common technique to maintain persistent - access. - - - {stats.permissionChanges > 0 ? ( - <> - - Mailbox permission changes were detected. Verify that each - change was authorized and necessary for legitimate business - purposes. - - - {becData.MailboxPermissionChanges.slice(0, 5).map( - (change, index) => ( - - User: {change.UserKey || 'Unknown'} - {'\n'} - Target: {change.ObjectId || 'N/A'} - {'\n'} - Permissions: {change.Permissions || 'Unknown'} - {change.TargetsSuspect === true && - '\n⚠️ Targets the investigated mailbox'} - - ) - )} - {becData.MailboxPermissionChanges.length > 5 && ( - - ... and {becData.MailboxPermissionChanges.length - 5} more - changes - - )} - - ) : ( - - No mailbox permission changes were detected during the - analysis period. - - )} -
    - - {/* Check 5: Sent Messages */} -
    - - Attackers use a compromised mailbox to send fraudulent invoices, - phishing, or internal impersonation mail. The message trace - shows what actually left the mailbox during the analysis period, - including the IP address it was sent from. - - - {stats.sentMessages > 0 ? ( - <> - - ℹ️ {stats.sentTotalMessages || stats.sentMessages}{' '} - message(s) to{' '} - {stats.sentTotalRecipients || stats.sentMessages}{' '} - recipient(s) were sent by this mailbox during the analysis - period - {stats.foreignSentMessages > 0 - ? `, including ${stats.foreignSentMessages} from an IP outside the user's assigned usage location.` - : '.'} - - - {stats.massMailFlagged && ( - - {stats.repeatedSubjects > 0 - ? `${stats.repeatedSubjects} subject(s) were sent as many separate messages or to many recipients. ` - : ''} - {stats.sendBursts > 0 - ? `${stats.sendBursts} short burst(s) of high-volume sending were detected. ` - : ''} - Identical-subject mass mail and send bursts are how a - compromised mailbox spreads phishing or fraudulent - invoices. Review the campaigns below and warn the - recipients if the content was malicious. - - )} - - {(becData?.SentMessageAnalysis?.RepeatedSubjects || []) - .slice(0, 5) - .map((group, index) => ( - - Messages: {group.MessageCount} - {'\n'} - Recipients: {group.RecipientCount} - {'\n'} - First sent: {group.FirstSent || 'N/A'} - {'\n'} - Last sent: {group.LastSent || 'N/A'} - - ))} - {(becData?.SentMessageAnalysis?.RepeatedSubjects?.length || - 0) > 5 && ( - - ... and{' '} - {becData.SentMessageAnalysis.RepeatedSubjects.length - 5}{' '} - more repeated subjects (see the retained investigation - record for the full list) - - )} - - {(becData?.SentMessageAnalysis?.Bursts || []) - .slice(0, 5) - .map((burst, index) => ( - - Starting: {burst.WindowStart || 'N/A'} - {burst.TopSubject && - `\nMost common subject: ${burst.TopSubject}`} - - ))} - {(becData?.SentMessageAnalysis?.Bursts?.length || 0) > 5 && ( - - ... and {becData.SentMessageAnalysis.Bursts.length - 5}{' '} - more bursts (see the retained investigation record for the - full list) - - )} - - {becData.SentMessages.slice(0, 10).map((msg, index) => ( - - To: {msg.RecipientAddress || 'N/A'} - {'\n'} - Status: {msg.Status || 'N/A'} - {'\n'} - Received: {msg.Received || 'N/A'} - {msg.FromIP && - `\nFrom IP: ${msg.FromIP}${msg.Country ? ` (${msg.Country})` : ''}`} - {msg.ForeignLocation === true && - '\n⚠️ Sent from outside the assigned usage location'} - - ))} - {becData.SentMessages.length > 10 && ( - - ... and {becData.SentMessages.length - 10} more messages - (see the retained investigation record for the full list) - - )} - - ) : ( - - No messages were sent by this mailbox during the analysis - period. - - )} -
    - - {/* Check 6: MFA Devices */} -
    - - Multi-factor authentication (MFA) devices provide an additional - layer of security. Reviewing registered MFA methods helps - identify if attackers have added unauthorized devices to bypass - security controls. - - - {stats.mfaDevices > 0 ? ( - <> - - ℹ️ {stats.mfaDevices} MFA device(s) registered - {stats.recentMfaDevices > 0 - ? `, including ${stats.recentMfaDevices} registered in the last ${windowDays} days. Verify the recent registrations were made by the user — attackers register their own method to keep access after a password reset.` - : '. Verify each device belongs to the user.'} - - - {[...becData.MFADevices] - .sort( - (a, b) => - new Date(b?.createdDateTime || 0) - - new Date(a?.createdDateTime || 0) - ) - .slice(0, 5) - .map((device, index) => ( - - Display Name: {device.displayName || 'N/A'} - {'\n'} - Registered: {formatDate(device.createdDateTime)} - {isRecentMfaDevice(device) && - ` -⚠️ Registered in the last ${windowDays} days`} - - ))} - {becData.MFADevices.length > 5 && ( - - ... and {becData.MFADevices.length - 5} more methods (see - JSON export for full list) - - )} - - ) : ( - - No multi-factor authentication devices are registered. MFA is - highly recommended to prevent unauthorized access. - - )} -
    - - {/* Check 7: Password Changes */} -
    - - Attackers often change passwords to lock out legitimate users. - Reviewing recent password changes in the tenant helps identify - if the compromised account's password was changed or if other - accounts were affected. - - - {stats.passwordChanges > 0 ? ( - <> - - ℹ️ {stats.passwordChanges} password change(s) detected in - the tenant during the analysis period. - - - {becData.ChangedPasswords.slice(0, 5).map((user, index) => ( - - Email: {user.userPrincipalName || 'N/A'} - {'\n'} - Last Password Change:{' '} - {formatDate(user.lastPasswordChangeDateTime)} - - ))} - {becData.ChangedPasswords.length > 5 && ( - - ... and {becData.ChangedPasswords.length - 5} more (see - JSON export for full list) - - )} - - ) : ( - - ℹ️ No password changes detected during the analysis period. - - )} -
    -
    - - {/* CHECK 8, 9, 10: SENDER LISTS, DEVICES, LOCATIONS */} - - {/* Check 8: Trusted & Blocked Senders */} -
    - - Attackers may add their own domain to the Trusted Senders list - so their fraudulent messages bypass spam filtering, or add - finance/security domains to the Blocked Senders list so warnings - and alerts are hidden from the victim in the Junk Email folder. - - - {becData?.SafelistError && ( - - {becData.SafelistError} - {'\n'} - An empty list here does not mean the mailbox has no trusted or - blocked senders. - - )} - - {stats.safelistChanges > 0 && ( - <> - - The audit log recorded changes to the Trusted/Blocked - Senders and Domains list on this mailbox. Review each change - carefully. - - - {becData.SafelistChanges.slice(0, 10).map((change, index) => ( - - Date: {formatDate(change.Date)} - {change.ClientIP && - `\nFrom: ${change.ClientIP}${change.Country ? ` (${change.Country})` : ''}`} - {change.ForeignLocation === true && - '\n⚠️ Originated outside the assigned usage location'} - {'\n'} - Trusted: {formatSafelistValue(change.Trusted)} - {'\n'} - Blocked: {formatSafelistValue(change.Blocked)} - - ))} - {becData.SafelistChanges.length > 10 && ( - - ... and {becData.SafelistChanges.length - 10} more changes - (see the retained investigation record for the full list) - - )} - - )} - - {stats.trustedSenders > 0 && ( - - {becData.TrustedSenders.slice(0, 15).join(', ')} - - )} - {stats.trustedSenders > 15 && ( - - ... and {stats.trustedSenders - 15} more trusted entries (see - JSON export for full list) - - )} - - {stats.blockedSenders > 0 && ( - - {becData.BlockedSenders.slice(0, 15).join(', ')} - - )} - {stats.blockedSenders > 15 && ( - - ... and {stats.blockedSenders - 15} more blocked entries (see - JSON export for full list) - - )} - - {!becData?.SafelistError && - stats.trustedSenders === 0 && - stats.blockedSenders === 0 && - stats.safelistChanges === 0 && ( - - No trusted or blocked sender/domain entries were found on - this mailbox. - - )} -
    - - {/* Check 9: Intune Devices */} -
    - - Newly enrolled Intune devices can indicate an attacker standing - up a VM or BYOD endpoint under the compromised identity, - including paths that re-register Windows Hello for Business. - Review devices enrolled during the analysis window first. - - - {becData?.Completeness?.IntuneDevices?.Skipped ? ( - - {becData.Completeness.IntuneDevices.Requirement - ? `Not checked - ${becData.Completeness.IntuneDevices.Requirement}. This is not a pass; the result is unknown.` - : becData.Completeness.IntuneDevices.Error} - - ) : becData?.IntuneDevicesError ? ( - - {becData?.Completeness?.IntuneDevices?.Error || - becData.IntuneDevicesError} - {'\n'} - An empty device list here does not mean the user has no Intune - devices. - - ) : stats.intuneDevices > 0 ? ( - <> - - ℹ️ {stats.intuneDevices} Intune-managed device(s) associated - with this user - {stats.recentIntuneDevices > 0 - ? `, including ${stats.recentIntuneDevices} enrolled in the last ${windowDays} days.` - : `. None were enrolled in the last ${windowDays} days.`} - - - {sortedIntuneDevices.slice(0, 5).map((device, index) => ( - - OS: {device.operatingSystem || 'N/A'} - {device.osVersion ? ` ${device.osVersion}` : ''} - {'\n'} - Enrolled: {formatDate(device.enrolledDateTime)} - {'\n'} - Compliance: {device.complianceState || 'N/A'} - {'\n'} - Enrollment Type: {device.deviceEnrollmentType || 'N/A'} - {device.serialNumber - ? `\nSerial: ${device.serialNumber}` - : ''} - - ))} - {sortedIntuneDevices.length > 5 && ( - - ... and {sortedIntuneDevices.length - 5} more devices (see - the retained investigation record for the full list) - - )} - - ) : ( - - No Intune-managed devices were found for this user. - - )} -
    - - {/* Check 10: Sign-in Locations */} -
    - - Sign-ins from countries the user does not work from are one of - the strongest compromise indicators. Each sign-in is compared - against the user's assigned usage location in Entra ID - {locationAnalysis?.UsageLocation - ? ` (${locationAnalysis.UsageLocation})` - : ''} - , and the client IPs behind rule changes, safelist changes, - sharing changes, and sent mail are geo-located and compared the - same way. - - - {becData?.SuspectUserSignInsError ? ( - - {becData.SuspectUserSignInsError} - {'\n'} - An empty list here does not mean the user has not signed in. - - ) : ( - <> - {!locationAnalysis?.UsageLocation && ( - - {locationAnalysis?.Note || - 'The user has no usage location assigned in Entra ID, so activity cannot be compared against an expected country.'} - - )} - - {(locationAnalysis?.SignInCountries?.length || 0) > 0 && ( - - {locationAnalysis.SignInCountries.map( - (c) => `${c.Country}: ${c.Count} sign-in(s)` - ).join('\n')} - - )} - - {stats.foreignSignIns > 0 || stats.foreignActivity > 0 ? ( - <> - - {stats.foreignSignIns} sign-in(s) (of which{' '} - {stats.foreignSuccessfulSignIns} succeeded),{' '} - {locationAnalysis?.ForeignRuleChangeCount || 0} inbox - rule change(s),{' '} - {locationAnalysis?.ForeignSafelistChangeCount || 0}{' '} - safelist change(s),{' '} - {locationAnalysis?.ForeignSharingChangeCount || 0}{' '} - sharing change(s), and{' '} - {locationAnalysis?.ForeignSentMessageCount || 0} sent - message(s) originated outside{' '} - {locationAnalysis?.UsageLocation}. Failed foreign - sign-ins are mostly password-spray noise; the successful - ones prove access. Review each carefully — a single - legitimate trip can explain some of this, but rule, - safelist, or sharing changes from a foreign IP rarely - have an innocent explanation. - - - {foreignSignIns.slice(0, 10).map((signIn, index) => ( - - Application: {signIn.AppDisplayName || 'N/A'} - {'\n'} - IP Address: {signIn.IPAddress || 'N/A'} - {'\n'} - City: {signIn.City || 'N/A'} - {'\n'} - Result: {signIn.Status || 'N/A'} - - ))} - {foreignSignIns.length > 10 && ( - - ... and {foreignSignIns.length - 10} more foreign - sign-ins (see the retained investigation record for - the full list) - - )} - - ) : locationAnalysis?.UsageLocation ? ( - - All located sign-ins and activity match the user's - assigned usage location ({locationAnalysis.UsageLocation} - ). - - ) : null} - - )} -
    - - {/* Check 11: Sharing Links */} -
    - - Attackers share OneDrive and SharePoint folders to give - themselves a data feed that survives a password reset, and - anonymous links expose the content to anyone holding the URL. - This check lists every sharing link the account created or - changed during the analysis period, including the IP address it - was done from. - - - {stats.sharingChanges > 0 ? ( - <> - - {stats.anonymousLinks > 0 - ? `${stats.anonymousLinks} of these involve anonymous links, which anyone with the URL can open. ` - : ''} - Review each link and remove any that are not explained, even - if the account has since been remediated. - - - {becData.SharingChanges.slice(0, 10).map((change, index) => ( - - Date: {formatDate(change.Date)} - {'\n'} - Workload: {change.Workload || 'N/A'} - {change.Target && `\nShared with: ${change.Target}`} - {change.ClientIP && - `\nFrom: ${change.ClientIP}${change.Country ? ` (${change.Country})` : ''}`} - {change.ForeignLocation === true && - '\n⚠️ Originated outside the assigned usage location'} - - ))} - {becData.SharingChanges.length > 10 && ( - - ... and {becData.SharingChanges.length - 10} more changes - (see the retained investigation record for the full list) - - )} - - ) : ( - - No sharing links were created or changed by this account - during the analysis period. - - )} -
    -
    - - {/* FULL INVESTIGATION PAGE */} - { - -
    - - A delegate with FullAccess or SendAs, a forwarding address, or - an automatic reply lets an attacker keep reading and - impersonating after the password is changed. - - {becData?.MailboxState?.HasForwarding && ( - - Mail is forwarded to{' '} - {becData.MailboxState.ForwardingSmtpAddress || - becData.MailboxState.ForwardingAddress} - {becData.MailboxState.DeliverToMailboxAndForward - ? ' (a copy stays in the mailbox)' - : ''} - . - - )} - {flaggedDelegations.length > 0 ? ( - <> - - External, guest or catch-all principals hold rights on - this mailbox. Remove any the user cannot explain. - - {flaggedDelegations.slice(0, 10).map((d, index) => ( - - Rights: {d.AccessRights} - {'\n'} - Resource: {d.Resource} - - ))} - - ) : ( - - {(becData?.Delegations || []).length} delegation(s) exist, - none to an external, guest or catch-all principal. - - )} -
    - -
    - - Applications the user consented to keep their access after a - password reset. A rogue-catalog match or a high-risk scope - from an unverified publisher is how mailboxes are synchronised - out of the tenant. - - {flaggedGrants.length > 0 ? ( - <> - - Revoke the grants below unless the user can explain them. - - {flaggedGrants.slice(0, 10).map((g, index) => ( - - Scopes: {g.Scope || 'N/A'} - {'\n'} - Publisher: {g.Publisher || 'Unknown'}{' '} - {g.PublisherVerified ? '(verified)' : '(not verified)'} - {g.CatalogMatch?.Name && - `\nCatalog: ${g.CatalogMatch.Name} (${g.CatalogMatch.Source})`} - - ))} - - ) : ( - - {(becData?.UserGrants || []).length} consent(s) and role - assignment(s) exist, none matching the rogue-app catalogs or - carrying a high-risk scope from an unverified publisher. - - )} -
    - -
    - - A tenant-wide transport rule that BCCs, redirects, deletes or - quarantines mail keeps a feed open after the mailbox itself is - cleaned. - - {flaggedTransportChanges.length > 0 || - flaggedTransportRules.length > 0 ? ( - <> - - Review each rule; disable any that cannot be explained. - - {flaggedTransportChanges.slice(0, 5).map((c, index) => ( - - Date: {formatDate(c.Date)} - {'\n'} - By: {c.Actor || 'Unknown'} - {c.ClientIP && - `\nFrom: ${c.ClientIP}${c.Country ? ` (${c.Country})` : ''}`} - {'\n'} - Risky parameters:{' '} - {Array.isArray(c.RiskyParameters) - ? c.RiskyParameters.join(', ') - : c.RiskyParameters} - - ))} - {flaggedTransportRules.slice(0, 5).map((r, index) => ( - - {Array.isArray(r.RiskReasons) - ? r.RiskReasons.join('\n') - : r.RiskReasons} - - ))} - - ) : ( - - No transport rule with a diversion or suppression action was - changed in the window or exists in the tenant. - - )} -
    - -
    - {flaggedAddIns.length > 0 ? ( - - {flaggedAddIns - .map( - (a) => - `${a.DisplayName} (${a.ProviderName || 'unknown provider'})` - ) - .join('\n')} - - ) : ( - - No enabled user-installed add-in from a non-Microsoft - provider was found. - - )} -
    - -
    - - The message that started the compromise usually arrived in the - window. Trace metadata is checked for phishing-shaped subjects - and look-alike sender domains; Defender for Office 365 - verdicts are included where licensed. No message content is - read. - - {becData?.ReceivedMailSummary && ( - - ℹ️ {becData.ReceivedMailSummary.TotalMessages} message(s) - from {becData.ReceivedMailSummary.UniqueSenders} sender(s) - were received in the window. - - )} - {receivedFindings.length > 0 || deliveredThreats.length > 0 ? ( - <> - - Look-alike sender domains are the strongest signal; - subject patterns are leads for review, not verdicts. - - {receivedFindings.slice(0, 8).map((f, index) => ( - - Subject: {f.Subject || '(no subject)'} - {'\n'} - Reason: {f.Reason} - {'\n'} - Received: {f.Received || 'N/A'} - {f.Status || 'N/A'} - - ))} - {deliveredThreats.slice(0, 5).map((d, index) => ( - - From: {d.SenderAddress || 'Unknown'} - {'\n'} - Subject: {d.Subject || '(no subject)'} - {'\n'} - Delivery: {d.DeliveryAction || 'N/A'} /{' '} - {d.LatestDeliveryLocation || 'N/A'} - - ))} - - ) : ( - - No phishing-shaped subjects, look-alike sender domains or - delivered Defender detections were found. - - )} -
    - -
    - {flaggedAudits.length > 0 ? ( - <> - - Security-info registration, consent, service principal, - device, password, token or role events involving this - user. - - {flaggedAudits.slice(0, 8).map((a, index) => ( - - Date: {formatDate(a.ActivityDateTime)} - {'\n'} - By: {a.InitiatedBy || 'Unknown'} - {a.ClientIP && - `\nFrom: ${a.ClientIP}${a.Country ? ` (${a.Country})` : ''}`} - {a.Targets && `\nTargets: ${a.Targets}`} - - ))} - - ) : ( - - {(becData?.DirectoryAudits || []).length} directory event(s) - involved this user in the window, none of the flagged kinds. - - )} -
    - -
    - {recentRegisteredDevices.length > 0 ? ( - - {recentRegisteredDevices - .map( - (d) => - `${d.displayName || d.deviceId} (${d.operatingSystem || 'unknown OS'}, ${d.trustType || 'unknown trust'}) registered ${formatDate(d.registrationDateTime)}` - ) - .join('\n')} - - ) : ( - - {(becData?.RegisteredDevices || []).length} registered - device(s), none new. - - )} - {foreignNonInteractive.length > 0 ? ( - - {foreignNonInteractive - .slice(0, 8) - .map( - (s) => - `${formatDate(s.CreatedDateTime)} - ${s.AppDisplayName || 'N/A'} from ${s.IPAddress || 'N/A'} (${s.Country || 'Unknown'})` - ) - .join('\n')} - - ) : ( - - {(becData?.NonInteractiveSignIns || []).length} recent - non-interactive sign-in(s), none successful from outside the - usage location. - - )} -
    - -
    - {mailActivitySummary ? ( - - Item accesses: {mailActivitySummary.MailItemsAccessedCount} - {'\n'} - Hard deletes: {mailActivitySummary.HardDeleteCount} - {mailActivitySummary.HardDeleteExceeded - ? ` ⚠️ exceeds the ${mailActivitySummary.HardDeleteThreshold} threshold` - : ''} - {'\n'} - Soft deletes: {mailActivitySummary.SoftDeleteCount} - {'\n'} - Sends: {mailActivitySummary.SendCount} - {'\n'} - Distinct client IPs: {mailActivitySummary.DistinctClientIPs} - {mailActivitySummary.SendAsByOthersCount > 0 && - `\nSent as/on behalf by others: ${mailActivitySummary.SendAsByOthersCount}`} - {'\n'} - Counts only - no items were read. - - ) : ( - - Mailbox activity counts were not available for this run. - - )} - {becData?.Completeness?.RiskState?.Skipped ? ( - - {becData.Completeness.RiskState.Requirement - ? `Not checked - ${becData.Completeness.RiskState.Requirement}. This is not a pass; whether the account is flagged as risky is unknown.` - : becData.Completeness.RiskState.Error} - - ) : riskState?.Listed ? ( - - {riskState.RiskDetail || 'No detail'} - last updated{' '} - {formatDate(riskState.RiskLastUpdatedDateTime)}. - {(riskState.Detections || []).length > 0 && - ` ${(riskState.Detections || []).length} risk detection(s) in the window.`} - - ) : ( - - Identity Protection does not list this user as risky. - - )} -
    -
    - } - - {/* RECOMMENDATIONS PAGE */} - -
    - - The immediate, evidence-specific actions for this account are - listed under Priority Remediation Actions in the - executive summary at the front of this report, most urgent - first, and should be carried out by your IT or security team - without delay. The strategies below reduce the chance of a - repeat once the account has been recovered. - -
    - -
    - - To prevent future Business Email Compromise attacks, implement - these security best practices: - - - - - {' '} - Require MFA for all users, especially those with - administrative privileges or access to financial systems. - - - {' '} - Educate employees about phishing, social engineering, and how - to identify suspicious emails. Regular training significantly - reduces successful attacks. - - - {' '} - Use email security solutions that detect and block phishing, - malware, and suspicious attachments. - - - {' '} - Restrict access based on location, device compliance, and risk - level to prevent unauthorized sign-ins. - - - {' '} - Regularly review audit logs for suspicious activities such as - unusual sign-in patterns, rule creation, or permission - changes. - - - {' '} - Implement multi-person approval processes for wire transfers - and payment changes to prevent fraudulent transactions. - - -
    - -
    - - Share these key points with the affected user to help prevent - future compromises: - - - - - Never click on links or open attachments in unexpected emails, - even if they appear to come from known contacts. - - - Always verify unusual requests for money transfers or - sensitive information through a separate communication channel - (phone call, in person). - - - Use strong, unique passwords for each account and consider - using a password manager. - - - Be cautious when authorizing new applications or granting - permissions to third-party services. - - - Report suspicious emails or activities to your IT security - team immediately. - - -
    -
    - - {/* COMPLIANCE & DOCUMENTATION PAGE */} - -
    - - This report supports compliance and documentation requirements - for various security frameworks and regulatory standards: - - - - - {' '} - Demonstrates incident detection, analysis, and response - procedures (Controls A.16.1.1 - A.16.1.7). - - - {' '} - Provides evidence of security incident monitoring, analysis, - and documentation (AC.L2-3.1.12, AU.L2-3.3.1). - - - {' '} - Documents detective and responsive controls for security - incidents (CC7.3, CC7.4). - - - {' '} - Aligns with Detect (DE.AE, DE.CM) and Respond (RS.AN, RS.MI) - functions. - - - {' '} - Demonstrates security breach detection and potential data - breach assessment (Articles 32, 33). - - -
    - -
    - - This investigation and resulting documentation provide an audit - trail for security incident response: - - - - Investigation Date: {formatDate(becData?.ExtractedAt)} - {'\n'} - Analyzed User: {userData?.userPrincipalName} - {'\n'} - Organization: {tenantName} - {'\n'} - Analysis Period: {windowDays} days - {'\n'} - Assigned Usage Location:{' '} - {locationAnalysis?.UsageLocation || 'Not assigned'} - {'\n'} - Audit Log Status: {becData?.ExtractResult || 'Unknown'} - - - - Threat Level: {threatLevel.level} - {'\n'} - Mailbox Rules Found: {stats.newRules} - {'\n'} - Rule Changes: {stats.ruleChanges} - {'\n'} - Permission Changes: {stats.permissionChanges} ( - {stats.permissionChangesTargetingUser} targeting this mailbox) - {'\n'} - New Applications: {stats.newApps} - {'\n'} - Known-Malicious Applications: {stats.maliciousApps} - {'\n'} - New Users: {stats.newUsers} - {'\n'} - Sent Messages: {stats.sentTotalMessages || stats.sentMessages} - {'\n'} - Repeated Subject Campaigns: {stats.repeatedSubjects} - {'\n'} - Send Bursts: {stats.sendBursts} - {'\n'} - MFA Devices: {stats.mfaDevices} - {'\n'} - Recent MFA Registrations ({windowDays}d):{' '} - {stats.recentMfaDevices} - {'\n'} - Password Changes: {stats.passwordChanges} - {'\n'} - Trusted Senders: {stats.trustedSenders} - {'\n'} - Blocked Senders: {stats.blockedSenders} - {'\n'} - Safelist Changes: {stats.safelistChanges} - {'\n'} - Sharing Changes: {stats.sharingChanges} - {'\n'} - Anonymous Links: {stats.anonymousLinks} - {'\n'} - Intune Devices: {stats.intuneDevices} - {'\n'} - Recent Intune Enrollments ({windowDays}d):{' '} - {stats.recentIntuneDevices} - {'\n'} - Foreign Sign-ins: {stats.foreignSignIns} ( - {stats.foreignSuccessfulSignIns} successful) - {'\n'} - Foreign Rule/Safelist/Sharing/Mail Activity:{' '} - {stats.foreignActivity} - -
    - -
    - - This report should be retained according to your organization's - document retention policy and regulatory requirements. Typical - retention periods range from 3-7 years depending on applicable - compliance frameworks. Store this document securely with - restricted access as it contains sensitive security information. - -
    - -
    - - For more information about Business Email Compromise and - cybersecurity best practices: - - - - - FBI IC3: Internet Crime Complaint Center (ic3.gov) - - - CISA: Cybersecurity & Infrastructure Security Agency - (cisa.gov) - - - Microsoft Security: Business Email Compromise resources - - -
    -
    - - )} -
    - ) -} - -// Main Button Component -export const BECRemediationReportButton = ({ - userData, - becData, - tenantName, -}) => { +import { useSettings } from '../hooks/use-settings' +import { ServerPdfPane, useServerPdf } from './CippPdf/useServerPdf' + +// The report PDF is rendered server-side (ExecGetBecReportPdf) by the shared CIPPSharp component kit, +// which reads the stored BEC run (BecReports/BecResults) by case id; the button fetches the finished +// PDF as a blob for preview and download. A run is identified by its caseId - userId is the fallback +// the server uses to pick the user's newest completed run. +export const BECRemediationReportButton = ({ userData, becData, tenantName }) => { const [dialogOpen, setDialogOpen] = useState(false) - const [isGenerating, setIsGenerating] = useState(false) - // 'full' = the complete report; 'summary' = the executive pages only, for a C-suite reader. - const [variant, setVariant] = useState('full') + const tenantFilter = useSettings().currentTenant - // Check if we have the necessary data + // Only offer the report once the BEC analysis has completed (its result is what the server reads). const hasData = userData && becData && !becData.Waiting - const brandingSettings = useBrandingSettings() - const variables = useReportVariables() - - const handleOpenDialog = () => { - setDialogOpen(true) - } - - const handleCloseDialog = () => { - setDialogOpen(false) - } + const params = new URLSearchParams({ + tenantFilter: tenantFilter ?? '', + caseId: becData?.CaseId ?? becData?.Run?.CaseId ?? '', + userId: userData?.id ?? userData?.userId ?? '', + userName: userData?.userPrincipalName ?? '', + userDisplayName: userData?.displayName ?? '', + }) + const pdf = useServerPdf({ url: `/api/ExecGetBecReportPdf?${params}`, enabled: dialogOpen }) + const handleOpenDialog = () => setDialogOpen(true) + const handleCloseDialog = () => setDialogOpen(false) + const fileName = `BEC_Report_${(userData?.userPrincipalName || 'user').replace(/[^a-zA-Z0-9]/g, '_')}_${new Date().toISOString().split('T')[0]}.pdf` if (!hasData) { return null // Don't show button if data isn't ready @@ -2321,7 +45,6 @@ export const BECRemediationReportButton = ({ <> - - } - fileName={`BEC_${variant === 'summary' ? 'Summary' : 'Report'}_${userData?.userPrincipalName}_${new Date().toISOString().split('T')[0]}.pdf`} - style={{ textDecoration: 'none' }} + - )} - + Download PDF + - ) -} + ); +} \ No newline at end of file diff --git a/frontend/src/components/CippBaselines/CippBaselineWhatIfReport.jsx b/frontend/src/components/CippBaselines/CippBaselineWhatIfReport.jsx index 2f35fbd942..d042317ccb 100644 --- a/frontend/src/components/CippBaselines/CippBaselineWhatIfReport.jsx +++ b/frontend/src/components/CippBaselines/CippBaselineWhatIfReport.jsx @@ -12,15 +12,7 @@ import { Tooltip, Typography, } from '@mui/material' -import { - Document, - Page, - StyleSheet, - Text, - View, -} from '@react-pdf/renderer' -import { CippPdfPreview } from '../CippPdf/CippPdfPreview' -import { parseCippDate } from '../../utils/parse-cipp-date' +import { ServerPdfPane, useServerPdf } from '../CippPdf/useServerPdf' const operatorLabels = { eq: 'equals', @@ -29,7 +21,8 @@ const operatorLabels = { notStartsWith: 'does not start with', } -// Human-readable summary of a stage's graduation conditions. Shared with the alignment page. +// Human-readable summary of a stage's graduation conditions. Shared with the alignment and +// templates pages; the server-rendered report words them the same way. export const describeStageConditions = (stage) => { if (!stage?.conditions?.length) return 'no conditions configured' const parts = stage.conditions.map((condition) => { @@ -49,352 +42,34 @@ export const describeStageConditions = (stage) => { return parts.join(stage.logic === 'or' ? ' OR ' : ' AND ') } -const accent = '#F77F00' - -const styles = StyleSheet.create({ - page: { - flexDirection: 'column', - backgroundColor: '#FFFFFF', - fontFamily: 'Helvetica', - fontSize: 10, - lineHeight: 1.5, - color: '#2D3748', - padding: 40, - }, - accentBar: { - height: 6, - backgroundColor: accent, - marginBottom: 16, - }, - title: { - fontSize: 22, - fontFamily: 'Helvetica-Bold', - marginBottom: 2, - }, - subtitle: { - fontSize: 11, - color: '#718096', - marginBottom: 18, - }, - sectionTitle: { - fontSize: 14, - fontFamily: 'Helvetica-Bold', - marginTop: 14, - marginBottom: 6, - color: '#1A202C', - }, - statRow: { - flexDirection: 'row', - gap: 8, - marginBottom: 4, - }, - statBox: { - flex: 1, - border: '1 solid #E2E8F0', - borderRadius: 6, - padding: 8, - }, - statValue: { - fontSize: 16, - fontFamily: 'Helvetica-Bold', - color: accent, - }, - statLabel: { - fontSize: 8, - color: '#718096', - textTransform: 'uppercase', - }, - item: { - marginBottom: 6, - paddingLeft: 8, - borderLeft: `2 solid ${accent}`, - }, - itemTitle: { - fontSize: 11, - fontFamily: 'Helvetica-Bold', - }, - itemText: { - fontSize: 9.5, - color: '#4A5568', - }, - meta: { - fontSize: 8.5, - color: '#718096', - }, - footer: { - position: 'absolute', - bottom: 24, - left: 40, - right: 40, - fontSize: 8, - color: '#A0AEC0', - textAlign: 'center', - }, -}) - -const WhatIfDocument = ({ - tenant, - stageStates, - simulatedTemplate, - catalogByName, -}) => { - const generatedAt = new Date().toLocaleDateString('en-US', { - year: 'numeric', - month: 'long', - day: 'numeric', - }) - const changesNow = tenant.rows.filter((row) => - [ - 'Drift', - 'Partially Accepted', - 'Denied - Remediate Pending', - 'Denied - Delete Pending', - ].includes(row.status) - ) - const acceptedRows = tenant.rows.filter((row) => row.status === 'Accepted') - const plannedStages = stageStates.filter((state) => state.nextStage) - - return ( - - - - Baseline What-If Report - - {tenant.displayName} ({tenant.tenantFilter}) - generated {generatedAt} - . No changes have been made; this report previews what applying the - configured standards would change. - - - Where you stand today - - - {tenant.alignedPercentage}% - - Compliant incl. accepted deviations - - - - {tenant.verifiedPercentage}% - Compliant with baseline - - - {changesNow.length} - Changes to make - - - {acceptedRows.length} - Agreed exceptions - - - - - Changes we would make now ({changesNow.length}) - - {changesNow.length === 0 && ( - - Nothing to change - every enforced standard is already in its - expected state. - - )} - {changesNow.map((row) => ( - - {row.standardLabel} - - {catalogByName[row.standardName]?.executiveText ?? - row.standardLabel} - - - {row.impact} - {row.secureScoreImpact > 0 - ? ` - increases Secure Score by up to ${row.secureScoreImpact} points` - : ''} - {row.status?.startsWith('Denied') - ? ' - deviation denied, fix pending' - : ''} - - - ))} - - - Planned future changes (staged rollout) - - {plannedStages.length === 0 && ( - - This tenant is in the final stage of every assigned baseline - no - further staged changes are planned. - - )} - {plannedStages.map((state) => { - const timeCondition = state.nextStage.conditions?.find( - (condition) => condition.type === 'time' - ) - const estimatedAt = timeCondition - ? new Date( - parseCippDate(state.enteredStageAt).getTime() + - timeCondition.days * - (timeCondition.unit === 'weeks' ? 7 : 1) * - 24 * - 60 * - 60 * - 1000 - ).toLocaleDateString('en-US', { - year: 'numeric', - month: 'long', - day: 'numeric', - }) - : null - return ( - - {state.templateName} - - Currently in Stage {state.currentStage} of {state.totalStages} ( - {state.stageName}). Next: Stage {state.currentStage + 1} ( - {state.nextStageName}) - advances when{' '} - {describeStageConditions(state.nextStage)} - {estimatedAt ? `, estimated around ${estimatedAt}` : ''}. - - {state.nextStage.standards.map((standardName) => { - const standard = catalogByName[standardName] - if (!standard) return null - return ( - - {standard.label} - - {standard.executiveText} - - - ) - })} - - ) - })} - - {simulatedTemplate && ( - <> - - What-if: additionally assigning the{' '} - {simulatedTemplate.templateName} baseline - - - {simulatedTemplate.description}. This baseline is not assigned to - the tenant today - below is what assigning it would roll out, - stage by stage. - - {simulatedTemplate.stages.map((stage, index) => ( - - - Stage {index + 1}: {stage.name} - {index === 0 - ? ' - applies immediately' - : ` - advances when ${describeStageConditions(stage)}`} - - {[ - ...new Set(stage.standards.map((key) => key.split('#')[0])), - ].map((name) => { - const standard = catalogByName[name] - if (!standard) return null - const currentRow = tenant.rows.find( - (row) => row.standardName === name - ) - const alreadyAligned = currentRow?.status === 'Compliant' - return ( - - {standard.label} - - {standard.executiveText} - - - {alreadyAligned - ? `No change - already aligned today (configured by ${currentRow.sourceTemplate})` - : 'Would change this tenant when the stage applies'} - - - ) - })} - - ))} - - )} - - {acceptedRows.length > 0 && ( - <> - - Agreed exceptions we will not change ({acceptedRows.length}) - - {acceptedRows.map((row) => ( - - {row.standardLabel} - {row.deviationReason} - - ))} - - )} - - - Generated by CIPP Baselines - what-if preview, no changes were made. - - - - ) -} - -// Button + preview dialog in the style of the Executive Report button. +// Button + preview dialog in the style of the Executive Report button. The PDF is rendered +// server-side (ExecGetBaselineWhatIfReportPdf) by the shared CIPPSharp component kit from the same +// alignment payload this page shows, and re-rendered while the dialog is open whenever the +// simulated baseline changes. export const CippBaselineWhatIfReport = ({ tenant, stageStates, baselines = [], - catalog = [], }) => { const [open, setOpen] = useState(false) const [simulatedTemplate, setSimulatedTemplate] = useState(null) - const catalogByName = Object.fromEntries( - catalog.map((standard) => [standard.name, standard]) - ) - // Baselines not currently rolled out to this tenant can be simulated in the report. const availableTemplates = baselines.filter( (template) => !stageStates.some((state) => state.templateId === template.GUID) ) - const reportDocument = ( - - ) + const pdf = useServerPdf({ + url: '/api/ExecGetBaselineWhatIfReportPdf', + body: { + tenantFilter: tenant.tenantFilter, + simulatedTemplateId: simulatedTemplate?.GUID ?? '', + }, + enabled: open, + }) - const handleDownload = () => { - import('@react-pdf/renderer').then(({ pdf }) => { - pdf(reportDocument) - .toBlob() - .then((blob) => { - const url = URL.createObjectURL(blob) - const link = document.createElement('a') - link.href = url - link.download = `WhatIf_Report_${tenant.displayName.replace(/[^a-zA-Z0-9]/g, '_')}.pdf` - document.body.appendChild(link) - link.click() - document.body.removeChild(link) - URL.revokeObjectURL(url) - }) - }) - } + const fileName = `WhatIf_Report_${String(tenant.displayName).replace(/[^a-zA-Z0-9]/g, '_')}.pdf` return ( <> @@ -438,26 +113,19 @@ export const CippBaselineWhatIfReport = ({ sx={{ maxWidth: 460 }} /> - {open && ( - + - {reportDocument} - - )} + errorText="The report could not be generated. Run the baseline for this tenant and try again." + /> + - + Exec-friendly preview - safe to send to customers. No changes are made. @@ -465,7 +133,8 @@ export const CippBaselineWhatIfReport = ({ @@ -475,7 +144,7 @@ export const CippBaselineWhatIfReport = ({ - ); + ) } export default CippBaselineWhatIfReport diff --git a/frontend/src/components/CippComponents/CippBecEvidenceDownload.jsx b/frontend/src/components/CippComponents/CippBecEvidenceDownload.jsx index dbbb2dd923..7511bdb1cc 100644 --- a/frontend/src/components/CippComponents/CippBecEvidenceDownload.jsx +++ b/frontend/src/components/CippComponents/CippBecEvidenceDownload.jsx @@ -1,16 +1,5 @@ import { useState } from 'react' import { ApiPostCall } from '../../api/ApiCall' -import { useBrandingSettings } from '../CippPdf/useBrandingSettings' -import { useReportVariables } from '../CippPdf/useReportVariables' -import { BECRemediationReportDocument } from '../BECRemediationReportButton' - -const blobToBase64 = (blob) => - new Promise((resolve, reject) => { - const reader = new FileReader() - reader.onloadend = () => resolve(String(reader.result).split(',')[1] || '') - reader.onerror = reject - reader.readAsDataURL(blob) - }) const base64ToBlob = (base64, type) => { const binary = atob(base64) @@ -30,73 +19,28 @@ const tenantOf = (row) => row?.Tenant ?? row?.tenantFilter const upnOf = (row) => row?.UserPrincipalName ?? row?.userPrincipalName /** - * Downloads a case's evidence package WITH the report PDFs: fetches the case results when the caller - * has none, renders the full report and the C-suite summary in-memory (no browser tab), posts them to - * the export endpoint (which collates the ZIP), and saves the ZIP under a name carrying the user and - * case. One hook serves the runs hub (per row) and the case page's export button. + * Downloads a case's evidence package. The export endpoint (ExecBECEvidenceExport) renders the report + * PDFs (the full report and the C-suite summary) server-side and collates the ZIP; the browser just + * requests it by case id and saves it. One hook serves the runs hub (per row) and the case page's + * export button. */ export const useBecEvidenceDownload = () => { - const brandingSettings = useBrandingSettings() - const variables = useReportVariables() const [pendingCaseId, setPendingCaseId] = useState(null) const [lastError, setLastError] = useState(null) const exportCall = ApiPostCall({}) - const download = async (row, providedBecData) => { + const download = async (row) => { const caseId = caseOf(row) const tenantFilter = tenantOf(row) if (!caseId || !tenantFilter || pendingCaseId) return setPendingCaseId(caseId) setLastError(null) try { - let becData = providedBecData - if (!becData) { - const response = await fetch( - `/api/execBECCheck?GUID=${encodeURIComponent(caseId)}&tenantFilter=${encodeURIComponent(tenantFilter)}` - ) - if (!response.ok) throw new Error('Could not load the case results') - becData = await response.json() - } - if (becData?.Waiting || becData?.Error) { - throw new Error('The case is not a completed run') - } - const userData = { - id: row?.UserId ?? row?.userId, - userPrincipalName: upnOf(row), - displayName: row?.DisplayName ?? row?.displayName ?? upnOf(row), - } - - let pdfBase64 = '' - let pdfSummaryBase64 = '' - try { - const { pdf } = await import('@react-pdf/renderer') - const render = async (reportVariant) => { - const blob = await pdf( - - ).toBlob() - return blobToBase64(blob) - } - pdfBase64 = await render('full') - pdfSummaryBase64 = await render('summary') - } catch (renderError) { - console.error( - 'BEC evidence: PDF render failed, exporting without it', - renderError - ) - } - await new Promise((resolve) => { exportCall.mutate( { url: '/api/ExecBECEvidenceExport', - data: { tenantFilter, caseId, pdfBase64, pdfSummaryBase64 }, + data: { tenantFilter, caseId }, }, { onSuccess: (result) => { @@ -107,7 +51,7 @@ export const useBecEvidenceDownload = () => { ) const link = document.createElement('a') link.href = url - link.download = `BEC_Evidence_${safe(upnOf(row) || userData.id)}_${safe(caseId)}.zip` + link.download = `BEC_Evidence_${safe(upnOf(row) || row?.UserId || caseId)}_${safe(caseId)}.zip` document.body.appendChild(link) link.click() document.body.removeChild(link) diff --git a/frontend/src/components/CippComponents/CippBecEvidenceExportButton.jsx b/frontend/src/components/CippComponents/CippBecEvidenceExportButton.jsx index 3803af8a8c..7e38cc61ec 100644 --- a/frontend/src/components/CippComponents/CippBecEvidenceExportButton.jsx +++ b/frontend/src/components/CippComponents/CippBecEvidenceExportButton.jsx @@ -3,15 +3,14 @@ import { CippIcons } from '../../utils/icon-registry' import { useBecEvidenceDownload } from './CippBecEvidenceDownload' /** - * Export evidence from the case page: renders both report PDFs in the browser, posts them to the - * backend which collates the package into a ZIP, and downloads it. There is no results panel; any - * error shows in the button's hover tooltip, so the control stays a single button in the action row. + * Export evidence from the case page: requests the evidence package (the backend renders both report + * PDFs server-side and collates the package into a ZIP) and downloads it. There is no results panel; + * any error shows in the button's hover tooltip, so the control stays a single button in the action row. */ export const CippBecEvidenceExportButton = ({ tenantFilter, caseId, userData, - becData, }) => { const { download, busy, lastError } = useBecEvidenceDownload() @@ -19,7 +18,7 @@ export const CippBecEvidenceExportButton = ({ ? 'Building evidence package…' : lastError ? `Last export failed: ${lastError}` - : 'Renders both report PDFs and packages the case evidence as a ZIP' + : 'Packages the case evidence (with the report PDFs) as a ZIP' return ( @@ -30,16 +29,13 @@ export const CippBecEvidenceExportButton = ({ color={lastError ? 'error' : 'primary'} startIcon={} onClick={() => - download( - { - CaseId: caseId, - Tenant: tenantFilter, - UserId: userData?.id, - UserPrincipalName: userData?.userPrincipalName, - DisplayName: userData?.displayName, - }, - becData - ) + download({ + CaseId: caseId, + Tenant: tenantFilter, + UserId: userData?.id, + UserPrincipalName: userData?.userPrincipalName, + DisplayName: userData?.displayName, + }) } disabled={busy || !caseId} > diff --git a/frontend/src/components/CippPdf/CippBrandingReportPreview.jsx b/frontend/src/components/CippPdf/CippBrandingReportPreview.jsx index 70f05e118e..e1390e53cf 100644 --- a/frontend/src/components/CippPdf/CippBrandingReportPreview.jsx +++ b/frontend/src/components/CippPdf/CippBrandingReportPreview.jsx @@ -1,122 +1,42 @@ -import { useMemo } from 'react' -import { CippPdfPreview } from './CippPdfPreview' -import { ExecutiveReportDocument } from '../ExecutiveReportButton' -import { ShadowAIReportDocument } from '../ShadowAIReportButton' -import { BECRemediationReportDocument } from '../BECRemediationReportButton' -import { SharingReportDocument } from './SharingReportButton' -import { PermissionsReportDocument } from './PermissionsReportButton' -import { MailFlowReportDocument } from './MailFlowReportButton' -import { ReportBuilderDocument } from '../ReportBuilder/ReportBuilderPDF' -import { SAMPLE_DATA_BY_REPORT, SAMPLE_TENANT_NAME } from './previewSampleData' -import { useReportVariables } from './useReportVariables' - -// Renders a real report — the same document a client would receive — against sample data, so the -// branding being edited can be checked on every page rather than on a mock of the cover. -// -// The sample data comes from previewSampleData, which nothing else may import. A report that has no -// real data still renders its empty states; this is the one place invented numbers are allowed. - -/** - * Every section on, so the preview shows the fullest version of each report. Whichever sections an - * operator later turns off, the branding they are choosing here still has to work. - * - * `securityStandards` is the one exception, and it is not a choice made here: the executive report - * renders the standards page only when drift compliance is *off*, and the drift pages only when - * standards is off (see the guards in ExecutiveReportButton). The two are alternative views of the - * same data, so no single document can contain both. Drift is the one shown because it is three - * pages to the standards page's one, and exercises more of the design — the donut, the stat row, - * the deviations table and the grouped bullet lists. - */ -const EXECUTIVE_SECTIONS = { - executiveSummary: true, - securityStandards: false, - driftCompliance: true, - secureScore: true, - licenseManagement: true, - deviceManagement: true, - conditionalAccess: true, - infographics: true, - shadowAI: true, -} - -/** - * Build the report document for a given type. Pure, and exported so it can be rendered to a real - * PDF in tests without mounting the viewer. - */ -export const buildPreviewDocument = (reportType, brandingSettings, variables) => { - const sample = SAMPLE_DATA_BY_REPORT[reportType] ?? SAMPLE_DATA_BY_REPORT.executive - const shared = { brandingSettings, tenantName: SAMPLE_TENANT_NAME, variables } - - switch (reportType) { - case 'reportBuilder': - return ( - - ) - case 'shadowAI': - return - case 'bec': - return ( - - ) - case 'sharing': - return - case 'permissions': - return - case 'mailFlow': - return - default: - return ( - - ) - } +import { useEffect, useMemo, useState } from 'react' +import { useSettings } from '../../hooks/use-settings' +import { ServerPdfPane, useServerPdf } from './useServerPdf' + +// Renders a real report server-side - the same document a client would receive - against sample +// data, so the branding being edited can be checked on every page rather than on a mock of the +// cover. The sample data lives beside the renderer (Config/ReportSampleData.json). + +// A colour picker drag changes the branding many times a second; the render is asked for once the +// values have settled. +const useSettled = (value, ms) => { + const [settled, setSettled] = useState(value) + useEffect(() => { + const timer = setTimeout(() => setSettled(value), ms) + return () => clearTimeout(timer) + }, [value, ms]) + return settled } -const CippBrandingReportPreview = ({ reportType = 'executive', brandingSettings }) => { - // Resolved against the selected tenant, so a footer being written with %cippurl% or a custom - // variable previews the value it will actually print rather than the token. - const variables = useReportVariables() - - // Rebuilding the document on every keystroke of a colour picker would re-run the whole PDF - // layout; keying on the branding values that actually reach the page keeps that to real changes. - const document = useMemo( - () => buildPreviewDocument(reportType, brandingSettings, variables), - // eslint-disable-next-line react-hooks/exhaustive-deps -- rebuild only on values the PDF reads - [ - reportType, - variables, - brandingSettings?.colour, - brandingSettings?.secondaryColour, - brandingSettings?.logo, - brandingSettings?.coverImage, - brandingSettings?.coverImageId, - brandingSettings?.coverStock, - brandingSettings?.footerText, - brandingSettings?.coverFooterText, - brandingSettings?.showFooter, - brandingSettings?.showPageNumbers, - brandingSettings?.watermarkText, - brandingSettings?.watermarkEnabled, - ] +const CippBrandingReportPreview = ({ + reportType = 'executive', + brandingSettings, +}) => { + // Resolved against the selected tenant, so a footer written with %cippurl% or a custom variable + // previews the value it will print rather than the token. + const tenantFilter = useSettings().currentTenant + const requestKey = useSettled( + JSON.stringify({ reportType, branding: brandingSettings, tenantFilter }), + 600 ) + const body = useMemo(() => JSON.parse(requestKey), [requestKey]) + const pdf = useServerPdf({ url: '/api/ExecPreviewBrandingReportPdf', body }) return ( - - {document} - + errorText="The preview could not be rendered." + /> ) } diff --git a/frontend/src/components/CippPdf/CippPdfPreview.jsx b/frontend/src/components/CippPdf/CippPdfPreview.jsx deleted file mode 100644 index af46101ee3..0000000000 --- a/frontend/src/components/CippPdf/CippPdfPreview.jsx +++ /dev/null @@ -1,163 +0,0 @@ -import { Box, Button, CircularProgress, Stack, Typography } from '@mui/material' -import { CippIcons } from '../../utils/icon-registry' -import { PDFViewer, usePDF } from '@react-pdf/renderer' -import { useIsMobileLayout } from '../../hooks/use-breakpoint' - -const formatSize = (bytes) => { - if (!bytes && bytes !== 0) return null - if (bytes < 1024 * 1024) return `${Math.max(1, Math.round(bytes / 1024))} KB` - return `${(bytes / (1024 * 1024)).toFixed(1)} MB` -} - -/** - * The mobile half. `PDFViewer` is an iframe pointed at a blob URL, and iOS Safari renders a - * PDF in an iframe as a fixed first-page preview: it does not scroll, at any iframe height. - * No amount of CSS fixes that, so below md we stop pretending to embed the document and hand - * it to the platform viewer, which scrolls, pinch-zooms, shares and prints. - * - * Both actions are real anchors rather than window.open in a click handler — a programmatic - * open from an async callback is what mobile popup blockers exist to stop. - */ -const MobileHandoff = ({ document, fileName, title, showDownload }) => { - const [instance] = usePDF({ document }) - - if (instance.loading) { - return ( - - - - Building report… - - - ); - } - - if (instance.error || !instance.url) { - return ( - - Report could not be generated - - {instance.error ? String(instance.error) : 'No document was produced.'} - - - ); - } - - const size = formatSize(instance.blob?.size) - - return ( - - - - - - - - {title ?? 'Report'} - - {size && ( - - PDF · {size} - - )} - - - - - {/* Off by default: six of the eight hosts already put a Download in their dialog - actions, and two of them side by side is what this looked like on a phone. */} - {showDownload && ( - - )} - - - ); -} - -/** - * Drop-in for ``: identical on desktop, a platform handoff below md. - * - * `title` labels the card and `fileName` names the download; both are mobile-only, as is - * `showDownload` — pass it only where the host has no download action of its own. `viewerKey` - * is applied to the desktop iframe alone: one caller remounts it per render to dodge a - * react-pdf error, and doing that on mobile would rebuild the blob every render. - */ -export const CippPdfPreview = (props) => { - const { children, fileName, title, viewerKey, showDownload = false, ...viewerProps } = props - const isMobile = useIsMobileLayout() - - if (isMobile) { - return ( - - ) - } - - return ( - - {children} - - ) -} - -export default CippPdfPreview diff --git a/frontend/src/components/CippPdf/MailFlowReportButton.jsx b/frontend/src/components/CippPdf/MailFlowReportButton.jsx index 83576f9177..065057ed48 100644 --- a/frontend/src/components/CippPdf/MailFlowReportButton.jsx +++ b/frontend/src/components/CippPdf/MailFlowReportButton.jsx @@ -3,7 +3,6 @@ import { CippIcons } from '../../utils/icon-registry' import { Box, Button, - CircularProgress, Dialog, DialogActions, DialogContent, @@ -12,369 +11,24 @@ import { Tooltip, Typography, } from '@mui/material' -import { PDFDownloadLink } from '@react-pdf/renderer' -import { CippPdfPreview } from './CippPdfPreview' -import { - AlertBox, - Bold, - BulletList, - ClearBox, - ContentPage, - CoverMeta, - DataTable, - DonutChart, - InfoBox, - Paragraph, - REPORT_COLOURS, - ReportDocument, - Section, - StatRow, - TrendChart, - severityColour, -} from './index' -import { useReportVariables } from './useReportVariables' -import { useBrandingSettings } from './useBrandingSettings' - -const nz = (value) => Number(value ?? 0) -const num = (value) => nz(value).toLocaleString() -const pct = (part, whole) => (whole > 0 ? Math.round((part / whole) * 1000) / 10 : 0) - -// Get-MailFlowStatusReport event types, in the order they read as a funnel: delivered mail first, -// then the things that stopped mail, worst last. -const DISPOSITIONS = [ - { key: 'GoodMail', label: 'Good mail' }, - { key: 'TransportRules', label: 'Transport rules' }, - { key: 'SpamDetections', label: 'Spam' }, - { key: 'EdgeBlockSpam', label: 'Edge blocked spam' }, - { key: 'EmailPhish', label: 'Phish' }, - { key: 'EmailMalware', label: 'Malware' }, -] - -/** - * Grades mail hygiene from the share of the window's mail that each threat class accounts for. - * - * Shares rather than counts, because a raw count of blocked phish means nothing without the volume - * it was drawn from. Malware and phish are weighted far harder than spam: spam is nuisance traffic - * every tenant carries, while a targeted-payload rate above roughly one percent of all mail means - * the organisation is being actively worked on rather than incidentally scraped. - */ -const assessHygiene = (totals, totalMail) => { - if (totalMail <= 0) return { level: 'Good', severity: 'low' } - const targeted = pct(nz(totals.EmailMalware) + nz(totals.EmailPhish), totalMail) - const spam = pct(nz(totals.SpamDetections) + nz(totals.EdgeBlockSpam), totalMail) - - if (targeted > 1 || spam > 25) return { level: 'Attention Needed', severity: 'high' } - if (targeted > 0.25 || spam > 10) return { level: 'Fair', severity: 'medium' } - return { level: 'Good', severity: 'low' } -} - -// Exported so the branding preview can render this report against sample data, and so tests can -// render it to a real PDF. -export const MailFlowReportDocument = ({ - mailFlowData, - brandingSettings, - tenantName, - generatedOn, - variables, -}) => { - const days = nz(mailFlowData?.days) || 14 - const totals = mailFlowData?.totals ?? {} - const directionTotals = mailFlowData?.directionTotals ?? {} - const daily = mailFlowData?.daily ?? [] - const topSenders = mailFlowData?.topSenders ?? [] - const topSpamRecipients = mailFlowData?.topSpamRecipients ?? [] - - const totalMail = DISPOSITIONS.reduce((sum, item) => sum + nz(totals[item.key]), 0) - const goodMailPct = pct(nz(totals.GoodMail), totalMail) - const phish = nz(totals.EmailPhish) - const malware = nz(totals.EmailMalware) - const threats = phish + malware - const transportRules = nz(totals.TransportRules) - - const hygiene = assessHygiene(totals, totalMail) - const hygieneColour = severityColour(hygiene.severity) - - const dayLabel = (value) => - value ? new Date(value).toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) : '' - - const volumeSeries = daily.map((row) => ({ - label: dayLabel(row.date), - value: DISPOSITIONS.reduce((sum, item) => sum + nz(row[item.key]), 0), - })) - - const directionSeries = [ - { label: 'Inbound', value: nz(directionTotals.Inbound) }, - { label: 'Outbound', value: nz(directionTotals.Outbound) }, - { label: 'Intra-org', value: nz(directionTotals.IntraOrg) }, - ] - - // Numbered after filtering, so a report that drops a conditional action still counts from one. - const priorityActions = [ - threats > 0 && { - label: 'Review anti-phishing and anti-malware policy strength.', - text: `${num(threats)} messages were blocked as phishing or malware in this window. Confirm the tenant is on the current preset security policies, that impersonation protection lists the people who would actually be impersonated, and that Safe Links and Safe Attachments cover every mailbox rather than a pilot group.`, - }, - topSpamRecipients.length > 0 && { - label: 'Give the most-targeted users stronger protection.', - text: 'The recipients listed in this report absorb a disproportionate share of unwanted mail. Priority accounts, tighter quarantine policy and a short conversation about what they are receiving cost little and are aimed exactly where the traffic is going.', - }, - { - label: 'Verify SPF, DKIM and DMARC are published and enforcing.', - text: 'These records decide whether mail claiming to be from the domain is accepted elsewhere. A DMARC policy left at p=none reports abuse without stopping it, which means the organisation can be impersonated to its own customers regardless of how well inbound filtering performs.', - }, - transportRules > 0 && { - label: 'Audit the transport rules acting on mail.', - text: `Transport rules handled ${num(transportRules)} messages here. Rules accumulate, outlive the reason they were written, and silently override filtering decisions — confirm each one is still wanted and that none bypasses protection for a sender that no longer needs the exception.`, - }, - ] - .filter(Boolean) - .map((item, index) => ({ ...item, marker: `${index + 1}.` })) - - const dispositionRows = DISPOSITIONS.map((item) => ({ - disposition: item.label, - messages: num(totals[item.key]), - share: `${pct(nz(totals[item.key]), totalMail)}%`, - })) - - return ( - - } - > - {/* EXECUTIVE SUMMARY */} - -
    - - Every message entering or leaving the organisation is given a disposition — delivered, - held by a transport rule, filtered as spam, or blocked as phishing or malware. Those - dispositions are the clearest single measure of what the mail environment is being asked - to handle, because they count what the filters actually did rather than what they are - configured to do. This report covers the last {days} days of mail flow at{' '} - {tenantName}. - - - 0 ? REPORT_COLOURS.warning : undefined, - }, - { - value: num(malware), - label: 'Malware Blocked', - colour: malware > 0 ? REPORT_COLOURS.danger : undefined, - }, - ]} - /> - - - {hygiene.level === 'Attention Needed' && - 'Threat traffic is a material share of total mail. At this rate the organisation is being targeted rather than incidentally caught by bulk campaigns, and the filters are absorbing volume that protection policy and user awareness should be reducing at source. Treat the recommendations as current work.'} - {hygiene.level === 'Fair' && - 'Threats are being caught at a level that is normal for an organisation of this profile, but not negligible. The filtering is working; the value now is in checking which users absorb most of it and whether their protection matches their exposure.'} - {hygiene.level === 'Good' && - 'Threat traffic is a small fraction of total mail and is being stopped before delivery. Nothing here needs action beyond keeping the review cadence, since a change in this profile is usually the first visible sign of a campaign starting.'} - -
    - -
    - - Figures come from Microsoft's mail flow status report for the tenant, aggregated as daily - counts per disposition and direction. It is a count of messages, not a record of them: - individual senders, subjects and recipients are not part of this data set, and a message - appears once under the disposition that was applied to it. - - - A blocked message is a filter working, not an incident. Nothing here indicates that a - threat reached a user or that an account was compromised — that requires message trace - and sign-in data, which are reviewed separately. Equally, a clean result does not prove - nothing got through; it proves nothing was recognised. - -
    -
    - - {/* VOLUME & DISPOSITIONS */} - -
    - - Total messages handled per day across every disposition. Steady volume with occasional - peaks is normal; a sustained step change usually reflects a business event — a campaign, - an onboarding, a new integration — and is worth being able to explain. - - -
    - -
    - - The share each disposition accounts for matters more than the counts. Good mail should - dominate; anything else growing as a proportion is the signal. - - -
    - -
    - - Inbound, outbound and internal traffic in proportion. An unusual outbound share is the - one to watch: mail leaving in volume that the business did not generate is how a - compromised mailbox or an unsecured relay first shows up in these figures. - - -
    -
    - - {/* SENDERS & SPAM TARGETS */} - -
    - - The heaviest senders are normally the ones you would expect — shared mailboxes, - ticketing systems, scan-to-email devices, marketing platforms. What is worth a second - look is a name that does not belong on that list. A user account sending at machine - volume is either an unmanaged automation nobody documented, or a mailbox someone else is - using. - - {topSenders.length > 0 ? ( - ({ - name: row.Name ?? row.name ?? 'Unknown', - count: num(row.Count ?? row.count), - }))} - limit={10} - /> - ) : ( - - Microsoft returned no top-sender breakdown for this window. - - )} -
    - -
    - - Unwanted mail does not spread evenly. A handful of addresses — usually the published - ones, and the people whose names appear on the website — absorb most of it, and those - same addresses are the ones a targeted attempt will use. Concentration here identifies - exactly who benefits most from stricter policy and from being asked to be careful. - - {topSpamRecipients.length > 0 ? ( - ({ - name: row.Name ?? row.name ?? 'Unknown', - count: num(row.Count ?? row.count), - }))} - limit={10} - /> - ) : ( - - No recipient stands out as absorbing spam over this window. - - )} -
    -
    - - {/* RECOMMENDATIONS */} - -
    - - Ordered by what this window's data actually shows, rather than by a generic checklist. - - -
    - -
    - -
    -
    -
    - ) -} +import { useSettings } from '../../hooks/use-settings' +import { ServerPdfPane, useServerPdf } from './useServerPdf' +// The report PDF is rendered server-side (ExecGetMailFlowReportPdf) by the shared CIPPSharp component +// kit, which re-gathers and aggregates the mail flow data; the button fetches the finished PDF as a +// blob for preview and download. export const MailFlowReportButton = ({ mailFlowData, tenantName, disabled = false }) => { const [dialogOpen, setDialogOpen] = useState(false) - const [generatedOn, setGeneratedOn] = useState('') - const brandingSettings = useBrandingSettings() - const variables = useReportVariables() + const tenantFilter = useSettings().currentTenant + const days = Number(mailFlowData?.days) || 14 const hasData = Object.keys(mailFlowData?.totals ?? {}).length > 0 - - const handleOpen = () => { - setGeneratedOn( - new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) - ) - setDialogOpen(true) - } - - const documentNode = ( - - ) + const pdf = useServerPdf({ + url: `/api/ExecGetMailFlowReportPdf?tenantFilter=${encodeURIComponent(tenantFilter)}&days=${days}`, + enabled: dialogOpen, + }) + const handleOpen = () => setDialogOpen(true) + const handleClose = () => setDialogOpen(false) + const fileName = `Mail_Flow_Report_${(tenantName || 'report').replace(/[^a-zA-Z0-9]/g, '_')}_${new Date().toISOString().split('T')[0]}.pdf` return ( <> @@ -394,7 +48,7 @@ export const MailFlowReportButton = ({ mailFlowData, tenantName, disabled = fals setDialogOpen(false)} + onClose={handleClose} maxWidth="lg" fullWidth slotProps={{ @@ -411,40 +65,28 @@ export const MailFlowReportButton = ({ mailFlowData, tenantName, disabled = fals Mail Flow Report Preview - setDialogOpen(false)} size="small"> + - {dialogOpen && ( - - {documentNode} - - )} + - - Close + - )} - + Download PDF + diff --git a/frontend/src/components/CippPdf/PermissionsReportButton.jsx b/frontend/src/components/CippPdf/PermissionsReportButton.jsx index 26957d02cc..560fd92c58 100644 --- a/frontend/src/components/CippPdf/PermissionsReportButton.jsx +++ b/frontend/src/components/CippPdf/PermissionsReportButton.jsx @@ -3,7 +3,6 @@ import { CippIcons } from '../../utils/icon-registry' import { Box, Button, - CircularProgress, Dialog, DialogActions, DialogContent, @@ -12,435 +11,22 @@ import { Tooltip, Typography, } from '@mui/material' -import { PDFDownloadLink } from '@react-pdf/renderer' -import { CippPdfPreview } from './CippPdfPreview' -import { - AlertBox, - Bold, - BulletList, - ClearBox, - ContentPage, - CoverMeta, - DataTable, - InfoBox, - Paragraph, - REPORT_COLOURS, - ReportDocument, - Section, - StatRow, - severityColour, -} from './index' -import { useReportVariables } from './useReportVariables' -import { useBrandingSettings } from './useBrandingSettings' - -const nz = (value) => Number(value ?? 0) -const plural = (count, singular, pluralForm) => - `${count} ${count === 1 ? singular : (pluralForm ?? `${singular}s`)}` - -/** - * Grades permission exposure. A tenant-wide claim dominates because it grants access to the whole - * organisation in one entry, regardless of what the site's membership says. - */ -const assessExposure = (summary) => { - let score = 0 - if (nz(summary.broadClaimGrants) > 0) score += 5 - if (nz(summary.externalGrants) > 0) score += 3 - if (nz(summary.directFullControlGrants) > 0) score += 2 - if (nz(summary.uniquePermissionLibraries) > 0) score += 1 - - if (score >= 7) return { level: 'High', severity: 'high' } - if (score >= 3) return { level: 'Medium', severity: 'medium' } - return { level: 'Low', severity: 'low' } -} - -const CLAIM_LABELS = { - Everyone: 'Everyone (includes external users)', - EveryoneExceptExternal: 'Everyone except external users', - AllUsers: 'All Users', -} - -// Exported so the branding preview can render this report against sample data, and so tests can -// render it to a real PDF. -export const PermissionsReportDocument = ({ - permissionsData, - brandingSettings, - tenantName, - generatedOn, - variables, -}) => { - const summary = permissionsData?.summary ?? {} - const assignments = permissionsData?.assignments ?? [] - const skippedSites = permissionsData?.skippedSites ?? [] - - const exposure = assessExposure(summary) - const exposureColour = severityColour(exposure.severity) - - // Placeholder rows carry no principal, and Limited Access grants nothing on its own — both are - // excluded from the counts, so they have to be excluded here or the tables contradict them. - const realAssignments = assignments.filter((row) => row.principalId && !row.isSystemManaged) - const broadClaimRows = realAssignments.filter((row) => row.broadClaim) - const externalGrantRows = realAssignments.filter((row) => row.isGuest === true) - const fullControlRows = realAssignments.filter( - (row) => row.permissionLevel === 'Full Control' && row.principalType !== 'SharePoint Group' - ) - const libraryRows = realAssignments.filter((row) => row.scope === 'Library') - - // getAllSites does not always return a display name, so fall back to the URL. - const siteLabel = (row) => row.siteName || row.siteUrl || 'Unnamed site' - const scopeLabel = (row) => - row.scope === 'Library' ? `${siteLabel(row)} / ${row.libraryTitle}` : siteLabel(row) - - return ( - - } - > - {/* EXECUTIVE SUMMARY */} - - -
    - - Permissions are set by administrators on a site or document library and decide who is - structurally allowed in. They change rarely, which is what makes them worth auditing: a - permission granted for one project stays in place indefinitely, and a permission granted - to the whole organisation looks identical to one granted to a single team until somebody - reads it. This report covers {tenantName}. - - - 0 ? REPORT_COLOURS.danger : undefined, - }, - { - value: nz(summary.externalGrants), - label: 'External Grants', - colour: nz(summary.externalGrants) > 0 ? REPORT_COLOURS.warning : undefined, - }, - { - value: nz(summary.directFullControlGrants), - label: 'Direct Full Control', - colour: - nz(summary.directFullControlGrants) > 0 ? REPORT_COLOURS.warning : undefined, - }, - { - value: nz(summary.uniquePermissionLibraries), - label: 'Detached Libraries', - }, - ]} - /> - - - {exposure.level === 'High' && - 'Content is reachable by people it was never meant for. A tenant-wide grant is present, which opens the content to the entire organisation regardless of who the site membership says should have it — and it is the most common reason material turns up unexpectedly in search results and AI assistant answers. Treat the findings below as immediate remediation work.'} - {exposure.level === 'Medium' && - 'Access extends past the intended audience in places. Each finding below is individually manageable, but each one widens what a single compromised account reaches.'} - {exposure.level === 'Low' && - 'Permissions broadly match what the structure intends. No tenant-wide grants were found. Continue reviewing periodically, particularly after site or library changes.'} - -
    - -
    - - {nz(summary.sitesScanned)} SharePoint sites and {nz(summary.librariesScanned)} document - libraries were read, producing {nz(summary.totalAssignments)} permission assignments. - Data is taken from the last completed sync, not read live. - - - Permissions are reported as grant paths, not effective access — a group holding a - permission is one entry and its members are not expanded, so a person may hold access - that shows here only via their group. Permissions on individual folders and files are - not enumerated. OneDrive personal sites are out of scope. Access handed out by sharing - link is a separate path, covered by the Sharing Report. - - {skippedSites.length > 0 ? ( - - These sites could not be read on the most recent scan. Where a site was read - successfully before, its earlier results are still shown and are as old as that scan; - a site never read successfully contributes nothing. Either way, an absence of findings - for these sites is not evidence of good configuration. - - ) : null} -
    -
    - - {/* FINDINGS */} - - -
    - - SharePoint offers a handful of special audiences — Everyone, Everyone except external - users, and All Users — that resolve to the whole organisation rather than to named - people. A library carrying one is readable by every employee no matter what the site's - membership says, and it is the single most common cause of data appearing in search - results or AI assistant answers where it was not expected. - - {broadClaimRows.length > 0 ? ( - <> - - Confirm the content is genuinely meant to be organisation-wide. If not, replace the - grant with a specific group — one edit removes access for everyone who was never - meant to have it. - - ({ - location: scopeLabel(row), - audience: CLAIM_LABELS[row.broadClaim] ?? row.broadClaim, - level: row.permissionLevel, - }))} - /> - - ) : ( - - No site or library grants access to Everyone, Everyone except external users, or All - Users. - - )} -
    - -
    - - Guest accounts holding permissions retain that access until somebody removes it — unlike - a sharing link, nothing expires it. Guests from finished projects are a common source of - standing access nobody is reviewing. - - {externalGrantRows.length > 0 ? ( - <> - - Verify each guest still needs access and that the relationship is current. - - ({ - location: scopeLabel(row), - identity: row.email || row.title || row.loginName, - level: row.permissionLevel, - }))} - /> - - ) : ( - - No guest or external identity holds a permission on a scanned site or library. - - )} -
    -
    - - {/* FINDINGS CONTINUED */} - - -
    - - Every site has an Owners group that holds Full Control by design, and that is expected. - Full Control granted straight to a person or a directory group is different: it sits - outside the membership structure, so it is not removed when someone leaves a team and it - is easy to overlook when reviewing who administers a site. - - {fullControlRows.length > 0 ? ( - <> - - Move these into the site's Owners group where the access is legitimate, so - membership changes take effect automatically. - - ({ - location: scopeLabel(row), - principal: row.title || row.email || row.loginName, - type: row.principalType, - }))} - /> - - ) : ( - - No user or directory group holds Full Control outside a site's Owners group. - - )} -
    - -
    - - A library normally inherits from its site, so managing the site manages everything in - it. A detached library keeps its own permissions and later site-level changes no longer - reach it. That is legitimate when deliberate and a blind spot when not — removing - somebody from the site does not remove them here. - - {nz(summary.uniquePermissionLibraries) > 0 ? ( - - {nz(summary.uniquePermissionLibraries)} of {nz(summary.librariesScanned)} libraries no - longer inherit from their site. Their assignments are listed in the appendix. Review - whether each detachment was intentional and is still needed. - - ) : ( - - Every scanned library takes its permissions from its site, so site-level access - management covers them all. - - )} -
    -
    - - {/* RECOMMENDATIONS */} - - -
    - - Ordered by how much access each removes relative to the effort involved. - - -
    - -
    - -
    -
    - - {/* APPENDIX */} - - -
    - ({ - site: siteLabel(row), - library: row.libraryTitle, - principal: row.title || row.email || row.loginName, - level: row.permissionLevel, - }))} - limit={40} - emptyText="No libraries hold their own permissions." - /> -
    -
    -
    - ) -} +import { useSettings } from '../../hooks/use-settings' +import { ServerPdfPane, useServerPdf } from './useServerPdf' +// The report PDF is rendered server-side (ExecGetPermissionsReportPdf) by the shared CIPPSharp +// component kit; the button fetches the finished PDF as a blob for preview and download. export const PermissionsReportButton = ({ permissionsData, tenantName }) => { const [dialogOpen, setDialogOpen] = useState(false) - const [generatedOn, setGeneratedOn] = useState('') - const brandingSettings = useBrandingSettings() - const variables = useReportVariables() + const tenantFilter = useSettings().currentTenant const hasData = !!permissionsData?.summary - - const handleOpen = () => { - setGeneratedOn( - new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) - ) - setDialogOpen(true) - } - - const documentNode = ( - - ) + const pdf = useServerPdf({ + url: `/api/ExecGetPermissionsReportPdf?tenantFilter=${encodeURIComponent(tenantFilter)}`, + enabled: dialogOpen, + }) + const handleOpen = () => setDialogOpen(true) + const handleClose = () => setDialogOpen(false) + const fileName = `Permissions_Report_${(tenantName || 'report').replace(/[^a-zA-Z0-9]/g, '_')}_${new Date().toISOString().split('T')[0]}.pdf` return ( <> @@ -460,7 +46,7 @@ export const PermissionsReportButton = ({ permissionsData, tenantName }) => { setDialogOpen(false)} + onClose={handleClose} maxWidth="lg" fullWidth slotProps={{ @@ -477,40 +63,28 @@ export const PermissionsReportButton = ({ permissionsData, tenantName }) => { Permissions Report Preview - setDialogOpen(false)} size="small"> + - {dialogOpen && ( - - {documentNode} - - )} + - - Close + - )} - + Download PDF + diff --git a/frontend/src/components/CippPdf/ReportDocument.jsx b/frontend/src/components/CippPdf/ReportDocument.jsx deleted file mode 100644 index f5810cd1ea..0000000000 --- a/frontend/src/components/CippPdf/ReportDocument.jsx +++ /dev/null @@ -1,125 +0,0 @@ -import { Document } from '@react-pdf/renderer' -import { ReportProvider } from './reportContext' -import { applyFooterText, createReportTheme } from './reportTheme' -import { createReportStyles, DEFAULT_PAGE_SETUP } from './reportPdfStyles' -import { CoverPage } from './reportPdfPrimitives' -import { resolveCoverImage } from './resolveCoverImage' - -/** - * The whole scaffold of a report: theme, styles, cover page, and the context every primitive reads. - * - * This exists so that writing a new report is writing its *content*. Before it, each report opened - * with the same forty lines — build the theme, build the styles, resolve the cover image, assemble - * the date string, hand-roll a cover page, and thread `styles`/`theme` into everything — which is - * why six reports had six slightly different covers and six ways of deciding a footer. - * - * A report now looks like: - * - * - * - *
    - *
    - *
    - * - * Everything below it — pages, headers, footers, watermark, page numbers, colours — is decided - * centrally and needs no argument. - */ -export const ReportDocument = ({ - brandingSettings, - tenantName, - reportName, - generatedOn, - - // Cover. Pass `cover={false}` for a report that opens straight into content. - cover = true, - coverLabel, - coverTitle, - coverAccent, - coverTitleFontSize, - coverSubtitle, - coverTenant, - coverMeta, - coverFallbackImage, - coverFooterNote, - - // The report's own footer wording, used when branding configures none. - footerLabel, - - // Resolved CIPP variables, from `useReportVariables`. - variables: cippVariables, - - size = DEFAULT_PAGE_SETUP.size, - orientation = DEFAULT_PAGE_SETUP.orientation, - - children, -}) => { - const theme = createReportTheme(brandingSettings) - const styles = createReportStyles(theme) - const logo = brandingSettings?.logo || null - const coverImage = resolveCoverImage(brandingSettings, coverFallbackImage) - - const date = - generatedOn ?? - new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) - - // What every `%variable%` resolves to in this report. The report's own three override CIPP's, - // so `%tenantname%` still resolves before the variables fetch lands. - const variables = { - ...cippVariables, - tenantname: tenantName || 'Organization', - reportname: reportName || '', - reportdate: date, - } - - const context = { theme, styles, variables, logo, footerLabel, size, orientation, date } - - // Branding's cover note wins; a report's own wording is the fallback. Leave the prop undefined - // when neither is set so CoverPage's default confidentiality line still appears. Variables are - // filled here so a configured `%tenantname%` note resolves the same way the page footer does. - const coverNoteTemplate = theme.coverFooterText || coverFooterNote - const coverNote = coverNoteTemplate - ? applyFooterText(coverNoteTemplate, variables) - : undefined - - return ( - - - {cover ? ( - - {coverMeta} - - ) : null} - {children} - - - ) -} - -export default ReportDocument diff --git a/frontend/src/components/CippPdf/SharingReportButton.jsx b/frontend/src/components/CippPdf/SharingReportButton.jsx index 59df3dfb54..e96298e521 100644 --- a/frontend/src/components/CippPdf/SharingReportButton.jsx +++ b/frontend/src/components/CippPdf/SharingReportButton.jsx @@ -3,7 +3,6 @@ import { CippIcons } from '../../utils/icon-registry' import { Box, Button, - CircularProgress, Dialog, DialogActions, DialogContent, @@ -12,426 +11,22 @@ import { Tooltip, Typography, } from '@mui/material' -import { PDFDownloadLink } from '@react-pdf/renderer' -import { CippPdfPreview } from './CippPdfPreview' -import { - AlertBox, - Bold, - BulletList, - ClearBox, - ContentPage, - CoverMeta, - DataTable, - InfoBox, - Paragraph, - REPORT_COLOURS, - ReportDocument, - Section, - StatRow, - severityColour, -} from './index' -import { useReportVariables } from './useReportVariables' -import { useBrandingSettings } from './useBrandingSettings' - -const nz = (value) => Number(value ?? 0) -const joinList = (value) => (Array.isArray(value) ? value.join(', ') : (value ?? '')) -const plural = (count, singular, pluralForm) => - `${count} ${count === 1 ? singular : (pluralForm ?? `${singular}s`)}` - -/** - * Grades sharing exposure. An anonymous link that also allows editing dominates because it is the - * only combination that lets an unidentified person change content, with no record of who did it. - */ -const assessExposure = (summary) => { - let score = 0 - if (nz(summary.anonymousEditLinks) > 0) score += 5 - if (nz(summary.neverExpiringAnonymous) > 0) score += 3 - if (nz(summary.anonymousLinks) > 0) score += 2 - if (nz(summary.folderShares) > 0) score += 2 - if (nz(summary.externalLinks) > 0) score += 1 - - if (score >= 7) return { level: 'High', severity: 'high' } - if (score >= 3) return { level: 'Medium', severity: 'medium' } - return { level: 'Low', severity: 'low' } -} - -// Exported so the branding preview can render this report against sample data, and so tests can -// render it to a real PDF. -export const SharingReportDocument = ({ - sharingData, - brandingSettings, - tenantName, - generatedOn, - variables, -}) => { - const summary = sharingData?.summary ?? {} - const links = sharingData?.links ?? [] - const topRecipients = sharingData?.topRecipients ?? [] - const topLibraries = sharingData?.topLibraries ?? [] - - const exposure = assessExposure(summary) - const exposureColour = severityColour(exposure.severity) - - const canEdit = (row) => - joinList(row.roles).includes('write') || joinList(row.roles).includes('owner') - const anonEditRows = links.filter((row) => row.classification === 'Anonymous' && canEdit(row)) - const neverExpiringRows = links.filter( - (row) => row.classification === 'Anonymous' && !row.expirationDateTime - ) - const folderShareRows = links.filter( - (row) => row.itemType === 'Folder' && ['Anonymous', 'External'].includes(row.classification) - ) - const externalRows = links.filter((row) => row.classification === 'External') - - const locationOf = (row) => - `${row.siteName || row.siteUrl || 'Unknown site'}${row.driveName ? ` / ${row.driveName}` : ''}` - const expiryOf = (row) => - row.expirationDateTime ? new Date(row.expirationDateTime).toLocaleDateString() : 'Never' - - return ( - - } - > - {/* EXECUTIVE SUMMARY */} - - -
    - - Sharing links are created by users on individual files and folders. They hand out access - outside the permission structure an administrator sets on a site or library, they - accumulate quietly as people work, and nothing prompts anyone to review them. This - report covers what exists today across SharePoint and OneDrive in{' '} - {tenantName}. - - - 0 ? REPORT_COLOURS.danger : undefined, - }, - { - value: nz(summary.neverExpiringAnonymous), - label: 'Anonymous, No Expiry', - colour: nz(summary.neverExpiringAnonymous) > 0 ? REPORT_COLOURS.danger : undefined, - }, - { - value: nz(summary.folderShares), - label: 'Shared Folders', - colour: nz(summary.folderShares) > 0 ? REPORT_COLOURS.warning : undefined, - }, - { - value: nz(summary.externalRecipients), - label: 'External Recipients', - colour: nz(summary.externalRecipients) > 0 ? REPORT_COLOURS.warning : undefined, - }, - ]} - /> - - - {exposure.level === 'High' && - 'Content is reachable by people who cannot be identified. Anonymous links work for anyone holding them, with no sign-in and no record of use — and where those links also allow editing, changes are attributed to nobody. Treat the findings below as immediate remediation work.'} - {exposure.level === 'Medium' && - 'Sharing extends beyond the intended audience in places. Each finding below is individually manageable, but every open link widens what a single forwarded message can expose.'} - {exposure.level === 'Low' && - 'No high-risk sharing was found. Links are scoped and time-bounded. Continue reviewing periodically, since sharing accumulates as projects come and go.'} - -
    - -
    - - {nz(summary.totalLinks)} sharing links and external shares across{' '} - {nz(summary.sharePointSites)} SharePoint sites, {nz(summary.teamsSites)} Teams-connected - sites and {nz(summary.oneDriveAccounts)} OneDrive accounts, covering{' '} - {nz(summary.itemsShared)} distinct shared items. Data is taken from the last completed - sync, not read live. - - - This report covers sharing links only. Permissions granted on a site or document library - are a separate access path, governed differently, and are covered by the Permissions - Report. A clean result here does not mean access is restricted — it means nothing has - been shared out by link. - -
    -
    - - {/* FINDINGS */} - - -
    - - An anonymous link works for anyone who holds it — no sign-in, no record of who used it. - When that link also grants editing, anyone it has been forwarded to can change or delete - the content, and the change is attributed to nobody. This is the only combination that - allows untraceable modification. - - {anonEditRows.length > 0 ? ( - <> - - Revoke these, or downgrade them to view-only where the sharing is still needed. - - ({ - item: row.fileName, - location: locationOf(row), - expires: expiryOf(row), - }))} - /> - - ) : ( - - No anonymous link grants write access. - - )} -
    - -
    - - A link with no expiry date stays live indefinitely, long after the reason for sharing - has passed. Expiry is the only control that withdraws this access without somebody - remembering to do it. - - {neverExpiringRows.length > 0 ? ( - <> - - Set a tenant-level default expiry so this cannot recur, then revoke the existing - links that are no longer needed. - - ({ - item: row.fileName, - location: locationOf(row), - roles: joinList(row.roles), - }))} - /> - - ) : ( - - Every anonymous link has an expiry date set. - - )} -
    -
    - - {/* FINDINGS CONTINUED */} - - -
    - - Sharing a folder shares everything inside it, including anything added later. The - recipient's access grows over time without anyone re-approving it, which is the main way - a small share quietly becomes a large one. - - {folderShareRows.length > 0 ? ( - <> - - Check what each folder holds now, not what it held when it was shared. - - ({ - item: row.fileName, - location: locationOf(row), - audience: row.classification, - }))} - /> - - ) : ( - - External and anonymous shares point at individual files rather than folders. - - )} -
    - -
    - - Every external recipient is a person outside the organisation holding access that was - granted individually, usually for a specific piece of work. Nothing withdraws it when - that work ends. - - {topRecipients.length > 0 ? ( - <> - - {plural(nz(summary.externalRecipients), 'external identity', 'external identities')}{' '} - hold shared content, across {plural(externalRows.length, 'share')}. The most - frequent are listed below. - - ({ - recipient: row.recipient, - shares: String(row.links), - }))} - limit={15} - /> - - ) : ( - - Nothing has been shared with an identity outside the organisation. - - )} -
    - - {topLibraries.length > 0 && ( -
    - - The libraries below account for the most sharing links. Concentration is not a problem - in itself, but it shows where a review will have the most effect. - - ({ - library: row.library, - links: String(row.links), - }))} - limit={10} - /> -
    - )} -
    - - {/* RECOMMENDATIONS */} - - -
    - - Ordered by how much exposure each removes relative to the effort involved. - - -
    - -
    - -
    -
    -
    - ) -} +import { useSettings } from '../../hooks/use-settings' +import { ServerPdfPane, useServerPdf } from './useServerPdf' +// The report PDF is rendered server-side (ExecGetSharingReportPdf) by the shared CIPPSharp component +// kit; the button fetches the finished PDF as a blob for preview and download. export const SharingReportButton = ({ sharingData, tenantName }) => { const [dialogOpen, setDialogOpen] = useState(false) - const [generatedOn, setGeneratedOn] = useState('') - const brandingSettings = useBrandingSettings() - const variables = useReportVariables() + const tenantFilter = useSettings().currentTenant const hasData = !!sharingData?.summary - - const handleOpen = () => { - setGeneratedOn( - new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) - ) - setDialogOpen(true) - } - - const documentNode = ( - - ) + const pdf = useServerPdf({ + url: `/api/ExecGetSharingReportPdf?tenantFilter=${encodeURIComponent(tenantFilter)}`, + enabled: dialogOpen, + }) + const handleOpen = () => setDialogOpen(true) + const handleClose = () => setDialogOpen(false) + const fileName = `Sharing_Report_${(tenantName || 'report').replace(/[^a-zA-Z0-9]/g, '_')}_${new Date().toISOString().split('T')[0]}.pdf` return ( <> @@ -451,7 +46,7 @@ export const SharingReportButton = ({ sharingData, tenantName }) => { setDialogOpen(false)} + onClose={handleClose} maxWidth="lg" fullWidth slotProps={{ @@ -468,40 +63,28 @@ export const SharingReportButton = ({ sharingData, tenantName }) => { Sharing Report Preview - setDialogOpen(false)} size="small"> + - {dialogOpen && ( - - {documentNode} - - )} + - - Close + - )} - + Download PDF + diff --git a/frontend/src/components/CippPdf/charts.jsx b/frontend/src/components/CippPdf/charts.jsx deleted file mode 100644 index f9d17e2deb..0000000000 --- a/frontend/src/components/CippPdf/charts.jsx +++ /dev/null @@ -1,382 +0,0 @@ -import { - Circle, - G, - Line, - Path, - Rect, - Svg, - Text, - Text as SvgText, - View, -} from '@react-pdf/renderer' -import { REPORT_COLOURS } from './reportTheme' -import { useReportStyles } from './reportContext' - -// Charts for CIPP's PDF reports. -// -// react-pdf has no charting library, so these draw SVG paths directly. They used to live inline in -// ExecutiveReportButton as ~150-line IIFEs inside JSX; pulling them out is what lets the report -// builder offer a chart block at all, since there was previously no chart to reuse. -// -// Every chart draws into a 400x200 viewBox and scales with its container, so a caller only decides -// data and colour — never geometry. - -const VIEW_WIDTH = 400 -const VIEW_HEIGHT = 200 - -const toNumber = (value) => { - const parsed = typeof value === 'number' ? value : parseFloat(value) - return Number.isFinite(parsed) ? parsed : 0 -} - -/** Normalise the loose shapes a caller (or a saved report block) might hold data in. */ -export const normaliseChartData = (data) => - (Array.isArray(data) ? data : []) - .map((entry) => ({ - label: String(entry?.label ?? ''), - value: toNumber(entry?.value), - colour: entry?.colour || entry?.color || null, - })) - .filter((entry) => entry.label !== '' || entry.value !== 0) - -const seriesColour = (theme, entry, index) => - entry.colour || theme?.series?.[index % (theme?.series?.length || 1)] || REPORT_COLOURS.info - -const ChartFrame = ({ styles, title, caption, children, empty, emptyText }) => ( - - {title ? {title} : null} - {empty ? ( - {emptyText} - ) : ( - - {children} - - )} - {caption && !empty ? {caption} : null} - -) - -/** - * Legend laid out in rows beneath the plot. - * - * The original inline version placed every entry on one row at a fixed 90pt pitch, so a fifth - * category ran off the right edge of the canvas. Widths are measured off the label lengths here so - * entries wrap instead. - */ -const renderLegend = ({ entries, theme, y }) => { - const perRow = entries.length <= 3 ? entries.length : Math.ceil(entries.length / 2) - const columnWidth = (VIEW_WIDTH - 40) / Math.max(perRow, 1) - - return entries.map((entry, index) => { - const row = Math.floor(index / perRow) - const column = index % perRow - const x = 20 + column * columnWidth - const rowY = y + row * 14 - - return ( - - - - {`${entry.label} (${entry.value})`} - - - ) - }) -} - -/** - * Donut chart with an optional total in the middle. - * - * A slice covering the whole circle is drawn at 359.99° rather than 360°: at exactly 360 the arc's - * start and end points coincide and the renderer draws nothing at all, so a tenant whose data fell - * entirely into one category used to get an empty chart. - */ -export const DonutChart = ({ - data, - title, - caption, - centreLabel, - emptyText = 'No data available for this chart.', - ...props -}) => { - const { styles, theme } = useReportStyles(props) - const entries = normaliseChartData(data).map((entry, index) => ({ ...entry, seriesIndex: index })) - const visible = entries.filter((entry) => entry.value > 0) - const total = visible.reduce((sum, entry) => sum + entry.value, 0) - - if (total === 0) { - return - } - - const centreX = VIEW_WIDTH / 2 - const centreY = 85 - const outerRadius = 60 - const innerRadius = 25 - - // Each slice's start is derived from the values before it rather than accumulated in a running - // variable, so the geometry is a pure function of the data — the list is short enough that the - // repeated prefix sum costs nothing. - const slices = visible.map((entry, index) => { - const preceding = visible.slice(0, index).reduce((sum, item) => sum + item.value, 0) - // Start at twelve o'clock rather than three. - const startAngle = -90 + (preceding / total) * 360 - const angle = Math.min((entry.value / total) * 360, 359.99) - const endAngle = startAngle + angle - - const point = (radius, degrees) => ({ - x: centreX + radius * Math.cos((degrees * Math.PI) / 180), - y: centreY + radius * Math.sin((degrees * Math.PI) / 180), - }) - - const outerStart = point(outerRadius, startAngle) - const outerEnd = point(outerRadius, endAngle) - const innerStart = point(innerRadius, startAngle) - const innerEnd = point(innerRadius, endAngle) - const largeArc = angle > 180 ? 1 : 0 - - return { - entry, - d: [ - `M ${outerStart.x} ${outerStart.y}`, - `A ${outerRadius} ${outerRadius} 0 ${largeArc} 1 ${outerEnd.x} ${outerEnd.y}`, - `L ${innerEnd.x} ${innerEnd.y}`, - `A ${innerRadius} ${innerRadius} 0 ${largeArc} 0 ${innerStart.x} ${innerStart.y}`, - 'Z', - ].join(' '), - } - }) - - return ( - - {slices.map((slice, index) => ( - - ))} - - {String(total)} - - {centreLabel ? ( - - {centreLabel} - - ) : null} - {renderLegend({ entries: visible, theme, y: 172 })} - - ) -} - -/** - * Line-and-area trend, as used for the secure score history. - * - * `max` fixes the top of the axis. Left unset it is taken from the data, which is right for an - * arbitrary series but wrong for a score out of a known maximum — pass it when one exists. - */ -export const TrendChart = ({ - data, - title, - caption, - max, - emptyText = 'Historical data not available.', - ...props -}) => { - const { styles, theme } = useReportStyles(props) - const entries = normaliseChartData(data) - - if (entries.length === 0) { - return - } - - const colour = theme?.primary || REPORT_COLOURS.info - const plotLeft = 40 - const plotTop = 20 - const plotWidth = 320 - const plotHeight = 140 - const plotBottom = plotTop + plotHeight - - const dataMax = Math.max(...entries.map((entry) => entry.value), 0) - const scaleMax = toNumber(max) > 0 ? toNumber(max) : dataMax > 0 ? dataMax : 1 - const spacing = plotWidth / Math.max(entries.length - 1, 1) - - const pointAt = (entry, index) => ({ - x: plotLeft + index * spacing, - y: plotBottom - Math.min(entry.value / scaleMax, 1) * plotHeight, - }) - - const points = entries.map(pointAt) - // A single reading has no line to draw — the point alone still shows where it sits on the axis. - const linePath = points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`).join(' ') - const areaPath = `${linePath} L ${points[points.length - 1].x} ${plotBottom} L ${points[0].x} ${plotBottom} Z` - - // Only label as many x ticks as will fit without overlapping. - const labelStride = Math.ceil(entries.length / 7) - - return ( - - - {[0, 1, 2, 3, 4].map((index) => ( - - ))} - {points.length > 1 ? : null} - {points.length > 1 ? : null} - {points.map((point, index) => ( - - ))} - {entries.map((entry, index) => - index % labelStride === 0 ? ( - - {entry.label} - - ) : null - )} - {[0, 0.25, 0.5, 0.75, 1].map((fraction, index) => ( - - {String(Math.round(scaleMax * fraction))} - - ))} - - ) -} - -/** Vertical bar chart, for category comparisons that do not sum to a meaningful whole. */ -export const BarChart = ({ - data, - title, - caption, - emptyText = 'No data available for this chart.', - ...props -}) => { - const { styles, theme } = useReportStyles(props) - const entries = normaliseChartData(data) - - if (entries.length === 0) { - return - } - - const plotLeft = 40 - const plotTop = 20 - const plotWidth = 340 - const plotHeight = 130 - const plotBottom = plotTop + plotHeight - - const maxValue = Math.max(...entries.map((entry) => entry.value), 0) || 1 - const slot = plotWidth / entries.length - const barWidth = Math.min(slot * 0.6, 46) - - return ( - - - {entries.map((entry, index) => { - // Zero-valued bars still get a hairline so the category reads as present-but-empty rather - // than missing from the chart. - const height = Math.max((entry.value / maxValue) * plotHeight, entry.value > 0 ? 2 : 1) - const x = plotLeft + index * slot + (slot - barWidth) / 2 - const y = plotBottom - height - - return ( - - - - {String(entry.value)} - - - {entry.label.length > 14 ? `${entry.label.slice(0, 13)}…` : entry.label} - - - ) - })} - - ) -} - -export const CHART_KINDS = [ - { label: 'Donut', value: 'donut' }, - { label: 'Bar', value: 'bar' }, - { label: 'Trend line', value: 'trend' }, -] - -/** Render whichever chart a saved block asked for. Unknown kinds fall back to a bar chart. */ -export const ReportChart = ({ kind, ...props }) => { - if (kind === 'donut') return - if (kind === 'trend') return - return -} diff --git a/frontend/src/components/CippPdf/index.js b/frontend/src/components/CippPdf/index.js index c78de85ec9..d7f4da9d91 100644 --- a/frontend/src/components/CippPdf/index.js +++ b/frontend/src/components/CippPdf/index.js @@ -1,13 +1,6 @@ -// One entry point for the PDF report system. Reports import from here rather than reaching into -// individual modules, so the internal split between theme, styles, primitives and charts can move -// without touching every report. - -// Imported for its side effects: the global hyphenation and emoji registrations. First, so they -// are in place before any report builds a document. -import './reportFonts' - -export { noHyphenation } from './reportFonts' -export { measureText, wrapLongTokens } from './measureText' +// One entry point for what remains of the client-side report kit now that the PDFs render +// server-side (CIPPSharp): the theme and page metrics the report builder and the branding editor's +// cover mock still read, cover-image resolution, and the branding-settings hook. export { DEFAULT_BRAND_COLOUR, @@ -39,41 +32,6 @@ export { createReportStyles, } from './reportPdfStyles' -export { - AlertBox, - Bold, - Bullet, - BulletList, - ClearBox, - Columns, - ContentPage, - CoverMeta, - CoverPage, - DataTable, - HeroPage, - INFO_TONES, - InfoBox, - Note, - PageFooter, - PageHeader, - Paragraph, - ProgressList, - ReportPage, - STATUS_TONES, - Section, - StatRow, - StatusText, - Watermark, - severityColour, - splitAccentTitle, - tableColumns, -} from './reportPdfPrimitives' - -export { ReportDocument } from './ReportDocument' -export { ReportProvider, useReport, useReportStyles } from './reportContext' - -export { BarChart, CHART_KINDS, DonutChart, ReportChart, TrendChart, normaliseChartData } from './charts' - export { COVER_IMAGE_NOT_FOUND, COVER_STOCK_NONE, @@ -86,7 +44,6 @@ export { resolveCoverImage, } from './resolveCoverImage' -export { useReportVariables } from './useReportVariables' export { useBrandingSettings, fetchBrandingSettings, diff --git a/frontend/src/components/CippPdf/measureText.js b/frontend/src/components/CippPdf/measureText.js deleted file mode 100644 index ccb954f74b..0000000000 --- a/frontend/src/components/CippPdf/measureText.js +++ /dev/null @@ -1,103 +0,0 @@ -// Measuring Helvetica, so a table cell can wrap a long value without a hyphen. -// -// react-pdf only breaks inside a word through hyphenation, and its line breaker inserts a literal -// `-` glyph at every such break (@react-pdf/textkit `breakLines`: a penalty breakpoint calls -// `insertGlyph(…, HYPHEN, …)`). There is no way to opt out — the only breakpoint that does not draw -// one is a glue node, which requires a whitespace syllable, and every whitespace character in the -// standard-14 WinAnsi encoding has a visible width. So a mid-token break that draws nothing has to -// be a real line break that we put there ourselves, which means knowing how wide the text is. -// -// These are the Adobe AFM advances for the standard-14 Helvetica faces, in 1/1000 em, for -// ASCII 32–126 — read straight out of the metrics @react-pdf/pdfkit embeds, not transcribed. Every -// report uses Helvetica; a report that registers its own font would need its own metrics, which is -// why `measureText` is deliberately conservative about characters it does not know. - -const FIRST_CODE = 32 -const LAST_CODE = 126 - -const HELVETICA = [ - 278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278, - 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, - 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, - 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, - 333, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, - 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, -] - -const HELVETICA_BOLD = [ - 278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, - 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, - 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, - 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, - 333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, - 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, -] - -// Anything outside ASCII — an em dash, an accented name, a bullet — is assumed as wide as the -// widest ASCII glyph. Over-estimating means we break a shade early; under-estimating would let the -// value run out of its column, which is the bug this exists to fix. -const FALLBACK_WIDTH = 1015 - -const advanceOf = (character, bold) => { - const code = character.charCodeAt(0) - if (code < FIRST_CODE || code > LAST_CODE) return FALLBACK_WIDTH - return (bold ? HELVETICA_BOLD : HELVETICA)[code - FIRST_CODE] -} - -/** Width of `text` in points at `fontSize`. */ -export const measureText = (text, fontSize, bold = false) => { - let units = 0 - for (const character of String(text ?? '')) units += advanceOf(character, bold) - return (units * fontSize) / 1000 -} - -// Seams a long identifier reads naturally at, in preference order. Breaking after `_` in -// `Defender_for_Business` looks deliberate; breaking mid-syllable does not. -const SEAM = /[_\-/\\.,:;@+&]/ - -/** - * Insert real line breaks into any token too wide for `maxWidth`. - * - * Ordinary text is returned untouched — the layout engine wraps it at spaces as usual, and short - * words keep their normal behaviour. Only a single run with no space in it that cannot fit its - * container is touched, and it is cut at the last seam that fits, or mid-run when there is no seam - * (a GUID, a thumbprint, a base64 blob). - * - * The result is a hard break, so it costs nothing at render time and draws no hyphen. - */ -export const wrapLongTokens = (text, maxWidth, fontSize, bold = false) => { - const value = String(text ?? '') - if (!value || !(maxWidth > 0)) return value - - return value - .split(/(\s+)/) - .map((token) => { - if (!token.trim() || measureText(token, fontSize, bold) <= maxWidth) return token - - const pieces = [] - let current = '' - let currentWidth = 0 - let lastSeam = -1 - - for (const character of token) { - const width = (advanceOf(character, bold) * fontSize) / 1000 - - if (currentWidth + width > maxWidth && current) { - // Prefer the last seam, as long as it does not leave a uselessly short piece. - const cut = lastSeam > 0 && lastSeam >= current.length / 2 ? lastSeam + 1 : current.length - pieces.push(current.slice(0, cut)) - current = current.slice(cut) - currentWidth = measureText(current, fontSize, bold) - lastSeam = -1 - } - - current += character - currentWidth += width - if (SEAM.test(character)) lastSeam = current.length - 1 - } - - if (current) pieces.push(current) - return pieces.join('\n') - }) - .join(''); -} diff --git a/frontend/src/components/CippPdf/previewSampleData.js b/frontend/src/components/CippPdf/previewSampleData.js index fe7fa30c3e..0b844bc2e9 100644 --- a/frontend/src/components/CippPdf/previewSampleData.js +++ b/frontend/src/components/CippPdf/previewSampleData.js @@ -1,431 +1,10 @@ -/** - * Sample data for the branding preview, and for nothing else. - * - * ───────────────────────────────────────────────────────────────────────────── - * THIS MUST NEVER BE IMPORTED BY A REPORT. - * ───────────────────────────────────────────────────────────────────────────── - * - * The point of the branding preview is to show what a report will look like before a tenant has - * been scanned, so someone tuning colours is not staring at an empty page. That means inventing - * numbers — and invented numbers reaching a client report would be far worse than an empty section. - * A report that has no data must keep saying so. - * - * The separation is structural rather than a convention to remember: this module is imported only - * by CippBrandingReportPreview, and `previewSampleData.test.js` fails the build if anything under - * `src/` other than the preview imports it. - * - * Everything here is deliberately obvious as fiction — Contoso, round numbers, example.com — so a - * screenshot of the preview can never be mistaken for a real client's report. - */ +// The figures the branding editor's cover mock quotes for each report. They are the headline +// values of the server-side samples (backend/Config/ReportSamples/.json) the live preview +// renders, copied here because the static frontend build cannot read the backend tree; keep the two +// in step so the mock and the rendered preview describe the same report. export const SAMPLE_TENANT_NAME = 'Contoso (sample data)' -/** Executive report — user counts, secure score history, licences, devices, CA policies. */ -export const SAMPLE_EXECUTIVE = { - userStats: { - licensedUsers: 128, - unlicensedUsers: 12, - guests: 9, - globalAdmins: 3, - }, - secureScoreData: { - isSuccess: true, - translatedData: { currentScore: 61, maxScore: 100, percentageCurrent: 61 }, - secureScore: { - data: { - Results: [ - { createdDateTime: '2026-08-04T00:00:00Z', currentScore: 61 }, - { createdDateTime: '2026-08-03T00:00:00Z', currentScore: 60 }, - { createdDateTime: '2026-08-02T00:00:00Z', currentScore: 58 }, - { createdDateTime: '2026-08-01T00:00:00Z', currentScore: 57 }, - { createdDateTime: '2026-07-31T00:00:00Z', currentScore: 55 }, - { createdDateTime: '2026-07-30T00:00:00Z', currentScore: 52 }, - { createdDateTime: '2026-07-29T00:00:00Z', currentScore: 50 }, - ], - }, - }, - }, - // Field names match what the report's tables actually read. They were previously invented - // (`skuPartNumber`, `consumedUnits`) and every cell fell through to its 'N/A' fallback, so the - // preview showed an empty-looking table while the real report showed data. - licensingData: [ - { - License: 'Microsoft 365 E3', - CountUsed: 88, - CountAvailable: 12, - TotalLicenses: 100, - }, - { - License: 'Exchange Online (Plan 1)', - CountUsed: 27, - CountAvailable: 3, - TotalLicenses: 30, - }, - { - License: 'Microsoft Defender for Office 365', - CountUsed: 64, - CountAvailable: 36, - TotalLicenses: 100, - }, - ], - // A plain array, matching `deviceData?.data?.Results` in the real report — the wrapper the sample - // used to carry meant `Array.isArray` failed and the whole Device Management section was skipped. - deviceData: [ - { - deviceName: 'SAMPLE-LT-001', - operatingSystem: 'Windows', - complianceState: 'compliant', - isEncrypted: true, - lastSyncDateTime: '2026-08-05T09:14:00Z', - }, - { - deviceName: 'SAMPLE-LT-002', - operatingSystem: 'Windows', - complianceState: 'noncompliant', - isEncrypted: false, - lastSyncDateTime: '2026-07-28T17:02:00Z', - }, - { - deviceName: 'SAMPLE-MB-003', - operatingSystem: 'iOS', - complianceState: 'compliant', - isEncrypted: true, - lastSyncDateTime: '2026-08-05T07:45:00Z', - }, - { - deviceName: 'SAMPLE-MB-004', - operatingSystem: 'Android', - complianceState: 'compliant', - isEncrypted: true, - lastSyncDateTime: '2026-08-04T21:30:00Z', - }, - // A Windows 365 Cloud PC: isEncrypted is false (no BitLocker) but the disk is - // platform-encrypted by Azure, so the report counts it as encrypted. - { - deviceName: 'CPC-SAMPLE-005', - operatingSystem: 'Windows', - complianceState: 'compliant', - isEncrypted: false, - deviceType: 'cloudPC', - model: 'Cloud PC Enterprise 2vCPU/8GB/128GB', - manufacturer: 'Microsoft Corporation', - lastSyncDateTime: '2026-08-05T08:20:00Z', - }, - ], - // Also a plain array — `conditionalAccessData?.data?.Results` in the real report. - conditionalAccessData: [ - { - displayName: 'Require MFA for all users', - state: 'enabled', - includeApplications: 'All', - builtInControls: ['mfa'], - }, - { - displayName: 'Block legacy authentication', - state: 'enabled', - includeApplications: 'All', - builtInControls: ['block'], - }, - { - displayName: 'Require compliant device for Exchange', - state: 'enabledForReportingButNotEnforced', - includeApplications: 'Exchange Online', - builtInControls: ['compliantDevice'], - }, - ], - // `driftComplianceData?.data` in the real report: the array itself, not a wrapper around it. - driftComplianceData: [ - { - tenantFilter: 'contoso.com', - alignedCount: 42, - currentDeviations: [ - { - standardName: 'standards.AntiPhishPolicy', - receivedValue: 'Disabled', - }, - { - standardName: 'standards.SafeLinksPolicy', - receivedValue: 'Disabled', - }, - ], - acceptedDeviations: [ - { standardName: 'standards.AuditLog', receivedValue: 'Custom' }, - ], - deniedDeviations: [ - { - standardName: 'standards.DisableBasicAuth', - receivedValue: 'Enabled', - }, - ], - customerSpecificDeviations: [ - { standardName: 'standards.Guests', receivedValue: 'Allowed' }, - ], - }, - ], - // Drives two things: the Security Standards page, and the Applied Standards section of the drift - // pages — which is built from this rather than from driftComplianceData, so without it that page - // rendered empty and never appeared in the preview at all. - // - // Keys are real standard names where possible, so they resolve against the generated - // standards.json catalogue and show its wording. A key it does not know still renders, through - // the report's own fallback naming — which is worth exercising here too. - standardsCompareData: [ - { - tenantFilter: 'contoso.com', - 'standards.AntiPhishPolicy': { Value: true }, - 'standards.SafeLinksPolicy': { Value: true }, - 'standards.AuditLog': { - CurrentValue: 'Enabled', - ExpectedValue: 'Enabled', - }, - 'standards.Guests': { CurrentValue: 'Allowed', ExpectedValue: 'Blocked' }, - 'standards.PasswordExpireDisabled': { Value: true }, - 'standards.DisableBasicAuth': { - CurrentValue: 'Partial', - ExpectedValue: 'Disabled', - }, - 'standards.IntuneTemplate.8f2a1c4e-6b3d-4f5a-9e7c-1d2b3a4c5e6f': { - Value: true, - }, - }, - ], - standardTemplatesData: [ - { - standards: { - IntuneTemplate: [ - { - TemplateList: { - value: '8f2a1c4e-6b3d-4f5a-9e7c-1d2b3a4c5e6f', - label: 'Windows Device Compliance', - }, - }, - ], - }, - }, - ], -} - -/** Report builder — one of each block type, so every renderer is exercised in the preview. */ -export const SAMPLE_REPORT_BUILDER_BLOCKS = [ - { - id: 'sample-1', - type: 'blank', - title: 'Summary', - static: true, - content: - '

    This preview uses sample data so you can see how your branding lands before running a report against a real tenant.

    ', - }, - { - id: 'sample-2', - type: 'scorecard', - title: 'Environment Overview', - static: true, - stats: [ - { label: 'Licensed Users', value: '128' }, - { label: 'Devices', value: '96' }, - { label: 'Global Admins', value: '3', caption: 'Target: 2–4' }, - { label: 'Guests', value: '9' }, - ], - }, - { - id: 'sample-3', - type: 'chart', - title: 'Device Compliance', - static: true, - chartKind: 'donut', - chartCentreLabel: 'Devices', - chartData: [ - { label: 'Compliant', value: 78 }, - { label: 'Non-compliant', value: 14 }, - { label: 'Not evaluated', value: 4 }, - ], - }, - { - id: 'sample-4', - type: 'chart', - title: 'Secure Score Trend', - static: true, - chartKind: 'trend', - chartMax: 100, - chartCaption: 'Current: 61 / 100 (61%)', - chartData: [ - { label: 'Jul 29', value: 50 }, - { label: 'Jul 31', value: 55 }, - { label: 'Aug 2', value: 58 }, - { label: 'Aug 4', value: 61 }, - ], - }, - { - id: 'sample-5', - type: 'progress', - title: 'Control Coverage', - static: true, - items: [ - { label: 'MFA enforced', value: 92, max: 100 }, - { label: 'Disk encryption', value: 78, max: 100 }, - { label: 'Defender onboarded', value: 64, max: 100 }, - ], - }, - { id: 'sample-6', type: 'pagebreak', title: '', static: true }, - { - id: 'sample-7', - type: 'hero', - title: 'seconds', - static: true, - heroHighlight: '39', - heroSubText: 'a business falls victim to ransomware', - heroFooterText: 'Proactive defense beats reactive recovery', - heroImage: '/reportImages/working.jpg', - }, - { - id: 'sample-8', - type: 'test', - title: 'Multi-factor authentication', - status: 'Failed', - static: false, - content: - '14 of 128 accounts can still sign in without a second factor.\n\n## Results\n\n| Account | Method | Last sign-in |\n| --- | --- | --- |\n| sample.one@example.com | None | 2 days ago |\n| sample.two@example.com | None | 9 days ago |\n', - }, - { - id: 'sample-9', - type: 'chart', - title: 'Devices by Platform', - static: true, - chartKind: 'bar', - chartData: [ - { label: 'Windows', value: 62 }, - { label: 'macOS', value: 18 }, - { label: 'iOS', value: 12 }, - { label: 'Android', value: 4 }, - ], - }, - { - // A database block in its markdown form: the query result arrives as a markdown table, which is - // the path that exercises the table renderer and the long-value wrapping in it. The deliberately - // over-long identifier is there to show a value being broken across lines rather than running - // out of its column. - id: 'sample-10', - type: 'database', - title: 'Query Results', - static: true, - format: 'text', - content: - '| Policy | Identifier | State |\n| --- | --- | --- |\n| Baseline | 8f2a1c4e-6b3d-4f5a-9e7c-1d2b3a4c5e6f | Enabled |\n| Hardened | Microsoft_Defender_for_Business_Servers | Report only |\n', - }, - { - // The same block type in its raw form, which renders as a code block instead. - id: 'sample-11', - type: 'database', - title: 'Raw Response', - static: true, - format: 'json', - content: - '{\n "tenant": "contoso.com",\n "policies": 191,\n "enabled": 5\n}', - }, -] - -/** - * Shadow AI report. - * - * Field names match what the report reads — `tools` not `count`, `aiTool` not `name`, and a `status` - * of exactly 'Sanctioned' on at least one app, which is what builds the Company Sanctioned AI Tools - * page. The earlier sample invented its own names, so every table in the preview rendered its empty - * state and the sanctioned page never appeared at all. - */ -export const SAMPLE_SHADOW_AI = { - summary: { - aiToolsDetected: 18, - deviceInstalls: 36, - consentedAiApps: 5, - highRiskTools: 3, - sanctionedTools: 2, - }, - byRisk: [ - { risk: 'High', tools: 3 }, - { risk: 'Medium', tools: 7 }, - { risk: 'Low', tools: 6 }, - { risk: 'Informational', tools: 2 }, - ], - topTools: [ - { - tool: 'Sample AI Assistant', - category: 'Chat', - status: 'Unsanctioned', - devices: 22, - users: 18, - }, - { - tool: 'Sample Code Helper', - category: 'Development', - status: 'Unsanctioned', - devices: 14, - users: 9, - }, - { - tool: 'Sample Notetaker', - category: 'Meetings', - status: 'Sanctioned', - devices: 11, - users: 24, - }, - ], - detectedApps: [ - { - application: 'Sample AI Assistant Desktop', - aiTool: 'Sample AI Assistant', - vendor: 'Example Corp', - category: 'Chat', - risk: 'High', - status: 'Unsanctioned', - deviceCount: 22, - }, - { - application: 'Sample Code Helper', - aiTool: 'Sample Code Helper', - vendor: 'Example Labs', - category: 'Development', - risk: 'Medium', - status: 'Unsanctioned', - deviceCount: 14, - }, - { - application: 'Sample Notetaker', - aiTool: 'Sample Notetaker', - vendor: 'Example Corp', - category: 'Meetings', - risk: 'Informational', - status: 'Sanctioned', - deviceCount: 11, - }, - ], - consentedApps: [ - { - applicationId: '00000000-0000-0000-0000-000000000001', - application: 'Sample AI Connector', - aiTool: 'Sample AI Assistant', - vendor: 'Example Corp', - category: 'Chat', - risk: 'High', - status: 'Unsanctioned', - activeUsersLast7Days: 18, - firstConsentedDateTime: '2026-06-11T10:22:00Z', - }, - { - applicationId: '00000000-0000-0000-0000-000000000002', - application: 'Sample Meeting Notes', - aiTool: 'Sample Notetaker', - vendor: 'Example Corp', - category: 'Meetings', - risk: 'Informational', - status: 'Sanctioned', - activeUsersLast7Days: 24, - firstConsentedDateTime: '2026-03-02T14:05:00Z', - }, - ], -} - -/** BEC remediation report. Field shapes mirror the real Push-BECRun payload so the preview - * renders every report section with plausible values rather than 'Unknown' placeholders. */ export const SAMPLE_BEC = { userData: { displayName: 'Sample User', @@ -696,82 +275,22 @@ export const SAMPLE_BEC = { }, } -/** SharePoint sharing report. */ export const SAMPLE_SHARING = { summary: { totalLinks: 24, itemsShared: 18, externalRecipients: 6, - anonymousLinks: 4, - anonymousEditLinks: 1, - neverExpiringAnonymous: 2, - folderShares: 3, - externalLinks: 6, - sharePointSites: 5, - teamsSites: 3, - oneDriveAccounts: 12, }, - links: [ - { - itemName: 'Sample Proposal.docx', - siteName: 'Sample Marketing', - linkType: 'Anonymous', - scope: 'Edit', - expires: 'Never', - recipients: 'Anyone with the link', - }, - { - itemName: 'Sample Budget.xlsx', - siteName: 'Sample Finance', - linkType: 'External', - scope: 'View', - expires: '2026-12-31', - recipients: 'partner@example.com', - }, - ], - topRecipients: [ - { recipient: 'partner@example.com', links: 5 }, - { recipient: 'supplier@example.net', links: 3 }, - ], - topLibraries: [ - { library: 'Sample Marketing / Documents', links: 9 }, - { library: 'Sample Finance / Documents', links: 6 }, - ], } -/** SharePoint permissions report. */ export const SAMPLE_PERMISSIONS = { summary: { + totalAssignments: 156, sitesScanned: 12, librariesScanned: 34, - totalAssignments: 156, - broadClaimGrants: 2, - externalGrants: 5, - directFullControlGrants: 3, - uniquePermissionLibraries: 7, }, - assignments: [ - { - siteName: 'Sample Marketing', - libraryName: 'Documents', - principal: 'Everyone except external users', - permission: 'Edit', - type: 'Broad claim', - }, - { - siteName: 'Sample Finance', - libraryName: 'Documents', - principal: 'partner@example.com', - permission: 'Full Control', - type: 'External', - }, - ], } -/** - * Exchange mail flow report. Fourteen days of daily disposition counts, shaped as the page hands - * them over: totals per event type, direction totals, and the per-day rows behind them. - */ export const SAMPLE_MAIL_FLOW = { days: 14, totals: { @@ -782,167 +301,4 @@ export const SAMPLE_MAIL_FLOW = { EmailPhish: 412, EmailMalware: 37, }, - directionTotals: { - Inbound: 39280, - Outbound: 8940, - IntraOrg: 11879, - }, - daily: [ - { - date: '2026-08-04', - GoodMail: 3410, - TransportRules: 96, - SpamDetections: 430, - EdgeBlockSpam: 281, - EmailPhish: 29, - EmailMalware: 3, - }, - { - date: '2026-08-05', - GoodMail: 3688, - TransportRules: 104, - SpamDetections: 468, - EdgeBlockSpam: 302, - EmailPhish: 33, - EmailMalware: 2, - }, - { - date: '2026-08-06', - GoodMail: 3572, - TransportRules: 88, - SpamDetections: 451, - EdgeBlockSpam: 295, - EmailPhish: 31, - EmailMalware: 4, - }, - { - date: '2026-08-07', - GoodMail: 3740, - TransportRules: 112, - SpamDetections: 502, - EdgeBlockSpam: 318, - EmailPhish: 38, - EmailMalware: 1, - }, - { - date: '2026-08-08', - GoodMail: 3495, - TransportRules: 97, - SpamDetections: 476, - EdgeBlockSpam: 304, - EmailPhish: 35, - EmailMalware: 3, - }, - { - date: '2026-08-09', - GoodMail: 1180, - TransportRules: 21, - SpamDetections: 268, - EdgeBlockSpam: 174, - EmailPhish: 12, - EmailMalware: 0, - }, - { - date: '2026-08-10', - GoodMail: 1042, - TransportRules: 18, - SpamDetections: 251, - EdgeBlockSpam: 166, - EmailPhish: 10, - EmailMalware: 1, - }, - { - date: '2026-08-11', - GoodMail: 3820, - TransportRules: 118, - SpamDetections: 529, - EdgeBlockSpam: 341, - EmailPhish: 41, - EmailMalware: 5, - }, - { - date: '2026-08-12', - GoodMail: 3903, - TransportRules: 121, - SpamDetections: 544, - EdgeBlockSpam: 352, - EmailPhish: 44, - EmailMalware: 4, - }, - { - date: '2026-08-13', - GoodMail: 3766, - TransportRules: 109, - SpamDetections: 511, - EdgeBlockSpam: 329, - EmailPhish: 36, - EmailMalware: 2, - }, - { - date: '2026-08-14', - GoodMail: 3841, - TransportRules: 114, - SpamDetections: 498, - EdgeBlockSpam: 321, - EmailPhish: 34, - EmailMalware: 3, - }, - { - date: '2026-08-15', - GoodMail: 3612, - TransportRules: 102, - SpamDetections: 470, - EdgeBlockSpam: 303, - EmailPhish: 30, - EmailMalware: 4, - }, - { - date: '2026-08-16', - GoodMail: 1214, - TransportRules: 22, - SpamDetections: 264, - EdgeBlockSpam: 172, - EmailPhish: 20, - EmailMalware: 3, - }, - { - date: '2026-08-17', - GoodMail: 1127, - TransportRules: 18, - SpamDetections: 258, - EdgeBlockSpam: 322, - EmailPhish: 19, - EmailMalware: 2, - }, - ], - topSenders: [ - { Name: 'notifications@sample-crm.example.com', Count: 4820 }, - { Name: 'billing@example.com', Count: 3115 }, - { Name: 'scanner-3f@example.com', Count: 2064 }, - { Name: 'sample.user@example.com', Count: 1893 }, - { Name: 'helpdesk@example.com', Count: 1477 }, - ], - topSpamRecipients: [ - { Name: 'info@example.com', Count: 1840 }, - { Name: 'sales@example.com', Count: 1226 }, - { Name: 'sample.user@example.com', Count: 744 }, - { Name: 'accounts@example.com', Count: 517 }, - { Name: 'careers@example.com', Count: 388 }, - ], -} - -/** - * Which sample set feeds which report, keyed by the ids in REPORT_COVER_PRESETS so the preview's - * report picker and its data stay in step. - */ -export const SAMPLE_DATA_BY_REPORT = { - // The executive report can append the Shadow AI pages, so it is given the same sample the Shadow - // AI report uses. Without it that whole section was silently absent from the preview. - executive: { ...SAMPLE_EXECUTIVE, shadowAIData: SAMPLE_SHADOW_AI }, - reportBuilder: { blocks: SAMPLE_REPORT_BUILDER_BLOCKS }, - shadowAI: { data: SAMPLE_SHADOW_AI }, - bec: SAMPLE_BEC, - sharing: { sharingData: SAMPLE_SHARING }, - permissions: { permissionsData: SAMPLE_PERMISSIONS }, - mailFlow: { mailFlowData: SAMPLE_MAIL_FLOW }, } diff --git a/frontend/src/components/CippPdf/reportContext.jsx b/frontend/src/components/CippPdf/reportContext.jsx deleted file mode 100644 index f2574f492d..0000000000 --- a/frontend/src/components/CippPdf/reportContext.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import { createContext, useContext } from 'react' - -/** - * The report a component is being rendered inside. - * - * Every primitive here needs the same three things — the theme, the resolved stylesheet and the - * variables that fill `%reportname%` and friends — and threading them through by hand meant a - * `styles={styles} theme={theme}` on every single call. That is noise on the page and, worse, a - * component that quietly renders unstyled the day someone forgets one. - * - * A report is one React tree, so the context carries them instead. An explicit prop still wins, - * which is what lets a caller restyle a single component without a second provider — and is why - * the older reports kept working while they were migrated one at a time. - */ -const ReportContext = createContext(null) - -export const ReportProvider = ReportContext.Provider - -/** Read the surrounding report. Returns an empty object outside one, so primitives can fall back. */ -export const useReport = () => useContext(ReportContext) ?? {} - -/** - * Resolve a primitive's styles and theme: explicit props first, the surrounding report second. - * - * Written as one helper rather than repeated in each primitive so the precedence is stated once. - */ -export const useReportStyles = (props = {}) => { - const report = useReport() - return { - styles: props.styles ?? report.styles, - theme: props.theme ?? report.theme, - variables: props.variables ?? report.variables, - logo: props.logo ?? report.logo, - } -} diff --git a/frontend/src/components/CippPdf/reportFonts.js b/frontend/src/components/CippPdf/reportFonts.js deleted file mode 100644 index b4d97cae06..0000000000 --- a/frontend/src/components/CippPdf/reportFonts.js +++ /dev/null @@ -1,32 +0,0 @@ -import { Font } from '@react-pdf/renderer' - -// Global @react-pdf font behaviour for every CIPP report. -// -// These registrations are process-wide, not per-document, which is exactly why they belong here. -// They used to be side effects of two individual reports: ShadowAIReportButton registered the -// hyphenation callback and ReportBuilder registered the emoji source. Because the executive report -// imports the Shadow AI pages, whatever Shadow AI chose silently became the rule for every other -// report — and any report loaded on its own got react-pdf's default instead. - -/** - * Never hyphenate. - * - * react-pdf's default English hyphenation turns "Detected" into "De-tected" in a narrow stat card, - * and there is no such thing as a hyphen-free hyphenation break: @react-pdf/textkit's `breakLines` - * inserts a literal `-` glyph at every penalty breakpoint. So the only way to have no hyphens in a - * report is to do no hyphenation at all. - * - * A value too wide for its container is handled instead by putting a real line break in it, at a - * measured position — see `wrapLongTokens` in measureText.js, which is how table cells wrap a GUID - * or a long SKU without either a hyphen or an overflow. - */ -export const noHyphenation = (word) => [word] - -Font.registerHyphenationCallback(noHyphenation) - -// Helvetica has no emoji glyphs; react-pdf draws them as inline Twemoji images instead. Registered -// here rather than in the report builder so an emoji renders the same whichever report it lands in. -Font.registerEmojiSource({ - format: 'png', - url: 'https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/', -}) diff --git a/frontend/src/components/CippPdf/reportPdfPrimitives.jsx b/frontend/src/components/CippPdf/reportPdfPrimitives.jsx deleted file mode 100644 index e4450b681e..0000000000 --- a/frontend/src/components/CippPdf/reportPdfPrimitives.jsx +++ /dev/null @@ -1,785 +0,0 @@ -import { Children } from 'react' -import { Text, View, Image, Page } from '@react-pdf/renderer' -import { - REPORT_COLOURS, - applyFooterText, - applyWatermarkText, -} from './reportTheme' -import { useReport, useReportStyles } from './reportContext' -import { - DEFAULT_PAGE_SETUP, - TABLE_ROW_PADDING, - contentWidth, -} from './reportPdfStyles' -import { wrapLongTokens } from './measureText' - -// Breathing room between one column's text and the next, matching the `paddingRight` tableColumns -// puts on every cell but the last. -const CELL_GUTTER = 6 - -// Shared building blocks for CIPP's client-facing PDF reports. -// -// Each one takes its stylesheet from the surrounding ReportDocument, so inside a report they are -// written bare: ``. An explicit `styles`/`theme` prop still overrides that, -// which is what lets a caller restyle one component without a second provider. - -export const PageHeader = ({ title, subtitle, ...props }) => { - const { styles, logo } = useReportStyles(props) - return ( - - - {title} - {subtitle ? {subtitle} : null} - - {logo ? ( - - ) : null} - - ) -} - -/** - * A titled block of content. The unit a report is actually written in. - * - * Every report was repeating `…` - * by hand, which is how one of them ended up without the page-break-avoidance the others had. - */ -export const Section = ({ title, children, ...props }) => { - const { styles } = useReportStyles(props) - return ( - - {/* minPresenceAhead pushes the title to the next page when too little room is left below it, - so a section heading never strands alone at the foot of a page ahead of its content. */} - {title ? ( - - {title} - - ) : null} - {children} - - ) -} - -// Body copy. The single most repeated line in every report — 41 hand-written copies of the same -// `` before this existed. `indent` steps it in under a heading or a -// list, which the BEC report was doing with an inline margin. -export const Paragraph = ({ indent = false, children, ...props }) => { - const { styles } = useReportStyles(props) - return ( - - {children} - - ) -} - -/** - * Bold run inside a Paragraph. - * - * Reports emphasise a tenant name or a figure mid-sentence constantly, and used to do it with an - * inline `{ fontWeight: 'bold' }` object. Inside the kit a report has no `styles` in scope to reach - * for, so without this the only way to bold a word would be to hand-write the style again. - */ -export const Bold = ({ children, ...props }) => { - const { styles } = useReportStyles(props) - return {children} -} - -/** - * Lay children out side by side, sharing the width equally. - * - * Two callouts in a row is a layout decision, not a content one, so it belongs here rather than as - * an inline `flexDirection: 'row'` in each report. Children are wrapped in an equal-flex box, which - * means they do not each need a `flex: 1` of their own. - */ -export const Columns = ({ children, gap = 12, ...props }) => { - const { styles } = useReportStyles(props) - const items = Children.toArray(children) - return ( - - {items.map((child, index) => ( - - {child} - - ))} - - ) -} - -/** - * A content page: header at the top, footer at the bottom, watermark over the lot, content between. - * - * The three pieces of page furniture are decided here rather than by each report, so a page cannot - * be added without them — which is exactly how the Secure Score page ended up without a watermark - * and every report ended up with its own idea of what the footer said. - */ -export const ContentPage = ({ title, subtitle, children, ...props }) => { - const { styles, theme, variables, logo } = useReportStyles(props) - const report = useReport() - const size = props.size ?? report.size - const orientation = props.orientation ?? report.orientation - const footerLabel = props.footerLabel ?? report.footerLabel - - return ( - - {/* `fixed` repeats the header on every physical page this one flows onto. Without it, a page - whose content spills produces an unheaded continuation — which is what the report builder - had to work around by wrapping its own header. */} - {title ? ( - - - - ) : null} - {children} - - - ) -} - -/** - * Footer with optional branded text and page numbers. - * - * `label` is the caller-supplied text the older reports pass. When a theme is given instead, the - * footer text comes from the branding template with its `%variable%` tokens filled in, which is how - * the configurable footer reaches every report without each one knowing about branding settings. - */ -export const PageFooter = ({ styles, label, theme, variables }) => { - const templated = theme?.footer?.enabled - ? applyFooterText(theme.footer.template, variables) - : '' - // Configured branding wins over the report's own label. The reverse — which this did at first — - // meant every report that passed a label silently ignored the footer text an MSP had set, which - // reads as the setting doing nothing at all. The label stays as the fallback, so a report with no - // branded footer still identifies itself exactly as it used to — unless the footer has been - // switched off outright, which has to suppress the label as well. - const showText = theme ? theme.footer?.show !== false : true - const text = showText ? templated || label || '' : '' - const showPageNumbers = theme ? theme.footer?.showPageNumbers !== false : true - - if (!text && !showPageNumbers) return null - - return ( - - {text} - {showPageNumbers ? ( - /* No `fixed` needed here — the wrapper above carries it, and the callback fires per page - either way. What this node does need is the `lineHeight` reset in styles.pageNumber; - without it the number lays out but is never painted. */ - - `Page ${pageNumber} of ${totalPages}` - } - /> - ) : null} - - ) -} - -/** - * Every page in every report goes through here. - * - * It exists so that "what does a page always carry" is answered once. Adding the watermark to each - * page by hand meant the answer lived in six files and drifted immediately — a page that happened - * to declare its style differently, or a whole report added later, simply missed out. - * - * `style` is the report's own page style. Anything a page must always have is added around it. - */ -export const ReportPage = ({ - styles, - theme, - style, - size = 'A4', - orientation = 'portrait', - watermarkOnDark = false, - children, -}) => ( - - {children} - {/* Last, so it paints over the content. Underneath, any solid background hides it entirely. */} - - -) - -/** - * Diagonal mark drawn over every page. Same `%variable%` substitution as the footer — branding - * stores a template (e.g. `%tenantname%`), and the report fills it from the surrounding context. - * The 40-character ceiling is applied to the *resolved* string, after variables expand. - */ -export const Watermark = ({ - styles, - theme, - text, - variables: variablesProp, - onDark = false, -}) => { - const report = useReport() - const variables = variablesProp ?? report.variables - const template = - text ?? (theme?.watermark?.enabled ? theme.watermark.text : '') - const value = template ? applyWatermarkText(template, variables) : '' - if (!value) return null - - return ( - - {/* A brand-coloured mark at 8% disappears on the dark full-bleed pages, so those get a - light mark instead. Same text, same placement — only the ink changes. */} - - {value} - - - ) -} - -/** - * Split a report name so the last word can take the brand accent colour, as the cover pages do. - * A single-word name keeps its whole self in the lead position rather than rendering as accent - * alone, which loses the contrast the two-tone treatment exists for. - */ -export const splitAccentTitle = (title) => { - const words = String(title ?? '') - .trim() - .split(/\s+/) - .filter(Boolean) - if (words.length <= 1) return { lead: words.join(' '), accent: '' } - return { lead: words.slice(0, -1).join(' '), accent: words[words.length - 1] } -} - -/** - * The branded cover page shared by every report. - * - * `titleFontSize` is calculated by the caller when the title is data-driven (a template name can be - * any length); the fixed-title reports just take the default. - */ -export const CoverPage = ({ - styles, - theme, - size = 'A4', - orientation = 'portrait', - coverImage, - logo, - date, - label, - title, - accentTitle, - titleFontSize, - subtitle, - tenantName, - footerNote = 'Confidential & Proprietary', - children, -}) => ( - - {coverImage ? ( - - ) : null} - - - - {logo ? : null} - - {date ? {date} : null} - - - - {label ? {label} : null} - - {title} - {accentTitle ? ( - <> - {'\n'} - {accentTitle} - - ) : null} - - {subtitle ? {subtitle} : null} - {tenantName ? ( - - {tenantName} - - ) : null} - {children} - - - {footerNote ? ( - - {footerNote} - - ) : null} - -) - -/** - * Extra detail under the tenant name on the cover — a scan count, a grade, a date range. - * - * Passed to `ReportDocument` as `coverMeta`. It is created in the report's scope but rendered - * inside `CoverPage`, so it still reads the surrounding theme. - */ -export const CoverMeta = ({ lines = [], note, ...props }) => { - const { styles } = useReportStyles(props) - return ( - - {lines.filter(Boolean).map((line, index) => ( - - {line} - - ))} - {note ? {note} : null} - - ) -} - -/** - * Full-bleed chapter divider — the "statistic page" pattern from the executive report. A whole page - * rather than a block, because its background has to reach the paper edge. - */ -export const HeroPage = ({ - backgroundImage, - overtitle, - highlight, - headline, - subText, - footerText, - ...props -}) => { - const { styles, theme } = useReportStyles(props) - const report = useReport() - const size = props.size ?? report.size ?? 'A4' - const orientation = props.orientation ?? report.orientation ?? 'portrait' - - return ( - - {backgroundImage ? ( - - ) : null} - - {/* `overtitle` and `headline` bracket the big figure, so a statistic can read as a sentence - — "Every / 39 / seconds" — rather than a number with a caption under it. */} - {overtitle ? ( - {overtitle} - ) : null} - {highlight ? ( - {highlight} - ) : null} - {headline ? {headline} : null} - {subText ? {subText} : null} - - {footerText ? ( - {footerText} - ) : null} - - ) -} - -// A row of stat cards. Pass at most four — beyond that they get too narrow to read. -export const StatRow = ({ stats, ...props }) => { - const { styles } = useReportStyles(props) - return ( - - {stats.map((stat, index) => ( - - - {stat.value} - - {stat.label} - {stat.caption ? ( - {stat.caption} - ) : null} - - ))} - - ) -} - -/** - * Labelled progress bars. `value` and `max` are taken as given — a value over the maximum is - * clamped to a full bar rather than overflowing the track, since the number beside it still tells - * the true story. - */ -export const ProgressList = ({ items, ...props }) => { - const { styles, theme } = useReportStyles(props) - return ( - - {items.map((item, index) => { - const max = Number(item.max) > 0 ? Number(item.max) : 100 - const value = Number(item.value) || 0 - const percent = Math.max(0, Math.min(100, (value / max) * 100)) - const colour = - item.colour || theme?.series?.[index % (theme?.series?.length || 1)] - - return ( - - {item.label} - - - - - {item.display ?? `${Math.round(percent)}%`} - - - ) - })} - - ) -} - -/** - * A note in a panel, with the accent stripe down its left edge. - * - * `colour` recolours that stripe — the reports use it to grade a note by severity or risk band, and - * each one used to restate the whole `borderLeft: 4px solid …` shorthand to do it. `tintTitle` - * carries the colour up into the heading, which is what the risk-level cards want and the plain - * informational ones do not. - */ -export const INFO_TONES = { ok: 'okBox', warn: 'warnBox' } -const INFO_TONE_TITLES = { ok: 'okTitle', warn: 'warnTitle' } - -export const InfoBox = ({ - title, - colour, - tone, - tintTitle = false, - children, - ...props -}) => { - const { styles } = useReportStyles(props) - const toneStyle = INFO_TONES[tone] ? styles[INFO_TONES[tone]] : null - const toneTitle = INFO_TONE_TITLES[tone] - ? styles[INFO_TONE_TITLES[tone]] - : null - return ( - // wrap={false}: a callout is one card. Splitting it across a page break (title on one page, body on - // the next) is the artefact we avoid — react-pdf bumps a non-wrapping block that does not fit to the - // next page instead. Callouts are short by design, so they never exceed a page and get clipped. - - {title ? ( - - {title} - - ) : null} - {children} - - ) -} - -/** - * A small italic aside — "… and 14 more, see the export". Every report writes one after a truncated - * list and each had its own inline margin and italic. - */ -export const Note = ({ children, ...props }) => { - const { styles } = useReportStyles(props) - return {children} -} - -export const AlertBox = ({ title, colour, children, ...props }) => { - const { styles } = useReportStyles(props) - return ( - - - {title} - - {children} - - ) -} - -// The all-clear counterpart to AlertBox, for a check that found nothing. -export const ClearBox = ({ title, children, ...props }) => { - const { styles } = useReportStyles(props) - return ( - - {title} - {children} - - ) -} - -/** - * A single bullet. Written as a child of `BulletList` when the text carries its own markup — a - * data value, a second bold run — which the `items` array form cannot express. - */ -export const Bullet = ({ label, marker = '•', children, ...props }) => { - const { styles } = useReportStyles(props) - return ( - - {marker} - - {label ? {label} : null} - {children} - - - ) -} - -/** - * Bulleted list. Takes either an `items` array of `{ marker, label, text }` — the concise form for - * plain copy — or `Bullet` children when an entry needs markup of its own. - */ -export const BulletList = ({ items, children, ...props }) => { - const { styles } = useReportStyles(props) - return ( - - {items - ? items.map((item, index) => ( - - {item.text} - - )) - : children} - - ) -} - -/** - * Column widths for a table, as percentages of the table width. - * - * Every row in a react-pdf table is its own flex container, so `flex: 1` sizes a row's columns by - * what that row happens to contain — two rows with different content end up on different grids, and - * neither lines up with the header. Worse, a cell that carries both `flex` and a `width` grows past - * the width it was given. Percentages of the table width put every row on one grid by construction. - * - * `weights` are relative, so [2, 1, 1, 3] gives the first column twice the second and the last three - * times it. Pass the same array to the header and to every row. - */ -export const tableColumns = (weights) => { - const total = weights.reduce((sum, weight) => sum + weight, 0) || 1 - return weights.map((weight, index) => ({ - width: `${((weight / total) * 100).toFixed(4)}%`, - paddingRight: index === weights.length - 1 ? 0 : 6, - })) -} - -/** - * Fixed-width data table. `columns` is [{ header, key, width }] where width is a flex ratio. - * Rows beyond `limit` are dropped with a note rather than running to hundreds of pages — the - * full set is always available from the table export on the page itself. - */ -/** - * The one table in the design system. - * - * A column is `{ header, key }` plus whatever presentation it needs: - * - * - `width` — flex weight, defaulting to 1 - * - `bold` — the identifying first column, usually - * - `align` — `'center'` / `'right'` - * - `colour` — `(row) => '#RRGGBB'`, for a value graded by severity or risk - * - `render` — `(row) => node`, the escape hatch for a cell that is not text - * - * Every report used to hand-write its rows to get any of these, which is how five tables ended up - * with five different ideas of a header cell — and how the executive report's policy table kept a - * `flex: 1` that fought the explicit widths and skewed every column. - */ -export const DataTable = ({ - columns, - rows, - limit = 25, - emptyText = 'Nothing to report.', - ...props -}) => { - const { styles } = useReportStyles(props) - const report = useReport() - const shown = rows.slice(0, limit) - const hidden = rows.length - shown.length - - // Percentages, not flex — see tableColumns. This used `flex: column.width` and had the exact - // misalignment that helper exists to prevent, so every table built on it was on a per-row grid. - const weights = columns.map((column) => column.width ?? 1) - const widths = tableColumns(weights) - const cellWidth = (index) => widths[index] - - // How wide each column actually is, in points, so an over-long value can be wrapped rather than - // left to overflow. react-pdf cannot break inside a word without drawing a hyphen, so the break - // has to be a real one that we place — see measureText.js. - const totalWeight = weights.reduce((sum, weight) => sum + weight, 0) || 1 - const tableWidth = - contentWidth(report.size ?? DEFAULT_PAGE_SETUP.size, report.orientation) - - TABLE_ROW_PADDING * 2 - const columnPoints = (index) => - (tableWidth * weights[index]) / totalWeight - CELL_GUTTER - - const cellText = (value, index, bold) => - wrapLongTokens( - value ?? '', - columnPoints(index), - styles.tableCell.fontSize, - bold - ) - - return ( - <> - - {hidden > 0 ? ( - - … and {hidden} more. Export the table from the report page for the - full list. - - ) : null} - - ) -} - -/** - * A status word in a table cell — "Compliant", "Report Only", "Denied". - * - * `tone` is the shared vocabulary: four reports each reached into the stylesheet for - * `statusPassed` / `statusInvestigate` / `statusFailed` by hand, which is the only reason any of - * them still needed a stylesheet in scope at all. - */ -export const STATUS_TONES = { - pass: 'statusPassed', - warn: 'statusInvestigate', - fail: 'statusFailed', - muted: 'statusSkipped', -} - -export const StatusText = ({ tone, children, ...props }) => { - const { styles } = useReportStyles(props) - const toneStyle = STATUS_TONES[tone] ? styles[STATUS_TONES[tone]] : null - return ( - - {children} - - ) -} - -// Shared severity vocabulary so every report grades findings the same way. -export const severityColour = (severity) => { - switch (severity) { - case 'high': - return REPORT_COLOURS.danger - case 'medium': - return REPORT_COLOURS.warning - default: - return REPORT_COLOURS.success - } -} diff --git a/frontend/src/components/CippPdf/reportPdfStyles.js b/frontend/src/components/CippPdf/reportPdfStyles.js index 504a97574c..4242801c35 100644 --- a/frontend/src/components/CippPdf/reportPdfStyles.js +++ b/frontend/src/components/CippPdf/reportPdfStyles.js @@ -1,4 +1,3 @@ -import { StyleSheet } from '@react-pdf/renderer' import { asReportTheme, DEFAULT_BRAND_COLOUR, REPORT_COLOURS, withAlpha } from './reportTheme' // The @react-pdf/renderer style system shared by CIPP's client-facing PDF reports: a cover page, @@ -28,8 +27,7 @@ export const PAGE_ORIENTATIONS = [ export const DEFAULT_PAGE_SETUP = { size: 'A4', orientation: 'portrait' } -// Paper widths in points, matching the names above. Needed because a table has to know how wide its -// columns actually are to wrap a long value without a hyphen — see measureText.js. +// Paper widths in points, matching the names above; the report builder sizes its page setup off these. const PAGE_WIDTHS = { A4: 595.28, LETTER: 612, LEGAL: 612, A3: 841.89, A5: 419.53 } const PAGE_HEIGHTS = { A4: 841.89, LETTER: 792, LEGAL: 1008, A3: 1190.55, A5: 595.28 } @@ -87,7 +85,7 @@ export const createReportStyles = (themeOrColour = DEFAULT_BRAND_COLOUR) => { // header band, which are brand surfaces rather than any one content role. const brandColor = theme.primary - return StyleSheet.create({ + return ({ page: { flexDirection: 'column', backgroundColor: '#FFFFFF', diff --git a/frontend/src/components/CippPdf/reportTheme.js b/frontend/src/components/CippPdf/reportTheme.js index 91c74dd1b6..49a1414875 100644 --- a/frontend/src/components/CippPdf/reportTheme.js +++ b/frontend/src/components/CippPdf/reportTheme.js @@ -306,9 +306,9 @@ export const buildPalette = (branding, { primary, secondary }) => { * footer might want — `%tenantname%` foremost — is already a CIPP variable, so it is not restated * here. The `report` prefix keeps these clear of the reserved names in Get-CIPPTextReplacement. * - * A PDF renders in the browser, so Get-CIPPTextReplacement never sees this text. `useReportVariables` - * supplies the resolved values instead. Being a CIPP variable is about where it is documented and - * offered, not about who substitutes it. + * The PDFs render server-side, where ConvertTo-CippReportPdf runs the branding text through + * Get-CIPPTextReplacement; the branding editor's cover mock substitutes the two report tokens itself. + * Being a CIPP variable is about where it is documented and offered, not about who substitutes it. */ export const REPORT_VARIABLES = [ { value: '%reportname%', label: 'Report name' }, @@ -322,8 +322,8 @@ export const REPORT_VARIABLES = [ * and an unknown token is left as written rather than blanked — that is what tells whoever * configured it that they mistyped, instead of silently swallowing it. * - * Given the values rather than looking them up: the report's own tokens plus whatever - * `useReportVariables` resolved for the tenant. + * Given the values rather than looking them up: the report's own tokens plus any tenant values the + * caller already holds. */ export const applyReportVariables = (template, variables = {}) => { if (!template) return '' diff --git a/frontend/src/components/CippPdf/useBrandingSettings.js b/frontend/src/components/CippPdf/useBrandingSettings.js index 95872bb9b7..a63f5f4421 100644 --- a/frontend/src/components/CippPdf/useBrandingSettings.js +++ b/frontend/src/components/CippPdf/useBrandingSettings.js @@ -21,12 +21,16 @@ export const DEFAULT_BRANDING = Object.freeze({ logoUploads: [], coverImage: null, coverUploads: [], + // The gallery covers by id and name, for pickers that offer them (the report builder's Infographic). + coverImages: [], footerText: '', coverFooterText: '', showFooter: true, showPageNumbers: true, watermarkText: '', watermarkEnabled: true, + // Which of the tenant's names a report prints: 'alias' (the name CIPP shows), 'name' or 'domain'. + tenantLabel: 'alias', reportDefaults: {}, roleColours: {}, }) diff --git a/frontend/src/components/CippPdf/useReportVariables.js b/frontend/src/components/CippPdf/useReportVariables.js deleted file mode 100644 index 8d19f69790..0000000000 --- a/frontend/src/components/CippPdf/useReportVariables.js +++ /dev/null @@ -1,47 +0,0 @@ -import { useMemo } from 'react' -import { ApiGetCall } from '../../api/ApiCall' -import { useSettings } from '../../hooks/use-settings' - -// One shared reference for the empty case, so callers can put it in a dependency array. -const EMPTY = {} - -/** - * Resolved values of every CIPP variable, for substitution into report footers and watermarks by - * `applyReportVariables`. A PDF renders in the browser and never passes through - * Get-CIPPTextReplacement, so the values are read from ListCustomVariables instead. - * - * Fetched by the caller rather than inside the document: react-pdf renders through its own - * reconciler, outside the React tree, where there is no query client. - */ -export const useReportVariables = (tenantFilter) => { - const currentTenant = useSettings()?.currentTenant - const tenant = tenantFilter ?? currentTenant - - // AllTenants has no single answer for %tenantname% and the endpoint declines to guess one. - const enabled = Boolean(tenant) && tenant !== 'AllTenants' - - const variables = ApiGetCall({ - url: '/api/ListCustomVariables', - data: { tenantFilter: tenant }, - queryKey: `ReportVariables-${tenant}`, - waiting: enabled, - staleTime: Infinity, - }) - - return useMemo(() => { - const results = variables.data?.Results - if (!Array.isArray(results)) return EMPTY - - const resolved = {} - for (const variable of results) { - // Valueless variables (mostly system tokens, expanded on an endpoint) are left out, so the - // token stays written in the footer rather than resolving to nothing. - if (variable?.Name && variable.Value !== null && variable.Value !== undefined && variable.Value !== '') { - resolved[variable.Name] = variable.Value - } - } - return resolved - }, [variables.data]) -} - -export default useReportVariables diff --git a/frontend/src/components/CippPdf/useServerPdf.jsx b/frontend/src/components/CippPdf/useServerPdf.jsx new file mode 100644 index 0000000000..b84ac5246a --- /dev/null +++ b/frontend/src/components/CippPdf/useServerPdf.jsx @@ -0,0 +1,120 @@ +import { useEffect, useState } from 'react' +import { Box, CircularProgress, Typography } from '@mui/material' + +const requestInit = (body) => + body + ? { + method: 'POST', + credentials: 'same-origin', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + } + : { credentials: 'same-origin' } + +/** Fetch a server-rendered PDF as a Blob (GET, or POST when `body` is given); rejects with the HTTP status. */ +export const fetchServerPdf = (url, body) => + fetch(url, requestInit(body)).then((res) => + res.ok ? res.blob() : Promise.reject(res.status) + ) + +const saveUrl = (href, fileName) => { + const link = document.createElement('a') + link.href = href + link.download = fileName + document.body.appendChild(link) + link.click() + document.body.removeChild(link) +} + +/** Render on the server and save the result straight to a file, without a preview. */ +export const downloadServerPdf = (url, body, fileName) => + fetchServerPdf(url, body).then((blob) => { + const objectUrl = URL.createObjectURL(blob) + saveUrl(objectUrl, fileName) + URL.revokeObjectURL(objectUrl) + }) + +const idle = { pdfUrl: '', loading: false, error: null } + +/** + * Fetches a server-rendered PDF as an object URL for an iframe, re-fetching when the request changes + * and revoking the URL on change or unmount. `enabled` gates the fetch, so a dialog only renders while + * open. `error` is the HTTP status of a failed fetch (0 for a network failure), else null. + */ +export const useServerPdf = ({ url, body, enabled = true }) => { + const [state, setState] = useState(idle) + const requestKey = enabled ? JSON.stringify({ url, body }) : '' + + useEffect(() => { + if (!requestKey) { + setState((prev) => (prev === idle ? prev : idle)) + return undefined + } + let objectUrl + let cancelled = false + setState({ pdfUrl: '', loading: true, error: null }) + fetchServerPdf(url, body) + .then((blob) => { + if (cancelled) return + objectUrl = URL.createObjectURL(blob) + setState({ pdfUrl: objectUrl, loading: false, error: null }) + }) + .catch((status) => { + if (!cancelled) + setState({ + pdfUrl: '', + loading: false, + error: typeof status === 'number' ? status : 0, + }) + }) + return () => { + cancelled = true + if (objectUrl) URL.revokeObjectURL(objectUrl) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [requestKey]) + + return { + ...state, + download: (fileName) => state.pdfUrl && saveUrl(state.pdfUrl, fileName), + } +} + +const centred = { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + height: '100%', + gap: 2, + p: 4, + textAlign: 'center', +} + +/** The preview pane for a server-rendered PDF: spinner while rendering, `errorText` on failure, else the iframe. */ +export const ServerPdfPane = ({ pdfUrl, loading, error, errorText, title }) => { + if (loading) { + return ( + + + Generating report… + + ) + } + if (error) { + return ( + + + {errorText} + + + ) + } + if (!pdfUrl) return null + return ( +