<?php

namespace EventManagerIntegration\Module;

class Event extends \Modularity\Module
{
    public $slug = 'event';
    public $supports = array();
    public $templateDir = EVENTMANAGERINTEGRATION_PATH.'source/php/Module/Event/views/';
    public $template;
    public $lang;

    public function init()
    {
        $this->nameSingular = __('Event', 'event-integration');
        $this->namePlural = __('Events', 'event-integration');
        $this->description = __('Outputs a list if upcoming events', 'event-integration');
        $this->lang = function_exists('pll_current_language') ? pll_current_language('slug') : null;

        add_action('wp_ajax_nopriv_ajax_pagination', array($this, 'ajaxPagination'));
        add_action('wp_ajax_ajax_pagination', array($this, 'ajaxPagination'));

        add_filter(
            'acf/fields/taxonomy/wp_list_categories/name=mod_event_categories_list',
            array($this, 'filterEventCategories'), 10, 2
        );
    }

    public function template()
    {
        $this->getTemplateData($this->template);

        return 'event-'.$this->template.'.blade.php';
    }

    public function getTemplateData($template)
    {
        $template = explode('-', $template);
        $template = array_map('ucwords', $template);
        $template = implode('', $template);
        $class = '\EventManagerIntegration\Module\Event\TemplateController\\'.$template.'Template';

        if (class_exists($class)) {
            $controller = new $class($this, $this->args, $this->data);
            $this->data = array_merge($this->data, $controller->data);
        }

    }

    public function data(): array
    {
        if (get_option('timezone_string')) {
            date_default_timezone_set(get_option('timezone_string'));
        }

        $id = $this->ID ?? $_POST['id'] ?? null;
        $page = (!empty($_POST['page'])) ? $_POST['page'] : 1;
        $data = get_fields($id);

        // Cards module data
        $data['settings'] = $data;
        $data['nonce'] = wp_create_nonce('wp_rest');
        $this->template = !empty($data['mod_event_display']) ? $data['mod_event_display'] : 'list';
        $data['template'] = $this->template;
        $data['archive_url'] = get_post_type_archive_link('event');
        $data['rest_url'] = get_rest_url();
        $days_ahead = isset($data['mod_event_interval']) ? $data['mod_event_interval'] : 0;
        $data['start_date'] = date('Y-m-d', strtotime("now"));
        $data['end_date'] = date('Y-m-d', strtotime("today midnight +$days_ahead days"));
        $data['lat'] = (isset($data['mod_event_geographic']['lat'])) ? $data['mod_event_geographic']['lat'] : null;
        $data['lng'] = (isset($data['mod_event_geographic']['lng'])) ? $data['mod_event_geographic']['lng'] : null;
        $data['distance'] = (isset($data['mod_event_distance'])) ? $data['mod_event_distance'] : null;
        $data['age_range'] = $this->getAgeFilterRange($id);
        // Taxonomies filter
        $data['categories'] = $this->getFilterableCategories($id);
        $data['groups'] = $this->getModuleGroups($id);
        $data['tags'] = $this->getModuleTags($id);

        // List module data
        $data['pagesCount'] = $this->countPages($id);
        $data['events'] = $this->getEvents($id, $page);
        $data['mod_event_fields'] = isset($data['mod_event_fields']) && is_array($data['mod_event_fields'])
            ? $data['mod_event_fields'] : array();
        $data['descr_limit'] = !empty($data['mod_event_descr_limit']) ? $data['mod_event_descr_limit'] : null;
        $data['date_now'] = strtotime('now');

        // Language
        $data['lang'] = $this->lang;

        $data['classes'] = !empty($this->args) ? implode(
            ' ',
            apply_filters('Modularity/Module/Classes', array('box', 'box-panel'), 'mod-event', $this->args)
        ) : array();

        return $data;
    }

    /**
     * Get events on pagination click
     */
    public function ajaxPagination()
    {
        echo \EventManagerIntegration\Helper\RenderBlade::blade(
            'partials/list',
            $this->data(),
            $this->templateDir
        );

        wp_die();
    }

    public function countPages($module_id)
    {
        $fields = json_decode(json_encode(get_fields($module_id)));

        if (isset($fields->mod_event_limit) && $fields->mod_event_limit == 0) {
            return $fields->mod_event_limit;
        }

        $max_per_page = (isset($fields->mod_event_limit)) ? $fields->mod_event_limit : 10;
        $events = self::getEvents($module_id, 1, false);
        $total_posts = count($events);  //Total number of posts returned
        $pages = ceil($total_posts / $max_per_page);

        if (isset($fields->mod_event_pagination_limit) && $fields->mod_event_pagination_limit >= 0
            && $fields->mod_event_pagination_limit <= $pages) {
            $pages = $fields->mod_event_pagination_limit;
        }

        return $pages;
    }

    /**
     * Get included Events
     * @param  int  $module_id Module ID
     * @param  int  $page      Pagination page number
     * @param  bool $useLimit  True = limit by setting, false = get all
     * @return array           Array with event objects
     */
    public function getEvents($module_id, $page = 1, $useLimit = true)
    {
        $fields = json_decode(json_encode(get_fields($module_id)));
        $display_limit = ($useLimit == true && isset($fields->mod_event_limit)) ? $fields->mod_event_limit : -1;
        $days_ahead = isset($fields->mod_event_interval) ? $fields->mod_event_interval : 0;

        // Set start and end date
        $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);

        // Save categories, groups and tags IDs as arrays
        $categories = $this->getModuleCategories($module_id);
        $tags = $this->getModuleTags($module_id);
        $groups = $this->getModuleGroups($module_id);

        $taxonomies = (!empty($taxonomies)) ? $taxonomies : null;
        $params = array(
            'start_date' => $start_date,
            'end_date' => $end_date,
            'display_limit' => $display_limit,
            'categories' => $categories,
            'tags' => $tags,
            'groups' => $groups,
            'location' => (isset($fields->mod_event_geographic)) ? $fields->mod_event_geographic : null,
            'distance' => (isset($fields->mod_event_distance)) ? $fields->mod_event_distance : null,
            'lang' => $this->lang,
        );

        $events = \EventManagerIntegration\Helper\QueryEvents::getEventsByInterval($params, $page);

        return $events;
    }

    /**
     * Return categories filter
     * @param int $moduleId The module ID
     * @return array|null
     */
    public function getModuleCategories($moduleId): array
    {
        $categories = array();
        $showAllCategories = get_field('mod_event_categories_show', $moduleId);

        $moduleCategories = get_field('mod_event_categories_list', $moduleId);
        if ($showAllCategories === false && !empty($moduleCategories) && is_array($moduleCategories)) {
            $categories = $moduleCategories;
        }

        return $categories;
    }

    /**
     * Return categories available for filtering
     * @param int $moduleId The module ID
     * @return array|null
     */
    public function getFilterableCategories($moduleId): array
    {
        $categories = array();
        $showAllCategories = get_field('mod_event_categories_show', $moduleId);

        $moduleCategories = get_field('mod_event_categories_list', $moduleId);
        if ($showAllCategories === false && !empty($moduleCategories) && is_array($moduleCategories)) {
            $categories = $moduleCategories;
            foreach ($categories as $key => &$category) {
                $category = get_term($category, 'event_categories');
                // If category is missing, remove it from the list
                if (!$category) {
                    unset($categories[$key]);
                }
            }
        } else {
            $categories = get_terms(
                'event_categories',
                array(
                    'hide_empty' => false,
                )
            );
        }

        $categories = \EventManagerIntegration\Helper\Translations::filterTermsByLanguage($categories);

        foreach ($categories as &$category) {
            $category = array(
                'id' => $category->term_id,
                'title' => $category->name,
                'checked' => false,
            );
        }

        return $categories;
    }

    /**
     * Return groups filter
     * @param int $moduleId The module ID
     * @return array|null
     */
    public function getModuleGroups($moduleId): array
    {
        $groups = array();
        $showAllGroups = get_field('mod_event_groups_show', $moduleId);

        $moduleGroups = get_field('mod_event_groups_list', $moduleId);
        if ($showAllGroups === false && !empty($moduleGroups) && is_array($moduleGroups)) {
            $groups = $moduleGroups;
        }

        return $groups;
    }

    /**
     * Return tags filter
     * @param int $moduleId The module ID
     * @return array|null
     */
    public function getModuleTags($moduleId): array
    {
        $tags = array();
        $showAllTags = get_field('mod_event_tags_show', $moduleId);

        $moduleTags = get_field('mod_event_tags_list', $moduleId);
        if ($showAllTags === false && !empty($moduleTags) && is_array($moduleTags)) {
            $tags = $moduleTags;
        }

        return $tags;
    }

    /**
     * Return list with filterable ages
     * @param int $moduleId The module ID
     * @return array
     */
    public function getAgeFilterRange($moduleId): array
    {
        $years = array();
        $from = (int)get_field('mod_event_filter_age_range_from', $moduleId);
        $to = (int)get_field('mod_event_filter_age_range_to', $moduleId);

        if ($from < $to) {
            foreach (range($from, $to) as $value) {
                $years[] = array(
                    'value' => $value,
                    'checked' => false,
                );
            }
        }

        return $years;
    }

    /**
     * Enqueue your scripts and/or styles with wp_enqueue_script / wp_enqueue_style
     */
    public function script()
    {
        wp_enqueue_script(
            'vendor-pagination',
            EVENTMANAGERINTEGRATION_URL.'/source/js/vendor/simple-pagination/jquery.simplePagination.min.js',
            'jquery',
            false,
            true
        );

        if ($this->template === 'index') {
            // Enqueue React
            class_exists('\Modularity\Helper\React') ?
                \Modularity\Helper\React::enqueue() :
                \EventManagerIntegration\Helper\React::enqueue();

            wp_enqueue_script(
                'modularity-'.$this->slug,
                EVENTMANAGERINTEGRATION_URL.'/dist/'.\EventManagerIntegration\Helper\CacheBust::name(
                    'js/Module/Event/Index.js'
                ),
                array('jquery', 'react', 'react-dom'),
                false,
                true
            );

            wp_localize_script(
                'modularity-'.$this->slug,
                'modEvent',
                array(
                    'moreEvents' => __('More events', 'event-integration'),
                    'noEventsFound' => __('No events found', 'event-integration'),
                    'next' => __('Next', 'event-integration'),
                    'prev' => __('Previous', 'event-integration'),
                    'search' => __('Search', 'event-integration'),
                    'from' => __('From', 'event-integration'),
                    'to' => __('To', 'event-integration'),
                    'date' => __('date', 'event-integration'),
                    'categories' => __('Categories', 'event-integration'),
                    'age' => __('Age', 'event-integration'),
                    'ageGroupDescription' => __(
                        'Filter on events that is targeted for given the age',
                        'event-integration'
                    ),
                    'years' => __('years', 'event-integration'),
                    'selectAge' => __('Select age', 'event-integration'),
                )
            );
        }
    }

    /**
     * Filter to show categories in default language
     * @param  array $args  An array of arguments passed to the wp_list_categories function
     * @param  array $field An array containing all the field settings
     * @return array $args
     */
    public function filterEventCategories($args, $field)
    {
        if (function_exists('pll_default_language')) {
            $args['lang'] = pll_default_language();
        }

        return $args;
    }
}


