<?php

namespace EventManagerIntegration\Entity;

abstract class CustomPostType
{
    protected $namePlural;
    protected $nameSingular;
    protected $slug;
    protected $args;

    public $tableColumns = array();
    public $tableSortableColumns = array();
    public $tableColumnsContentCallback = array();

    /**
     * Registers a custom post type
     * @param string $namePlural   Post type name in plural
     * @param string $nameSingular Post type name in singular
     * @param string $slug         Post type slug
     * @param array  $args         Post type arguments
     */
    public function __construct($namePlural, $nameSingular, $slug, $args = array())
    {
        $this->namePlural = $namePlural;
        $this->nameSingular = $nameSingular;
        $this->slug = $slug;
        $this->args = $args;

        // Register post type on init
        add_action('init', array($this, 'registerPostType'));
        add_filter('manage_edit-' . $this->slug . '_columns', array($this, 'tableColumns'));
        add_filter('manage_edit-' . $this->slug . '_sortable_columns', array($this, 'tableSortableColumns'));
        add_action('manage_' . $this->slug . '_posts_custom_column', array($this, 'tableColumnsContent'), 10, 2);
    }

    /**
     * Registers the post type with WP
     * @return string Post type slug
     */
    public function registerPostType()
    {
        $labels = array(
            'name'                => $this->nameSingular,
            'singular_name'       => $this->nameSingular,
            'add_new'             => sprintf(__('Add new %s', 'event-manager'), $this->nameSingular),
            'add_new_item'        => sprintf(__('Add new %s', 'event-manager'), $this->nameSingular),
            'edit_item'           => sprintf(__('Edit %s', 'event-manager'), $this->nameSingular),
            'new_item'            => sprintf(__('New %s', 'event-manager'), $this->nameSingular),
            'view_item'           => sprintf(__('View %s', 'event-manager'), $this->nameSingular),
            'search_items'        => sprintf(__('Search %s', 'event-manager'), $this->namePlural),
            'not_found'           => sprintf(__('No %s found', 'event-manager'), $this->namePlural),
            'not_found_in_trash'  => sprintf(__('No %s found in trash', 'event-manager'), $this->namePlural),
            'parent_item_colon'   => sprintf(__('Parent %s:', 'event-manager'), $this->nameSingular),
            'menu_name'           => $this->namePlural,
        );

        $this->args['labels'] = $labels;

        register_post_type($this->slug, $this->args);

        return $this->slug;
    }

    /**
     * Adds a column to the admin list table
     * @param string   $key             Column key
     * @param string   $title           Column title
     * @param boolean  $sortable        Sortable or not
     * @param callback $contentCallback Callback function for displaying
     *                                  column content (params: $columnKey, $postId)
     */
    public function addTableColumn($key, $title, $sortable = false, $contentCallback = false)
    {
        $this->tableColumns[$key] = $title;

        if ($sortable === true) {
            $this->tableSortableColumns[$key] = $key;
        }

        if ($contentCallback !== false) {
            $this->tableColumnsContentCallback[$key] = $contentCallback;
        }
    }

    /**
     * Set up table columns
     * @param  array $columns Default columns
     * @return array          New columns
     */
    public function tableColumns($columns)
    {
        if (!empty($this->tableColumns) && is_array($this->tableColumns)) {
            $columns = $this->tableColumns;
        }

        return $columns;
    }

    /**
     * Setup sortable columns
     * @param  array $columns Default columns
     * @return array          New columns
     */
    public function tableSortableColumns($columns)
    {
        if (!empty($this->tableSortableColumns) && is_array($this->tableSortableColumns)) {
            $columns = $this->tableColumns;
        }

        function arraytolower(array $columns, $round = 0)
        {
            return unserialize(strtolower(serialize($columns)));
        }

        return arraytolower($columns);
    }

    /**
     * Set table column content with callback functions
     * @param  string  $column Key of the column
     * @param  integer $postId Post id of the current row in table
     * @return void
     */
    public function tableColumnsContent($column, $postId)
    {
        if (!isset($this->tableColumnsContentCallback[$column])) {
            return;
        }

        call_user_func_array($this->tableColumnsContentCallback[$column], array($column, $postId));
    }
}


<?php
/**
 * Saving publishing groups as taxonomies
 */

namespace EventManagerIntegration\Parser;

class EventManagerGroups extends \EventManagerIntegration\Parser
{
    public function __construct($url)
    {
        parent::__construct($url);
    }

    public function start()
    {
        $page = 1;
        $taxonomy = 'event_groups';
        // Get group list from option
        $optionList = get_option('event_user_groups', array());
        // Loop through each page with Groups from API
        while ($page != false) {
            // Dont verify ssl cert in dev mode
            $args = array(
                'timeout' => 120,
                'sslverify' => defined('DEV_MODE') && DEV_MODE == true ? false : true,
            );
            $request = wp_remote_get($this->url . '?per_page=100&page=' . $page, $args);
            $responseCode = wp_remote_retrieve_response_code($request);
            $body = wp_remote_retrieve_body($request);
            // Test if response is WP_ERROR or response code is not 200 OK
            if (is_wp_error($request) || $responseCode != 200 || empty($groups = json_decode($body, false))) {
                $page = false;
                break;
            }

            foreach ($groups as $group) {
                // Store groups to option array
                $optionList[$group->id] = array(
                    'id' => $group->id,
                    'name' => $group->name,
                    'slug' => $group->slug,
                    'parent' => $group->parent
                );

                // Save groups as taxonomies
                if ($group->parent == 0) {
                    $parent_term = $this->saveTerms($group->name, $group->slug, $taxonomy);
                    if ($group->children) {
                        foreach ($group->children as $child) {
                            $child_term = $this->saveTerms($child->name, $child->slug, $taxonomy, $parent_term);
                            if ($child->children) {
                                foreach ($child->children as $grand_child) {
                                    $grand_child_term = $this->saveTerms($grand_child->name, $grand_child->slug, $taxonomy, $child_term);
                                }
                            }
                        }
                    }
                }
            }

            $page++;
        }

        // Save groups to option
        update_option('event_user_groups', $optionList, false);
    }

    /**
     * Save or update taxonomy term
     * @param  string  $name     name
     * @param  string  $slug     slug
     * @param  string  $taxonomy taxonomy
     * @param  integer $parent   parent id
     * @return int
     */
    public function saveTerms($name, $slug, $taxonomy, $parent = 0)
    {
        $term = get_term_by('slug', $slug, $taxonomy);
        if ($term == false) {
            $new_term = wp_insert_term($name, $taxonomy, array('slug' => $slug, 'parent' => $parent));
            $term_id = $new_term['term_id'];

            $this->activateNewGroup($term_id, $parent);
        } else {
            wp_update_term($term->term_id, $taxonomy, array('name' => $name, 'parent' => $parent));
            $term_id = $term->term_id;
        }

        return $term_id;
    }

    /**
     * Automatically add new terms to Group filter option if the parent is selected
     * @param  int $termId Term ID
     * @param  int $parentId Terms parent ID
     * @return void
     */
    public function activateNewGroup($termId, $parentId)
    {
        $selectedGroups = get_option('options_event_filter_group', false);
        if (is_array($selectedGroups) && !empty($selectedGroups)) {
            if (in_array($parentId, $selectedGroups)) {
                // Add children to option array
                $selectedGroups[] = (int) $termId;
                update_field('event_filter_group', $selectedGroups, 'option');
            }
        }
    }
}


<?php

namespace EventManagerIntegration\Entity;

abstract class PostManager
{
    /**
     * Post object sticky values
     */
    public $post_type = null;
    public $post_status = 'publish';

    /**
     * Keys that counts as post object properties
     * Any other key will be treated as meta properties
     * @var array
     */
    public $allowedPostFields = array(
        'ID',
        'post_author',
        'post_date',
        'post_date_gmt',
        'post_content',
        'post_content_filtered',
        'post_title',
        'post_excerpt',
        'post_status',
        'comment_status',
        'ping_status',
        'post_password',
        'post_name',
        'to_ping',
        'pinged',
        'post_modified',
        'post_modified_gmt',
        'post_parent',
        'menu_order',
        'post_mime_type',
        'guid'
    );

    /**
     * Constructor
     * @param array $postData Post object fields as array
     * @param array $metaData Post meta as array
     */
    public function __construct($postData = array(), $metaData = array())
    {
        if (is_null($this->post_type)) {
            throw new \Exception('You need to specify a post type by setting the class property $postType');
            exit;
        }

        // Add post data as separate object parameters
        foreach ($postData as $key => $value) {
            $this->{$key} = $value;
        }

        // Add meta data as separate object parameters
        foreach ($metaData as $key => $value) {
            $this->{$key} = $value;
        }
    }

    /**
     * Get  posts
     * @param  integer      $count      Number of posts to get
     * @param  array        $metaQuery  Meta query
     * @param  string       $postType   Post type
     * @param  array|string $postStatus Post status
     * @return array                       Found posts
     */
    public static function get($count, $metaQuery, $postType, $postStatus = array('publish', 'draft'))
    {
        $args = array(
            'posts_per_page' => $count,
            'post_type' => $postType,
            'orderby' => 'date',
            'order' => 'DESC'
        );

        $args['post_status'] = (array)$postStatus;

        if (is_array($metaQuery)) {
            $args['meta_query'] = $metaQuery;
        }

        $posts = get_posts($args);

        if ($count == 1 && isset($posts[0])) {
            $posts = $posts[0];
        }

        return $posts;
    }

    public function filterMeta($meta)
    {
        $meta = array_filter($meta, array($this, 'removeEmpty'));
        return $meta;
    }

    /**
     * Remove all values that are empty, except the value 0
     * @param  $metaValue
     * @return $metaValue
     */
    public function removeEmpty($metaValue)
    {
        if (is_array($metaValue)) {
            return $metaValue;
        }

        return ($metaValue !== null && $metaValue !== false && $metaValue !== '');
    }

    /**
     * Get all values that are empty
     * @param  $metaValue
     * @return $metaValue
     */
    public function getEmptyValues($metaValue)
    {
        if (is_array($metaValue) && empty($metaValue)) {
            return $metaValue;
        }

        return ($metaValue == null || $metaValue == false || $metaValue == '');
    }

