Aller au contenu

Référence des hooks

Tous les hooks ci-dessous sont réellement émis par le code, à l’exception de celui signalé en fin de page. Ils sont diffusés par EventDispatcher, qui préfixe les noms par solis_forms_ : ce sont des hooks WordPress ordinaires, consommables avec add_action() / add_filter().

Les actions reçoivent un objet unique plutôt qu’une liste d’arguments positionnels : ajouter une donnée à une charge utile ne casse alors aucun abonné existant.

Actions

solis_forms_register_field_types

Ajouter des types de champs. Émis une fois par requête, à la première consultation du registre.

  • Charge utile : SolisForms\Domain\Field\FieldTypeRegistry
  • Émis dans : FieldTypeRegistry::notify_extensions()
add_action( 'solis_forms_register_field_types', function ( $registry ) {
    $registry->register( new Mon_Champ_Signature() );
} );

solis_forms_register_addons

Enregistrer une extension. Émis sur init en priorité 5, avant les traitements branchés à la priorité par défaut.

  • Charge utile : SolisForms\AddOns\AddOnManager
  • Émis dans : AddOnsServiceProvider::boot()
add_action( 'solis_forms_register_addons', function ( $manager ) {
    $manager->register( new Mon_Extension() );
} );

solis_forms_entry_created

Une soumission vient d’être enregistrée. C’est le point d’accroche des intégrations externes (CRM, webhook, journalisation).

  • Charge utile : Events\Payload\EntryCreatedEvent — entry, form, form_id
  • Émis dans : SubmissionHandler::handle()
  • Note : entry->values contient les valeurs réellement enregistrées.
add_action( 'solis_forms_entry_created', function ( $event ) {
    error_log( sprintf( 'Soumission %d sur le formulaire %d', $event->entry->id, $event->form_id ) );
} );

solis_forms_form_saved

Un formulaire a été créé ou mis à jour via l’API REST.

  • Charge utile : Events\Payload\FormSavedEvent — form, is_new
  • Émis dans : FormsController::create() et ::update()

solis_forms_captcha_verification_failed

Le service de vérification du captcha est injoignable ou répond de façon inexploitable — jamais lorsqu’il rejette légitimement un jeton. La soumission est refusée par prudence ; sans abonné à ce hook, une clé expirée bloquerait tout le trafic sans laisser de trace.

  • Charge utile : Events\Payload\CaptchaVerificationFailedEvent — provider, form_id, reason
  • Émis dans : AbstractCaptchaProvider

solis_forms_stats_page_rendered

La page des statistiques a fini de se rendre. Une extension peut y ajouter sa propre lecture — c’est ce que fait l’analytique de friction.

  • Charge utile : Events\Payload\StatsPageRenderedEvent — form_id, range, from, to
  • Émis dans : StatsPage::render()
  • Note : le formulaire est déjà vérifié comme accessible à l’utilisateur, et les bornes de dates sont déjà calculées. Les recalculer soi-même finirait par diverger d’un jour selon le fuseau retenu.
add_action( 'solis_forms_stats_page_rendered', function ( $event ) {
    printf(
        '<p>Formulaire %d, du %s au %s</p>',
        $event->form_id,
        esc_html( $event->from ),
        esc_html( $event->to )
    );
} );

solis_forms_addon_boot_failed

Une extension a levé une erreur pendant son démarrage. L’exception ayant été interceptée pour ne pas mettre le site à terre, ce hook est le seul moyen d’en être informé.

  • Charge utile : Events\Payload\AddOnBootFailedEvent — addon_id, addon_name, exception
  • Émis dans : AddOnManager::boot_all()

Filtres

solis_forms_settings_sections

Ajouter une section à l’écran des réglages globaux. Une clé d’API ne se range pas par formulaire : c’est ici qu’une intégration place la sienne.

La section obtient d’un coup le stockage, le rendu, l’assainissement et sa valeur par défaut ; il n’y a aucune interface à écrire. Elle se relit ensuite par PluginSettings::section( 'ma-section' ).

  • Valeur : array<string, array{title: string, description: string|callable, fields: array<string, array{label: string, type: string, default: string, choices?: array, help?: string}>}>
  • Émis dans : SettingsRegistry::sections()
  • Types de champs rendus : text, password, number, select, checkbox
add_filter( 'solis_forms_settings_sections', function ( array $sections ) {
    $sections['mon-crm'] = array(
        'title'       => 'Mon CRM',
        'description' => 'Identifiants du compte.',
        'fields'      => array(
            'api_key' => array(
                'label'   => 'Clé d\'API',
                'type'    => 'password',
                'default' => '',
            ),
        ),
    );

    return $sections;
} );

slf:step-changed (événement JavaScript)

Émis sur le formulaire à chaque changement d’étape, y compris au premier rendu. Il permet à un module public de savoir où en est le visiteur sans surveiller l’attribut hidden des pages — c’est-à-dire sans déduire une intention d’un détail d’affichage, qui se casse au premier changement de rendu.

  • Détail : { step, total }, tous deux à partir de 1
  • Émis dans : le module multistep du cœur
  • Remonte : oui (bubbles: true)
form.addEventListener( 'slf:step-changed', ( event ) => {
    console.log( `Étape ${ event.detail.step } sur ${ event.detail.total }` );
} );

