Published : August 9, 2026

CVE-2026-59557: Events Made Easy <= 3.1.3 Missing Authorization PoC, Patch Analysis & Rule

Severity Medium (CVSS 5.3)
CWE 862
Vulnerable Version 3.1.3
Patched Version 3.1.4
Disclosed July 23, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-59557:

The Events Made Easy plugin for WordPress suffers from a missing authorization vulnerability in versions up to and including 3.1.3. This flaw permits unauthenticated attackers to perform unauthorized actions, a critical security oversight. The vulnerability, assessed with a CVSS score of 5.3, stems from insufficient capability checks in a function responsible for processing personal information updates.

Root Cause:

The root cause is located in the `eme_cpi_form` action handler within the `includes/eme-gdpr.php` file. In vulnerable versions, the function `eme_add_update_person_from_form` is executed without first verifying the identity or authorization of the requester. The patch adds a critical security check after retrieving the person record via `eme_get_person( $person_id )`. It then validates that a logged-in user owns the person record, or that a valid nonce (`eme_cpi_nonce`) is provided and verified. The absence of this authorization layer allows any remote user to modify the personal details of any person in the system, provided they know or can guess the `person_id` parameter.

Exploitation:

An attacker can exploit this vulnerability by sending a crafted POST request to the WordPress admin-ajax endpoint, specifically targeting the `eme_cpi_form` action. The attack does not require authentication. The attacker must include the `person_id` parameter with the target’s ID. The request can optionally include all other parameters normally submitted via the form, such as `name`, `email`, and other profile fields. The vulnerability lies in the fact that the AJAX handler processes this request without a nonce or login session, allowing the attacker to modify the victim’s personal information. No specific malicious payload is needed; the lack of authorization is the exploit.

Patch Analysis:

The patch in version 3.1.4 introduces a robust authorization check. After fetching the person record, it first determines the associated WordPress user ID (`wp_id`). If a logged-in user is updating their own profile (where `wp_id` matches `get_current_user_id()`), the request is allowed. If this is not the case, the patch requires the presence of a valid nonce (`eme_cpi_nonce`) generated for the specific action and person. The correct nonce is now included in the form as a hidden field. This two-tier check ensures that only the rightful owner of a profile or an administrator with a valid form submission can update the data, effectively neutralizing the unauthorized access vector.

Impact:

