Published : June 11, 2026

CVE-2026-7537: MDJM Event Management <= 1.7.8.3 Authenticated (Administrator+) Arbitrary File Upload via 'mdjm_email_upload_file' Parameter PoC, Patch Analysis & Rule

CVE ID CVE-2026-7537
Severity High (CVSS 7.2)
CWE 434
Vulnerable Version 1.7.8.3
Patched Version 1.7.8.4
Disclosed June 4, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-7537:
This vulnerability allows authenticated attackers with administrator-level access to upload arbitrary files to the MDJM Event Management WordPress plugin. The flaw exists in the `mdjm_send_comm_email` function within `/includes/admin/communications/comms-functions.php`. The vulnerability is classified as CWE-434 (Unrestricted Upload of File with Dangerous Type) and carries a CVSS score of 7.2, indicating high severity due to the potential for remote code execution.

Root Cause: The vulnerable code in the `mdjm_send_comm_email` function (lines 1-324 of the diff) handles file uploads from the `mdjm_email_upload_file` parameter with zero validation. The original code at line 290 uses `move_uploaded_file()` directly after checking only that the file name is not empty. There is no check for file extension, MIME type, file size within safe limits, or any WordPress file validation function like `wp_check_filetype()` or `wp_handle_upload()`. Files are moved to the WordPress uploads directory using the original filename from `$_FILES[‘mdjm_email_upload_file’][‘name’]`, which allows attackers to control the extension.

Exploitation: An attacker with administrator credentials sends a POST request to `/wp-admin/admin-post.php` with `action=mdjm-send_comm_email` and `mdjm_nonce` (a valid nonce can be obtained from the communications page form). The request includes a malicious PHP file in the `mdjm_email_upload_file` parameter of a multipart/form-data POST. The file is saved to the WordPress uploads directory with its original name and extension. The attacker then accesses the uploaded file directly via its URL to execute PHP code.

Patch Analysis: The patch in the updated code replaces the insecure `move_uploaded_file()` call with WordPress’s `wp_handle_upload()` function (line 300 of the patched code). It also adds a file extension check using `pathinfo()` against a constant `MDJM_BLOCKED_UPLOAD_EXTS` (line 297). If the file extension is blocked, the function aborts with a `comm_invalid_file` error message. The new `admin-notices.php` diff (lines 258-265) adds a handler to display this error to the user. The `wp_handle_upload()` function performs comprehensive validation including file type checking, MIME type verification, and applies WordPress’s built-in file extension whitelist.

Impact: Successful exploitation allows an attacker to upload arbitrary PHP files to the server. This leads to remote code execution with the privileges of the web server user. The attacker can then execute system commands, install backdoors, read/modify the WordPress database, access other sites on shared hosting, and potentially pivot to other systems on the same network. Given the administrator privilege requirement, the attack surface is limited to high-privilege users, but compromised admin accounts or malicious insiders can leverage this for complete site compromise.

Differential between vulnerable and patched code

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

Code Diff
--- a/mobile-dj-manager/includes/admin/admin-notices.php
+++ b/mobile-dj-manager/includes/admin/admin-notices.php
@@ -258,6 +258,14 @@
 			'error'
 		);
 	}
