Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Migration - Migrate Customer service options page to Symfony#41421

Open
ga-devfront wants to merge 2 commits into
PrestaShop:migration/customer-servicefrom
ga-devfront:migration/customer-service-options
Open

Migration - Migrate Customer service options page to Symfony#41421
ga-devfront wants to merge 2 commits into
PrestaShop:migration/customer-servicefrom
ga-devfront:migration/customer-service-options

Conversation

@ga-devfront
Copy link
Copy Markdown
Contributor

@ga-devfront ga-devfront commented May 6, 2026

Questions Answers
Branch? develop
Description? Migrates the Customer service options page (Sell > Customer Service > Customer Service > Options) to the Symfony controller behind feature flag customer_threads. Replaces the two legacy fields_options panels with iso-functional Symfony forms backed by AbstractMultistoreConfiguration. Part of the AdminCustomerThreads → Symfony migration. Built on top of #41420 (KPI strip + listing stats); rebase against develop once #41418 and #41420 merge.
Type? improvement
Category? BO
BC breaks? no
Deprecations? no
How to test? (1) Enable the customer_threads feature flag. (2) Open Sell > Customer Service > Customer Service > Options. (3) Verify the Contact options panel saves the file-upload toggle and the translatable default signature for each language. (4) Verify the Customer service options panel saves the IMAP server URL, port, user, password and the seven IMAP option toggles. (5) Run composer create-test-db && ./vendor/bin/behat -c tests/Integration/Behaviour/behat.yml -s customer_service — expect 9 scenarios green.
UI Tests https://github.com/ga-devfront/ga.tests.ui.pr/actions/runs/26462102053
Fixed issue or discussion? Part of the AdminCustomerThreads → Symfony migration umbrella tracked in #41417.
Related PRs Stacked on top of #41420 (KPI strip + listing stats), itself stacked on #41418 (Behat retrofit). Please merge upstream PRs first; this branch will rebase cleanly.
Sponsor company @PrestaShopCorp

What this PR adds

Configuration adapters

Two new classes under src/Adapter/CustomerService/Configuration/, both extending AbstractMultistoreConfiguration so values respect the active ShopConstraint:

  • CustomerServiceOptionsConfiguration — Contact options panel: file_upload (PS_CUSTOMER_SERVICE_FILE_UPLOAD) and translatable signature (PS_CUSTOMER_SERVICE_SIGNATURE, stored as a per-language array).
  • ImapConfiguration — IMAP panel: 13 settings covering connection (URL / port / user / password), behaviour flags (delete after sync, create unrecognized threads) and the seven IMAP option toggles. The legacy concatenated PS_SAV_IMAP_OPT value is intentionally not written: grep across the whole codebase confirms no consumers, and syncImap rebuilds the option string from the individual PS_SAV_IMAP_OPT_* toggles at call time.

Form layer

Following the existing LogsController convention used for option pages elsewhere in PrestaShop:

  • CustomerServiceOptionsType and ImapOptionsType (Symfony form types). The signature uses TranslatableType over TextareaType; booleans use SwitchType.
  • CustomerServiceOptionsFormDataProvider and ImapOptionsFormDataProvider bridging the forms to the configuration adapters via FormDataProviderInterface.
  • Two Core\Form\Handler services (prestashop.adapter.customer_service.options.form_handler and prestashop.adapter.customer_service.imap.form_handler).

Controller actions and routing

CustomerThreadController gains:

  • optionsAction (GET /options) — renders both forms.
  • saveOptionsAction (POST /options) — saves the Contact panel.
  • saveImapOptionsAction (POST /options/imap) — saves the IMAP panel.
  • A private processOptionsForm helper shared by both save actions.

The two save flows are isolated so a validation error on one panel doesn't reject the other (matches legacy iso-functional behaviour with two separate submit buttons).

All routes carry _legacy_feature_flag: customer_threads; merchants on the legacy controller continue to see the old options panels until the flag is enabled.

Behat coverage

customer_service_options.feature exercises both data providers through the DI container and asserts persisted values via the legacy Configuration::get adapter:

  • Save the Contact options panel (file upload + translatable signature).
  • Toggle the file upload off.
  • Save the full IMAP panel (URL, port, user, password, behaviour flags, option toggles).

Step regexes are anchored so the boolean-state checks (should be enabled / should be disabled) don't conflate with string-value checks (should be "...").

What is intentionally NOT in this PR

  • CQRS commands for options updates — PrestaShop's option pages traditionally use DataConfigurationInterface directly (Logs, Customer preferences, Invoice options, etc.). Introducing CQRS commands here would add boilerplate without benefit and diverge from the surrounding codebase. The Admin API in a later PR will inject the configuration adapters directly, the same way the BO controller does.
  • IMAP connection validation — the legacy isValidImapUrl validator is not yet ported to a Symfony constraint. Will land alongside PR 4 (IMAP sync) which is the only consumer.
  • Playwright additions — existing 04_contactOptions.ts and 05_customerServiceOptions.ts campaigns already exercise the same field labels and DB keys; they will be re-run against the migrated page in the dedicated Playwright PR.

@ga-devfront ga-devfront requested a review from a team as a code owner May 6, 2026 16:26
@github-project-automation github-project-automation Bot moved this to Ready for review in PR Dashboard May 6, 2026
@ps-jarvis ps-jarvis added Improvement Type: Improvement develop Branch labels May 6, 2026
@ga-devfront ga-devfront added the Blocked Status: The issue is blocked by another task label May 6, 2026
@ga-devfront ga-devfront added this to the 9.2.0 milestone May 6, 2026
Copy link
Copy Markdown
Collaborator