solisForms.formSettingsPanels (filtre JavaScript)

Ajouter un panneau aux réglages du formulaire, dans le constructeur. C’est le pendant côté interface du filtre précédent : l’un porte les identifiants du compte, l’autre ce qui se règle formulaire par formulaire.

Le composant reçoit settings — l’intégralité des réglages du formulaire — et onChange, qui applique une modification partielle. Rangez vos valeurs sous une clé qui vous est propre, relue côté PHP par FormSettings::section().

  • Valeur : Array<{ name: string, render: Function }>
  • Émis dans : components/FormSettingsPanel.js
import { addFilter } from '@wordpress/hooks';

addFilter( 'solisForms.formSettingsPanels', 'mon-extension/crm', ( panels ) => [
    ...panels,
    { name: 'mon-crm', render: MonPanneau },
] );

solis_forms_settings_tabs

Renommer un onglet de l’écran des réglages, ou en ajouter un.

Le tableau est indexé par clé d’onglet ; la valeur est le libellé affiché. Une clé inconnue crée un onglet, et les sections déclarées par solis_forms_settings_sections s’y rangent en le nommant.

Retirer une clé n’efface pas ses sections : elles retombent dans l’onglet « Autre », plutôt que de disparaître d’un écran sans que personne le remarque.

  • Arguments : array<string, string> $titres
  • Émis dans : Settings\SettingsTabs::titles()
add_filter(
	'solis_forms_settings_tabs',
	static function ( array $titres ): array {
		$titres['mon_module'] = 'Mon module';

		return $titres;
	}
);

solis_forms_payment_gateways

Ajouter une passerelle de paiement.

  • Valeur : array<string, PaymentGatewayInterface>
  • Émis dans : GatewayRegistry::notify_extensions()
add_filter( 'solis_forms_payment_gateways', function ( array $gateways ) {
    $gateways['mollie'] = new Ma_Passerelle_Mollie();
    return $gateways;
} );

solis_forms_form_templates

Proposer des modèles de formulaires supplémentaires. Chaque entrée est indexée par identifiant et fournit label, description et path (chemin absolu vers un fichier au format de l’enveloppe d’export).

  • Valeur : array<string, array{label: string, description: string, path: string}>
  • Émis dans : TemplateRegistry::notify_extensions()
add_filter( 'solis_forms_form_templates', function ( array $templates ) {
    $templates['mon-modele'] = array(
        'label'       => 'Mon modèle',
        'description' => 'Formulaire préconfiguré.',
        'path'        => plugin_dir_path( __FILE__ ) . 'templates/mon-modele.json',
    );
    return $templates;
} );

solis_forms_rest_controllers

Servir ses propres routes REST. Le filtre reçoit les instances des contrôleurs du cœur : une extension construit le sien avec ses propres dépendances, que le conteneur du cœur ne connaît pas. Toute valeur qui n’étend pas AbstractController est écartée sans bruit — une extension mal réglée ne prive pas le site de son API.

  • Valeur : array<int, SolisForms\Rest\AbstractController>
  • Émis dans : RestServiceProvider::controllers(), sur rest_api_init
add_filter( 'solis_forms_rest_controllers', function ( array $controllers ) {
    $controllers[] = new Mon_Controleur();
    return $controllers;
} );

solis_forms_notifications

Choisir quelles notifications partent. Le cœur n’en retient qu’une — la première active — et le filtre reçoit ce choix, le formulaire et la soumission. Il peut lui substituer la liste qu’il veut : c’est ainsi que l’extension payante rétablit les notifications multiples et l’envoi conditionnel, qui ne se décide pas sans la soumission.

Une valeur qui n’est pas un tableau est ignorée.

  • Valeur : array<int, array<string, mixed>>
  • Contexte : SolisForms\Domain\Form, SolisForms\Domain\Entry
  • Émis dans : NotificationManager::notifications(), sur envoi et sur relance
add_filter( 'solis_forms_notifications', function ( array $notifications, $form, $entry ) {
    $notifications[] = array(
        'id'      => 'copie-comptabilite',
        'enabled' => true,
        'to'      => 'compta@example.com',
        'subject' => 'Nouvelle commande',
        'message' => 'Voir la soumission {entry_id}.',
    );

    return $notifications;
}, 10, 3 );

solis_forms_mail_args

Retoucher un courriel juste avant son expédition : copie, copie cachée, adresse de réponse, pièces jointes, sujet, corps. Chaque clé filtrée est confrontée au type attendu et retombe sur la valeur du cœur si elle n’y correspond pas.

Le journal d’envoi consigne les valeurs filtrées : c’est bien ce qui a été expédié qui reste consultable sur le détail d’une soumission.

  • Valeur : array{to: string[], subject: string, message: string, is_html: bool, cc: string[], bcc: string[], reply_to: string, attachments: string[], text_alternative: string}
  • Contexte : la notification, Form, Entry
  • Émis dans : NotificationManager::mail_args()
add_filter( 'solis_forms_mail_args', function ( array $args ) {
    $args['bcc'][] = 'archive@example.com';
    return $args;
} );

solis_forms_form_availability