    /**
     * Delete meta data from db if it's deleted from API
     * @param  array $meta    empty meta data fields
     * @param  int   $post_id post object id
     * @return void
     */
    public function removeEmptyMeta($meta, $post_id)
    {
        foreach ($meta as $key => $value) {
            $meta = get_post_meta($post_id, $key, true);
            if ($meta) {
                delete_post_meta($post_id, $key);
            }
        }
    }

    /**
     * Saves the event and it's data
     * @return integer The inserted/updated post id
     */
    public function save()
    {
        $this->beforeSave();

        // Arrays for holding save data
        $post = array();
        $meta = array();
        $post['post_status'] = $this->post_status;

        // Get the default class variables and set it's keys to forbiddenKeys
        $defaultData = get_class_vars(get_class($this));
        $forbiddenKeys = array_keys($defaultData);

        $data = array_filter(get_object_vars($this), function ($item) use ($forbiddenKeys) {
            return !in_array($item, $forbiddenKeys);
        }, ARRAY_FILTER_USE_KEY);

        // If data key is allowed post field add to $post else add to $meta
        foreach ($data as $key => $value) {
            if (in_array($key, $this->allowedPostFields)) {
                $post[$key] = $value;
                continue;
            }

            $meta[$key] = $value;
        }

        // Save empty meta to array
        $empty_meta_values = array_filter($meta, array($this, 'getEmptyValues'));

        // Do not include null values in meta
        $meta = array_filter($meta, array($this, 'removeEmpty'));

        $post['post_type'] = $this->post_type;
        $post['meta_input'] = $meta;

        // Check if duplicate by matching "_event_manager_id" meta value
        if (isset($meta['_event_manager_id'])) {
            $duplicate = self::get(
                1,
                array(
                    'relation' => 'AND',
                    array(
                        'key' => '_event_manager_id',
                        'value' => $meta['_event_manager_id'],
                        'compare' => '='
                    )
                ),
                $this->post_type
            );
        }

        // Update if duplicate
        if (isset($duplicate->ID)) {
            //Check if event needs to be updated
            if (get_post_meta($duplicate->ID, 'last_update', true) != $meta['last_update']) {
                $post['ID'] = $duplicate->ID;
                $this->ID = wp_update_post($post);
                $isDuplicate = true;
            } else {
                return false;
            }
        } else {
            // Create if not duplicate
            $this->ID = wp_insert_post($post, true);
        }

        // Remove empty meta values from db
        $this->removeEmptyMeta($empty_meta_values, $this->ID);

        return $this->afterSave();
    }

    /**
     * Uploads an image from a specified url and sets it as the current post's featured image
     * @param $url
     * @param $featured
     * @return bool|object
     */
    public function setFeaturedImageFromUrl($url, $featured)
    {
        if (!isset($this->ID)) {
            return false;
        }

        if (!isset($url) || strlen($url) === 0 || !$this->isUrl($url)) {
            return false;
        }

        // Upload paths
        $uploadDir = wp_upload_dir();
        $uploadDir = $uploadDir['basedir'];
        $uploadDir = $uploadDir . '/events';

        if (!is_dir($uploadDir)) {
            if (!mkdir($uploadDir, 0776)) {
                return new WP_Error('event', __('Could not create folder', 'event-integration') . ' "' . $uploadDir . '", ' . __('please go ahead and create it manually and rerun the import.', 'event-integration'));
            }
        }

        $filename = sanitize_file_name(basename($url));
        if (stripos(basename($url), '.aspx')) {
            $filename = md5($filename) . '.jpg';
        }

        // Get the file content
        $options = array(
            'ssl' => array(
                'verify_peer' => defined('DEV_MODE') && DEV_MODE === true ? false : true,
                'verify_peer_name' => defined('DEV_MODE') && DEV_MODE === true ? false : true
            )
        );
        $imgContent = file_get_contents($url, false, stream_context_create($options));

        // Check if image already exists in library
        if ($attachmentId = $this->attachmentExists($uploadDir . '/' . basename($filename))) {
            $newImageHash = md5($imgContent);
            $localImageHash = md5_file($uploadDir . '/' . basename($filename));
            // Test if the new image and the local is identical
            if ($newImageHash === $localImageHash) {
                set_post_thumbnail($this->ID, $attachmentId);
                return true;
            } else {
                // Rename filename if the images did not match
                $filename = uniqid() . $filename;
            }
        }

        // Save file to server
        $save = fopen($uploadDir . '/' . $filename, 'w');
        fwrite($save, $imgContent);
        fclose($save);

        // Detect file type
        $filetype = wp_check_filetype($filename, null);

        // Insert the file to media library
        $attachmentId = wp_insert_attachment(array(
            'guid' => $uploadDir . '/' . basename($filename),
            'post_mime_type' => $filetype['type'],
            'post_title' => $filename,
            'post_content' => '',
            'post_status' => 'inherit',
            'post_parent' => $this->ID
        ), $uploadDir . '/' . $filename, $this->ID);

        // Generate attachment meta
        require_once(ABSPATH . 'wp-admin/includes/image.php');
        $attachData = wp_generate_attachment_metadata($attachmentId, $uploadDir . '/' . $filename);
        wp_update_attachment_metadata($attachmentId, $attachData);

        // Set image as featured image or add to gallery
        if ($featured) {
            set_post_thumbnail($this->ID, $attachmentId);
        } else {
            $gallery_meta = get_post_meta($this->ID, 'event_gallery', true);
            if (empty($gallery_meta)) {
                add_post_meta($this->ID, 'event_gallery', array($attachmentId));
            } else {
                $gallery_meta[] .= $attachmentId;
                update_post_meta($this->ID, 'event_gallery', array_unique($gallery_meta));
            }
        }

        return true;
    }

    /**
     * Validates url
     * @param  string $url Url to validate
     * @return boolean
     */
    private function isUrl($url)
    {
        if (is_string($url) && preg_match('/^(?:[;\/?:@&=+$,]|(?:[^\W_]|[-_.!~*\()\[\] ])|(?:%[\da-fA-F]{2}))*$/', $url)) {
            return true;
        }

        return false;
    }

    /**
     * Checks if a attachment src already exists in media library
     * @param  string $src Media url
     * @return mixed
     */
    private function attachmentExists($src)
    {
        global $wpdb;
        $query = "SELECT ID FROM {$wpdb->posts} WHERE guid = '$src'";
        $id = $wpdb->get_var($query);

        if (!empty($id) && $id > 0) {
            return $id;
        }

        return false;
    }
}


<?php

/**
 * Shortcodes to display event submit form
 */

namespace EventManagerIntegration\Shortcodes;

class SubmitForm
{
    public function __construct()
    {
        add_action('init', array($this, 'addShortcodes'));
    }

    /**
     * Create shortcode
     */
    public function addShortcodes()
    {
        add_shortcode('event_submit_form', array($this, 'submitFormCallback'));
    }

    /**
     * Return form markup
     * @param array  $atts    Attributes
     * @param string $content Content
     * @param string $tag     Shortcode name
     * @return string Form markup
     */
    public function submitFormCallback($atts = [], $content = null, $tag = '')
    {
        // Normalize attribute keys, lowercase
        $atts = array_change_key_case((array)$atts, CASE_LOWER);

        // Override default attributes with user attributes
        $data = shortcode_atts([
            'user_groups' => '',
        ], $atts, $tag);

        $data = array_merge($data, \EventManagerIntegration\Helper\SubmitEvent::geFields());

        return \EventManagerIntegration\Helper\RenderBlade::blade(
            'formfields',
             $data,
            EVENTMANAGERINTEGRATION_PATH . 'source/php/Module/SubmitForm/views/'
        );
    }
}


<?php

namespace EventManagerIntegration;

class EventArchive
{
    private $eventPostType = 'event';
    private $dbTable;

    public function __construct()
    {
        //Setup local wpdb instance
        global $wpdb;
        $this->db = $wpdb;
        $this->dbTable = $wpdb->prefix . "integrate_occasions";

        //Run functions if table exists
        if ($this->db->get_var("SHOW TABLES LIKE '" . $this->dbTable . "'") !== null) {
            add_action('pre_get_posts', array($this, 'filterEvents'), 100);
        }

        add_filter('post_type_link', array($this, 'addEventDateArgsToPermalinks'), 10, 3);
        add_filter('get_the_time', array($this, 'filterEventDate'), 10, 3);
    }

    /**
     * Show event start & end date instead of publish date when calling "the_time()" within an event archive
     */
    public function filterEventDate($the_time, $d, $post)
    {
        if ($post->post_type != $this->eventPostType || !isset($post->start_date) || !isset($post->end_date)) {
            return $the_time;
        }

        if (get_option('time_format') == $d) {
            return '';
        }

        return App::formatEventDate($post->start_date, $post->end_date);
    }

    /**
     * Add date arguments to event permalink URL
     */
    public function addEventDateArgsToPermalinks($permalink, $post, $leavename)
    {
        if (!isset($post->start_date) || $post->post_type != $this->eventPostType) {
            return $permalink;
        }

        return esc_url(add_query_arg('date', preg_replace('/\D/', '', $post->start_date), $permalink));
    }

    /**
     * Filter events
     * @param  object $query object WP Query
     */
    public function filterEvents($query)
    {
        if (is_admin() || !is_post_type_archive($this->eventPostType)) {
            return $query;
        }

        $query->set('posts_per_page', 50);

        add_filter('posts_fields', array($this, 'eventFilterSelect'));
        add_filter('posts_join', array($this, 'eventFilterJoin'));
        add_filter('posts_where', array($this, 'eventFilterWhere'), 10, 2);
        add_filter('posts_groupby', array($this, 'eventFilterGroupBy'));
        add_filter('posts_orderby', array($this, 'eventFilterOrderBy'));

        return $query;
    }

    /**
     * Select tables
     * @param  string $select Original query
     * @return string         Modified query
     */
    public function eventFilterSelect($select)
    {
        return $select . ",{$this->dbTable}.start_date,{$this->dbTable}.end_date,{$this->dbTable}.door_time,{$this->dbTable}.status,{$this->dbTable}.exception_information,{$this->dbTable}.content_mode,{$this->dbTable}.content ";
    }

    /**
     * Join taxonomies and postmeta to sql statement
     * @param  string $join current join sql statement
     * @return string       updated statement
     */
    public function eventFilterJoin($join)
    {
        return $join . " LEFT JOIN {$this->dbTable} ON ({$this->db->posts}.ID = {$this->dbTable}.event_id) ";
    }

