Skip to content

Add PhoneField content field with intl-tel-input integration#468

Merged
MikeAlhayek merged 22 commits into
mainfrom
mikealhayek/adding-editors
Jun 7, 2026
Merged

Add PhoneField content field with intl-tel-input integration#468
MikeAlhayek merged 22 commits into
mainfrom
mikealhayek/adding-editors

Conversation

@MikeAlhayek

@MikeAlhayek MikeAlhayek commented Jun 6, 2026

Copy link
Copy Markdown
Member

Motivation

The Omnichannel Management module's PhoneNumberInfoPart.Number was originally a TextField, which can only store a single string value. This made it impossible to correctly reload the country flag when editing a phone number, because countries like the US and Canada share the same calling code (+1). A dedicated field type is needed to persist the country code alongside the phone number.

Approach

New PhoneField content field

Adds a custom PhoneField in the new CrestApps.OrchardCore.ContentFields module that stores three properties:

  • PhoneNumber -- full E.164 format (e.g. +14155552671)
  • CountryCode -- ISO 3166-1 alpha-2 code (e.g. US, CA) for correct flag display
  • NationalNumber -- the local portion without the country calling code

The field includes a display driver with server-side validation via IPhoneNumberService, a settings driver (with Required, Hint, DefaultCountryCode), and edit/display/settings views.

Shared intl-tel-input resources

The intl-tel-input library assets (JS, CSS, flag images) live in CrestApps.OrchardCore.Resources with CDN + SRI hashes and local fallback copies. ContentFields depends on Resources for reusability.

Gulp 5 binary copy fix

Fixed buildCopyPipeline in gulpfile.js to pass { encoding: false } so Gulp 5 does not corrupt binary assets (.webp flag images) during copy.

Omnichannel migration

  • PhoneNumberInfoPart.Number changed from TextField to PhoneField
  • ContactMethodMigrations.UpdateFrom1Async removes the old TextField and adds a PhoneField
  • All downstream code (OmnichannelContactIndexProvider, OmnichannelHelper, import handler, tests) updated from .Number.Text to .Number.PhoneNumber

Validation

  • .NET build passes with zero warnings (-warnaserror)
  • All 338 unit tests pass
  • Asset rebuild (npm run rebuild) clean
  • Docusaurus docs site builds successfully

MikeAlhayek and others added 2 commits June 5, 2026 18:29
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown

This pull request has merge conflicts. Please resolve those before requesting a review.

@MikeAlhayek

Copy link
Copy Markdown
Member Author

@copilot resolve the merge conflicts in this pull request

Co-authored-by: MikeAlhayek <24724371+MikeAlhayek@users.noreply.github.com>