Revoir la disponibilité d’un formulaire : liste d’attente, quota par rôle, ouverture anticipée pour un membre. Le filtre peut fermer un formulaire ouvert comme rouvrir un formulaire fermé — la disponibilité est une règle de gestion, pas un contrôle de sécurité. Ce qui n’est pas un AvailabilityStatus est ignoré.

Le contrôle étant refait à la soumission, une fermeture prononcée ici résiste à une requête forgée.

  • Valeur : SolisForms\Domain\Availability\AvailabilityStatus
  • Contexte : SolisForms\Domain\Form
  • Émis dans : FormAvailability::filtered()
use SolisForms\Domain\Availability\AvailabilityStatus;

add_filter( 'solis_forms_form_availability', function ( $status, $form ) {
    return ma_liste_est_pleine( $form->id ) ? AvailabilityStatus::QuotaReached : $status;
}, 10, 2 );

solis_forms_can_submit

Refuser une soumission que le cœur admettait : connexion exigée, quota propre, liste noire.

Le filtre ne va que dans un sens. Il n’est consulté que sur les soumissions déjà admises, et une valeur qui les admettrait de nouveau est ignorée : une extension peut durcir l’admission, jamais l’assouplir. Sans cette règle, une extension rouvrirait en une ligne ce que le nonce et les contrôles anti-spam viennent de refuser.

Les valeurs soumises ne sont pas transmises : un refus fondé sur le contenu relève d’un contrôle anti-spam, que SpamCheckManager sait accueillir et qui s’exécute au bon moment dans le quota de débit.

  • Valeur : SolisForms\Frontend\AuthorizationOutcome
  • Contexte : FormSettings, int $form_id
  • Émis dans : SubmissionAuthorization::hardened()
use SolisForms\Frontend\AuthorizationOutcome;

add_filter( 'solis_forms_can_submit', function ( $outcome, $settings, $form_id ) {
    return is_user_logged_in() ? $outcome : AuthorizationOutcome::RejectedExtension;
}, 10, 3 );

solis_forms_pro_active

Faire taire ce que le cœur annonce de l’extension payante. Répondre true retire les pastilles du constructeur, les panneaux verrouillés et l’entrée « SolisForms Pro » du menu : celui qui a payé n’a que faire d’une réclame.

Le contrôle passe par un filtre plutôt que par la présence d’une classe : le cœur n’a ainsi rien à connaître des noms de l’extension, et n’importe quelle distribution — Freemius, serveur maison, licence à vie — répond de la même façon.

  • Valeur : bool (faux par défaut)
  • Consulté dans : Support\ProFeatures::active()
add_filter( 'solis_forms_pro_active', '__return_true' );

Les champs, eux, n’ont pas besoin de ce filtre : un type de champ n’est annoncé que s’il est absent du registre. Une extension tierce qui déclare rating fait donc disparaître sa pastille sans rien avoir à dire.

solis_forms_pro_url

Changer la destination des pastilles. Par défaut, un écran interne du plugin — aucune adresse extérieure n’est codée en dur, et le plugin ne dirige nulle part sans qu’on l’ait décidé.

  • Valeur : string
  • Consulté dans : Support\ProFeatures::url()
add_filter( 'solis_forms_pro_url', fn (): string => 'https://mon-site.test/pro' );

solis_forms_pro_allowed_registration_roles

Ouvrir la liste des rôles qu’un formulaire peut attribuer à l’inscription. Par défaut, subscriber seul.

Le passage par le code est délibéré : un formulaire ne doit pas pouvoir élever les privilèges de qui le remplit depuis un menu de l’interface d’administration.

Un second contrôle s’applique après ce filtre et ne se lève pas : un rôle portant une capacité de reprise du site — manage_options, edit_users, install_plugins, unfiltered_html, solis_forms_manage_settings… — reste écarté, et administrator est refusé nommément. L’erreur à craindre n’est pas la malveillance de l’administrateur, c’est sa méprise : un rôle créé par une extension tierce peut porter edit_users sans que son nom le laisse deviner.

  • Valeur : array<int, string> (['subscriber'] par défaut)
  • Consulté dans : SolisFormsPro\UserAccounts\AccountRoles::allowed()
add_filter(
	'solis_forms_pro_allowed_registration_roles',
	static fn ( array $roles ): array => array_merge( $roles, array( 'membre' ) )
);

solis_forms_submission_errors

Refuser une soumission après validation des champs, juste avant son enregistrement. C’est par ce chemin que le règlement d’un formulaire payant est contrôlé : la transaction doit exister, être marquée payée, n’être rattachée à aucune autre soumission et porter exactement le montant attendu.

Le filtre ne peut que refuser : les erreurs déjà constatées ne lui sont pas soumises, il ne saurait donc les effacer. Un point d’extension capable de faire accepter ce que le cœur refuse finirait par le faire par mégarde.

  • Valeur : array<string, array<int, string>> — messages indexés par identifiant de champ
  • Contexte : Form, array $values (valeurs retenues)
  • Émis dans : SubmissionHandler::guarded()
add_filter( 'solis_forms_submission_errors', function ( array $errors, $form, array $values ) {
    if ( ! mon_reglement_est_conforme( $form, $values ) ) {
        $errors['reglement'] = array( 'Le règlement ne correspond pas.' );
    }

    return $errors;
}, 10, 3 );

Le pendant de ce garde est solis_forms_entry_created : c’est là qu’on rattache à la soumission ce que le garde a vérifié.