@cnavarro-prestashop cnavarro-prestashop left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$resolver = (new OptionsResolver())
->setDefined(array_keys(self::FIELD_TO_CONFIG_KEY))
->setAllowedTypes('imap_url', 'string')
->setAllowedTypes('imap_port', ['string', 'int'])
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why having int and string ? Easier ton handle string no ?

Comment on lines +89 to +91
foreach (self::FIELD_TO_CONFIG_KEY as $field => $configKey) {
$this->updateConfigurationValue($configKey, $field, $configuration, $shopConstraint);
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imap_password belongs to FIELD_TO_CONFIG_KEY. If in the form type there's not changed for imap_password it's supposed to be empty. When you'll save it will erase the imap_password in configuration. You should ignore the field imap_password if $configuration[$field] is empty :

Suggested change
foreach (self::FIELD_TO_CONFIG_KEY as $field => $configKey) {
$this->updateConfigurationValue($configKey, $field, $configuration, $shopConstraint);
}
foreach (self::FIELD_TO_CONFIG_KEY as $field => $configKey) {
if ($field === 'imap_password' && $configuration[$field] === '') {
continue;
}
$this->updateConfigurationValue($configKey, $field, $configuration, $shopConstraint);
}

@ps-jarvis ps-jarvis added the Waiting for author Status: action required, waiting for author feedback label May 7, 2026
@ps-jarvis ps-jarvis moved this from Ready for review to Waiting for author in PR Dashboard May 7, 2026
*/
#[AsQueryHandler]
final class GetCustomerServiceListingStatisticsHandler implements GetCustomerServiceListingStatisticsHandlerInterface
{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This handler performs multiple separate database queries to aggregate statistics. To improve performance, consider combining these into a single query using COUNT(IF(...)) or GROUP BY. Also, it should explicitly handle a ShopConstraint instead of relying on the global legacy context.

Suggested fix: Optimize to a single query and use explicit ShopConstraint.

return array_map('intval', $customerThreadIds);
}

private function processOptionsForm(Request $request, FormHandlerInterface $formHandler): RedirectResponse
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using guard clauses here would reduce the nesting level and improve readability.

Suggested fix:

private function processOptionsForm(Request $request, FormHandlerInterface $formHandler): RedirectResponse
{
    $form = $formHandler->getForm();
    $form->handleRequest($request);

    if (!$form->isSubmitted() || !$form->isValid()) {
        return $this->redirectToRoute('admin_customer_threads_options');
    }

    $saveErrors = $formHandler->save($form->getData());
    // ...
}

@ga-devfront ga-devfront changed the base branch from develop to migration/customer-service May 26, 2026 16:12
Adds the Customer service options page (Sell > Customer Service >
Customer Service > Options) to the Symfony controller, replacing the
legacy fields_options panels behind feature flag customer_threads.

Splits the legacy single-page options into two iso-functional panels:

  * Contact options
    file_upload (PS_CUSTOMER_SERVICE_FILE_UPLOAD)
    signature   (PS_CUSTOMER_SERVICE_SIGNATURE, translatable)

  * Customer service options (IMAP)
    13 keys covering URL/port/user/password, the two behavior flags
    (delete after sync, create unrecognized threads), and the seven
    IMAP option toggles. The legacy PS_SAV_IMAP_OPT concatenated value
    is intentionally not written: grep confirms no consumers, and the
    sync handler rebuilds the option string from the individual
    toggles at call time.

Architecture follows the LogsController convention used elsewhere in
PrestaShop for option pages:

  CustomerServiceOptionsConfiguration / ImapConfiguration
    extend AbstractMultistoreConfiguration so values are stored with
    the proper ShopConstraint and respect multi-shop scoping.

  CustomerServiceOptionsFormDataProvider / ImapOptionsFormDataProvider
    bridge the form layer to the configuration adapters via
    FormDataProviderInterface.

  CustomerServiceOptionsType / ImapOptionsType
    Symfony form types with translatable signature (TranslatableType
    over TextareaType) and the standard SwitchType for booleans.

  CustomerThreadController
    new optionsAction (GET) renders both forms; saveOptionsAction and
    saveImapOptionsAction (POST) delegate to a shared
    processOptionsForm helper.

  Routes
    admin_customer_threads_options          (GET  /options)
    admin_customer_threads_options_save     (POST /options)
    admin_customer_threads_imap_options_save (POST /options/imap)
    All carry _legacy_feature_flag: customer_threads.

Behat:
  customer_service_options.feature with three scenarios driving the
  contact and IMAP form data providers through the container and
  asserting Configuration values via the legacy adapter. Dedicated
  step regexes disambiguate "should be \"value\"" from
  "should be enabled / disabled".
@ga-devfront ga-devfront force-pushed the migration/customer-service-options branch from 51709a5 to 5b23e6e Compare May 26, 2026 16:32
The two multi-line render(controller(...)) and include(...) calls in
the migrated CustomerThread listing template were missing a trailing
comma on their last argument. The current twig-cs-fixer ruleset
flags any multi-line array / parameter list without one, so CI fails
even though the template renders correctly.
@ga-devfront ga-devfront removed Waiting for author Status: action required, waiting for author feedback Blocked Status: The issue is blocked by another task labels May 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

develop Branch Improvement Type: Improvement

Projects

Status: Waiting for author

Development

Successfully merging this pull request may close these issues.

4 participants