'squat-radar-widget', 'description' => 'Radar Events List', ); $this->connector = new Squat_Radar_Connector(); parent::__construct( 'Squat_Radar', 'Squat Radar Events', $widget_ops ); } /** * Register the widget */ public static function register_widget() { register_widget( __CLASS__ ); add_action( 'wp_ajax_squat_radar_events', [__CLASS__, 'ajax_callback'] ); add_action( 'wp_ajax_nopriv_squat_radar_events', [__CLASS__, 'ajax_callback'] ); add_action( 'wp_enqueue_scripts', [__CLASS__, 'widget_script'] ); add_action( 'wp_enqueue_scripts', [__CLASS__, 'widget_style'] ); add_action( 'squat_radar_widget_cache_cron', [__CLASS__, 'cache_cron'] ); add_option( 'squat_radar_widget_cron_run', []); } /** * Enqueue scripts callback, add CSS. */ static public function widget_style() { wp_register_style( 'squat-radar-widget', SQUAT_RADAR_URL . 'assets/squat-radar.css' ); wp_enqueue_style( 'squat-radar-widget' ); } /** * Enqueue scripts callback, add JS. */ static public function widget_script() { wp_register_script( 'squat-radar-widget', SQUAT_RADAR_URL . 'assets/squat-radar.js', ['jquery'] ); } /** * Cron action. * * Uses an option to keep track of when run, and updates any (experimental) widgets that update using a cron period instead of ajax. */ public static function cache_cron() { $now = time(); $last_run = get_option('squat_radar_widget_cron_run', []); foreach (self::cron_instances() as $number => $instance) { if (! isset($last_run[$number]) || $last_run[$number] + $instance['cache_expire'] < $now ) { if (self::cache_refresh($instance)) { $last_run[$number] = $now; } } } set_option('squat_radar_widget_cron_run', $last_run); } /** * Refresh an individual widget instance for cache_cron(). */ protected static function cache_refresh($instance) { $connector = new Squat_Radar_Connector(); $languages = apply_filters( 'wpml_active_languages', NULL); $languages = array_keys($languages); $languages = array_merge($instance['url']['keys']['language'], (array) $languages); foreach ($languages as $language) { try { // Force update. Don't set expire. $data = $connector->events($instance['url']['keys']['facets'], $instance['fields'], $language, $instance['limit'], 0, TRUE ); } catch ( Squat_Radar_Connector_Exception $e ) { return FALSE; } } return TRUE; } /** * Implementation of WP_Widget::widget(). * * Outputs the events for the correct instance of the widget. */ public function widget( $args, $instance ) { $widget_id = 'squat_radar_widget_' . $this->number; echo $args['before_widget']; if ( ! empty( $instance['title'] ) ) { echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'] ) . $args['after_title']; } if ( ! empty($instance['use_cron']) ) { try { echo self::instance_events_html($instance); } catch ( Squat_Radar_Connector_Exception $e ) { if ( current_user_can( 'administrator' ) ) { echo $e->getCode() . ': ' . $e->getMessage(); } echo '
' . esc_url( $instance['url']['value'] ) . '
'; } } else { wp_enqueue_script( 'squat-radar-widget'); wp_localize_script( 'squat-radar-widget', 'squat_radar_widget', [ 'ajaxurl' => admin_url( 'admin-ajax.php' ) ] ); wp_localize_script( 'squat-radar-widget', $widget_id, ['number' => $this->number] ); echo '
' . esc_url( $instance['url']['value'] ) . '
'; } echo $args['after_widget']; } /** * Action callback for AJAX widget display. */ public static function ajax_callback() { if ( ! array_key_exists('instance', $_POST) ) { wp_die(); } $data = []; // Load instance configuration from ID. $instance_number = (int) $_POST['instance']['number']; $widget_options_all = get_option('widget_squat_radar'); if ( ! isset($widget_options_all[$instance_number]) ) { wp_die(); } try { $data['html'] = self::instance_events_html($widget_options_all[$instance_number]); } catch ( Squat_Radar_Connector_Exception $e ) { $data = ['is_error' => TRUE]; if ( current_user_can( 'administrator' ) ) { $data['error']['code'] = $e->getCode(); $data['error']['message'] = $e->getMessage(); } } wp_send_json($data); } public static function instance_events_html($instance) { $language = defined('ICL_LANGUAGE_CODE') ? ICL_LANGUAGE_CODE : $instance['url']['keys']['language']; $connector = new Squat_Radar_Connector(); $data = $connector->events($instance['url']['keys']['facets'], $instance['fields'], $language, $instance['limit'], $instance['cache_expire']); $html = ''; foreach ($data['result'] as $id => $event) { $output = apply_filters( 'squat_radar_format_event', $event, $instance['fields'], ['instance' => $instance] ); $html .= implode(' ', $output); } return $html; } /** * Implementation of WP_Widget::form(). * * Widget options. */ public function form( $instance ) { // // Title. // $field_id = esc_attr( $this->get_field_id( 'title' ) ); $field_name = esc_attr( $this->get_field_name( 'title' ) ); $field_label = esc_attr( 'Title:', 'squat-radar' ); $field_value = empty( $instance['title'] ) ? '' : esc_attr( $instance['title'] ); $field_class = 'widefat'; echo "

"; echo ""; echo ""; echo "

"; // // Limit // $field_id = esc_attr( $this->get_field_id( 'limit' ) ); $field_name = esc_attr( $this->get_field_name( 'limit' ) ); $field_label = esc_attr( 'Max number of events to display:', 'squat-radar' ); $field_value = empty( $instance['limit'] ) ? '10' : (int) $instance['limit']; $field_class = 'tiny-text'; echo "

"; echo ""; echo ""; echo "

"; // // URL. // $field_error = ! empty( $instance['url']['error'] ); $field_id = esc_attr( $this->get_field_id( 'url' ) ); $field_name = esc_attr( $this->get_field_name( 'url' ) ); $field_label = esc_attr( 'Event Search URL:', 'squat-radar' ); $field_value = empty( $instance['url']['value'] ) ? '' : esc_attr( $instance['url']['value'] ); $field_class = 'widefat' . $field_error ? ' error' : ''; echo "

"; echo ""; echo ""; echo "

"; if ( $field_error ) { echo '
' . __('The URL was not recognised as a Radar Events search result. It needs to include the domain and the rest of the /events/search/path like: https://radar.squat.net/en/events/city/City_Name/group/123 Start from https://radar.squat.net/en/events and use the filters in the right hand colunm there before copying the URL from your browser address bar.', 'squat-radar') . '
'; } else { echo '
' . __('Go to https://radar.squat.net/en/events and filter for the events you want to show. Then copy the URL from your address bar into here. It will look similar to: https://radar.squat.net/en/events/city/City_Name/group/123 for example the URL to show all international callouts is https://radar.squat.net/en/events/callout/international-callout', 'squat-radar') . '
'; } if ( empty($instance['url']['error']) && ! empty( $instance['url']['keys'] ) ) { echo '
'; echo '

' . __('Currently selecting events:', 'squat-radar') . '

'; echo '
'; echo '
' . __('Default language', 'squat-radar') . '
'; echo '
' . esc_html($instance['url']['keys']['language']) . '
'; foreach ($instance['url']['keys']['facets'] as $key => $value) { echo '
' . esc_html($key) . '
'; echo '
' . esc_html($value) . '
'; } echo '
'; } echo '
'; echo '
'; echo '' . __('Fields', 'squat-radar') . ''; echo '

'; // Some sensible checkbox defaults. if ( empty($instance['fields']) ) { $instance['fields'] = [ 'title_field' => '', 'date_time:time_start' => '', 'body:summary' => '', 'category' => '', 'offline:address' => '', 'offline:map' => '', 'url' => '', ]; } foreach ($this->preset_fields() as $api_field_name => $field_label) { $field_id = esc_attr( $this->get_field_id( 'field-' . $api_field_name ) ); $field_name = esc_attr( $this->get_field_name( 'field-' . $api_field_name ) ); $field_label = esc_attr( $field_label ); $checked = ''; if ( isset($instance['fields'][$api_field_name]) ) { unset($instance['fields'][$api_field_name]); $checked = ' checked="checked"'; } echo ""; echo "
"; } echo '

'; echo '
'; // ADVANCED echo '
'; echo '
'; echo '' . __('Advanced settings', 'squat-radar') . ''; // // Fields. // $field_id = esc_attr( $this->get_field_id( 'fields' ) ); $field_name = esc_attr( $this->get_field_name( 'fields' ) ); $field_label = esc_attr( 'Additional fields:', 'squat-radar' ); $field_value = empty( $instance['fields'] ) ? '' : esc_attr( implode( ', ', $instance['fields'] ) ); $field_class = 'widefat'; echo "

"; echo ""; echo ""; echo "

"; echo '
' . __('A comma seperated list of field API names. Examples: phone, price, flyer, offline:address:thoroughfare. Some fields might need an additonal filter to format them properly.') . '
'; // // Cache expiry. // $field_id = esc_attr( $this->get_field_id( 'cache_expire' ) ); $field_name = esc_attr( $this->get_field_name( 'cache_expire' ) ); $field_label = esc_attr( 'Cache length:', 'squat-radar' ); $field_value = empty( $instance['cache_expire'] ) ? 10800 : (int) $instance['cache_expire']; $field_class = 'widefat'; echo "

"; echo ""; echo ""; echo "

"; echo '
' . __('Length of time the cache of events will be kept. Longer faster, but updated less often.') . '
'; $field_id = esc_attr( $this->get_field_id( 'use_cron' ) ); $field_name = esc_attr( $this->get_field_name( 'use_cron' ) ); $field_label = esc_attr__( 'Use cron' ); $use_cron = isset($instance['use_cron']) ? (bool) $instance['use_cron'] : false; $checked = checked( $use_cron, TRUE, FALSE ); echo ""; echo "
"; echo '
' . __('Do not use AJAX, but always display the cached version of the events. Update the cache after the expiry length using cron. Works best if you have a regular external cronjob running.') . '
'; echo '
'; } /** * Implementation of WP_Widget::update(). * * Save widget options. */ public function update( $new_instance, $old_instance ) { $options = []; if ( ! empty( $new_instance['title'] ) ) { $options['title'] = sanitize_text_field( $new_instance['title'] ); } else { $options['title'] = ''; } if ( ! empty($new_instance['url']) ) { $keys = $this->connector->decode_search_url($new_instance['url']); $options['url']['keys'] = $keys; $options['url']['value'] = $new_instance['url']; if (empty($keys)) { $options['url']['error'] = 'URL not recognised'; } } else { $options['url'] = ['value' => '', 'keys' => []]; } $options['fields'] = []; foreach ($this->preset_fields() as $field_name => $field_label) { if ( ! empty($new_instance['field-' . $field_name]) ) { $options['fields'][$field_name] = $field_name; } } if ( ! empty($new_instance['fields']) ) { $matches = []; preg_match_all('/([a-zA-Z_:]+)/', $new_instance['fields'], $matches); $options['fields'] += array_combine($matches[0], $matches[0]); } if ( ! empty( $new_instance['limit'] ) ) { $options['limit'] = (int) $new_instance['limit']; } if ( ! empty( $new_instance['cache_expire'] ) ) { $options['cache_expire'] = (int) $new_instance['cache_expire']; } else { $options['cache_expire'] = 10800; } if ( empty( $new_instance['use_cron'] )) { $options['use_cron'] = FALSE; $cron_instances = self::cron_instances(); unset($cron_instances[$this->number]); if ( empty($cron_instances) && ($timestamp = wp_next_scheduled( 'squat_radar_widget_cache_cron' ) )) { wp_unschedule_event( $timestamp, 'squat_radar_widget_cache_cron' ); } } else { $options['use_cron'] = TRUE; self::cache_refresh($options); if ( ! wp_next_scheduled( 'squat_radar_widget_cache_cron' ) ) { wp_schedule_event( time() + $options['cache_expire'], 'hourly', 'squat_radar_widget_cache_cron'); } } return $options; } public function preset_fields() { return [ 'title_field' => __( 'Title' ), 'event_status' => __( 'Event status (proposed, or cancelled)' ), 'date_time' => __( 'Date and Time (start and optional end)' ), 'date_time:time_start' => __( 'Date and Time (start only)' ), 'body' => __( 'Body' ), 'body:summary' => __( 'Body (teaser, summary)' ), 'category' => __( 'Categories' ), 'topic' => __( 'Tags' ), 'offline:address' => __( 'Address' ), 'offline:map' => __( 'Map (link)' ), 'og_group_ref' => __( 'Groups' ), 'price_category' => __( 'Price category' ), 'image:file:url' => __( 'Image' ), 'link' => __( 'Event URL (entered not Radar)' ), 'url' => __( 'More link (to event on Radar)' ), ]; } public static function cron_instances() { $cron_instances = []; $instances = get_option( 'widget_squat_radar' ); foreach ($instances as $number => $instance) { if (! empty($instance['use_cron']) ) { $cron_instances[$number] = $instance; } } return $cron_instances; } }