solis_forms_draft_saved

Diffusé après l’enregistrement d’un brouillon.

Le cœur enregistre, nettoie et signe ; une extension qui veut enrichir un brouillon — d’un consentement, d’une relance programmée — n’a pas à reproduire ces contrôles pour autant. Elle écoute, et travaille sur ce que le cœur a déjà retenu.

created distingue la première sauvegarde des suivantes : une sauvegarde automatique renouvelée toutes les deux minutes ne doit pas être traitée comme la découverte d’une saisie.

  • Charge : Events\Payload\DraftSavedEvent — entry_id, form, values, created
  • Diffusé dans : Frontend\SubmissionHandler::save_draft()
add_action(
	'solis_forms_draft_saved',
	static function ( SolisForms\Events\Payload\DraftSavedEvent $event ): void {
		if ( $event->created ) {
			// Première sauvegarde de cette saisie.
		}
	}
);

solis_forms_submission_values

Dernier mot sur les valeurs soumises, avant leur validation.

Sert à ce que le serveur décide et que le navigateur ne peut pas choisir : la valeur d’un champ verrouillé, par exemple, qu’une requête forgée aurait remplacée.

Le filtre s’applique avant la validation : une valeur substituée ici suit exactement le même chemin que celle d’un visiteur, et ne contourne donc aucun contrôle de champ.

  • Valeur : array<string, mixed>
  • Contexte : le Form soumis
  • Consulté dans : Frontend\SubmissionHandler::handle()
add_filter(
	'solis_forms_submission_values',
	static function ( array $values ): array {
		$values['source'] = 'interne';

		return $values;
	}
);

solis_forms_pro_prefill_value

Extension payante. Fournit la valeur d’un préremplissage de source métier.

C’est le seul endroit prévu pour brancher une source propre au site : il vit dans le code, sous la responsabilité de qui l’écrit. Ce que le filtre rend passe ensuite par la sanitation du type de champ visé.

  • Valeur : string (vide par défaut)
  • Contexte : la clé déclarée, l’identifiant du champ, le Form
  • Consulté dans : Prefill\PrefillResolver
add_filter(
	'solis_forms_pro_prefill_value',
	static function ( string $valeur, string $cle ): string {
		return 'numero_dossier' === $cle ? mon_numero_de_dossier() : $valeur;
	},
	10,
	2
);

solis_forms_pro_ai_providers

Extension payante. Ajouter un fournisseur de complétion à l’assistant d’administration.

Le module en livre un. En livrer plusieurs demanderait de maintenir plusieurs formes de requête, de réponse et d’erreur, pour une fonctionnalité qui doit rester facultative — ce filtre ouvre donc la porte à qui veut la sienne, comme le plugin le fait déjà pour les passerelles de paiement et les types de champs.

Une valeur qui n’implémente pas AiProviderInterface est écartée sans bruit : un module tiers mal réglé ne prive pas le site de son assistant.

Le contrat est volontairement étroit — un fournisseur reçoit un texte et rend un texte. Il ne connaît ni les formulaires, ni les champs : composer la demande appartient à PromptBuilder, juger la réponse à AiSuggestionValidator. Un fournisseur tiers n’a donc aucun moyen de faire entrer autre chose qu’un texte, qui sera reconstruit clé par clé avant d’atteindre un écran.

  • Valeur : array<string, AiProviderInterface>
  • Émis dans : Ai\AiProviderRegistry::all()
add_filter(
	'solis_forms_pro_ai_providers',
	static function ( array $fournisseurs ): array {
		$fournisseurs['mon-service'] = new Mon_Fournisseur();

		return $fournisseurs;
	}
);

solis_forms_pro_personal_data_providers

Extension payante. Déclarer un module qui détient des données liées à une personne, pour qu’il entre dans l’export et dans l’effacement du centre de confidentialité.

Le centre ne balaie pas les tables. Il le pourrait — elles sont toutes préfixées slf_ — mais il faudrait qu’il connaisse la colonne portant l’adresse dans chacune : vingt fois le même savoir au mauvais endroit, et un module ajouté demain serait oublié sans que l’oubli se voie. Un export incomplet a l’air complet.

Chaque module déclare donc ce qu’il détient. Celui qui ne le déclare pas n’apparaît ni à l’export ni à l’effacement — et c’est constatable, puisque le registre se liste à l’écran.

erase n’est pas forcément une suppression : un module qui doit garder une trace — une écriture comptable, un paiement — rend ce qu’il a anonymisé plutôt que supprimé, et le dit. Le centre ne tranche pas à sa place, il rapporte.

Tout est idempotent : une demande se rejoue après un échec partiel, et un module qui n’a plus rien rend un résultat vide et réussi. Sans cela, la seconde exécution échouerait sur ce que la première avait correctement effacé.

  • Valeur : array<int, PersonalDataProviderInterface>
  • Émis dans : Privacy\PersonalDataRegistry::all()
add_filter(
	'solis_forms_pro_personal_data_providers',
	static function ( array $fournisseurs ): array {
		$fournisseurs[] = new Mon_Fournisseur();

		return $fournisseurs;
	}
);

solis_forms_form_pages

Recomposer les pages d’un formulaire avant leur rendu.