Successful exploitation of this vulnerability allows an unauthenticated attacker to modify the personal information of any person record managed by the Events Made Easy plugin. This could lead to a range of severe consequences, including identity theft, unauthorized changes to names, email addresses, and other sensitive data. The attacker could potentially alter contact details to intercept communications or manipulate user data for further social engineering attacks. The integrity and confidentiality of the user data managed by the plugin are directly compromised.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/events-made-easy/events-manager.php
+++ b/events-made-easy/events-manager.php
@@ -6,7 +6,7 @@

 /*
 Plugin Name: Events Made Easy
-Version: 3.1.3
+Version: 3.1.4
 Plugin URI: https://www.e-dynamics.be/wordpress
 Description: Manage and display events and memberships. Also includes recurring events; locations; widgets; maps; RSVP; ICAL and RSS feeds; Paypal, Stripe, Mollie and others.
 Author: Franky Van Liedekerke
@@ -35,7 +35,7 @@
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

-define( 'EME_VERSION', '3.1.3' );
+define( 'EME_VERSION', '3.1.4' );
 define( 'EME_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
 define( 'EME_INCLUDE_DIR', EME_PLUGIN_DIR . 'includes/' );
 define( 'EME_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
--- a/events-made-easy/includes/eme-actions.php
+++ b/events-made-easy/includes/eme-actions.php
@@ -412,7 +412,14 @@
         wp_register_style( 'eme-markercluster-css2', EME_PLUGIN_URL . 'js/leaflet-markercluster-1.4.1/MarkerCluster.Default.css', [], EME_VERSION );
         wp_register_style( 'eme-gestures-css', EME_PLUGIN_URL . 'js/leaflet-gesturehandling-1.2.1/leaflet-gesture-handling.min.css', [], EME_VERSION );
         wp_register_script( 'eme-show-maps', EME_PLUGIN_URL . 'js/eme_show_maps.js', [ 'eme-leaflet-maps' ], EME_VERSION, true );
+        $translation_array = [
+            'translate_addressnotfound' => __('Address not found', 'events-made-easy' ),
+            'translate_couldnotcalcroute' => __('Could not calculate route', 'events-made-easy' )
+        ];
+        wp_localize_script( 'eme-show-maps', 'emeshowmaps', $translation_array );
         wp_register_script( 'eme-edit-maps', EME_PLUGIN_URL . 'js/eme_edit_maps.js', [ 'eme-leaflet-maps' ], EME_VERSION, true );
+        wp_register_script( 'eme-leaflet-routing', EME_PLUGIN_URL . 'js/leaflet-routing-machine-3.2.12/leaflet-routing-machine.min.js', [ 'eme-leaflet-maps' ], EME_VERSION, true );
+        wp_register_style( 'eme-leaflet-routing-css', EME_PLUGIN_URL . 'js/leaflet-routing-machine-3.2.12/leaflet-routing-machine.css', [ 'eme-leaflet-css' ], EME_VERSION );
         $translation_array = [
             'translate_map_zooming'   => get_option( 'eme_map_zooming' ) ? 'true' : 'false',
             'translate_default_map_icon'  => get_option( 'eme_location_map_icon' ),
--- a/events-made-easy/includes/eme-events.php
+++ b/events-made-easy/includes/eme-events.php
@@ -9636,7 +9636,7 @@
             'translate_name'                       => __( 'Name', 'events-made-easy' ),
             'translate_insertnewevent'             => __( 'Insert New Event', 'events-made-easy' ),
             // translators: %s is the event name
-            'translate_editeventstring'            => __( "Edit Event '%s'", 'events-made-easy' ),
+            'translate_editeventstring'            => __( "Edit Event", 'events-made-easy' ),
             'translate_status'                     => __( 'Status', 'events-made-easy' ),
             'translate_copy'                       => __( 'Copy', 'events-made-easy' ),
             'translate_csv'                        => __( 'CSV', 'events-made-easy' ),
--- a/events-made-easy/includes/eme-formfields.php
+++ b/events-made-easy/includes/eme-formfields.php
@@ -1882,7 +1882,7 @@

     // invite URL overrides
     $invite_readonly = '';
-    if ( eme_check_invite_url( $event['event_id'] ) && ! $eme_is_admin_request ) {
+    if ( eme_check_invite_url( $event_id ) && ! $eme_is_admin_request ) {
         if ( ! empty( $_GET['eme_email'] ) ) {
             $person['email'] = eme_sanitize_email( $_GET['eme_email'] );
         }
@@ -2123,7 +2123,7 @@
         return "<textarea {$ctx['required_att']} name='eme_rsvpcomment' $dfc placeholder='$placeholder_text' >$bookerComment</textarea>";
     };

-    $handlers['/#_SEATS$|#_SPACES$/'] = function( $result, $matches, $ctx ) use ( $event_id, $is_multibooking, $editing_booking_from_backend, $bookedSeats, $booked_seats_options, $waitinglist, $new_booking_in_frontend, $min_allowed_is_multi, $min_allowed, $max_allowed, $dynamic_price_class_basic ) {
+    $handlers['/#_SEATS$|#_SPACES$/'] = function( $result, $matches, $ctx ) use ( $event_id, $event, $is_multibooking, $editing_booking_from_backend, $bookedSeats, $booked_seats_options, $waitinglist, $new_booking_in_frontend, $min_allowed_is_multi, $min_allowed, $max_allowed, $dynamic_price_class_basic ) {
         $var_prefix  = "bookings[$event_id][";
         $var_postfix = ']';
         $fieldname   = "{$var_prefix}bookedSeats{$var_postfix}";
--- a/events-made-easy/includes/eme-fs.php
+++ b/events-made-easy/includes/eme-fs.php
@@ -437,8 +437,7 @@
                 break;
             case 'location_name':
                 $required = 1;
-                $type     = 'text';
-                $more    .= " class='clearable'";
+                $type     = 'search';
                 break;
             case 'event_name':
                 $required = 1;
--- a/events-made-easy/includes/eme-gdpr.php
+++ b/events-made-easy/includes/eme-gdpr.php
@@ -359,6 +359,43 @@
 		wp_die();
 	}

+	$person = eme_get_person( $person_id );
+	if ( empty( $person ) ) {
+		$message = __( "Form tampering detected. If you believe you've received this message in error please contact the site owner.", 'events-made-easy' );
+		echo wp_json_encode(
+			[
+				'Result'      => 'NOK',
+				'htmlmessage' => $message,
+			]
+		);
+		wp_die();
+	}
+
+	$wp_id = intval( $person['wp_id'] );
+	$current_user_id = get_current_user_id();
+
+	if ( ! empty( $wp_id ) && ! empty( $current_user_id ) && $wp_id === $current_user_id ) {
+		// logged-in user owns this person
+	} elseif ( ! isset( $_POST['eme_cpi_nonce'] ) ) {
+		$message = __( "Form tampering detected. If you believe you've received this message in error please contact the site owner.", 'events-made-easy' );
+		echo wp_json_encode(
+			[
+				'Result'      => 'NOK',
+				'htmlmessage' => $message,
+			]
+		);
+		wp_die();
+	} elseif ( ! wp_verify_nonce( eme_sanitize_request( $_POST['eme_cpi_nonce'] ), "change_pi $person_id " . $person['email'] ) ) {
+		$message = __( "Form tampering detected. If you believe you've received this message in error please contact the site owner.", 'events-made-easy' );
+		echo wp_json_encode(
+			[
+				'Result'      => 'NOK',
+				'htmlmessage' => $message,
+			]
+		);
+		wp_die();
+	}
+
 	$captcha_res = eme_check_captcha();

 	[$person_id, $add_update_message] = eme_add_update_person_from_form( $person_id );
@@ -390,6 +427,8 @@
 		esc_html__( 'Email: ', 'events-made-easy' ) . '#_EMAIL <br>';
 	$format         = eme_nl2br_save_html( get_option( 'eme_cpi_form', $format_default ) );

+	$cpi_nonce = wp_create_nonce( "change_pi $person_id " . $person['email'] );
+
 	usleep( 2 );
 	$form_id   = "eme_".eme_random_id(); // JS selectors need to start with a letter, so to be sure we prefix it
 	$form_html   = "<noscript><div class='eme-noscriptmsg'>" . __( 'Javascript is required for this form to work properly', 'events-made-easy' ) . "</div></noscript>
@@ -397,6 +436,7 @@
 		$nonce
 		<span id='honeypot_check'><input type='text' name='honeypot_check' value='' autocomplete='off'></span>
 		<input type='hidden' name='person_id' value='" . $person_id . "'>
+		<input type='hidden' name='eme_cpi_nonce' value='" . $cpi_nonce . "'>
    		";
 	$form_html  .= eme_replace_cpiform_placeholders( $format, $person );
 	$form_html  .= '</form></div>';
--- a/events-made-easy/includes/eme-locations.php
+++ b/events-made-easy/includes/eme-locations.php
@@ -2709,22 +2709,66 @@
 }

 function eme_add_directions_form( $location ) {
-    $locale_code = substr( get_locale(), 0, 2 );
-    $res         = '';
-    if ( isset( $location['location_address1'] ) && isset( $location['location_city'] ) ) {
-        $res .= '<form action="//maps.google.com/maps" method="get" target="_blank" rel="noopener noreferrer" style="text-align:left;">';
-        $res .= '<div id="eme_direction_form"><label for="saddr">' . __( 'Your Street Address', 'events-made-easy' ) . '</label><br>';
-        $res .= '<input type="text" name="saddr" id="saddr" value="">';
-        $res .= '<input type="hidden" name="daddr" value="' . esc_attr( $location['location_address1'] . ', ' . $location['location_city'] ) . '">';
-        $res .= '<input type="hidden" name="hl" value="' . esc_attr( $locale_code ) . '"></div>';
-        $res .= '<input type="submit" value="' . esc_attr__( 'Get Directions', 'events-made-easy' ) . '">';
-        $res .= '</form>';
+    if ( ! isset( $location['location_id'] ) || $location['location_id'] <= 0 ) {
+        return '';
     }
+    if ( empty( $location['location_latitude'] ) || empty( $location['location_longitude'] ) ) {
+        return '';
+    }
+
+    wp_enqueue_style( 'eme-leaflet-css' );
+    wp_enqueue_style( 'eme-leaflet-routing-css' );
+    if ( get_option( 'eme_map_gesture_handling' ) ) {
+        wp_enqueue_script( 'eme-leaflet-gestures' );
+        wp_enqueue_style( 'eme-gestures-css' );
+    }
+    wp_enqueue_script( 'eme-show-maps' );
+    wp_enqueue_script( 'eme-leaflet-routing' );

-    # some people might want to change the form to their liking
-    if ( has_filter( 'eme_directions_form_filter' ) ) {
-        $res = apply_filters( 'eme_directions_form_filter', $res );
+    $id_base = preg_replace( '/D/', '_', microtime( 1 ) );
+    if ( isset( $location['event_id'] ) ) {
+        $id_base = $location['event_id'] . '_' . $id_base;
+    } else {
+        $id_base = wp_rand() . '_' . $id_base;
     }
+    $map_id         = 'eme-directions-map_' . $id_base;
+    $origin_id      = 'eme-directions-origin_' . $id_base;
+    $instructions_id = 'eme-directions-instructions_' . $id_base;
+    $enable_zooming = get_option( 'eme_map_zooming' ) ? 'true' : 'false';
+    $gestures       = get_option( 'eme_map_gesture_handling' ) ? 'true' : 'false';
+    $zoom_factor    = get_option( 'eme_indiv_zoom_factor' );
+    if ( $zoom_factor > 14 ) {
+        $zoom_factor = 14;
+    }
+
+    $dest_address_parts = array_filter( [ $location['location_address1'], $location['location_city'] ] );
+    $dest_address       = esc_attr( implode( ', ', $dest_address_parts ) );
+
+    // the next line is the demo site, not to be used in prod
+    //$osrm_url = apply_filters( 'eme_osrm_service_url', 'https://router.project-osrm.org/route/v1' );
+
+    // the next one is community maintained for now
+    $osrm_url = apply_filters( 'eme_osrm_service_url', 'https://routing.openstreetmap.de/routed-car/route/v1' );
+
+    $data  = "data-zoom_factor='$zoom_factor'";
+    $data .= " data-enable_zooming='$enable_zooming'";
+    $data .= " data-gestures='$gestures'";
+    $data .= " data-osrm-url='" . esc_attr( $osrm_url ) . "'";
+
+    $res = "<div class='eme-directions-form-wrapper'>";
+    $res .= "<form class='eme-directions-form' data-map-id='$map_id' data-instructions-id='$instructions_id'>";
+    $res .= '<label for="' . $origin_id . '">' . __( 'Your Street Address', 'events-made-easy' ) . '</label><br>';
+    $res .= "<input type='text' id='$origin_id' class='eme-directions-origin' value='' placeholder='" . esc_attr__( 'e.g. 123 Main St, City', 'events-made-easy' ) . "'>";
+    $res .= "<input type='hidden' name='eme_directions_dest_lat' value='" . esc_attr( $location['location_latitude'] ) . "'>";
+    $res .= "<input type='hidden' name='eme_directions_dest_lon' value='" . esc_attr( $location['location_longitude'] ) . "'>";
+    $res .= "<input type='hidden' name='eme_directions_dest_address' value='$dest_address'>";
+    $res .= "<input type='submit' value='" . esc_attr__( 'Get Directions', 'events-made-easy' ) . "'>";
+    $res .= '</form>';
+
+    $res .= "<div id='$map_id' class='eme-directions-map' style='display:none;width:100%;height:400px;' $data></div>";
+
+    $res .= "<div id='$instructions_id' class='eme-directions-instructions' style='display:none;'></div>";
+    $res .= '</div>';

     return $res;
 }
--- a/events-made-easy/includes/eme-mailer.php
+++ b/events-made-easy/includes/eme-mailer.php
@@ -1390,6 +1390,7 @@
     $rsvp_status = $conditions['rsvp_status'] ?? 0;
     $only_unpaid = $conditions['only_unpaid'] ?? 0;
     $exclude_registered = $conditions['exclude_registered'] ?? 0;
+    $exclude_registered_events = $conditions['exclude_registered_events'] ?? '';

     if ( ! empty( $conditions['pending_approved'] ) ) {
         if ( $conditions['pending_approved'] == 1 ) $rsvp_status = EME_RSVP_STATUS_PENDING;
@@ -1411,11 +1412,11 @@
             case 'all_people':
             case 'all_people_not_registered':
             case 'people_and_groups':
-                eme_process_event_people_groups( $event, $conditions, $ignore_massmail, $rsvp_status, $only_unpaid, $exclude_registered, $mail_subject, $mail_message, $mail_text_html, $batch, $atts_arr );
+                eme_process_event_people_groups( $event, $conditions, $ignore_massmail, $rsvp_status, $only_unpaid, $exclude_registered, $exclude_registered_events, $mail_subject, $mail_message, $mail_text_html, $batch, $atts_arr );
                 break;
             case 'all_wp':
             case 'all_wp_not_registered':
-                eme_process_event_wp_users( $event, $conditions, $exclude_registered, $mail_subject, $mail_message, $mail_text_html, $batch, $atts_arr );
+                eme_process_event_wp_users( $event, $conditions, $exclude_registered, $exclude_registered_events, $mail_subject, $mail_message, $mail_text_html, $batch, $atts_arr );
                 break;
         }
     }
@@ -1465,7 +1466,7 @@
 /**
  * Process people, groups, and members for an event and add them to the mail batch.
  */