<?php

/**
 * Admin page with forms to authenticate with OAuth1 server
 */

namespace EventManagerIntegration\OAuth;

class OAuthAdmin
{
    public function __construct()
    {
        add_action('admin_menu', array($this, 'createOauthPage'));
    }

    /**
     * Adds submenu page "API authentication", under custom post type parent.
     */
    public function createOauthPage() {
        add_submenu_page(
            'edit.php?post_type=event',
            __( 'API authentication', 'event-integration' ),
            __( 'API authentication', 'event-integration' ),
            'manage_options',
            'oauth-request',
            array($this, 'oauthRequestCallback')
        );
    }

    /**
     * Callback for the submenu page. Forms to complete authorization.
     */
    public function oauthRequestCallback() {
        ?>

<?php _e('API authentication', 'event-integration' ); ?>

        <?php if (get_option('_event_authorized') == false): ?>

<?php _e('Request authorization', 'event-integration'); ?>

<?php _e('To publish events from this client to the API you need authentication. Enter your given client keys and send request.', 'event-integration' ); ?>

            <form method="post" id="oauth-request" action="/wp-admin/admin-ajax.php">

                            <?php _e( 'Client key', 'event-integration' )?>

                            <input type="text" class="client-key" name="client-key" id="client-key" value="<?php echo esc_attr(get_option('_event_client')); ?>" />

                            <?php _e( 'Client secret', 'event-integration' )?>

                            <input type="text" class="client-secret" name="client-secret" id="client-secret" value="<?php echo esc_attr(get_option('_event_secret')); ?>" />

                    <input name='submit' type='submit' id='oauth-request-submit' class='button-primary' value='<?php _e('Send request', 'event-integration') ?>' />

            </form>

<?php _e('Enter verification token', 'event-integration'); ?>

            <form method="post" id="oauth-access" action="/wp-admin/admin-ajax.php">

                            <?php _e('Verification token', 'event-integration'); ?>

                            <input type="text" class="verification-token" name="verification-token" id="verification-token"/>

                    <input name='submit' type='submit' class='button-primary' value='<?php _e('Grant access', 'event-integration');  ?>' />

            </form>

        <?php endif ?>

        <?php if (get_option('_event_authorized') == true): ?>

<?php _e('Authorized', 'event-integration' ); ?>

<?php _e('This client is authorized to submit events to the API.', 'event-integration' ); ?>

            <form method="post" id="oauth-authorized" action="/wp-admin/admin-ajax.php">

                            <?php _e( 'Client key', 'event-integration' ); ?>

                                <?php echo get_option('_event_client'); ?>

                <input name='submit' type='submit' class='button' value='<?php _e('Remove client', 'event-integration') ?>' />

            </form>

        <?php endif ?>
    <?php
    }
}


<?php

/**
 * OAuth1 functions to authenticate clients and publish to wp rest api
 */

namespace EventManagerIntegration\OAuth;

class OAuthRequests
{
    public function __construct()
    {
        add_action('wp_ajax_request_oauth', array($this, 'requestOAuth'));
        add_action('wp_ajax_access_oauth', array($this, 'accessOAuth'));
        add_action('wp_ajax_delete_oauth', array($this, 'deleteOAuth'));
        add_action('wp_ajax_nopriv_submit_event', array($this, 'submitEvent'));
        add_action('wp_ajax_submit_event', array($this, 'submitEvent'));
        add_action('wp_ajax_nopriv_submit_image', array($this, 'submitImage'));
        add_action('wp_ajax_submit_image', array($this, 'submitImage'));
    }

    /**
     * OAuth1 verification step 1: Send a request to API with client keys. Return verification URL if succeeded.
     * OAuth1 verification step 2 (handled on the OAuth1 server): User authorizes the client from the verification URL and will be given a verifier key,
     * that will be used in step 3
     * @return void
     */
    public function requestOAuth()
    {
        if ((!isset($_POST['client'])) || (!isset($_POST['secret'])) || (empty($_POST['client'])) || (empty($_POST['secret']))) {
            wp_send_json_error(__('Request credentials is missing', 'event-integration'));
        }

        // Skip SSL verification while DEV_MODE is true
        $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
            )
        );

        $consumerKey = $_POST['client'];
        $consumerSecret = $_POST['secret'];
        // Get API base url
        $apiOauthUrl = get_field('event_api_oauth_url', 'option');
        if (! $apiOauthUrl) {
            wp_send_json_error(__('You must enter OAuth1 endpoint url found under Event settings to proceed.', 'event-integration'));
        }
        $url                  = rtrim($apiOauthUrl, '/');
        $requestTokenUrl      = $url . "/request";
        $authorizeUrl         = $url . "/authorize";
        $oauthTimestamp       = time();
        $nonce                = md5(mt_rand());
        $oauthSignatureMethod = "HMAC-SHA1";
        $oauthVersion         = "1.0";
        // Create OAuth signature base string
        $sigBase = "GET&" . rawurlencode($requestTokenUrl) . "&"
            . rawurlencode("oauth_consumer_key=" . rawurlencode($consumerKey)
            . "&oauth_nonce=" . rawurlencode($nonce)
            . "&oauth_signature_method=" . rawurlencode($oauthSignatureMethod)
            . "&oauth_timestamp=" . $oauthTimestamp
            . "&oauth_version=" . $oauthVersion);
        // URL encode consumer secret key to bo used in signature
        $sigKey = rawurlencode($consumerSecret) . "&";
        // Signature = HMAC-SHA1 hash base string + key
        $oauthSig = base64_encode(hash_hmac("sha1", $sigBase, $sigKey, true));
        // Create OAuth request url with our signature
        $requestUrl = $requestTokenUrl . "?"
            . "oauth_consumer_key=" . rawurlencode($consumerKey)
            . "&oauth_nonce=" . rawurlencode($nonce)
            . "&oauth_signature_method=" . rawurlencode($oauthSignatureMethod)
            . "&oauth_timestamp=" . rawurlencode($oauthTimestamp)
            . "&oauth_version=" . rawurlencode($oauthVersion)
            . "&oauth_signature=" . rawurlencode($oauthSig);
        // Get results from request
        $response = file_get_contents($requestUrl, false, stream_context_create($options));

        // Return error message if request failed
        if ($response === false) {
            wp_send_json_error(__('The credentials you supplied were not correct', 'event-integration'));
        }

        parse_str(trim($response), $values);

        // Save client keys and temporary tokens to db
        update_option('_event_client', $consumerKey);
        update_option('_event_secret', $consumerSecret);
        update_option('_temp_request_token', $values["oauth_token"]);
        update_option('_temp_request_token_secret', $values["oauth_token_secret"]);

        $verificationUrl = $authorizeUrl . "?oauth_token=" . $values["oauth_token"];

        // Return JSON response with verification url
        $data = array("message" => __('Request succeeded!', 'event-integration'), "url" => '' . __('Click here to authorize this client and get verification token', 'event-integration') . '');
        wp_send_json_success($data);
    }

    /**
     * OAuth1 verification step 3: Request access to API and save returned tokens if succeeded.
     * @return void
     */
    public function accessOAuth()
    {
        if ((!isset($_POST['verifier'])) || (empty($_POST['verifier']))) {
            wp_send_json_error(__('Verifier is missing', 'event-integration'));
        }

        // Skip SSL verification while DEV_MODE is true
        $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
            )
        );

        $oauthVerifier = $_POST['verifier'];
        $consumerKey = get_option('_event_client');
        $consumerSecret = get_option('_event_secret');

        // Get API base url
        $apiOauthUrl          = get_field('event_api_oauth_url', 'option');
        $url                  = rtrim($apiOauthUrl, '/');
        $accessTokenUrl       = $url."/access";
        $oauthVersion         = "1.0";
        $oauthSignatureMethod = "HMAC-SHA1";
        $nonce                = md5(mt_rand());
        $oauthTimestamp       = time();

        $sigBase = "GET&" . rawurlencode($accessTokenUrl) . "&"
            . rawurlencode("oauth_consumer_key=" . rawurlencode($consumerKey)
            . "&oauth_nonce=" . rawurlencode($nonce)
            . "&oauth_signature_method=" . rawurlencode($oauthSignatureMethod)
            . "&oauth_timestamp=" . rawurlencode($oauthTimestamp)
            . "&oauth_token=" . rawurlencode(get_option('_temp_request_token'))
            . "&oauth_verifier=" . rawurlencode($oauthVerifier)
            . "&oauth_version=" . rawurlencode($oauthVersion));

        $sigKey = rawurlencode($consumerSecret) . "&" . rawurlencode(get_option('_temp_request_token_secret'));
        $oauthSig = base64_encode(hash_hmac("sha1", $sigBase, $sigKey, true));

        $requestUrl = $accessTokenUrl . "?"
            . "oauth_consumer_key=" . rawurlencode($consumerKey)
            . "&oauth_nonce=" . rawurlencode($nonce)
            . "&oauth_signature_method=" . rawurlencode($oauthSignatureMethod)
            . "&oauth_timestamp=" . rawurlencode($oauthTimestamp)
            . "&oauth_token=" . rawurlencode(get_option('_temp_request_token'))
            . "&oauth_verifier=" . rawurlencode($oauthVerifier)
            . "&oauth_version=". rawurlencode($oauthVersion)
            . "&oauth_signature=" . rawurlencode($oauthSig);

        $response = file_get_contents($requestUrl, false, stream_context_create($options));
        // Return error message if request failed
        if ($response === false) {
            wp_send_json_error(__('The verifier you supplied were not correct', 'event-integration'));
        }

        parse_str(trim($response), $values);
        // Save new tokens to db
        update_option('_event_token',  $values["oauth_token"]);
        update_option('_event_token_secret', $values["oauth_token_secret"]);
        update_option('_event_authorized', true);
        // Remove temporary tokens
        delete_option('_temp_request_token');
        delete_option('_temp_request_token_secret');

        wp_send_json_success(__('You are authorized!', 'event-integration'));
    }

    /**
     * Submit an event to Event Manager API
     */
    public function submitEvent()
    {
        if (! isset($_POST['data'])) {
            wp_send_json_error(__('Form data is missing, please try again.', 'event-integration'), 'event-integration');
        }

        $postData                    = $this->sanitizeInput($_POST['data']);
        // Add consumer name to postData
        $clientUrl                   = parse_url(get_site_url());
        $postData['consumer_client'] = !empty($postData['client_name']) ? $clientUrl['host'] . ' - ' . $postData['client_name'] : $clientUrl['host'];
        $postData                    = json_encode($postData);
        $consumerKey                 = get_option('_event_client');
        $consumerSecret              = get_option('_event_secret');
        $accessToken                 = get_option('_event_token');
        $accessTokenSecret           = get_option('_event_token_secret');
        $oauthVersion                = "1.0";
        $apiUrl                      = rtrim(get_field('event_api_url', 'option'), '/');
        $apiResourceUrl              = $apiUrl . '/event';
        $nonce                       = md5(mt_rand());
        $oauthSignatureMethod        = "HMAC-SHA1";
        $oauthTimestamp              = time();

        $sigBase = "POST&" . rawurlencode($apiResourceUrl) . "&"
            . rawurlencode("oauth_consumer_key=" . rawurlencode($consumerKey)
            . "&oauth_nonce=" . rawurlencode($nonce)
            . "&oauth_signature_method=" . rawurlencode($oauthSignatureMethod)
            . "&oauth_timestamp=" . $oauthTimestamp
            . "&oauth_token=" . rawurlencode($accessToken)
            . "&oauth_version=" . rawurlencode($oauthVersion));
        $sigKey = rawurlencode($consumerSecret) . "&" . rawurlencode($accessTokenSecret);
        $oauthSig = base64_encode(hash_hmac("sha1", $sigBase, $sigKey, true));
        $authHeader = "OAuth oauth_consumer_key=" . rawurlencode($consumerKey) . ","
            . "oauth_nonce=" . rawurlencode($nonce) . ","
            . "oauth_signature_method=" . rawurlencode($oauthSignatureMethod) . ","
            . "oauth_signature=" . rawurlencode($oauthSig) . ","
            . "oauth_timestamp=". rawurlencode($oauthTimestamp) . ","
            . "oauth_token=" . rawurlencode($accessToken) . ","
            . "oauth_version=" . rawurlencode($oauthVersion);

        // Make the request
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $apiResourceUrl);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, defined('DEV_MODE') && DEV_MODE === true ? false : 2);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, defined('DEV_MODE') && DEV_MODE === true ? false : 2);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            'Content-type: application/json',
            'Authorization: OAuth ' . $authHeader
        ));

        $output = curl_exec($ch);
        $output = json_decode($output);

        // Get Http code
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        // Return response
        if ($httpCode === 201) {
            // Event was created
            wp_send_json_success($output);
        } else {
            // Something went wrong
            wp_send_json_error(__('Something went wrong posting the event', 'event-integration'));
        }
    }

    /**
     * Upload image to Event Manager API media end point
     */
    public function submitImage()
    {
        if (! isset($_FILES['file'])) {
            wp_send_json_error(__('Image is not selected, please try again.', 'event-integration'), 'event-integration');
        }

        $endPoint             = '/media';
        $consumerKey          = get_option('_event_client');
        $consumerSecret       = get_option('_event_secret');
        $accessToken          = get_option('_event_token');
        $accessTokenSecret    = get_option('_event_token_secret');
        $oauthVersion         = "1.0";
        $apiUrl               = rtrim(get_field('event_api_url', 'option'), '/');
        $apiResourceUrl       = $apiUrl . $endPoint;
        $nonce                = md5(mt_rand());
        $oauthSignatureMethod = "HMAC-SHA1";
        $oauthTimestamp       = time();

        // Create multipart content body. Holds media to be uploaded
        $eol = "\r\n";
        define('MULTIPART_BOUNDARY', '--------------------------'.microtime(true));
        $fileContents = file_get_contents($_FILES['file']['tmp_name']);
        $content =  "--" . MULTIPART_BOUNDARY . $eol .
                    "Content-Disposition: form-data; name=\"file\"; filename=\"".rawurlencode($_FILES['file']['name'])."\"". $eol .
                    "Content-Type: " . $_FILES['file']['type']. $eol . $eol .
                    $fileContents . $eol;
        $content .= "--" . MULTIPART_BOUNDARY . "--" . $eol;

        $sigBase = "POST&" . rawurlencode($apiResourceUrl) . "&"
            . rawurlencode("oauth_consumer_key=" . rawurlencode($consumerKey)
            . "&oauth_nonce=" . rawurlencode($nonce)
            . "&oauth_signature_method=" . rawurlencode($oauthSignatureMethod)
            . "&oauth_timestamp=" . $oauthTimestamp
            . "&oauth_token=" . rawurlencode($accessToken)
            . "&oauth_version=" . rawurlencode($oauthVersion)
         );
        $sigKey = rawurlencode($consumerSecret) . "&" . rawurlencode($accessTokenSecret);
        $oauthSig = base64_encode(hash_hmac("sha1", $sigBase, $sigKey, true));
        $authHeader = "OAuth oauth_consumer_key=" . rawurlencode($consumerKey) . ","
            . "oauth_nonce=" . rawurlencode($nonce) . ","
            . "oauth_signature_method=" . rawurlencode($oauthSignatureMethod) . ","
            . "oauth_signature=" . rawurlencode($oauthSig) . ","
            . "oauth_timestamp=". rawurlencode($oauthTimestamp) . ","
            . "oauth_token=" . rawurlencode($accessToken) . ","
            . "oauth_version=" . rawurlencode($oauthVersion);

        $context = stream_context_create(array(
            "http" => array(
                'method' => 'POST',
                'header' => 'Content-Type: multipart/form-data; boundary=' . MULTIPART_BOUNDARY . $eol
                            . "Content-Disposition: attachment filename=\"" . rawurlencode($_FILES['file']['name']) . "\"\r\n"
                            . "Authorization: " . $authHeader . $eol,
                        'content' => $content
            ),
            'ssl' => array(
                'verify_peer' => defined('DEV_MODE') && DEV_MODE === true ? false : true,
                'verify_peer_name' => defined('DEV_MODE') && DEV_MODE === true ? false : true
            )));

        $result = file_get_contents($apiResourceUrl, false, $context);

        if ($result === false) {
            wp_send_json_error(__('Something went wrong uploading your image', 'event-integration'));
        }

        // return uploaded media id
        $resObj = json_decode($result);
        wp_send_json_success($resObj->id);
    }

    /**
     * Remove all client tokens
     */
    public function deleteOAuth()
    {
        delete_option('_event_authorized');
        delete_option('_event_client');
        delete_option('_event_secret');
        delete_option('_event_token');
        delete_option('_event_token_secret');
        delete_option('_temp_request_token');
        delete_option('_temp_request_token_secret');

        echo "Client removed";
        wp_die();
    }

    /**
     * Sanitize array with input data
     * @param  array &$array Defualt array
     * @return array         Sanitized array
     */
    public function sanitizeInput(&$array) {
        foreach ($array as $key => &$value) {
            if(!is_array($value)) {
                if ($key == 'content') {
                    $value = sanitize_textarea_field($value);
                } else {
                    $value = sanitize_text_field($value);
                }
            } else {
                $this->sanitizeInput($value);
            }
        }

        return $array;
    }
}


