Import) and its form handlers. * * @package BeariusCalendar */ defined( 'ABSPATH' ) || exit; /** * Address of the Import screen. * * @param array $args Query arguments. * @return string */ function bcal_import_url( $args = array() ) { return add_query_arg( $args, admin_url( 'edit.php?post_type=' . BCAL_PT . '&page=bcal-settings&tab=import' ) ); } /** * Select with terms of a taxonomy. * * @param string $name Field name. * @param string $taxonomy Taxonomy. * @param int $selected Selected term ID. * @param string $none Label of the empty option. * @param string $id Element ID. */ function bcal_import_term_select( $name, $taxonomy, $selected, $none, $id = '' ) { $terms = get_terms( array( 'taxonomy' => $taxonomy, 'hide_empty' => false, 'number' => 500, 'orderby' => 'name', ) ); echo ''; } /** * Switch row. * * @param string $name Field name. * @param bool $checked Checked. * @param string $label Label. * @param string $help Help text. * @param string $attrs Extra attributes of the input. */ function bcal_import_switch( $name, $checked, $label, $help = '', $attrs = '' ) { $id = 'bcal-' . md5( $name ); echo '
'; if ( $help ) { echo '

' . esc_html( $help ) . '

'; } echo wp_kses( '
', bcal_kses_html() ); } /** * Segmented choice row. * * @param string $name Field name. * @param string $value Current value. * @param array $options value => label. * @param string $label Label. * @param string $help Help text. */ function bcal_import_seg( $name, $value, $options, $label, $help = '' ) { echo '
' . esc_html( $label ) . ''; if ( $help ) { echo '

' . esc_html( $help ) . '

'; } echo '
'; foreach ( $options as $key => $text ) { echo ''; } echo '
'; } /** * Options shared by one-time imports and subscriptions. * * @param array $o Current values. * @param string $context once|feed. */ function bcal_import_option_fields( $o, $context ) { $o = array_merge( BCal_Import_ICS::defaults(), (array) $o ); echo '
'; bcal_import_seg( 'bcal_opt[status_new]', $o['status_new'], array( 'publish' => __( 'Publish', 'bearius-calendar' ), 'pending' => __( 'Pending review', 'bearius-calendar' ), 'draft' => __( 'Draft', 'bearius-calendar' ), ), __( 'New events', 'bearius-calendar' ) ); if ( 'feed' === $context ) { bcal_import_seg( 'bcal_opt[on_removed]', $o['on_removed'], array( 'keep' => __( 'Keep', 'bearius-calendar' ), 'draft' => __( 'Move to drafts', 'bearius-calendar' ), 'cancelled' => __( 'Mark as cancelled', 'bearius-calendar' ), ), __( 'Event disappears from the source', 'bearius-calendar' ), __( 'Only future events within the dates the source still covers are affected.', 'bearius-calendar' ) ); } echo '

' . esc_html__( 'For events without a category in the source.', 'bearius-calendar' ) . '

'; bcal_import_term_select( 'bcal_opt[default_category]', 'bcal_category', (int) $o['default_category'], __( '(none)', 'bearius-calendar' ), 'bcal-imp-cat' ); echo '
'; echo '

' . esc_html__( 'When the city cannot be recognised from the location.', 'bearius-calendar' ) . '

'; bcal_import_term_select( 'bcal_opt[default_city]', 'bcal_city', (int) $o['default_city'], __( '(none)', 'bearius-calendar' ), 'bcal-imp-city' ); echo '
'; bcal_import_seg( 'bcal_opt[cat_new]', $o['cat_new'], array( 'create' => __( 'Add them', 'bearius-calendar' ), 'skip' => __( 'Skip them', 'bearius-calendar' ), ), __( 'Categories that do not exist yet', 'bearius-calendar' ), __( 'Existing categories are matched by name and aliases first.', 'bearius-calendar' ) ); echo '
'; // Images. $rights = isset( $o['rights'] ) && is_array( $o['rights'] ) ? $o['rights'] : array(); $granted = ! empty( $rights['time'] ); echo '
'; bcal_import_switch( 'bcal_opt[images]', ! empty( $o['images'] ) && $granted, __( 'Download images', 'bearius-calendar' ), __( 'Images already in your media library are always reused, without downloading.', 'bearius-calendar' ), 'data-bcal-images-toggle' ); echo '
'; echo wp_kses( bcal_admin_icon( 'shield' ), bcal_kses_svg() ); echo '
'; if ( $granted ) { $user = get_userdata( (int) $rights['user'] ); echo '

' . esc_html( sprintf( /* translators: 1: user name, 2: date, 3: website address */ __( 'Rights confirmed by %1$s on %2$s for %3$s.', 'bearius-calendar' ), $user ? $user->display_name : '#' . (int) $rights['user'], wp_date( (string) get_option( 'date_format' ), (int) $rights['time'] ), isset( $rights['host'] ) ? $rights['host'] : '' ) ) . '

'; } else { echo ''; echo '

' . esc_html__( 'Your name, the date and the source address are saved with the confirmation. The plugin cannot check the rights itself.', 'bearius-calendar' ) . '

'; } echo '
'; bcal_import_switch( 'bcal_opt[credit]', ! empty( $o['credit'] ), __( 'Caption with the source', 'bearius-calendar' ), __( 'Adds "Source: domain" to each downloaded image.', 'bearius-calendar' ) ); bcal_import_switch( 'bcal_opt[enrich]', ! empty( $o['enrich'] ), __( 'Fill in details from event pages', 'bearius-calendar' ), __( 'Reads the event page linked in the calendar: image, price, ticket link, address, organizer and status. Only empty fields are filled.', 'bearius-calendar' ) ); bcal_import_switch( 'bcal_opt[keep_images]', ! empty( $o['keep_images'] ), __( 'Keep images inside descriptions', 'bearius-calendar' ), __( 'Off: images loaded from other servers are removed from descriptions. They can track your visitors.', 'bearius-calendar' ) ); echo '
'; // Search engines and filters. echo '
'; bcal_import_seg( 'bcal_opt[seo]', $o['seo'], array( 'none' => __( 'Index normally', 'bearius-calendar' ), 'canonical' => __( 'Canonical to the original', 'bearius-calendar' ), 'noindex' => __( 'Do not index', 'bearius-calendar' ), ), __( 'Search engines', 'bearius-calendar' ), __( 'For events copied from other websites, pointing to the original avoids duplicate content.', 'bearius-calendar' ) ); bcal_import_switch( 'bcal_opt[show_source]', ! empty( $o['show_source'] ), __( 'Show the source on the event page', 'bearius-calendar' ), __( 'A "Source: domain" link under the dates.', 'bearius-calendar' ) ); bcal_import_switch( 'bcal_opt[past]', ! empty( $o['past'] ), __( 'Import past events too', 'bearius-calendar' ) ); echo '

' . esc_html__( 'Comma or new line separated. Empty imports everything.', 'bearius-calendar' ) . '

'; echo '
'; echo '
'; } /** * Reads options posted by the forms. * * @param array $in Raw input. * @return array */ function bcal_import_read_options( $in ) { $in = is_array( $in ) ? $in : array(); $pick = function ( $key, $allowed, $default ) use ( $in ) { $value = isset( $in[ $key ] ) ? sanitize_key( (string) $in[ $key ] ) : ''; return in_array( $value, $allowed, true ) ? $value : $default; }; $term = function ( $key, $taxonomy ) use ( $in ) { $id = isset( $in[ $key ] ) ? absint( $in[ $key ] ) : 0; return ( $id && term_exists( $id, $taxonomy ) ) ? $id : 0; }; return array( 'status_new' => $pick( 'status_new', array( 'publish', 'pending', 'draft' ), 'publish' ), 'on_removed' => $pick( 'on_removed', array( 'keep', 'draft', 'cancelled' ), 'keep' ), 'images' => empty( $in['images'] ) ? 0 : 1, 'credit' => empty( $in['credit'] ) ? 0 : 1, 'enrich' => empty( $in['enrich'] ) ? 0 : 1, 'keep_images' => empty( $in['keep_images'] ) ? 0 : 1, 'seo' => $pick( 'seo', array( 'none', 'canonical', 'noindex' ), 'none' ), 'show_source' => empty( $in['show_source'] ) ? 0 : 1, 'past' => empty( $in['past'] ) ? 0 : 1, 'default_category' => $term( 'default_category', 'bcal_category' ), 'default_city' => $term( 'default_city', 'bcal_city' ), 'cat_new' => $pick( 'cat_new', array( 'create', 'skip' ), 'create' ), 'include' => isset( $in['include'] ) ? sanitize_textarea_field( (string) $in['include'] ) : '', 'exclude' => isset( $in['exclude'] ) ? sanitize_textarea_field( (string) $in['exclude'] ) : '', // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_exclude -- Keyword filter for imported events, not a query argument. ); } /** * Notices after form actions. */ function bcal_import_notices() { $code = isset( $_GET['bcal_msg'] ) ? sanitize_key( wp_unslash( $_GET['bcal_msg'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( '' === $code ) { return; } $messages = array( 'started' => array( 'success', __( 'Import started. It runs in the background, you can leave this page.', 'bearius-calendar' ) ), 'tec_none' => array( 'error', __( 'No data from The Events Calendar was found.', 'bearius-calendar' ) ), 'file_error' => array( 'error', __( 'The file could not be used. Choose an .ics calendar file up to 20 MB.', 'bearius-calendar' ) ), 'url_error' => array( 'error', __( 'Enter a valid calendar address (https:// or webcal://).', 'bearius-calendar' ) ), 'rights' => array( 'error', __( 'To download images, confirm that you have the right to publish them.', 'bearius-calendar' ) ), 'feed_saved' => array( 'success', __( 'Subscription saved.', 'bearius-calendar' ) ), 'feed_started' => array( 'success', __( 'Subscription saved. The first import has started.', 'bearius-calendar' ) ), 'feed_deleted' => array( 'success', __( 'Subscription removed.', 'bearius-calendar' ) ), 'feed_busy' => array( 'warning', __( 'This subscription is already being imported.', 'bearius-calendar' ) ), 'control' => array( 'success', __( 'Done.', 'bearius-calendar' ) ), ); if ( isset( $messages[ $code ] ) ) { echo '

' . esc_html( $messages[ $code ][1] ) . '

'; } } /** * Import tab. */ function bcal_render_import_tab() { if ( ! current_user_can( 'manage_options' ) ) { return; } bcal_import_notices(); echo '
'; $feed_id = isset( $_GET['feed'] ) ? sanitize_key( wp_unslash( $_GET['feed'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( '' !== $feed_id ) { bcal_render_feed_editor( $feed_id ); bcal_render_import_history(); echo '
'; return; } bcal_render_import_history(); bcal_render_import_tec(); bcal_render_import_ics(); bcal_render_import_feeds(); echo ''; } /** * Section header. * * @param string $title Title. * @param string $desc Description. * @param string $icon Icon. */ function bcal_import_section_head( $title, $desc, $icon ) { echo wp_kses( '

' . bcal_admin_icon( $icon ) . '' . esc_html( $title ) . '

', bcal_kses_html() ); if ( $desc ) { echo '

' . esc_html( $desc ) . '

'; } echo '
'; } /** * The Events Calendar card. */ function bcal_render_import_tec() { $info = BCal_Import_TEC::detect(); echo '
'; bcal_import_section_head( __( 'From The Events Calendar', 'bearius-calendar' ), __( 'Events are converted in place: addresses, images, comments and SEO data stay. The original data is not changed, so the conversion can be undone.', 'bearius-calendar' ), 'import' ); echo '
'; if ( ! $info['events'] && ! $info['venues'] && ! $info['organizers'] ) { echo '

'; echo $info['converted'] ? esc_html( sprintf( /* translators: %d: number of events */ _n( '%d event was converted from The Events Calendar.', '%d events were converted from The Events Calendar.', $info['converted'], 'bearius-calendar' ), $info['converted'] ) ) : esc_html__( 'No data from The Events Calendar was found on this site.', 'bearius-calendar' ); echo '

'; return; } $stats = array( array( $info['events'], __( 'Events', 'bearius-calendar' ) ), array( $info['recurring'] + $info['occurrence'], __( 'Recurring or series dates', 'bearius-calendar' ) ), array( $info['images'], __( 'With an image', 'bearius-calendar' ) ), array( $info['venues'], __( 'Venues', 'bearius-calendar' ) ), array( $info['organizers'], __( 'Organizers', 'bearius-calendar' ) ), array( $info['categories'], __( 'Categories', 'bearius-calendar' ) ), ); echo '
'; foreach ( $stats as $stat ) { echo '
' . esc_html( number_format_i18n( $stat[0] ) ) . '' . esc_html( $stat[1] ) . '
'; } echo '
'; $notes = array(); if ( $info['active'] ) { $notes[] = array( 'warn', __( 'The Events Calendar is active. Deactivate it after the conversion so both plugins do not use the same addresses.', 'bearius-calendar' ) ); } if ( $info['listed'] ) { /* translators: %d: number of events */ $notes[] = array( 'info', sprintf( _n( '%d repeat rule is not supported: its dates will be saved as a list of dates.', '%d repeat rules are not supported: their dates will be saved as lists of dates.', $info['listed'], 'bearius-calendar' ), $info['listed'] ) ); } if ( $info['occurrence'] ) { $notes[] = array( 'info', __( 'Older recurring series store every date as a separate post. Their dates are added to the main event; the separate posts are left untouched.', 'bearius-calendar' ) ); } if ( $info['tagged'] ) { $notes[] = array( 'info', __( 'Tags stay in the database but are not shown for events.', 'bearius-calendar' ) ); } if ( $info['converted'] ) { /* translators: %d: number of events */ $notes[] = array( 'info', sprintf( _n( '%d event was already converted earlier.', '%d events were already converted earlier.', $info['converted'], 'bearius-calendar' ), $info['converted'] ) ); } foreach ( $notes as $note ) { echo wp_kses( '
' . bcal_admin_icon( 'warn' === $note[0] ? 'alert' : 'check' ) . '

' . esc_html( $note[1] ) . '

', bcal_kses_html() ); } echo '
'; echo '
'; echo ''; wp_nonce_field( 'bcal_import_tec' ); echo '
'; bcal_import_switch( 'use_slugs', true, __( 'Keep the addresses of The Events Calendar', 'bearius-calendar' ), __( 'Sets the calendar URL bases so event links stay the same. Pages of venues, organizers, categories and calendar views redirect to their new addresses.', 'bearius-calendar' ), 'data-bcal-slugs-toggle' ); echo '
' . esc_html__( 'Addresses', 'bearius-calendar' ) . '
'; $host = (string) wp_parse_url( home_url( '/' ), PHP_URL_HOST ); echo ''; echo ''; echo '
'; bcal_import_switch( 'website_to_ticket', true, __( 'Event website becomes the ticket link', 'bearius-calendar' ), __( 'The Events Calendar has one "Event website" field; it is usually the ticket or registration page.', 'bearius-calendar' ) ); echo '
'; echo wp_kses( '
', bcal_kses_html() ); echo '
'; } /** * One-time .ics import. */ function bcal_render_import_ics() { echo '
'; bcal_import_section_head( __( 'Import an .ics calendar', 'bearius-calendar' ), __( 'A calendar file or address, imported once. Running the same calendar again updates events instead of adding them twice.', 'bearius-calendar' ), 'when' ); echo '
'; echo '
'; echo ''; wp_nonce_field( 'bcal_import_ics' ); echo '
'; bcal_import_seg( 'source', 'url', array( 'url' => __( 'Calendar address', 'bearius-calendar' ), 'file' => __( 'File from my computer', 'bearius-calendar' ), ), __( 'Source', 'bearius-calendar' ) ); echo '
'; echo ''; echo '
'; echo '
' . esc_html__( 'Import options', 'bearius-calendar' ) . ''; bcal_import_option_fields( BCal_Import_ICS::defaults(), 'once' ); echo '
'; echo wp_kses( '
', bcal_kses_html() ); echo '
'; } /** * Subscriptions list. */ function bcal_render_import_feeds() { $feeds = BCal_Import_Feeds::all(); $intervals = BCal_Import_Feeds::intervals(); echo '
'; bcal_import_section_head( __( 'Subscriptions', 'bearius-calendar' ), __( 'Calendars checked on a schedule: new events are added and changed ones updated. Fields you edit by hand are never overwritten.', 'bearius-calendar' ), 'feed' ); echo '
'; if ( ! $feeds ) { echo '

' . esc_html__( 'No subscriptions yet.', 'bearius-calendar' ) . '

'; } else { echo '
    '; foreach ( $feeds as $feed ) { $state = (array) $feed['state']; $host = (string) wp_parse_url( $feed['url'], PHP_URL_HOST ); $name = '' !== $feed['name'] ? $feed['name'] : $host; echo '
  • '; echo wp_kses( '' . bcal_admin_icon( 'feed' ) . '', bcal_kses_html() ); echo '' . esc_html( $name ) . ''; echo '' . esc_html( $host ) . ' · ' . esc_html( isset( $intervals[ $feed['interval'] ] ) ? $intervals[ $feed['interval'] ][1] : '' ) . ''; echo ''; if ( ! $feed['enabled'] ) { echo '' . esc_html__( 'Paused', 'bearius-calendar' ) . ''; } elseif ( ! empty( $state['error'] ) ) { echo '' . esc_html__( 'Last check failed', 'bearius-calendar' ) . ''; } elseif ( ! empty( $state['last_ok'] ) ) { /* translators: %s: time ago such as "5 minutes" */ echo wp_kses( '' . bcal_admin_icon( 'check' ) . esc_html( sprintf( __( 'Checked %s ago', 'bearius-calendar' ), human_time_diff( (int) $state['last_ok'] ) ) ) . '', bcal_kses_html() ); } else { echo '' . esc_html__( 'Not checked yet', 'bearius-calendar' ) . ''; } echo ''; echo ''; echo '
    '; wp_nonce_field( 'bcal_feed_sync' ); echo wp_kses( '
    ', bcal_kses_html() ); echo '' . esc_html__( 'Settings', 'bearius-calendar' ) . ''; echo '
  • '; } echo '
'; } echo wp_kses( '
', bcal_kses_html() ); echo '
'; } /** * Subscription editor. * * @param string $feed_id Subscription ID or "new". */ function bcal_render_feed_editor( $feed_id ) { $feed = 'new' === $feed_id ? BCal_Import_Feeds::defaults() : BCal_Import_Feeds::get( $feed_id ); $intervals = BCal_Import_Feeds::intervals(); if ( ! $feed ) { echo '

' . esc_html__( 'This subscription does not exist.', 'bearius-calendar' ) . '

'; return; } echo '

← ' . esc_html__( 'All import options', 'bearius-calendar' ) . '

'; echo '
'; bcal_import_section_head( 'new' === $feed_id ? __( 'New subscription', 'bearius-calendar' ) : __( 'Subscription settings', 'bearius-calendar' ), __( 'Paste the calendar address (.ics or webcal). The first import starts when you save.', 'bearius-calendar' ), 'feed' ); echo '
'; echo '
'; echo ''; wp_nonce_field( 'bcal_feed_save' ); echo '
'; echo '

' . esc_html__( 'Only for you, e.g. "Culture centre".', 'bearius-calendar' ) . '

'; echo '
'; echo '
'; if ( ! empty( $feed['state']['refresh'] ) ) { /* translators: %s: interval as written by the source, such as PT6H */ echo '

' . esc_html( sprintf( __( 'The source suggests: %s', 'bearius-calendar' ), $feed['state']['refresh'] ) ) . '

'; } echo '
'; bcal_import_switch( 'enabled', ! empty( $feed['enabled'] ), __( 'Active', 'bearius-calendar' ), __( 'Paused subscriptions are not checked.', 'bearius-calendar' ) ); echo '
'; bcal_import_option_fields( $feed, 'feed' ); // Category mapping. $seen = isset( $feed['state']['seen_cats'] ) ? (array) $feed['state']['seen_cats'] : array(); if ( $seen ) { $map = (array) $feed['cat_map']; $terms = get_terms( array( 'taxonomy' => 'bcal_category', 'hide_empty' => false, 'number' => 500, 'orderby' => 'name', ) ); echo '
' . esc_html__( 'Categories of this calendar', 'bearius-calendar' ) . '

' . esc_html__( '"Automatic" matches by name and aliases, or adds the category when allowed above.', 'bearius-calendar' ) . '

'; foreach ( $seen as $name => $count ) { $norm = bcal_normalize_name( $name ); $choice = isset( $map[ $norm ] ) ? (string) $map[ $norm ] : 'auto'; echo ''; } echo '
'; } echo '
'; echo '
'; if ( 'new' !== $feed_id ) { echo '
'; echo ''; wp_nonce_field( 'bcal_feed_delete' ); echo ''; echo wp_kses( '
', bcal_kses_html() ); } echo '
'; } /** * Import history with progress, summaries and actions. */ function bcal_render_import_history() { // Quiet subscription checks: only the latest one per subscription is shown. $sessions = array(); $quiet = array(); foreach ( BCal_Import::sessions( true ) as $session ) { if ( 'approved' === $session['status'] && in_array( $session['message'], array( 'no_changes', 'not_modified' ), true ) ) { if ( isset( $quiet[ $session['feed'] ] ) ) { continue; } $quiet[ $session['feed'] ] = true; } $sessions[] = $session; } $sessions = array_slice( $sessions, 0, 15 ); if ( ! $sessions ) { return; } $counts = BCal_Import::count_labels(); $warnings = BCal_Import::warning_labels(); $active = false; echo '
'; bcal_import_section_head( __( 'Imports', 'bearius-calendar' ), __( 'Imports run in the background in small batches. Until you approve an import, everything it added or changed can be undone.', 'bearius-calendar' ), 'repeat' ); echo '
'; if ( $active ) { echo ''; } } /* -------------------------------------------------------------------------- Handlers -------------------------------------------------------------------------- */ /** * Checks permission. Each handler verifies its own nonce right after. */ function bcal_import_guard() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You are not allowed to do this.', 'bearius-calendar' ), 403 ); } } /** * Moves an uploaded calendar file into the private import folder through * the WordPress upload API. The file type is checked by the caller (name * and BEGIN:VCALENDAR header), so the MIME sniffing of the upload API is * skipped: many servers report calendar files as plain text. * * @param array $file Entry from $_FILES. * @param string $dir Import folder with a trailing slash. * @return string Full path, or an empty string on failure. */ function bcal_import_store_upload( $file, $dir ) { if ( ! function_exists( 'wp_handle_upload' ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; } $folder = untrailingslashit( $dir ); $name = 'upload-' . strtolower( wp_generate_password( 12, false, false ) ) . '.ics'; $to_dir = static function ( $uploads ) use ( $folder ) { $uploads['path'] = $folder; $uploads['subdir'] = ''; $uploads['url'] = ''; $uploads['error'] = false; return $uploads; }; add_filter( 'upload_dir', $to_dir ); $moved = wp_handle_upload( $file, array( 'test_form' => false, 'test_type' => false, 'unique_filename_callback' => static function () use ( $name ) { return $name; }, ) ); remove_filter( 'upload_dir', $to_dir ); if ( ! is_array( $moved ) || ! empty( $moved['error'] ) || empty( $moved['file'] ) ) { return ''; } return (string) $moved['file']; } /** * Back to the Import screen with a message. * * @param string $message Message code. * @param array $args Extra arguments. */ function bcal_import_back( $message, $args = array() ) { wp_safe_redirect( bcal_import_url( array_merge( array( 'bcal_msg' => $message ), $args ) ) ); exit; } add_action( 'admin_post_bcal_import_tec', function () { bcal_import_guard(); check_admin_referer( 'bcal_import_tec' ); $info = BCal_Import_TEC::detect(); if ( ! $info['events'] && ! $info['venues'] && ! $info['organizers'] ) { bcal_import_back( 'tec_none' ); } BCal_Import::create( 'tec', __( 'The Events Calendar', 'bearius-calendar' ), array( 'use_slugs' => empty( $_POST['use_slugs'] ) ? 0 : 1, 'slug_archive' => isset( $_POST['slug_archive'] ) ? sanitize_title( wp_unslash( $_POST['slug_archive'] ) ) : '', 'slug_single' => isset( $_POST['slug_single'] ) ? sanitize_title( wp_unslash( $_POST['slug_single'] ) ) : '', 'website_to_ticket' => empty( $_POST['website_to_ticket'] ) ? 0 : 1, ) ); bcal_import_back( 'started' ); } ); add_action( 'admin_post_bcal_import_ics', function () { bcal_import_guard(); check_admin_referer( 'bcal_import_ics' ); $raw = isset( $_POST['bcal_opt'] ) && is_array( $_POST['bcal_opt'] ) ? wp_unslash( $_POST['bcal_opt'] ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $options = bcal_import_read_options( $raw ); $source = isset( $_POST['source'] ) && 'file' === $_POST['source'] ? 'file' : 'url'; if ( 'url' === $source ) { $url = BCal_Import_ICS::normalize_url( isset( $_POST['url'] ) ? wp_unslash( $_POST['url'] ) : '' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized if ( ! $url ) { bcal_import_back( 'url_error' ); } $options['mode'] = 'url'; $options['url'] = $url; $host = (string) wp_parse_url( $url, PHP_URL_HOST ); $label = $host; } else { $file = isset( $_FILES['bcal_file'] ) ? $_FILES['bcal_file'] : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput $dir = BCal_Import::work_dir(); $name = $file && isset( $file['name'] ) ? sanitize_file_name( (string) $file['name'] ) : ''; $ok = $file && $dir && isset( $file['error'], $file['tmp_name'], $file['size'] ) && UPLOAD_ERR_OK === (int) $file['error'] && (int) $file['size'] > 0 && (int) $file['size'] <= 20 * MB_IN_BYTES && is_uploaded_file( $file['tmp_name'] ) && preg_match( '/\.(ics|ical|ifb|txt)$/i', $name ); if ( ! $ok ) { bcal_import_back( 'file_error' ); } $head = (string) file_get_contents( $file['tmp_name'], false, null, 0, 2048 ); // phpcs:ignore WordPress.WP.AlternativeFunctions if ( 0 !== stripos( preg_replace( '/^\xEF\xBB\xBF/', '', ltrim( $head ) ), 'BEGIN:VCALENDAR' ) ) { bcal_import_back( 'file_error' ); } $target = bcal_import_store_upload( $file, $dir ); if ( '' === $target ) { bcal_import_back( 'file_error' ); } $options['mode'] = 'file'; $options['file'] = wp_basename( $target ); $host = __( 'file', 'bearius-calendar' ); $label = $name; } if ( $options['images'] ) { if ( empty( $raw['rights_confirm'] ) ) { bcal_import_back( 'rights' ); } $options['rights'] = array( 'user' => get_current_user_id(), 'time' => time(), 'host' => $host, ); } BCal_Import::create( 'ics', $label, $options ); bcal_import_back( 'started' ); } ); add_action( 'admin_post_bcal_feed_save', function () { bcal_import_guard(); check_admin_referer( 'bcal_feed_save' ); $id = isset( $_POST['id'] ) ? sanitize_key( wp_unslash( $_POST['id'] ) ) : ''; $feed = $id ? BCal_Import_Feeds::get( $id ) : null; $feed = $feed ? $feed : BCal_Import_Feeds::defaults(); $new = empty( $feed['id'] ); $url = BCal_Import_ICS::normalize_url( isset( $_POST['url'] ) ? wp_unslash( $_POST['url'] ) : '' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized if ( ! $url ) { bcal_import_back( 'url_error', array( 'feed' => $new ? 'new' : $feed['id'] ) ); } $raw = isset( $_POST['bcal_opt'] ) && is_array( $_POST['bcal_opt'] ) ? wp_unslash( $_POST['bcal_opt'] ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $host = (string) wp_parse_url( $url, PHP_URL_HOST ); $feed = array_merge( $feed, bcal_import_read_options( $raw ) ); $feed['name'] = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : ''; $feed['enabled'] = empty( $_POST['enabled'] ) ? 0 : 1; $interval = isset( $_POST['interval'] ) ? sanitize_key( wp_unslash( $_POST['interval'] ) ) : 'daily'; $feed['interval'] = array_key_exists( $interval, BCal_Import_Feeds::intervals() ) ? $interval : 'daily'; // A different source address needs a new confirmation of image rights. if ( $feed['url'] !== $url || empty( $feed['rights']['host'] ) || $feed['rights']['host'] !== $host ) { $feed['rights'] = array(); } $feed['url'] = $url; $message = $new ? 'feed_started' : 'feed_saved'; if ( $feed['images'] && empty( $feed['rights'] ) ) { if ( ! empty( $raw['rights_confirm'] ) ) { $feed['rights'] = array( 'user' => get_current_user_id(), 'time' => time(), 'host' => $host, ); } else { $feed['images'] = 0; $message = 'rights'; } } $map = array(); if ( isset( $_POST['catmap'] ) && is_array( $_POST['catmap'] ) ) { foreach ( wp_unslash( $_POST['catmap'] ) as $norm => $choice ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $norm = bcal_normalize_name( (string) $norm ); $choice = sanitize_key( (string) $choice ); if ( '' !== $norm && ( in_array( $choice, array( 'auto', 'skip' ), true ) || ctype_digit( $choice ) ) ) { $map[ $norm ] = $choice; } } } $feed['cat_map'] = $map; if ( empty( $feed['owner'] ) ) { $feed['owner'] = get_current_user_id(); } $saved = BCal_Import_Feeds::save( $feed ); if ( $new && $feed['enabled'] ) { BCal_Import_Feeds::start( $saved, true ); } bcal_import_back( $message, 'rights' === $message ? array( 'feed' => $saved ) : array() ); } ); add_action( 'admin_post_bcal_feed_sync', function () { bcal_import_guard(); check_admin_referer( 'bcal_feed_sync' ); $id = isset( $_POST['id'] ) ? sanitize_key( wp_unslash( $_POST['id'] ) ) : ''; bcal_import_back( BCal_Import_Feeds::start( $id, true ) ? 'started' : 'feed_busy' ); } ); add_action( 'admin_post_bcal_feed_delete', function () { bcal_import_guard(); check_admin_referer( 'bcal_feed_delete' ); $id = isset( $_POST['id'] ) ? sanitize_key( wp_unslash( $_POST['id'] ) ) : ''; $feed = BCal_Import_Feeds::get( $id ); if ( $feed && ! empty( $_POST['purge'] ) ) { BCal_Import::create( 'ics', /* translators: %s: subscription name */ sprintf( __( 'Deleting events of %s', 'bearius-calendar' ), '' !== $feed['name'] ? $feed['name'] : (string) wp_parse_url( $feed['url'], PHP_URL_HOST ) ), array( 'mode' => 'purge', 'purge_feed' => $id, ) ); } BCal_Import_Feeds::delete( $id ); bcal_import_back( 'feed_deleted' ); } ); add_action( 'admin_post_bcal_import_control', function () { bcal_import_guard(); check_admin_referer( 'bcal_import_control' ); $id = isset( $_POST['id'] ) ? sanitize_key( wp_unslash( $_POST['id'] ) ) : ''; $do = isset( $_POST['do'] ) ? sanitize_key( wp_unslash( $_POST['do'] ) ) : ''; BCal_Import::control( $id, $do, array( 'keep_edited' => ! empty( $_POST['keep_edited'] ) ) ); bcal_import_back( 'control' ); } );