Le découpage du cœur suit les sauts de page, et rien d’autre. Un module qui veut présenter une question par écran n’avait, sans ce filtre, qu’un seul recours : refaire le balisage en JavaScript après coup — donc réécrire la navigation, la progression et la prise de focus que le cœur tient déjà, et les laisser diverger.

Une page est un tableau {title, fields}. Rendre autre chose qu’une liste de pages valides ramène au découpage du cœur : un formulaire doit s’afficher même si une extension se trompe.

  • Valeur : array<int, array{title: string, fields: array}>
  • Contexte : le Form rendu
  • Consulté dans : Frontend\FormRenderer::pages()
add_filter(
	'solis_forms_form_pages',
	static function ( array $pages, SolisForms\Domain\Form $form ): array {
		// Une question par écran.
		return array_merge( ...array_map(
			static fn ( array $page ): array => array_map(
				static fn ( array $field ): array => array(
					'title'  => '',
					'fields' => array( $field ),
				),
				$page['fields']
			),
			$pages
		) );
	},
	10,
	2
);

solis_forms_form_markup

Envelopper le balisage d’un formulaire rendu.

Sert à ce qui entoure le formulaire sans lui appartenir : une coque, un en-tête de page dédiée, un conteneur portant des couleurs. Le balisage du formulaire lui-même reste celui du cœur.

Rendre autre chose qu’une chaîne, ou une chaîne vide, ramène au balisage d’origine : une extension qui se trompe ne doit pas faire disparaître le formulaire de la page, car la panne serait totale et silencieuse.

  • Valeur : string
  • Contexte : le Form rendu
  • Consulté dans : Frontend\FormRenderer::render_form()
add_filter(
	'solis_forms_form_markup',
	static fn ( string $markup ): string => '<div class="ma-coque">' . $markup . '</div>'
);

solis_forms_entry_action

Traiter une action postée depuis le détail d’une soumission — rembourser, interrompre un abonnement.

Les contrôles génériques sont faits avant la diffusion : capacité de consultation des soumissions, et accès au formulaire. Ce qui est propre à l’action reste à votre charge : son nonce, et sa capacité particulière. Rembourser engage le compte du site, interrompre un engagement périodique n’est pas rattrapable ; ni l’un ni l’autre ne se contrôle comme une consultation.

  • Charge utile : Events\Payload\EntryActionEvent — action, entry, form
  • Émis dans : EntryActionHandler::handle()

solis_forms_entry_detail_sections

Ajouter une section à l’écran de détail d’une soumission, entre les réponses et le statut. Chaque section reçoit la soumission et son formulaire, et retourne son balisage — c’est à elle de l’échapper.

  • Valeur : array<string, callable> — fn ( Entry $entry, Form $form ): string
  • Contexte : Entry, Form
  • Émis dans : EntryDetailPage::render_extension_sections()

solis_forms_entry_status_changed

Le statut d’une soumission vient de changer : corbeille, indésirable, rétablissement.

Diffusé depuis le dépôt et non depuis les écrans, qui sont déjà plusieurs à changer un statut — la liste, le détail, l’action groupée, la REST. Une garantie qui dépend de chaque appelant pour s’en souvenir n’en est pas une.

Et seulement si la valeur change. Reposer un statut déjà écrit ne diffuse rien : l’annoncer obligerait chaque abonné à relire la ligne pour savoir si quelque chose a bougé, c’est-à-dire à refaire le travail que l’événement existe pour éviter.

L’état quitté voyage avec le nouveau, parce qu’il ne se déduit pas : une soumission peut passer de la corbeille à indésirable sans repasser par l’actif.

  • Charge utile : Events\Payload\EntryStatusChangedEvent — entry_id, from, to
  • Émis dans : Database\Repository\EntryRepository::update_status()
add_action(
	'solis_forms_entry_status_changed',
	static function ( \SolisForms\Events\Payload\EntryStatusChangedEvent $event ): void {
		if ( \SolisForms\Domain\EntryStatus::Active !== $event->to ) {
			mon_module_libere( $event->entry_id );
		}
	}
);

solis_forms_entry_deleting

Purger ce qu’une extension range par soumission. Diffusé avant que la ligne ne disparaisse : l’abonné peut donc encore lire ce qu’il doit effacer.

Toute extension qui stocke des données par soumission doit s’y abonner. Une suppression au titre du RGPD ne doit rien laisser derrière elle, et le cœur ne connaît pas les tables des autres.

  • Charge utile : Events\Payload\EntryDeletingEvent — entry_id
  • Émis dans : EntryRepository::delete()

solis_forms_entry_columns

Ajouter une colonne au tableau des soumissions. Une liste filtrée qui aurait perdu la clé cb est rejetée en bloc : sans case à cocher, l’écran perdrait ses actions groupées.

  • Valeur : array<string, string> — identifiant de colonne, en-tête
  • Contexte : Form|null
  • Émis dans : EntriesListTable::get_columns()

solis_forms_entry_column_value

Remplir une colonne ajoutée. La valeur retournée est affichée telle quelle : c’est à l’extension de l’échapper.

  • Valeur : string
  • Contexte : string $column, Entry, Form|null
  • Émis dans : EntriesListTable::column_default()