<?php

namespace EventManagerIntegration\PostTypes;

class Events extends \EventManagerIntegration\Entity\CustomPostType
{
    public static $postTypeSlug = 'event';

    public function __construct()
    {
        parent::__construct(
            _x('Events', 'Post type plural', 'event-integration'),
            _x('Event', 'Post type singular', 'event-integration'),
            self::$postTypeSlug,
            array(
                'description'          =>   'Events',
                'menu_icon'            =>   'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iNTEycHgiIGhlaWdodD0iNTEycHgiIHZpZXdCb3g9IjAgMCA5ODUuMzMzIDk4NS4zMzQiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDk4NS4zMzMgOTg1LjMzNDsiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnPjxwYXRoIGQ9Ik04NjguNTY1LDQ5Mi44Yy00LjQsMjIuMTAxLTI0LDM4LjItNDcuNSwzOS4yYy03LjQsMC4zLTEzLjcsNS43LTE1LjEwMSwxM2MtMS41LDcuMywyLjIsMTQuNyw4LjksMTcuOCAgIGMyMS4zLDEwLDMzLjIsMzIuNCwyOC43LDU0LjVsLTQuMiwyMWMtNS41LDI3LjctMzYuMTAxLDQ1LTYyLjksMzguNGMtNy41LTEuOC0xNS4yLTMuMi0yMi44LTQuN2MtMTEuMi0yLjItMjIuNC00LjUtMzMuNi02LjcgICBjLTE0LjgwMS0zLTI5LjYwMS01Ljg5OS00NC40LTguODk5Yy0xNy42LTMuNS0zNS4zLTcuMTAxLTUyLjktMTAuNjAxYy0xOS42OTktNC0zOS4zOTktNy44OTktNTkuMS0xMS44OTkgICBjLTIxLTQuMi00Mi4xLTguNC02My4xLTEyLjdjLTIxLjYwMS00LjMtNDMuMi04LjctNjQuNy0xM2MtMjEuNC00LjMtNDIuNy04LjYwMS02NC4xMDEtMTIuOWMtMjAuMzk5LTQuMS00MC44LTguMi02MS4xOTktMTIuMyAgIGMtMTguNy0zLjctMzcuMy03LjUtNTYtMTEuMmMtMTYuMi0zLjItMzIuNC02LjUtNDguNS05LjdjLTEyLjktMi42LTI1LjgtNS4xOTktMzguOC03LjhjLTguOS0xLjgtMTcuODAxLTMuNi0yNi43LTUuMzk5ICAgYy00LjEwMS0wLjgwMS04LjItMS43LTEyLjMtMi41Yy0wLjIsMC0wLjQtMC4xMDEtMC42MDEtMC4xMDFjMi4yLDEwLjQsMS4yLDIxLjUtMy42LDMxLjljLTEwLjEwMSwyMS44LTMzLjYwMSwzMy4yLTU2LjIsMjguOCAgIGMtNi43LTEuMy0xNCwxLjItMTYuOSw3LjRsLTksMTkuNWMtMi44OTksNi4xOTksMCwxMy4zOTksNS4zMDEsMTcuNjk5YzEsMC44MDEsNzIxLjgsMzMzLjEwMSw3MjIuOTk5LDMzMy40ICAgYzYuNywxLjMsMTQtMS4yLDE2LjktNy40bDktMTkuNWMyLjktNi4xOTksMC0xMy4zOTktNS4zLTE3LjY5OWMtMTgtMTQuMzAxLTI0LjYwMS0zOS42MDEtMTQuNS02MS40YzEwLjEtMjEuOCwzMy42LTMzLjIsNTYuMi0yOC44ICAgYzYuNjk5LDEuMywxNC0xLjIsMTYuODk5LTcuNGw5LTE5LjVjMi45LTYuMiwwLTEzLjM5OS01LjMtMTcuN2MtMTgtMTQuMy0yNC42LTM5LjYtMTQuNS02MS4zOTlzMzMuNi0zMy4yLDU2LjItMjguOCAgIGM2LjcsMS4zLDE0LTEuMiwxNi45LTcuNGw5LTE5LjVjMi44OTktNi4yLDAtMTMuNC01LjMwMS0xNy43Yy0xOC0xNC4zLTI0LjYtMzkuNi0xNC41LTYxLjRjMTAuMTAxLTIxLjgsMzMuNjAxLTMzLjE5OSw1Ni4yLTI4LjggICBjNi43LDEuMywxNC0xLjIsMTYuOS03LjM5OWw5Ljg5OS0yMS42MDFjMi45LTYuMiwwLjItMTMuNS02LTE2LjM5OWwtMTA3LjY5OS00OS43TDg2OC41NjUsNDkyLjh6IiBmaWxsPSIjRkZGRkZGIi8+PHBhdGggZD0iTTkuNjY1LDQ4NS45YzEuMiwwLjYsNzc5LjMsMTU2LjY5OSw3ODAuNiwxNTYuNjk5YzYuODAxLTAuMywxMy40LTQuNSwxNC43LTExLjFsNC4yLTIxYzEuMy02LjctMy4xLTEzLjEtOS4zLTE2ICAgYy0yMC44LTkuOC0zMy4xMDEtMzIuOC0yOC40LTU2LjRjNC43LTIzLjYsMjUtNDAuMSw0OC00MS4xYzYuOC0wLjMsMTMuNC00LjUsMTQuNy0xMS4xbDMuMS0xNS40bDEuMTAxLTUuNyAgIGMxLjMtNi43LTMuMTAxLTEzLjEtOS4zLTE2Yy0yMC44MDEtOS44LTMzLjEwMS0zMi44LTI4LjQtNTYuMzk5YzQuNy0yMy42MDEsMjUtNDAuMTAxLDQ4LTQxLjEwMWM2LjgtMC4zLDEzLjQtNC41LDE0LjctMTEuMSAgIGw0LjItMjFjMS4zLTYuNy0zLjEwMS0xMy4xLTkuMzAxLTE2Yy0yMC44LTkuOC0zMy4xLTMyLjgtMjguMzk5LTU2LjRjNC43LTIzLjYsMjUtNDAuMSw0OC00MS4xYzYuOC0wLjMsMTMuMzk5LTQuNSwxNC43LTExLjEgICBsNC42OTktMjMuM2MxLjMwMS02LjctMy0xMy4yLTkuNjk5LTE0LjVjMCwwLTc4MS45LTE1Ni44LTc4Mi43LTE1Ni44Yy01LjgsMC0xMC45LDQuMS0xMi4xLDkuOWwtNC43LDIzLjMgICBjLTEuMyw2LjcsMy4xLDEzLjEsOS4zLDE2YzIwLjgsOS44LDMzLjEsMzIuOCwyOC40LDU2LjRjLTQuNywyMy42LTI1LDQwLjEtNDgsNDEuMWMtNi44MDEsMC4zLTEzLjQsNC41LTE0LjcsMTEuMWwtNC4yLDIxICAgYy0xLjMsNi43LDMuMSwxMy4xLDkuMywxNmMyMC44LDkuOCwzMy4xMDEsMzIuOCwyOC40LDU2LjRjLTQuNywyMy42LTI1LDQwLjEtNDgsNDEuMWMtNi44LDAuMy0xMy40LDQuNS0xNC43LDExLjFsLTQuMiwyMSAgIGMtMS4zLDYuNywzLjEwMSwxMy4xLDkuMywxNmMyMC44MDEsOS44LDMzLjEwMSwzMi44LDI4LjQsNTYuNGMtNC43LDIzLjYwMS0yNSw0MC4xMDEtNDgsNDEuMTAxYy02LjgsMC4zLTEzLjQsNC41LTE0LjcsMTEuMSAgIGwtNC4yLDIxQy0wLjkzNSw0NzYuNywzLjQ2NCw0ODMsOS42NjUsNDg1Ljl6IE02NzYuMTY1LDIyOS42YzIuNy0xMy41LDE1LjktMjIuMywyOS40LTE5LjZzMjIuMywxNS45LDE5LjYsMjkuNGwtMzMsMTY0LjIgICBsLTIwLjMsMTAxLjJjLTIuNCwxMS45LTEyLjgsMjAuMTAxLTI0LjUsMjAuMTAxYy0xLjYwMSwwLTMuMy0wLjItNC45LTAuNWMtMTMuNS0yLjctMjIuMy0xNS45LTE5LjYtMjkuNGwyMi43LTExMi45TDY3Ni4xNjUsMjI5LjYgICB6IE0yMjUuMzY1LDEzOS4xYzIuNy0xMy41LDE1LjktMjIuMywyOS40LTE5LjZzMjIuMywxNS45LDE5LjYsMjkuNGwtMTEuNCw1Ni43bC0xMi44OTksNjQuM2wtMTAuNCw1MS44bC0xOC41LDkyLjYgICBjLTIuMzk5LDExLjktMTIuOCwyMC4xMDEtMjQuNSwyMC4xMDFjLTEuNiwwLTMuMy0wLjItNC44OTktMC41Yy0wLjctMC4xMDEtMS40LTAuMzAxLTItMC41Yy0xMi40LTMuNjAxLTIwLjEwMS0xNi4xMDEtMTcuNS0yOC45ICAgbDMuNjk5LTE4LjdsOS43LTQ4LjRMMjI1LjM2NSwxMzkuMXoiIGZpbGw9IiNGRkZGRkYiLz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PC9zdmc+',
                'public'               =>   true,
                'publicly_queriable'   =>   true,
                'show_ui'              =>   true,
                'show_in_nav_menus'    =>   true,
                'has_archive'          =>   true,
                'rewrite'              =>   array(
                    'slug'       =>   self::$postTypeSlug,
                    'with_front' =>   false
                ),
                'hierarchical'          =>  false,
                'exclude_from_search'   =>  false,
                'supports'              =>  is_admin() ? false : array('title'),
                // Disable create new post
                'capability_type' => 'post',
                'capabilities' => array(
                    'create_posts' => 'do_not_allow',
                ),
                'map_meta_cap' => true,
                'show_in_rest' => true,
                'rest_base'    => 'event'
            )
        );
        $this->addTableColumn('cb', '<input type="checkbox">');
        $this->addTableColumn('title', __('Title', 'event-integration'));
        $this->addTableColumn(
            'occasion',
            __('Occasion', 'event-integration'),
            false,
            function ($columnKey, $postId) {
                $occasions = $this->getPostOccasions($postId);
                if (!$occasions) {
                    return;
                }
                echo($occasions);
            }
        );
        $this->addTableColumn(
            'acceptAndDeny',
            __('Public', 'event-integration'),
            false,
            function ($column, $postId) {
                $post_status = get_post_status($postId);

                $first = '';
                $second = '';
                if ($post_status == 'publish') {
                    $first = 'hidden';
                } elseif ($post_status == 'draft' || $post_status == 'trash') {
                    $second = 'hidden';
                }
                echo ''.__(
                        'Accept',
                        'event-integration'
                    ).'
            '.__(
                        'Deny',
                        'event-integration'
                    ).'';
            }
        );
        $this->addTableColumn('date', __('Date', 'event-integration'));
        add_action('init', array($this, 'registerEventCategories'), 1);
        add_action('init', array($this, 'registerEventTags'), 1);
        add_action('init', array($this, 'registerEventGroups'), 1);
        add_action('wp_ajax_import_events', array($this, 'importEvents'));
        add_action('wp_ajax_accept_or_deny', array($this, 'acceptOrDeny'));
        add_filter('views_edit-event', array($this, 'addImportButtons'));
        add_filter('the_content', array($this, 'eventContent'));
        add_filter('the_lead', array($this, 'eventContentLead'));
        add_filter('query_vars', array($this, 'addDateQueryVar'));
        add_filter('Modularity/Module/Posts/Date', array($this, 'formatPostDate'), 10, 3);
        add_filter('Municipio/viewData', array($this, 'singleViewData'));