    /**
     * Add where statements
     * @param  string $where current where statement
     * @return string        updated statement
     */
    public function eventFilterWhere($where)
    {
        $from = null;
        $to = null;

        if (isset($_GET['from']) && !empty($_GET['from'])) {
            $from = sanitize_text_field($_GET['from']);
        }

        if (isset($_GET['to']) && !empty($_GET['to'])) {
            $to = date('Y-m-d', strtotime("+1 day", strtotime(sanitize_text_field($_GET['to']))));
        }

        if (!is_null($from) && !is_null($to)) {
            // USE BETWEEN ON START DATE
            $where = str_replace(
                "{$this->db->posts}.post_date >= '{$from}'",
                "{$this->dbTable}.start_date BETWEEN CAST('{$from}' AS DATE) AND CAST('{$to}' AS DATE)",
                $where
            );
            $where = str_replace(
                "AND {$this->db->posts}.post_date <= '{$to}'",
                "",
                $where
            );
        } elseif (!is_null($from) || !is_null($to)) {
            // USE FROM OR TO
            $where = str_replace("{$this->db->posts}.post_date >=", "{$this->dbTable}.start_date >=", $where);
            $where = str_replace("{$this->db->posts}.post_date <=", "{$this->dbTable}.end_date <=", $where);
        }

        return $where;
    }

    /**
     * Add group by statement
     * @param  string $groupby current group by statement
     * @return string          updated statement
     */
    public function eventFilterGroupBy($groupby)
    {
        global $wpdb;
        $groupby = "{$wpdb->posts}.ID ,{$this->dbTable}.start_date, {$this->dbTable}.end_date";

        return $groupby;
    }

    /**
     * Add group by statement
     * @param  string $groupby current group by statement
     * @return string          updated statement
     */
    public function eventFilterOrderBy($orderby)
    {
        return "{$this->dbTable}.start_date ASC";
    }

}


<?php

namespace EventManagerIntegration\Helper;

class SingleEventData
{
    /**
     * Get single event date
     * @return array
     */
    public static function singleEventDate()
    {
        global $post;

        $singleOccasion = array();
        $get_date = (!empty(get_query_var('date'))) ? get_query_var('date') : false;
        $occasions = QueryEvents::getEventOccasions($post->ID);

        //Get nearest occasions from time if no date arg
        if (!$get_date) {
            $get_date = self::getNextOccasionDate($occasions);
        }

        if (is_array($occasions) && count($occasions) == 1) {
            $singleOccasion = (array)$occasions[0];
            $singleOccasion['formatted'] = \EventManagerIntegration\App::formatEventDate($singleOccasion['start_date'], $singleOccasion['end_date']);
            $singleOccasion['date_parts'] = self::dateParts($singleOccasion['start_date']);
        } elseif (is_array($occasions) && $get_date != false) {
            foreach ($occasions as $occasion) {
                $occasion = (array)$occasion;
                $event_date = preg_replace('/\D/', '', $occasion['start_date']);
                if ($get_date == $event_date) {
                    $singleOccasion = $occasion;
                    $singleOccasion['formatted'] = \EventManagerIntegration\App::formatEventDate($occasion['start_date'], $occasion['end_date']);
                    $singleOccasion['date_parts'] = self::dateParts($occasion['start_date']);
                }
            }
        }

        return $singleOccasion;
    }

    /**
     * Get next event occasion date in time
     * @param  array  $occasions  All occasions of an event
     * @param  string $dateFormat event start date
     * @return string/boolean
     */
    public static function getNextOccasionDate($occasions, $dateFormat = 'YmdHis')
    {
        if (!is_array($occasions) || empty($occasions)) {
            return false;
        }

        $startDates = array();
        foreach ($occasions as $occasion) {
            //Skip if event has ended
            if (time() > strtotime($occasion->end_date) || !isset($occasion->start_date) || !isset($occasion->end_date)) {
                continue;
            }

            $startDates[] = strtotime($occasion->start_date);
        }

        return date($dateFormat, self::getClosest(time(), $startDates));
    }

    /**
     * Get the closest value from array of values
     * @param  string $search Value to compare
     * @param  array  $arr    event start date
     * @return string/boolean
     */
    public static function getClosest($search, $arr)
    {
        $closest = null;
        foreach ($arr as $item) {
            if ($closest === null || abs($search - $closest) > abs($item - $search)) {
                $closest = $item;
            }
        }
        return $closest;
    }

    /**
     * Get date parts as array
     * @param  string $start_date event start date
     * @return array              date values
     */
    public static function dateParts($start_date)
    {
        $start = date('Y-m-d H:i:s', strtotime($start_date));
        $date = array(
            'date' => mysql2date('j', $start, true),
            'month' => mysql2date('F', $start, true),
            'month_short' => substr(mysql2date('F', $start, true), 0, 3),
            'year' => mysql2date('Y', $start, true),
            'time' => mysql2date('H:i', $start, true),
        );

        return $date;
    }
}


<?php

namespace EventManagerIntegration\Admin;

class Options
{
    public function __construct()
    {
        if (function_exists('acf_add_options_sub_page')) {
            acf_add_options_sub_page(array(
                'page_title' => _x('Event Manager Integration settings', 'ACF', 'event-integration'),
                'menu_title' => _x('Options', 'Event Manager Integration settings', 'event-integration'),
                'menu_slug' => 'event-options',
                'parent_slug' => 'edit.php?post_type=event',
                'capability' => 'manage_options'
            ));
        }

        add_action('wp_ajax_save_draw_points', array($this, 'saveDrawPoints'));
        add_action('acf/render_field/type=message', array($this, 'renderAcfField'), 11, 1);
        add_filter('acf/update_value/name=event_import_from_location', array($this, 'updateLocationOption'), 10, 1);
    }

    /**
     * Add custom markup to ACF fields
     * @param $field
     */
    public function renderAcfField($field)
    {
        if ($field['key'] !== 'field_5b0e6394d6399') {
            return;
        }
        echo '<button class="button" id="clear-draw-map">' . __('Clear map', 'event-integration') . '</button>

';
    }

    /**
     * Remove area option if its not selected
     * @param  string $value   the value of the field
     * @return string          the new value
     */
    public function updateLocationOption($value)
    {
        if ($value != 'area') {
            update_option('event_import_area', null);
        }

        return $value;
    }

    /**
     * Save area coordinates as option
     */
    public function saveDrawPoints()
    {
        if (!isset($_POST['coordinates']) && !empty($_POST['coordinates'])) {
            wp_send_json_error("Missing coordinates");
        }

        $points = $_POST['coordinates'];
        // Convert coordinates to float
        foreach ($points as &$point) {
            $point = array_map('floatval', $point);
        }
        update_option('event_import_area', $points);
        wp_send_json_success($points);
    }
}


<?php

namespace EventManagerIntegration;

class App
{
    /* Set to 'dev' or 'min' */
    public static $assetSuffix = 'min';

    public function __construct()
    {
        add_action('admin_init', array($this, 'checkDatabaseTable'));
        add_action('wp_enqueue_scripts', array($this, 'enqueueFront'), 950);
        add_action('admin_enqueue_scripts', array($this, 'enqueueAdmin'));

        /* Register cron action */
        add_action('import_events_daily', array($this, 'importEventsCron'));

        new Api\Events();
        new OAuth\OAuthAdmin();
        new OAuth\OAuthRequests();
        new PostTypes\Events();
        new EventArchive();
        new Acf\AcfConfig();
        new Widget\DisplayEvents();
        new Admin\Options();
        new Admin\AdminDisplayEvent();
        new Shortcodes\SingleEvent();
        new Shortcodes\SubmitForm();

        /* Register Modularity v2 modules */
        add_action('plugins_loaded', function () {
            if (function_exists('modularity_register_module')) {
                modularity_register_module(
                    EVENTMANAGERINTEGRATION_PATH . 'source/php/Module/Event',
                    'Event'
                );

                modularity_register_module(
                    EVENTMANAGERINTEGRATION_PATH . 'source/php/Module/Location',
                    'Location'
                );

                modularity_register_module(
                    EVENTMANAGERINTEGRATION_PATH . 'source/php/Module/SubmitForm',
                    'SubmitForm'
                );
            }
        });

        add_action('widgets_init', function () {
            register_widget('EventManagerIntegration\Widget\DisplayEvents');
        });

        // Add view paths
        add_filter('Municipio/blade/view_paths', array($this, 'addViewPaths'), 2, 1);
    }

    /**
     * Add searchable blade template paths
     * @param array  $array Template paths
     * @return array        Modified template paths
     */
    public function addViewPaths($array)
    {
        // If child theme is active, insert plugin view path after child views path.
        if (is_child_theme()) {
            array_splice( $array, 2, 0, array(EVENTMANAGERINTEGRATION_VIEW_PATH) );
        } else {
            // Add view path first in the list if child theme is not active.
            array_unshift($array, EVENTMANAGERINTEGRATION_VIEW_PATH);
        }

        return $array;
    }

    /**
     * Enqueue required styles and scripts for admin ui
     * @return void
     */
    public function enqueueAdmin()
    {
        // Styles
        wp_register_style('event-integration-admin',
            EVENTMANAGERINTEGRATION_URL . '/dist/' . Helper\CacheBust::name('css/event-manager-integration-admin.css'));
        wp_enqueue_style('event-integration-admin');

        // Scripts
        wp_register_script('event-integration-admin', EVENTMANAGERINTEGRATION_URL . '/dist/' . Helper\CacheBust::name('js/event-integration-admin.js'), true);
        wp_localize_script('event-integration-admin', 'eventintegration', array(
            'ajaxurl' => admin_url('admin-ajax.php')
        ));
        wp_localize_script('event-integration-admin', 'eventIntegrationAdmin', array(
            'loading' => __("Loading", 'event-integration'),
            'options' => array(
                'areaCoordinates' => get_option('event_import_area') ? get_option('event_import_area') : null
            )
        ));
        wp_enqueue_script('event-integration-admin');

        // Re-enqueue Google Maps JS Api with additional libraries: Places, Drawing
        if (isset($_GET['page']) && $_GET['page'] === 'event-options' && $googleApiKey = get_field('google_geocode_key', 'option')) {
            wp_enqueue_script('google-maps-api', '//maps.googleapis.com/maps/api/js?key=' . $googleApiKey . '&libraries=places,drawing', array(), '', true);
        }
    }