add_filter( 'solis_forms_entry_columns', function ( array $columns ) {
    $columns['montant'] = 'Montant';
    return $columns;
} );

add_filter( 'solis_forms_entry_column_value', function ( $value, $column, $entry ) {
    return 'montant' === $column ? esc_html( mon_montant( $entry->id ) ) : $value;
}, 10, 3 );

solis_forms_entry_row_actions

Ajouter une action de ligne. Les actions du cœur sont déjà réduites aux droits de l’utilisateur : une extension qui ajoute la sienne n’a pas à refaire ce contrôle, et n’en réintroduit pas non plus.

  • Valeur : array<string, string> — identifiant, lien HTML
  • Contexte : Entry, Form|null
  • Émis dans : EntriesListTable::build_row_actions()

solis_forms_pro_workflow_transitioned

Extension payante. Un dossier de traitement vient de changer d’état.

Diffusé après l’écriture, et seulement si elle a eu lieu : une transition refusée, ou perdue dans un conflit de révision, n’émet rien. Ce qui écoute peut donc se fier à l’événement sans relire la ligne pour savoir si elle a bougé.

L’état quitté peut être une clé que la configuration ne déclare plus — un état retiré des réglages reste écrit sur les dossiers qui s’y trouvaient.

  • Arguments : int $entry_id, string $from, string $to
  • Émis dans : Workflows\WorkflowService::update()
add_action(
	'solis_forms_pro_workflow_transitioned',
	static function ( int $entry_id, string $from, string $to ): void {
		if ( 'resolved' === $to ) {
			mon_tableau_de_bord_enregistre( $entry_id );
		}
	},
	10,
	3
);

solis_forms_pro_workflow_assigned

Extension payante. Un dossier de traitement vient d’être assigné à quelqu’un.

Diffusé avant le réglage « prévenir la personne assignée », et indépendamment de lui : une équipe peut vouloir l’annonce dans son canal sans vouloir le message individuel. Les lier aurait obligé à activer l’un pour obtenir l’autre.

L’échéance est celle du dossier au moment de l’assignation, ou null s’il n’en porte pas.

  • Arguments : int $entry_id, int $form_id, int $assignee_id, string|null $due_at
  • Émis dans : Workflows\WorkflowService::assign()
add_action(
	'solis_forms_pro_workflow_assigned',
	static function ( int $entry_id, int $form_id, int $assignee_id, ?string $due_at ): void {
		mon_canal_annonce( $entry_id, $assignee_id, $due_at );
	},
	10,
	4
);

solis_forms_pro_workflow_overdue

Extension payante. Un dossier a dépassé son échéance.

Diffusé après la réservation du rappel, donc une seule fois par échéance dépassée : le contrôle horaire s’exécute sur chaque site du réseau et la prise de ligne ne laisse passer qu’un seul processus.

Il existe pour qu’un module puisse prévenir ailleurs qu’en courriel sans aller lire la table des dossiers, ce qui aurait lié deux modules que rien ne garantit activés ensemble.

  • Arguments : int $entry_id, int $form_id, int $assignee_id, string|null $due_at
  • Émis dans : Workflows\WorkflowService::remind_overdue()
add_action(
	'solis_forms_pro_workflow_overdue',
	static function ( int $entry_id, int $form_id, int $assignee_id, ?string $due_at ): void {
		mon_canal_alerte( $entry_id, $due_at );
	},
	10,
	4
);

solis_forms_pro_booking_reserved

Extension payante. Un créneau vient d’être réservé.

Diffusé après la réservation et sa notification, donc une seule fois par place obtenue : la prise rend null quand la place est déjà prise, et le module s’arrête avant d’en arriver là.

Il existe pour qu’un module puisse réagir — le premier à s’en servir envoie un SMS de confirmation — sans aller lire la table des réservations, ce qui aurait lié deux modules que rien ne garantit activés ensemble.

Le créneau est transmis tel qu’il a été soumis, et non reformaté : ce qui écoute décide de son propre affichage.

  • Arguments : int $entry_id, int $form_id, string $slot
  • Émis dans : Bookings\BookingsAddOn::on_entry_created()
add_action(
	'solis_forms_pro_booking_reserved',
	static function ( int $entry_id, int $form_id, string $slot ): void {
		mon_module_confirme( $entry_id, $slot );
	},
	10,
	3
);

solis_forms_pro_recovery_reminded

Extension payante. Un brouillon abandonné vient d’être relancé par courriel.

Émis seulement si le courriel est parti : un module qui doublerait la relance par un SMS ne doit pas l’envoyer quand la relance elle-même a échoué, sans quoi la personne reçoit un rappel sans le lien qu’il annonce.

Émis après le contrôle de consentement du module : une relance n’a lieu que si la personne l’a explicitement acceptée, et ce qui s’y greffe hérite de cet accord.

  • Arguments : int $entry_id, int $form_id
  • Émis dans : Abandonment\RecoveryService::remind()
add_action(
	'solis_forms_pro_recovery_reminded',
	static function ( int $entry_id, int $form_id ): void {
		mon_module_double_la_relance( $entry_id );
	},
	10,
	2
);

solis_forms_pro_booking_cancelled

Extension payante. Une réservation de créneau vient d’être annulée.