+	if ( isset( $_GET['mdjm-message'] ) && 'comm_invalid_file' === $_GET['mdjm-message'] ) {
+		add_settings_error(
+			'mdjm-notices',
+			'mdjm-comm_invalid_file',
+			__( 'The file could not be attached because this file type is not permitted.', 'mobile-dj-manager' ),
+			'error'
+		);
+	}
 	if ( isset( $_GET['mdjm-message'] ) && 'comm_missing_content' === $_GET['mdjm-message'] ) {
 		add_settings_error(
 			'mdjm-notices',
--- a/mobile-dj-manager/includes/admin/communications/comms-functions.php
+++ b/mobile-dj-manager/includes/admin/communications/comms-functions.php
@@ -1,324 +1,334 @@
-<?php
-/**
- * @author: Mike Howard, Jack Mawhinney, Dan Porter
- *
- * Contains the communication page for sending manual emails.
- *
- * @package     MDJM
- * @subpackage  Comms
- * @since       1.3
- */
-
-// Exit if accessed directly.
-if ( ! defined( 'ABSPATH' ) ) {
-	exit;
-}
-
-/**
- * Display the send email form on the communications page.
- *
- * @since   1.3
- */
-function mdjm_comms_page() {
-
-	if ( ! mdjm_employee_can( 'send_comms' ) ) {
-		wp_die(
-			'<h1>' . esc_html__( 'Cheatin’ uh?', 'mobile-dj-manager' ) . '</h1>' .
-			'<p>' . esc_html__( 'You do not have permission to access this page.', 'mobile-dj-manager' ) . '</p>',
-			403
-		);
-	}
-
-	global $current_user;
-
-	if ( mdjm_employee_can( 'list_all_clients' ) ) {
-		$clients = mdjm_get_clients();
-	} else {
-		$clients = mdjm_get_employee_clients();
-	}
-
-	if ( mdjm_employee_can( 'mdjm_employee_edit' ) ) {
-		$employees = mdjm_get_employees();
-	}
-
-	?>
-	<div class="wrap">
-		<h1><?php esc_html_e( 'Client and Employee Communications', 'mobile-dj-manager' ); ?></h1>
-		<form name="mdjm_form_send_comms" id="mdjm_form_send_comms" method="post" enctype="multipart/form-data">
-			<?php wp_nonce_field( 'send_comm_email', 'mdjm_nonce', true, true ); ?>
-			<?php mdjm_admin_action_field( 'send_comm_email' ); ?>
-			<input type="hidden" name="mdjm_email_from_address" id="mdjm_email_from_address" value="<?php echo esc_attr( $current_user->user_email ); ?>" />
-			<input type="hidden" name="mdjm_email_from_name" id="mdjm_email_from_name" value="<?php echo esc_attr( $current_user->display_name ); ?>" />
-			<?php do_action( 'mdjm_pre_comms_table' ); ?>
-			<table class="form-table">
-				<?php do_action( 'mdjm_add_comms_fields_before_recipient' ); ?>
-				<tr>
-					<th scope="row"><label for="mdjm_email_to"><?php esc_html_e( 'Select a Recipient', 'mobile-dj-manager' ); ?></label></th>
-					<td>
-						<select name="mdjm_email_to" id="mdjm_email_to">
-							<option value=""><?php esc_attr_e( 'Select a Recipient', 'mobile-dj-manager' ); ?></option>
-							<optgroup label="<?php esc_attr_e( 'Clients', 'mobile-dj-manager' ); ?>">
-								<?php
-								if ( empty( $clients ) ) {
-									echo '<option disabled="disabled">' . esc_html__( 'No Clients Found', 'mobile-dj-manager' ) . '</option>';
-								} else {
-									foreach ( $clients as $client ) {
-										echo '<option value="' . esc_attr( $client->ID ) . '">' . esc_html( $client->display_name ) . '</option>';
-									}
-								}
-								?>
-							</optgroup>
-							<?php
-							if ( ! empty( $employees ) ) {
-
-								echo '<optgroup label="' . esc_html__( 'Employees', 'mobile-dj-manager' ) . '">';
-
-								foreach ( $employees as $employee ) {
-									echo '<option value="' . esc_attr( $employee->ID ) . '">' . esc_html( $employee->display_name ) . '</option>';
-								}
-
-								echo '</optgroup>';
-							}
-							?>
-						</select>
-					</td>
-				</tr>
-				<?php do_action( 'mdjm_add_comms_fields_before_subject' ); ?>
-				<tr>
-					<th scope="row"><label for="mdjm_email_subject"><?php esc_html_e( 'Subject', 'mobile-dj-manager' ); ?></label></th>
-					<td><input type="text" name="mdjm_email_subject" id="mdjm_email_subject" class="regular-text" value="<?php echo isset( $_GET['template'] ) ? esc_attr( get_the_title( sanitize_text_field( wp_unslash( $_GET['template'] ) ) ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification ?>" /></td>
-				</tr>
-				<tr>
-					<th scope="row"><label for="mdjm_email_copy_to"><?php esc_html_e( 'Copy Yourself?', 'mobile-dj-manager' ); ?></label></th>
-					<td><input type="checkbox" name="mdjm_email_copy_to" id="mdjm_email_copy_to" value="<?php echo esc_attr( $current_user->user_email ); ?>" /> <span class="description"><?php esc_html_e( 'Settings may dictate that additional email copies are also sent', 'mobile-dj-manager' ); ?></span></td>
-				</tr>
-				<?php do_action( 'mdjm_add_comms_fields_before_template' ); ?>
-				<tr>
-					<th scope="row"><label for="mdjm_email_template"><?php esc_html_e( 'Select a Template', 'mobile-dj-manager' ); ?></label></th>
-					<td>
-						<select name="mdjm_email_template" id="mdjm_email_template">
-							<option value="0"><?php esc_html_e( 'No Template', 'mobile-dj-manager' ); ?></option>
-							<?php $template = isset( $_GET['template'] ) ? sanitize_text_field( wp_unslash( $_GET['template'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification ?>
-							<?php echo mdjm_comms_template_options( $template ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
-						</select>
-					</td>
-				</tr>
-				<?php do_action( 'mdjm_add_comms_fields_before_event' ); ?>
-				<tr>
-					<?php /* translators: %s = Staff type */ ?>
-					<th scope="row"><label for="mdjm_email_event"><?php printf( esc_html__( 'Associated %s', 'mobile-dj-manager' ), esc_html( mdjm_get_label_singular() ) ); ?></label></th>
-					<td>
-						<?php if ( isset( $_GET['event_id'] ) || ( isset( $_GET['mdjm-action'] ) && 'respond_unavailable' === $_GET['mdjm-action'] ) ) : // phpcs:ignore WordPress.Security.NonceVerification ?>
-							<?php
-							$value  = mdjm_get_event_date( absint( wp_unslash( $_GET['event_id'] ) ) ) . ' '; // phpcs:ignore WordPress.Security.NonceVerification
-							$value .= esc_html__( 'from', 'mobile-dj-manager' ) . ' '; // phpcs:ignore WordPress.Security.NonceVerification
-							$value .= mdjm_get_event_start( absint( wp_unslash( $_GET['event_id'] ) ) ) . ' '; // phpcs:ignore WordPress.Security.NonceVerification
-							$value .= '(' . mdjm_get_event_status( absint( wp_unslash( $_GET['event_id'] ) ) ) . ')'; // phpcs:ignore WordPress.Security.NonceVerification
-							?>
-							<input type="text" name="mdjm_email_event_show" id="mdjm_email_event_show" value="<?php echo esc_attr( $value ); ?>" readonly size="50" />
-							<input type="hidden" name="mdjm_email_event" id="mdjm_email_event" value="<?php echo absint( wp_unslash( $_GET['event_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification ?>" />
-						<?php else : ?>
-							<select name="mdjm_email_event" id="mdjm_email_event">
-							<option value="0"><?php esc_attr_e( 'Select an Event', 'mobile-dj-manager' ); ?></option>
-							</select>
-						<?php endif; ?>
-						<?php /* translators: %s placeholder is Event or Events if Plural */ ?>
-						<p class="description"><?php printf( __( 'If no %s is selected <code>{event_*}</code> content tags may not be used', 'mobile-dj-manager' ), mdjm_get_label_singular( true ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></p>
-					</td>
-				</tr>
-				<?php do_action( 'mdjm_add_comms_fields_before_file' ); ?>
-				<tr>
-					<th scope="row"><label for="mdjm_email_upload_file"><?php esc_html_e( 'Attach a File', 'mobile-dj-manager' ); ?></label></th>
-					<td><input type="file" name="mdjm_email_upload_file" id="mdjm_email_upload_file" class="regular-text" value="" />
-					<?php /* translators: %d WordPress max file size*/ ?>
-						<p class="description"><?php printf( __( 'Max file size %dMB. Change php.ini <code>post_max_size</code> to increase', 'mobile-dj-manager' ), ini_get( 'post_max_size' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></p>
-					</td>
-				</tr>
-				<?php do_action( 'mdjm_add_comms_fields_before_content' ); ?>
-				<tr>
-					<td colspan="2">
-						<?php
-							$content = isset( $_GET['template'] ) ? mdjm_get_email_template_content( absint( wp_unslash( $_GET['template'] ) ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
-
-							wp_editor(
-								$content,
-								'mdjm_email_content',
-								array(
-									'media_buttons' => true,
-									'textarea_rows' => 10,
-									'editor_class'  => 'required',
-								)
-							);
-						?>
-					</td>
-				</tr>
-			</table>
-			<?php do_action( 'mdjm_post_comms_table' ); ?>
-			<?php submit_button( __( 'Send Email', 'mobile-dj-manager' ), 'primary', 'submit', true ); ?>
-		</form>
-	</div>
-	<?php
-
-} // mdjm_comms_page
-
-/**
- * Retrieve the templates
- *
- * @since   1.3
- * @param   str|arr $type   The type of template to retrieve.
- * @return  obj|bool    WP_Query object or false if none found
- */
-function mdjm_get_templates( $type = array( 'contract', 'email_template' ) ) {
-
-	$templates = get_posts(
-		array(
-			'post_type'      => $type,
-			'posts_per_page' => -1,
-			'post_status'    => 'publish',
-		)
-	);
-
-	return $templates;
-
-} // mdjm_get_templates
-
-/**
- * Generates the options for a select list of templates grouped by post type
- *
- * @since   1.3
- * @param   int $selected   The ID of the template that should be initially selected.
- * @return  str     HTML Output for the select options.
- */
-function mdjm_comms_template_options( $selected = 0 ) {
-
-	$templates = mdjm_get_templates();
-
-	$output = '';
-
-	if ( ! $templates ) {
-		$output .= '<option disabled>' . __( 'No Templates Found', 'mobile-dj-manager' ) . '</option>';
-	} else {
-
-		foreach ( $templates as $template ) {
-
-			$comms_templates[ $template->post_type ][ $template->ID ] = $template->post_title;
-
-		}
-
-		foreach ( $comms_templates as $group => $comms_template ) {
-			$output .= '<optgroup label="' . strtoupper( get_post_type_object( $group )->label ) . '">';
-
-			foreach ( $comms_template as $template_id => $template_name ) {
-
-				$output .= '<option value="' . $template_id . '"' . selected( $selected, $template_id, false ) . '>' . $template_name . '</option>';
-			}
-
-			$output .= '</optgroup>';
-		}
-	}
-
-	return $output;
-
-} // mdjm_comms_template_options
-
-/**
- * Process the sending of the email
- *
- * @since   1.3
- * @param   arr $data   Super global $_POST array.
- * @return  void
- */
-function mdjm_send_comm_email( $data ) {
-
-	$url = remove_query_arg( array( 'mdjm-message', 'event_id', 'template', 'recipient', 'mdjm-action' ) );
-
-	if ( ! wp_verify_nonce( $data['mdjm_nonce'], 'send_comm_email' ) ) {
-		$message = 'nonce_fail';
-	} elseif ( empty( $data['mdjm_email_to'] ) || empty( $data['mdjm_email_subject'] ) || empty( $data['mdjm_email_content'] ) ) {
-		$message = 'comm_missing_content';
-	} else {
-
-		if ( isset( $_FILES['mdjm_email_upload_file'] ) && '' !== $_FILES['mdjm_email_upload_file']['name'] ) {
-			$upload_dir = wp_upload_dir();
-
-			$file_name = $_FILES['mdjm_email_upload_file']['name']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
-			$file_path = $upload_dir['path'] . '/' . $file_name;
-			$tmp_path  = $_FILES['mdjm_email_upload_file']['tmp_name']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
-
-			if ( move_uploaded_file( $tmp_path, $file_path ) ) {
-				$attachments[] = $file_path;
-			}
-		}
-
-		if ( empty( $attachments ) ) {
-			$attachments = array();
-		}
-
-		$attachments = apply_filters( 'mdjm_send_comm_email_attachments', $attachments, $data );
-		$client_id   = $data['mdjm_email_to'];
-
-		if ( ! empty( $data['mdjm_email_event'] ) ) {
-			$event     = new MDJM_Event( $data['mdjm_email_event'] );
-			$client_id = $event->client;
-		}
-
-		$email_args = array(
-			'to_email'    => mdjm_get_client_email( $data['mdjm_email_to'] ),
-			'from_name'   => $data['mdjm_email_from_name'],
-			'from_email'  => $data['mdjm_email_from_address'],
-			'event_id'    => $data['mdjm_email_event'],
-			'client_id'   => $client_id,
-			'subject'     => stripslashes( $data['mdjm_email_subject'] ),
-			'attachments' => ! empty( $attachments ) ? $attachments : array(),
-			'message'     => stripslashes( $data['mdjm_email_content'] ),
-			'track'       => true,
-			'copy_to'     => ! empty( $data['mdjm_email_copy_to'] ) ? array( $data['mdjm_email_copy_to'] ) : array(),
-			'source'      => __( 'Communication Feature', 'mobile-dj-manager' ),
-		);
-
-		if ( mdjm_send_email_content( $email_args ) ) {
-			$message = 'comm_sent';
-
-			if ( ! empty( $data['mdjm_event_reject'] ) ) {
-
-				$args = array(
-					'reject_reason' => ! empty( $data['mdjm_email_reject_reason'] ) ? $data['mdjm_email_reject_reason'] : __( 'No reason specified', 'mobile-dj-manager' ),
-				);
-
-				mdjm_update_event_status( $email_args['event_id'], 'mdjm-rejected', get_post_status( $email_args['event_id'] ), $args );
-			}
-		} else {
-			$message = 'comm_not_sent';
-		}
-	}
-
-	wp_safe_redirect( add_query_arg( 'mdjm-message', $message, $url ) );
-
-	exit;
-
-} // mdjm_send_comm_email
-add_action( 'mdjm-send_comm_email', 'mdjm_send_comm_email' );
-
-/**
- * Add the comment field to record why an event is being rejected.
- *
- * @since   1.3.3
- */
-function mdjm_add_reject_reason_field() {
-
-	if ( ! isset( $_GET['mdjm-action'] ) || 'respond_unavailable' !== $_GET['mdjm-action'] ) { // phpcs:ignore WordPress.Security.NonceVerification
-		return;
-	}
-
-	$output  = '<tr>';
-	$output .= '<th scope="row"><label for="mdjm_email_reject_reason">' . __( 'Rejection Reason', 'mobile-dj-manager' ) . '</label></th>';
-	$output .= '<td><textarea name="mdjm_email_reject_reason" id="mdjm_email_reject_reason" cols="50" rows="3" clas="class="large-text code"></textarea>';
-	$output .= '<p class="description">' . __( 'Optional. If completed, this entry will be added to the event journal.', 'mobile-dj-manager' ) . '</p>';
-	$output .= '<input type="hidden" name="mdjm_event_reject" id="mdjm_event_reject" value="1" />';
-	$output .= '</td>';
-	$output .= '</tr>';
-
-	echo apply_filters( 'mdjm_add_reject_reason_field', $output ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
-
-} // mdjm_add_reject_reason_field
-add_action( 'mdjm_add_comms_fields_before_file', 'mdjm_add_reject_reason_field' );
+<?php
+/**
+ * @author: Mike Howard, Jack Mawhinney, Dan Porter
+ *
+ * Contains the communication page for sending manual emails.
+ *
+ * @package     MDJM
+ * @subpackage  Comms
+ * @since       1.3
+ */
+
+// Exit if accessed directly.
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
+/**
+ * Display the send email form on the communications page.
+ *
+ * @since   1.3
+ */
+function mdjm_comms_page() {
+
+	if ( ! mdjm_employee_can( 'send_comms' ) ) {
+		wp_die(
+			'<h1>' . esc_html__( 'Cheatin’ uh?', 'mobile-dj-manager' ) . '</h1>' .
+			'<p>' . esc_html__( 'You do not have permission to access this page.', 'mobile-dj-manager' ) . '</p>',
+			403
+		);
+	}
+
+	global $current_user;
+
+	if ( mdjm_employee_can( 'list_all_clients' ) ) {
+		$clients = mdjm_get_clients();
+	} else {
+		$clients = mdjm_get_employee_clients();
+	}
+
+	if ( mdjm_employee_can( 'mdjm_employee_edit' ) ) {
+		$employees = mdjm_get_employees();
+	}
+
+	?>
+	<div class="wrap">
+		<h1><?php esc_html_e( 'Client and Employee Communications', 'mobile-dj-manager' ); ?></h1>
+		<form name="mdjm_form_send_comms" id="mdjm_form_send_comms" method="post" enctype="multipart/form-data">
+			<?php wp_nonce_field( 'send_comm_email', 'mdjm_nonce', true, true ); ?>
+			<?php mdjm_admin_action_field( 'send_comm_email' ); ?>
+			<input type="hidden" name="mdjm_email_from_address" id="mdjm_email_from_address" value="<?php echo esc_attr( $current_user->user_email ); ?>" />
+			<input type="hidden" name="mdjm_email_from_name" id="mdjm_email_from_name" value="<?php echo esc_attr( $current_user->display_name ); ?>" />
+			<?php do_action( 'mdjm_pre_comms_table' ); ?>
+			<table class="form-table">
+				<?php do_action( 'mdjm_add_comms_fields_before_recipient' ); ?>
+				<tr>
+					<th scope="row"><label for="mdjm_email_to"><?php esc_html_e( 'Select a Recipient', 'mobile-dj-manager' ); ?></label></th>
+					<td>
+						<select name="mdjm_email_to" id="mdjm_email_to">
+							<option value=""><?php esc_attr_e( 'Select a Recipient', 'mobile-dj-manager' ); ?></option>
+							<optgroup label="<?php esc_attr_e( 'Clients', 'mobile-dj-manager' ); ?>">
+								<?php
+								if ( empty( $clients ) ) {
+									echo '<option disabled="disabled">' . esc_html__( 'No Clients Found', 'mobile-dj-manager' ) . '</option>';
+								} else {
+									foreach ( $clients as $client ) {
+										echo '<option value="' . esc_attr( $client->ID ) . '">' . esc_html( $client->display_name ) . '</option>';
+									}
+								}
+								?>
+							</optgroup>
+							<?php
+							if ( ! empty( $employees ) ) {
+
+								echo '<optgroup label="' . esc_html__( 'Employees', 'mobile-dj-manager' ) . '">';
+
+								foreach ( $employees as $employee ) {
+									echo '<option value="' . esc_attr( $employee->ID ) . '">' . esc_html( $employee->display_name ) . '</option>';
+								}
+
+								echo '</optgroup>';
+							}
+							?>
+						</select>
+					</td>
+				</tr>
+				<?php do_action( 'mdjm_add_comms_fields_before_subject' ); ?>
+				<tr>
+					<th scope="row"><label for="mdjm_email_subject"><?php esc_html_e( 'Subject', 'mobile-dj-manager' ); ?></label></th>
+					<td><input type="text" name="mdjm_email_subject" id="mdjm_email_subject" class="regular-text" value="<?php echo isset( $_GET['template'] ) ? esc_attr( get_the_title( sanitize_text_field( wp_unslash( $_GET['template'] ) ) ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification ?>" /></td>
+				</tr>
+				<tr>
+					<th scope="row"><label for="mdjm_email_copy_to"><?php esc_html_e( 'Copy Yourself?', 'mobile-dj-manager' ); ?></label></th>
+					<td><input type="checkbox" name="mdjm_email_copy_to" id="mdjm_email_copy_to" value="<?php echo esc_attr( $current_user->user_email ); ?>" /> <span class="description"><?php esc_html_e( 'Settings may dictate that additional email copies are also sent', 'mobile-dj-manager' ); ?></span></td>
+				</tr>
+				<?php do_action( 'mdjm_add_comms_fields_before_template' ); ?>
+				<tr>
+					<th scope="row"><label for="mdjm_email_template"><?php esc_html_e( 'Select a Template', 'mobile-dj-manager' ); ?></label></th>
+					<td>
+						<select name="mdjm_email_template" id="mdjm_email_template">
+							<option value="0"><?php esc_html_e( 'No Template', 'mobile-dj-manager' ); ?></option>
+							<?php $template = isset( $_GET['template'] ) ? sanitize_text_field( wp_unslash( $_GET['template'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification ?>
+							<?php echo mdjm_comms_template_options( $template ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+						</select>
+					</td>
+				</tr>
+				<?php do_action( 'mdjm_add_comms_fields_before_event' ); ?>
+				<tr>
+					<?php /* translators: %s = Staff type */ ?>
+					<th scope="row"><label for="mdjm_email_event"><?php printf( esc_html__( 'Associated %s', 'mobile-dj-manager' ), esc_html( mdjm_get_label_singular() ) ); ?></label></th>
+					<td>
+						<?php if ( isset( $_GET['event_id'] ) || ( isset( $_GET['mdjm-action'] ) && 'respond_unavailable' === $_GET['mdjm-action'] ) ) : // phpcs:ignore WordPress.Security.NonceVerification ?>
+							<?php
+							$value  = mdjm_get_event_date( absint( wp_unslash( $_GET['event_id'] ) ) ) . ' '; // phpcs:ignore WordPress.Security.NonceVerification
+							$value .= esc_html__( 'from', 'mobile-dj-manager' ) . ' '; // phpcs:ignore WordPress.Security.NonceVerification
+							$value .= mdjm_get_event_start( absint( wp_unslash( $_GET['event_id'] ) ) ) . ' '; // phpcs:ignore WordPress.Security.NonceVerification
+							$value .= '(' . mdjm_get_event_status( absint( wp_unslash( $_GET['event_id'] ) ) ) . ')'; // phpcs:ignore WordPress.Security.NonceVerification
+							?>
+							<input type="text" name="mdjm_email_event_show" id="mdjm_email_event_show" value="<?php echo esc_attr( $value ); ?>" readonly size="50" />
+							<input type="hidden" name="mdjm_email_event" id="mdjm_email_event" value="<?php echo absint( wp_unslash( $_GET['event_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification ?>" />
+						<?php else : ?>
+							<select name="mdjm_email_event" id="mdjm_email_event">
+							<option value="0"><?php esc_attr_e( 'Select an Event', 'mobile-dj-manager' ); ?></option>
+							</select>
+						<?php endif; ?>
+						<?php /* translators: %s placeholder is Event or Events if Plural */ ?>
+						<p class="description"><?php printf( __( 'If no %s is selected <code>{event_*}</code> content tags may not be used', 'mobile-dj-manager' ), mdjm_get_label_singular( true ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></p>
+					</td>
+				</tr>
+				<?php do_action( 'mdjm_add_comms_fields_before_file' ); ?>
+				<tr>
+					<th scope="row"><label for="mdjm_email_upload_file"><?php esc_html_e( 'Attach a File', 'mobile-dj-manager' ); ?></label></th>
+					<td><input type="file" name="mdjm_email_upload_file" id="mdjm_email_upload_file" class="regular-text" value="" />
+					<?php /* translators: %d WordPress max file size*/ ?>
+						<p class="description"><?php printf( __( 'Max file size %dMB. Change php.ini <code>post_max_size</code> to increase', 'mobile-dj-manager' ), ini_get( 'post_max_size' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></p>
+					</td>
+				</tr>
+				<?php do_action( 'mdjm_add_comms_fields_before_content' ); ?>
+				<tr>
+					<td colspan="2">
+						<?php
+							$content = isset( $_GET['template'] ) ? mdjm_get_email_template_content( absint( wp_unslash( $_GET['template'] ) ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
+
+							wp_editor(
+								$content,
+								'mdjm_email_content',
+								array(
+									'media_buttons' => true,
+									'textarea_rows' => 10,
+									'editor_class'  => 'required',
+								)
+							);
+						?>
+					</td>
+				</tr>
+			</table>
+			<?php do_action( 'mdjm_post_comms_table' ); ?>
+			<?php submit_button( __( 'Send Email', 'mobile-dj-manager' ), 'primary', 'submit', true ); ?>
+		</form>
+	</div>
+	<?php
+
+} // mdjm_comms_page
+
+/**
+ * Retrieve the templates
+ *
+ * @since   1.3
+ * @param   str|arr $type   The type of template to retrieve.
+ * @return  obj|bool    WP_Query object or false if none found
+ */
+function mdjm_get_templates( $type = array( 'contract', 'email_template' ) ) {
+
+	$templates = get_posts(
+		array(
+			'post_type'      => $type,
+			'posts_per_page' => -1,
+			'post_status'    => 'publish',
+		)
+	);
+
+	return $templates;
+
+} // mdjm_get_templates
+
+/**
+ * Generates the options for a select list of templates grouped by post type
+ *
+ * @since   1.3
+ * @param   int $selected   The ID of the template that should be initially selected.
+ * @return  str     HTML Output for the select options.
+ */
+function mdjm_comms_template_options( $selected = 0 ) {
+
+	$templates = mdjm_get_templates();
+
+	$output = '';
+
+	if ( ! $templates ) {
+		$output .= '<option disabled>' . __( 'No Templates Found', 'mobile-dj-manager' ) . '</option>';
+	} else {
+
+		foreach ( $templates as $template ) {
+
+			$comms_templates[ $template->post_type ][ $template->ID ] = $template->post_title;
+
+		}
+
+		foreach ( $comms_templates as $group => $comms_template ) {
+			$output .= '<optgroup label="' . strtoupper( get_post_type_object( $group )->label ) . '">';
+
+			foreach ( $comms_template as $template_id => $template_name ) {
+
+				$output .= '<option value="' . $template_id . '"' . selected( $selected, $template_id, false ) . '>' . $template_name . '</option>';
+			}
+
+			$output .= '</optgroup>';
+		}
+	}
+
+	return $output;
+
+} // mdjm_comms_template_options
+
+/**
+ * Process the sending of the email
+ *
+ * @since   1.3
+ * @param   arr $data   Super global $_POST array.
+ * @return  void
+ */
+function mdjm_send_comm_email( $data ) {
+
+	$url = remove_query_arg( array( 'mdjm-message', 'event_id', 'template', 'recipient', 'mdjm-action' ) );
+
+	if ( ! wp_verify_nonce( $data['mdjm_nonce'], 'send_comm_email' ) ) {
+		$message = 'nonce_fail';
+	} elseif ( empty( $data['mdjm_email_to'] ) || empty( $data['mdjm_email_subject'] ) || empty( $data['mdjm_email_content'] ) ) {
+		$message = 'comm_missing_content';
+	} else {
+
+		if ( isset( $_FILES['mdjm_email_upload_file'] ) && '' !== $_FILES['mdjm_email_upload_file']['name'] ) {
+			$file_ext = strtolower( pathinfo( sanitize_file_name( $_FILES['mdjm_email_upload_file']['name'] ), PATHINFO_EXTENSION ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
+
+			if ( in_array( $file_ext, MDJM_BLOCKED_UPLOAD_EXTS, true ) ) {
+				$message = 'comm_invalid_file';
+			} else {
+				$uploaded = wp_handle_upload(
+					$_FILES['mdjm_email_upload_file'], // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
+					array( 'test_form' => false )
+				);
+
+				if ( $uploaded && ! isset( $uploaded['error'] ) ) {
+					$attachments[] = $uploaded['file'];
+				}
+			}
+		}
+
+		if ( isset( $message ) ) {
+			wp_safe_redirect( add_query_arg( 'mdjm-message', $message, $url ) );
+			exit;
+		}
+
+		if ( empty( $attachments ) ) {
+			$attachments = array();
+		}
+
+		$attachments = apply_filters( 'mdjm_send_comm_email_attachments', $attachments, $data );
+		$client_id   = $data['mdjm_email_to'];
+
+		if ( ! empty( $data['mdjm_email_event'] ) ) {
+			$event     = new MDJM_Event( $data['mdjm_email_event'] );
+			$client_id = $event->client;
+		}
+
+		$email_args = array(
+			'to_email'    => mdjm_get_client_email( $data['mdjm_email_to'] ),
+			'from_name'   => $data['mdjm_email_from_name'],
+			'from_email'  => $data['mdjm_email_from_address'],
+			'event_id'    => $data['mdjm_email_event'],
+			'client_id'   => $client_id,
+			'subject'     => stripslashes( $data['mdjm_email_subject'] ),
+			'attachments' => ! empty( $attachments ) ? $attachments : array(),
+			'message'     => stripslashes( $data['mdjm_email_content'] ),
+			'track'       => true,
+			'copy_to'     => ! empty( $data['mdjm_email_copy_to'] ) ? array( $data['mdjm_email_copy_to'] ) : array(),
+			'source'      => __( 'Communication Feature', 'mobile-dj-manager' ),
+		);
+
+		if ( mdjm_send_email_content( $email_args ) ) {
+			$message = 'comm_sent';
+
+			if ( ! empty( $data['mdjm_event_reject'] ) ) {
+
+				$args = array(
+					'reject_reason' => ! empty( $data['mdjm_email_reject_reason'] ) ? $data['mdjm_email_reject_reason'] : __( 'No reason specified', 'mobile-dj-manager' ),
+				);
+
+				mdjm_update_event_status( $email_args['event_id'], 'mdjm-rejected', get_post_status( $email_args['event_id'] ), $args );
+			}
+		} else {
+			$message = 'comm_not_sent';
+		}
+	}
+
+	wp_safe_redirect( add_query_arg( 'mdjm-message', $message, $url ) );
+
+	exit;
+
+} // mdjm_send_comm_email
+add_action( 'mdjm-send_comm_email', 'mdjm_send_comm_email' );
+
+/**
+ * Add the comment field to record why an event is being rejected.
+ *
+ * @since   1.3.3
+ */
+function mdjm_add_reject_reason_field() {
+
+	if ( ! isset( $_GET['mdjm-action'] ) || 'respond_unavailable' !== $_GET['mdjm-action'] ) { // phpcs:ignore WordPress.Security.NonceVerification
+		return;
+	}
+
+	$output  = '<tr>';
+	$output .= '<th scope="row"><label for="mdjm_email_reject_reason">' . __( 'Rejection Reason', 'mobile-dj-manager' ) . '</label></th>';
+	$output .= '<td><textarea name="mdjm_email_reject_reason" id="mdjm_email_reject_reason" cols="50" rows="3" clas="class="large-text code"></textarea>';
+	$output .= '<p class="description">' . __( 'Optional. If completed, this entry will be added to the event journal.', 'mobile-dj-manager' ) . '</p>';
+	$output .= '<input type="hidden" name="mdjm_event_reject" id="mdjm_event_reject" value="1" />';
+	$output .= '</td>';
+	$output .= '</tr>';
+
+	echo apply_filters( 'mdjm_add_reject_reason_field', $output ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+
+} // mdjm_add_reject_reason_field
+add_action( 'mdjm_add_comms_fields_before_file', 'mdjm_add_reject_reason_field' );
--- a/mobile-dj-manager/includes/admin/events/events.php
+++ b/mobile-dj-manager/includes/admin/events/events.php
@@ -866,7 +866,7 @@
     }

     if (mdjm_employee_can('read_events')) {
-        echo '<a class="button-primary" href="' . esc_url(mdjm_get_admin_page('events')) . '">' .
+        echo '<a class="button-secondary" href="' . esc_url(mdjm_get_admin_page('events')) . '">' .
             /* translators: %s Event or Events */
             sprintf(esc_html__('Back to %s list', 'mobile-dj-manager'), esc_html(mdjm_get_label_singular())), "</a>rn";
     } else {
--- a/mobile-dj-manager/includes/admin/pages/event-fields.php
+++ b/mobile-dj-manager/includes/admin/pages/event-fields.php
@@ -257,7 +257,7 @@
 		function update_field()
 		{

-			if (!isset($_POST['custom_field_id']) || !isset($_GET['id']) || !isset($_POST['field_label'])) {
+			if (!isset($_POST['custom_field_id']) || !isset($_POST['field_label'])) {
 				wp_safe_redirect(mdjm_get_admin_page('custom_event_fields') . '&message=5');
 				exit;
 			}
@@ -549,12 +549,7 @@

 					<p>
 						<label for="field_label"><?php esc_html_e('Label', 'mobile-dj-manager'); ?>:</label><br />
-						<input type="text" name="field_label" id="field_label" class="regular-text" value="
-			<?php
-			echo (!empty($editing) ?
-				esc_attr(get_the_title(absint(wp_unslash($_GET['id'])))) : '');
-			?>
-			" class="regular-text" required="required" />
+						<input type="text" name="field_label" id="field_label" class="regular-text" value="<?php echo ( ! empty( $editing ) ? esc_attr( get_the_title( absint( wp_unslash( $_GET['id'] ) ) ) ) : '' ); ?>" required="required" />
 					</p>

 					<p>
@@ -588,23 +583,14 @@
 						<p>
 							<label
 								for="_mdjm_field_options"><?php esc_html_e('Selectable Options', 'mobile-dj-manager'); ?>:</label><br />
-							<textarea name="_mdjm_field_options" id="_mdjm_field_options" class="all-options" rows="5">
-																<?php
-																echo (!empty($editing) ? esc_html(get_post_meta(absint(wp_unslash($_GET['id'])), '_mdjm_field_options', true)) : '');
-																?>
-																	</textarea><br />
+							<textarea name="_mdjm_field_options" id="_mdjm_field_options" class="all-options" rows="5"><?php echo ( ! empty( $editing ) ? esc_html( get_post_meta( absint( wp_unslash( $_GET['id'] ) ), '_mdjm_field_options', true ) ) : '' ); ?></textarea><br />
 							<span class="description"><?php esc_html_e('One entry per line', 'mobile-dj-manager'); ?></span>
 						</p>
 					</div>
 					<div id="value_field_checkbox">
 						<p>
 							<label for="_mdjm_field_value"><?php esc_html_e('Checked Value', 'mobile-dj-manager'); ?>:</label><br />
-							<input type="text" name="_mdjm_field_value" id="_mdjm_field_value" value="
-				<?php
-				echo (!empty($editing) ?
-					esc_attr(get_post_meta(absint(wp_unslash($_GET['id'])), '_mdjm_field_value', true)) : '1');
-				?>
-					" class="small-text" />
+							<input type="text" name="_mdjm_field_value" id="_mdjm_field_value" value="<?php echo ( ! empty( $editing ) ? esc_attr( get_post_meta( absint( wp_unslash( $_GET['id'] ) ), '_mdjm_field_value', true ) ) : '1' ); ?>" class="small-text" />
 						</p>

 						<p>
@@ -640,12 +626,7 @@

 					<p>
 						<label for="field_desc"><?php esc_html_e('Description', 'mobile-dj-manager'); ?>:</label><br />
-						<input type="text" name="field_desc" id="field_desc" value="
-			<?php
-			echo (!empty($editing) ?
-				esc_attr($field->post_content) : '');
-			?>
-			" class="regular-text" /><br />
+						<input type="text" name="field_desc" id="field_desc" value="<?php echo ( ! empty( $editing ) ? esc_attr( $field->post_content ) : '' ); ?>" class="regular-text" /><br />
 						<span class="description"><?php esc_html_e("Not visible to client's", 'mobile-dj-manager'); ?></span>
 					</p>

--- a/mobile-dj-manager/includes/admin/roles/class-mdjm-permissions.php
+++ b/mobile-dj-manager/includes/admin/roles/class-mdjm-permissions.php
@@ -1,726 +1,726 @@
-<?php
-/**
- * @author: Mike Howard, Jack Mawhinney, Dan Porter
- */
-	defined( 'ABSPATH' ) or die( 'Direct access to this page is disabled!!!' );
-
-/**
- * Class Name: MDJM_Permissions
- * Manage User permissions within MDJM
- */
-class MDJM_Permissions {
-	/**
-	 * Class constructor
-	 */
-	public function __construct() {
-		// Capture form submissions
-		add_action( 'init', array( &$this, 'init' ) );
-	}
-
-	/**
-	 * Hook into the init action to process form submissions
-	 */
-	public function init() {
-		if ( isset( $_POST['set-permissions'], $_POST['mdjm_set_permissions'] ) ) {
-			$this->set_permissions();
-		}
-	} // init
-
-	/**
-	 * Set permissions for the given roles
-	 *
-	 * @since   1.3
-	 * @param
-	 * @return
-	 */
-	public function set_permissions() {
-
-		if ( ! isset( $_POST['employee_roles'] ) ) {
-			return;
-		}
-
-		$fields = array(
-			'comm_permissions'     => 'mdjm_comms',
-			'client_permissions'   => 'mdjm_client',
-			'employee_permissions' => 'mdjm_employee',
-			'event_permissions'    => 'mdjm_event',
-			'package_permissions'  => 'mdjm_package',
-			'quote_permissions'    => 'mdjm_quote',
-			'report_permissions'   => 'mdjm_reports',
-			'template_permissions' => 'mdjm_template',
-			'txn_permissions'      => 'mdjm_txn',
-			'venue_permissions'    => 'mdjm_venue',
-		);
-
-		foreach ( wp_unslash( $_POST['employee_roles'] ) as $_role ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
-			$role = get_role( $_role );
-
-			// If the role is to become admin
-			if ( ! empty( $_POST[ 'manage_mdjm_' . $_role ] ) ) {
-
-				$role->add_cap( 'manage_mdjm' );
-				$this->make_admin( $_role );
-				continue;
-
-			} else {
-				$role->remove_cap( 'manage_mdjm' );
-			}
-
-			// Every role has the MDJM Employee capability
-			$role->add_cap( 'mdjm_employee' );
-
-			foreach ( $fields as $field => $prefix ) {
-
-				$caps = empty( $_POST[ $field . '_' . $_role ] ) ?
-					$this->get_capabilities( $prefix . '_none' ) :
-					$this->get_capabilities( wp_unslash( $_POST[ $field . '_' . $_role ] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
-
-				foreach ( $caps as $cap => $val ) {
-
-					if ( empty( $val ) ) {
-						$role->remove_cap( $cap );
-					} else {
-						$role->add_cap( $cap );
-					}
-				}
-			}
-		}
-
-		wp_safe_redirect( wp_unslash( $_SERVER['HTTP_REFERER'] ) . '&role_action=1&message=4' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
-		exit;
-
-	} // set_permissions
-
-	/**
-	 * Defines all of the required capabilities for the requested capability.
-	 *
-	 * @since   1.3
-	 * @param   str $cap        The capability being requested
-	 * @return  arr     $caps       The required capabilities that the requested capability needs or false if they cannot be calculated
-	 */
-	public function get_capabilities( $cap ) {
-
-		switch ( $cap ) {
-			/**
-			 * Clients
-			 */
-			case 'mdjm_client_none':
-				$caps = array(
-					'mdjm_client_edit'     => false,
-					'mdjm_client_edit_own' => false,
-				);
-				break;
-
-			case 'mdjm_client_edit_own':
-				$caps = array(
-					'mdjm_client_edit'     => false,
-					'mdjm_client_edit_own' => true,
-				);
-				break;
-
-			case 'mdjm_client_edit':
-				$caps = array(
-					'mdjm_client_edit'     => true,
-					'mdjm_client_edit_own' => true,
-				);
-				break;
-			/**
-			 * Communications
-			 */
-			case 'mdjm_comms_none':
-				$caps = array(
-					'mdjm_comms_send'             => false,
-					'edit_mdjm_comms'             => false,
-					'edit_others_mdjm_comms'      => false,
-					'publish_mdjm_comms'          => false,
-					'read_private_mdjm_comms'     => false,
-					'edit_published_mdjm_comms'   => false,
-					'delete_mdjm_comms'           => false,
-					'delete_others_mdjm_comms'    => false,
-					'delete_private_mdjm_comms'   => false,
-					'delete_published_mdjm_comms' => false,
-					'edit_private_mdjm_comms'     => false,
-				);
-				break;
-
-			case 'mdjm_comms_send':
-				$caps = array(
-					'mdjm_comms_send'             => true,
-					'edit_mdjm_comms'             => true,
-					'edit_others_mdjm_comms'      => true,
-					'publish_mdjm_comms'          => true,
-					'read_private_mdjm_comms'     => true,
-					'edit_published_mdjm_comms'   => true,
-					'delete_mdjm_comms'           => true,
-					'delete_others_mdjm_comms'    => true,
-					'delete_private_mdjm_comms'   => true,
-					'delete_published_mdjm_comms' => true,
-					'edit_private_mdjm_comms'     => true,
-				);
-				break;
-
-			/**
-			 * Employees
-			 */
-			case 'mdjm_employee_none':
-				$caps = array( 'mdjm_employee_edit' => false );
-				break;
-
-			case 'mdjm_employee_edit':
-				$caps = array( 'mdjm_employee_edit' => true );
-				break;
-			/**
-			 * Events
-			 */
-			case 'mdjm_event_none':
-				$caps = array(
-					'mdjm_event_read'           => false,
-					'mdjm_event_read_own'       => false,
-					'mdjm_event_edit'           => false,
-					'mdjm_event_edit_own'       => false,
-					'publish_mdjm_events'       => false,
-					'edit_mdjm_events'          => false,
-					'edit_others_mdjm_events'   => false,
-					'delete_mdjm_events'        => false,
-					'delete_others_mdjm_events' => false,
-					'read_private_mdjm_events'  => false,
-				);
-				break;
-
-			case 'mdjm_event_read_own':
-				$caps = array(
-					'mdjm_event_read'           => false,
-					'mdjm_event_read_own'       => true,
-					'mdjm_event_edit'           => false,
-					'mdjm_event_edit_own'       => false,
-					'publish_mdjm_events'       => false,
-					'edit_mdjm_events'          => true,
-					'edit_others_mdjm_events'   => true,
-					'delete_mdjm_events'        => false,
-					'delete_others_mdjm_events' => false,
-					'read_private_mdjm_events'  => true,
-				);
-				break;
-
-			case 'mdjm_event_read':
-				$caps = array(
-					'mdjm_event_read'           => true,
-					'mdjm_event_read_own'       => true,
-					'mdjm_event_edit'           => false,
-					'mdjm_event_edit_own'       => false,
-					'publish_mdjm_events'       => false,
-					'edit_mdjm_events'          => true,
-					'edit_others_mdjm_events'   => true,
-					'delete_mdjm_events'        => false,
-					'delete_others_mdjm_events' => false,
-					'read_private_mdjm_events'  => true,
-				);
-				break;
-
-			case 'mdjm_event_edit_own':
-				$caps = array(
-					'mdjm_event_read'           => false,
-					'mdjm_event_read_own'       => true,
-					'mdjm_event_edit'           => false,
-					'mdjm_event_edit_own'       => true,
-					'publish_mdjm_events'       => true,
-					'edit_mdjm_events'          => true,
-					'edit_others_mdjm_events'   => true,
-					'delete_mdjm_events'        => false,
-					'delete_others_mdjm_events' => false,
-					'read_private_mdjm_events'  => true,
-				);
-				break;
-
-			case 'mdjm_event_edit':
-				$caps = array(
-					'mdjm_event_read'           => true,
-					'mdjm_event_read_own'       => true,
-					'mdjm_event_edit'           => true,
-					'mdjm_event_edit_own'       => true,
-					'publish_mdjm_events'       => true,
-					'edit_mdjm_events'          => true,
-					'edit_others_mdjm_events'   => true,
-					'delete_mdjm_events'        => true,
-					'delete_others_mdjm_events' => true,
-					'read_private_mdjm_events'  => true,
-				);
-				break;
-			/**
-			 * Packages
-			 */
-			case 'mdjm_package_none':
-				$caps = array(
-					'mdjm_package_edit_own'       => true,
-					'mdjm_package_edit'           => false,
-					'publish_mdjm_packages'       => false,
-					'edit_mdjm_packages'          => false,
-					'edit_others_mdjm_packages'   => false,
-					'delete_mdjm_packages'        => false,
-					'delete_others_mdjm_packages' => false,
-					'read_private_mdjm_packages'  => false,
-				);
-				break;
-
-			case 'mdjm_package_edit_own':
-				$caps = array(
-					'mdjm_package_edit_own'       => true,
-					'mdjm_package_edit'           => false,
-					'publish_mdjm_packages'       => true,
-					'edit_mdjm_packages'          => true,
-					'edit_others_mdjm_packages'   => false,
-					'delete_mdjm_packages'        => false,
-					'delete_others_mdjm_packages' => false,
-					'read_private_mdjm_packages'  => false,
-				);
-				break;
-
-			case 'mdjm_package_edit':
-				$caps = array(
-					'mdjm_package_edit_own'       => true,
-					'mdjm_package_edit'           => true,
-					'publish_mdjm_packages'       => true,
-					'edit_mdjm_packages'          => true,
-					'edit_others_mdjm_packages'   => true,
-					'delete_mdjm_packages'        => true,
-					'delete_others_mdjm_packages' => true,
-					'read_private_mdjm_packages'  => true,
-				);
-				break;
-			/**
-			 * Quotes
-			 */
-			case 'mdjm_quote_none':
-				$caps = array(
-					'mdjm_quote_view_own'          => false,
-					'mdjm_quote_view'              => false,
-					'edit_mdjm_quotes'             => false,
-					'edit_others_mdjm_quotes'      => false,
-					'publish_mdjm_quotes'          => false,
-					'read_private_mdjm_quotes'     => false,
-					'edit_published_mdjm_quotes'   => false,
-					'edit_private_mdjm_quotes'     => false,
-					'delete_mdjm_quotes'           => false,
-					'delete_others_mdjm_quotes'    => false,
-					'delete_private_mdjm_quotes'   => false,
-					'delete_published_mdjm_quotes' => false,
-				);
-				break;
-
-			case 'mdjm_quote_view_own':
-				$caps = array(
-					'mdjm_quote_view_own'          => true,
-					'mdjm_quote_view'              => false,
-					'edit_mdjm_quotes'             => true,
-					'edit_others_mdjm_quotes'      => false,
-					'publish_mdjm_quotes'          => false,
-					'read_private_mdjm_quotes'     => false,
-					'edit_published_mdjm_quotes'   => false,
-					'edit_private_mdjm_quotes'     => false,
-					'delete_mdjm_quotes'           => false,
-					'delete_others_mdjm_quotes'    => false,
-					'delete_private_mdjm_quotes'   => false,
-					'delete_published_mdjm_quotes' => false,
-				);
-				break;
-
-			case 'mdjm_quote_view':
-				$caps = array(
-					'mdjm_quote_view_own'          => true,
-					'mdjm_quote_view'              => true,
-					'edit_mdjm_quotes'             => true,
-					'edit_others_mdjm_quotes'      => true,
-					'publish_mdjm_quotes'          => true,
-					'read_private_mdjm_quotes'     => true,
-					'edit_published_mdjm_quotes'   => true,
-					'edit_private_mdjm_quotes'     => true,
-					'delete_mdjm_quotes'           => true,
-					'delete_others_mdjm_quotes'    => true,
-					'delete_private_mdjm_quotes'   => true,
-					'delete_published_mdjm_quotes' => true,
-				);
-				break;
-			/**
-			 * Reports
-			 */
-			case 'mdjm_reports_none':
-				$caps = array(
-					'view_event_reports' => false,
-				);
-				break;
-			case 'mdjm_reports_run':
-				$caps = array(
-					'view_event_reports' => true,
-				);
-				break;
-			/**
-			 * Templates
-			 */
-			case 'mdjm_template_none':
-				$caps = array(
-					'mdjm_template_edit'              => false,
-					'edit_mdjm_templates'             => false,
-					'edit_others_mdjm_templates'      => false,
-					'publish_mdjm_templates'          => false,
-					'read_private_mdjm_templates'     => false,
-					'edit_published_mdjm_templates'   => false,
-					'edit_private_mdjm_templates'     => false,
-					'delete_mdjm_templates'           => false,
-					'delete_others_mdjm_templates'    => false,
-					'delete_private_mdjm_templates'   => false,
-					'delete_published_mdjm_templates' => false,
-				);
-				break;
-
-			case 'mdjm_template_edit':
-				$caps = array(
-					'mdjm_template_edit'              => true,
-					'edit_mdjm_templates'             => true,
-					'edit_others_mdjm_templates'      => true,
-					'publish_mdjm_templates'          => true,
-					'read_private_mdjm_templates'     => true,
-					'edit_published_mdjm_templates'   => true,
-					'edit_private_mdjm_templates'     => true,
-					'delete_mdjm_templates'           => true,
-					'delete_others_mdjm_templates'    => true,
-					'delete_private_mdjm_templates'   => true,
-					'delete_published_mdjm_templates' => true,
-				);
-				break;
-			/**
-			 * Transactions
-			 */
-			case 'mdjm_txn_none':
-				$caps = array(
-					'mdjm_txn_edit'              => false,
-					'edit_mdjm_txns'             => false,
-					'edit_others_mdjm_txns'      => false,
-					'publish_mdjm_txns'          => false,
-					'read_private_mdjm_txns'     => false,
-					'edit_published_mdjm_txns'   => false,
-					'edit_private_mdjm_txns'     => false,
-					'delete_mdjm_txns'           => false,
-					'delete_others_mdjm_txns'    => false,
-					'delete_private_mdjm_txns'   => false,
-					'delete_published_mdjm_txns' => false,
-				);
-				break;
-
-			case 'mdjm_txn_edit':
-				$caps = array(
-					'mdjm_txn_edit'              => true,
-					'edit_mdjm_txns'             => true,
-					'edit_others_mdjm_txns'      => true,
-					'publish_mdjm_txns'          => true,
-					'read_private_mdjm_txns'     => true,
-					'edit_published_mdjm_txns'   => true,
-					'edit_private_mdjm_txns'     => true,
-					'delete_mdjm_txns'           => true,
-					'delete_others_mdjm_txns'    => true,
-					'delete_private_mdjm_txns'   => true,
-					'delete_published_mdjm_txns' => true,
-				);
-				break;
-			/**
-			 * Venues
-			 */
-			case 'mdjm_venue_none':
-				$caps = array(
-					'mdjm_venue_read'              => false,
-					'mdjm_venue_edit'              => false,
-					'edit_mdjm_venues'             => false,
-					'edit_others_mdjm_venues'      => false,
-					'publish_mdjm_venues'          => false,
-					'read_private_mdjm_venues'     => false,
-					'edit_published_mdjm_venues'   => false,
-					'edit_private_mdjm_venues'     => false,
-					'delete_mdjm_venues'           => false,
-					'delete_others_mdjm_venues'    => false,
-					'delete_private_mdjm_venues'   => false,
-					'delete_published_mdjm_venues' => false,
-				);
-				break;
-
-			case 'mdjm_venue_read':
-				$caps = array(
-					'mdjm_venue_read'              => true,
-					'mdjm_venue_edit'              => false,
-					'edit_mdjm_venues'             => true,
-					'edit_others_mdjm_venues'      => true,
-					'publish_mdjm_venues'          => false,
-					'read_private_mdjm_venues'     => true,
-					'edit_published_mdjm_venues'   => true,
-					'edit_private_mdjm_venues'     => true,
-					'delete_mdjm_venues'           => false,
-					'delete_others_mdjm_venues'    => false,
-					'delete_private_mdjm_venues'   => false,
-					'delete_published_mdjm_venues' => false,
-				);
-				break;
-
-			case 'mdjm_venue_edit':
-				$caps = array(
-					'mdjm_venue_read'              => true,
-					'mdjm_venue_edit'              => true,
-					'edit_mdjm_venues'             => true,
-					'edit_others_mdjm_venues'      => true,
-					'publish_mdjm_venues'          => true,
-					'read_private_mdjm_venues'     => true,
-					'edit_published_mdjm_venues'   => true,
-					'edit_private_mdjm_venues'     => true,
-					'delete_mdjm_venues'           => true,
-					'delete_others_mdjm_venues'    => true,
-					'delete_private_mdjm_venues'   => true,
-					'delete_published_mdjm_venues' => true,
-				);
-				break;
-
-			default:
-				return false;
-				break;
-
-		}
-
-		return ! empty( $caps ) ? $caps : false;
-	} // get_capabilities
-
-	/**
-	 * Determine if the currently logged in employee user has the relevant permissions to perform the action/view the page
-	 *
-	 * @since   1.3
-	 * @param   str $action         Required: The action being performed
-	 * @param   int $user_id        Optional: The ID of the user to query. Default current user
-	 * @return  bool    $granted        true|false
-	 */
-	public function employee_can( $action, $user_id = '' ) {
-		if ( empty( $user_id ) ) {
-			$user = wp_get_current_user();
-		} else {
-			$user = get_user_by( 'id', $user_id );
-		}
-
-		// MDJM Admins can do everything
-		if ( mdjm_is_admin( $user->ID ) ) {
-			return true;
-		}
-
-		// Non employees can't do anything
-		if ( ! mdjm_is_employee( $user->ID ) ) {
-			return false;
-		}
-
-		switch ( $action ) {
-
-			case 'view_clients_list':
-				$allowed_roles = array( 'mdjm_client_edit', 'mdjm_client_edit_own' );
-				break;
-
-			case 'list_all_clients':
-				$allowed_roles = array( 'mdjm_client_edit' );
-				break;
-
-			case 'manage_employees':
-				$allowed_roles = array( 'mdjm_employee_edit' );
-				break;
-
-			case 'read_events':
-				$allowed_roles = array( 'mdjm_event_read', 'mdjm_event_read_own', 'mdjm_event_edit', 'mdjm_event_edit_own' );
-				break;
-
-			case 'read_events_all':
-				$allowed_roles = array( 'mdjm_event_read', 'mdjm_event_edit' );
-				break;
-
-			case 'manage_events':
-				$allowed_roles = array( 'mdjm_event_edit', 'mdjm_event_edit_own' );
-				break;
-
-			case 'manage_all_events':
-				$allowed_roles = array( 'mdjm_event_edit' );
-				break;
-
-			case 'manage_packages':
-				$allowed_roles = array( 'mdjm_package_edit', 'mdjm_package_edit_own' );
-				break;
-
-			case 'manage_templates':
-				$allowed_roles = array( 'mdjm_template_edit' );
-				break;
-
-			case 'edit_txns':
-				$allowed_roles = array( 'mdjm_txn_edit' );
-				break;
-
-			case 'list_all_quotes':
-				$allowed_roles = array( 'mdjm_quote_view' );
-				break;
-
-			case 'list_own_quotes':
-				$allowed_roles = array( 'mdjm_quote_view_own', 'mdjm_quote_view' );
-				break;
-
-			case 'list_venues':
-				$allowed_roles = array( 'mdjm_venue_read', 'mdjm_venue_edit' );
-				break;
-
-			case 'add_venues':
-				$allowed_roles = array( 'mdjm_venue_edit' );
-				break;
-
-			case 'send_comms':
-				$allowed_roles = array( 'mdjm_comms_send' );
-				break;
-
-			case 'run_reports':
-				$allowed_roles = array( 'view_event_reports' );
-			default:
-				return false;
-				break;
-		} // switch
-
-		if ( empty( $allowed_roles ) ) {
-			return false;
-		}
-
-		foreach ( $allowed_roles as $allowed ) {
-			if ( user_can( $user->ID, $allowed ) ) {
-				return true;
-			}
-		}
-
-		return false;
-	} // employee_can
-
-	/**
-	 * Make the current role a full admin
-	 *
-	 * @since   1.3
-	 * @param   int|str $where      The user ID or role name to which the permission
-	 *
-	 * @return  void
-	 */
-	public function make_admin( $where, $remove = false ) {
-
-		$caps = array(
-			// MDJM Admin
-			'manage_mdjm'                     => true,
-
-			// Clients
-			'mdjm_client_edit'                => true,
-			'mdjm_client_edit_own'            => true,
-
-			// Employees
-			'mdjm_employee_edit'              => true,
-
-			// Packages
-			'mdjm_package_edit_own'           => true,
-			'mdjm_package_edit'               => true,
-			'publish_mdjm_packages'           => true,
-			'edit_mdjm_packages'              => true,
-			'edit_others_mdjm_packages'       => true,
-			'delete_mdjm_packages'            => true,
-			'delete_others_mdjm_packages'     => true,
-			'read_private_mdjm_packages'      => true,
-
-			// Comm posts
-			'mdjm_comms_send'                 => true,
-			'edit_mdjm_comms'                 => true,
-			'edit_others_mdjm_comms'          => true,
-			'publish_mdjm_comms'              => true,
-			'read_private_mdjm_comms'         => true,
-			'edit_published_mdjm_comms'       => true,
-			'delete_mdjm_comms'               => true,
-			'delete_others_mdjm_comms'        => true,
-			'delete_private_mdjm_comms'       => true,
-			'delete_published_mdjm_comms'     => true,
-			'edit_private_mdjm_comms'         => true,
-
-			// Event posts
-			'mdjm_event_read'                 => true,
-			'mdjm_event_read_own'             => true,
-			'mdjm_event_edit'                 => true,
-			'mdjm_event_edit_own'             => true,
-			'publish_mdjm_events'             => true,
-			'edit_mdjm_events'                => true,
-			'edit_others_mdjm_events'         => true,
-			'delete_mdjm_events'              => true,
-			'delete_others_mdjm_events'       => true,
-			'read_private_mdjm_events'        => true,
-
-			// Quote posts
-			'mdjm_quote_view_own'             => true,
-			'mdjm_quote_view'                 => true,
-			'edit_mdjm_quotes'                => true,
-			'edit_others_mdjm_quotes'         => true,
-			'publish_mdjm_quotes'             => true,
-			'read_private_mdjm_quotes'        => true,
-			'edit_published_mdjm_quotes'      => true,
-			'edit_private_mdjm_quotes'        => true,
-			'delete_mdjm_quotes'              => true,
-			'delete_others_mdjm_quotes'       => true,
-			'delete_private_mdjm_quotes'      => true,
-			'delete_published_mdjm_quotes'    => true,
-
-			// Reports
-			'view_event_reports'              => true,
-
-			// Templates
-			'mdjm_template_edit'              => true,
-			'edit_mdjm_templates'             => true,
-			'edit_others_mdjm_templates'      => true,
-			'publish_mdjm_templates'          => true,
-			'read_private_mdjm_templates'     => true,
-			'edit_published_mdjm_templates'   => true,
-			'edit_private_mdjm_templates'     => true,
-			'delete_mdjm_templates'           => true,
-			'delete_others_mdjm_templates'    => true,
-			'delete_private_mdjm_templates'   => true,
-			'delete_published_mdjm_templates' => true,
-
-			// Transaction posts
-			'mdjm_txn_edit'                   => true,
-			'edit_mdjm_txns'                  => true,
-			'edit_others_mdjm_txns'           => true,
-			'publish_mdjm_txns'               => true,
-			'read_private_mdjm_txns'          => true,
-			'edit_published_mdjm_txns'        => true,
-			'edit_private_mdjm_txns'          => true,
-			'delete_mdjm_txns'                => true,
-			'delete_others_mdjm_txns'         => true,
-			'delete_private_mdjm_txns'        => true,
-			'delete_published_mdjm_txns'      => true,
-
-			// Venue posts
-			'mdjm_venue_read'                 => true,
-			'mdjm_venue_edit'                 => true,
-			'edit_mdjm_venues'                => true,
-			'edit_others_mdjm_venues'         => true,
-			'publish_mdjm_venues'             => true,
-			'read_private_mdjm_venues'        => true,
-			'edit_published_mdjm_venues'      => true,
-			'edit_private_mdjm_venues'        => true,
-			'delete_mdjm_venues'              => true,
-			'delete_others_mdjm_venues'       => true,
-			'delete_private_mdjm_venues'      => true,
-			'delete_published_mdjm_venues'    => true,
-		);
-
-		$role = ( is_numeric( $where ) ? new WP_User( $where ) : get_role( $where ) );
-
-		// Fire a filter to enable default capabilities to be manipulated
-		$caps = apply_filters( 'mdjm_all_caps', $caps );
-
-		foreach ( $caps as $cap => $set ) {
-
-			if ( ! empty( $remove ) ) {
-				$role->remove_cap( $cap );
-			} elseif ( ! empty( $set ) ) {
-				$role->add_cap( $cap );
-			} else {
-				$role->remove_cap( $cap );
-			}
-		}
-
-	} // make_admin
-
-} // MDJM_Permissions
+<?php
+/**
+ * @author: Mike Howard, Jack Mawhinney, Dan Porter
+ */
+	defined( 'ABSPATH' ) or die( 'Direct access to this page is disabled!!!' );
+
+/**
+ * Class Name: MDJM_Permissions
+ * Manage User permissions within MDJM
+ */
+class MDJM_Permissions {
+	/**
+	 * Class constructor
+	 */
+	public function __construct() {
+		// Capture form submissions
+		add_action( 'init', array( &$this, 'init' ) );
+	}
+
+	/**
+	 * Hook into the init action to process form submissions
+	 */
+	public function init() {
+		if ( isset( $_POST['set-permissions'], $_POST['mdjm_set_permissions'] ) ) {
+			$this->set_permissions();
+		}
+	} // init
+
+	/**
+	 * Set permissions for the given roles
+	 *
+	 * @since   1.3
+	 * @param
+	 * @return
+	 */
+	public function set_permissions() {
+
+		if ( ! isset( $_POST['employee_roles'] ) ) {
+			return;
+		}
+
+		$fields = array(
+			'comm_permissions'     => 'mdjm_comms',
+			'client_permissions'   => 'mdjm_client',
+			'employee_permissions' => 'mdjm_employee',
+			'event_permissions'    => 'mdjm_event',
+			'package_permissions'  => 'mdjm_package',
+			'quote_permissions'    => 'mdjm_quote',
+			'report_permissions'   => 'mdjm_reports',
+			'template_permissions' => 'mdjm_template',
+			'txn_permissions'      => 'mdjm_txn',
+			'venue_permissions'    => 'mdjm_venue',
+		);
+
+		foreach ( wp_unslash( $_POST['employee_roles'] ) as $_role ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+			$role = get_role( $_role );
+
+			// If the role is to become admin
+			if ( ! empty( $_POST[ 'manage_mdjm_' . $_role ] ) ) {
+
+				$role->add_cap( 'manage_mdjm' );
+				$this->make_admin( $_role );
+				continue;
+
+			} else {
+				$role->remove_cap( 'manage_mdjm' );
+			}
+
+			// Every role has the MDJM Employee capability
+			$role->add_cap( 'mdjm_employee' );
+
+			foreach ( $fields as $field => $prefix ) {
+
+				$caps = empty( $_POST[ $field . '_' . $_role ] ) ?
+					$this->get_capabilities( $prefix . '_none' ) :
+					$this->get_capabilities( wp_unslash( $_POST[ $field . '_' . $_role ] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+
+				foreach ( $caps as $cap => $val ) {
+
+					if ( empty( $val ) ) {
+						$role->remove_cap( $cap );
+					} else {
+						$role->add_cap( $cap );
+					}
+				}
+			}
+		}
+
+		wp_safe_redirect( wp_unslash( $_SERVER['HTTP_REFERER'] ) . '&role_action=1&message=4' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
+		exit;
+
+	} // set_permissions
+
+	/**
+	 * Defines all of the required capabilities for the requested capability.
+	 *
+	 * @since   1.3
+	 * @param   str $cap        The capability being requested
+	 * @return  arr     $caps       The required capabilities that the requested capability needs or false if they cannot be calculated
+	 */
+	public function get_capabilities( $cap ) {
+
+		switch ( $cap ) {
+			/**
+			 * Clients
+			 */
+			case 'mdjm_client_none':
+				$caps = array(
+					'mdjm_client_edit'     => false,
+					'mdjm_client_edit_own' => false,
+				);
+				break;
+
+			case 'mdjm_client_edit_own':
+				$caps = array(
+					'mdjm_client_edit'     => false,
+					'mdjm_client_edit_own' => true,
+				);
+				break;
+
+			case 'mdjm_client_edit':
+				$caps = array(
+					'mdjm_client_edit'     => true,
+					'mdjm_client_edit_own' => true,
+				);
+				break;
+			/**
+			 * Communications
+			 */
+			case 'mdjm_comms_none':
+				$caps = array(
+					'mdjm_comms_send'             => false,
+					'edit_mdjm_comms'             => false,
+					'edit_others_mdjm_comms'      => false,
+					'publish_mdjm_comms'          => false,
+					'read_private_mdjm_comms'     => false,
+					'edit_published_mdjm_comms'   => false,
+					'delete_mdjm_comms'           => false,
+					'delete_others_mdjm_comms'    => false,
+					'delete_private_mdjm_comms'   => false,
+					'delete_published_mdjm_comms' => false,
+					'edit_private_mdjm_comms'     => false,
+				);
+				break;
+
+			case 'mdjm_comms_send':
+				$caps = array(
+					'mdjm_comms_send'             => true,
+					'edit_mdjm_comms'             => true,
+					'edit_others_mdjm_comms'      => true,
+					'publish_mdjm_comms'          => true,
+					'read_private_mdjm_comms'     => true,
+					'edit_published_mdjm_comms'   => true,
+					'delete_mdjm_comms'           => true,
+					'delete_others_mdjm_comms'    => true,
+					'delete_private_mdjm_comms'   => true,
+					'delete_published_mdjm_comms' => true,
+					'edit_private_mdjm_comms'     => true,
+				);
+				break;
+
+			/**
+			 * Employees
+			 */
+			case 'mdjm_employee_none':
+				$caps = array( 'mdjm_employee_edit' => false );
+				break;
+
+			case 'mdjm_employee_edit':
+				$caps = array( 'mdjm_employee

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-7537
# Blocks arbitrary file upload via mdjm_email_upload_file parameter
# Targets the specific admin-post.php action used by the vulnerable plugin
SecRule REQUEST_URI "@streq /wp-admin/admin-post.php" 
    "id:20267537,phase:2,deny,status:403,chain, 
    msg:'CVE-2026-7537 - MDJM Event Management Arbitrary File Upload Attempt', 
    severity:'CRITICAL',tag:'CVE-2026-7537',tag:'WordPress',tag:'FileUpload'"
    SecRule ARGS_POST:action "@streq send_comm_email" "chain"
        SecRule FILES:mdjm_email_upload_file "@rx .(php|phtml|php3|php4|php5|php7|pht|psd|shtml|phar|cgi|pl|py|jsp|asp|aspx|exe|dll|com|bat|cmd|msi|scr)$" 
            "t:lowercase, 
            msg:'CVE-2026-7537 - Attempt to upload executable file via MDJM comms feature', 
            severity:'CRITICAL',tag:'CVE-2026-7537'"

Frequently Asked Questions

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
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School