-function eme_process_event_people_groups( $event, $conditions, $ignore_massmail, $rsvp_status, $only_unpaid, $exclude_registered, $mail_subject, $mail_message, $mail_text_html, EMEMailBatch $batch, $atts_arr ) {
+function eme_process_event_people_groups( $event, $conditions, $ignore_massmail, $rsvp_status, $only_unpaid, $exclude_registered, $exclude_registered_events, $mail_subject, $mail_message, $mail_text_html, EMEMailBatch $batch, $atts_arr ) {
     $event_id = $event['event_id'];
     $mail_type = $conditions['eme_mail_type'];

@@ -1496,7 +1497,14 @@
     }

     // Get registered person IDs if we need to exclude them
-    $registered_ids = ( $exclude_registered || $mail_type == 'all_people_not_registered' ) ? eme_get_attendee_ids( $event_id ) : [];
+    $exclude_event_ids = [];
+    if ( $exclude_registered || $mail_type == 'all_people_not_registered' ) {
+        $exclude_event_ids[] = $event_id;
+    }
+    if ( ! empty( $exclude_registered_events ) ) {
+        $exclude_event_ids = array_merge( $exclude_event_ids, explode( ',', $exclude_registered_events ) );
+    }
+    $registered_emails = ! empty( $exclude_event_ids ) ? eme_get_attendee_emails( array_unique( $exclude_event_ids ) ) : [];

     $handled_emails = [];

@@ -1504,11 +1512,11 @@
     foreach ( $member_ids as $member_id ) {
         $member = eme_get_member( $member_id );
         if ( ! $member ) continue;
-        if ( in_array( $member['person_id'], $registered_ids ) ) continue;

         $person = eme_get_person( $member['person_id'] );
         if ( ! $person ) continue;
         if ( ! $ignore_massmail && ! $person['massmail'] && ! in_array( $member_id, $cond_member_ids ) ) continue;
+        if ( in_array( $person['email'], $registered_emails ) ) continue;

         $handled_emails[] = $person['email'];
         $person_name = eme_format_full_name( $person['firstname'], $person['lastname'], $person['email'] );
@@ -1530,10 +1538,10 @@

     // Process people
     foreach ( $person_ids as $person_id ) {
-        if ( in_array( $person_id, $registered_ids ) ) continue;
         $person = eme_get_person( $person_id );
         if ( ! $person ) continue;
         if ( ! $ignore_massmail && ! $person['massmail'] ) continue;
+        if ( in_array( $person['email'], $registered_emails ) ) continue;
         if ( in_array( $person['email'], $handled_emails ) ) continue;

         $handled_emails[] = $person['email'];
@@ -1556,10 +1564,17 @@
 /**
  * Process WordPress users for an event and add them to the mail batch.
  */
-function eme_process_event_wp_users( $event, $conditions, $exclude_registered, $mail_subject, $mail_message, $mail_text_html, EMEMailBatch $batch, $atts_arr ) {
+function eme_process_event_wp_users( $event, $conditions, $exclude_registered, $exclude_registered_events, $mail_subject, $mail_message, $mail_text_html, EMEMailBatch $batch, $atts_arr ) {
     $mail_type = $conditions['eme_mail_type'];
     $wp_users = get_users();
-    $attendee_wp_ids = ( $mail_type == 'all_wp_not_registered' || $exclude_registered ) ? eme_get_wp_ids_for( $event['event_id'] ) : [];
+    $exclude_event_ids = [];
+    if ( $mail_type == 'all_wp_not_registered' || $exclude_registered ) {
+        $exclude_event_ids[] = $event['event_id'];
+    }
+    if ( ! empty( $exclude_registered_events ) ) {
+        $exclude_event_ids = array_merge( $exclude_event_ids, explode( ',', $exclude_registered_events ) );
+    }
+    $attendee_wp_ids = ! empty( $exclude_event_ids ) ? eme_get_wp_ids_for( array_unique( $exclude_event_ids ) ) : [];
     $lang = eme_detect_lang();
     $handled_emails = [];

@@ -2444,7 +2459,7 @@
     $mail_message    = $parsed['mail_message'];

     $event_ids = isset( $post_data['event_ids'] ) ? wp_parse_id_list( $post_data['event_ids'] ) : [];
-    if ( ! eme_is_numeric_array( $event_ids ) ) {
+    if ( empty($event_ids) || ! eme_is_numeric_array( $event_ids ) ) {
         return [ 'success' => false, 'message' => "<div id='message' class='error eme-message-admin'><p>" . __( 'Please select at least one event.', 'events-made-easy' ) . '</p></div>' ];
     }

@@ -2484,6 +2499,9 @@
     $conditions['rsvp_status']          = isset( $post_data['rsvp_status'] )        ? intval( $post_data['rsvp_status'] )        : 0;
     $conditions['only_unpaid']          = isset( $post_data['only_unpaid'] )         ? intval( $post_data['only_unpaid'] )         : 0;
     $conditions['exclude_registered']   = isset( $post_data['exclude_registered'] )  ? intval( $post_data['exclude_registered'] )  : 0;
+    $conditions['exclude_registered_events'] = ( ! empty( $post_data['exclude_registered_events'] ) && eme_is_numeric_array( $post_data['exclude_registered_events'] ) )
+        ? join( ',', array_map( 'intval', $post_data['exclude_registered_events'] ) )
+        : '';

     $current_userid     = get_current_user_id();
     $mail_problems      = 0;
@@ -2556,8 +2574,10 @@
     $mygroups        = [];
     $mymembergroups  = [];
     $myevents        = [];
+    $myexcludeevents = [];
     $person_ids      = [];
     $event_ids       = [];
+    $exclude_registered_event_ids = [];
     $membership_ids  = [];
     $persongroup_ids = [];
     $membergroup_ids = [];
@@ -2605,6 +2625,7 @@
     $exclude_registered_checked     = '';
     $only_unpaid_checked            = '';
     $eme_mail_type                  = '';
+    $eme_rsvp_status                = '';
     $send_to_all_people_checked     = '';
     $event_mail_subject             = '';
     $event_mail_message             = '';
@@ -2741,6 +2762,9 @@
                 if ( ! empty( $conditions['eme_mail_type'] ) ) {
                     $eme_mail_type = $conditions['eme_mail_type'];
                 }
+                if ( ! empty( $conditions['rsvp_status'] ) ) {
+                    $eme_rsvp_status = $conditions['rsvp_status'];
+                }
                 if ( ! empty( $conditions['exclude_registered'] ) ) {
                     $exclude_registered_checked = "checked='checked'";
                 }
@@ -2755,6 +2779,13 @@
                         $myevents[ $event['event_id'] ] = $event['event_name']. ' (' . eme_localized_date( $event['event_start'], EME_TIMEZONE, 1 ) . ')';
                     }
                 }
+                if ( ! empty( $conditions['exclude_registered_events'] ) ) {
+                    $exclude_registered_event_ids = explode( ',', $conditions['exclude_registered_events'] );
+                    $exclude_events                = eme_get_events( extra_conditions: [ 'event_id' => array_map( 'intval', $exclude_registered_event_ids ) ] );
+                    foreach ( $exclude_events as $exclude_event ) {
+                        $myexcludeevents[ $exclude_event['event_id'] ] = $exclude_event['event_name']. ' (' . eme_localized_date( $exclude_event['event_start'], EME_TIMEZONE, 1 ) . ')';
+                    }
+                }
                 if ( ! empty( $conditions['eme_eventmail_send_persons'] ) ) {
                     $person_ids = explode( ',', $conditions['eme_eventmail_send_persons'] );
                     $persons    = eme_get_persons( $person_ids );
@@ -2872,7 +2903,7 @@
     <h1><?php esc_html_e( 'Send event related emails', 'events-made-easy' ); ?></h1>
     <form id='send_mail' name='send_mail' action="#" method="post" onsubmit="return false;">
     <div id='send_event_mail_div'>
-        <table>
+        <table class='widefat'>
         <tr>
         <td><?php
             $label      = esc_html__( 'Select the event(s)', 'events-made-easy' );
@@ -2880,7 +2911,7 @@
             echo $label; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped at assignment
         ?>
         </td>
-        <td><?php echo eme_ui_multiselect( $event_ids, 'event_ids', $myevents, 5, '', 0, 'eme_snapselect_events_class', $aria_label ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- trusted HTML from eme_ui_multiselect() ?>
+        <td><?php echo eme_ui_multiselect( $event_ids, 'event_ids', $myevents, 5, '', 1, 'eme_snapselect_events_class', $aria_label ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- trusted HTML from eme_ui_multiselect() ?>
         <br><label><input id="eventsearch_all" name='eventsearch_all' value='1' type='checkbox'> <?php esc_html_e( 'Check this box to search through all events and not just future ones.', 'events-made-easy' ); ?> </label>
             <p class='eme_smaller'><?php esc_html_e( 'Remark: if you select multiple events, a mailing will be created for each selected event', 'events-made-easy' ); ?></p>
         </td>
@@ -2896,18 +2927,21 @@
                 'people_and_groups' => __('Email to people and/or groups registered in EME', 'events-made-easy'),
                 'all_wp' => __('Email to all WP users', 'events-made-easy'),
             ];
-            echo eme_ui_select( $eme_mail_type, 'eme_mail_type', $eme_mail_type_arr, ' ', 1); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- trusted HTML from eme_ui_select()
+            echo eme_ui_select( $eme_mail_type, 'eme_mail_type', $eme_mail_type_arr, ' ', 1, 'eme_snapselect'); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- trusted HTML from eme_ui_select()
         ?>
         </td>
         </tr>
         <tr id="eme_rsvp_status_row">
         <td><?php esc_html_e( 'Select your target audience', 'events-made-easy' ); ?></td>
         <td>
-            <select name="rsvp_status">
-            <option value="0"><?php esc_html_e( 'All registered persons', 'events-made-easy' ); ?></option>
-            <option value="<?php echo esc_attr( EME_RSVP_STATUS_APPROVED ); ?>"><?php esc_html_e( 'Only approved bookings', 'events-made-easy' ); ?></option>
-            <option value="<?php echo esc_attr( EME_RSVP_STATUS_PENDING ); ?>"><?php esc_html_e( 'Only pending bookings', 'events-made-easy' ); ?></option>
-            </select>
+        <?php
+            $target_audience = [
+                0 => __( 'All registered persons', 'events-made-easy' ),
+                EME_RSVP_STATUS_APPROVED => __( 'Only approved bookings', 'events-made-easy' ),
+                EME_RSVP_STATUS_PENDING => __( 'Only pending bookings', 'events-made-easy' ),
+            ];
+            echo eme_ui_select( $eme_rsvp_status, 'rsvp_status', $target_audience, '', 0, 'eme_snapselect'); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- trusted HTML from eme_ui_select()
+        ?>
         </td>
         </tr>
         <tr id="eme_exclude_registered_row">
@@ -2916,6 +2950,16 @@
         <input type="checkbox" name="exclude_registered" value="1" <?php echo $exclude_registered_checked; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- hardcoded checked attribute ?>>
         </td>
         </tr>
+        <tr id="eme_exclude_registered_events_row">
+        <td><?php
+            $exclude_label      = esc_html__( 'Also exclude emails of people already registered for these other event(s)', 'events-made-easy' );
+            $exclude_aria_label = 'aria-label="' . $exclude_label . '"';
+            echo $exclude_label; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped at assignment
+        ?> </td>
+        <td><?php echo eme_ui_multiselect( $exclude_registered_event_ids, 'exclude_registered_events', $myexcludeevents, 5, '', 0, 'eme_snapselect_events_class', $exclude_aria_label ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- trusted HTML from eme_ui_multiselect() ?>
+        <p class='eme_smaller'><?php esc_html_e( 'Optional: pick any event(s) whose registrants should be skipped, independent of which event(s) this mailing is about.', 'events-made-easy' ); ?></p>
+        </td>
+        </tr>
         <tr id="eme_only_unpaid_row">
         <td><span id="span_unpaid_attendees"><?php esc_html_e( 'Only send emails to attendees who did not pay yet', 'events-made-easy' ); ?></span>
         <span id="span_unpaid_bookings"><?php esc_html_e( 'Only take unpaid bookings into account', 'events-made-easy' ); ?></span>
--- a/events-made-easy/includes/eme-members.php
+++ b/events-made-easy/includes/eme-members.php
@@ -1113,10 +1113,10 @@
         } elseif ( eme_is_empty_string( $_POST['lastname'] ) ) {
             // we need at least lastname
             $err = __( 'Please enter at least the last name for a new member', 'events-made-easy' );
-        } elseif ( ! $eme_is_admin_request && ! eme_is_email_frontend( sanitize_text_field( wp_unslash( $_POST['email'] ) ) ) ) {
+        } elseif ( ! $eme_is_admin_request && ! eme_is_email_frontend( eme_sanitize_email( $_POST['email'] ) ) ) {
             // we need an email
             $err = __( 'Please enter a valid email address', 'events-made-easy' );
-        } elseif ( $membership['properties']['create_wp_user'] && ! eme_is_email( sanitize_text_field( wp_unslash( $_POST['email'] ) ) ) ) {
+        } elseif ( $membership['properties']['create_wp_user'] && ! eme_is_email( eme_sanitize_email( $_POST['email'] ) ) ) {
             // we need an email
             $err = __( 'Please enter a valid email address', 'events-made-easy' );
         } else {
@@ -2838,7 +2838,7 @@
     if (empty($limit_to_group)) {
 ?>
     <button id="StoreQueryButton" class="button action eme_admin_button_middle"><?php esc_html_e( 'Store result as dynamic group', 'events-made-easy' ); ?></button>
-    <div id="StoreQueryDiv"><?php esc_html_e( 'Enter a name for this dynamic group', 'events-made-easy' ); ?> <input type="text" id="dynamicgroupname" name="dynamicgroupname" class="clearable" size=20>
+    <div id="StoreQueryDiv"><?php esc_html_e( 'Enter a name for this dynamic group', 'events-made-easy' ); ?> <input type="search" id="dynamicgroupname" name="dynamicgroupname" size=20>
         <button id="StoreQuerySubmitButton" class="button action"><?php esc_html_e( 'Store dynamic group', 'events-made-easy' ); ?></button>
     </div>
 <?php
--- a/events-made-easy/includes/eme-people.php
+++ b/events-made-easy/includes/eme-people.php
@@ -1987,7 +1987,7 @@
     if (empty($limit_to_group)) {
 ?>
         <button id="StoreQueryButton" class="button action eme_admin_button_middle"><?php esc_html_e( 'Store result as dynamic group', 'events-made-easy' ); ?></button>
-        <div id="StoreQueryDiv"><?php esc_html_e( 'Enter a name for this dynamic group', 'events-made-easy' ); ?> <input type="text" id="dynamicgroupname" name="dynamicgroupname" class="clearable" size=20>
+        <div id="StoreQueryDiv"><?php esc_html_e( 'Enter a name for this dynamic group', 'events-made-easy' ); ?> <input type="search" id="dynamicgroupname" name="dynamicgroupname" size=20>
     <button id="StoreQuerySubmitButton" class="button action"><?php esc_html_e( 'Store dynamic group', 'events-made-easy' ); ?></button>
         </div>
 <?php
@@ -2808,7 +2808,7 @@
         </tr>
         <tr>
         <td><label for="People"><?php esc_html_e( 'People', 'events-made-easy' ); ?></label></td>
-        <td><?php echo eme_ui_multiselect( $grouppersons, 'persons', $mygroups, 5, '', 1, 'eme_snapselect_people_class' ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- trusted HTML from eme_ui_multiselect() ?></td>
+        <td><?php echo eme_ui_multiselect( $grouppersons, 'persons', $mygroups, 5, '', 0, 'eme_snapselect_people_class' ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- trusted HTML from eme_ui_multiselect() ?></td>
         </tr>
 <?php
             } elseif ( $group['type'] == 'dynamic_people' ) {
--- a/events-made-easy/includes/eme-recurrence.php
+++ b/events-made-easy/includes/eme-recurrence.php
@@ -405,12 +405,11 @@
 	// 2 steps for updating events for a recurrence:
 	// First step: check the existing events and
 	//       if they still match the recurrence days or they have existing bookings (future events only), update them
-	// 	otherwise delete the old event
+	// 	     otherwise delete the old event
 	// Reason for doing this: we want to keep possible booking data for a recurrent event as well
-	// and just deleting all current events for a recurrence and inserting new ones would break the link
-	// between booking id and event id
+	// and just deleting all current events for a recurrence and inserting new ones would break the link between booking id and event id
 	// Second step: check all days of the recurrence and if no event exists yet, insert it
-	$prepared_sql = $wpdb->prepare( "SELECT event_id,event_start FROM $events_table WHERE recurrence_id = %d AND event_status <> %d", $recurrence['recurrence_id'], EME_EVENT_STATUS_TRASH ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+	$prepared_sql = $wpdb->prepare( "SELECT event_id,event_start,event_rsvp,event_tasks FROM $events_table WHERE recurrence_id = %d AND event_status <> %d", $recurrence['recurrence_id'], EME_EVENT_STATUS_TRASH ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
 	$events = $wpdb->get_results( $prepared_sql, ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared

 	// in order to take tasks into account for recurring events, we need to know the difference in days between the events
@@ -425,13 +424,18 @@
 		$day       = eme_get_date_from_dt( $existing_event['event_start'] );
 		$array_key = array_search( $day, $matching_days );
 		$existing_event_start_obj = new emeExpressiveDate( $existing_event['event_start'], EME_TIMEZONE );
-		// if future events in the recurrence have bookings, we won't delete those but keep them in the recurrence series
+		// if future events in the recurrence have bookings or task signups, we won't delete those but keep them in the recurrence series
+        $bookings_count = 0;
+        $tasksignups_count = 0;
 		if ( $existing_event_start_obj >= $eme_date_obj_now ) {
-			$bookings_count = eme_count_bookings_for( $existing_event['event_id'] );
-		} else {
-			$bookings_count = 0;
+            if ($existing_event['event_rsvp']) {
+                $bookings_count = eme_count_bookings_for( $existing_event['event_id'] );
+            }
+            if ($existing_event['event_tasks']) {
+                $tasksignups_count = eme_count_tasksignups_for( $existing_event['event_id'] );
+            }
 		}
-		if ( $array_key !== false || $bookings_count > 0 ) {
+		if ( $array_key !== false || $bookings_count > 0 || $tasksignups_count > 0 ) {
 			if ( ! $only_change_recdates ) {
 				$event['event_start'] = "$day $event_start_time";
 				$eme_date_obj         = new emeExpressiveDate( $event['event_start'], EME_TIMEZONE );
--- a/events-made-easy/includes/eme-rsvp.php
+++ b/events-made-easy/includes/eme-rsvp.php
@@ -3177,41 +3177,23 @@
     return $bookings;
 }

-function eme_count_bookings_for( $event_ids, $rsvp_status = 0, $paid_status = 0 ) {
+function eme_count_bookings_for( $event_id ) {
     global $wpdb;
     $bookings_table = EME_DB_PREFIX . EME_BOOKINGS_TBNAME;

-    $bookings = [];
-    if ( ! $event_ids ) {
-        return $bookings;
-    }
-
-    $where = [];
-    if ( is_array( $event_ids ) && eme_is_numeric_array( $event_ids ) ) {
-        $ids_arr_int = array_map('intval', $event_ids);
-        $placeholders = implode(',', array_fill(0, count($ids_arr_int), '%d'));
-        $where[] = $wpdb->prepare( 'bookings.event_id IN (' . $placeholders . ')', ...$ids_arr_int );
-    } elseif ( is_numeric( $event_ids ) ) {
-        $event_id = intval( $event_ids );
-        $where[] = $wpdb->prepare( 'bookings.event_id = %d', $event_id );
-    } else {
-        $where[] = 'bookings.event_id = 0';
+    if ( ! $event_id ) {
+        return false;
     }

-    if ( $rsvp_status ) {
-        $rsvp_status_int = intval( $rsvp_status );
-        $where[] = $wpdb->prepare( 'bookings.status = %d', $rsvp_status_int );
+    $where_arr = [];
+    if ( is_numeric( $event_id ) ) {
+        $where_arr[] = $wpdb->prepare( 'bookings.event_id = %d', $event_id );
     } else {
-        $where[] = $wpdb->prepare( 'bookings.status != %d', EME_RSVP_STATUS_TRASH );
+        $where_arr[] = 'bookings.event_id = 0';
     }
+    $where_arr[] = $wpdb->prepare( 'bookings.status != %d', EME_RSVP_STATUS_TRASH );

-    if ( $paid_status == 1 ) {
-        $where[] = 'bookings.booking_paid=0';
-    } elseif ( $paid_status == 2 ) {
-        $where[] = 'bookings.booking_paid=1';
-    }
-    $where = 'WHERE ' . implode( ' AND ', $where );
-    #$sql = "SELECT * FROM $bookings_table $where ORDER BY booking_id";
+    $where = 'WHERE ' . implode( ' AND ', $where_arr );
     $sql = "SELECT COUNT(*) FROM $bookings_table AS bookings $where"; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
     return $wpdb->get_var( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
 }
@@ -3325,7 +3307,16 @@
     global $wpdb;
     $bookings_table = EME_DB_PREFIX . EME_BOOKINGS_TBNAME;
     $people_table   = EME_DB_PREFIX . EME_PEOPLE_TBNAME;
-    $prepared_sql   = $wpdb->prepare( "SELECT DISTINCT people.wp_id FROM $bookings_table AS bookings LEFT JOIN $people_table AS people ON bookings.person_id=people.person_id WHERE bookings.status IN (%d,%d,%d) AND bookings.event_id = %d AND people.wp_id != 0", EME_RSVP_STATUS_PENDING, EME_RSVP_STATUS_USERPENDING, EME_RSVP_STATUS_APPROVED, $event_id ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+    if ( is_array( $event_id ) && eme_is_numeric_array( $event_id ) ) {
+        $ids_arr_int  = array_map( 'intval', $event_id );
+        $placeholders = implode( ',', array_fill( 0, count( $ids_arr_int ), '%d' ) );
+        $prepared_sql = $wpdb->prepare(
+            "SELECT DISTINCT people.wp_id FROM $bookings_table AS bookings LEFT JOIN $people_table AS people ON bookings.person_id=people.person_id WHERE bookings.status IN (%d,%d,%d) AND bookings.event_id IN ($placeholders) AND people.wp_id != 0",
+            EME_RSVP_STATUS_PENDING, EME_RSVP_STATUS_USERPENDING, EME_RSVP_STATUS_APPROVED, ...$ids_arr_int
+        ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+    } else {
+        $prepared_sql = $wpdb->prepare( "SELECT DISTINCT people.wp_id FROM $bookings_table AS bookings LEFT JOIN $people_table AS people ON bookings.person_id=people.person_id WHERE bookings.status IN (%d,%d,%d) AND bookings.event_id = %d AND people.wp_id != 0", EME_RSVP_STATUS_PENDING, EME_RSVP_STATUS_USERPENDING, EME_RSVP_STATUS_APPROVED, $event_id ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+    }
     return $wpdb->get_col( $prepared_sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
 }

@@ -3420,6 +3411,23 @@
     return $attendees;
 }

+function eme_get_attendee_emails( $event_id ) {
+    global $wpdb;
+    $bookings_table = EME_DB_PREFIX . EME_BOOKINGS_TBNAME;
+    $people_table   = EME_DB_PREFIX . EME_PEOPLE_TBNAME;
+    if ( is_array( $event_id ) && eme_is_numeric_array( $event_id ) ) {
+        $ids_arr_int  = array_map( 'intval', $event_id );
+        $placeholders = implode( ',', array_fill( 0, count( $ids_arr_int ), '%d' ) );
+        $sql = $wpdb->prepare(
+            "SELECT DISTINCT people.email FROM $bookings_table AS bookings LEFT JOIN $people_table AS people ON bookings.person_id=people.person_id WHERE bookings.event_id IN ($placeholders) AND bookings.person_id>0 AND people.email != ''",
+            ...$ids_arr_int
+        ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+    } else {
+        $sql = $wpdb->prepare( "SELECT DISTINCT people.email FROM $bookings_table AS bookings LEFT JOIN $people_table AS people ON bookings.person_id=people.person_id WHERE bookings.event_id = %d AND bookings.person_id>0 AND people.email != ''", $event_id ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+    }
+    return $wpdb->get_col( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+}
+
 // for backwards compat
 function eme_get_attendees_list_for( $event, $template_id = 0, $template_id_header = 0, $template_id_footer = 0, $rsvp_status = 0, $paid_status = 0, $order = '' ) {
     return eme_get_attendees_list( $event, $template_id, $template_id_header, $template_id_footer, $rsvp_status, $paid_status, $order );
--- a/events-made-easy/includes/eme-tasks.php
+++ b/events-made-easy/includes/eme-tasks.php
@@ -387,6 +387,13 @@
     return $wpdb->get_var( $prepared_sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
 }

+function eme_count_tasksignups_for( $event_id ) {
+    global $wpdb;
+    $table = EME_DB_PREFIX . EME_TASK_SIGNUPS_TBNAME;
+    $prepared_sql = $wpdb->prepare( "SELECT COUNT(*) FROM $table WHERE event_id=%d", $event_id ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+    return $wpdb->get_var( $prepared_sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+}
+
 function eme_count_task_approved_signups( $task_id ) {
     global $wpdb;
     $table = EME_DB_PREFIX . EME_TASK_SIGNUPS_TBNAME;
@@ -463,7 +470,7 @@
     }
 }

-function eme_count_event_task_signups( $event_id ) {
+function eme_count_event_task_signups_per_task( $event_id ) {
     global $wpdb;
     $table      = EME_DB_PREFIX . EME_TASK_SIGNUPS_TBNAME;
     $prepared_sql = $wpdb->prepare( "SELECT task_id, COUNT(*) as signup_count FROM $table WHERE event_id=%d GROUP BY task_id", $event_id ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
@@ -1892,12 +1899,12 @@
             }
         }
         // the next is an array with as key the task id and value the number of signups for it
-        $event_task_count_signups = eme_count_event_task_signups( $event_id );
+        $event_count_signups_per_task = eme_count_event_task_signups_per_task( $event_id );
         foreach ( $task_id_arr as $task_id ) {
             $task_id = intval( $task_id );
             $task    = eme_get_task( $task_id );
             // if full, continue
-            if ( isset( $event_task_count_signups[ $task_id ] ) && $event_task_count_signups[ $task_id ] >= $task['spaces'] ) {
+            if ( isset( $event_count_signups_per_task[ $task_id ] ) && $event_count_signups_per_task[ $task_id ] >= $task['spaces'] ) {
                 $message .= __( 'No more open spaces for this task', 'events-made-easy' );
                 $message .= '<br>';
                 $nok      = 1;
--- a/events-made-easy/langs/events-made-easy-fr_FR.l10n.php
+++ b/events-made-easy/langs/events-made-easy-fr_FR.l10n.php
@@ -1,4 +1,4 @@
 <?php
-return ['domain'=>NULL,'plural-forms'=>'nplurals=2; plural=n > 1;','language'=>'fr','project-id-version'=>'Plugins - Events Made Easy - Stable (latest release)','pot-creation-date'=>'2026-06-23T08:24:23+00:00','po-revision-date'=>'2026-06-21 15:11:02+0000','x-generator'=>'GlotPress/4.0.3','messages'=>['See <a target="_blank" rel="noopener noreferrer" href="%s">this page</a> for info on what you can enter here.'=>'Consultez <a target="_blank" rel="noopener noreferrer" href="%s">cette page</a> pour savoir ce que vous pouvez saisir ici.','For all information concerning frontend submit, see <a target='_blank' rel='noopener noreferrer' href='%s'>the documentation</a>'=>'Pour toute information concernant la soumission à partir de l‘interface publique, voir <a target='_blank' rel='noopener noreferrer' href='%s'>la documentation</a>','For all placeholders you can use here, see <a target='_blank' rel='noopener noreferrer' href='%s'>the documentation</a>'=>'Pour tous les espaces réservés que vous pouvez utiliser ici, voir <a target='_blank' rel='noopener noreferrer' href='%s'>la documentation</a>','Score threshold below which submissions are rejected'=>'Seuil de note en dessous duquel les entrées sont rejetées','Google reCAPTCHA minimum score (0.0–1.0, default 0.5)'=>'Score minimum requis pour Google reCAPTCHA (0,0–1,0, par défaut : 0,5)','Your Google cloud project ID'=>'Votre ID de projet Google Cloud','Google reCAPTCHA Cloud project ID'=>'ID du projet Google reCAPTCHA Cloud','Go to https://console.cloud.google.com/apis/credentials to create a reCAPTCHA API key for your project'=>'Rendez-vous sur https://console.cloud.google.com/apis/credentials pour créer une clé API reCAPTCHA pour votre projet','Google reCAPTCHA API key (Cloud project)'=>'Clé API Google reCAPTCHA (projet Cloud)','Go to https://console.cloud.google.com/security/recaptcha to get your reCAPTCHA key'=>'Rendez-vous sur https://console.cloud.google.com/security/recaptcha pour obtenir votre clé reCAPTCHA','The conflicting page can be edited <a href="%s" target="_blank" rel="noopener noreferrer">here</a>.'=>'La page en conflit peut être modifiée <a href="%s" target="_blank" rel="noopener noreferrer">ici</a>.','Select'=>'Sélectionner','Confirm delete'=>'Confirmer la suppression','Occurrence deleted and remaining occurrences renumbered.'=>'L'occurrence a été supprimée et les occurrences restantes ont été renumérotées.','Invalid parameters'=>'Paramètres invalides','No permission'=>'Aucun droit','Delete this group'=>'Supprimer ce groupe','Are you sure you want to delete this group? This cannot be undone.'=>'Confirmez-vous vouloir supprimer ce groupe ? Cette action ne peut pas être annulée.','RSVP and generic fields'=>'Champs « RSVP » et champs génériques','Membership fields'=>'Champs relatifs à l'adhésion','Member fields'=>'Champs des membres','Location fields'=>'Champs de localisation','Check this option if you want to use Cloudflare Turnstile on the booking/cancel/membership forms, to thwart spammers a bit. You can then either add #_CAPTCHA to your form layout yourself or it will automatically added just above the submit button if not present.'=>'Cochez cette option si vous souhaitez utiliser le Turnstile de Cloudflare sur les formulaires de réservation/annulation/adhésion, afin de contrer un peu les spammeurs. Vous pouvez alors ajouter vous-même #_CAPTCHA à la mise en page de votre formulaire ou il sera automatiquement ajouté juste au-dessus du bouton d'envoi s'il n'est pas présent.','Check this option if you want to use hCaptcha on the booking/cancel/membership forms, to thwart spammers a bit. You can then either add #_CAPTCHA to your form layout yourself or it will automatically added just above the submit button if not present.'=>'Cochez cette option si vous souhaitez utiliser hCaptcha sur les formulaires de réservation/annulation/adhésion, afin de déjouer un peu les spammeurs. Vous pouvez alors ajouter vous-même #_CAPTCHA à la mise en page de votre formulaire ou il sera automatiquement ajouté juste au-dessus du bouton d'envoi s'il n'est pas présent.','Send emails to members upon changes being made?'=>'Envoyer des e-mails aux membres lors de modifications ?','Invalid column name!'=>'Nom de colonne invalide !','The body of the email that will be sent to the contact person when a booking is made.'=>'Le corps de l’e-mail qui sera envoyé à la personne de contact lorsqu’une réservation est faite.','The subject of the email that will be sent to the contact person when a booking is made.'=>'L'objet de l'e-mail qui sera envoyé à la personne de contact quand une réservation est faite.','If pressing Save does not seem to be doing anything, then check all other tabs to make sure all required fields are filled out.'=>'Si le fait d'appuyer sur Enregistrer ne semble rien donner, vérifiez alors tous les autres onglets pour vous assurer que tous les champs obligatoires sont remplis.','Task Signup Pending Email'=>'E-mail d’inscription à une tâche, en attente','Booking cancelled and refunded'=>'Réservation annulée et remboursée','Todo reminder for event #_EVENTNAME: '=>'Rappel de tâche pour l'événement #_EVENTNAME : ','Add new todo'=>'Ajouter une nouvelle tâche','Event start offset (days)'=>'Décalage par rapport à la date de début de l’événement (jours)','When the template is being used as an attacment in a mail, the attachment has a default name. If you don't like the name given to the attachment in the mail, you can change it here. Relevant placeholders are allowed in their context (event/membership/booking/member/...). The '.pdf' extension will get added automatically, so no need to mention it.'=>'Lorsque le modèle est utilisé comme pièce jointe dans un e-mail, la pièce jointe porte un nom par défaut. Si vous n'aimez pas le nom donné à la pièce jointe dans l’e-mail, vous pouvez le modifier ici. Les espaces réservés pertinents sont autorisés dans leur contexte (événement/adhésion/réservation/membre/...). L'extension '.pdf' sera ajoutée automatiquement, il n'est donc pas nécessaire de la mentionner.','PDF mail attach format'=>'Format PDF de la pièce jointe','Newlines will get translated to HTML br-tags when/where/if appropriate but for templates that will get used in HTML output: be sure to not include newlines (certainly not empty lines) unless wanted.'=>'Les nouvelles lignes seront traduites en balises HTML br quand/où/si cela est approprié, mais pour les modèles qui seront utilisés dans la sortie HTML : assurez-vous de ne pas inclure de nouvelles lignes (et certainement pas des lignes vides) à moins que cela ne soit nécessaire.','Filter content'=>'Contenu du filtre','Task related mail'=>'E-mail lié aux tâches','Task form'=>'Formulaire de tâches','Signup failed'=>'L'inscription a échoué','Only one signup allowed'=>'Une seule inscription autorisée','Allow only one sign up for tasks per event for a person?'=>'N'autoriser qu'une seule inscription à des tâches par événement pour une personne ?','Consider pending (unapproved) task signups as available for new signups'=>'Considérer les inscriptions à des tâches en attente (non approuvées) comme disponibles pour les nouvelles inscriptions','Require approval for task signups?'=>'Exiger une approbation pour les inscriptions aux tâches ?','If the number of spaces for a task is 0, the task description will be treated as a section header for the next set of tasks.'=>'Si le nombre de places pour une tâche est de 0, la description de la tâche sera traitée comme un en-tête de section pour la prochaine série de tâches.','%d person already signed up for this task'=>'%d personne s'est déjà inscrite pour cette tâche' . "" . '%d personnes se sont déjà inscrites pour cette tâche','Task Signup Form (personal info section)'=>'Formulaire d'inscription aux tâches (section des informations personnelles)','Warning: this override will only be used when inside a single event, otherwise the generic setting will always be used!'=>'Attention : cette surcharge ne sera utilisée qu'à l'intérieur d'un seul évènement, sinon le paramètre générique sera toujours utilisé !','The layout of the task entry section in the signup form.'=>'La disposition de la section de saisie des tâches dans le formulaire d'inscription.','Task Signup Form (task entry section)'=>'Formulaire d'inscription à une tâche (section de saisie des tâches)','Approve selected task signups'=>'Approuver les inscriptions aux tâches sélectionnées','Send reminders for task signups'=>'Envoyer des rappels pour les inscriptions aux tâches','Attendance noted.'=>'Présence constatée.','Nothing done.'=>'Rien n’a été fait.','Event fields'=>'Champs d’Informations sur l’événement','Send emails to contact person too?'=>'Envoyer les e-mails à la personne de contact aussi ?','Mark person as present (attendance record)'=>'Marquer la personne comme présente (fiche de présence)','Send the configured reminder mail for approved bookings'=>'Envoyer le courriel de rappel configuré pour les réservations approuvées','Remove selected persons from group'=>'Retirer les personnes sélectionnées du groupe','Add selected persons to group'=>'Ajouter les personnes sélectionnées au groupe','Send the configured reminder mail for pending bookings'=>'Envoyer le courriel de rappel configuré pour les réservations en attente','Select an event'=>'Sélectionner un évènement','The location does not allow this many people to be present at the same time.'=>'L'emplacement ne permet pas à autant de personnes d'être présentes en même temps.','No valid membership found.'=>'Aucune adhésion valide n'a été trouvée.','Not allowed'=>'Non autorisé','From %s onwards (automatically extended)'=>'À partir de %s (extension automatique)','A person with this name and email already exists'=>'Une personne portant ce nom et cet e-mail existe déjà','Edit dynamic group of members'=>'Modifier un groupe dynamique de membres','Edit dynamic group of people'=>'Modifier un groupe dynamique de personnes','Add dynamic group of members'=>'Ajouter un groupe dynamique de membres','Add dynamic group of people'=>'Ajouter un groupe dynamique de personnes','By default, this is only set to true when a WP user is created by EME (when creating a member or doing a reservation for an event and the option to create a WP user is set). An admin will never be deleted.'=>'Par défaut, cette option n’est activée que lorsqu’un utilisateur WP est créé par EME (lors de la création d’un membre ou de la réservation d’un événement et lorsque l’option de création d’un utilisateur WP est activée). Un administrateur ne sera jamais supprimé.','Set this to yes if you want the linked WP user to be deleted when the EME person gets removed (moved to trash bin).'=>'Mettez cette option à oui si vous voulez que l'utilisateur WP lié soit supprimé lorsque la personne EME est supprimée (déplacée dans la corbeille).','Delete linked WP user?'=>'Supprimer un utilisateur WP lié ?','Select a WP user'=>'Sélectionnez un utilisateur WordPress','Please correct these errors: all EME people should have an unique name and email combination.'=>'Veuillez corriger ces erreurs : tous les personnes devraient avoir une combinaison unique de nom et d'adresse électronique.','The table below shows the people that have an identical name and email'=>'Le tableau ci-dessous indique les personnes dont le nom et l'adresse électronique sont identiques','Verify unique name/email combinations'=>'Vérifier les combinaisons uniques de nom et d'adresse électronique','Non-existing WP user linked!!'=>'Utilisateur WP lié non existant  !','Payment Gateway'=>'Passerelle de paiement','Discount info'=>'Informations de réduction','No access'=>'Pas d'accès','Updated person %1$d: %2$s'=>'Personne %1$d mise à jour: %2$s','Configured'=>'Configuré','Booking refunded for %1$s, payment id %2$d'=>'Réservation remboursée pour %1$s, identifiant de paiement %2$d','Webhook not created: localhost not allowed.'=>'Webhook non créé : localhost non autorisé.','Incorrect currency.'=>'Devise incorrecte.','No event found linked to this payment. If you believe you've received this message in error please contact the site owner.'=>'Aucun évènement lié à ce paiement n’a été trouvé. Si vous pensez avoir reçu ce message par erreur, veuillez contacter le propriétaire du site.','No member found linked to this payment. If you believe you've received this message in error please contact the site owner.'=>'Aucun membre lié à ce paiement n'a été trouvé. Si vous pensez avoir reçu ce message par erreur, veuillez contacter le propriétaire du site.','Event submission %s'=>'Soumission d'événement %s','Bancontact Pay - Wero'=>'Bancontact Pay - Wero','The HTML editor used by the plugin.'=>'L'éditeur HTML utilisé par le plugin.','Jodit'=>'Jodit','TinyMCE'=>'TinyMCE','HTML Editor'=>'Éditeur HTML','Documentation on date and time formatting.'=>'Documentation sur le formatage de date et heure.','CSV delimiter'=>'Délimiteur CSV','The default form format for submitting a new event.'=>'Format du formulaire par défaut pour soumettre un nouvel évènement.','Default form format'=>'Format du formulaire par défaut','Check this option if you want to allow image upload in the frontend wysiwyg editor for the event notes.'=>'Cocher cette option si vous souhaitez permettre le téléchargement d’image dans l’éditeur wysiwig sur le frontend pour les notes d’évènement.','Allow image upload?'=>'Permettre le téléchargement d’image ?','Check this option if you want to use a frontend wysiwyg editor for the event notes.'=>'Cocher cette option si vous voulez utiliser un éditeur wysiwig sur le frontend pour les notes d’évènement.','Use wysiwyg?'=>'Utiliser le wysiwyg ?','Permission needed to submit a new event when guest submit is not allowed. Default: %s'=>'Permission nécessaire pour soumettre un nouvel évènement quand la soumission par des invités n’est pas autorisée. Par défaut : %s','Access right to submit new events'=>'Droit nécessaire pour soumettre un nouvel évènement','Check this option if you want the submitter to be redirected to the login page if not logged in (or does not have the needed access rights) and guests are not allowed to submit new events.'=>'Cochez cette option si vous voulez que le soumissionnaire soit redirigé vers la page de login s’il n’est pas identifié (ou qu’il n’a pas les droits nécessaires) et que les invités ne sont pas autorisés à soumettre de nouveaux évènements.','Redirect to login page'=>'Redirection vers la page de login','The text shown to a guest when trying to submit a new event when they are not allowed to do so and the option to redirect to the login page is not set.'=>'Le texte affiché à un invité qui essaierait de soumettre un nouvel évènement sans y être autorisé, et que l’option de redirection vers la page de login n’a pas été cochée.','Guests not allowed text'=>'Texte affiché aux invités non autorisés','Check this option if you want to always show the success message even if the person submitting the event has the right to see the newly submitted event. This also means no redirection will happen.'=>'Cocher cette option si vous voulez toujours afficher le message de succès, même si la personne qui soumet l’évènement a les droits pour voir la page du nouvel évènement soumis. Ça veut dire qu’il n’y aura pas de redirection après.','Always show success message'=>'Toujours afficher le message de succès','Indicate in seconds how many seconds to wait before redirecting to the newly created event after showing the success message. If 0, the success message will not be shown and the redirect will happen immediately.'=>'Indiquez le nombre de secondes avant la redirection vers la page de l’évènement nouvellement créé après l’affichage du message de succès. Si 0, le message de réussite ne sera pas affiché et la redirection sera immédiate.','The message shown after successfully submitting a new event if the person submitting the event has no right to see the newly submitted event. The message is also shown if the event submission needs to be paid for and the redirection wait period (setting below this one) is not 0. This message can contain all event placeholders.'=>'Le message affiché après la soumission réussie d’un nouvel évènement si la personne qui soumet n’a pas le droit de voir le nouvel évènement soumis. Le message est aussi affiché si la soumission de l’évènement nécessite le paiement et si le temps d’attente avant la redirection (indiquée ci-dessous) n’est pas à 0. Ce message peut contenir tout espace réservé d’évènement.','Success Message'=>'Message de réussite','Check this option if you want guests also to be able to add new events.'=>'Cochez cette option si vous souhaitez que les invités puissent également ajouter de nouveaux événements.','Allow guest submit?'=>'Autorisé la soumission par un invité ?','Check this option if you want the location to be always created, even if the user does not have the needed capability set in EME to create locations.'=>'Cochez cette option si vous souhaitez que l’emplacement soit toujours créé, même si l'utilisateur n'a pas les capacités nécessaires dans EME pour créer des emplacements.','Force location creation?'=>'Forcer la création d’un emplacement ?','The default category assigned to an event if nothing is selected in the form.'=>'Catégorie par défaut attribuée à un événement si rien n'est sélectionné dans le formulaire.','Default category for new event'=>'Catégorie par défaut pour le nouvel événement','No contact'=>'Aucun contact','Select the contact person that will receive email notifications whenever an event gets submitted or paid for. The mail templates are to be configured in the regular mail templates section.'=>'Sélectionnez la personne de contact qui recevra des notifications par courrier électronique lorsqu'un événement est soumis ou payé. Les modèles de courrier doivent être configurés dans la section "Modèles de courrier".','The payment method(s) to be used when submitted events need to be paid for. This is not related to RSVP settings inside the submitted event itself!'=>'Le(s) mode(s) de paiement à utiliser lorsque les événements soumis doivent être payés. Cela n'a rien à voir avec les paramètres RSVP de l'événement soumis lui-même !','The currency used when submitted events need to be paid for. This is not related to RSVP settings inside the submitted event itself!'=>'La devise utilisée lorsque les événements soumis doivent être payés. Cela n'a rien à voir avec les paramètres RSVP de l'événement soumis lui-même !','The price to pay in order to submit a new event. Leave empty or 0 if no price is to be paid. This is not related to RSVP settings inside the submitted event itself! Configure the optional payment form header/footer sections in the EME Payment settings.'=>'Le prix à payer pour soumettre un nouvel événement. Laisser vide ou 0 si aucun prix ne doit être payé. Cela n'a rien à voir avec les paramètres RSVP de l'événement soumis lui-même ! Configurez les sections optionnelles d'en-tête et de pied de page du formulaire de paiement dans les paramètres de paiement de l'EME.','The state for a newly submitted event.'=>'L'état d'un événement nouvellement soumis.','Also check out the 'Email templates' and the 'Payment' sections for some extra frontend submit settings.'=>'Consultez également les sections "Modèles d'e-mail" et "Paiement" pour obtenir des paramètres de soumission frontaux supplémentaires.','Frontend Submit options'=>'Options de la soumission à partir du Frontend',''Yes' enables map scroll-wheel zooming. 'No' disables map scroll-wheel zooming. A browser refresh on a page showing a map will be necessary to see the effect of this change.'=>'« Oui » active le zoom à l'aide de la molette de la souris. « Non » désactive le zoom à l'aide de la molette de la souris. Il sera nécessaire d'actualiser la page affi

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.