Émis depuis le dépôt et non chez les appelants, qui sont déjà deux — le lien public et l’écran d’administration. Une garantie qui dépend de chaque appelant pour s’en souvenir n’en est pas une : le premier qu’on oublierait laisserait un rendez-vous annulé dans l’agenda de quelqu’un.

Une annulation rejouée ne l’émet pas deux fois : la condition porte sur le nombre de lignes réellement modifiées.

  • Arguments : int $booking_id, int $entry_id, int $form_id
  • Émis dans : Database\Repository\BookingRepository::cancel()
add_action(
	'solis_forms_pro_booking_cancelled',
	static function ( int $booking_id, int $entry_id, int $form_id ): void {
		mon_agenda_retire( $booking_id );
	},
	10,
	3
);

solis_forms_pro_booking_promoted

Extension payante. Une réservation en liste d’attente vient d’obtenir une place.

Une place promue est un rendez-vous comme un autre : elle mérite d’entrer dans un agenda, ce qu’une place en attente ne méritait pas. C’est la raison d’être de cet événement, distinct de celui d’une réservation confirmée d’emblée.

  • Arguments : int $booking_id, int $entry_id, int $form_id
  • Émis dans : Database\Repository\BookingRepository::promote()
add_action(
	'solis_forms_pro_booking_promoted',
	static function ( int $booking_id, int $entry_id, int $form_id ): void {
		mon_agenda_pose( $booking_id );
	},
	10,
	3
);

solis_forms_process_entries (capacité méta)

Extension payante. Le droit de traiter les soumissions d’un formulaire.

S’emploie avec l’identifiant du formulaire en argument. Elle est traduite par map_meta_cap en deux conditions déjà accordées par le site — voir les soumissions, et avoir la main sur ce formulaire — et n’est donc attribuée à personne : un site mis à jour ne perd aucun accès.

C’est sur elle, et non sur l’assignation, que repose l’accès : être désigné sur un dossier ne donne jamais le droit de le lire. La liste des personnes assignables en est dérivée.

Un site qui veut un rôle « traiteur » distinct la rebranche par un filtre de priorité supérieure, sans toucher au code.

  • Déclarée dans : Workflows\WorkflowAccess::register()
add_filter(
	'map_meta_cap',
	static function ( array $caps, string $cap, int $user_id, array $args ): array {
		if ( 'solis_forms_process_entries' !== $cap ) {
			return $caps;
		}

		return user_can( $user_id, 'mon_role_traiteur' ) ? array( 'read' ) : $caps;
	},
	20,
	4
);

solis_forms_pro_portal_decided

Extension payante. Une soumission vient de changer d’état de publication dans un portail.

Diffusé après l’écriture, et seulement si elle a eu lieu : une transition refusée, ou perdue dans un conflit de révision, n’émet rien. Ce qui écoute peut donc se fier à l’événement sans relire la décision pour savoir si elle a bougé.

La décision porte le couple portail/soumission : la même entrée peut être approuvée ici et refusée là, et l’événement est émis une fois par portail.

  • Arguments : int $entry_id, int $view_id, string $from, string $to
  • Émis dans : Views\PortalModerationService::decide()
add_action(
	'solis_forms_pro_portal_decided',
	static function ( int $entry_id, int $view_id, string $from, string $to ): void {
		if ( 'approved' === $to ) {
			mon_annuaire_rafraichit( $view_id );
		}
	},
	10,
	4
);

solis_forms_moderate_form (capacité méta)

Extension payante. Le droit de décider ce qui paraît dans les portails d’un formulaire.

S’emploie avec l’identifiant du formulaire en argument. Elle exige d’abord la capacité solis_forms_moderate_entries, puis la portée : tous les formulaires pour qui les gère, les siens pour qui ne gère que les siens.

L’ordre compte. Exiger la portée d’abord aurait laissé tout gestionnaire de formulaires modérer sans qu’on le lui ait accordé, alors que c’est précisément ce que cette capacité sert à séparer : modérer n’est ni modifier le formulaire, ni rembourser un paiement, ni résilier un abonnement.

  • Déclarée dans : Support\FormAccess::register()
if ( current_user_can( 'solis_forms_moderate_form', $form_id ) ) {
	// …
}

Routes d’abonnement Zapier (API REST)

Extension payante. Quatre routes sous solis-forms/v1, employées par le client Zapier et par rien d’autre.

Elles sont documentées ici parce qu’elles sont publiques au sens de l’API : une extension tierce peut s’y brancher, et un intégrateur peut vouloir construire son propre client plutôt que de passer par celui de la place de marché.

RouteRôle
GET /zapier/formsFormulaires dont le déclencheur est allumé, pour un menu
POST /zapier/subscriptions{form_id, target_url} → {id, secret}
DELETE /zapier/subscriptions/<id>Éteint un Zap
GET /zapier/sample?form_id=<id>Un élément d’exemple, fabriqué

Chacune exige deux contrôles : la capacité solis_forms_view_entries et l’accès au formulaire visé. La première seule laisserait un gestionnaire de ses propres formulaires abonner un Zap au formulaire d’autrui, en changeant un chiffre.

L’exemple n’est jamais une vraie soumission. Zapier le conserve dans la configuration du Zap, où il survivrait à l’effacement de la donnée.

Le secret rendu à la souscription n’est stocké nulle part : il se redérive du sel du site et du rang de l’abonnement à chaque signature.