    /**
     * Enqueue required styles and scripts on front views
     * @return void
     */
    public function enqueueFront()
    {
        // Styles
        wp_enqueue_style(
            'event-integration',
            EVENTMANAGERINTEGRATION_URL . '/dist/' . Helper\CacheBust::name('css/event-manager-integration.css'));

        // Scripts

        // Google Maps JS Api
        if ($googleApiKey = get_field('google_geocode_key', 'option')) {
            wp_enqueue_script('google-maps-api', '//maps.googleapis.com/maps/api/js?key=' . $googleApiKey . '', array(), '', true);
        }

        wp_register_script('auto-complete', EVENTMANAGERINTEGRATION_URL . '/source/js/vendor/auto-complete/auto-complete.min.js', 'jquery', false, true);
        wp_enqueue_script('auto-complete');

        wp_register_script('event-integration', EVENTMANAGERINTEGRATION_URL . '/dist/' . Helper\CacheBust::name('js/event-integration-front.js'), 'jquery', false, true);
        wp_localize_script('event-integration', 'eventintegration', array(
            'ajaxurl' => admin_url('admin-ajax.php'),
            'apiurl' => get_field('event_api_url', 'option'),
        ));
        wp_localize_script('event-integration', 'eventIntegrationFront', array(
            'event_pagination_error' => __("Something went wrong, please try again later.", 'event-integration'),
            'email_not_matching' => __("The email addresses does not match.", 'event-integration'),
            'must_upload_image' => __("You must upload an image.", 'event-integration'),
        ));
        wp_enqueue_script('event-integration');
    }

    /**
     * Format start and end date
     * @param  string $start_date occasion start date
     * @param  string $end_date   occasion end date
     * @return string             formatted date
     */
    public static function formatEventDate($start_date, $end_date)
    {
        $start = date('Y-m-d H:i:s', strtotime($start_date));
        $end = date('Y-m-d H:i:s', strtotime($end_date));
        $date = mysql2date('j F Y, H:i', $start, true) . ' ' . __('to', 'event-integration') . ' ' . mysql2date('j F Y, H:i', $end, true);
        if (date('Y-m-d', strtotime($start)) == date('Y-m-d', strtotime($end))) {
            $date = mysql2date('j F Y, H:i', $start, true) . ' - ' . mysql2date('H:i', $end, true);
        }

        return $date;
    }

    /**
     * Format short start date
     * @param  string $start_date occasion start date
     * @return array              date values
     */
    public static function formatShortDate($start_date)
    {
        $start = date('Y-m-d H:i:s', strtotime($start_date));
        $today = (date('Ymd') == date('Ymd', strtotime($start_date))) ? true : false;
        $date = array(
            'today' => $today,
            'date' => mysql2date('j', $start, true),
            'month' => substr(mysql2date('F', $start, true), 0, 3),
            'time' => mysql2date('H:i', $start, true),
        );

        return $date;
    }

    /**
     * Format start and end date
     * @param  string $start_date occasion start date
     * @return string             formatted date
     */
    public static function formatDoorTime($door_time)
    {
        $start = date('Y-m-d H:i:s', strtotime($door_time));
        $date = mysql2date('j F Y', $start, true) . ', ' . mysql2date('H:i', $start, true);
        return $date;
    }

    /**
     * Start cron jobs
     * @return void
     */
    public function importEventsCron()
    {
        if (get_field('event_daily_import', 'option') == true && $apiUrl = Helper\ApiUrl::buildApiUrl()) {
            new Parser\EventManagerApi($apiUrl);
        }
    }

    public static function addCronJob()
    {
        wp_schedule_event(time(), 'hourly', 'import_events_daily');
    }

    public static function removeCronJob()
    {
        wp_clear_scheduled_hook('import_events_daily');
    }

    /**
     * Import publishing groups from Event Manager API
     * @return void
     */
    public static function importPublishingGroups()
    {
        $api_url = get_field('event_api_url', 'option');
        if ($api_url) {
            $api_url = rtrim($api_url, '/') . '/user_groups';
            new Parser\EventManagerGroups($api_url);
        }
    }

    /**
     * Create database table if not exist
     * @return void
     */
    public static function checkDatabaseTable()
    {
        global $wpdb;
        $table_name = $wpdb->prefix . 'integrate_occasions';
        if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) {
            self::databaseCreation();
        }

        return;
    }

    /**
     * Creates event occasion database table on plugin activation
     */
    public static function databaseCreation()
    {
        global $wpdb;
        global $event_db_version;
        $table_name = $wpdb->prefix . 'integrate_occasions';
        $charset_collate = $wpdb->get_charset_collate();
        $sql = "CREATE TABLE $table_name (
        ID BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
        event_id BIGINT(20) UNSIGNED NOT NULL,
        start_date DATETIME NOT NULL,
        end_date DATETIME NOT NULL,
        door_time DATETIME DEFAULT NULL,
        status VARCHAR(50) DEFAULT NULL,
        exception_information VARCHAR(400) DEFAULT NULL,
        content_mode VARCHAR(50) DEFAULT NULL,
        content LONGTEXT DEFAULT NULL,
        PRIMARY KEY  (ID)
        ) $charset_collate;";
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);
        add_option('event_db_version', $event_db_version);
    }
}


<?php

namespace EventManagerIntegration;

abstract class Parser
{
    protected $url;
    protected $nrOfNewEvents;

    public function __construct($url)
    {
        ini_set('max_execution_time', 300);

        $this->url = $url;
        $this->nrOfNewEvents = 0;
        $this->start();
    }

    public function getCreatedData()
    {
        return array('events' => $this->nrOfNewEvents);
    }

    /**
     * Check if event already exist and return it's id
     * @param  [type] $event_manager_id [description]
     * @return [type]                   [description]
     */
    public function checkIfEventExists($event_manager_id)
    {
        global $wpdb;
        $results = $wpdb->get_results(
            "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_event_manager_id' AND meta_value = $event_manager_id LIMIT 1",
            ARRAY_A
        );
        if (!empty($results[0]['post_id'])) {
            return $results[0]['post_id'];
        } else {
            return false;
        }
    }

    /**
     * Used to start the parsing
     */
    abstract public function start();

    /**
     * Request to Api
     * @param  string $url Request Url
     * @return array|bool|\WP_Error
     */
    public static function requestApi($url)
    {
        // Dont verify ssl cert in dev mode
        $args = array(
            'timeout' => 120,
            'sslverify' => defined('DEV_MODE') && DEV_MODE == true ? false : true,
            'headers' => array(
                'ClientSolution' => 'WordPress Integration '.EVENTMANAGERINTEGRATION_ID,
                'PhpVersion' => phpversion(),
                'referrer' => get_home_url(),
            ),
        );
        $request = wp_remote_get($url, $args);
        $responseCode = wp_remote_retrieve_response_code($request);
        $body = wp_remote_retrieve_body($request);

        // Decode JSON
        $body = json_decode($body, true);

        // Return null if the request was successful but result is empty
        if (isset($body['code']) && $body['code'] === 'empty_result') {
            return null;
        }

        // Return WP_Error if response code is not 200 OK or result is empty
        if ($responseCode !== 200 || !is_array($body) || empty($body)) {
            return new \WP_Error('error', __('API request failed.', 'event-integration'));
        }

        return $body;
    }

}


<?php

namespace EventManagerIntegration\Helper;

