/home/arranoyd/energyclinic/wp-content/plugins/docket-cache/includes/src/EventList.php
<?php
/**
 * Docket Cache.
 *
 * @author  Nawawi Jamili
 * @license MIT
 *
 * @see    https://github.com/nawawi/docket-cache
 */

/*
 * Credits:
 *  plugins/wp-crontrol/src/event-list-table.php
 *  plugins/wp-crontrol/src/event.php
 *  plugins/query-monitor/classes/Util.php
 */

namespace Nawawi\DocketCache;

\defined('ABSPATH') || exit;

if (!class_exists('\\WP_List_Table', false)) {
    require_once trailingslashit(ABSPATH).'wp-admin/includes/class-wp-list-table.php';
}

class EventList extends \WP_List_Table
{
    private $pt;

    public function __construct(Plugin $pt)
    {
        parent::__construct(
            [
                'singular' => 'eventlist-event',
                'plural' => 'eventlist-events',
                'ajax' => false,
                'screen' => 'eventlist-events',
            ]
        );

        $this->pt = $pt;
    }

    public function get_schedules()
    {
        $schedules = wp_get_schedules();
        uasort(
            $schedules,
            function (array $a, array $b) {
                return $a['interval'] - $b['interval'];
            }
        );

        array_walk(
            $schedules,
            function (array &$schedule, $name) {
                $schedule['name'] = $name;
            }
        );

        return $schedules;
    }

    public function get_crons()
    {
        $is_switch = $this->pt->switch_cron_site();

        $crons = $this->pt->get_crons(true);
        $events = [];

        if (empty($crons)) {
            if ($is_switch) {
                restore_current_blog();
            }

            return [];
        }

        foreach ($crons as $time => $cron) {
            foreach ($cron as $hook => $dings) {
                if (!has_action($hook)) {
                    // wp_clear_scheduled_hook($hook);
                    continue;
                }
                foreach ($dings as $sig => $data) {
                    $events[$hook.'-'.$sig.'-'.$time] = (object) [
                        'hook' => $hook,
                        'time' => $time,
                        'sig' => $sig,
                        'args' => $data['args'],
                        'schedule' => $data['schedule'],
                        'interval' => isset($data['interval']) ? $data['interval'] : null,
                    ];
                }
            }
        }

        uasort(
            $events,
            function ($a, $b) {
                if ($a->time === $b->time) {
                    return 0;
                }

                return ($a->time > $b->time) ? 1 : -1;
            }
        );

        if ($is_switch) {
            restore_current_blog();
        }

        return $events;
    }

    public function shorten_path($callback)
    {
        return preg_replace_callback(
            '@\\\\[a-zA-Z0-9_\\\\]{4,}\\\\@',
            function ($mm) {
                preg_match_all('@\\\\([a-zA-Z0-9_])@', $mm[0], $m);

                return '\\'.implode('\\', $m[1]).'\\';
            },
            $callback
        );
    }

    public function populate_callback($callback)
    {
        $callback = (array) $callback;

        if (method_exists('\QM_Util', 'populate_callback')) {
            return \QM_Util::populate_callback($callback);
        }

        if (\is_string($callback['function']) && (false !== strpos($callback['function'], '::'))) {
            $callback['function'] = explode('::', $callback['function']);
        }

        if (\is_array($callback['function'])) {
            if (\is_object($callback['function'][0])) {
                $class = \get_class($callback['function'][0]);
                $access = '->';
            } else {
                $class = $callback['function'][0];
                $access = '::';
            }

            $callback['name'] = $this->shorten_path($class.$access.$callback['function'][1].'()');
        } elseif (\is_object($callback['function'])) {
            if (is_a($callback['function'], 'Closure')) {
                $callback['name'] = 'Closure';
            } else {
                $class = \get_class($callback['function']);

                $callback['name'] = $this->shorten_path($class).'->__invoke()';
            }
        } else {
            $callback['name'] = $this->shorten_path($callback['function']).'()';
        }

        return $callback;
    }