window.solisForms.registerModule() (registre JavaScript)

Brancher un comportement public sur chaque formulaire de la page. Le point d’entrée du cœur passe par ce même registre : un champ dont le rendu réclame du JavaScript n’a donc rien à obtenir de particulier.

bind reçoit le formulaire et son contexte — context.schema décrit les champs, context.refresh() rejoue les réévaluations déclarées. Un module qui retourne une fonction la voit ajoutée à ces réévaluations. La priorité ordonne le branchement (10 par défaut, l’interception de l’envoi étant à 100). Un module qui lève une erreur est consigné et ignoré : il ne prive pas le formulaire de sa soumission.

Déclarez votre script avec solis-forms-frontend en dépendance, pour qu’il s’exécute après le registre.

context.hold( () => message | null ) retient la soumission : le module de paiement s’en sert pour empêcher l’envoi tant que le règlement n’est pas confirmé. Le cœur consulte les retenues avant d’envoyer et affiche le premier message rendu — il n’a donc pas à savoir ce qui retient l’envoi.

window.solisForms.collectFormValues( form ) l’accompagne : un module qui réagit à la saisie a besoin de lire le formulaire, et cette lecture connaît les conventions de noms du plugin — indices de lignes répétables, cases à cocher multiples. La recopier ferait vivre deux lectures d’une même structure. pro/assets/src/frontend/core.js en montre l’emprunt.

window.solisForms.registerModule( 'mon-extension/signature', ( form, context ) => {
    const champ = form.querySelector( '[data-signature]' );

    if ( ! champ ) {
        return;
    }

    // Retourner une fonction la fait rejouer par context.refresh().
    return () => mettreAJour( champ, context.schema );
} );

solisForms.formSettingsSections (filtre JavaScript)

Remplacer une section du panneau de réglages du formulaire, là où solisForms.formSettingsPanels en ajoute une. Une extension qui enrichit une fonctionnalité existante en a besoin : sans cela, les réglages d’un même objet se retrouveraient éclatés entre deux panneaux — l’un portant le sujet du courriel, l’autre sa copie cachée.

Le filtre reçoit un objet vide et retourne les remplacements indexés par nom de section. Le composant substitué reçoit les mêmes propriétés qu’un panneau ajouté : settings et onChange.

  • Valeur : Record<string, Function>
  • Sections du cœur : notifications, confirmation, anti-spam, save-resume, availability
  • Émis dans : components/FormSettingsPanel.js
import { addFilter } from '@wordpress/hooks';

addFilter(
    'solisForms.formSettingsSections',
    'mon-extension/notifications',
    ( sections ) => ( { ...sections, notifications: MonEditeur } )
);

solisForms.fieldInspectorControls (filtre JavaScript)

Ajouter un réglage propre à un type de champ, dans l’inspecteur du constructeur.

Le schéma déclaré par get_settings_schema() engendre l’essentiel de cette colonne, et suffit à un contrôle simple. Il ne sait pas décrire un contrôle qui a besoin du reste du formulaire — une formule de calcul, qui propose les identifiants des champs voisins. C’est ce que ce filtre permet.

Chaque contrôle reçoit le champ sélectionné et applique une modification partielle ; il lui appartient de ne rien rendre pour un type qui ne le concerne pas.

  • Valeur : Array<{ name: string, render: Function }>
  • Émis dans : components/Inspector.js
addFilter(
    'solisForms.fieldInspectorControls',
    'mon-extension/signature',
    ( controls ) => [ ...controls, { name: 'signature', render: MonEditeur } ]
);

function MonEditeur( { field, onChange } ) {
    if ( 'signature' !== field.type ) {
        return null;
    }

    return <MonControle onChange={ ( style ) => onChange( { style } ) } />;
}

window.solisForms.builder (API du constructeur)

Ce que le constructeur publie à l’usage des extensions, au chargement de son bundle :

CléContenu
ConditionalLogicEditorÉditeur de conditions du cœur, tel que les panneaux du formulaire l’emploient
MERGE_TAGSJetons de fusion reconnus par le serveur, pour les rappeler à l’auteur d’un message
STORE_NAMENom du magasin @wordpress/data du constructeur, pour lire les champs du formulaire en cours

Un panneau d’extension en a besoin dès qu’il propose un envoi conditionnel : recopier l’éditeur ferait vivre deux implémentations du même format de règles. Déclarez solis-forms-builder en dépendance de votre script, et résolvez le composant au rendu — un cœur absent doit priver le panneau de son éditeur, jamais emporter le constructeur.

  • Publié dans : assets/src/builder/expose.js, appelé par builder/index.js
  • Exemple complet : pro/assets/src/builder/core.js
function ConditionalLogicEditor( props ) {
    const Editor = window.solisForms?.builder?.ConditionalLogicEditor;

    return Editor ? <Editor { ...props } /> : null;
}

Déclaré mais non émis

EventNames::PAYMENT_COMPLETED (solis_forms_payment_completed) existe dans les constantes mais aucun code ne le diffuse à ce jour. Ne vous y abonnez pas : il ne se déclenchera pas. L’état d’un paiement s’observe aujourd’hui via solis_forms_entry_created, la soumission n’étant enregistrée qu’une fois la transaction vérifiée.