class QueryEvents
{
    /**
     * Get events with occurance date within date range and other parameters
     * @param  array       $params      array of query arguments:
     *                                  string      'start_date'    start date range
     *                                  string      'end_date'      end date range
     *                                  int         'limit'         maximum events to get
     *                                  array       'taxonomies'    array of taxonomies IDs
     *                                  object      'location'      object with location data
     *                                  string      'distance'      max distance from location
     * @param  int          $page       selected pagination page number
     * @return array                    Events list
     */
    public static function getEventsByInterval($params, $page = 1)
    {
        global $wpdb;

        $idString = null;
        // Get event near a location
        $location = !empty($params['location']) ? (array)$params['location'] : null;
        if (!empty($location['lat']) && !empty($location['lng'])) {
            $distance = (!empty($params['distance'])) ? $params['distance'] : 0;
            $locationIds = self::getNearbyLocations($location['lat'], $location['lng'], floatval($distance));
            $idString = ($locationIds) ? implode(',', array_column($locationIds, 'post_id')) : "0";
        }

        // Get events in given language
        $languageId = null;
        if (function_exists('pll_the_languages') && !empty($params['lang'])) {
            $languages = pll_the_languages(array('raw' => 1, 'hide_if_empty' => 0));
            $languageId = $languages[$params['lang']]['id'] ?? null;
        }

        // Build categories string
        $categories = (!empty($params['categories']) && is_array($params['categories'])) ? implode(
            ",",
            $params['categories']
        ) : null;

        // Build tags string
        $tags = (!empty($params['tags']) && is_array($params['tags'])) ? implode(
            ",",
            $params['tags']
        ) : null;

        // Build groups string
        $groups = (!empty($params['groups']) && is_array($params['groups'])) ? implode(
            ",",
            $params['groups']
        ) : null;

        // Search by text string
        $searchString = !empty($params['search_string']) ? $params['search_string'] : null;

        // Filter by age
        $ageGroup = (!empty($params['age_group']) && is_array($params['age_group'])) ? $params['age_group'] : null;

        // Calculate offset
        $page = (!is_numeric($page)) ? 1 : $page;
        $limit = intval($params['display_limit']);
        $offset = ($page - 1) * $limit;

        $db_table = $wpdb->prefix."integrate_occasions";
        $query = "
        SELECT      *, $wpdb->posts.ID AS ID
        FROM        $wpdb->posts
        LEFT JOIN   $db_table ON ($wpdb->posts.ID = $db_table.event_id) ";
        $query .= ($ageGroup) ? "LEFT JOIN $wpdb->postmeta age_from ON $wpdb->posts.ID = age_from.post_id " : '';
        $query .= ($ageGroup) ? "LEFT JOIN $wpdb->postmeta age_to ON $wpdb->posts.ID = age_to.post_id " : '';
        $query .= ($categories) ? "LEFT JOIN $wpdb->term_relationships term1 ON ($wpdb->posts.ID = term1.object_id) " : '';
        $query .= ($tags) ? "LEFT JOIN $wpdb->term_relationships term2 ON ($wpdb->posts.ID = term2.object_id) " : '';
        $query .= ($groups) ? "LEFT JOIN $wpdb->term_relationships term3 ON ($wpdb->posts.ID = term3.object_id) " : '';
        $query .= ($languageId) ? "LEFT JOIN $wpdb->term_relationships term4 ON ($wpdb->posts.ID = term4.object_id) " : '';
        $query .= "
        WHERE $wpdb->posts.post_type = %s
        AND $wpdb->posts.post_status = %s
        AND ($db_table.start_date BETWEEN %s AND %s OR $db_table.end_date BETWEEN %s AND %s) ";

        if ($ageGroup) {
            $query .= "AND (age_from.meta_key = 'age_group_from' AND age_to.meta_key = 'age_group_to' AND (";

            $numItems = count($ageGroup);
            $i = 0;
            foreach ($ageGroup as $key => $age) {
                if (++$i === $numItems) {
                    $query .= "{$age} BETWEEN age_from.meta_value AND age_to.meta_value";
                } else {
                    $query .= "{$age} BETWEEN age_from.meta_value AND age_to.meta_value OR ";
                }
            }
            $query .= ")) ";
        }

        $query .= ($searchString) ? "AND (($wpdb->posts.post_title LIKE %s) OR ($wpdb->posts.post_content LIKE %s))" : '';
        $query .= ($categories) ? "AND (term1.term_taxonomy_id IN ($categories)) " : '';
        $query .= ($tags) ? "AND (term2.term_taxonomy_id IN ($tags)) " : '';
        $query .= ($groups) ? "AND (term3.term_taxonomy_id IN ($groups)) " : '';
        $query .= ($languageId) ? "AND (term4.term_taxonomy_id IN ($languageId)) " : '';
        $query .= ($idString != null) ? "AND ($wpdb->posts.ID IN ($idString)) " : '';
        $query .= "GROUP BY $wpdb->posts.ID, $db_table.start_date, $db_table.end_date ";
        $query .= "ORDER BY $db_table.start_date ASC";
        $query .= ($limit == -1) ? '' : ' LIMIT'.' '.$offset.','.$limit;

        $placeholders = array(
            'event',
            'publish',
            $params['start_date'],
            $params['end_date'],
            $params['start_date'],
            $params['end_date'],
        );
        if ($searchString) {
            $placeholders[] = '%'.$wpdb->esc_like($searchString).'%';
            $placeholders[] = '%'.$wpdb->esc_like($searchString).'%';
        }

        $completeQuery = $wpdb->prepare(
            $query,
            $placeholders
        );
        $events = $wpdb->get_results($completeQuery);

        return $events;
    }

    /**
     * Get event occasions
     * @param  int $post_id post id
     * @param  bool $custom True to get occasions with custom content
     * @return array        object with occasions
     */
    public static function getEventOccasions($post_id, $custom = false)
    {
        global $wpdb;
        $db_table = $wpdb->prefix."integrate_occasions";

        $query = "
        SELECT      *
        FROM        $db_table
        WHERE       $db_table.event_id = %d
        ";
        $query .= ($custom == true) ? "AND $db_table.content_mode = 'custom'" : '';
        $query .= "ORDER BY $db_table.start_date ASC";

        $completeQuery = $wpdb->prepare($query, $post_id);
        $occasions = $wpdb->get_results($completeQuery);

        return $occasions;
    }

    /**
     * Get event meta data
     * @param  int $post_id post id
     * @return array        array with occasions
     */
    public static function getEventMeta($post_id)
    {
        global $wpdb;

        $query = "
        SELECT      *
        FROM        $wpdb->postmeta
        WHERE       $wpdb->postmeta.post_id = %s
        ";

        $completeQuery = $wpdb->prepare($query, $post_id);
        $event_meta = $wpdb->get_results($completeQuery);

        return $event_meta;
    }

    /**
     * Limits event description
     * @param  string $string content string to limit
     * @param  int    $limit  maximum number of letters
     * @return string
     */
    public static function stringLimiter($string, $limit)
    {
        if (strlen($string) <= $limit || $limit == -1) {
            return $string;
        } else {
            $y = mb_substr($string, 0, $limit, "utf-8").'...';

            return $y;
        }
    }

    /**
     * Get event that occurs nearby a locations and within given distance
     * @param  string       $lat       latitude
     * @param  string       $lng       longitude
     * @param  int/float    $distance  radius distance in km
     * @return array with locations
     */
    public static function getNearbyLocations($lat, $lng, $distance = 0)
    {
        global $wpdb;

        // Radius of the earth in kilometers.
        $earth_radius = 6371;
        $sql = $wpdb->prepare(
            "
                SELECT DISTINCT
                    latitude.post_id,
                    post.post_title,
                    latitude.meta_value as lat,
                    longitude.meta_value as lng,
                    (%s * ACOS(
                        COS(RADIANS( %s )) * COS(RADIANS(latitude.meta_value)) * COS(
                        RADIANS(longitude.meta_value) - RADIANS( %s )
                        ) + SIN(RADIANS( %s )) * SIN(RADIANS(latitude.meta_value))
                    )) AS distance
                    FROM $wpdb->posts post
                    INNER JOIN $wpdb->postmeta latitude ON post.ID = latitude.post_id
                    INNER JOIN $wpdb->postmeta longitude ON post.ID = longitude.post_id
                    AND post.post_type   = 'event'
                    AND post.post_status = 'publish'
                    AND latitude.meta_key = 'latitude'
                    AND longitude.meta_key = 'longitude'
                    HAVING distance <= %s
                    ORDER BY distance ASC",
            $earth_radius,
            $lat,
            $lng,
            $lat,
            $distance
        );

        $nearby_locations = $wpdb->get_results($sql, ARRAY_A);

        if ($nearby_locations) {
            return $nearby_locations;
        }
    }

}


<?php

/**
 * Shortcodes to display events with meta data
 */

namespace EventManagerIntegration\Shortcodes;

class SingleEvent
{
    public static $postTypeSlug = 'event';

    public function __construct()
    {
        add_action('init', array($this, 'addShortcodes'));
    }

    // Create shortcodes
    public function addShortcodes()
    {
        add_shortcode('single_event_information', array($this, 'singleEventInformation'));
        add_shortcode('single_event_accordion', array($this, 'singleEventAccordion'));
    }

    /**
     * Unserialize arrays
     * @param  mixed $data data to be formatted
     * @return string
     */
    public static function unserData($data)
    {
        $unserialized = @unserialize($data);
        if ($unserialized !== false) {
            return $unserialized;
        } else {
            return $data;
        }
    }