        add_filter('acf/update_value/name=event_filter_group', array($this, 'updateGroupValue'), 10, 3);
        add_filter('acf/update_value/name=mod_event_groups_list', array($this, 'updateGroupValue'), 10, 3);
        add_filter('acf/update_value/name=event_api_url', array($this, 'getUserGroups'), 10, 3);
    }

    /**
     * Add event data to single view
     * @param array $data Default view data
     * @return array Modified view data
     */
    public function singleViewData($data)
    {
        // Bail if not event
        if (get_post_type() !== self::$postTypeSlug || is_archive()) {
            return $data;
        }

        global $post;

        // Gather event data
        $eventData = array();
        $eventData['occasion'] = \EventManagerIntegration\Helper\SingleEventData::singleEventDate();
        $eventData['cancelled'] = !empty($eventData['occasion']['status']) && $eventData['occasion']['status'] === 'cancelled' ? __('Cancelled', 'event-integration') : null;
        $eventData['rescheduled'] = !empty($eventData['occasion']['status']) && $eventData['occasion']['status'] === 'rescheduled' ? __('Rescheduled', 'event-integration') : null;
        $eventData['exception_information'] = !empty($eventData['occasion']['exception_information']) ? $eventData['occasion']['exception_information'] : null;

        if (function_exists('municipio_get_thumbnail_source')) {
            $eventData['image_src'] = municipio_get_thumbnail_source(
                null,
                array(750, 750),
                '16:9'
            );
        } else {
            $thumbnailId = get_post_thumbnail_id($post->ID);
            $image = wp_get_attachment_image_src($thumbnailId, 'medium');
            $eventData['image_src'] = $image[0] ?? null;
        }

        $location = get_post_meta($post->ID, 'location', true);
        $eventData['location'] = !empty($location['title']) ? $location : null;

        $bookingLink = get_post_meta($post->ID, 'booking_link', true);
        $eventData['booking_link'] = !empty($bookingLink) ? $bookingLink : null;

        $ageGroupFrom = get_post_meta($post->ID, 'age_group_from', true);
        $ageGroupTo = get_post_meta($post->ID, 'age_group_to', true);

        $eventData['age_group'] = null;
        if (!empty($ageGroupFrom) && !empty($ageGroupTo)) {
            $eventData['age_group'] = sprintf('%s-%s %s', $ageGroupFrom, $ageGroupTo, __('years', 'event-integration'));
        } elseif(!empty($ageGroupFrom)) {
            $eventData['age_group'] = sprintf('%s %s %s', __('From', 'event-integration'), $ageGroupFrom, __('years', 'event-integration'));
        } elseif(!empty($ageGroupTo)) {
            $eventData['age_group'] = sprintf('%s %s %s', __('Up to', 'event-integration'), $ageGroupTo, __('years', 'event-integration'));
        }

        $data['post'] = $post;
        $data['event'] = $eventData;

        return $data;
    }

    public function formatPostDate($date, $postId, $postType)
    {
        if ($postType !== self::$postTypeSlug) {
            return $date;
        }
        $occasions = get_post_meta($postId, 'occasions_complete', true);

        return $occasions[0]['start_date'] ?? '';
    }

    /**
     * Import user groups when saving settings
     * @param  string $value   the value of the field
     * @param  int    $post_id the post id to save against
     * @param  array  $field   the field object
     * @return string          the new value
     */
    public function getUserGroups($value, $post_id, $field)
    {
        if (!empty($value)) {
            \EventManagerIntegration\App::importPublishingGroups();
        }

        return $value;
    }

    /**
     * Add term children to user group value
     * @param  string $value   the value of the field
     * @param  int    $post_id the post id to save against
     * @param  array  $field   the field object
     * @return string          the new value
     */
    public function updateGroupValue($value, $post_id, $field)
    {
        // Get old value
        if ($post_id === 'options') {
            $selectedGroups = get_field('event_filter_group', 'options');
        } else {
            $selectedGroups = get_field('mod_event_groups_list', $post_id);
        }
        // Return if settings value is empty to only auto-activate children on first save
        if (!empty($selectedGroups)) {
            return $value;
        }

        if (!empty($value)) {
            foreach ($value as $v) {
                $term_children = get_term_children($v, 'event_groups');
                if ($term_children) {
                    $value = array_merge($value, $term_children);
                }
            }
            $value = array_unique($value);
        }

        return $value;
    }

    /**
     * Add date query var
     * @param array $vars query variables
     */
    public function addDateQueryVar($vars)
    {
        $vars[] = "date";

        return $vars;
    }

    /**
     * Replace content if occasion has custom content.
     * @param  string $content Content of the current post.
     * @return string
     */
    public function eventContent($content)
    {
        if (!is_singular($this->slug)) {
            return $content;
        }

        $custom_content = $this->getCustomContent();
        if ($custom_content) {
            $content = $custom_content;
        }

        return $content;
    }

    /**
     * Remove lead content if the occasion has custom content
     * @param  string $lead Lead content of the current post.
     * @return string
     */
    public function eventContentLead($lead)
    {
        if (!is_singular($this->slug)) {
            return $lead;
        }

        $custom_content = $this->getCustomContent();
        if ($custom_content) {
            $lead = null;
        }

        return $lead;
    }

    /**
     * Get custom occasion content
     * @return string
     */
    public function getCustomContent()
    {
        global $post;
        $get_date = (!empty(get_query_var('date'))) ? get_query_var('date') : false;

        if ($get_date != false) {
            $occasions = \EventManagerIntegration\Helper\QueryEvents::getEventOccasions($post->ID, false);
            foreach ($occasions as $o) {
                $event_date = preg_replace('/\D/', '', $o->start_date);
                // Replace content with occasion custom content
                if ($get_date == $event_date && !empty($o->content) && $o->content_mode == 'custom') {
                    return $o->content;
                }
            }
        }
    }

    /**
     * Register event categories as taxonomy
     */
    public function registerEventCategories()
    {
        $labels = array(
            'name'                  => _x('Event categories', 'Taxonomy plural name', 'event-integration'),
            'singular_name'         => _x('Event category', 'Taxonomy singular name', 'event-integration'),
            'search_items'          => __('Search categories', 'event-integration'),
            'popular_items'         => __('Popular categories', 'event-integration'),
            'all_items'             => __('All categories', 'event-integration'),
            'parent_item'           => __('Parent category', 'event-integration'),
            'parent_item_colon'     => __('Parent category', 'event-integration'),
            'edit_item'             => __('Edit category', 'event-integration'),
            'update_item'           => __('Update category', 'event-integration'),
            'add_new_item'          => __('Add new category', 'event-integration'),
            'new_item_name'         => __('New category', 'event-integration'),
            'add_or_remove_items'   => __('Add or remove categories', 'event-integration'),
            'choose_from_most_used' => __('Choose from most used categories', 'event-integration'),
            'menu_name'             => __('Categories', 'event-integration'),
        );

        $args = array(
            'labels'                => $labels,
            'public'                => true,
            'show_in_nav_menus'     => true,
            'show_admin_column'     => true,
            'hierarchical'          => true,
            'show_tagcloud'         => true,
            'show_ui'               => false,
            'query_var'             => true,
            'rewrite'               => true
        );

        register_taxonomy('event_categories', array('event'), $args);
    }

    /**
     * Register event tags as taxonomy
     */
    public function registerEventTags()
    {
        $labels = array(
            'name'                  => _x('Event tags', 'Taxonomy plural name', 'event-integration'),
            'singular_name'         => _x('Event tag', 'Taxonomy singular name', 'event-integration'),
            'search_items'          => __('Search tags', 'event-integration'),
            'popular_items'         => __('Popular tags', 'event-integration'),
            'all_items'             => __('All tags', 'event-integration'),
            'parent_item'           => __('Parent', 'event-integration'),
            'parent_item_colon'     => __('Parent', 'event-integration'),
            'edit_item'             => __('Edit tag', 'event-integration'),
            'update_item'           => __('Update', 'event-integration'),
            'add_new_item'          => __('Add new tag', 'event-integration'),
            'new_item_name'         => __('New tag', 'event-integration'),
            'add_or_remove_items'   => __('Add or remove tags', 'event-integration'),
            'choose_from_most_used' => __('Choose from most used tags', 'event-integration'),
            'menu_name'             => __('Tags', 'event-integration'),
        );

        $args = array(
            'labels'                => $labels,
            'public'                => true,
            'show_in_nav_menus'     => true,
            'show_admin_column'     => true,
            'hierarchical'          => false,
            'show_tagcloud'         => true,
            'show_ui'               => false,
            'query_var'             => true,
            'rewrite'               => true
        );

        register_taxonomy('event_tags', array('event'), $args);
    }

    /**
     * Register event groups as taxonomy
     */
    public function registerEventGroups()
    {
        $labels = array(
            'name'                  => _x('Event groups', 'Taxonomy plural name', 'event-integration'),
            'singular_name'         => _x('Event group', 'Taxonomy singular name', 'event-integration'),
            'search_items'          => __('Search groups', 'event-integration'),
            'popular_items'         => __('Popular groups', 'event-integration'),
            'all_items'             => __('All groups', 'event-integration'),
            'parent_item'           => __('Parent group', 'event-integration'),
            'parent_item_colon'     => __('Parent group', 'event-integration'),
            'edit_item'             => __('Edit group', 'event-integration'),
            'update_item'           => __('Update group', 'event-integration'),
            'add_new_item'          => __('Add new group', 'event-integration'),
            'new_item_name'         => __('New group', 'event-integration'),
            'add_or_remove_items'   => __('Add or remove groups', 'event-integration'),
            'choose_from_most_used' => __('Choose from most used groups', 'event-integration'),
            'menu_name'             => __('Groups', 'event-integration'),
        );

        $args = array(
            'labels'                => $labels,
            'public'                => true,
            'show_in_nav_menus'     => true,
            'show_admin_column'     => true,
            'hierarchical'          => true,
            'show_tagcloud'         => true,
            'show_ui'               => false,
            'query_var'             => true,
            'rewrite'               => true
        );

        register_taxonomy('event_groups', array('event'), $args);
    }

    public function getPostOccasions($post_id)
    {
        global $wpdb;

        $db_table = $wpdb->prefix . "integrate_occasions";
        $occasions = $wpdb->get_results("SELECT start_date FROM $db_table WHERE $db_table.event_id = $post_id ORDER BY start_date ASC", ARRAY_A);

        if (count($occasions) == 0) {
            return;
        }

        return date('Y-m-d H:i', strtotime($occasions[0]['start_date']));
    }

    /**
     * Add buttons to start parsing xcap and Cbis
     * @return void
     */
    public function addImportButtons($views)
    {
        if (current_user_can('administrator') || current_user_can('editor')) {
            $button  = '
';

            if (get_field('event_api_url', 'option')) {
                $button .= '
' . __('Import events', 'event-integration') . '
';
            }
            $button .= '
';
            $views['import-buttons'] = $button;
        }

        return $views;
    }

    /**
     * Start parsing event importer
     */
    public function importEvents()
    {
        if ($apiUrl = \EventManagerIntegration\Helper\ApiUrl::buildApiUrl()) {
            $importer = new \EventManagerIntegration\Parser\EventManagerApi($apiUrl);
            $data = $importer->getCreatedData();
            wp_send_json($data);
        }

        wp_die();
    }

    /**
     * Accept or deny an event. Changes post status to draft if denied.
     * @return int $value
     */
    public function acceptOrDeny()
    {
        if (!isset($_POST['postId']) || !isset($_POST['value'])) {
            echo _e('Something went wrong!', 'event-integration');
            die();
        }

        $postId = $_POST['postId'];
        $value = $_POST['value'];

        $post = get_post($postId);
        if ($value == 0) {
            $post->post_status = 'draft';
        }
        if ($value == 1) {
            $post->post_status = 'publish';
        }

        $update = wp_update_post($post, true);
        if (is_wp_error($update)) {
            echo _e('Error', 'event-integration');
            die();
        }

        echo $value;
        die();
    }
}