    public function pretty_args($input)
    {
        $json_options = 0;

        if (\defined('JSON_UNESCAPED_SLASHES')) {
            $json_options |= \JSON_UNESCAPED_SLASHES;
        }
        if (\defined('JSON_PRETTY_PRINT')) {
            $json_options |= \JSON_PRETTY_PRINT;
        }

        return wp_json_encode($input, $json_options);
    }

    public function get_hook_callbacks($name)
    {
        global $wp_filter;

        $actions = [];

        if (isset($wp_filter[$name])) {
            $action = $wp_filter[$name];

            foreach ($action as $priority => $callbacks) {
                foreach ($callbacks as $callback) {
                    $callback = $this->populate_callback($callback);

                    $actions[] = [
                        'priority' => $priority,
                        'callback' => $callback,
                    ];
                }
            }
        }

        return $actions;
    }

    public function get_schedule_name(\stdClass $event)
    {
        $schedules = $this->get_schedules();

        if (isset($event->schedule) && isset($schedules[$event->schedule])) {
            return $schedules[$event->schedule]['display'];
        }

        /* translators: %s: Schedule name */
        $error_text = sprintf(__('Unknown (%s)', 'docket-cache'), $event->schedule);

        return new \WP_Error('unknown_schedule', $error_text);
    }

    public function interval($since)
    {
        // Array of time period chunks.
        $chunks = [
            /* translators: %s: The number of years in an interval of time. */
            [60 * 60 * 24 * 365, _n_noop('%s year', '%s years', 'docket-cache')],
            /* translators: %s: The number of months in an interval of time. */
            [60 * 60 * 24 * 30, _n_noop('%s month', '%s months', 'docket-cache')],
            /* translators: %s: The number of weeks in an interval of time. */
            [60 * 60 * 24 * 7, _n_noop('%s week', '%s weeks', 'docket-cache')],
            /* translators: %s: The number of days in an interval of time. */
            [60 * 60 * 24, _n_noop('%s day', '%s days', 'docket-cache')],
            /* translators: %s: The number of hours in an interval of time. */
            [60 * 60, _n_noop('%s hour', '%s hours', 'docket-cache')],
            /* translators: %s: The number of minutes in an interval of time. */
            [60, _n_noop('%s minute', '%s minutes', 'docket-cache')],
            /* translators: %s: The number of seconds in an interval of time. */
            [1, _n_noop('%s second', '%s seconds', 'docket-cache')],
        ];

        if ($since <= 0) {
            return __('now', 'docket-cache');
        }

        $j = \count($chunks);

        for ($i = 0; $i < $j; ++$i) {
            $seconds = $chunks[$i][0];
            $name = $chunks[$i][1];

            $count = floor($since / $seconds);
            if ($count) {
                break;
            }
        }

        $output = sprintf(translate_nooped_plural($name, $count, 'docket-cache'), $count);

        if ($i + 1 < $j) {
            $seconds2 = $chunks[$i + 1][0];
            $name2 = $chunks[$i + 1][1];
            $count2 = floor(($since - ($seconds * $count)) / $seconds2);
            if ($count2) {
                $output .= ' '.sprintf(translate_nooped_plural($name2, $count2, 'docket-cache'), $count2);
            }
        }

        return $output;
    }

    public function is_late($event)
    {
        $event = (object) $event;
        $until = $event->time - time();

        return $until < (0 - (10 * MINUTE_IN_SECONDS));
    }

    public function prepare_items()
    {
        $events = $this->get_crons();

        if (!empty($_GET['s'])) {
            $s = sanitize_text_field(wp_unslash($_GET['s']));

            $events = array_filter(
                $events,
                function ($event) use ($s) {
                    return false !== strpos($event->hook, $s);
                }
            );
        }

        $count = \count($events);
        $per_page = 50;
        $offset = ($this->get_pagenum() - 1) * $per_page;

        $this->items = \array_slice($events, $offset, $per_page);

        $this->set_pagination_args(
            [
                'total_items' => $count,
                'per_page' => $per_page,
                'total_pages' => ceil($count / $per_page),
            ]
        );
    }

