<?php
/**
* Events importer
*
* @category Features
* @package My Calendar Pro
* @author Joe Dolson
* @license GPLv2 or later
* @link https://www.joedolson.com/my-calendar-pro/
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Update importer settings.
*
* @param string $url Remote import URL.
* @param string $type Type of import(csv or ics).
* @param string $nonce Nonce.
*
* @return mixed string|boolean
*/
function mcs_importer_update( $url = false, $type = false, $nonce = false ) {
// save settings.
if ( isset( $_FILES['mcs_importer'] ) || isset( $_POST['mcs_remote_import'] ) || ( false !== $url && false !== $type ) ) {
$nonce = isset( $_POST['_wpnonce'] ) ? $_POST['_wpnonce'] : $nonce;
if ( ! wp_verify_nonce( $nonce, 'importer' ) ) {
return false;
}
$url = ( ( $url ) && false !== stripos( $url, 'mc-api' ) ) ? add_query_arg( 'file', 'false', $url ) : $url;
$constructed = false;
$delimiter = ',';
$type = ( ( isset( $_POST['mcs_remote_type'] ) && 'csv' === $_POST['mcs_remote_type'] ) || 'csv' === $type ) ? 'csv' : 'ics';
$url = ( isset( $_POST['mcs_remote_import'] ) ) ? str_replace( 'webcals://', 'http://', $_POST['mcs_remote_import'] ) : $url;
$event_cat = isset( $_POST['mcs_import_category'] ) ? absint( $_POST['mcs_import_category'] ) : false;
if ( isset( $_POST['mcs_import_limit'] ) ) {
$import_limit = sanitize_text_field( $_POST['mcs_import_limit'] );
update_option( 'mcs_import_limit', $import_limit );
}
if ( isset( $_POST['mcs_new_import_status'] ) ) {
$import_status = sanitize_text_field( $_POST['mcs_new_import_status'] );
update_option( 'mcs_new_import_status', $import_status );
}
if ( isset( $_POST['mcs_import_status'] ) && ! empty( $_POST['mcs_import_status'] ) ) {
// If import status set for this import, use that for import management.
$import_status = sanitize_text_field( $_POST['mcs_import_status'] );
}
if ( $event_cat || $import_status ) {
$import_conditions = array(
'event_cat' => $event_cat,
'import_status' => $import_status,
);
update_option( md5( $url ), $import_conditions );
}
$import_settings = get_option( md5( $url ) );
$event_cat = ( '' !== $import_settings && is_string( $import_settings ) ) ? absint( get_option( md5( $url ) ) ) : $event_cat;
$url = ( ! $url ) ? __( 'the current uploaded file', 'my-calendar-pro' ) : $url;
$schedule = ( isset( $_POST['mcs_schedule'] ) && '' !== $_POST['mcs_schedule'] ) ? sanitize_text_field( $_POST['mcs_schedule'] ) : false;
$offset = ( isset( $_POST['mcs_schedule_start'] ) ) ? sanitize_text_field( $_POST['mcs_schedule_start'] ) : 0;
$prev_schedule = false;
if ( $schedule ) {
if ( isset( $_POST['mcs_schedule_id'] ) ) {
$prev_schedule = explode( ',', sanitize_text_field( urldecode( $_POST['mcs_schedule_data'] ) ) );
$prev_schedule = array(
'ts' => $prev_schedule[6],
'url' => urlencode( $prev_schedule[0] ),
'format' => $prev_schedule[4],
);
delete_option( md5( $prev_schedule['url'] ) );
}
$scheduled = mcs_setup_schedule( $schedule, $url, $type, $offset, $prev_schedule );
if ( $prev_schedule ) {
wp_admin_notice(
__( 'Scheduled Import has been updated.', 'my-calendar-pro' ),
array(
'type' => 'success',
)
);
return false;
}
} else {
// Translators: Remote URL.
$scheduled = sprintf( __( 'One time import of the events from <code>%s</code>', 'my-calendar-pro' ), $url );
}
if ( isset( $_FILES['mcs_importer'] ) && ! empty( $_FILES['mcs_importer']['name'] ) ) {
$data = mcs_get_file_import( $_FILES, $event_cat );
$csv = $data['csv'];
$constructed = $data['constructed'];
} else {
$data = mcs_get_remote_import( $url, $event_cat, $type );
$csv = $data['csv'];
$constructed = $data['constructed'];
}
if ( ! $csv ) {
return false;
}
/**
* Separate each file into an index of an array and store the total
* number of rows that exist in the array. This assumes that
* each row of the CSV file is on a new line.
*/
$csv_rows = ( ! is_array( $csv ) ) ? explode( PHP_EOL, $csv ) : $csv;
// If there are blank lines, unset them in array.
foreach ( $csv_rows as $key => $row ) {
if ( empty( $row ) ) {
unset( $csv_rows[ $key ] );
}
}
$total_rows = count( $csv_rows );
// number of rows per file.
$number_per_row = 30;
// Store the title row. This will be written at the top of every file.
$title_row = $csv_rows[0];
if ( is_array( $title_row ) ) {
$title_row = implode( $delimiter, array_map( 'trim', $title_row ) );
} else {
$titles = explode( $delimiter, $title_row );
$titles = array_map( 'trim', $titles );
$title_row = implode( $delimiter, $titles );
}
/*
* Calculate the number of rows that will consist of $number_per_row, and then calculate
* the number of rows for the final file.
*/
$rows = ceil( $total_rows / $number_per_row );
// have to have at least one file.
$rows = ( 0 === $rows ) ? 1 : $rows;
$remaining_row = ( $total_rows % $number_per_row );
// Prepare to write out the files. This could just as easily be a for loop.
$file_counter = 0;
while ( 0 < $rows ) {
$csv_data = '';
/*
* Create the output file that will contain a title row and a set of ten rows.
* The filename is generated based on which file we're importing.
*/
$loops = ( 1 === $rows && 0 !== $remaining_row ) ? $remaining_row : $number_per_row;
// set a batch of rows into a string.
for ( $i = 1; $i < $loops; $i++ ) {
/**
* Read the value from the array and then remove from
* the array so it's not read again.
*/
if ( isset( $csv_rows[ $i ] ) && is_array( $csv_rows[ $i ] ) ) {
$data = implode( $delimiter, array_map( 'mcs_wrap_field', $csv_rows[ $i ] ) );
} elseif ( isset( $csv_rows[ $i ] ) && ! is_array( $csv_rows[ $i ] ) ) {
$data = $csv_rows[ $i ];
} else {
$data = false;
}
// extra PHP EOL will break data.
$csv_data .= ( $data && '' !== trim( $data ) ) ? str_replace( PHP_EOL, '', $data ) . PHP_EOL : PHP_EOL;
unset( $csv_rows[ $i ] );
}
/**
* Write out the file and then close the handle since we'll be opening
* a new one on the next iteration.
*/
if ( '' !== trim( $csv_data ) ) {
$csv_data = $title_row . PHP_EOL . $csv_data;
$output_file = fopen( trailingslashit( mcs_import_directory() ) . 'mcs_csv_' . $file_counter . '.csv', 'w' );
if ( is_bool( $output_file ) ) {
wp_admin_notice(
__( 'Cannot open CSV stream to prepare for import.', 'my-calendar-pro' ),
array(
'type' => 'error',
)
);
return false;
} else {
fwrite( $output_file, $csv_data );
fclose( $output_file );
}
/**
* Increase the file counter by one and decrement the rows,
* reset the keys for the rows of the array, and then reset the
* string of data to the title row
*/
++$file_counter;
}
--$rows;
$csv_rows = array_values( $csv_rows );
}
// lengthened expiration time on all transients for cron execution.
set_transient( 'mcs-number-of-files', $file_counter, 90 * $file_counter );
set_transient( 'mcs-parsed-files', 'true', 90 * $file_counter );
if ( $constructed ) {
// needed to change this delimiter to a single character to remove errors.
$delimiter = '|';
} else {
$delimiter = ',';
}
set_transient( 'mcs-delimiter', $delimiter, 90 * $file_counter );
$titles = explode( $delimiter, $title_row );
$imports = $total_rows - 1;
// Translators: Number of events to import.
$heading = sprintf( _n( 'Importing %s event.', 'Importing %s events.', $imports, 'my-calendar-pro' ), "<strong>$imports</strong>" );
$return = "<div class='notice notice-success'><p>$scheduled</p></div>
<h3>" . $heading . '</h3>
<p><strong>' . __( 'Mapping your data to My Calendar fields:', 'my-calendar-pro' ) . "</strong></p>
<ul class='mcs-import-data'>";
$odd = 0;
foreach ( $titles as $title ) {
// If CSV is UTF-8 with BOM, strip BOM.
$title = str_replace( "\xEF\xBB\xBF", '', trim( str_replace( '"', '', $title ) ) );
$display = ( '' === $title ) ? __( '(No field name)', 'my-calendar-pro' ) : $title;
$fields = mcs_option_fields();
$class = ( 1 === $odd ) ? 'odd' : 'even';
$keys = array_map( 'trim', array_keys( $fields ) );
if ( in_array( $title, $keys, true ) ) {
$target = $fields[ $title ];
$class .= '';
} else {
$target = ( '' !== $title ) ? __( 'Unrecognized field - will be ignored', 'my-calendar-pro' ) : false;
$class .= ' unknown';
}
if ( $target ) {
// Translators: Imported field title, target field title.
$return .= "<li class='$class'>" . sprintf( __( '“<strong>%1$s</strong>” to %2$s', 'my-calendar-pro' ), $display, $target ) . '</li>';
$odd = ( 1 === $odd ) ? 0 : 1;
}
}
unset( $display );
$return .= '</ul>';
$return .= "<button type='button' name='mcs_import_events' class='button-primary' type='button'>" . __( 'Import Events', 'my-calendar-pro' ) . "</button>
<div class='mcs-importer-progress' aria-live='assertive' aria-atomic='false'><span>" . __( 'Importing...', 'my-calendar-pro' ) . "<strong class='percent'></strong></span></div>";
return $return;
}
return false;
}
/**
* Fetch event data from a remote URL.
*
* @param string $url A remote URL.
* @param int $event_cat Event category to assign.
* @param string $type Type of data to expect.
*
* @return array
*/
function mcs_get_remote_import( $url, $event_cat, $type ) {
// remote file.
$constructed = false;
$response = wp_remote_get(
$url,
array(
'user-agent' => 'WordPress/My Calendar Importer; ' . home_url(),
)
);
$body = wp_remote_retrieve_body( $response );
if ( 'csv' === $type ) {
$csv = $body;
} else {
// converting ics requires file input.
$file = fopen( trailingslashit( mcs_import_directory() ) . 'mcs_csv_source.ics', 'w' );
fwrite( $file, $body );
fclose( $file );
$constructed = true;
$csv = mcs_convert_ics( trailingslashit( mcs_import_directory() ) . 'mcs_csv_source.ics', $event_cat );
unlink( trailingslashit( mcs_import_directory() ) . 'mcs_csv_source.ics' );
}
return array(
'csv' => $csv,
'constructed' => $constructed,
);
}
/**
* Read the contents of an uploaded file and convert to CSV.
*
* @param array $files A global $_FILES array.
* @param int $event_cat Event category ID for this import.
*
* @return array
*/
function mcs_get_file_import( $files, $event_cat ) {
// Read the contents of the file.
$path = $files['mcs_importer']['name'];
$ext = pathinfo( $path, PATHINFO_EXTENSION );
$rows = '';
$constructed = false;
if ( 'ics' === $ext ) {
$constructed = true;
$rows = mcs_convert_ics( $files['mcs_importer']['tmp_name'], $event_cat );
}
if ( '' === $rows ) {
$file = fopen( $_FILES['mcs_importer']['tmp_name'], 'r' );
while ( ( $row = fgetcsv( $file, 0, ',' ) ) !== false ) { // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
$csv[] = $row;
}
} else {
$csv = $rows;
}
return array(
'constructed' => $constructed,
'csv' => $csv,
);
}
/**
* Try to make sure all fields are delimited correctly
*
* @param string $content to delimit.
*
* @return wrapped string
*/
function mcs_wrap_field( $content ) {
return '"' . wptexturize( $content ) . '"';
}
/**
* Fix iCal params that aren't supported.
*
* @param string $check String to check for undesired charset parameters.
*
* @return string
*/
function mcs_fix_params( $check ) {
$check = str_replace( 'CHARSET=utf-8:', '', $check );
return $check;
}
/**
* Convert an .ics (iCal) formatted file into a CSV for importing
*
* @param string $file Path to file to convert.
* @param boolean|int $category Default category ID for this import.
*
* @return string CSV rows.
*/
function mcs_convert_ics( $file, $category = false ) {
require_once __DIR__ . '/classes/class.iCalReader.php';
$ics = new ical( $file );
if ( 0 === (int) $ics->event_count ) {
wp_admin_notice(
__( 'That event resource did not contain any events.', 'my-calendar-pro' ),
array(
'type' => 'warning',
)
);
return;
}
// is timezone from/to data in $ics->cal['VCALENDAR'].
$events = $ics->cal['VEVENT'];
$uids = array();
/**
* Override status of event to automatically approve publication during imports.
*
* @hook mcs_publish_imports
*
* @param {bool} $import true to import with published status. Default false.
* @param {array} $events Array of events being imported in this pull.
*
* @return {bool}
*/
$publish_imports = apply_filters( 'mcs_publish_imports', false, $events );
/**
* Set locations to import to location database. Note: ical data is unstructured, so locations will only import as a location name.
*
* @hook mcs_import_locations
*
* @param {bool} $import true to import with published status. Default false.
* @param {array} $events Array of events being imported in this pull.
*
* @return {bool}
*/
$import_locations = apply_filters( 'mcs_import_locations', false, $events );
// map each element to existing My Calendar fields.
$row_category = ( $category ) ? '|||event_category' : '';
$row_approval = ( 'true' === get_option( 'mcs_automatic_approval' ) || $publish_imports ) ? '|||event_approved' : '';
$row_import = ( $import_locations ) ? '|||copy_to_locations' : '';
$rows = 'event_begin|||event_time|||event_end|||event_endtime|||content|||event_label|||event_title|||event_group_id|||UID' . $row_category . $row_approval . $row_import . PHP_EOL;
if ( ! is_array( $events ) ) {
return;
}
$tz = array();
$i = 0;
foreach ( $events as $event ) {
if ( ! isset( $event['DTSTART'] ) ) {
continue;
}
if ( is_array( $event ) ) {
foreach ( $event as $key => $check ) {
// This has only come up once, and this fixes that case.
$event[ $key ] = ( is_array( $check ) ) ? map_deep( $check, 'mcs_fix_params' ) : mcs_fix_params( $check );
}
}
++$i;
$start = 'datetime';
$end = 'datetime';
if ( false !== stripos( $event['DTSTART'], 'VALUE=DATE:' ) ) {
$event['DTSTART'] = str_replace( 'VALUE=DATE:', '', $event['DTSTART'] );
$start = 'date';
}
// DTSTART;TZID=America/Detroit:20160426T153000.
if ( false !== stripos( $event['DTSTART'], 'TZID' ) ) {
$items = explode( ':', $event['DTSTART'] );
if ( isset( $items[2] ) && ! is_numeric( $items[1] ) ) {
$tz = explode( '=', $items[0] . $items[1] );
$date = $items[2];
} else {
$tz = explode( '=', $items[0] );
$date = $items[1];
}
/**
* Take date & timezone ID string and return timezone-adapted date
*/
$date = apply_filters( 'mcs_apply_timezone', $date, $tz );
$event['DTSTART'] = $date;
} elseif ( false !== stripos( $event['DTSTART'], 'Z' ) ) {
$date_start = mcs_date( 'Y-m-d H:i:s', strtotime( str_replace( array( 'T', 'Z' ), ' ', $event['DTSTART'] ) ), false );
$date = get_date_from_gmt( $date_start, 'Y-m-d\TH:i:s' );
$event['DTSTART'] = $date;
}
$event['DTSTART'] = apply_filters( 'mcs_import_dtstart', $event['DTSTART'], $event );
$event_begin = mcs_date( 'Y-m-d', strtotime( $event['DTSTART'] ), false );
$event_time = ( 'datetime' === $start ) ? mcs_date( 'H:i:00', strtotime( $event['DTSTART'] ), false ) : '00:00:00';
// If this event is before 'x' time restriction, ignore.
$import_limit = get_option( 'mcs_import_limit' );
if ( $import_limit ) {
$import_limit = ( 'past' === $import_limit ) ? 'now' : $import_limit;
if ( strtotime( $event_begin . ' ' . $event_time ) < strtotime( $import_limit ) ) {
continue;
}
}
$event_dtend = isset( $event['DTEND'] ) ? $event['DTEND'] : '';
if ( false !== stripos( $event_dtend, 'VALUE=DATE:' ) ) {
$event['DTEND'] = str_replace( 'VALUE=DATE:', '', $event['DTEND'] );
$end = 'date';
}
if ( false !== stripos( $event_dtend, 'TZID' ) ) {
$items = explode( ':', $event['DTEND'] );
$tz = explode( '=', $items[0] );
$date = $items[1];
/**
* Take date & timezone ID string and return timezone-adapted date
*/
$date = apply_filters( 'mcs_apply_timezone', $date, $tz );
$event['DTEND'] = $date;
} elseif ( false !== stripos( $event_dtend, 'Z' ) ) {
$date_end = mcs_date( 'Y-m-d H:i:s', strtotime( str_replace( array( 'T', 'Z' ), ' ', $event['DTEND'] ) ), false );
$date = get_date_from_gmt( $date_end, 'Y-m-d\TH:i:s' );
$event['DTEND'] = $date;
}
if ( ! isset( $event['DTEND'] ) ) {
$end_date_time = strtotime( $event_begin . ' ' . $event_time . ' + 1 hour' );
$event['DTEND'] = get_date_from_gmt( mcs_date( 'Y-m-d H:i:s', $end_date_time, false ), 'Y-m-d\TH:i:s' );
}
$event['DTEND'] = apply_filters( 'mcs_import_dtend', $event['DTEND'], $event );
$event_end = mcs_date( 'Y-m-d', strtotime( $event['DTEND'] ), false );
$event_endtime = ( 'datetime' === $end ) ? mcs_date( 'H:i:00', strtotime( $event['DTEND'] ), false ) : '23:59:59';
$time_diff = strtotime( $event_endtime ) - strtotime( $event_time );
// if time_diff = 1 second - subtract one day from date - this is an all day event.
if ( ( DAY_IN_SECONDS - 1 ) === $time_diff ) {
$event_end = mcs_date( 'Y-m-d', ( strtotime( $event_end ) - DAY_IN_SECONDS ), false );
}
$description = ( isset( $event['DESCRIPTION'] ) ) ? $event['DESCRIPTION'] : '';
$location = ( isset( $event['LOCATION'] ) ) ? $event['LOCATION'] : '';
$summary = ( isset( $event['SUMMARY'] ) ) ? $event['SUMMARY'] : '';
$uid = ( isset( $event['UID'] ) ) ? $event['UID'] : '';
// add UID field to event object; use to track for imports.
if ( in_array( $uid, $uids, true ) ) {
// If the UID already exists, this is an exploded recurring event.
// Generate a new UID, but use the same Group ID.
$uid = ( in_array( $uid, $uids, true ) ) ? $uid . '-' . $i : $uid;
$group_id = mc_group_id();
} else {
$group_id = 'default';
$uids[] = $uid;
}
$event_category = ( $category ) ? "|||\"$category\"" : '';
$event_approval = ( 'true' === get_option( 'mcs_automatic_approval' ) || $publish_imports ) ? '|||\"1\"' : '';
$location_import = ( $import_locations ) ? '|||\"1\"' : '';
$row = "\"$event_begin\"|||\"$event_time\"|||\"$event_end\"|||\"$event_endtime\"|||\"$description\"|||\"$location\"|||\"$summary\"|||\"$group_id\"|||\"$uid\"" . $event_category . $event_approval . $location_import . PHP_EOL;
$rows .= $row;
}
unset( $event );
return trim( $rows );
}
add_action( 'admin_enqueue_scripts', 'mcs_importer_enqueue_scripts' );
/**
* Enqueue importer scripts.
*/
function mcs_importer_enqueue_scripts() {
global $mcs_version;
if ( isset( $_GET['page'] ) && 'my-calendar-submissions' === $_GET['page'] ) {
wp_enqueue_script( 'mcs.import', plugins_url( 'js/import.js', __FILE__ ), array( 'jquery', 'wp-a11y', 'wp-i18n' ), $mcs_version, true );
}
}
add_filter( 'mcs_settings_tabs', 'mcs_importer_tabs' );
/**
* Add importer tab.
*
* @param array $tabs Array of added tabs.
*
* @return array
*/
function mcs_importer_tabs( $tabs ) {
$tabs['importer'] = __( 'Importer', 'my-calendar-pro' );
return $tabs;
}
add_filter( 'mcs_settings_panels', 'mcs_importer_settings' );
/**
* Add settings page.
*
* @param array $panels Existing panels.
*/
function mcs_importer_settings( $panels ) {
// delete any old data from incomplete imports.
$update = mcs_importer_update();
$crons = mcs_import_schedules();
$importer = '
<h2>' . __( 'Import Events', 'my-calendar-pro' ) . '</h2>
<div class="inside">
' . $update;
if ( false === $update ) {
if ( isset( $_GET['test_import'] ) ) {
mcs_import_files();
}
$import_limit = get_option( 'mcs_import_limit' );
$import_status = get_option( 'mcs_new_import_status' );
$importer .= '
<fieldset class="mcs-importer">
<legend>' . __( 'Default Importer Settings', 'my-calendar-pro' ) . '</legend>
<p>
<label for="mcs_import_limit">' . __( 'Restrict event import by time:', 'my-calendar-pro' ) . '</label>
<select name="mcs_import_limit" id="mcs_import_limit">
<option value="">' . __( 'Import All Events', 'my-calendar-pro' ) . '</option>
<option value="past"' . selected( 'past', $import_limit, false ) . '>' . __( 'Ignore events in the past', 'my-calendar-pro' ) . '</option>
<option value="-1 month"' . selected( '-1 month', $import_limit, false ) . '>' . __( 'Ignore events more than one month in the past', 'my-calendar-pro' ) . '</option>
<option value="-1 year"' . selected( '-1 year', $import_limit, false ) . '>' . __( 'Ignore events more than one year in the past', 'my-calendar-pro' ) . '</option>
</select>
</p>
<p>
<label for="mcs_new_import_status">' . __( 'Default imported event status', 'my-calendar-pro' ) . '</label>
<select name="mcs_new_import_status" id="mcs_new_import_status" aria-describedby="mcs_import_status_description">
<option value="">--</option>
<option value="1"' . selected( 1, $import_status, false ) . '>' . __( 'Publish', 'my-calendar-pro' ) . '</option>
<option value="0"' . selected( 0, $import_status, false ) . '>' . __( 'Draft', 'my-calendar-pro' ) . '</option>
<option value="2"' . selected( 2, $import_status, false ) . '>' . __( 'Trash', 'my-calendar-pro' ) . '</option>
</select>
<span class="description" id="mcs_import_status_description">' . __( 'Imported events that set the <code>event_approved</code> status will override this setting.', 'my-calendar-pro' ) . '</span>
</p>
</fieldset>
<p>
<label for="mcs_importer_mode">' . __( 'Upload File (.csv or .ics)', 'my-calendar-pro' ) . '</label>
<input type="file" name="mcs_importer" id="mcs_importer_mode" />
</p>
<fieldset class="mcs-importer">
<legend>' . __( 'Remote/scheduled import', 'my-calendar-pro' ) . '</legend>
<p>
<label for="mcs_remote_import">' . __( 'Import from URL', 'my-calendar-pro' ) . '</label><br />
<input type="url" name="mcs_remote_import" id="mcs_remote_import" class="widefat" />
</p>
<p>
<input type="radio" name="mcs_remote_type" id="mcs_remote_type_ics" value="ics" /> <label for="mcs_remote_type_ics">' . __( 'iCal format (.ics)', 'my-calendar-pro' ) . '</label> <input type="radio" name="mcs_remote_type" id="mcs_remote_type_csv" value="csv" checked="checked" /> <label for="mcs_remote_type_csv">' . __( 'Character separated values (.csv)', 'my-calendar-pro' ) . '</label>
</p>
<p class="import_category">
<label for="mcs_import_category">' . __( 'Import to category:', 'my-calendar-pro' ) . '</label>
<select name="mcs_import_category" id="mcs_import_category">
<option value="">' . __( 'Default', 'my-calendar-pro' ) . '</option>
' . mc_category_select( false, true, false ) . '
</select>
</p>
<p class="import_status">
<label for="mcs_import_status">' . __( 'Default status', 'my-calendar-pro' ) . '</label>
<select name="mcs_import_status" id="mcs_import_status">
<option value="">--</option>
<option value="1">' . __( 'Publish', 'my-calendar-pro' ) . '</option>
<option value="0">' . __( 'Draft', 'my-calendar-pro' ) . '</option>
<option value="2">' . __( 'Trash', 'my-calendar-pro' ) . '</option>
</select>
</p>
<p>
<label for="mcs_schedule">' . __( 'Remote import frequency', 'my-calendar-pro' ) . '</label>
<select name="mcs_schedule" id="mcs_schedule">
<option value="">' . __( 'One time import', 'my-calendar-pro' ) . '</option>';
// note: if a schedule is set, show checkbox to delete existing schedule and create new.
$schedules = wp_get_schedules();
foreach ( $schedules as $key => $schedule ) {
$importer .= "<option value='$key'>$schedule[display]</option>";
}
$importer .= '
</select>
</p>
<p>
<label for="mcs_schedule_start">' . __( 'Remote import start time', 'my-calendar-pro' ) . '</label>
<input type="time" name="mcs_schedule_start" id="mcs_schedule_start" value="' . esc_attr( gmdate( 'H:i', mc_date() + 300 ) ) . '">
</p>
</fieldset>
{submit}';
}
$importer .= '
<div role="alert"><button type="button" class="button-primary mcs-reset">' . __( 'Import more events', 'my-calendar-pro' ) . '</button></div>
</div>';
$panels['importer']['after_form'] = $crons;
$panels['importer']['content'] = $importer;
$panels['importer']['label'] = __( 'Import Events', 'my-calendar-pro' );
return $panels;
}
add_filter( 'cron_schedules', 'mcs_custom_schedules' );
/**
* Add custom cron schedules.
*
* @param array $schedules Existing schedules.
*
* @return array new schedules.
*/
function mcs_custom_schedules( $schedules ) {
$schedules = apply_filters( 'mcs_cron_schedules', $schedules );
$schedules['weekly'] = array(
'interval' => 604800,
'display' => __( 'Once Weekly', 'my-calendar-pro' ),
);
$schedules['monthly'] = array(
'interval' => 2635200,
'display' => __( 'Once Monthly', 'my-calendar-pro' ),
);
return $schedules;
}
/**
* Set up import schedule.
*/
function mcs_import_schedules() {
$cron = _get_cron_array();
$schedules = wp_get_schedules();
$date_format = _x( 'M j, Y @ G:i', 'Scheduled imports date format', 'my-calendar-pro' );
$clear_queue = wp_nonce_url( admin_url( 'admin.php?page=my-calendar-submissions&mcsimport=clear#mcs_importer_tab' ) );
$schedule = '';
$jobs = '';
$notice = '';
$offset = ( 60 * 60 * get_option( 'gmt_offset' ) );
foreach ( $cron as $ts => $cronhooks ) {
foreach ( (array) $cronhooks as $hook => $events ) {
$i = 0;
foreach ( (array) $events as $event ) {
if ( 'mcsimport' === $hook ) {
++$i;
if ( count( $event['args'] ) ) {
$url = $event['args']['url'];
$format = $event['args']['format'];
$schedule = ( '' !== $event['schedule'] ) ? $event['schedule'] : '<em>' . __( 'Currently processing import', 'my-calendar-pro' ) . '</em>';
}
$id = md5( implode( '', $event['args'] ) );
if ( ( isset( $_GET['mcsimport'] ) && 'clear' === $_GET['mcsimport'] ) && ( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'] ) ) ) {
wp_unschedule_event(
$ts,
$hook,
array(
'url' => $url,
'format' => $format,
'iteration' => 0,
)
);
$notice = "<div id='message' class='notice notice-success'><p>" . sprintf( __( 'Import schedules have been cleared.', 'my-calendar-pro' ) ) . '</p></div>';
} elseif ( isset( $_GET['mcsdelete'] ) && $id === $_GET['mcsdelete'] ) {
wp_unschedule_event(
$ts,
$hook,
array(
'url' => $url,
'format' => $format,
'iteration' => 0,
)
);
$notice = "<div id='message' class='notice notice-success'><p>" . __( 'Scheduled import has been deleted.', 'my-calendar-pro' ) . '</p></div>';
} else {
$time_diff = ( $ts < mc_date() ) ? __( 'Missed Schedule', 'my-calendar-pro' ) : '~' . human_time_diff( $ts + $offset, mc_date() ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
$import_settings = get_option( md5( $url ) );
$event_cat = false;
$import_status = get_option( 'mcs_new_import_status' );
if ( $import_settings ) {
$event_cat = ( is_string( $import_settings ) ) ? $import_settings : $import_settings['event_cat'];
$import_status = ( ! is_string( $import_settings ) ) ? $import_settings['import_status'] : $import_status;
}
$category = ( $event_cat ) ? '(' . mc_get_category_detail( $event_cat, 'category_name' ) . ')' : '';
$status = ( $import_status ) ? mc_event_states_label( $import_status ) : '';
$status = ( $status ) ? '<br>' . esc_html__( 'Status when imported:', 'my-calendar-pro' ) . ' <strong>' . $status . '</strong>' : '';
$time = gmdate( 'H:i', $ts );
$values = json_encode( array( $url, $time, $schedule, $event_cat, $format, $import_status, $ts ) );
$jobs .= "
<tr>
<th scope='row'>" . wp_date( $date_format, ( $ts + $offset ) ) . '<br /><small>(' . $time_diff . ")</small></th>
<td><code>$url</code> $category $status</td>
<td>$schedule</td>
<td>
<a class='button-secondary delete' href='" . esc_url( add_query_arg( 'mcsdelete', $id, admin_url( 'admin.php?page=my-calendar-submissions' ) ) ) . "#mcs_importer_tab'>" . __( 'Delete', 'my-calendar-pro' ) . "</a>
<button type='button' data-value='" . esc_attr( wp_unslash( $values ) ) . "' class='button-secondary edit-import-schedule' href='" . esc_url( add_query_arg( 'mcsedit', $id, admin_url( 'admin.php?page=my-calendar-submissions' ) ) ) . "#mcs_importer_tab'>" . __( 'Edit', 'my-calendar-pro' ) . '</button>
</td>
</tr>';
}
}
}
}
}
$approval_note = ( 'true' === get_option( 'mcs_automatic_approval' ) ) ? __( 'Events imported will be automatically published.', 'my-calendar-pro' ) : __( 'Events will be imported as drafts unless otherwise indicated.', 'my-calendar-pro' );
$schedules = "<div class='ui-sortable meta-box-sortables'><div class='postbox'><h2>" . __( 'Scheduled Imports', 'my-calendar-pro' ) . "</h2>
<div class='inside'>
<p>$approval_note</p>
<table class='widefat fixed mcs-importer-schedule'>
<thead>
<tr>
<th scope='col'>" . __( 'Next', 'my-calendar-pro' ) . "</th>
<th scope='col'>" . __( 'URL', 'my-calendar-pro' ) . "</th>
<th scope='col'>" . __( 'Frequency', 'my-calendar-pro' ) . "</th>
<th scope='col'>" . __( 'Manage', 'my-calendar-pro' ) . '</th>
</tr>
</thead>
<tbody>';
if ( '' !== $jobs ) {
$schedules .= $jobs;
}
$schedules .= "
</tbody>
</table>
<p><a href='$clear_queue'>" . __( 'Clear Schedule', 'my-calendar-pro' ) . '</a></p>
</div>
</div>
</div>';
return ( '' === $jobs ) ? $notice . '<p>' . __( 'No event import schedules are currently set up.', 'my-calendar-pro' ) . '</p>' : $notice . $schedules;
}
/**
* Schedule custom importer event.
*
* @param string $schedule Type of schedule.
* @param string $url URL to query.
* @param string $format Type of data at URL.
* @param string|int $offset Time when initial schedule should start. `0` for now.
* @param array|bool $scheduled Existing schedule if updating schedule.
*
* @return string message.
*/
function mcs_setup_schedule( $schedule, $url, $format, $offset = 0, $scheduled = false ) {
$schedules = wp_get_schedules();
$current = $schedules[ $schedule ];
$offset = ( $offset ) ? strtotime( $offset ) - strtotime( '00:00:00' ) : 0;
$start = mc_date( 'Y-m-d', false, false );
$timestamp = strtotime( $start ) + $offset;
$args = array(
'url' => $url,
'format' => $format,
'iteration' => 0,
);
if ( $scheduled ) {
// Remove the previous version of this import schedule.
wp_unschedule_event(
$scheduled['ts'],
'mcsimport',
array(
'url' => $scheduled['url'],
'format' => $scheduled['format'],
'iteration' => 0,
)
);
}
wp_schedule_event( $timestamp, $schedule, 'mcsimport', $args );
// Translators: 1) Schedule frequency, 2) remote URL.
return sprintf( __( '%1$s import of the events at <code>%2$s</code> has been scheduled. You should still complete the current import.', 'my-calendar-pro' ), $current['display'], $url );
}
register_deactivation_hook( __FILE__, 'mcs_deactivate_cron' );
/**
* Deactivate cron job on deactivation.
*/
function mcs_deactivate_cron() {
wp_clear_scheduled_hook( 'mcsimport' );
}
add_action( 'mcsimport', 'mcs_import_schedule', 10, 3 );
/**
* Set up import schedule for larger than max events.
*
* @param string $url URL to import.
* @param string $format Format of file to import.
* @param int $iteration Number of this execution.
*/
function mcs_import_schedule( $url, $format, $iteration ) {
// mapped arguments to not require POST variables, still need to handle nonce.
if ( 0 === $iteration ) {
$nonce = wp_create_nonce( 'importer' );
mcs_importer_update( $url, $format, $nonce );
}
if ( mcs_import_files( $iteration, $url ) ) {
$iteration = $iteration + 1;
wp_schedule_single_event(
time() + 60,
'mcsimport',
array(
'url' => $url,
'format' => $format,
'iteration' => $iteration,
)
);
}
}
/**
* Setup the hook for the Ajax request.
*
* @param int $i Repetition.
* @param string $url Source URL for a scheduled import.
*
* @return bool
*/
function mcs_import_files( $i = 0, $url = '' ) {
set_transient( "mcs-parsing-$i", 'true', 90 );
$content = array();
$defaults = mcs_default_event_values();
$number_of_files = get_transient( 'mcs-number-of-files' );
$delimiter = get_transient( 'mcs-delimiter' );
$return = true;
if ( ! $delimiter || ! $number_of_files ) {
return false;
}
if ( $i < $number_of_files ) {
$filename = trailingslashit( mcs_import_directory() ) . 'mcs_csv_' . $i . '.csv';
if ( file_exists( $filename ) ) {
$file = fopen( $filename, 'r' );
// $content = fread( $input_file, filesize( $filename ) );.
// $f = file( $filename );.
while ( ( $row = fgetcsv( $file, 0, $delimiter ) ) !== false ) { // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
// at least two fields are required to import.
if ( ! empty( $row ) && count( $row ) > 1 ) {
$content[] = $row;
}
}
$array = mcs_translate_csv( $content, $delimiter );
unset( $content );
fclose( $file );
foreach ( $array as $event ) {
if ( ! is_array( $event ) || ( isset( $event['event_title'] ) && 'event_title' === $event['event_title'] ) ) {
continue;
}
if ( isset( $event['event_group_id'] ) && 'default' === $event['event_group_id'] ) {
unset( $event['event_group_id'] );
}
$event = array_merge( $defaults, $event );
if ( isset( $event['event_desc'] ) ) {
// If contains decoded line breaks.
$event['event_desc'] = str_replace( '\r\n', PHP_EOL, $event['event_desc'] );
}
// Verify if author exists.
$author_exists = false;
if ( isset( $event['event_author'] ) ) {
if ( is_numeric( $event['event_author'] ) ) {
$author_exists = get_user_by( 'ID', $event['event_author'] );
} else {
if ( is_email( $event['event_author'] ) ) {
$author_exists = get_user_by( 'email', $event['event_author'] );
} else {
$author_exists = get_user_by( 'slug', $event['event_author'] );
}
}
}
if ( ! $author_exists ) {
$event['event_author'] = apply_filters( 'mc_event_author_passed_not_exists', 0, $event );
}
// Verify if category exists & fetch category ID if a string passed.
$category_exists = false;
if ( is_numeric( $event['event_category'] ) ) {
$category_exists = mcs_category_exists( $event['event_category'] );
}
$label = ( isset( $event['event_label'] ) ) ? $event['event_label'] : '';
// If no label, don't copy.
if ( '' !== $label ) {
// If location already exists, get location ID.
$posts = get_posts(
array(
's' => $label,
'post_type' => 'mc-locations',
'numberposts' => 1,
)
);
if ( ! empty( $posts ) ) {
if ( $posts[0]->post_title === $label ) {
$location_post = $posts[0]->ID;
$location = mc_get_location_id( $location_post );
$event['location_preset'] = $location;
}
}
}
if ( isset( $event['event_category'] ) && ! $category_exists ) {
// event category is not numeric, so check database to see if exists.
$cat_id = mcs_category_by_name( $event['event_category'] );
if ( ! $cat_id ) {
$color = false;
$icon = false;
$private = false;
if ( isset( $event['category_color'] ) ) {
$color = $event['category_color'];
}
if ( isset( $event['category_icon'] ) ) {
$icon = $event['category_icon'];
}
if ( isset( $event['category_private'] ) ) {
$private = $event['category_private'];
}
$cat_id = ( '' === $event['event_category'] ) ? 1 : mcs_insert_category( $event['event_category'], $color, $icon, $private );
}
$event['event_category'] = $cat_id;
}
// If a non-numeric value is used in the location preset, move it to event_label if that is not set.
if ( isset( $event['location_preset'] ) && ! is_numeric( $event['location_preset'] ) ) {
if ( ! isset( $event['event_label'] ) || empty( $event['event_label'] ) ) {
$event['event_label'] = $event['location_preset'];
}
unset( $event['location_preset'] );
}
$mcs_guid_event = false;
if ( isset( $event['UID'] ) ) {
$mcs_guid_event = mcs_guid_exists( $event['UID'], $event );
}
if ( $mcs_guid_event ) {
mcs_update_event( $event, $mcs_guid_event );
} else {
mcs_import_event( $event, $url );
}
}
unset( $event );
// update count of parsed files; close & delete input file.
if ( file_exists( $filename ) ) {
unlink( $filename );
}
} else {
$return = false;
}
$return = true;
} else {
// delete data about imports.
delete_transient( 'mcs-parsed-files' );
delete_transient( 'mcs-number-of-files' );
delete_transient( 'mcs-delimiter' );
$return = false;
}
delete_transient( "mcs-parsing-$i" );
return $return;
}
add_action( 'wp_ajax_mcs_import_files', 'mcs_import_files' );
/**
* See if this event has already been imported & return event ID.
*
* @param string $guid Unique ID.
* @param object $event Event object to examine.
*
* @return bool|int
*/
function mcs_guid_exists( $guid, $event ) {
// If the event source doesn't maintain a persistent UID, the next best option may be some other field.
$guid = apply_filters( 'mcs_alternate_guid', $guid, $event );
// check for existing $guid.
$post_ID = false;
$args = array(
'post_type' => 'mc-events',
'posts_per_page' => 1,
'meta_query' => array(
array(
'key' => '_mc_guid',
'value' => $guid,
),
),
);
$posts = get_posts( $args );
if ( ! empty( $posts ) && is_object( $posts[0] ) ) {
$post_ID = $posts[0]->ID;
}
$event_id = get_post_meta( $post_ID, '_mc_event_id', true );
// If this event doesn't return a My Calendar object, it's leftover.
if ( ! $event_id || ! mc_get_event_core( $event_id ) ) {
return false;
}
return $event_id;
}
add_action( 'wp_ajax_mcs_get_import_status', 'mcs_get_import_status' );
/**
* Define the hook for getting import status.
*/
function mcs_get_import_status() {
/*
* $progress indicates how far we are during the import,
* -1 indicates that we're done
*/
if ( false !== get_transient( 'mcs-parsed-files' ) ) {
$parsed_files = floatval( get_transient( 'mcs-parsed-files' ) );
$total_files = floatval( get_transient( 'mcs-number-of-files' ) );
if ( 0 !== (int) $total_files ) {
// prevent duplicate imports.
if ( 'true' === $parsed_files ) {
$parsed_files = 0;
}
if ( 'true' !== get_transient( "mcs-parsing-$parsed_files" ) ) {
mcs_import_files( $parsed_files );
// Update data about imports.
set_transient( 'mcs-parsed-files', $parsed_files + 1, 90 );
}
$progress = $parsed_files / $total_files;
// Return progress values.
if ( 1 === (int) $progress ) {
die( '-1' );
}
die( esc_html( $progress ) );
} else {
die( '0' );
}
} else {
die( '-1' );
}
}
/**
* Switch status types to integers.
*
* @param string $type Status type (publish, trash, draft).
*
* @return int
*/
function mcs_get_status( $type ) {
switch ( $type ) {
case 'draft':
$return = 0;
break;
case 'publish':
$return = 1;
break;
case 'trash':
$return = 2;
break;
default:
$return = 0;
}
return $return;
}
/**
* Take parsed array and import event if new event
*
* @param array $event Array of event data.
* @param string $url Source URL for scheduled imports.
*
* @return int
*/
function mcs_import_event( $event, $url = '' ) {
// When importing an event, use the new import status unless an event approval value is set.
$default_approval = get_option( 'mcs_new_import_status', '' );
$source_config = ( $url ) ? get_option( md5( $url ) ) : false;
if ( $source_config ) {
$source_approval = ( is_array( $source_config ) ) ? $source_config['import_status'] : $default_approval;
} else {
$source_approval = $default_approval;
}
$event_approval = isset( $event['event_approved'] ) ? $event['event_approved'] : $source_approval;
// If the value passed is a string label, attempt to get integer state.
$event_approval = ( is_string( $event_approval ) && ! is_numeric( $event_approval ) ) ? mcs_get_status( $event_approval ) : $event_approval;
if ( ! isset( $event['event_approved'] ) && is_numeric( $event_approval ) ) {
$event['event_approved'] = $default_approval;
}
if ( isset( $event['event_approved'] ) && empty( $event['event_approved'] ) ) {
unset( $event['event_approved'] );
}
/**
* Filter imported event to customize what gets added to database. Return false to skip event.
*
* @hook mcs_imported_event
*
* @param {array} $event Array of event data passed to `mc_check_data`.
*
* @return {array|false}
*/
$event = apply_filters( 'mcs_imported_event', $event );
if ( false === $event ) {
return;
}
$check = mc_check_data( 'add', $event, 0, true );
$event_id = false;
if ( $check[0] ) {
$response = my_calendar_save( 'add', $check );
$event_id = $response['event_id'];
$response = $response['message'];
$e = mc_get_first_event( $event_id );
$post_id = $e->event_post;
if ( isset( $event['event_image'] ) && '' !== $event['event_image'] ) {
$image = media_sideload_image( $event['event_image'], $post_id, null, 'id' );
if ( ! is_wp_error( $image ) ) {
set_post_thumbnail( $post_id, $image );
}
}
if ( isset( $event['event_location'] ) && '' !== $event['event_location'] ) {
$location_id = (int) $event['event_location'];
update_post_meta( $post_id, '_mc_event_location', $location_id );
mc_update_event( 'event_location', $location_id, $event_id );
}
if ( isset( $event['UID'] ) ) {
$uid = apply_filters( 'mcs_set_alternate_guid', $event['UID'], $event );
update_post_meta( $post_id, '_mc_guid', $uid );
}
if ( isset( $event['language'] ) ) {
$available = get_available_languages();
if ( in_array( $event['language'], $available, true ) ) {
update_post_meta( $post_id, '_event_language', $event['language'] );
}
}
}
return $event_id;
}
/**
* Take parsed event and update existing event if identified as already existing
*
* @param array $event Event data.
* @param int $event_edit Event ID.
*
* @return int
*/
function mcs_update_event( $event, $event_edit ) {
/**
* Filter imported event data that is updating an existing event.
*
* @hook mcs_updated_event
*
* @param {array} $event Event data.
* @param {int} $event_edit Event ID.
*
* @return {array}
*/
$event = apply_filters( 'mcs_updated_event', $event, $event_edit );
// If the event's status isn't set in the source, set it to the current value so it isn't changed.
$event_approved = ( isset( $event['event_approved'] ) ) ? $event['event_approved'] : false;
$event_approved = ( isset( $event['event_status'] ) ) ? $event['status'] : $event_approved;
// If the value passed is a string label, attempt to get integer state.
$event_approved = ( is_string( $event_approved ) && ! is_numeric( $event_approved ) ) ? mcs_get_status( $event_approved ) : $event_approved;
if ( ! $event_approved ) {
$event['event_approved'] = mc_get_data( 'event_approved', $event_edit );
} else {
$event['event_approved'] = $event_approved;
}
$check = mc_check_data( 'add', $event, 0, true );
$event_id = false;
if ( $check[0] ) {
// Grant the importer permissions to edit events.
add_filter( 'mc_api_can_edit_event', 'mcs_api_can_edit_event', 10, 2 );
$response = my_calendar_save( 'edit', $check, $event_edit );
// Remove permissions.
remove_filter( 'mc_api_can_edit_event', 'mcs_api_can_edit_event', 10, 2 );
$event_id = $response['event_id'];
$response = $response['message'];
if ( isset( $event['event_image'] ) && '' !== $event['event_image'] ) {
$e = mc_get_first_event( $event_id );
$post_id = $e->event_post;
$image = media_sideload_image( $event['event_image'], $post_id, null, 'id' );
if ( ! is_wp_error( $image ) ) {
set_post_thumbnail( $post_id, $image );
}
}
if ( isset( $event['language'] ) ) {
$available = get_available_languages();
if ( in_array( $event['language'], $available, true ) ) {
update_post_meta( $post_id, '_event_language', $event['language'] );
}
}
}
return $event_id;
}
/**
* Grant permissions for importer to edit event.
*
* @param bool $default_permission Default permissions value.
* @param int $event_id ID of event being edited.
*
* @return true
*/
function mcs_api_can_edit_event( $default_permission, $event_id ) {
// Need to refine this to provide some filtering & awareness of whether API is firing.
return true;
}
/**
* Array of field descriptions to display in import process
*/
function mcs_option_fields() {
$return = apply_filters(
'mcs_option_fields',
array(
// Event data.
'event_title' => __( 'Title', 'my-calendar-pro' ),
'event_begin' => __( 'Starting Date', 'my-calendar-pro' ),
'occur_begin' => __( 'Starting Date/Time', 'my-calendar-pro' ),
'event_end' => __( 'Ending Date', 'my-calendar-pro' ),
'occur_end' => __( 'Ending Date/Time', 'my-calendar-pro' ),
'event_time' => __( 'Starting Time', 'my-calendar-pro' ),
'event_endtime' => __( 'Ending Time', 'my-calendar-pro' ),
'content' => __( 'Description', 'my-calendar-pro' ),
'event_desc' => __( 'Description', 'my-calendar-pro' ),
'event_short' => __( 'Excerpt', 'my-calendar-pro' ),
'event_link' => __( 'External event URL', 'my-calendar-pro' ),
'event_link_expires' => __( 'Link expiration', 'my-calendar-pro' ),
'event_recur' => __( 'Recurring frequency period', 'my-calendar-pro' ),
// above codes: S - single,D - day,E - weekdays,W - weekly,M - month/date,U - month/day,Y - year.
'event_repeats' => __( 'Number of repetitions', 'my-calendar-pro' ),
// above means: 4 = event repeats 4 times, for a total of 5 occurrences.
'event_every' => __( 'Recurrence frequency multiplier', 'my-calendar-pro' ),
// above represents: D + 3 == every 3 days, 2 + W == every two weeks.
'event_image' => __( 'Event Image URL', 'my-calendar-pro' ),
'event_allday' => __( 'Event is all-day', 'my-calendar-pro' ),
'event_author' => __( 'Author ID, username, or email.', 'my-calendar-pro' ),
'event_approved' => __( 'Publishing status', 'my-calendar-pro' ),
'event_category' => __( 'Category Name or ID', 'my-calendar-pro' ),
'category_color' => __( 'Category Color', 'my-calendar-pro' ),
'category_icon' => __( 'Category Icon', 'my-calendar-pro' ),
'category_private' => __( 'Category Privacy status', 'my-calendar-pro' ),
'event_fifth_week' => __( 'Omit week 5 recurrences', 'my-calendar-pro' ),
'event_holiday' => __( 'Cancel on Holidays', 'my-calendar-pro' ),
'event_group_id' => __( 'Event Group ID', 'my-calendar-pro' ),
'event_span' => __( 'Event spans multiple days', 'my-calendar-pro' ),
'event_hide_end' => __( 'Hide end date', 'my-calendar-pro' ),
// Ticketing/Registration data.
'event_open' => __( 'Obsolete field; will be ignored', 'my-calendar-pro' ),
'event_status' => __( 'Event Status', 'my-calendar-pro' ),
'event_flagged' => __( 'Event Flagged as Spam', 'my-calendar-pro' ),
'event_tickets' => __( 'Event Tickets Link', 'my-calendar-pro' ),
'event_registration' => __( 'Event Registration Info', 'my-calendar-pro' ),
'event_host' => __( 'Event Host ID', 'my-calendar-pro' ),
'events_access' => __( 'Event Accessibility Data', 'my-calendar-pro' ), // Event access features.
// location data.
'location_preset' => __( 'Location ID', 'my-calendar-pro' ),
'event_label' => __( 'Location Label', 'my-calendar-pro' ),
'event_street' => __( 'Location Street', 'my-calendar-pro' ),
'event_street2' => __( 'Location Street (2)', 'my-calendar-pro' ),
'event_city' => __( 'Location City', 'my-calendar-pro' ),
'event_state' => __( 'Location State', 'my-calendar-pro' ),
'event_postcode' => __( 'Location Postcode', 'my-calendar-pro' ),
'event_region' => __( 'Location Region', 'my-calendar-pro' ),
'event_country' => __( 'Location Country', 'my-calendar-pro' ),
'event_url' => __( 'Location URL', 'my-calendar-pro' ),
'event_phone' => __( 'Location Phone Number', 'my-calendar-pro' ),
'event_phone2' => __( 'Alternate Location Phone', 'my-calendar-pro' ),
'event_longitude' => __( 'Location longitude', 'my-calendar-pro' ),
'event_latitude' => __( 'Location latitude', 'my-calendar-pro' ),
'event_zoom' => __( 'Location map zoom level', 'my-calendar-pro' ),
'event_location' => __( 'Location ID in locations table', 'my-calendar-pro' ),
'event_access' => __( 'Location access features', 'my-calendar-pro' ),
// meta data.
'UID' => __( 'Unique event ID. If an event exists with this UID already, it will be updated instead of added.', 'my-calendar-pro' ),
'occur_id' => __( 'Unique date ID in source calendar', 'my-calendar-pro' ),
'event_added' => __( 'Date this event was added to source calendar', 'my-calendar-pro' ),
'language' => __( 'Language this event listing is written in', 'my-calendar-pro' ),
)
);
return $return;
}
/**
* Turn a CSV into an array of events for importing
*
* @param array $content Array of rows from a CSV document.
* @param string $delimiter What separates content.
* @param string $enclosure What encloses complex string content.
* @param string $escape What's the escape character.
* @param string $terminator End of line indicator.
*
* @return array
*/
function mcs_translate_csv( $content, $delimiter = ';', $enclosure = '"', $escape = '\\', $terminator = PHP_EOL ) {
$output = array();
$titles = $content[0];
unset( $content[0] );
foreach ( $content as $key => $row ) {
if ( $row ) {
$r = array();
// convert back into string.
$values = $row;
$i = 0;
foreach ( $values as $value ) {
$value = str_replace( '\n', '<br />', $value );
$value = str_replace( '\r', '<br />', $value );
$value = str_replace( array( $enclosure, $escape ), '', $value );
if ( in_array( $titles[ $i ], array( 'event_begin', 'event_end', 'event_time', 'event_endtime', 'occur_begin', 'occur_end' ), true ) ) {
if ( in_array( $titles[ $i ], array( 'event_begin', 'event_end', 'occur_begin', 'occur_end' ), true ) ) {
$value = mcs_date( 'Y-m-d', strtotime( $value ), false );
} else {
$value = mcs_date( 'H:i:s', strtotime( $value ), false );
}
// endtime must be listed after start time.
if ( 'event_endtime' === $titles[ $i ] && '00:00:00' === $value ) {
$value = mcs_date( 'H:i:s', strtotime( $r['event_time'] . ' + 1 hour' ), false );
}
$r[ $titles[ $i ] ] = ( isset( $value ) ) ? trim( $value ) : '';
} else {
$r[ $titles[ $i ] ] = ( isset( $value ) ) ? trim( $value ) : '';
}
++$i;
}
unset( $value );
}
$is_recurring = ( isset( $r['event_recur'] ) && ( 0 !== stripos( 'S', $r['event_recur'] ) ) ) ? true : false;
$event_begin = ( isset( $r['event_begin'] ) ) ? $r['event_begin'] : '';
$event_end = ( isset( $r['event_end'] ) ) ? $r['event_end'] : '';
$event_begin = ( isset( $r['occur_begin'] ) && ! $is_recurring ) ? $r['occur_begin'] : $event_begin;
$event_end = ( isset( $r['occur_end'] ) && ! $is_recurring ) ? $r['occur_end'] : $event_end;
$r['event_begin'] = array( $event_begin );
$r['event_end'] = array( $event_end );
$r['event_time'] = ( ! is_array( $r['event_time'] ) ) ? array( $r['event_time'] ) : $r['event_time'];
if ( isset( $r['event_endtime'] ) ) {
$r['event_endtime'] = ( ! is_array( $r['event_endtime'] ) ) ? array( $r['event_endtime'] ) : $r['event_endtime'];
}
if ( isset( $r['event_desc'] ) && ! isset( $r['content'] ) || ( isset( $r['event_desc'] ) && isset( $r['content'] ) && $r['event_desc'] !== $r['content'] ) ) {
// If event_desc present, it will be preferred over content.
$r['content'] = $r['event_desc'];
}
if ( strtotime( $event_end ) < strtotime( $event_begin ) ) {
$r['event_end'] = array( $event_begin );
}
$output[] = $r;
}
unset( $row );
return $output;
}
/**
* Set up default event values for fields that are set by default when creating events in admin
*/
function mcs_default_event_values() {
$expires = ( function_exists( 'mc_event_link_expires' ) && ! mc_event_link_expires() ) ? 1 : 0;
// import values from settings & autogenerate generated values.
$defaults = array(
'event_fifth_week' => ( 'true' === get_option( 'event_fifth_week' ) ) ? 1 : '',
'event_holiday' => ( 'true' === get_option( 'mc_skip_holidays' ) ) ? 1 : '',
'event_group_id' => mc_group_id(),
'event_nonce_name' => wp_create_nonce( 'event_nonce' ),
'event_category' => 1,
'event_recur' => 'S',
'event_repeats' => 0,
'event_link_expires' => $expires,
);
return apply_filters( 'mcs_default_event_values', $defaults );
}
/**
* Fetch the category ID for categories passed by name
*
* @param string $category_name Name of a possible category.
*
* @return int
*/
function mcs_category_by_name( $category_name ) {
global $wpdb;
$cat_id = false;
$sql = 'SELECT * FROM ' . my_calendar_categories_table() . ' WHERE category_name = %s';
$cat = $wpdb->get_row( $wpdb->prepare( $sql, $category_name ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
if ( is_object( $cat ) ) {
$cat_id = $cat->category_id;
}
return $cat_id;
}
/**
* See whether category exists if ID passed
*
* @param int $number Category ID.
*
* @return int
*/
function mcs_category_exists( $number ) {
global $wpdb;
$number = (int) $number;
$cat_id = false;
$sql = 'SELECT * FROM ' . my_calendar_categories_table() . ' WHERE category_id = %d';
$cat = $wpdb->get_row( $wpdb->prepare( $sql, $number ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
if ( is_object( $cat ) ) {
$cat_id = $cat->category_id;
}
return $cat_id;
}
/**
* Insert a new category using provided data if did not already exist.
*
* @param string $category_name Name of category.
* @param string $color Color string.
* @param string $icon Img name.
* @param bool $is_private Is a private category.
*
* @return int
*/
function mcs_insert_category( $category_name, $color, $icon, $is_private ) {
global $wpdb;
$cat_id = false;
$formats = array( '%s', '%s', '%s', '%d', '%d' );
$term = wp_insert_term( $category_name, 'mc-event-category' );
if ( ! is_wp_error( $term ) ) {
$term = $term['term_id'];
} else {
$term = false;
}
$add = array(
'category_name' => $category_name,
'category_color' => ( false !== $color ) ? $color : '#243f82',
'category_icon' => ( false !== $icon ) ? $icon : 'event.png',
'category_private' => ( false !== $is_private ) ? $is_private : 0,
'category_term' => $term,
);
// actions and filters.
$wpdb->insert( my_calendar_categories_table(), $add, $formats );
$cat_id = $wpdb->insert_id;
return $cat_id;
}
/**
* Get My Calendar import directory root.
*
* @param string $path Subdirectory, optional.
*
* @return string
*/
function mcs_import_directory( $path = '' ) {
/**
* Filter the My Calendar import directory path.
*
* @hook mcs_import_directory
*
* @param {string} $path Subdirectory to append to source path. Optional.
*
* @return {string}
*/
$filepath = apply_filters( 'mcs_import_directory', wp_upload_dir()['path'], $path );
$path = trailingslashit( $filepath ) . $path;
return $path;
}