    /**
     * Shortcode to display complete event information
     * @return string
     */
    public function singleEventAccordion()
    {
        global $post;

        if (!(isset($post->post_type) && $post->post_type === self::$postTypeSlug)) {
            return;
        }

        $id = $post->ID;
        $fields = is_array(get_field('event_shortc_fields', 'options')) ? get_field('event_shortc_fields', 'options') : array();
        $get_meta = get_post_meta($id);
        $occasions = \EventManagerIntegration\Helper\QueryEvents::getEventOccasions($id);
        $query_var_date = (!empty(get_query_var('date'))) ? get_query_var('date') : false;
        $meta = array();

        if (is_array($get_meta) && !empty($get_meta)) {
            foreach ($get_meta as $key => $value) {
                if (is_array($value) && count($value) == 1) {
                    $meta[$key] = self::unserData($value[0]);
                } else {
                    $meta[$key] = $value;
                }
            }
        }

        $i = 0;
        $ret = '
';

        // Information
        $event_info = $this->eventInfo($query_var_date, $occasions, $meta);
        if (in_array('information', $fields) && !empty($event_info)) {
            $i++;
            $ret .= '
';
            $ret .= '<input type="radio" name="active-section" id="accordion-section-' . $i . '">';
            $ret .= '';
            $ret .= '
' . __('Information', 'event-integration') . '
';
            $ret .= '';
            $ret .= '
';
            $ret .= $event_info;
            $ret .= '
';
            $ret .= '
';
        }

        // Location
        if (in_array('location', $fields) && !empty($meta['location'])) {
            $i++;
            $ret .= '
';
            $ret .= '<input type="radio" name="active-section" id="accordion-section-' . $i . '">';
            $ret .= '';
            $ret .= '
' . __('Location', 'event-integration') . '
';
            $ret .= '';
            $ret .= '
';
            $ret .= $this->eventLocation($meta, $fields);
            $ret .= '
';
            $ret .= '
';
        }

        // Booking information
        $booking_info = $this->eventBooking($meta);
        if (in_array('booking', $fields) && strpos($booking_info, '
')) {
            $i++;
            $ret .= '
';
            $ret .= '<input type="radio" name="active-section" id="accordion-section-' . $i . '">';
            $ret .= '';
            $ret .= '
' . __('Booking and tickets', 'event-integration') . '
';
            $ret .= '';
            $ret .= '
';
            $ret .= $booking_info;
            $ret .= '
';
            $ret .= '
';
        }

        // Contact
        $contact_info = $this->eventContact($meta);
        if (in_array('contact', $fields) && !empty($contact_info)) {
            $i++;
            $ret .= '
';
            $ret .= '<input type="radio" name="active-section" id="accordion-section-' . $i . '">';
            $ret .= '';
            $ret .= '
' . __('Contact', 'event-integration') . '
';
            $ret .= '';
            $ret .= '
';
            $ret .= '
' . $contact_info . '
';
            $ret .= '
';
            $ret .= '
';
        }

        // Organizers
        if (!empty($meta['organizers']) && in_array('organizers', $fields)) {
            $i++;
            $ret .= '
';
            $ret .= '<input type="radio" name="active-section" id="accordion-section-' . $i . '">';
            $ret .= '';
            $ret .= '
' . __('Organizer', 'event-integration') . '
';
            $ret .= '';
            $ret .= '
';
            $ret .= $this->eventOrganizer($meta, $fields);
            $ret .= '
';
            $ret .= '
';
        }

        // Occasions
        if (in_array('occasion', $fields)) {
            $i++;
            $ret .= '
';
            $ret .= '<input type="radio" name="active-section" id="accordion-section-' . $i . '">';
            $ret .= '';
            $ret .= '
' . __('Occasions', 'event-integration') . '
';
            $ret .= '';
            $ret .= '
';
            $ret .= $this->eventOccasions($occasions);
            $ret .= '
';
            $ret .= '
';
        }

        // Check if any links exist in array
        $link_keys = array('facebook', 'twitter', 'instagram', 'google_music', 'spotify', 'soundcloud', 'deezer', 'youtube', 'vimeo');
        $links_exist = (count(array_intersect_key(array_flip($link_keys), $meta)) > 0) ? true : false;

        // Social media and streaming links
        if ($links_exist && in_array('links', $fields)) {
            $i++;
            $ret .= '
';
            $ret .= '<input type="radio" name="active-section" id="accordion-section-' . $i . '">';
            $ret .= '';
            $ret .= '
' . __('Links', 'event-integration') . '
';
            $ret .= '';
            $ret .= '
';
            $ret .= $this->eventLinks($meta);
            $ret .= '
';
            $ret .= '
';
        }

        $ret .= '
'; // End accordion

        return $ret;
    }

    /**
     * Shortcode to display complete event information
     * @return string
     */
    public function singleEventInformation()
    {
        global $post;

        if (!(isset($post->post_type) && $post->post_type === self::$postTypeSlug)) {
            return;
        }

        $id = $post->ID;
        // Get custom wrapper and div box classes
        $class = (!empty(get_field('event_shortc_wrapper', 'options'))) ? get_field('event_shortc_wrapper', 'options') : '';
        $class = str_replace(' ', '', $class);
        $wrapper_class = str_replace(',', ' ', $class);
        $class = (!empty(get_field('event_shortc_inner', 'options'))) ? get_field('event_shortc_inner', 'options') : '';
        $class = str_replace(' ', '', $class);
        $box_class = str_replace(',', ' ', $class);
        $fields = is_array(get_field('event_shortc_fields', 'options')) ? get_field('event_shortc_fields', 'options') : array();
        $get_meta = get_post_meta($id);
        $occasions = \EventManagerIntegration\Helper\QueryEvents::getEventOccasions($id);
        $query_var_date = (!empty(get_query_var('date'))) ? get_query_var('date') : false;

        $meta = array();
        foreach ($get_meta as $key => $value) {
            if (is_array($value) && count($value) == 1) {
                $meta[$key] = self::unserData($value[0]);
            } else {
                $meta[$key] = $value;
            }
        }

        $ret = '
';

        // Information
        $event_info = $this->eventInfo($query_var_date, $occasions, $meta);
        if (in_array('information', $fields) && !empty($event_info)) {
            $ret .= '
';
            $ret .= '

' . __('Information', 'event-integration') . '

';
            $ret .= $event_info;
            $ret .= '
';
        }

        // Location
        if (in_array('location', $fields) && !empty($meta['location']['title'])) {
            $ret .= '
';
            $ret .= '

' . __('Location', 'event-integration') . '

';
            $ret .= $this->eventLocation($meta, $fields);
            $ret .= '
';
        }

        // Booking information
        $booking_info = $this->eventBooking($meta);
        if (in_array('booking', $fields) && strpos($booking_info, '
')) {
            $ret .= '
';
            $ret .= '

' . __('Booking and tickets', 'event-integration') . '

';
            $ret .= $booking_info;
            $ret .= '
';
        }

        // Contact
        if (in_array('contact', $fields) && !empty($contact_info = $this->eventContact($meta))) {
            $ret .= '
';
            $ret .= '

' . __('Contact', 'event-integration') . '

';
            $ret .= '
' . $contact_info . '
';
            $ret .= '
';
        }

        // Organizers
        if (!empty($meta['organizers']) && in_array('organizers', $fields)) {
            $ret .= '
';
            $ret .= '

' . __('Organizer', 'event-integration') . '

';
            $ret .= $this->eventOrganizer($meta, $fields);
            $ret .= '
';
        }

        // Occasions
        if (in_array('occasion', $fields)) {
            $ret .= '
';
            $ret .= '

' . __('Occasions', 'event-integration') . '

';
            $ret .= $this->eventOccasions($occasions);
            $ret .= '
';
        }

        // Check if any links exist in array
        $link_keys = array('facebook', 'twitter', 'instagram', 'google_music', 'spotify', 'soundcloud', 'deezer', 'youtube', 'vimeo');
        $links_exist = (count(array_intersect_key(array_flip($link_keys), $meta)) > 0) ? true : false;

        // Social media and streaming links
        if ($links_exist && in_array('links', $fields)) {
            $ret .= '
';
            $ret .= '

' . __('Links', 'event-integration') . '

';
            $ret .= $this->eventLinks($meta);
            $ret .= '
';
        }

        $ret .= '
'; // End event-info-shortcode

        return $ret;
    }

    public static function eventInfo($query_var_date = false, $occasions = array(), $meta = array())
    {
        $ret = '';
        // Get current occasion
        if ($query_var_date) {
            foreach ($occasions as $occasion) {
                $event_date = preg_replace('/\D/', '', $occasion->start_date);
                if ($query_var_date == $event_date) {
                    if (isset($occasion->status) && ($occasion->status != 'scheduled' || !$occasion->status)) {
                        $ret .= '
';
                        $ret .= ($occasion->status == 'cancelled') ? '
' . __('Cancelled', 'event-integration') . '
' : '';
                        $ret .= ($occasion->status == 'rescheduled') ? '
' . __('Rescheduled', 'event-integration') . '
' : '';
                        $ret .= (!empty($occasion->exception_information)) ? '
' . $occasion->exception_information . '
' : '';
                        $ret .= '
';
                    }
                    $ret .= '
';
                    $ret .= '
' . __('Occasion', 'event-integration') . '
';
                    $ret .= (!empty($occasion->start_date) && !empty($occasion->end_date)) ? '
' . \EventManagerIntegration\App::formatEventDate($occasion->start_date, $occasion->end_date) . '
' : '';
                    $ret .= (!empty($occasion->door_time)) ? '
' . __('Door time', 'event-integration') . '
' : '';
                    $ret .= (!empty($occasion->door_time)) ? '
' . \EventManagerIntegration\App::formatDoorTime($occasion->door_time) . '
' : '';
                    $ret .= '
';
                }
            }
        }

        if (!empty($meta['event_link'])) {
            $link = preg_replace("(^https?://)", "", $meta['event_link']);
            $ret .= '

' . $link . '

';
        }

        if (!empty($meta['additional_links']) && is_array($meta['additional_links'])) {
            $ret .= '

' . __('Other links', 'event-integration') . '

';
            $ret .= '
';
            foreach ($meta['additional_links'] as $link) {
                $ret .= '
' . $link['link'] . '
';
            }
            $ret .= '
';
        }

        if (!empty($meta['related_events']) && is_array($meta['related_events'])) {
            $ret .= '

' . __('Related events', 'event-integration') . '

';
            $ret .= '
';
            foreach ($meta['related_events'] as $event) {
                $ret .= '
' . $event['post_title'] . '
';
            }
            $ret .= '
';
        }

        if (!empty($meta['age_group_from']) && !empty($meta['age_group_to'])) {
            $ret .= '

' . __('Age', 'event-integration') . '

' . $meta['age_group_from'] . '-' . $meta['age_group_to'] . ' ' . __('years', 'event-integration') . '

';
        } elseif(!empty($meta['age_group_from'])) {
            $ret .= '

' . __('Age', 'event-integration') . '

' . __('From', 'event-integration') . ' ' . $meta['age_group_from'] . ' ' . __('years', 'event-integration') . '

';
        } elseif(!empty($meta['age_group_to'])) {
            $ret .= '

' . __('Age', 'event-integration') . '

' . __('Up to', 'event-integration') . ' ' . $meta['age_group_to'] . ' ' . __('years', 'event-integration') . '

';
        }

        if (!empty($meta['accessibility']) && is_array($meta['accessibility'])) {
            $ret .= '

' . __('Accessibility on the location', 'event-integration') . '

';
            $ret .= '
';
            foreach ($meta['accessibility'] as $accessibility) {
                $ret .= '
' . $accessibility . '
';
            }
            $ret .= '
';
        }

        return $ret;
    }

    public static function eventLocation($meta = array(), $fields = array('additional_locations'))
    {
        $ret = '';
        $location = (isset($meta['location'])) ? $meta['location'] : null;

        // Google Map
        if (in_array('map', $fields) && !empty($meta['latitude']) && !empty($meta['latitude'])) {
            $locationTitle = $location['title'] ?? '';
            $ret .= '

';
        }
        if (is_array($location) && !empty($location)) {
            $ret .= '
';
            $ret .= (!empty($location['title'])) ? '
' . $location['title'] . '
' : '';
            $ret .= (!empty($location['street_address'])) ? '
' . $location['street_address'] . '
' : '';
            $ret .= (!empty($location['postal_code'])) ? '
' . $location['postal_code'] . '
' : '';
            $ret .= (!empty($location['city'])) ? '
' . $location['city'] . '
' : '';
            $ret .= '
';
        }
        // Additional locations
        if (in_array('additional_locations', $fields) && !empty($meta['additional_locations'])) {
            $ret .= '

' . __('Other locations', 'event-integration') . '

';
            if (is_array($meta['additional_locations']) && !empty($meta['additional_locations'])) {
                foreach ($meta['additional_locations'] as $location) {
                    $ret .= '
';
                    $ret .= (!empty($location['title'])) ? '
' . $location['title'] . '
' : '';
                    $ret .= (!empty($location['street_address'])) ? '
' . $location['street_address'] . '
' : '';
                    $ret .= (!empty($location['postal_code'])) ? '
' . $location['postal_code'] . '
' : '';
                    $ret .= (!empty($location['city'])) ? '
' . $location['city'] . '
' : '';
                    $ret .= '
';
                }
            }
        }

        return $ret;
    }

    public static function eventBooking($meta = array())
    {
        $ret = '
';
        $ret .= (!empty($meta['booking_link'])) ? '
' . __('Book here', 'event-integration') . '
' : '';
        $ret .= (!empty($meta['booking_phone'])) ? '
' . $meta['booking_phone'] . '
' : '';
        $ret .= (!empty($meta['booking_email'])) ? '
' . $meta['booking_email'] . '

' : '';
        $ret .= (isset($meta['price_adult']) && $meta['price_adult'] != '') ? '
' . __('Adult', 'event-integration') . ': ' . self::priceOutput($meta['price_adult']) . '
' : '';
        $children_age = (!empty($meta['children_age'])) ? ' (' . __('below', 'event-integration') . ' ' . $meta['children_age'] . ')' : '';
        $ret .= (isset($meta['price_children']) && $meta['price_children'] != '') ? '
' . __('Children', 'event-integration') . $children_age . ': ' . self::priceOutput($meta['price_children']) . '
' : '';
        $senior_age = (!empty($meta['senior_age'])) ? ' (' . __('above', 'event-integration') . ' ' . $meta['senior_age'] . ')' : '';
        $ret .= (isset($meta['price_senior']) && $meta['price_senior'] != '') ? '
' . __('Senior', 'event-integration') . $senior_age . ': ' . self::priceOutput($meta['price_senior']) . '
' : '';
        $ret .= (isset($meta['price_student']) && $meta['price_student'] != '') ? '
' . __('Student', 'event-integration') . ': ' . self::priceOutput($meta['price_student']) . '
' : '';
        $ret .= (!empty($meta['age_restriction'])) ? '
' . __('Age restriction', 'event-integration') . ': ' . $meta['age_restriction'] . ' ' . __('years', 'event-integration') . '
' : '';
        $ret .= (!empty($meta['ticket_release_date'])) ? '
' . __('Ticket release date', 'event-integration') . ': ' . mysql2date('j F Y, H:i', $meta['ticket_release_date'], true) . '
' : '';
        $ret .= (isset($meta['tickets_remaining']) && $meta['tickets_remaining'] != '') ? '
' . __('Tickets remaining', 'event-integration') . ': ' . $meta['tickets_remaining'] . '
' : '';
        $ret .= '
';
        if (!empty($meta['additional_ticket_types']) && is_array($meta['additional_ticket_types'])) {
            $ret .= '

' . __('Ticket types', 'event-integration') . '

';
            foreach ($meta['additional_ticket_types'] as $index => $ticketType) {
                $ret .= '
';
                $ret .= (!empty($ticketType['ticket_name'])) ? '
' . $ticketType['ticket_name'] . '
' : '';
                if (!empty($ticketType['ticket_type'])) {
                    switch (strtolower($ticketType['ticket_type'])) {
                        case('seated'):
                            $ret .= '
' . __('Seated', 'event-integration') . '
';
                            break;
                        case('standing'):
                            $ret .= '
' . __('Standing', 'event-integration') . '
';
                            break;
                    }
                }
                $ret .= (isset($ticketType['minimum_price']) && $ticketType['minimum_price'] != '') ? '
' . __('Minimum price', 'event-integration') . ': ' . self::priceOutput($ticketType['minimum_price']) . '
' : '';
                $ret .= (isset($ticketType['maximum_price']) && $ticketType['maximum_price'] != '') ? '
' . __('Maximum price', 'event-integration') . ': ' . self::priceOutput($ticketType['maximum_price']) . '
' : '';
                $ret .= '
';
            }
        }
        $ret .= '
';
        $ret .= '
';
        $ret .= '
';
        if (!empty($meta['price_range']) && is_array($meta['price_range']) && !empty(array_filter($meta['price_range']))) {
            $ret .= '

' . __('Price range', 'event-integration') . '

';
            $ret .= '
';
            $ret .= (isset($meta['price_range']['seated_minimum_price']) && $meta['price_range']['seated_minimum_price'] != '') ? '
' . __('Seated minimum price', 'event-integration') . ': ' . self::priceOutput($meta['price_range']['seated_minimum_price']) . '
' : '';
            $ret .= (isset($meta['price_range']['seated_maximum_price']) && $meta['price_range']['seated_maximum_price'] != '') ? '
' . __('Seated maximum price', 'event-integration') . ': ' . self::priceOutput($meta['price_range']['seated_maximum_price']) . '
' : '';
            $ret .= (isset($meta['price_range']['standing_minimum_price']) && $meta['price_range']['standing_minimum_price'] != '') ? '
' . __('Standing minimum price', 'event-integration') . ': ' . self::priceOutput($meta['price_range']['standing_minimum_price']) . '
' : '';
            $ret .= (isset($meta['price_range']['standing_maximum_price']) && $meta['price_range']['standing_maximum_price'] != '') ? '
' . __('Standing maximum price', 'event-integration') . ': ' . self::priceOutput($meta['price_range']['standing_maximum_price']) . '
' : '';
            $ret .= '
';
        }
        $ret .= '
';
        $ret .= '
';
        if (!empty($meta['additional_ticket_retailers']) && is_array($meta['additional_ticket_retailers'])) {
            $ret .= '

' . __('Ticket retailers', 'event-integration') . '

';
            foreach ($meta['additional_ticket_retailers'] as $index => $retailer) {
                $ret .= '
';
                $ret .= (!empty($retailer['retailer_name'])) ? '
' . $retailer['retailer_name'] . '
' : '';
                $ret .= (!empty($retailer['booking_url'])) ? '
' . preg_replace("(^https?://)", "", $retailer['booking_url']) . '
' : '';
                $ret .= (!empty($retailer['ticket_release_date'])) ? '
' . __('Ticket release date', 'event-integration') . ': ' . mysql2date('j F Y, H:i', $retailer['ticket_release_date'], true) . '
' : '';
                $ret .= (!empty($retailer['ticket_stop_date'])) ? '
' . __('Ticket release date', 'event-integration') . ': ' . mysql2date('j F Y, H:i', $retailer['ticket_stop_date'], true) . '
' : '';

                $ret .= '
';
            }
        }
        $ret .= '
';
        $ret .= '
';
        if (!empty($meta['booking_group']) && is_array($meta['booking_group'])) {
            $ret .= '

' . __('Group prices', 'event-integration') . '

';
            $ret .= '
';
            foreach ($meta['booking_group'] as $group) {
                $ret .= '
' . $group['min_persons'] . ' - ' . $group['max_persons'] . ' ' . __('persons', 'event-integration') . ': ' . self::priceOutput($group['price_group']) . '
';
            }
            $ret .= '
';
        }
        $ret .= '
';
        if (!empty($meta['membership_cards']) && is_array($meta['membership_cards'])) {
            $ret .= '

' . __('Included in membership cards', 'event-integration') . '

';
            $ret .= '
';
            foreach ($meta['membership_cards'] as $card) {
                $ret .= '
' . $card['post_title'] . '
';
            }
            $ret .= '
';
        }
        $ret .= (!empty($meta['price_information'])) ? '

' . __('Price information', 'event-integration') . '

 ' . $meta['price_information'] . '

' : '';
        $ret .= (!empty($meta['ticket_includes'])) ? '

' . __('Ticket includes', 'event-integration') . '

 ' . $meta['ticket_includes'] . '

' : '';

        return $ret;
    }

    public static function eventContact($meta = array())
    {
        $ret = '';
        $ret .= (!empty($meta['contact_information'])) ? '
' . $meta['contact_information'] . '

' : '';
        $ret .= (!empty($meta['contact_phone'])) ? '
' . $meta['contact_phone'] . '
' : '';
        $ret .= (!empty($meta['contact_email'])) ? '
' . $meta['contact_email'] . '
' : '';

        return $ret;
    }

    public static function eventOrganizer($meta = array(), $fields = array('sponsors'))
    {
        $ret = '';
        if (!empty($meta['organizers']) && is_array($meta['organizers'])) {
            foreach ($meta['organizers'] as $organizer) {
                $ret .= '
';
                $ret .= (!empty($organizer['organizer'])) ? '
' . $organizer['organizer'] . '
' : '';
                $ret .= (!empty($organizer['organizer_phone'])) ? '
' . $organizer['organizer_phone'] . '
' : '';
                $ret .= (!empty($organizer['organizer_email'])) ? '
' . $organizer['organizer_email'] . '
' : '';
                $ret .= (!empty($organizer['organizer_link'])) ? '
' . preg_replace("(^https?://)", "", $organizer['organizer_link']) . '
' : '';
                $ret .= '
';
            }

            // Sponsors
            if (in_array('sponsors', $fields) && !empty($meta['supporters'])) {
                $ret .= '

' . __('Sponsors', 'event-integration') . '

';
                if (is_array($meta['supporters']) && !empty($meta['supporters'])) {
                    foreach ($meta['supporters'] as $sponsor) {
                        $ret .= '
';
                        $ret .= (!empty($sponsor['post_title'])) ? '
' . $sponsor['post_title'] . '
' : '';
                        $ret .= '
';
                    }
                }
            }
        }

        return $ret;
    }

    public static function eventOccasions($occasions = array())
    {
        $ret = '';
        foreach ($occasions as $occasion) {
            $date = (!empty($occasion->start_date) && !empty($occasion->end_date)) ? \EventManagerIntegration\App::formatEventDate($occasion->start_date, $occasion->end_date) : '';
            $date .= (isset($occasion->status) && $occasion->status == 'cancelled') ? ' - ' . __('Cancelled', 'event-integration') . '' : '';
            $date .= (isset($occasion->status) && $occasion->status == 'rescheduled') ? ' - ' . __('Rescheduled', 'event-integration') . '' : '';
            $ret .= '

';
            $ret .= $date;
            $ret .= '

';
        }

        return $ret;
    }

    public static function eventLinks($meta = array())
    {
        $ret = '
';
        $ret .= (!empty($meta['facebook'])) ? '
Facebook
' : '';
        $ret .= (!empty($meta['twitter'])) ? '
Twitter
' : '';
        $ret .= (!empty($meta['instagram'])) ? '
Instagram
' : '';
        $ret .= (!empty($meta['google_music'])) ? '
Google Music
' : '';
        $ret .= (!empty($meta['spotify'])) ? '
Spotify
' : '';
        $ret .= (!empty($meta['soundcloud'])) ? '
Soundcloud
' : '';
        $ret .= (!empty($meta['deezer'])) ? '
Deezer
' : '';
        if (!empty($meta['youtube']) && is_array($meta['youtube'])) {
            foreach ($meta['youtube'] as $youtube) {
                $ret .= '
YouTube
';
            }
        }
        if (!empty($meta['vimeo']) && is_array($meta['vimeo'])) {
            foreach ($meta['vimeo'] as $vimeo) {
                $ret .= '
Vimeo
';
            }
        }
        $ret .= '
';

        return $ret;
    }

    public static function eventTaxonomies($meta = array())
    {
        $ret = '
';
        if (isset($meta['groups']) && !empty($meta['groups']) && is_array($meta['groups'])) {
            $ret .= '

' . __('Groups', 'event-integration') . '

';
            foreach ($meta['groups'] as $group) {
                $ret .= '
' . $group['name'] . '
';
            }
        }
        if (isset($meta['categories']) && !empty($meta['categories']) && is_array($meta['categories'])) {
            $ret .= '

' . __('Categories', 'event-integration') . '

';
            foreach ($meta['categories'] as $category) {
                $ret .= '
' . $category . '
';
            }
        }
        if (isset($meta['tags']) && !empty($meta['tags']) && is_array($meta['tags'])) {
            $ret .= '

' . __('Tags', 'event-integration') . '

';
            foreach ($meta['tags'] as $tag) {
                $ret .= '
' . $tag . '
';
            }
        }
        $ret .= '
';

        return $ret;
    }

    /**
     * Outputs prices
     * @param $price
     * @return string
     */
    public static function priceOutput($price)
    {
        if ($price == 0) {
            $price = _x('Free', 'Free event entrance', 'event-integration');
        } elseif ($price !== '') {
            $price .= ' kr';
        }

        return $price;
    }
}


<?php

namespace EventManagerIntegration\Widget;

use \EventManagerIntegration\Helper\QueryEvents as QueryEvents;

class DisplayEvents extends \WP_Widget
{
    public function __construct()
    {
        parent::__construct(
            'display_events',
            __('Events', 'event-integration'),
            array(
                "description" => __('Display upcoming events from Event Manager API.', 'event-integration')
            )
        );
    }

    /**
    * Outputs the content for the current Display Events widget instance.
    *
    * @param array $args     Widget arguments.
    * @param array $instance Saved values from database.
    */
    public function widget($args, $instance)
    {
        if ( ! isset( $args['widget_id'] ) ) {
            $args['widget_id'] = $this->id;
        }
        $title = ( ! empty( $instance['title'] ) ) ? $instance['title'] : __( 'Recent Posts' );
        $title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
        $limit = isset($instance['limit']) ? intval($instance['limit']) : null;
        $days_ahead = isset($instance['days_ahead']) ? $instance['days_ahead'] : null;
        $show_date = isset( $instance['show_date'] ) ? $instance['show_date'] : true;
        $show_image = isset( $instance['show_image'] ) ? $instance['show_image'] : false;
        $show_content = isset( $instance['show_content'] ) ? $instance['show_content'] : false;
        $show_location = isset( $instance['show_location'] ) ? $instance['show_location'] : false;
        $content_limit = isset($instance['content_limit']) ? $instance['content_limit'] : null;

        $start_date = date('Y-m-d H:i:s', strtotime("today midnight"));
        $end_date = date('Y-m-d H:i:s', strtotime("tomorrow midnight +$days_ahead days") - 1);
        $params = array('start_date'    => $start_date,
                        'end_date'      => $end_date,
                        'display_limit' => $limit
                        );
        $events = QueryEvents::getEventsByInterval($params);

        ?>
        <?php echo $args['before_widget']; ?>
        <?php if ( $title ) {
            echo $args['before_title'] . $title . $args['after_title'];
        } ?>

        <?php if (!$events) : ?>

<?php _e('No events found', 'event-integration'); ?>

        <?php else: ?>
        <?php foreach ($events as $event) : ?>

            <?php if ($show_image) : ?>
                <?php echo get_the_post_thumbnail( $event->ID, 'large', array('class' => 'event-img')); ?>
            <?php endif; ?>

            <?php if (! empty($event->post_title && isset($event->post_title))) : ?>
                <?php $date_url = preg_replace('/\D/', '', $event->start_date); ?>
                <a href="<?php echo esc_url( add_query_arg( 'date', $date_url, get_page_link($event->ID) ) )?>"><?php echo $event->post_title ?>
            <?php endif; ?>

            <?php if ($show_date && ! empty($event->start_date && isset($event->start_date))) : ?>
                <?php echo \EventManagerIntegration\App::formatEventDate($event->start_date, $event->end_date) ?>
            <?php endif; ?>

           <?php if ($show_location && get_post_meta($event->ID, 'location', true)) : ?>
                <?php $location = get_post_meta($event->ID, 'location', true); ?>
                <?php echo sprintf(__('Location: %s', 'event-integration'), $location['title']) ?>
            <?php endif; ?>

            <?php if ($show_content && $event->content_mode == 'custom' && ! empty($event->content)) : ?>
                <?php echo QueryEvents::stringLimiter( $event->content, $content_limit ); ?>
            <?php elseif($show_content && ! empty($event->post_content)): ?>
                <?php echo QueryEvents::stringLimiter( $event->post_content, $content_limit ); ?>
            <?php endif; ?>

        <?php endforeach; ?>
        <?php endif; ?>

        <?php echo $args['after_widget'];
    }

    /**
    * Handles updating the settings for the current Display Events widget instance.
    *
    * @param array $new_instance Values just sent to be saved.
    * @param array $old_instance Previously saved values from database.
    *
    * @return array Updated safe values to be saved.
    */
    public function update($new_instance, $old_instance)
    {
        $instance = array();
        $instance['title'] = (! empty($new_instance['title'])) ? strip_tags($new_instance['title']) : '';
        $instance['limit'] = absint($new_instance['limit']);
        $instance['days_ahead'] = absint($new_instance['days_ahead']);
        $instance['show_date'] = isset( $new_instance['show_date'] ) ? (bool) $new_instance['show_date'] : false;
        $instance['show_location'] = isset( $new_instance['show_location'] ) ? (bool) $new_instance['show_location'] : false;
        $instance['show_image'] = isset( $new_instance['show_image'] ) ? (bool) $new_instance['show_image'] : false;
        $instance['show_content'] = isset( $new_instance['show_content'] ) ? (bool) $new_instance['show_content'] : false;
        $instance['content_limit'] = $new_instance['content_limit'];
        return $instance;
    }

    /**
    * Outputs the settings form for the Display Events widget.
    *
    * @param array $instance Previously saved values from database.
    */
    public function form($instance)
    {
        $title = ! empty( $instance['title'] ) ? $instance['title'] : '';
        $limit = isset( $instance['limit'] ) ? $instance['limit'] : 5;
        $days_ahead = isset( $instance['days_ahead'] ) ? absint( $instance['days_ahead'] ) : 5;
        $show_date = isset( $instance['show_date'] ) ? (bool) $instance['show_date'] : true;
        $show_image = isset( $instance['show_image'] ) ? (bool) $instance['show_image'] : false;
        $show_content = isset( $instance['show_content'] ) ? (bool) $instance['show_content'] : false;
        $show_location = isset( $instance['show_location'] ) ? (bool) $instance['show_location'] : false;
        $content_limit = isset( $instance['content_limit'] ) ? $instance['content_limit'] : -1;
        ?>

<label for="<?php echo esc_attr($this->get_field_id('title'));?>"><?php esc_attr_e('Title:', 'event-integration'); ?>
        <input class="widefat" id="<?php echo esc_attr($this->get_field_id('title')); ?>" name="<?php echo esc_attr($this->get_field_name('title')); ?>" type="text" value="<?php echo esc_attr($title);?>">

<label for="<?php echo esc_attr($this->get_field_id('limit'));?>"><?php esc_attr_e('Number of events to show:', 'event-integration'); ?>
        <input class="tiny-text" id="<?php echo esc_attr($this->get_field_id('limit')); ?>" name="<?php echo esc_attr($this->get_field_name('limit')); ?>" type="number" value="<?php echo esc_attr($limit);?>">

<label for="<?php echo esc_attr($this->get_field_id('days_ahead'));?>"><?php esc_attr_e('Days interval:', 'event-integration'); ?>
        <input id="<?php echo esc_attr($this->get_field_id('days_ahead')); ?>" name="<?php echo esc_attr($this->get_field_name('days_ahead')); ?>" type="number" min="0" size="5" value="<?php echo esc_attr($days_ahead);?>">

<input class="checkbox" type="checkbox"<?php checked( $show_date ); ?> id="<?php echo $this->get_field_id( 'show_date' ); ?>" name="<?php echo $this->get_field_name( 'show_date' ); ?>" />
        <label for="<?php echo $this->get_field_id( 'show_date' ); ?>"><?php _e( 'Display date', 'event-integration' ); ?>

<input class="checkbox" type="checkbox"<?php checked( $show_image ); ?> id="<?php echo $this->get_field_id( 'show_image' ); ?>" name="<?php echo $this->get_field_name( 'show_image' ); ?>" />
        <label for="<?php echo $this->get_field_id( 'show_image' ); ?>"><?php _e( 'Display featured image', 'event-integration' ); ?>

<input class="checkbox" type="checkbox"<?php checked( $show_location ); ?> id="<?php echo $this->get_field_id( 'show_location' ); ?>" name="<?php echo $this->get_field_name( 'show_location' ); ?>" />
        <label for="<?php echo $this->get_field_id( 'show_location' ); ?>"><?php _e( 'Display location', 'event-integration' ); ?>

<input class="checkbox" type="checkbox"<?php checked( $show_content ); ?> id="<?php echo $this->get_field_id( 'show_content' ); ?>" name="<?php echo $this->get_field_name( 'show_content' ); ?>" />
        <label for="<?php echo $this->get_field_id( 'show_content' ); ?>"><?php _e( 'Display description', 'event-integration' ); ?>

<label for="<?php echo esc_attr($this->get_field_id('content_limit'));?>"><?php esc_attr_e('Description letter limit:', 'event-integration'); ?>
        <input id="<?php echo esc_attr($this->get_field_id('content_limit')); ?>" name="<?php echo esc_attr($this->get_field_name('content_limit')); ?>" type="number" min="-1" size="5" placeholder="<?php _e( '-1 equals no limit', 'event-integration' ); ?>" value="<?php echo esc_attr($content_limit);?>">

        <?php
    }

}