Copilot AI commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Merge conflicts have been resolved in commit Merge origin/main. The four conflicted files were addressed as follows:

  • changelog/v2.0.0.md – kept both sets of bullet points (this PR's entries + main's new entries)
  • omnichannel/management.md – kept both the InternationalTelephone note from this PR and the ContactMethods enforcement documentation from main
  • Manifest.cs – included both CrestApps.OrchardCore.ContentFields (this PR) and PhoneNumbersConstants.Features.Area (main) as dependencies
  • ContactMethodMigrations.cs – used the InternationalTelephone editor (this PR's intent) while adopting main's code style for the Number field configuration

MikeAlhayek and others added 3 commits June 5, 2026 19:32
Remove hardcoded CSS variable overrides for flag image paths that were
breaking image resolution. The library's own CSS uses relative paths
(../img/) which correctly resolve both from CDN and local static files.

Add SRI integrity hashes (sha384) for both the CSS and JS CDN resources
to ensure subresource integrity verification.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@MikeAlhayek MikeAlhayek changed the title Add international telephone TextField editor Add InternationalTelephone TextField editor using intl-tel-input Jun 6, 2026
@MikeAlhayek

Copy link
Copy Markdown
Member Author

@copilot i think the .webp file used for the flags needs to also be copied from the node_modules folder using the copy action in assets.json

Copilot AI commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

@copilot i think the .webp file used for the flags needs to also be copied from the node_modules folder using the copy action in assets.json

The .webp flag images are already copied via Assets.json copy actions in the CrestApps.OrchardCore.Resources module (which owns the intl-tel-input npm package):

{ "copy": true, "inputs": ["node_modules/intl-tel-input/build/img/flags.webp"],    "output": "wwwroot/vendors/intl-tel-input/img/flags.webp" },
{ "copy": true, "inputs": ["node_modules/intl-tel-input/build/img/flags@2x.webp"],  "output": "wwwroot/vendors/intl-tel-input/img/flags@2x.webp" },
{ "copy": true, "inputs": ["node_modules/intl-tel-input/build/img/globe.webp"],     "output": "wwwroot/vendors/intl-tel-input/img/globe.webp" },
{ "copy": true, "inputs": ["node_modules/intl-tel-input/build/img/globe@2x.webp"],  "output": "wwwroot/vendors/intl-tel-input/img/globe@2x.webp" }

The ContentFields module's Assets.json only handles the editor-specific JS/CSS; the vendor assets (JS, CSS, and images) live in Resources since that's where the intl-tel-input npm dependency is declared. The ContentFields manifest declares CrestApps.OrchardCore.Resources as an explicit dependency, so the images are always available at the relative ../img/ path the library's CSS expects.

MikeAlhayek and others added 2 commits June 5, 2026 20:27
Gulp 5 defaults gulp.src() to encoding: 'utf8' which corrupts binary
files during copy operations. Add { encoding: false } to the copy
pipeline to preserve binary data integrity.

Also add .gitattributes rules to explicitly mark image and font files
as binary to prevent future text-mode corruption.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the InternationalTelephone TextField editor with a dedicated PhoneField
content field that stores the E.164 phone number, ISO country code, and national
number separately. This ensures the correct country flag is always displayed when
the field is loaded for editing, even for countries that share a calling code
(e.g. US and CA both use +1).

- Add PhoneField, PhoneFieldSettings, display driver, and settings driver
- Add edit, display, and settings views with intl-tel-input integration
- Migrate PhoneNumberInfoPart.Number from TextField to PhoneField
- Update Omnichannel index provider, helper, import handler, and tests
- Update documentation, changelog, and sidebar navigation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@MikeAlhayek MikeAlhayek changed the title Add InternationalTelephone TextField editor using intl-tel-input Add PhoneField content field with intl-tel-input integration Jun 6, 2026
MikeAlhayek and others added 9 commits June 6, 2026 09:23
Replace the plain DefaultCountryCode text input with a three-option
InitialCountryMode setting (Globe, Current culture, Specific) that controls
which country flag is pre-selected when the phone field is empty. Specific
mode shows a dropdown of all supported countries populated from
IPhoneNumberService.GetSupportedRegions().

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Schedule a background deferred task in UpdateFrom1Async that migrates
existing PhoneNumber content items from the old TextField format
(Number.Text) to the new PhoneField format (Number.PhoneNumber,
Number.CountryCode, Number.NationalNumber). Assumes US region for
existing records, validates via IPhoneNumberService, and skips invalid
numbers. Processes in batches of 100 with a separate session per batch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rewrite MigratePhoneNumberDataAsync to:
- First query IContentDefinitionManager for all content types that have
  PhoneNumberInfoPart attached instead of hardcoding a single type
- Use a while loop with Skip/Take pagination (100 per batch)
- Create a fresh session per batch to avoid holding all items in memory
- Break when a batch returns no more records

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PhoneNumber content items are embedded inside a BagPart named
ContactMethods on parent content items that have OmnichannelContactPart.
They are not separate YesSql documents, so the previous approach of
querying ContentItemIndex for PhoneNumber type would find nothing.

Now the migration:
- Discovers content types with OmnichannelContactPart attached
- Queries those parent documents via cursor-based pagination
- Navigates into ContactMethods BagPart -> ContentItems array
- Migrates each inner PhoneNumber item's Number field from TextField
  to PhoneField format

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ContentTransferMigrations.UpdateFrom1Async() was failing with a
SqliteException because it queries the Direction column that only exists
in the v2 CreateAsync schema. Tenants at v1 never had this column added.

The exception caused the DataMigrationManager to cancel the YesSql
session, which prevented ALL other migration version updates in the same
startup cycle from being committed -- including ContactMethodMigrations.
This caused ContactMethodMigrations to re-run from version 1 on every
app restart, and its schema changes (TextField to PhoneField) were never
persisted.

Fix: Add the Direction column and its index in UpdateFrom1Async before
attempting to use it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MikeAlhayek and others added 5 commits June 6, 2026 11:39
OmnichannelContactsMigrations.UpdateFrom2Async was throwing an uncaught
SqliteException (duplicate column name: TimeZoneId) because the DDL
committed on a previous run but the version was never saved. This
cancelled the YesSql session and prevented ContactMethodMigrations from
persisting its version update.

Also downgrade ContentTransferMigrations catch from LogError to
LogWarning since the duplicate column scenario is expected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use a MutationObserver to detect new [data-phone-field] elements added
to the DOM after page load (e.g. when adding a contact method via
BagPart). The observer watches document.body for childList mutations
and initializes intl-tel-input on any newly inserted phone fields.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@MikeAlhayek MikeAlhayek merged commit c27950b into main Jun 7, 2026
9 checks passed
@MikeAlhayek MikeAlhayek deleted the mikealhayek/adding-editors branch June 7, 2026 01:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants