<?php
/**
* Main submissions form
*
* @category Submissions
* @package My Calendar Pro
* @author Joe Dolson
* @license GPLv2 or later
* @link https://www.joedolson.com/my-calendar-pro/
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
add_shortcode( 'submit_event', 'mcs_submit_form' );
/**
* Create the submissions form via shortcode
*
* @param array $atts Shortcode attributes.
* @param string $content Wrapped shortcode content.
*
* @return submit form
*/
function mcs_submit_form( $atts, $content = '' ) {
$args = shortcode_atts(
array(
'fields' => 'submitter,event_title,event_date',
'categories' => '1',
'locations' => 'either',
'category' => '1', // default category value.
'location' => '0', // default location value.
'location_fields' => 'event_label,street,street2,phone,city,state,postcode,country,url',
'form_id' => '',
'is_widget' => '0',
),
$atts,
'submit_event'
);
// Convert this shortcode into a form ID.
$args = mcs_convert_args( $args );
$form_id = $args['form_id'];
$forms = get_option( 'mcs_forms', array() );
$loc = false;
$cat_field = array();
if ( '' !== $form_id && isset( $forms[ $form_id ] ) ) {
$settings = $forms[ $form_id ];
$fld = $settings['fields'];
$categories = isset( $settings['categories'] ) ? $settings['categories'] : 1;
$locations = isset( $settings['locations'] ) ? $settings['locations'] : 'either';
$category = isset( $settings['category'] ) ? $settings['category'] : '1';
$location = isset( $settings['location'] ) ? $settings['location'] : '0';
$loc = isset( $settings['location_fields'] ) ? $settings['location_fields'] : false;
$cat_field = isset( $settings['category_field'] ) ? $settings['category_field'] : $cat_field;
} else {
$categories = $args['categories'];
$locations = $args['locations'];
$category = $args['category'];
$location = $args['location'];
$fld = mcs_create_field_array( $args['fields'] );
}
$is_widget = isset( $args['is_widget'] ) ? 1 : 0;
if ( ! $loc && isset( $args['location_fields'] ) ) {
$loc = mcs_create_field_array( $args['location_fields'] );
}
if ( mcs_user_can_submit_events() ) {
$args = array(
'fields' => $fld,
'categories' => $categories,
'locations' => $locations,
'category' => $category,
'location' => $location,
'location_fields' => $loc,
'form_id' => $form_id,
'category_field' => $cat_field,
'is_widget' => $is_widget,
);
return mcs_build_submit_form( $args );
} else {
return ( '' === $content ) ? mcs_submission_not_available() : $content;
}
}
/**
* Convert old shortcode arguments into a form ID.
*
* @param array $args Array of arguments used to build forms.
*
* @return array
*/
function mcs_convert_args( $args ) {
if ( isset( $args['form_id'] ) && '' !== $args['form_id'] ) {
$form_id = $args['form_id'];
} else {
$form_id = md5( implode( ',', $args ) );
}
$is_widget = isset( $args['is_widget'] ) && '0' !== $args['is_widget'] ? true : false;
$forms = get_option( 'mcs_forms' );
if ( isset( $forms[ $form_id ] ) ) {
$return = array(
'form_id' => $form_id,
);
if ( $is_widget ) {
$return['is_widget'] = 1;
}
} else {
$form_fields = isset( $args['fields'] ) ? $args['fields'] : array();
$location_fields = isset( $args['location_fields'] ) ? $args['location_fields'] : array();
$category_field = array( 'entry_type' => 'single' );
$return = array(
'fields' => mcs_create_field_array( $form_fields ),
'category' => isset( $args['category'] ) ? $args['category'] : '',
'categories' => isset( $args['categories'] ) ? $args['categories'] : '',
'category_field' => $category_field,
'locations' => isset( $args['locations'] ) ? $args['locations'] : '',
'location' => isset( $args['location'] ) ? $args['location'] : '',
'location_fields' => mcs_create_field_array( $location_fields ),
'form_id' => $form_id,
);
if ( $is_widget ) {
$return['is_widget'] = 1;
}
$forms[ $form_id ] = $return;
update_option( 'mcs_forms', $forms );
}
return $return;
}
/**
* Verify data from hidden input fields, that shouldn't be changed. Checks that the hidden fields present at form render match the values submitted.
*
* @param array $post Array of data sent from form.
* @param string $form_id ID of current form.
*
* @return bool
*/
function mcs_verify_hidden_inputs( $post, $form_id ) {
$fields = mcs_hidden_input_fields( $form_id );
$test = array();
foreach ( $fields as $key => $value ) {
$test[ $key ] = ( isset( $post[ $key ] ) ) ? (string) $post[ $key ] : '';
if ( ! isset( $post[ $key ] ) || '' === $post[ $key ] ) {
unset( $test[ $key ] );
}
}
// The timestamp will always change between initial state and submission, so it's not a useful test here.
unset( $test['mcs_form_timestamp'] );
$cookie = ( isset( $_COOKIE['mcs_unique_id'] ) ) ? sanitize_text_field( wp_unslash( $_COOKIE['mcs_unique_id'] ) ) : false;
$fields = ( $cookie ) ? get_transient( 'mcs_hidden_fields_' . $cookie ) : false;
// If the transient doesn't exist, that's a DB problem, not a user problem. Don't block submissions if the transient isn't available.
if ( $test === $fields || ! $fields ) {
return true;
}
return false;
}
add_action( 'init', 'mcs_run_processor' );
/**
* Process submission before page executes.
*/
function mcs_run_processor() {
if ( ! is_admin() && function_exists( 'mc_kses_post' ) ) {
// Nonce verification happens in `mcs_processor()`.
$post = map_deep( wp_unslash( $_POST ), 'mc_kses_post' );
$response = mcs_processor( $post );
$unique_id = isset( $_COOKIE['mcs_unique_id'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['mcs_unique_id'] ) ) : false;
if ( false !== $response ) {
set_transient( 'mcs_' . $unique_id, $response, 10 );
/**
* The form submission has been processed and transient storage created.
*
* @hook mcs_run_processor_completed
*
* @param {string} $unique_id Unique string tracking this submission.
* @param {array} $response Server response after submission.
*/
do_action( 'mcs_run_processor_completed', $unique_id, $response );
} else {
delete_transient( 'mcs_' . $unique_id );
delete_transient( 'mcs_hidden_fields_' . $unique_id );
}
}
}
/**
* Get stored response from last request
*/
function mcs_processor_response() {
$unique_id = isset( $_COOKIE['mcs_unique_id'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['mcs_unique_id'] ) ) : false;
$response = get_transient( 'mcs_' . $unique_id );
if ( '' !== $response ) {
return $response;
}
}
/**
* Set up arguments and JS to configure Duet date picker.
*/
function mcs_setup_duetjs() {
/**
* Disable the datepicker. Falls back to a standard browser date input.
*
* @hook mcs_datepicker_enabled
*
* @param {bool} $enabled True if enabled, false to disable.
*
* @return {bool}
*/
if ( false === apply_filters( 'mcs_datepicker_enabled', true ) ) {
return;
}
global $mcs_version;
if ( SCRIPT_DEBUG && true === SCRIPT_DEBUG ) {
$mcs_version = $mcs_version . uniqid();
}
wp_enqueue_script( 'duet.js' );
wp_enqueue_style( 'duet.css' );
wp_enqueue_script( 'mcs.duet' );
wp_localize_script(
'mcs.duet',
'duetFormats',
array(
'date' => get_option( 'mcs_date_format' ),
)
);
wp_localize_script(
'mcs.duet',
'duetLocalization',
array(
'buttonLabel' => __( 'Choose date', 'my-calendar-pro' ),
'placeholder' => mcs_parse_date_format(),
'selectedDateMessage' => __( 'Selected date is', 'my-calendar-pro' ),
'prevMonthLabel' => __( 'Previous month', 'my-calendar-pro' ),
'nextMonthLabel' => __( 'Next month', 'my-calendar-pro' ),
'monthSelectLabel' => __( 'Month', 'my-calendar-pro' ),
'yearSelectLabel' => __( 'Year', 'my-calendar-pro' ),
'closeLabel' => __( 'Close window', 'my-calendar-pro' ),
'keyboardInstruction' => __( 'You can use arrow keys to navigate dates', 'my-calendar-pro' ),
'calendarHeading' => __( 'Choose a date', 'my-calendar-pro' ),
'dayNames' => array(
date_i18n( 'D', strtotime( 'Sunday' ) ),
date_i18n( 'D', strtotime( 'Monday' ) ),
date_i18n( 'D', strtotime( 'Tuesday' ) ),
date_i18n( 'D', strtotime( 'Wednesday' ) ),
date_i18n( 'D', strtotime( 'Thursday' ) ),
date_i18n( 'D', strtotime( 'Friday' ) ),
date_i18n( 'D', strtotime( 'Saturday' ) ),
),
'monthNames' => array(
date_i18n( 'F', strtotime( 'January 1' ) ),
date_i18n( 'F', strtotime( 'February 1' ) ),
date_i18n( 'F', strtotime( 'March 1' ) ),
date_i18n( 'F', strtotime( 'April 1' ) ),
date_i18n( 'F', strtotime( 'May 1' ) ),
date_i18n( 'F', strtotime( 'June 1' ) ),
date_i18n( 'F', strtotime( 'July 1' ) ),
date_i18n( 'F', strtotime( 'August 1' ) ),
date_i18n( 'F', strtotime( 'September 1' ) ),
date_i18n( 'F', strtotime( 'October 1' ) ),
date_i18n( 'F', strtotime( 'November 1' ) ),
date_i18n( 'F', strtotime( 'December 1' ) ),
),
'monthNamesShort' => array(
date_i18n( 'M', strtotime( 'January 1' ) ),
date_i18n( 'M', strtotime( 'February 1' ) ),
date_i18n( 'M', strtotime( 'March 1' ) ),
date_i18n( 'M', strtotime( 'April 1' ) ),
date_i18n( 'M', strtotime( 'May 1' ) ),
date_i18n( 'M', strtotime( 'June 1' ) ),
date_i18n( 'M', strtotime( 'July 1' ) ),
date_i18n( 'M', strtotime( 'August 1' ) ),
date_i18n( 'M', strtotime( 'September 1' ) ),
date_i18n( 'M', strtotime( 'October 1' ) ),
date_i18n( 'M', strtotime( 'November 1' ) ),
date_i18n( 'M', strtotime( 'December 1' ) ),
),
'locale' => str_replace( '_', '-', get_locale() ),
)
);
}
/**
* Search location titles.
*
* @param string $query Location query.
*
* @return array locations
*/
function mcs_search_locations( $query = '' ) {
global $wpdb;
$search = '';
$db_type = mc_get_db_type();
$query = esc_sql( $query );
if ( '' !== $query ) {
// Fulltext is supported in InnoDB since MySQL 5.6; minimum required by WP is 5.0 as of WP 5.5.
// 37% of installs still below 5.6 as of 11/30/2020.
// InnoDB requires a fulltext index to be added for support.
/**
* Filter fields used to search locations when using MATCH queries on MyISAM dbs.
*
* @hook mcs_location_search_fields
*
* @param {string} $fields Comma separated list of database columns. Default `location_label`.
* @param {string} $query Search query.
*
* @return {string}
*/
$search_fields = apply_filters( 'mcs_location_search_fields', 'location_label', $query );
if ( 'MyISAM' === $db_type ) {
$search = ' WHERE MATCH(' . $search_fields . ") AGAINST ( '$query' IN BOOLEAN MODE ) ";
} else {
$search = " WHERE location_label LIKE '%$query%' ";
}
} else {
$search = '';
}
$locations = $wpdb->get_results( 'SELECT location_id, location_label FROM ' . my_calendar_locations_table() . " $search ORDER BY location_label ASC" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
return $locations;
}
/**
* Get information about location.
*/
function mcs_autocomplete_search_locations() {
if ( isset( $_REQUEST['action'] ) && 'mcs_autocomplete_search_locations' === $_REQUEST['action'] ) {
$security = $_REQUEST['security'];
if ( ! wp_verify_nonce( $security, 'mc-search-locations' ) ) {
wp_send_json(
array(
'success' => 0,
'response' => array( 'error' => 'Invalid security value.' ),
)
);
}
$query = $_REQUEST['data'];
$locations = mcs_search_locations( $query );
$response = array();
foreach ( $locations as $location ) {
$response[] = array(
'location_id' => $location->location_id,
'location_label' => html_entity_decode( wp_strip_all_tags( $location->location_label ) ),
);
}
wp_send_json(
array(
'success' => 1,
'response' => $response,
)
);
}
}
add_action( 'wp_ajax_mcs_autocomplete_search_locations', 'mcs_autocomplete_search_locations' );
add_action( 'wp_ajax_nopriv_mcs_autocomplete_search_locations', 'mcs_autocomplete_search_locations' );
/**
* Register scripts and styles.
*/
function mcs_register_scripts() {
global $mcs_version;
$submit_url = plugins_url( 'js/jquery.mcs-submit.min.js', __FILE__ );
$datepicker_js = plugins_url( 'js/mcs-datepicker.min.js', __FILE__ );
if ( SCRIPT_DEBUG && true === SCRIPT_DEBUG ) {
$mcs_version = $mcs_version . uniqid();
$submit_url = plugins_url( 'js/jquery.mcs-submit.js', __FILE__ );
$datepicker_js = plugins_url( 'js/mcs-datepicker.js', __FILE__ );
}
wp_register_script( 'mcs.submit', $submit_url, array( 'jquery' ), $mcs_version, true );
wp_register_script( 'mcs.charcount', plugins_url( '/js/jquery.charcount.js', __FILE__ ), array( 'jquery' ), $mcs_version, true );
if ( ! is_admin() ) {
// My Calendar Pro needs this on the front-end; My Calendar already has it in the admin.
if ( SCRIPT_DEBUG ) {
wp_register_script( 'accessible-autocomplete', plugins_url( '/js/accessible-autocomplete.js', __FILE__ ), array(), $mcs_version );
} else {
wp_register_script( 'accessible-autocomplete', plugins_url( '/js/accessible-autocomplete.min.js', __FILE__ ), array(), $mcs_version );
}
}
wp_register_script( 'mcs-autocomplete', plugins_url( '/js/locations-autocomplete.js', __FILE__ ), array( 'accessible-autocomplete' ), $mcs_version, true );
wp_register_script( 'duet.js', plugins_url( 'js/duet/duet.js', __FILE__ ), array(), $mcs_version, true );
wp_register_style( 'duet.css', plugins_url( 'js/duet/themes/default.css', __FILE__ ), array(), $mcs_version );
wp_register_script( 'mcs.duet', $datepicker_js, array( 'duet.js' ), $mcs_version );
}
add_action( 'wp_enqueue_scripts', 'mcs_register_scripts' );
add_action( 'admin_enqueue_scripts', 'mcs_register_scripts' );
/**
* Build the submission form from an array of data specifying what should be in it.
*
* @param array $args {
* Array of data used to build submit form.
*
* @type array $fields Fields to include in the form.
* @type int $categories Included or not included.
* @type int $locations Included or not included.
* @type int $category Default category.
* @type int $location Default location.
* @type array $location_fields Array of location fields to include.
* @type string $form_id Unique ID for this form.
* @type bool $is_widget Whether this form is generated from a widget.
* }
*
* @return string
*/
function mcs_build_submit_form( $args ) {
$form_id = ( isset( $args['form_id'] ) && '' !== $args['form_id'] ) ? $args['form_id'] : md5( get_the_permalink() . implode( '-', $args['fields'] ) );
$is_widget = ( isset( $args['is_widget'] ) ) ? $args['is_widget'] : false;
global $mcs_version;
if ( SCRIPT_DEBUG && true === SCRIPT_DEBUG ) {
$mcs_version = $mcs_version . '-' . uniqid();
}
$args['form_id'] = $form_id;
$args['is_widget'] = $is_widget;
/**
* Filter the event fields enabled for a form.
*
* @hook mcs_submit_fields
*
* @param {array} $fields Array of fields as defined by shortcode or ID.
* @param {string} $form_id Form ID.
*
* @return {array}
*/
$args['fields'] = apply_filters( 'mcs_submit_fields', $args['fields'], $form_id );
/**
* Filter the location fields enabled for a form.
*
* @hook mcs_submit_location_fields
*
* @param {array} $location_fields Array of fields as defined by shortcode or ID.
* @param {string} $form_id Form ID.
*
* @return {array}
*/
$args['location_fields'] = apply_filters( 'mcs_submit_location_fields', $args['location_fields'], $form_id );
wp_enqueue_script( 'mcs.submit' );
wp_localize_script(
'mcs.submit',
'mcssubmit',
array(
'ajaxurl' => admin_url( 'admin-ajax.php' ),
'security' => wp_create_nonce( 'mc-query-purchase' ),
'action' => 'mcs_purchase_request',
)
);
wp_enqueue_script( 'mcs.charcount' );
wp_localize_script(
'mcs.charcount',
'mcsCounter',
array(
'text' => __( 'Words left: ', 'my-calendar-pro' ),
)
);
if ( function_exists( 'mc_count_locations' ) ) {
$count = mc_count_locations();
/**
* Set number of locations where front-end submissions switches from a select to an autocomplete.
*
* @hook mcs_convert_locations_select_to_autocomplete
*
* @param {int} $count Number of locations. Default 90.
* @param {string} $form_id ID for the current form.
*
* @return {int}
*/
if ( $count > apply_filters( 'mcs_convert_locations_select_to_autocomplete', 90, $form_id ) ) {
wp_enqueue_script( 'mcs-autocomplete' );
wp_localize_script(
'mcs-autocomplete',
'mcslocations',
array(
'ajaxurl' => admin_url( 'admin-ajax.php' ),
'security' => wp_create_nonce( 'mc-search-locations' ),
'action' => 'mcs_autocomplete_search_locations',
)
);
}
}
mcs_setup_duetjs();
$message = '';
$response = mcs_processor_response();
$event = false;
$mc_id = false;
if ( empty( $response[1] ) && isset( $_GET['mcs_id'] ) && is_user_logged_in() ) {
$mc_id = intval( $_GET['mcs_id'] );
$event = mc_form_data( $mc_id );
if ( ! mc_can_edit_event( absint( $event->event_id ) ) ) {
$event = false;
}
}
if ( isset( $_GET['mcs_id'] ) && ! is_user_logged_in() ) {
$message = "<div class='updated'><p>" . __( "You'll need to log-in to edit this event.", 'my-calendar-pro' ) . '</p></div>';
}
// To Do: function that parses $data to populate an object in this manner.
$data = ( ! empty( $response[1] ) ) ? $response[1] : $event;
$payment_form = ( mcs_payment_required() ) ? mcs_payment_form() : '';
$enctype = ( 'true' === get_option( 'mcs_upload_images' ) ) ? ' enctype="multipart/form-data"' : '';
if ( current_user_can( 'mc_manage_events' ) || current_user_can( 'mc_add_events' ) ) {
$action = ! empty( $_GET['action'] ) ? sanitize_text_field( $_GET['action'] ) : '';
$confirm = ! empty( $_GET['confirm'] ) ? sanitize_text_field( $_GET['confirm'] ) : 'false';
$event_id = ! empty( $_GET['mcs_id'] ) ? intval( $_GET['mcs_id'] ) : '';
if ( 'delete' === $action ) {
if ( 'false' === $confirm ) {
$title = ( $event ) ? stripslashes( esc_html( $event->event_title ) ) : 'Untitled Event';
// Translators: Title of event to delete.
$delete_text = sprintf( __( 'You are deleting “%s”.', 'my-calendar-pro' ), $title );
$message .= "<div class='updated'>
<p>" . $delete_text . " <a href='" . mcs_delete_url( $event_id, true ) . "'>" . __( 'Please confirm deletion.', 'my-calendar-pro' ) . '</a></p>
</div>';
} elseif ( 'true' === $confirm ) {
$verify = wp_verify_nonce( $_GET['_mc_delete_event'], 'mc-delete-event' );
if ( $verify ) {
$message .= mc_delete_event( $event_id );
} else {
$message .= __( 'Could not verify your request.', 'my-calendar-pro' );
}
}
}
}
$message = str_replace( array( 'class="', "class='" ), array( 'class="notice ', "class='notice " ), $message );
$respond = '';
if ( $response ) {
$respond = str_replace( array( 'class="', "class='" ), array( 'class="notice ', "class='notice " ), $response[0] );
}
$submit_id = get_option( 'mcs_submit_id' );
$message = ( 'true' === get_option( 'mcs_ajax', 'true' ) ) ? "<div class='mcs-status' id='mcs-status-$form_id' tabindex='-1' aria-live='assertive'>" . $message . '</div>' : $message;
// if there are multiple forms, submissions should go to the current form.
if ( ( is_single() || $is_widget ) && get_the_ID() !== $submit_id ) {
$submit_id = get_the_ID();
}
$action = ( $submit_id ) ? get_permalink( $submit_id ) : '';
$action = ( isset( $_GET['confirm'] ) ) ? $action : add_query_arg( 'mcs_id', $mc_id, $action );
$action = ( $form_id ) ? add_query_arg( 'form-id', sanitize_title( $form_id ), $action ) : $action;
$id_form = ( $form_id ) ? " id='mcs_" . sanitize_title( $form_id ) . "'" : '';
$return = "
<div class='mc-submissions'$id_form>
$message
$respond
$payment_form
<form action='" . esc_url( $action ) . "' method='post' id='" . esc_attr( $form_id ) . "' class='mcs-submission' $enctype>
" . mcs_input_fields( $data, $event, $args );
if ( 'true' === get_option( 'mcs_automatic_approval' ) || current_user_can( 'mc_approve_events' ) ) {
$name = 'save';
} else {
$name = 'draft';
}
$button_text = ( isset( $_GET['mcs_id'] ) && ! isset( $_GET['confirm'] ) ) ? __( 'Edit your event', 'my-calendar-pro' ) : __( 'Submit your event', 'my-calendar-pro' );
$return .= "<p class='mcs-submit-button'><input type='submit' id='mcs-submit-$form_id' class='button-primary mc-submit' name='$name' value='" . $button_text . "' /></p>";
$edit = '';
if ( current_user_can( 'manage_options' ) ) {
$edit_link = add_query_arg( 'form_id', $form_id, admin_url( '/admin.php?page=my-calendar-shortcodes' ) ) . '#mc_builder';
$edit = wpautop( '<a class="mcs-edit-form" href="' . esc_url( $edit_link ) . '">' . __( 'Edit this form', 'my-calendar-pro' ) ) . '</a>';
}
$return .= '
</form>
</div>' . $edit;
/**
* Filter the complete output of the My Calendar submissions form.
*
* @hook mcs_after_submission
*
* @param {string} $return HTML for form.
* @param {string} $response Feedback after submitting form.
* @param {string} $form_id Form ID.
*
* @return {string}
*/
$return = apply_filters( 'mcs_after_submissions', $return, $response, $form_id );
return $return;
}
/**
* Determine what type of image upload we're allowing.
*
* @param string|int $image Image URL or post thumbnail ID.
*
* @return string
*/
function mcs_image_input_type( $image = '' ) {
if ( 'true' === get_option( 'mcs_upload_images' ) && ( '' === $image || is_int( $image ) ) ) {
$input_type = 'file';
} else {
$input_type = 'url';
}
return $input_type;
}
/**
* Array of empty input field containers.
*
* @return array
*/
function mcs_input_field_array() {
$input_fields = array(
'event_title' => '',
'event_date' => '',
'event_time' => '',
'event_end' => '',
'mcs_name' => '',
'mcs_email' => '',
'event_host' => '',
'event_recurring' => '',
'description' => '',
'short_description' => '',
'access' => '',
'event_link' => '',
'event_image' => '',
'event_category' => '',
'registration' => '',
'locations' => '',
);
return $input_fields;
}
/**
* Set up hidden inputs required when editing an event.
*
* @param object $event Event object.
* @param object $data Saved data object.
*
* @return string
*/
function mcs_setup_editing_event( $event, $data ) {
if ( $event && ! isset( $_GET['confirm'] ) ) {
$begin = ( ! empty( $data ) ) ? esc_attr( mcs_date( 'Y-m-d', strtotime( $data->event_begin ), false ) ) : '';
$recur = ( ! empty( $data ) ) ? esc_attr( $data->event_recur ) : 'S';
$recurs = str_split( $recur, 1 );
$recur = $recurs[0];
$every = ( isset( $recurs[1] ) && ! empty( $data ) ) ? str_replace( $recurs[0], '', $data->event_recur ) : 1;
$every = ( 1 === $every && 'B' === $recur ) ? 2 : $every;
$repeats = ( ! empty( $data ) ) ? esc_attr( $data->event_repeats ) : '';
// For older events with a numeric repetition, update the repetition to the new format.
if ( $event && is_numeric( $repeats ) ) {
$occurrences = mc_get_occurrences( $event->event_id );
$last = array_pop( $occurrences );
$instance = mc_get_instance_data( $last->occur_id );
$repeats = gmdate( 'Y-m-d', strtotime( $instance->occur_begin ) );
}
return "<input type='hidden' name='event_edit' value='$event->event_id' />
<input type='hidden' name='prev_event_begin' value='$begin' />
<input type='hidden' name='prev_event_repeats' value='$repeats' />
<input type='hidden' name='prev_event_recur' value='$recur' />
<input type='hidden' name='prev_event_status' value='$event->event_approved' />
<input type='hidden' name='event_post' value='$event->event_post' />";
} else {
return '';
}
}
/**
* Hidden input fields.
*
* @param string $form_id ID for currently processed form.
*
* @return array array of field keys for hidden inputs in calendar.
*/
function mcs_hidden_input_fields( $form_id ) {
$fields = array(
'event_link_expires' => '',
'event_holiday' => '',
'event_fifth_week' => '',
'event_nonce_name' => '',
'mcs_submission' => '',
'event_author' => '',
'event_group_id' => '',
'mcs_form_id' => '',
'mcs_form_timestamp' => '',
'event_all_day' => '',
'event_hide_end' => '',
'mcs_check_conflicts' => '',
'location_preset' => '',
);
$forms = get_option( 'mcs_forms', array() );
$form = isset( $forms[ $form_id ] ) ? $forms[ $form_id ] : array();
$allcats = mc_no_category_default();
// If there's only one category, the form is removed and inserted as a hidden field regardless of settings.
if ( '0' === $form['categories'] || ( '1' === $form['categories'] && 1 === count( $allcats ) ) || 'hidden' === $form['category_field']['entry_type'] ) {
$fields['event_category'] = isset( $form['category'] ) ? $form['category'] : '1';
}
$omit_location_preset = array( 'either', 'choose' );
// If users can select locations, then location_preset is not a hidden field.
if ( in_array( $form['locations'], $omit_location_preset, true ) ) {
unset( $fields['location_preset'] );
}
/**
* Filter the hidden field inputs used in My Calendar Pro forms.
*
* @hook mcs_hidden_fields
*
* @param {array} $fields An array of fields in format [key => $value].
* @param {string} $form_id The ID of the current form.
*
* @return {array}
*/
$return = (array) apply_filters( 'mcs_hidden_fields', $fields, $form_id );
return $return;
}
/**
* Hidden inputs used in My Calendar submissions.
*
* @param object $event Event object.
* @param object $data Saved data object.
* @param array $fields Fields passed from shortcode.
* @param array $hidden Hidden location & category inputs.
* @param string $form_id Form ID passed from shortcode.
*
* @return string
*/
function mcs_submissions_hidden_inputs( $event, $data, $fields, $hidden, $form_id ) {
$user = wp_get_current_user();
$auth = ( is_user_logged_in() ) ? $user->ID : 0;
$values = mcs_hidden_input_fields( $form_id );
$values = array_merge( $values, $hidden );
$hidden_fields = '';
if ( $event && ! isset( $_GET['confirm'] ) ) {
$values['event_link_expires'] = $event->event_link_expires;
$values['event_holiday'] = $event->event_holiday;
$values['event_fifth_week'] = $event->event_fifth_week;
} else {
$values['event_link_expires'] = ( function_exists( 'mc_event_link_expires' ) && ! mc_event_link_expires() ) ? 1 : 0;
$values['event_holiday'] = ( 'true' === get_option( 'mc_skip_holidays' ) ) ? 'on' : 'false';
$values['event_fifth_week'] = ( function_exists( 'mc_no_fifth_week' ) && mc_no_fifth_week() ) ? 1 : '';
}
$values['event_nonce_name'] = wp_create_nonce( 'event_nonce' );
$values['mcs_submission'] = 'on';
$values['event_author'] = (int) $auth;
$values['event_group_id'] = mc_group_id();
$values['mcs_form_id'] = $form_id;
$values['mcs_form_timestamp'] = time();
if ( ! isset( $fields['event_allday'] ) ) {
/**
* Set the all day event value to 1 (true). Default 0 (false). Obsolete in My Calendar Pro 3.0.
*
* @hook mcs_event_allday
*
* @param {int} $hidden Default '0', false.
*
* @return {int}
*/
if ( true === (bool) apply_filters( 'mcs_event_allday', 0 ) ) {
$values['event_allday'] = '1';
}
}
if ( ! isset( $fields['event_hide_end'] ) ) {
/**
* Set the event hide end value to 1 (true). Default 0 (false). Obsolete in My Calendar Pro 3.0.
*
* @hook mcs_event_hide_end
*
* @param {int} $hidden Default '0', false.
*
* @return {int}
*/
if ( true === (bool) apply_filters( 'mcs_event_hide_end', 0 ) ) {
$values['event_hide_end'] = '1';
}
}
if ( 'true' === get_option( 'mcs_check_conflicts' ) ) {
$values['mcs_check_conflicts'] = 'true';
}
foreach ( $values as $key => $value ) {
// Hidden fields don't support array values, but categories can be arrays.
if ( is_array( $value ) ) {
$value = $value[0];
}
$values[ $key ] = (string) $value;
if ( '' !== $value ) {
$hidden_fields .= '<input type="hidden" name="' . $key . '" value="' . esc_attr( $value ) . '">' . PHP_EOL;
} else {
unset( $values[ $key ] );
}
}
$unique_id = ( isset( $_COOKIE['mcs_unique_id'] ) ) ? sanitize_text_field( wp_unslash( $_COOKIE['mcs_unique_id'] ) ) : false;
// Form timestamp always changes between render and submission, so can't be tested this way.
if ( $unique_id ) {
unset( $values['mcs_form_timestamp'] );
delete_transient( 'mcs_hidden_fields_' . $unique_id );
set_transient( 'mcs_hidden_fields_' . $unique_id, $values, HOUR_IN_SECONDS );
}
$edit = mcs_setup_editing_event( $event, $data );
$hidden_inputs = "
<div class='hidden-inputs'>
$hidden_fields
<div style='display: none;' class='mcs-hidden'>
<label for='your_name'>" . __( 'Do not fill-in this field.', 'my-calendar-pro' ) . "</label>
<input type='text' name='your_name' id='your_name' value='' autocomplete='off' />
</div>
$edit";
$hidden_inputs .= '</div>';
return $hidden_inputs;
}
/**
* Generate user-fillable input fields for a submit form (all arguments passed from mc_submit_form function).
*
* @param object $data Object with event data.
* @param object|bool $event Event object; false if new event.
* @param array $args Array of objects passed to form. Documented on mcs_build_submit_form.
*
* @return string
*/
function mcs_input_fields( $data, $event, $args ) {
$fields = $args['fields'];
$form_id = $args['form_id'];
$has_data = ( is_object( $data ) && property_exists( $data, 'event_id' ) ) ? true : false;
$event_id = ( $has_data ) ? $data->event_id : false;
$location_fields = mcs_location_fields( $args, $data );
$category = $args['category'];
$categories = $args['categories'];
$category_field = $args['category_field']; // meta data about category field.
$selected_cat = ( $has_data ) ? mc_get_categories( $event_id ) : $category;
$category_fields = mcs_category_fields( $selected_cat, $categories, $category_field );
$hidden = array_merge( $category_fields['hidden'], $location_fields['hidden'] );
$return = '';
$defaults = mcs_default_fields();
$input_fields = mcs_input_field_array();
/**
* Filter the required fields in front-end event editing and creation.
*
* @hook mcs_required_fields
*
* @param {array} $required Array of required fields.
* @param {string} $form_id Form ID.
*
* @return {array}
*/
$required_fields = apply_filters( 'mcs_required_fields', get_option( 'mcs_required_fields', array() ), $form_id );
$required_text = mcs_required_text();
if ( mcs_payment_required() ) {
$key = ( isset( $_POST['mcs_key'] ) ) ? esc_attr( sanitize_text_field( wp_unslash( $_POST['mcs_key'] ) ) ) : '';
$key = ( isset( $_GET['mcs_key'] ) ) ? esc_attr( sanitize_text_field( wp_unslash( $_GET['mcs_key'] ) ) ) : $key;
$return .= "<p><label for='mcs_key'>" . __( 'Payment Key', 'my-calendar-pro' ) . ' ' . $required_text . "</label> <input type='text' name='mcs_key' id='mcs_key' value='$key' required='required' /></p>";
}
$hidden_inputs = mcs_submissions_hidden_inputs( $event, $data, $fields, $hidden, $form_id );
$input_fields['event_title'] = mcs_title_input( $data, $fields );
$input_fields['event_date'] = mcs_event_date_input( $data, $fields );
$input_fields['event_end'] = mcs_end_date_input( $data, $fields );
if ( isset( $fields['submitter'] ) ) {
$input_fields['submitter'] = mcs_name_and_email_input( $data, $fields );
} else {
$input_fields['mcs_name'] = mcs_name_and_email_input( $data, $fields );
}
$input_fields['event_host'] = mcs_event_host_input( $data, $fields );
$input_fields['event_recurring'] = mcs_event_recurring_input( $data, $fields );
$input_fields['registration'] = mcs_event_registration_input( $data, $fields );
$input_fields['description'] = mcs_description_input( $data, $fields );
$input_fields['short_description'] = mcs_short_description_input( $data, $fields );
// This stays as it is, because it's using a function from My Calendar.
if ( isset( $fields['access'] ) && function_exists( 'mc_event_accessibility' ) ) {
$flabel = ( 'true' !== $fields['access'] ) ? $fields['access'] : $defaults['access']['field_label'];
$input_fields['access'] = mc_event_accessibility( '', $data, $flabel );
}
$input_fields['event_link'] = mcs_event_link_input( $data, $fields );
$input_fields['event_image'] = mcs_event_image_input( $data, $fields );
$input_fields['categories'] = $category_fields['html'];
$input_fields['locations'] = $location_fields['html'];
/**
* Filter the contents of the `custom_fields` parameter in My Calendar Pro. Legacy support for older custom field setup.
*
* @hook mc_event_details
*
* @param {string} $html HTML output for custom fields.
* @param {bool} $has_data Whether current event object is populated.
* @param {object} $data Event object.
* @param {string} $public Public context.
*
* @return {string}
*/
$input_fields['custom_fields'] = apply_filters( 'mc_event_details', '', $has_data, $data, 'public' );
/**
* Set up custom fields to a form that are sortable in Pro.
*
* @hook mc_custom_fields
*
* @param {array} $input_fields Array of fields to add.
* @param {bool} $has_data Boolean indicating whether the current event is populated.
* @param {object} $data Event object.
* @param {string} $public 'public' indicating that this is the public display of these fields.
* @param {string} $form_id Form ID.
*
* @return {array}
*/
$input_fields = apply_filters( 'mc_custom_fields', $input_fields, $has_data, $data, 'public', $form_id );
// These fields are always required.
$mandatory_fields = array(
'event_title' => true,
'event_date' => true,
'event_time' => true,
'submitter' => true,
'custom_fields' => true,
);
// If a required field is already called in the shortcode settings, don't append to top.
foreach ( $mandatory_fields as $field => $enabled ) {
$field_keys = array_keys( $fields );
if ( in_array( $field, $field_keys, true ) ) {
unset( $mandatory_fields[ $field ] );
}
}
$fields = array_merge( $mandatory_fields, $fields );
foreach ( $required_fields as $req ) {
if ( ! in_array( $req, $fields, true ) ) {
$fields[ $req ] = true;
}
}
// Locations are always added at the end unless set elsewhere. Locations field setter controls output.
if ( ! isset( $fields['locations'] ) ) {
$fields['locations'] = 'true';
}
if ( ! isset( $fields['categories'] ) ) {
$fields['categories'] = 'true';
}
/**
* Filter the input fields for a given form.
*
* @hook mcs_input_fields
*
* @param {array} $fields Array of field data & form parameters.
* @param {array} $mandatory_fields The array of fields that are always required by forms.
* @param {string} $form_id ID for the current form.
*
* @return {array}
*/
$fields = apply_filters( 'mcs_input_fields', $fields, $mandatory_fields, $form_id );
$event_end = true;
foreach ( $fields as $field => $label ) {
if ( ( 'end_time' === $field || 'end_date' === $field || 'event_end' === $field || 'event_endtime' === $field ) && true === $event_end ) {
// Only add end values once.
$event_end = false;
$end_field = ( isset( $input_fields['event_end'] ) ) ? $input_fields['event_end'] : '';
/**
* Filter the HTML output of a submissions input field. Special handling filter for end date/time fields.
*
* @hook mcs_{field}_input_field
*
* @param {string} $value HTML output for field.
* @param {string} $field Field keyword.
* @param {string} $form_id Form ID.
*
* @return {string}
*/
$return .= apply_filters( 'mcs_' . $field . '_input_field', $end_field, $field, $form_id );
} else {
if ( 'end_time' === $field || 'end_date' === $field || 'event_end' === $field || 'event_endtime' === $field ) {
continue;
}
$value = isset( $input_fields[ $field ] ) ? $input_fields[ $field ] : '';
/**
* Filter the HTML output of a submissions input field.
*
* @hook mcs_{field}_input_field
*
* @param {string} $value HTML output for field.
* @param {string} $field Field keyword.
* @param {string} $form_id Form ID.
*
* @return {string}
*/
$return .= apply_filters( 'mcs_' . $field . '_input_field', $value, $field, $form_id );
}
}
$submitting_as = '';
if ( is_user_logged_in() ) {
$name = wp_get_current_user()->display_name;
$email = wp_get_current_user()->user_email;
// Translators: user's name, user's email.
$submitting_as = '<div class="mcs_submitting"><p>' . sprintf( __( 'Submitting as %1$s at %2$s', 'my-calendar-pro' ), '<strong>' . esc_html( $name ) . '</strong>', '<code>' . esc_html( $email ) . '</code>' ) . '</p></div>';
}
return $submitting_as . $hidden_inputs . $return;
}
/**
* Return input fields for category selection
*
* @param int|array $category Category to be selected in form. Can be an array.
* @param int $categories integer (used as boolean) whether categories are enabled.
* @param array $field Array of field settings for categories.
*
* @return string; selection form
*/
function mcs_category_fields( $category, $categories, $field = array() ) {
$return = '';
$hidden = array();
if ( isset( $field['entry_type'] ) && 'hidden' === $field['entry_type'] ) {
$categories = false;
}
if ( ! $categories ) {
if ( ! $category || empty( $category ) ) {
$hidden['event_category'] = '1';
} else {
if ( is_array( $category ) ) {
foreach ( $category as $cat ) {
$hidden['event_category'][] = $cat;
}
} else {
$hidden['event_category'] = $category;
}
}
} else {
$allcats = mc_no_category_default();
if ( 1 === count( $allcats ) ) {
$hidden['event_category'] = '1';
} else {
if ( isset( $field['entry_type'] ) && 'single' === $field['entry_type'] ) {
$category = ( is_array( $category ) ) ? $category[0] : $category; // If categories are an array, can only use first selected.
$label = ( isset( $field['field_label'] ) && '' !== $field['field_label'] ) ? $field['field_label'] : __( 'Category', 'my-calendar-pro' );
$return = '<div class="mc-event-category mcs-select mc-category-single"><p><label for="event_category">' . $label . '</label><select id="event_category" name="event_category[]">' . mc_category_select( $category, true, false ) . '</select></p></div>';
} else {
$label = ( isset( $field['field_label'] ) && '' !== $field['field_label'] ) ? $field['field_label'] : __( 'Categories', 'my-calendar-pro' );
$return = '
<fieldset class="mc-event-category mc-fieldset mc-category-multiple">
<legend class="mc-categories-group">' . $label . '</legend>
<ul class="mc-categories checkboxes">
' . mc_category_select( $category, true, true ) . '
</ul>
</fieldset>';
}
}
}
$return = array(
'html' => $return,
'hidden' => $hidden,
);
return $return;
}
/**
* Return form fields for selecting or inputting a location
*
* @param array $args Array of location form arguments.
* @param object $event Event with location data.
*
* @return array string HTML for input fields and array of hidden inputs.
*/
function mcs_location_fields( $args, $event ) {
$location_fields = $args['location_fields'];
$locations = $args['locations'];
$has_data = ( is_object( $event ) && property_exists( $event, 'event_id' ) ) ? true : false;
$location = ( $has_data && property_exists( $event, 'event_location' ) ) ? $event->event_location : $args['location'];
$loc = mc_get_location( $location );
// Selected location data.
$return = '';
$hidden = array();
$selected_location = array(
'label' => ( ! empty( $loc ) ) ? $loc->location_label : '',
'street' => ( ! empty( $loc ) ) ? $loc->location_street : '',
'street2' => ( ! empty( $loc ) ) ? $loc->location_street2 : '',
'city' => ( ! empty( $loc ) ) ? $loc->location_city : '',
'state' => ( ! empty( $loc ) ) ? $loc->location_state : '',
'postcode' => ( ! empty( $loc ) ) ? $loc->location_postcode : '',
'country' => ( ! empty( $loc ) ) ? $loc->location_country : '',
'region' => ( ! empty( $loc ) ) ? $loc->location_region : '',
'url' => ( ! empty( $loc ) ) ? $loc->location_url : '',
'longitude' => ( ! empty( $loc ) ) ? $loc->location_longitude : '',
'latitude' => ( ! empty( $loc ) ) ? $loc->location_latitude : '',
'phone' => ( ! empty( $loc ) ) ? $loc->location_phone : '',
);
$autocomplete = false;
if ( function_exists( 'mc_count_locations' ) ) {
$count = mc_count_locations();
/**
* Set number of locations where front-end submissions switches from a select to an autocomplete.
*
* @hook mcs_convert_locations_select_to_autocomplete
*
* @param {int} $count Number of locations. Default 90.
* @param {string} $form_id ID for the current form.
*
* @return {int}
*/
if ( $count > apply_filters( 'mcs_convert_locations_select_to_autocomplete', 90, $args['form_id'] ) ) {
$autocomplete = true;
}
}
if ( 'none' === $locations && ! $location ) {
$hidden['location_preset'] = 'none';
} else {
if ( isset( $_GET['mcs_id'] ) ) {
$locations = 'choose';
}
switch ( $locations ) {
case 'choose':
$return .= "<p class='mc-select-location'>
<label for='mcs_event_location'>" . __( 'Location', 'my-calendar-pro' ) . '</label>';
if ( $autocomplete ) {
$location_label = ( $location && is_numeric( $location ) ) ? mc_get_location( $location )->location_label : '';
$return .= '<div id="mc-locations-autocomplete" class="mc-autocomplete autocomplete">
<input class="autocomplete-input" id="mcs_event_location" placeholder="' . __( 'Search locations...', 'my-calendar-pro' ) . '" value="' . esc_attr( $location_label ) . '" />
<ul class="autocomplete-result-list"></ul>
</div>
<input type="hidden" name="location_preset" id="mcs_event_location_value" value="' . esc_attr( $location ) . '" />';
} else {
$return .= "<select name='location_preset' id='mcs_event_location'>
" . mc_location_select( $location ) . '
</select>';
}
$return .= '</p>';
break;
case 'either':
$return .= "<div class='mc-select-location-group'><div class='mc-select-location'>
<label for='mcs_event_location'>" . __( 'Location', 'my-calendar-pro' ) . '</label>';
if ( $autocomplete ) {
$location_label = ( $location && is_numeric( $location ) ) ? mc_get_location( $location )->location_label : '';
$return .= '<div id="mc-locations-autocomplete" class="mc-autocomplete autocomplete">
<input class="autocomplete-input" id="mcs_event_location" placeholder="' . __( 'Search locations...', 'my-calendar-pro' ) . '" value="' . esc_attr( $location_label ) . '" />
<ul class="autocomplete-result-list"></ul>
</div>
<input type="hidden" name="location_preset" id="mcs_event_location_value" value="' . esc_attr( $location ) . '" />';
} else {
$return .= "<select name='location_preset' id='mcs_event_location'>
" . mc_location_select( $location ) . '
</select>';
}
$return .= '</div>';
/**
* Set whether location fields are hidden in the submission form with a toggle to display them or visible at all times.
*
* @hook mcs_expand_location_fields
*
* @param {bool} $visible true to show fields. Default false.
*
* @return {bool}
*/
if ( ! apply_filters( 'mcs_expand_location_fields', false ) ) {
/**
* Filter the HTML for the button used to toggle location fields.
*
* @hook mcs_expand_button
*
* @param {string} $html HTML string for button.
*
* @return {string}
*/
$button = apply_filters( 'mcs_expand_button', "<button type='button' class='toggle_location_fields' aria-expanded='false'><span class='mc-button-text'>" . __( 'Add New Location', 'my-calendar-pro' ) . "</span><span class='dashicons dashicons-plus' aria-hidden='true'></span></button>" );
$return .= "
$button
<div class='mcs_location_fields'>
" . mcs_location_form( $location_fields, $selected_location, 'hidden' ) . '
</div>
</div>';
} else {
$return .= mcs_location_form( $location_fields, $selected_location, 'visible' );
}
break;
case 'enter':
$return .= mcs_location_form( $location_fields, $selected_location, 'visible' );
$hidden['location_preset'] = 'none';
break;
default:
if ( $location ) {
$hidden['location_preset'] = $location;
} else {
$hidden['location_preset'] = 'none';
}
}
}
$return = array(
'html' => $return,
'hidden' => $hidden,
);
return $return;
}
/**
* Generate location field for frontend.
*
* @param string $field Field ID.
* @param array $data Field data.
* @param array $loc Location data.
* @param string $visibility Visibility of containing form.
*
* @return string
*/
function mcs_location_build_field( $field, $data, $loc, $visibility ) {
$defaults = mcs_default_location_fields();
$default_label = ( isset( $defaults[ $field ]['field_label'] ) ) ? $defaults[ $field ]['field_label'] : __( 'No label defined', 'my-calendar-pro' );
$control = str_replace( 'event_', '', $field );
$required = 'false';
$required_field = '';
$required_text = '';
if ( is_array( $data ) ) {
$label = ( isset( $data['field_label'] ) && 'true' !== $data['field_label'] ) ? $data['field_label'] : $default_label;
$required = ( isset( $data['required'] ) && 'on' === $data['required'] ) ? 'true' : 'false';
$required_field = ( 'true' === $required && 'visible' === $visibility ) ? 'required ' : '';
$required_text = ( 'true' === $required ) ? ' ' . mcs_required_text() : '';
} else {
$label = ( 'true' !== $data ) ? $data : $default_label;
}
$name = ( 0 === stripos( $field, 'event_' ) ) ? $field : 'event_' . $field;
if ( 'gps' === $field ) {
$return = '
<fieldset class="mcs-field mcs-location-field mcs-location-' . esc_attr( $field ) . '">
<legend>' . __( 'GPS Coordinates', 'my-calendar-pro' ) . '</legend>
<p>
<label for="event_latitude">' . __( 'Latitude', 'my-calendar-pro' ) . $required_text . '</label>
<input type="text" ' . $required_field . ' data-required="' . $required . '" id="event_latitude" name="event_latitude" class="input mc-location-field mc-location-' . esc_attr( $field ) . '" size="10" value="' . stripslashes( esc_attr( $loc['latitude'] ) ) . '" />
</p>
<p>
<label for="event_longitude">' . __( 'Longitude', 'my-calendar-pro' ) . $required_text . '</label>
<input type="text" ' . $required_field . ' data-required="' . $required . '" id="event_longitude" name="event_longitude" class="input mc-location-field mc-location-' . esc_attr( $field ) . '" size="10" value="' . stripslashes( esc_attr( $loc['longitude'] ) ) . '" />
</p>
<input type="hidden" name="event_zoom" value="16" />
</fieldset>';
} else {
// Correct legacy field name.
$field = ( 'event_label' === $field ) ? 'label' : $field;
$value = ( isset( $loc[ $field ] ) ) ? $loc[ $field ] : '';
$return = '
<p class="mcs-field mcs-location-field mcs-location-' . esc_attr( $field ) . '">
<label for="' . esc_attr( $name ) . '">' . $label . $required_text . '</label>';
if ( mc_controlled_field( $control ) && 'label' !== $control ) {
$return .= mc_location_controller( $control, $value, 'event' );
} else {
$return .= '<input type="text" ' . $required_field . ' data-required="' . $required . '" id="' . esc_attr( $name ) . '" name="' . esc_attr( $name ) . '" class="input" value="' . stripslashes( esc_attr( $value ) ) . '" />';
}
}
return $return;
}
/**
* Produce location form.
*
* @param array $fields Array of fields and labels to display.
* @param integer $loc Integer for pre-defined location in location tables to default to.
* @param string $visibility 'visible' or 'hidden' to indicate whether fields are visible by default.
*
* @return String Form HTML
*/
function mcs_location_form( $fields, $loc, $visibility ) {
$return = '';
foreach ( $fields as $field => $data ) {
$output[] = mcs_location_build_field( $field, $data, $loc, $visibility );
}
$return .= implode( PHP_EOL, $output );
return $return;
}
/**
* Process submission from form.
*
* @param array $post POSTed data.
*
* @return array|false
*/
function mcs_processor( $post ) {
$error = false;
if ( isset( $post['mcs_submission'] ) ) {
$attach_id = false;
$nonce = $post['event_nonce_name'];
$posted = mc_check_data( 'check', $post, 0 );
$form_id = ( isset( $post['mcs_form_id'] ) && '' !== $post['mcs_form_id'] ) ? $post['mcs_form_id'] : '';
if ( ! wp_verify_nonce( $nonce, 'event_nonce' ) ) {
return array( '<div class="notice error"><p>' . __( 'Invalid nonce provided. Please refresh your page.', 'my-calendar-pro' ) . '</p></div>', $posted[1], false );
}
if ( ! mcs_verify_hidden_inputs( $post, $form_id ) ) {
return array( '<div class="notice error"><p>' . __( 'Could not verify input.', 'my-calendar-pro' ) . '</p></div>', $posted[1], false );
}
// reject invalid email addresses.
if ( isset( $post['mcs_email'] ) && ! is_email( $post['mcs_email'] ) ) {
return array( '<div class="notice error"><p>' . __( 'Invalid email provided', 'my-calendar-pro' ) . '</p></div>', $posted[1], false );
}
// honeypot - only bots should complete this field.
$honeypot = ( isset( $post['your_name'] ) && '' !== $post['your_name'] ) ? true : false;
if ( $honeypot ) {
if ( SCRIPT_DEBUG ) {
return array( '<div class="notice error"><p>' . __( 'Honeypot', 'my-calendar-pro' ) . '</p></div>', $posted[1], false );
}
return false;
}
if ( isset( $post['mcs_form_timestamp'] ) ) {
$timestamp = absint( $post['mcs_form_timestamp'] );
$now = time();
// If current time is before the timestamp, it's been spoofed.
if ( $now < $timestamp ) {
if ( SCRIPT_DEBUG ) {
return array( '<div class="notice error"><p>' . __( 'Invalid time frame', 'my-calendar-pro' ) . '</p></div>', $posted[1], false );
}
return false;
}
$diff = $now - $timestamp;
/**
* Set minimum submission time limit for the My Calendar form honeypot. If it took less than x seconds to submit this form, reject it.
*
* @hook mcs_honeypot_limit
*
* @param {int} $limit Minimum number of seconds submissions are reasonably expected to take. Default 6.
*
* @return {int}
*/
$margin = apply_filters( 'mcs_honeypot_limit', 6 );
if ( $diff < $margin ) {
if ( SCRIPT_DEBUG ) {
return array( '<div class="notice error"><p>' . __( 'Too fast', 'my-calendar-pro' ) . '</p></div>', $posted[1], false );
}
return false;
}
}
/**
* Run action on front-end processing of event submission. After honeypot & other checks are run, before any event processing is done.
*
* @hook mcs_before_processing
*
* @param {array} $post POSTed event data.
*/
do_action( 'mcs_before_processing', $post );
$post_alt = false;
// if files being uploaded, upload file and convert to a string for $post.
if ( 'true' === get_option( 'mcs_ajax', 'true' ) ) {
if ( 'true' === get_option( 'mcs_upload_images' ) ) {
$post['event_image'] = ( isset( $post['event_image_id'] ) && is_numeric( $post['event_image_id'] ) ) ? wp_get_attachment_image_url( $post['event_image_id'], 'large' ) : '';
} else {
if ( isset( $post['event_image'] ) ) {
// Verify supplied URL is a valid image.
if ( mcs_check_remote_image( $post['event_image'] ) ) {
$post['event_image'] = esc_url_raw( $post['event_image'] );
$post_alt = sanitize_text_field( $post['event_image_alt'] );
} else {
$post['event_image'] = '';
}
}
}
} else {
if ( 'true' === get_option( 'mcs_upload_images' ) ) {
if ( ! empty( $_FILES['event_image'] ) ) {
require_once ABSPATH . '/wp-admin/includes/file.php';
require_once ABSPATH . '/wp-admin/includes/image.php';
$file = $_FILES['event_image'];
$upload = wp_handle_upload( $file, array( 'test_form' => false ) );
if ( ! isset( $upload['error'] ) && isset( $upload['file'] ) ) {
$filetype = wp_check_filetype( basename( $upload['file'] ), null );
$title = $file['name'];
$ext = strrchr( $title, '.' );
$title = ( false !== $ext ) ? substr( $title, 0, - strlen( $ext ) ) : $title;
$attachment = array(
'post_mime_type' => $filetype['type'],
'post_title' => addslashes( $title ),
'post_content' => '',
'post_status' => 'inherit',
);
$alt = ( isset( $post['event_image_alt'] ) ) ? sanitize_text_field( $post['event_image_alt'] ) : '';
$attach_id = wp_insert_attachment( $attachment, $upload['file'] );
$attach_data = wp_generate_attachment_metadata( $attach_id, $upload['file'] );
$post['event_image'] = esc_url_raw( $upload['url'] );
$post['event_image_id'] = (int) $attach_id;
update_post_meta( $attach_id, '_wp_attachment_image_alt', $alt );
wp_update_attachment_metadata( $attach_id, $attach_data );
}
}
} else {
// Verify supplied URL is a valid image.
if ( isset( $post['event_image'] ) && mcs_check_remote_image( $post['event_image'] ) ) {
$post['event_image'] = esc_url_raw( $post['event_image'] );
$post_alt = sanitize_text_field( $post['event_image_alt'] );
} else {
$post['event_image'] = '';
}
}
}
if ( 'true' === get_option( 'mcs_automatic_approval' ) || current_user_can( 'mc_approve_events' ) ) {
$post['event_approved'] = 1;
} else {
$post['event_approved'] = 0;
}
// end file upload.
$check = mc_check_data( 'add', $post, 0 );
$message = '';
if ( mcs_payment_required() ) {
$key = ( isset( $post['mcs_key'] ) ) ? $post['mcs_key'] : false;
$quantity = mcs_check_key( $key );
if ( ! $quantity ) {
$reason = mcs_key_status( $key );
$error = true;
// Translators: invalid payment key.
$return = array( "<div class='notice error'><p>" . sprintf( __( 'That was not a valid payment key: %s', 'my-calendar-pro' ), $reason ) . '</p></div>', $check[1], false );
} else {
// Translators: Number of submissions remaining.
$message = sprintf( "<div class='notice error'><p>" . _n( '%d submissions remaining with this payment key.', '%d submissions remaining with this payment key.', ( $quantity - 1 ), 'my-calendar-pro' ) . '</p></div>', ( $quantity - 1 ) );
}
}
if ( ! $error ) {
if ( $check[0] ) {
if ( ! isset( $post['event_edit'] ) ) {
$response = my_calendar_save( 'add', $check );
$action = 'add';
} else {
$response = my_calendar_save( 'edit', $check, (int) $post['event_edit'] );
$action = 'edit';
}
$event_id = $response['event_id'];
$notice = $response['message'];
$event = mc_get_first_event( $event_id );
$post_id = $event->event_post;
if ( $post_alt ) {
update_post_meta( $post_id, '_mcs_submitted_alt', $post_alt );
}
if ( isset( $post['mcs_form_id'] ) && '' !== $post['mcs_form_id'] ) {
update_post_meta( $post_id, '_mcs_form_id', sanitize_title( $post['mcs_form_id'] ) );
}
set_post_thumbnail( $post_id, $attach_id );
if ( '' !== $message ) {
$notice .= " $message";
}
$return = array( $notice, array(), true );
} else {
$return = array( $check[3], $check[1], false );
$error = true;
}
}
if ( $event_id && ! $error ) {
$name = $post['mcs_name'];
$email = $post['mcs_email'];
if ( mcs_payment_required() ) {
// Note: payments will be processed on both submissions & on edits.
mcs_update_key_quantity( $key, $quantity );
}
// if no errors and NOT SPAM send notifications.
if ( mcs_event_is_spam( $event_id ) ) {
/**
* The submitted event has been determined as spam.
*
* @hook mcs_spam_submission
*
* @param {string} $name Submitter's name.
* @param {string} $email Submitter's email.
* @param {int} $event_id Submitted event ID.
*/
do_action( 'mcs_spam_submission', $name, $email, $event_id );
} else {
/**
* An event has been submitted and accepted.
*
* @hook mcs_complete_submission
*
* @param {string} $name Submitter's name.
* @param {string} $email Submitter's email.
* @param {int} $event_id Submitted event ID.
* @param {string} $action Action taken. [edit,add,etc.].
*/
do_action( 'mcs_complete_submission', $name, $email, $event_id, $action );
}
}
/**
* Run action on front-end processing after event submission. After event processing is completed.
*
* @hook mcs_before_processing
*
* @param {array} $post POSTed event data.
*/
do_action( 'mcs_after_processing', $post, $return );
return $return;
} else {
return false;
}
}
/**
* Check if this event is spam.
*
* @param int $event_id Event ID.
*
* @return boolean.
*/
function mcs_event_is_spam( $event_id ) {
$event = mc_get_first_event( $event_id );
$flag = $event->event_flagged;
if ( 1 === (int) $flag ) {
return true;
}
return false;
}
/**
* Get the current submissions URL.
*
* @param int $event_id Event ID. Optional.
* @param object $event Event object. Optional.
*
* @return string URL.
*/
function mcs_submit_url( $event_id = false, $event = false ) {
$submit_id = get_option( 'mcs_submit_id' );
$referer = urlencode( mc_get_current_url() );
$url = admin_url( "admin.php?page=my-calendar&mode=edit&ref=$referer" );
if ( $event_id ) {
$url = add_query_arg( 'event_id', $event_id, $url );
}
if ( $submit_id ) {
$url = get_permalink( $submit_id );
if ( $url && $event_id ) {
if ( $event && is_object( $event ) ) {
$post_id = $event->event_post;
$form_id = get_post_meta( $post_id, '_mcs_form_id', true );
if ( $form_id ) {
$url = add_query_arg( 'form-id', $form_id, $url );
}
}
$url = add_query_arg( 'mcs_id', $event_id, $url );
}
}
/**
* Filter the target submission URL for a given event. Sends events to different submission forms if multiple forms exist.
*
* @hook mcs_submit_url
*
* @param {string} $url Submission URL.
* @param {int} $event_id ID of the event being parsed.
*
* @return {string}
*/
$url = apply_filters( 'mcs_submit_url', $url, $event_id );
return ( $url ) ? $url : '';
}
/**
* Get the current deletion URL.
*
* @param int $event_id Event ID.
* @param boolean $confirm Confirmation URL if true.
*
* @return string URL.
*/
function mcs_delete_url( $event_id = false, $confirm = false ) {
$submit_id = get_option( 'mcs_submit_id' );
$nonce = wp_create_nonce( 'mc-delete-event' );
$referer = urlencode( mc_get_current_url() );
$url = admin_url( "admin.php?page=my-calendar-manage&mode=delete&event_id=$event_id&ref=$referer" );
if ( $submit_id ) {
$url = get_permalink( $submit_id );
if ( $url && $event_id ) {
$url = add_query_arg( 'mcs_id', $event_id, $url );
$url = add_query_arg( 'action', 'delete', $url );
$url = add_query_arg( '_mc_delete_event', $nonce, $url );
$url = ( $confirm ) ? add_query_arg( 'confirm', 'true', $url ) : $url;
}
}
/**
* Filter the event deletion URL.
*
* @hook mcs_delete_url
*
* @param {string} $url URL to delete an event.
* @param {int} $event_id Event ID.
*
* @return {string}
*/
return ( $url ) ? apply_filters( 'mcs_delete_url', $url, $event_id ) : '';
}
add_filter( 'mc_before_event_form', 'mcs_show_author', 10, 2 );
/**
* Show who submitted the current event in the admin manager.
*
* @param string $content Content before event form.
* @param int $event_id Event ID.
*
* @return string Info.
*/
function mcs_show_author( $content, $event_id ) {
if ( $event_id ) {
$event = mc_get_first_event( $event_id );
if ( ! is_object( $event ) ) {
return '';
}
$post_id = $event->event_post;
$author = get_post_meta( $post_id, '_submitter_details', true );
if ( is_array( $author ) ) {
$fname = ( isset( $author['first_name'] ) ) ? $author['first_name'] : '';
$lname = ( isset( $author['last_name'] ) ) ? $author['last_name'] : '';
$email = ( isset( $author['email'] ) && is_email( $author['email'] ) ) ? $author['email'] : '';
$date = get_the_time( mc_date_format(), $post_id );
$return = ( '' !== $email ) ? "<a href='mailto:$email'>" . trim( $fname . ' ' . $lname ) . "</a> ($email)" : "$fname $lname";
// Translators: 1. Who submitted the event, 2. date it was submitted.
$return = sprintf( __( 'Event submitted by %1$s on %2$s', 'my-calendar-pro' ), $return, $date );
$content .= "
<div class='ui-sortable meta-box-sortables'>
<div class='postbox'>
<div class='inside'>
<p class='submitter'>$return</p>
</div>
</div>
</div>";
}
}
return $content;
}
/**
* Save public submitter data in event post
*
* @param string $fname Author first name.
* @param string $lname Author last name.
* @param string $email Author email.
* @param object $event Event object.
*/
function mcs_save_author( $fname, $lname, $email, $event ) {
$post_ID = $event->event_post;
if ( ! $post_ID ) {
$post = map_deep( $_POST, 'sanitize_textarea_field' );
$post_ID = mc_event_post( 'add', $post, $event->event_id );
}
$add = array(
'first_name' => $fname,
'last_name' => $lname,
'email' => $email,
);
add_post_meta( $post_ID, '_submitter_details', $add );
}
add_filter( 'mc_filter_shortcodes', 'mcs_tag_author', 10, 4 );
/**
* Make submitter data available from templates
*
* @param array $e Array of event tags.
* @param object $event Event object.
*
* @return updated array.
*/
function mcs_tag_author( $e, $event ) {
$submitter = get_post_meta( $event->event_post, '_submitter_details', true );
if ( is_array( $submitter ) ) {
$e['public_first'] = ( isset( $submitter['first_name'] ) ) ? $submitter['first_name'] : '';
$e['public_last'] = ( isset( $submitter['last_name'] ) ) ? $submitter['last_name'] : '';
$e['public_email'] = ( isset( $submitter['email'] ) ) ? $submitter['email'] : '';
} else {
$e['public_first'] = '';
$e['public_last'] = '';
$e['public_email'] = '';
}
return $e;
}
/**
* Process required fields to check status.
*
* @param string $errors error message.
* @param array $submit Submitted data after processing.
*
* @return string
*/
function mcs_handle_required_fields( $errors, $submit ) {
// This requirement applies only to front-end submissions.
if ( ! is_admin() ) {
/**
* Filter required fields.
*
* @hook mcs_required_fields
*
* @param {array} $mcs_required_fields Array of fields keys required by forms.
*
* @return {array}
*/
$required_fields = apply_filters( 'mcs_required_fields', get_option( 'mcs_required_fields', array() ) );
$map = array(
'event_title' => 'event_title',
'end_date' => 'event_end',
'end_time' => 'event_endtime',
'event_host' => 'event_host',
'description' => 'event_desc',
'short_description' => 'event_short',
'event_link' => 'event_link',
'event_image' => 'event_image',
);
$labels = array(
'event_title' => 'Event Title',
'end_date' => 'End Date',
'end_time' => 'End Time',
'event_host' => 'Event Host',
'description' => 'Event Description',
'short_description' => 'Excerpt',
'event_link' => 'Event Link',
'event_image' => 'Event Image',
);
$ids = array(
'event_title' => 'mc_event_title',
'end_date' => 'mc_event_enddate',
'end_time' => 'mc_event_endtime',
'event_host' => 'e_host',
'description' => 'mc_event_description',
'short_description' => 'mc_event_short_description',
'event_link' => 'mc_event_link',
'event_image' => 'mc_event_image',
);
foreach ( $required_fields as $field ) {
if ( ! isset( $map[ $field ] ) ) {
continue;
}
$key = $map[ $field ];
$value = $submit[ $key ];
$label = $labels[ $field ];
$id = $ids[ $field ];
$link = "#$id";
if ( ! $value || 'Untitled Event' === $value ) {
// Translators: Label of required field.
$errors .= "<a href='$link'>" . sprintf( __( '%s is a required field.', 'my-calendar-pro' ), $label ) . '</a><br />';
}
}
$errors = mc_show_error( $errors, false );
}
return $errors;
}
add_filter( 'mc_fields_required', 'mcs_handle_required_fields', 10, 2 );
/**
* Filter selected submission notices to be more suitable for front-end usage.
*
* @param string $message Original message.
* @param string|false $code Notification code.
*
* @return string
*/
function mcs_show_notice( $message, $code = false ) {
if ( ! $code ) {
return $message;
}
if ( ! is_admin() ) {
switch ( $code ) {
case 'event-deleted':
$message = __( 'You\'ve deleted your event!', 'my-calendar-pro' );
break;
case 'event-updated':
$message = __( 'Your event has been updated.', 'my-calendar-pro' );
break;
case 'date-updated':
$message = __( 'The date of your event has been changed.', 'my-calendar-pro' );
break;
case 'new-event':
$message = __( 'Your event has been published!', 'my-calendar-pro' );
break;
case 'draft-saved':
$message = __( 'Thanks for submitting an event! Your event will be reviewed by an administrator.', 'my-calendar-pro' );
break;
default:
$message = $message;
}
}
return $message;
}
add_filter( 'mc_filter_notice', 'mcs_show_notice', 10, 2 );
/**
* Render My Calendar event submissions on configured page.
*
* @param string $content Post content.
*
* @return string
*/
function mcs_render_submit( $content ) {
$mcs_submit_id = get_option( 'mcs_submit_id' );
if ( is_singular() && get_queried_object_id() === (int) $mcs_submit_id ) {
if ( ! has_shortcode( $content, 'submit_event' ) ) {
$content .= mcs_submit_form( array(), '' );
}
}
return $content;
}
add_filter( 'the_content', 'mcs_render_submit' );