<?php namespace STS\Tunneler\Jobs;

class CreateTunnel
{

    /**
     * The Command for checking if the tunnel is open
     * @var string
     */
    protected $ncCommand;

    /**
     * The command for creating the tunnel
     * @var string
     */
    protected $sshCommand;

    /**
     * Simple place to keep all output.
     * @var array
     */
    protected $output = [];

    public function __construct()
    {

        $this->ncCommand = sprintf('%s -z %s %d  > /dev/null 2>&1',
            config('tunneler.nc_path'),
            config('tunneler.local_address'),
            config('tunneler.local_port')
        );

        $this->bashCommand = sprintf('timeout 1 %s -c \'cat < /dev/null > /dev/tcp/%s/%d\' > /dev/null 2>&1',
            config('tunneler.bash_path'),
            config('tunneler.local_address'),
            config('tunneler.local_port')
        );

        $this->sshCommand = sprintf('%s %s %s -N -i %s -L %d:%s:%d -p %d %s@%s',
            config('tunneler.ssh_path'),
            config('tunneler.ssh_options'),
            config('tunneler.ssh_verbosity'),
            config('tunneler.identity_file'),
            config('tunneler.local_port'),
            config('tunneler.bind_address'),
            config('tunneler.bind_port'),
            config('tunneler.port'),
            config('tunneler.user'),
            config('tunneler.hostname')
        );
    }

    public function handle(): int
    {
        if ($this->verifyTunnel()) {
            return 1;
        }

        $this->createTunnel();

        $tries = config('tunneler.tries');
        for ($i = 0; $i < $tries; $i++) {
            if ($this->verifyTunnel()) {
                return 2;
            }

            // Wait a bit until next iteration
            usleep(config('tunneler.wait'));
        }

        throw new \ErrorException(sprintf("Could Not Create SSH Tunnel with command:\n\t%s\nCheck your configuration.",
            $this->sshCommand));
    }

    /**
     * Creates the SSH Tunnel for us.
     */
    protected function createTunnel()
    {
        $this->runCommand(sprintf('%s %s >> %s 2>&1 &',
            config('tunneler.nohup_path'),
            $this->sshCommand,
            config('tunneler.nohup_log')
        ));
        // Ensure we wait long enough for it to actually connect.
        usleep(config('tunneler.wait'));
    }

    /**
     * Verifies whether the tunnel is active or not.
     * @return bool
     */
    protected function verifyTunnel()
    {
        if (config('tunneler.verify_process') == 'bash') {
            return $this->runCommand($this->bashCommand);
        }

        return $this->runCommand($this->ncCommand);
    }

    /**
     * Runs a command and converts the exit code to a boolean
     * @param $command
     * @return bool
     */
    protected function runCommand($command)
    {
        $return_var = 1;
        exec($command, $this->output, $return_var);
        return (bool)($return_var === 0);
    }

}


