/**
  * Enqueue scripts/styles for admin screen
  *
  * @action admin_enqueue_scripts
  *
  * @param $hook
  *
  * @return void
  */
 public static function admin_enqueue_scripts($hook)
 {
     wp_register_script('select2', WP_STREAM_URL . 'ui/select2/select2.min.js', array('jquery'), '3.5.1', true);
     wp_register_style('select2', WP_STREAM_URL . 'ui/select2/select2.css', array(), '3.5.1');
     wp_register_script('timeago', WP_STREAM_URL . 'ui/timeago/timeago.js', array(), '1.4.1', true);
     $locale = substr(get_locale(), 0, 2);
     $file_tmpl = 'ui/timeago/locale/jquery.timeago.%s.js';
     if (file_exists(WP_STREAM_DIR . sprintf($file_tmpl, $locale))) {
         wp_register_script('timeago-locale', WP_STREAM_URL . sprintf($file_tmpl, $locale), array('timeago'), '1');
     } else {
         wp_register_script('timeago-locale', WP_STREAM_URL . sprintf($file_tmpl, 'en'), array('timeago'), '1');
     }
     wp_enqueue_style('wp-stream-admin', WP_STREAM_URL . 'ui/css/admin.css', array(), WP_Stream::VERSION);
     $script_screens = array('plugins.php', 'user-edit.php', 'user-new.php', 'profile.php');
     if ('index.php' === $hook) {
         wp_enqueue_script('wp-stream-dashboard', WP_STREAM_URL . 'ui/js/dashboard.js', array('jquery'), WP_Stream::VERSION);
         wp_enqueue_script('wp-stream-live-updates', WP_STREAM_URL . 'ui/js/live-updates.js', array('jquery', 'heartbeat'), WP_Stream::VERSION);
     } elseif (in_array($hook, self::$screen_id) || in_array($hook, $script_screens)) {
         wp_enqueue_script('select2');
         wp_enqueue_style('select2');
         wp_enqueue_script('timeago');
         wp_enqueue_script('timeago-locale');
         wp_enqueue_script('wp-stream-admin', WP_STREAM_URL . 'ui/js/admin.js', array('jquery', 'select2'), WP_Stream::VERSION);
         wp_enqueue_script('wp-stream-live-updates', WP_STREAM_URL . 'ui/js/live-updates.js', array('jquery', 'heartbeat'), WP_Stream::VERSION);
         wp_localize_script('wp-stream-admin', 'wp_stream', array('i18n' => array('confirm_defaults' => __('Are you sure you want to reset all site settings to default? This cannot be undone.', 'stream')), 'gmt_offset' => get_option('gmt_offset')));
     }
     wp_localize_script('wp-stream-live-updates', 'wp_stream_live_updates', array('current_screen' => $hook, 'current_page' => isset($_GET['paged']) ? esc_js($_GET['paged']) : '1', 'current_order' => isset($_GET['order']) ? esc_js($_GET['order']) : 'desc', 'current_query' => json_encode($_GET)));
     if (WP_Stream_Migrate::show_migrate_notice()) {
         $limit = absint(WP_Stream_Migrate::$limit);
         $record_count = absint(WP_Stream_Migrate::$record_count);
         $estimated_time = $limit < $record_count ? round($record_count / $limit * (0.04 * $limit) / 60) : 0;
         $migrate_time_message = $estimated_time > 1 ? sprintf(__('This will take about %d minutes.', 'stream'), absint($estimated_time)) : __('This could take a few minutes.', 'stream');
         $delete_time_message = $estimated_time > 1 && is_multisite() ? sprintf(__('This will take about %d minutes.', 'stream'), absint($estimated_time)) : __('This could take a few minutes.', 'stream');
         wp_enqueue_script('wp-stream-migrate', WP_STREAM_URL . 'ui/js/migrate.js', array('jquery'), WP_Stream::VERSION);
         wp_localize_script('wp-stream-migrate', 'wp_stream_migrate', array('i18n' => array('migrate_process_title' => __('Migrating Stream Records', 'stream'), 'delete_process_title' => __('Deleting Stream Records', 'stream'), 'error_message' => __('An unknown error occurred during migration. Please try again later or contact support.', 'stream'), 'migrate_process_message' => __('Please do not exit this page until the process has completed.', 'stream') . ' ' . esc_html($migrate_time_message), 'delete_process_message' => __('Please do not exit this page until the process has completed.', 'stream') . ' ' . esc_html($delete_time_message), 'confirm_start_migrate' => $estimated_time > 1 ? sprintf(__('Please note: This process will take about %d minutes to complete.', 'stream'), absint($estimated_time)) : __('Please note: This process could take a few minutes to complete.', 'stream'), 'confirm_migrate_reminder' => __('Please note: Your existing records will not appear in Stream until you have migrated them to your account.', 'stream'), 'confirm_delete_records' => sprintf(__('Are you sure you want to delete all %s existing Stream records without migrating? This will take %s minutes and cannot be undone.', 'stream'), number_format(WP_Stream_Migrate::$record_count), $estimated_time > 1 && is_multisite() ? sprintf(__('about %d', 'stream'), absint($estimated_time)) : __('a few', 'stream'))), 'chunk_size' => absint($limit), 'record_count' => absint($record_count), 'nonce' => wp_create_nonce('wp_stream_migrate-' . absint(get_current_blog_id()) . absint(get_current_user_id()))));
     }
     /**
      * The maximum number of items that can be updated in bulk without receiving a warning.
      *
      * Stream watches for bulk actions performed in the WordPress Admin (such as updating
      * many posts at once) and warns the user before proceeding if the number of items they
      * are attempting to update exceeds this threshold value. Since Stream will try to save
      * a log for each item, it will take longer than usual to complete the operation.
      *
      * The default threshold is 100 items.
      *
      * @return int
      */
     $bulk_actions_threshold = apply_filters('wp_stream_bulk_actions_threshold', 100);
     wp_enqueue_script('wp-stream-bulk-actions', WP_STREAM_URL . 'ui/js/bulk-actions.js', array('jquery'), WP_Stream::VERSION);
     wp_localize_script('wp-stream-bulk-actions', 'wp_stream_bulk_actions', array('i18n' => array('confirm_action' => sprintf(__('Are you sure you want to perform bulk actions on over %d items? This process could take a while to complete.', 'stream'), absint($bulk_actions_threshold)), 'confirm_import' => __('The Stream plugin must be deactivated before you can bulk import content into WordPress.', 'stream')), 'plugins_screen_url' => self_admin_url('plugins.php#stream'), 'threshold' => absint($bulk_actions_threshold)));
 }
 /**
  * Get a chunk of records formatted for Stream API ingestion
  *
  * @param  int  $limit  The number of rows to query
  *
  * @return mixed  An array of record arrays, or FALSE if no records were found
  */
 private static function get_records($limit = null)
 {
     $limit = is_int($limit) ? $limit : self::$limit;
     global $wpdb;
     $records = $wpdb->get_results($wpdb->prepare("\n\t\t\t\tSELECT s.*, sc.connector, sc.context, sc.action\n\t\t\t\tFROM {$wpdb->base_prefix}stream AS s, {$wpdb->base_prefix}stream_context AS sc\n\t\t\t\tWHERE s.site_id = %d\n\t\t\t\t\tAND s.blog_id = %d\n\t\t\t\t\tAND s.type = 'stream'\n\t\t\t\t\tAND sc.record_id = s.ID\n\t\t\t\tORDER BY s.created DESC\n\t\t\t\tLIMIT %d\n\t\t\t\t", self::$site_id, self::$blog_id, $limit), ARRAY_A);
     if (empty($records)) {
         return false;
     }
     self::$_records = array();
     foreach ($records as $record => $data) {
         $stream_meta = $wpdb->get_results($wpdb->prepare("SELECT meta_key, meta_value FROM {$wpdb->base_prefix}stream_meta WHERE record_id = %d", $records[$record]['ID']), ARRAY_A);
         $stream_meta_output = array();
         $author_meta_output = array();
         foreach ($stream_meta as $key => $meta) {
             if ('author_meta' === $meta['meta_key'] && !empty($meta['meta_value'])) {
                 $author_meta_output = maybe_unserialize($meta['meta_value']);
                 unset($stream_meta[$key]);
                 continue;
             }
             // Unserialize meta first so we can then check for malformed serialized strings
             $stream_meta_output[$meta['meta_key']] = maybe_unserialize($meta['meta_value']);
             // If any serialized data is still lingering in the meta value that means it's malformed and should be removed
             if (is_string($stream_meta_output[$meta['meta_key']]) && 1 === preg_match('/(a|O) ?\\x3a ?[0-9]+ ?\\x3a ?\\x7b/', $stream_meta_output[$meta['meta_key']])) {
                 unset($stream_meta_output[$meta['meta_key']]);
                 continue;
             }
             // All meta must be strings, so serialize any array meta values again
             $stream_meta_output[$meta['meta_key']] = (string) maybe_serialize($stream_meta_output[$meta['meta_key']]);
         }
         // All author meta must be strings
         array_walk($author_meta_output, function (&$v) {
             $v = (string) $v;
         });
         $records[$record]['stream_meta'] = $stream_meta_output;
         $records[$record]['author_meta'] = $author_meta_output;
         self::$_records[] = $records[$record];
         $records[$record]['created'] = wp_stream_get_iso_8601_extended_date(strtotime($records[$record]['created']));
         unset($records[$record]['ID']);
         unset($records[$record]['parent']);
         // Ensure required fields always exist
         $records[$record]['site_id'] = !empty($records[$record]['site_id']) ? $records[$record]['site_id'] : 1;
         $records[$record]['blog_id'] = !empty($records[$record]['blog_id']) ? $records[$record]['blog_id'] : 1;
         $records[$record]['object_id'] = !empty($records[$record]['object_id']) ? $records[$record]['object_id'] : 0;
         $records[$record]['author'] = !empty($records[$record]['author']) ? $records[$record]['author'] : 0;
         $records[$record]['author_role'] = !empty($records[$record]['author_role']) ? $records[$record]['author_role'] : '';
         $records[$record]['ip'] = !empty($records[$record]['ip']) ? $records[$record]['ip'] : '';
     }
     return $records;
 }