    public function get_columns()
    {
        /* translators: %s: UTC offset */
        $next_run_text = sprintf(esc_html__('Next Schedule (%s)', 'docket-cache'), $this->pt->get_utc_offset());

        return [
            'eventlist_hook' => esc_html__('Hook', 'docket-cache'),
            'eventlist_args' => esc_html__('Arguments', 'docket-cache'),
            'eventlist_next' => $next_run_text,
            'eventlist_actions' => esc_html__('Action', 'docket-cache'),
            'eventlist_recurrence' => esc_html__('Recurrence', 'docket-cache'),
        ];
    }

    public function get_table_classes()
    {
        return ['widefat', 'striped', $this->_args['plural']];
    }

    protected function handle_row_actions($event, $column_name, $primary)
    {
        if ($primary !== $column_name) {
            return '';
        }

        $action = $this->pt->action_query(
            'runeventuno-cronbot',
            [
                'idx' => 'cronbot',
                'ehk' => rawurlencode($event->hook),
                'eky' => rawurlencode($event->sig),
            ]
        );
        $links[] = "<a href='".$action."'>".esc_html__('Run Now', 'docket-cache').'</a>';

        return $this->row_actions($links);
    }

    public function column_eventlist_hook($event)
    {
        return esc_html($event->hook);
    }

    public function column_eventlist_args($event)
    {
        if (!empty($event->args)) {
            if (\count($event->args) > 1) {
                return sprintf(
                    '<pre>%s</pre>',
                    esc_html($this->pretty_args($event->args))
                );
            }

            return $event->args[0];
        }

        return sprintf(
            '<em>%s</em>',
            esc_html__('None', 'docket-cache')
        );
    }

    public function column_eventlist_actions($event)
    {
        $hook_callbacks = $this->get_hook_callbacks($event->hook);

        if (!empty($hook_callbacks)) {
            $callbacks = [];

            foreach ($hook_callbacks as $callback) {
                $callbacks[] = '<code>'.$callback['callback']['name'].'</code>';
            }

            return implode('<br>', $callbacks);
        }

        return sprintf(
            '<span class="status-eventlist-warning">%s</span>',
            esc_html__('None', 'docket-cache')
        );
    }

    public function column_eventlist_next($event)
    {
        $date_utc = gmdate('Y-m-d\TH:i:s+00:00', $event->time);
        $date_local = get_date_from_gmt(date('Y-m-d H:i:s', $event->time), 'Y-m-d H:i:s');

        $time = sprintf(
            '<time datetime="%1$s">%2$s</time>',
            esc_attr($date_utc),
            esc_html($date_local)
        );

        $until = $event->time - time();
        $late = $this->is_late($event);

        if ($late) {
            /* translators: %s: Time period, for example "8 minutes" */
            $ago = sprintf(__('%s ago', 'docket-cache'), $this->interval(abs($until)));

            return sprintf(
                '%s<br><span class="status-eventlist-warning">%s</span>',
                $time,
                esc_html($ago)
            );
        }

        return sprintf(
            '%s<br>%s',
            $time,
            esc_html($this->interval($until))
        );
    }

    public function column_eventlist_recurrence($event)
    {
        if ($event->schedule) {
            $schedule_name = $this->get_schedule_name($event);
            if (is_wp_error($schedule_name)) {
                return sprintf(
                    '<span class="status-eventlist-error"><span class="dashicons dashicons-warning" aria-hidden="true"></span> %s</span>',
                    esc_html($schedule_name->get_error_message())
                );
            }

            return esc_html($schedule_name);
        }

        return esc_html__('Non-repeating', 'docket-cache');
    }

    public function no_items()
    {
        if (empty($_GET['s'])) {
            esc_html_e('There are currently no scheduled cron events.', 'docket-cache');
        } else {
            esc_html_e('No matching cron events.', 'docket-cache');
        }
    }
}