<?php namespace STS\Supervisor\Daemon;

use Illuminate\Queue\Listener as QueueListener;
use Symfony\Component\Process\Process;

pcntl_async_signals(true);

class Listener extends QueueListener{

    /**
     * Set to true if we catch a signal and need to
     * stop after we finish what we are doing.
     *
     * @var bool
     */
    protected $stopGracefully = false;

    /**
     * Create a new queue listener.
     *
     * @param  string $commandPath
     */
    public function __construct($commandPath)
    {
        if (!function_exists('pcntl_signal')){
            throw new \RuntimeException('You must have PCNTL functions to use this Listener');
        }
        parent::__construct($commandPath);

        // Install the signal handler
        $this->setSignalHandler(SIGHUP);  /* Hangup (POSIX).             */
        $this->setSignalHandler(SIGINT);  /* Interrupt (ANSI).           */
        $this->setSignalHandler(SIGQUIT); /* Quit (POSIX).               */
        $this->setSignalHandler(SIGABRT); /* Abort (ANSI).               */
        $this->setSignalHandler(SIGTERM); /* Termination (ANSI).         */
        $this->setSignalHandler(SIGTSTP); /* Keyboard stop (POSIX).      */
    }

    /**
     * Sets the signal to be handled by either the closure or the built in
     * signal handler.
     *
     * @param int $signal
     * @param callable|null $closure
     *
     * @return bool
     */
    public function setSignalHandler(int $signal, callable $closure = null){
        if (empty($closure)){
            return pcntl_signal($signal, array($this, 'sigHandler'));
        }
        return pcntl_signal($signal, $closure);
    }

    /**
     * Built in Signal Handler.
     * @param int $signo
     */
    public function sigHandler( int $signo ){
        $this->handleWorkerOutput('warn', sprintf("Signal %d Caught, asking the daemon to stop gracefully.", $signo));
        $this->stopGracefully = true;
    }

    /**
     * Log that we are done and exiting.
     */
    public function gracefulStop(){
        $this->handleWorkerOutput('warn', "Work done, exiting now.");
        $this->stop();
    }
    /**
     * Run the given process.
     *
     * @param  Process  $process
     * @param  int  $memory
     * @return void
     */
    public function runProcess(Process $process, $memory)
    {
        try {
            $process->run(function ($type, $line) {
                $this->handleWorkerOutput($type, $line);
            });
        }catch (\Exception $e){
            dd($e);
        }

        // If we caught a signal and need to stop gracefully, this is the place to
        // do it.
        pcntl_signal_dispatch();
        if ($this->stopGracefully){
            $this->gracefulStop();
        }
        // Once we have run the job we'll go check if the memory limit has been
        // exceeded for the script. If it has, we will kill this script so a
        // process manager will restart this with a clean slate of memory.
        if ($this->memoryExceeded($memory)) {
            $this->stop();
        }
    }

    /**
     * Handle output from the worker process.
     *
     * @param  string  $type
     * @param  string  $line
     * @return void
     */
    protected function handleWorkerOutput($type, $line)
    {
        if (isset($this->outputHandler)) {
            call_user_func($this->outputHandler, $type, $line);
        }
    }
}


<?php

/**
 * This file is part of contao-community-alliance/dc-general-contao-frontend.
 *
 * (c) 2015-2019 Contao Community Alliance.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    contao-community-alliance/dc-general-contao-frontend
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Richard Henkenjohann <richardhenkenjohann@googlemail.com>
 * @copyright  2015-2019 Contao Community Alliance.
 * @license    https://github.com/contao-community-alliance/dc-general-contao-frontend/blob/master/LICENSE LGPL-3.0
 * @filesource
 */

namespace ContaoCommunityAlliance\DcGeneral\ContaoFrontend\View\ActionHandler;

use Contao\CoreBundle\Exception\PageNotFoundException;
use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator;
use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait;
use ContaoCommunityAlliance\DcGeneral\ContaoFrontend\View\EditMask;
use ContaoCommunityAlliance\DcGeneral\Data\ModelId;
use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface;
use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface;
use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent;
use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException;
use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException;
use ContaoCommunityAlliance\DcGeneral\Exception\NotEditableException;

/**
 * This class handles the edit actions in the frontend.
 */
class EditHandler
{
    use RequestScopeDeterminatorAwareTrait;

    /**
     * EditHandler constructor.
     *
     * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator.
     */
    public function __construct(RequestScopeDeterminator $scopeDeterminator)
    {
        $this->setScopeDeterminator($scopeDeterminator);
    }

    /**
     * Handle the event to process the action.
     *
     * @param ActionEvent $event The action event.
     *
     * @return void
     *
     * @throws PageNotFoundException             If item to edit was not found.
     * @throws DcGeneralInvalidArgumentException If the data container is not editable, closed.
     * @throws DcGeneralRuntimeException         When the definition is not editable.
     */
    public function handleEvent(ActionEvent $event): void
    {
        if (!$this->scopeDeterminator->currentScopeIsFrontend()) {
            return;
        }

        $action = $event->getAction();
        // Only handle if we do not have a manual sorting or we know where to insert.
        // Manual sorting is handled by clipboard.
        if ('edit' !== $action->getName()) {
            return;
        }

        // Only run when no response given yet.
        if (null !== $event->getResponse()) {
            return;
        }

        $response = $this->process($event->getEnvironment());
        if (false !== $response) {
            $event->setResponse($response);
        }
    }

    /**
     * Handle the action.
     *
     * @param EnvironmentInterface $environment The environment.
     *
     * @return string|bool
     *
     * @throws PageNotFoundException If item to edit was not found.
     * @throws NotEditableException  When the definition is not editable.
     */
    public function process(EnvironmentInterface $environment)
    {
        $definition      = $environment->getDataDefinition();
        $basicDefinition = $definition->getBasicDefinition();

        if (!$basicDefinition->isEditable()) {
            throw new NotEditableException('DataContainer ' . $definition->getName() . ' is not editable');
        }
        // We only support flat tables, sorry.
        if (BasicDefinitionInterface::MODE_FLAT !== $basicDefinition->getMode()) {
            return false;
        }
        $modelId = ModelId::fromSerialized($environment->getInputProvider()->getParameter('id'));

        $dataProvider = $environment->getDataProvider();
        $model        = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId()));
        $clone        = $dataProvider->getEmptyModel();

        if (null === $model) {
            throw new PageNotFoundException('Model not found: ' . $modelId->getSerialized());
        }

        return (new EditMask($environment, $model, $clone, null, null))->execute();
    }
}


<?php

/**
 * This file is part of contao-community-alliance/dc-general-contao-frontend.
 *
 * (c) 2015-2018 Contao Community Alliance.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    contao-community-alliance/dc-general-contao-frontend
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Richard Henkenjohann <richardhenkenjohann@googlemail.com>
 * @copyright  2015-2018 Contao Community Alliance.
 * @license    https://github.com/contao-community-alliance/dc-general-contao-frontend/blob/master/LICENSE LGPL-3.0
 * @filesource
 */

namespace ContaoCommunityAlliance\DcGeneral\ContaoFrontend;

use ContaoCommunityAlliance\DcGeneral\Action;
use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents;
use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface;
use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent;
use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory;
use ContaoCommunityAlliance\Translator\TranslatorInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * This class performs the real frontend editing.
 */
class FrontendEditor
{
    /**
     * The event dispatcher.
     *
     * @var EventDispatcherInterface
     */
    private $dispatcher;

    /**
     * The translator.
     *
     * @var TranslatorInterface
     */
    private $translator;

    /**
     * The already populated environments.
     *
     * @var EnvironmentInterface[]
     */
    private static $environments = [];

    /**
     * Create a new instance.
     *
     * @param EventDispatcherInterface $dispatcher The event dispatcher.
     *
     * @param TranslatorInterface      $translator The translator.
     */
    public function __construct(EventDispatcherInterface $dispatcher, TranslatorInterface $translator)
    {
        $this->dispatcher = $dispatcher;
        $this->translator = $translator;
    }

    /**
     * Create a frontend editor for the given table.
     *
     * @param string $containerName The name of the data container to edit.
     *
     * @param string $defaultAction The default action to issue, if none provided by the input provider.
     *
     * @return string
     */
    public function editFor($containerName, $defaultAction = 'showAll'): string
    {
        $environment = $this->createDcGeneral($containerName);
        $actionName  = $environment->getInputProvider()->getParameter('act') ?: $defaultAction;
        $action      = new Action($actionName);
        $event       = new ActionEvent($environment, $action);

        $this->dispatcher->dispatch(DcGeneralEvents::ACTION, $event);

        if (!$result = $event->getResponse()) {
            return 'Action ' . $action->getName() . ' is not supported yet.';
        }

        return $result;
    }

    /**
     * Create the dc-general and return it's environment instance.
     *
     * @param string $containerName The name of the data container to edit.
     *
     * @return EnvironmentInterface
     */
    public function createDcGeneral($containerName): EnvironmentInterface
    {
        if (!array_key_exists($containerName, self::$environments)) {
            $dcGeneral = (new DcGeneralFactory())
                ->setContainerName($containerName)
                ->setEventDispatcher($this->dispatcher)
                ->setTranslator($this->translator)
                ->createDcGeneral();

            self::$environments[$containerName] = $dcGeneral->getEnvironment();
        }

        return self::$environments[$containerName];
    }
}


<?php

/**
 * This file is part of contao-community-alliance/dc-general-contao-frontend.
 *
 * (c) 2015-2019 Contao Community Alliance.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    contao-community-alliance/dc-general-contao-frontend
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Richard Henkenjohann <richardhenkenjohann@googlemail.com>
 * @copyright  2015-2019 Contao Community Alliance.
 * @license    https://github.com/contao-community-alliance/dc-general-contao-frontend/blob/master/LICENSE LGPL-3.0
 * @filesource
 */

namespace ContaoCommunityAlliance\DcGeneral\ContaoFrontend\View;

use Contao\Input;
use ContaoCommunityAlliance\DcGeneral\ContaoFrontend\Event\BuildWidgetEvent;
use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent;
use ContaoCommunityAlliance\DcGeneral\ContaoFrontend\Event\DcGeneralFrontendEvents;
use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface;
use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag;
use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface;
use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException;
use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException;

/**
 * Class WidgetManager.
 *
 * This class is responsible for creating widgets and processing data through them.
 */
class WidgetManager
{
    /**
     * The environment in use.
     *
     * @var EnvironmentInterface
     */
    protected $environment;

    /**
     * The model for which widgets shall be generated.
     *
     * @var ModelInterface
     */
    protected $model;

    /**
     * Create a new instance.
     *
     * @param EnvironmentInterface $environment The environment in use.
     *
     * @param ModelInterface       $model       The model for which widgets shall be generated.
     */
    public function __construct(EnvironmentInterface $environment, ModelInterface $model)
    {
        $this->environment = $environment;
        $this->model       = $model;
    }

    /**
     * Retrieve the instance of a widget for the given property.
     *
     * @param string           $property Name of the property for which the widget shall be retrieved.
     *
     * @param PropertyValueBag $valueBag The input values to use (optional).
     *
     * @return \Widget
     *
     * @throws DcGeneralRuntimeException         When No widget could be build.
     * @throws DcGeneralInvalidArgumentException When property is not defined in the property definitions.
     */
    public function getWidget($property, PropertyValueBag $valueBag = null)
    {
        $environment         = $this->getEnvironment();
        $dispatcher          = $environment->getEventDispatcher();
        $propertyDefinitions = $environment->getDataDefinition()->getPropertiesDefinition();

        if (!$propertyDefinitions->hasProperty($property)) {
            throw new DcGeneralInvalidArgumentException(
                'Property ' . $property . ' is not defined in propertyDefinitions.'
            );
        }

        $model = clone $this->model;
        $model->setId($this->model->getId());

        if ($valueBag) {
            $values = new PropertyValueBag($valueBag->getArrayCopy());
            $this->environment->getController()->updateModelFromPropertyBag($model, $values);
        }

        $propertyDefinition = $propertyDefinitions->getProperty($property);
        $event              = new BuildWidgetEvent($environment, $model, $propertyDefinition);

        $dispatcher->dispatch(DcGeneralFrontendEvents::BUILD_WIDGET, $event);
        if (!$event->getWidget()) {
            throw new DcGeneralRuntimeException(
                sprintf('Widget was not build for property %s::%s.', $this->model->getProviderName(), $property)
            );
        }

        return $event->getWidget();
    }

    /**
     * Render the widget for the named property.
     *
     * @param string           $property     The name of the property for which the widget shall be rendered.
     *
     * @param bool             $ignoreErrors Flag if the error property of the widget shall get cleared prior rendering.
     *
     * @param PropertyValueBag $valueBag     The input values to use (optional).
     *
     * @return string
     *
     * @throws DcGeneralRuntimeException         For unknown properties.
     */
    public function renderWidget($property, $ignoreErrors = false, PropertyValueBag $valueBag = null)
    {
        $widget = $this->getWidget($property, $valueBag);

        if (!$widget) {
            throw new DcGeneralRuntimeException('No widget for property ' . $property);
        }

        if ($ignoreErrors) {
            // Clean the errors array and fix up the CSS class.
            $reflection = new \ReflectionProperty(get_class($widget), 'arrErrors');
            $reflection->setAccessible(true);
            $reflection->setValue($widget, []);
            $reflection = new \ReflectionProperty(get_class($widget), 'strClass');
            $reflection->setAccessible(true);
            $reflection->setValue($widget, str_replace('error', '', $reflection->getValue($widget)));
        } else {
            if ($valueBag && $valueBag->hasPropertyValue($property)
                && $valueBag->isPropertyValueInvalid($property)
            ) {
                foreach ($valueBag->getPropertyValueErrors($property) as $error) {
                    $widget->addError($error);
                }
            }
        }

        return $widget->parse();
    }

    /**
     * Process the input values from the input provider and update the information in the value bag.
     *
     * @param PropertyValueBag $valueBag The value bag to update.
     *
     * @return void
     */
    public function processInput(PropertyValueBag $valueBag)
    {
        $post = $this->hijackPost($valueBag);

        // Now get and validate the widgets.
        foreach (array_keys($valueBag->getArrayCopy()) as $property) {
            $this->processProperty($valueBag, $property);
        }

        $this->restorePost($post);
    }

    /**
     * Process a single property.
     *
     * @param PropertyValueBag $valueBag The value bag to update.
     *
     * @param string           $property The property to process.
     *
     * @return void
     *
     * @throws DcGeneralRuntimeException         When No widget could be build.
     * @throws DcGeneralInvalidArgumentException When property is not defined in the property definitions.
     */
    private function processProperty(PropertyValueBag $valueBag, $property)
    {
        // NOTE: the passed input values are RAW DATA from the input provider - aka widget known values and not
        // native data as in the model.
        // Therefore we do not need to decode them but MUST encode them.
        $widget = $this->getWidget($property, $valueBag);
        $widget->validate();

        if ($widget->hasErrors()) {
            foreach ($widget->getErrors() as $error) {
                $valueBag->markPropertyValueAsInvalid($property, $error);
            }

            return;
        }

        if (!$widget->submitInput()) {
            // Handle as abstaining property.
            $valueBag->removePropertyValue($property);
            return;
        }

        try {
            $valueBag->setPropertyValue($property, $this->encodeValue($property, $widget->value, $valueBag));
        } catch (\Exception $e) {
            $widget->addError($e->getMessage());
            foreach ($widget->getErrors() as $error) {
                $valueBag->markPropertyValueAsInvalid($property, $error);
            }
        }
    }

    /**
     * Remember current POST data and overwrite it with the values from the value bag.
     *
     * The previous values are returned and can be restored via a call to restorePost().
     *
     * @param PropertyValueBag $valueBag The value bag to retrieve the new post data from.
     *
     * @return array
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    private function hijackPost(PropertyValueBag $valueBag)
    {
        $post  = $_POST;
        $_POST = [];
        Input::resetCache();

        // Set all POST data, these get used within the Widget::validate() method.
        foreach ($valueBag as $property => $value) {
            $_POST[$property] = $value;
        }

        return $post;
    }

    /**
     * Restore the post values with the passed values.
     *
     * @param array $post The Post values to restore.
     *
     * @return void
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    private function restorePost($post)
    {
        $_POST = $post;
        Input::resetCache();
    }

    /**
     * Retrieve the environment.
     *
     * @return EnvironmentInterface
     */
    private function getEnvironment()
    {
        return $this->environment;
    }

    /**
     * Encode a value from the widget to native data of the data provider via event.
     *
     * @param string           $property The property.
     *
     * @param mixed            $value    The value of the property.
     *
     * @param PropertyValueBag $valueBag The property value bag the property value originates from.
     *
     * @return mixed
     */
    private function encodeValue($property, $value, PropertyValueBag $valueBag)
    {
        $environment = $this->getEnvironment();

        $event = new EncodePropertyValueFromWidgetEvent($environment, $this->model, $valueBag);
        $event
            ->setProperty($property)
            ->setValue($value);

        $environment->getEventDispatcher()->dispatch(EncodePropertyValueFromWidgetEvent::NAME, $event);

        return $event->getValue();
    }
}


<?php

/**
 * This file is part of contao-community-alliance/dc-general-contao-frontend.
 *
 * (c) 2015-2018 Contao Community Alliance.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    contao-community-alliance/dc-general-contao-frontend
 * @author     Richard Henkenjohann <richardhenkenjohann@googlemail.com>
 * @copyright  2015-2018 Contao Community Alliance.
 * @license    https://github.com/contao-community-alliance/dc-general-contao-frontend/blob/master/LICENSE LGPL-3.0
 * @filesource
 */

namespace ContaoCommunityAlliance\DcGeneral\ContaoFrontend\View\ActionHandler;

use Contao\CoreBundle\Exception\RedirectResponseException;
use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator;
use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait;
use ContaoCommunityAlliance\DcGeneral\Data\ModelId;
use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface;
use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface;
use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent;
use ContaoCommunityAlliance\DcGeneral\Event\PostDeleteModelEvent;
use ContaoCommunityAlliance\DcGeneral\Event\PreDeleteModelEvent;
use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException;
use ContaoCommunityAlliance\DcGeneral\Exception\NotDeletableException;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * This class handles the edit actions in the frontend.
 */
class DeleteHandler
{
    use RequestScopeDeterminatorAwareTrait;

    /**
     * The current request stack.
     *
     * @var RequestStack
     */
    private $requestStack;

    /**
     * DeleteHandler constructor.
     *
     * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator.
     *
     * @param RequestStack             $requestStack      The current request stack.
     */
    public function __construct(RequestScopeDeterminator $scopeDeterminator, RequestStack $requestStack)
    {
        $this->setScopeDeterminator($scopeDeterminator);

        $this->requestStack = $requestStack;
    }

    /**
     * Handle the event to process the action.
     *
     * @param ActionEvent $event The action event.
     *
     * @return void
     *
     * @throws RedirectResponseException After successful delete.
     * @throws DcGeneralRuntimeException When the DataContainer is not deletable.
     */
    public function handleEvent(ActionEvent $event): void
    {
        if (!$this->scopeDeterminator->currentScopeIsFrontend()) {
            return;
        }

        $action = $event->getAction();
        // Only handle if we do not have a manual sorting or we know where to insert.
        // Manual sorting is handled by clipboard.
        if ('delete' !== $action->getName()) {
            return;
        }

        // Only run when no response given yet.
        if (null !== $event->getResponse()) {
            return;
        }

        $this->process($event->getEnvironment());
    }

    /**
     * Handle the action.
     *
     * @param EnvironmentInterface $environment The environment.
     *
     * @return void
     *
     * @throws RedirectResponseException After successful delete.
     * @throws NotDeletableException     When the DataContainer is not deletable.
     */
    public function process(EnvironmentInterface $environment): void
    {
        $definition      = $environment->getDataDefinition();
        $basicDefinition = $definition->getBasicDefinition();

        if (!$basicDefinition->isDeletable()) {
            throw new NotDeletableException('DataContainer ' . $definition->getName() . ' is not deletable');
        }
        // We only support flat tables, sorry.
        if (BasicDefinitionInterface::MODE_FLAT !== $basicDefinition->getMode()) {
            return;
        }
        $modelId = ModelId::fromSerialized($environment->getInputProvider()->getParameter('id'));

        $dataProvider = $environment->getDataProvider();
        $model        = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId()));

        // Trigger event before the model will be deleted.
        $event = new PreDeleteModelEvent($environment, $model);
        $environment->getEventDispatcher()->dispatch($event::NAME, $event);

        $dataProvider->delete($model);

        // Trigger event after the model is deleted.
        $event = new PostDeleteModelEvent($environment, $model);
        $environment->getEventDispatcher()->dispatch($event::NAME, $event);

        throw new RedirectResponseException($this->requestStack->getCurrentRequest()->headers->get('referer'));
    }
}


<?php
namespace Neos\Imagine;

/*
 * This file is part of the Neos.Imagine package.
 *
 * (c) Contributors of the Neos Project - www.neos.io
 *
 * This package is Open Source Software. For the full copyright and license
 * information, please view the LICENSE file which was distributed with this
 * source code.
 */

use Neos\Flow\Annotations as Flow;

/**
 * Imagine factory for Imagine package
 *
 * @Flow\Scope("singleton")
 */
class ImagineFactory extends AbstractImagineFactory
{
    /**
     * @var \Neos\Flow\ObjectManagement\ObjectManagerInterface
     * @Flow\Inject
     */
    protected $objectManager;

    /**
     * Factory method which creates an Imagine instance.
     *
     * By default this factory creates an Imagine service according to the currently configured driver (for example GD
     * or ImageMagick).
     *
     * You may alternatively specify a class name of a driver-dependent class you need an instance of. For example,
     * specifying "Image" with the currently configured driver "Gd" will return an instance of the class
     * \Imagine\Gd\Image.
     *
     * @param string $className If specified, this factory will create an instance of the driver dependent class
     * @return \Imagine\Image\ImagineInterface
     * @throws \ReflectionException
     * @api
     */
    public function create($className = 'Imagine')
    {
        $this->configureDriverSpecificSettings();

        $className = 'Imagine\\' . $this->settings['driver'] . '\\' . $className;
        $arguments = array_slice(func_get_args(), 1);

        switch (count($arguments)) {
            case 0: $object = new $className(); break;
            case 1: $object = new $className($arguments[0]); break;
            case 2: $object = new $className($arguments[0], $arguments[1]); break;
            case 3: $object = new $className($arguments[0], $arguments[1], $arguments[2]); break;
            case 4: $object = new $className($arguments[0], $arguments[1], $arguments[2], $arguments[3]); break;
            case 5: $object = new $className($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]); break;
            case 6: $object = new $className($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5]); break;
            default:
                $class = new \ReflectionClass($className);
                $object =  $class->newInstanceArgs($arguments);
        }

        return $object;
    }

    /**
     * Set driver specific settings.
     *
     * @return void
     */
    protected function configureDriverSpecificSettings()
    {
        if ($this->settings['driver'] === 'Imagick') {
            $this->configureImagickSettings();
        }
    }

    /**
     * Sets limits for the Imagick driver.
     *
     * @return void
     */
    protected function configureImagickSettings()
    {
        if (!isset($this->settings['driverSpecific']['Imagick'])) {
            return;
        }

        $limits = $this->settings['driverSpecific']['Imagick']['limits'] ? $this->settings['driverSpecific']['Imagick']['limits'] : [];
        foreach ($limits as $resourceType => $limit) {
            \Imagick::setResourceLimit($resourceType, $limit);
        }
    }
}


<?php
namespace Neos\Imagine;

/*
 * This file is part of the Neos.Imagine package.
 *
 * (c) Contributors of the Neos Project - www.neos.io
 *
 * This package is Open Source Software. For the full copyright and license
 * information, please view the LICENSE file which was distributed with this
 * source code.
 */

use Neos\Flow\Annotations as Flow;

/**
 * Abstract factory for Imagine package
 *
 * @Flow\Scope("singleton")
 */
class AbstractImagineFactory
{

    /**
     * @var array
     */
    protected $settings = [];

    /**
     * Injects the settings.
     *
     * @param array $settings
     * @return void
     * @throws \InvalidArgumentException
     */
    public function injectSettings(array $settings)
    {
        $this->settings = $settings;
        if (!isset($settings['enabledDrivers'])) {
            // FIXME: This is a hotfix and should actually be fixed in the Neos setup step. As soon as it is fixed there, this condition can be removed.
            return;
        }
        if (!in_array($settings['driver'], array_keys(array_filter($settings['enabledDrivers'])), true)) {
            throw new \InvalidArgumentException('The "driver" for Imagine must be enabled by settings, check Neos.Imagine.enabledDrivers.', 1515402616);
        }
    }
}


<?php

/**
 * This file is part of contao-community-alliance/dc-general-contao-frontend.
 *
 * (c) 2015-2018 Contao Community Alliance.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    contao-community-alliance/dc-general-contao-frontend
 * @author     Richard Henkenjohann <richardhenkenjohann@googlemail.com>
 * @copyright  2015-2018 Contao Community Alliance.
 * @license    https://github.com/contao-community-alliance/dc-general-contao-frontend/blob/master/LICENSE LGPL-3.0
 * @filesource
 */

namespace ContaoCommunityAlliance\DcGeneral\ContaoFrontend\View\ActionHandler;

use Contao\CoreBundle\Exception\RedirectResponseException;
use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator;
use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait;
use ContaoCommunityAlliance\DcGeneral\Data\ModelId;
use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface;
use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface;
use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent;
use ContaoCommunityAlliance\DcGeneral\Event\PostDuplicateModelEvent;
use ContaoCommunityAlliance\DcGeneral\Event\PreDuplicateModelEvent;
use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException;
use ContaoCommunityAlliance\DcGeneral\Exception\NotCreatableException;
use ContaoCommunityAlliance\UrlBuilder\UrlBuilder;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * This class handles the copy actions in the frontend.
 */
class CopyHandler
{
    use RequestScopeDeterminatorAwareTrait;

    /**
     * The current request stack.
     *
     * @var RequestStack
     */
    private $requestStack;

    /**
     * CopyHandler constructor.
     *
     * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator.
     *
     * @param RequestStack             $requestStack      The current request stack.
     */
    public function __construct(RequestScopeDeterminator $scopeDeterminator, RequestStack $requestStack)
    {
        $this->setScopeDeterminator($scopeDeterminator);

        $this->requestStack = $requestStack;
    }

    /**
     * Handle the event to process the action.
     *
     * @param ActionEvent $event The action event.
     *
     * @return void
     *
     * @throws RedirectResponseException To redirect to the edit mask with cloned model.
     * @throws DcGeneralRuntimeException When the DataContainer is not creatable.
     */
    public function handleEvent(ActionEvent $event): void
    {
        if (!$this->scopeDeterminator->currentScopeIsFrontend()) {
            return;
        }

        $environment = $event->getEnvironment();
        $action      = $event->getAction();

        // Only handle if we do not have a manual sorting or we know where to insert.
        // Manual sorting is handled by clipboard.
        if ('copy' !== $action->getName()) {
            return;
        }

        // Only run when no response given yet.
        if (null !== $event->getResponse()) {
            return;
        }

        $this->process($environment);
    }

    /**
     * Handle the action.
     *
     * @param EnvironmentInterface $environment The environment.
     *
     * @return void
     *
     * @throws RedirectResponseException To redirect to the edit mask with cloned model.
     * @throws NotCreatableException     When the DataContainer is not creatable.
     */
    public function process(EnvironmentInterface $environment): void
    {
        $dispatcher      = $environment->getEventDispatcher();
        $definition      = $environment->getDataDefinition();
        $basicDefinition = $definition->getBasicDefinition();
        $currentUrl      = $this->requestStack->getCurrentRequest()->getUri();

        if (!$basicDefinition->isCreatable()) {
            throw new NotCreatableException('DataContainer ' . $definition->getName() . ' is not creatable');
        }
        // We only support flat tables, sorry.
        if (BasicDefinitionInterface::MODE_FLAT !== $basicDefinition->getMode()) {
            return;
        }
        $modelId = ModelId::fromSerialized($environment->getInputProvider()->getParameter('source'));

        $dataProvider = $environment->getDataProvider();
        $model        = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId()));
        $copyModel    = $environment->getController()->createClonedModel($model);

        // Dispatch pre duplicate event.
        $copyEvent = new PreDuplicateModelEvent($environment, $copyModel, $model);
        $dispatcher->dispatch($copyEvent::NAME, $copyEvent);

        // Save the copy.
        $provider = $environment->getDataProvider($copyModel->getProviderName());
        $provider->save($copyModel);

        // Dispatch post duplicate event.
        $copyEvent = new PostDuplicateModelEvent($environment, $copyModel, $model);
        $dispatcher->dispatch($copyEvent::NAME, $copyEvent);

        // Redirect to the edit mask of the cloned model
        throw new RedirectResponseException(
            UrlBuilder::fromUrl($currentUrl)
                ->setQueryParameter('act', 'edit')
                ->setQueryParameter('id', ModelId::fromModel($copyModel)->getSerialized())
                ->unsetQueryParameter('source')
                ->getUrl()
        );
    }
}


<?php

/**
 * This file is part of contao-community-alliance/dc-general-contao-frontend.
 *
 * (c) 2015-2019 Contao Community Alliance.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    contao-community-alliance/dc-general-contao-frontend
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Ingolf Steinhardt <info@e-spin.de>
 * @author     Richard Henkenjohann <richardhenkenjohann@googlemail.com>
 * @copyright  2015-2019 Contao Community Alliance.
 * @license    https://github.com/contao-community-alliance/dc-general-contao-frontend/blob/master/LICENSE LGPL-3.0
 * @filesource
 */

namespace ContaoCommunityAlliance\DcGeneral\ContaoFrontend\View\ActionHandler;

use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator;
use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait;
use ContaoCommunityAlliance\DcGeneral\ContaoFrontend\View\EditMask;
use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface;
use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface;
use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent;
use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException;
use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException;
use ContaoCommunityAlliance\DcGeneral\Exception\NotCreatableException;

/**
 * This class handles the create actions in the frontend.
 */
class CreateHandler
{
    use RequestScopeDeterminatorAwareTrait;

    /**
     * CreateHandler constructor.
     *
     * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator.
     */
    public function __construct(RequestScopeDeterminator $scopeDeterminator)
    {
        $this->setScopeDeterminator($scopeDeterminator);
    }

    /**
     * Handle the event to process the action.
     *
     * @param ActionEvent $event The action event.
     *
     * @return void
     *
     * @throws DcGeneralInvalidArgumentException If an unknown property is encountered in the palette.
     * @throws DcGeneralRuntimeException         If the data container is not editable, closed.
     */
    public function handleEvent(ActionEvent $event): void
    {
        if (!$this->scopeDeterminator->currentScopeIsFrontend()) {
            return;
        }

        $environment = $event->getEnvironment();
        $action      = $event->getAction();

        // Only handle if we do not have a manual sorting or we know where to insert.
        // Manual sorting is handled by clipboard.
        if ('create' !== $action->getName()) {
            return;
        }

        // Only run when no response given yet.
        if (null !== $event->getResponse()) {
            return;
        }

        $response = $this->process($environment);
        if ($response !== false) {
            $event->setResponse($response);
        }
    }

    /**
     * Handle the action.
     *
     * @param EnvironmentInterface $environment The environment.
     *
     * @return string|bool
     *
     * @throws NotCreatableException If the data container is not editable, closed.
     */
    public function process(EnvironmentInterface $environment)
    {
        $definition      = $environment->getDataDefinition();
        $basicDefinition = $definition->getBasicDefinition();

        if (!$basicDefinition->isCreatable()) {
            throw new NotCreatableException('DataContainer ' . $definition->getName() . ' is not creatable');
        }
        // We only support flat tables, sorry.
        if (BasicDefinitionInterface::MODE_FLAT !== $basicDefinition->getMode()) {
            return false;
        }

        $dataProvider = $environment->getDataProvider();
        $properties   = $definition->getPropertiesDefinition()->getProperties();
        $model        = $dataProvider->getEmptyModel();
        $clone        = $dataProvider->getEmptyModel();

        // If some of the fields have a default value, set it.
        foreach ($properties as $property) {
            $propName = $property->getName();

            if ($property->getDefaultValue() !== null) {
                $model->setProperty($propName, $property->getDefaultValue());
                $clone->setProperty($propName, $property->getDefaultValue());
            }
        }

        return (new EditMask($environment, $model, $clone, null, null))->execute();
    }
}