parent = &$parent; add_action( 'admin_init', array( &$this, 'set_presets' ) ); add_action( 'wp_ajax_infinite-scroll-edit-preset', array( &$this, 'process_ajax_edit' ) ); add_action( 'wp_ajax_infinite-scroll-delete-preset', array( &$this, 'process_ajax_delete' ) ); add_filter( $this->parent->prefix . 'presets', array( &$this, 'merge_custom_presets' ) ); add_filter( $this->parent->prefix . 'options', array( &$this, 'default_to_presets'), 9 ); add_action( $this->parent->prefix . 'refresh_cache', array( &$this, 'get_presets' ) ); } /** * Allow for class overloading * @param string $preset the theme slug to retrieve * @return array|bool the presets or false on failure */ function __get( $preset ) { return $this->get_preset( $preset ); } function getThemes($args) { if (function_exists("wp_get_themes")) { return wp_get_themes($args); } else { return get_themes(); } } function getTheme($theme) { if (function_exists("wp_get_theme")) { return wp_get_theme($theme); } else { return get_theme($theme); } } /** * Pulls preset array from cache, or retrieves and parses * @return array an array of preset objects * @todo Consider using TLC Transients in case cron isn't working */ function get_presets() { //check cache if ( $cache = get_transient( $this->parent->prefix . 'presets' ) ) return apply_filters( $this->parent->prefix . 'presets', $cache ); $data = wp_remote_get( $this->preset_url ); if ( is_wp_error( $data ) ) return array(); $data = wp_remote_retrieve_body( $data ); //parse CSV string into array $presets = $this->parse_csv( $data ); //sort by key alpha ascending asort( $presets ); set_transient( $this->parent->prefix . 'presets', $presets, $this->ttl ); return apply_filters( $this->parent->prefix . 'presets', $presets ); } /** * Return a theme's preset object * @param string $theme the slug of theme to retrieve * @return object the preset object */ function get_preset( $theme = null ) { if ( $theme == null ) $theme = get_stylesheet(); $presets = $this->get_presets(); //direct match found, return if ( array_key_exists( $theme, $presets ) ) return $presets[ $theme ]; //no direct match found, permahps this is a child theme? //theme isn't installed, no way to know if it's a child, so skip if ( !$this->theme_installed( $theme ) ) return false; //WP version 3.4+, use the new wp_get_themes function if ( function_exists( 'wp_get_theme' ) ) { $theme = $this->getTheme($theme); //not a theme or not a child if ( is_wp_error( $theme ) || !is_object( $theme->parent() ) ) return false; return $this->get_preset( $theme->parent()->stylesheet ); } //pre 3.4 back compat.. //get theme by slug $name = $this->get_name( $theme ); $themes = $this->getThemes(array()); $child = $themes[ $name ]; //not a child theme if ( !isset( $child['Template'] ) || empty( $child['Template'] ) || $child['Template'] == $child['Stylesheet'] ) return false; //pull up parent data to get its name $parent = $themes[$name]['Template']; $parent = get_theme_data( get_theme_root( $child['Template'] ) . '/' . $child['Template'] . '/style.css' ); $preset = $this->get_preset( $parent['Stylesheet'] ); //no parent preset if ( !$preset ) return false; //rename the theme of the parent preset object for consistent return $preset->theme = $theme; $preset->parentPreset = $parent['Stylesheet']; return $preset; } /** * On plugin activation register with WP_Cron API to asynchronously refresh cache every 24 hours * This will also asynchronously prime the cache on activation */ function schedule() { wp_schedule_event( time(), 'daily', $this->parent->prefix . 'refresh_cache' ); } /** * Clear chron schedule on deactivation */ function unschedule() { wp_clear_scheduled_hook( $this->parent->prefix . 'refresh_cache' ); } /** * Conditionally prompts users on options page to use the default selectors * @uses get_preset */ function preset_prompt() { $preset = $this->get_preset( ); if ( !$preset ) return; unset( $preset->theme ); unset( $preset->parentPreset ); //if they are already using the preset, don't prompt $using_default = true; foreach ( $preset as $key => $value ) { if ( $this->parent->options->$key != $value ) $using_default = false; } if ( $using_default ) return; require dirname( $this->parent->file ) . '/templates/preset-prompt.php'; } /** * Reset selectors to default */ function set_presets() { if ( !isset( $_GET['set_presets'] ) ) return; if ( !current_user_can( 'manage_options' ) ) return; check_admin_referer( 'infinite-scroll-presets', 'nonce' ); //don't delete options if we don't have a preset $preset = $this->get_preset( ); if ( !$preset ) return; foreach ( $this->keys as $key ) $this->parent->options->$key = null; wp_redirect( admin_url( 'options-general.php?page=infinite_scroll_options&settings-updated=true' ) ); exit(); } /** * Handles AJAX edits from the manage presets form */ function process_ajax_edit() { if ( !current_user_can( 'manage_options' ) ) wp_die( -1 ); if ( is_multisite() && !is_super_admin() ) wp_die( -1 ); $data = new stdClass; foreach ( $this->keys as $key ) $data->$key = addslashes( trim( $_POST[ $key . '_column-' . $key ] ) ); $this->set_custom_preset( $data->theme, $data ); wp_die( 1 ); } /** * Handles AJAX requests to delete presets from the manage presets form */ function process_ajax_delete() { if ( !current_user_can( 'manage_options' ) ) wp_die( -1 ); if ( is_multisite() && !is_super_admin() ) wp_die( -1 ); if ( !isset( $_GET['theme'] ) ) wp_die( -1 ); $this->delete_custom_preset( $_GET['theme'] ); } /** * Retreive global custom presets * @return array the custom preset array */ function get_custom_presets( ) { $presets = get_site_option( $this->custom_preset_key, array(), true ); return apply_filters( $this->parent->prefix . 'custom_presets', $presets ); } /** * Update global custom presets * @param array $presets the presets (all) * @return bool success/fail */ function set_custom_presets( $presets ) { return update_site_option( $this->custom_preset_key, $presets ); } /** * Store a theme's global presets * @param string $theme the theme name * @param array $preset the presets * @return bool success/fail */ function set_custom_preset( $theme, $preset ) { $presets = $this->get_custom_presets(); $presets[ $theme ] = $preset; return $this->set_custom_presets( $presets ); } /** * Removes a custom preset from the database * @param string $theme the theme to remove * @return bool success/fail */ function delete_custom_preset( $theme ) { $presets = $this->get_custom_presets(); unset( $presets[ $theme ] ); return $this->set_custom_presets( $presets ); } /** * Allow custom presets to merge/override community presets * @param unknown $presets * @return unknown */ function merge_custom_presets( $presets ) { // 2nd array overrides keys that overlap with first array $presets = array_merge( $presets, $this->get_custom_presets() ); //sort by key alpha ascending asort( $presets ); return $presets; } /** * If a selector is not set, try to grab a preset to save the user trouble * @param array $options the options array * @return array the defaulted options array */ function default_to_presets( $options ) { //we don't have a preset, no need to go any further if ( !( $preset = $this->get_preset( ) ) ) return $options; foreach ( $this->keys as $key ) { if ( empty( $options[$key] ) ) $options[$key] = $preset->$key; } return $options; } /** * Converts legacy csv.php format * Removes first two lines and last line * @param string $data the contents of the CSV (usually via wp_remote_get) * @param string the equivalent standard CSV */ function parse_legacy_csv( $data ) { if ( is_string( $data ) ) $data = explode( "\n", $data ); //remove first two lines $data = array_slice( $data, 2 ); //remove the last line array_pop( $data ); $presets = $this->parse_csv( $data ); $output = array(); //convert Theme Name to stylesheet and stuff into output array foreach( $presets as $theme ) { $theme->theme = $this->get_stylesheet( $theme->theme ); $output[ $theme->theme ] = $theme; } return $output; } /** * Parse CSV into array of preset objects * @param string|array the CSV data, either as a string or as an array of lines * @return array array of preset objects */ function parse_csv( $data ) { if ( is_string( $data ) ) $data = explode( "\n", $data ); //php 5.3+ if ( function_exists( 'str_getcsv' ) ) { foreach ( $data as &$line ) $line = str_getcsv( $line ); //php 5.2 // fgetcsv needs a file handle, // so write the string to a temp file before parsing } else { $fh = tmpfile(); fwrite( $fh, implode( "\n", $data ) ); fseek( $fh, 0 ); $data = array(); while( $line = fgetcsv( $fh ) ) $data[] = $line; fclose( $fh ); } $presets = array(); //build preset objects and stuff into keyed array foreach ( $data as &$line ) { $lineObj = new stdClass; foreach ( $this->keys as $id => $key ) $lineObj->$key = $line[ $id ]; $presets[ $lineObj->theme ] = $lineObj; } return $presets; } /** * Return object representing current theme's selectors * @return object the same as would be returned from get_preset() */ function current_selectors() { $theme = new stdClass(); foreach ( $this->keys as $key ) $theme->$key = $this->parent->options->$key; $theme->theme = get_stylesheet(); return $theme; } /** * Export CSS Selectors as CSV * @param bool $all (optional) whether to include community selectors in output * @return string CSV of selectors */ function export( $all = false ) { $presets = array(); //if the current theme is not a known preset or they want all if ( !$this->get_preset( ) || $all ) $presets[ get_stylesheet() ] = $this->current_selectors(); //user has access to global custom presets if ( is_multisite() && is_super_admin() ) { if ( $custom = $this->get_custom_presets() ); $presets = array_merge( $presets, $custom ); } //include community presets, if asked if ( $all ) $presets = array_merge( $this->get_presets(), $presets ); asort( $presets ); //workaround because fputcsv needs a file handle by default $fh = tmpfile(); $length = 0; foreach ( $presets as &$preset ) $length += fputcsv( $fh, (array) $preset ); if ( $length == 0 ) return false; fseek( $fh, 0 ); $csv = fread( $fh, $length ); fclose( $fh ); return $csv; } /** * Migrates legacy csv.php files to 2.5's custom presets format * @uses parse_legacy_csv */ function migrate() { //no preset file to migrate if ( !file_exists( dirname( $this->parent->file ) . '/PresetDB.csv.php' ) ) return; $data = file_get_contents( dirname( $this->parent->file ) . '/PresetDB.csv.php' ); $presets = $this->parse_legacy_csv( $data ); //this wiill override any existing presets, // but is okay as is only being fired when no presets exist $this->set_custom_presets( $presets ); return $presets; } /** * Determines whether a given theme is installed * @param string|object $theme either the theme slug or the preset object * @return bool true if insalled, otherwise false */ function theme_installed( $theme ) { // get theme name if $theme is an preset object if ( is_object( $theme ) ) { $theme = $theme->theme; } //3.4+ if ( function_exists( 'wp_get_theme' ) ) { return wp_get_theme( $theme )->exists(); } else { //pre 3.4 $themes = get_themes(); $name = $this->get_name( $theme ); return array_key_exists( $name, $themes ); } } /** * Given a theme name, returns the coresponding theme stylesheet * * Used for converting legacy CSVs which were name based to new CSVs which are stylesheet based * since 3.4 returns themes keyed to stylesheets, not names as it did pre-3.4 * * @param string $theme the theme name * @return string the stylesheet */ function get_stylesheet( $name ) { //pre 3.4 if ( !function_exists( 'wp_get_themes' ) ) { if ( $theme = get_theme( $name ) ) return $theme->stylesheet; //3.4+ } else { //we can't use wp_filter_list_object with WP_Theme objects, so filter manually foreach ( $this->getThemes(null) as $theme ) if ( $theme->name = $name ) return $theme->stylesheet; } return false; } /** * Given a theme stylesheet, return the coresponding theme name * * Used to normalize data between 3.3 and 3.4 where keying of themes switched from name to stylesheet * * @param string $stylesheet the theme stylesheet (slug) * @return string the theme name */ function get_name( $stylesheet ) { //3.4+ if ( function_exists( 'wp_get_theme' ) ) { if ( $theme = wp_get_theme( $stylesheet ) ) return $theme->name; //pre 3.4 } else { foreach ( get_themes() as $theme ) if ( $theme->stylesheet == $stylesheet ) return $theme->name; } //theme isn't installed, use the WP.org API to grab the name rather than risk losing data on upgrade $api = themes_api( 'theme_information', array( 'slug' => $stylesheet, 'fields' => array( 'sections' => false, 'tags' => false ) ) ); if ( is_wp_error( $api ) ) return false; return $api->name; } } if (!class_exists('WP_List_Table')) { require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php'; } /** * List table for manage custom presets page */ class Infinite_Scroll_Presets_Table extends WP_List_Table { /** * Register with Parent */ function __construct( $args = array() ) { parent::__construct( array( 'singular' => 'preset', 'plural' => 'presets', 'ajax' => true, ) ); } /** * Default column callback * @param object $item the item to display * @param string $column_name the column name * @return string the HTML to display */ function column_default( $item, $column_name ) { return $item->$column_name; } /** * Callback to display the theme column * @param object $item the preset object * @return string the HTML to display */ function column_theme( $item ) { global $infinite_scroll; $s = '' . $item->theme . ''; $s .= ''; $s .= ''; return $s; } /** * Callaback to return list of columns to display * @return array the columns to display */ function get_columns() { return array( 'theme' => 'Theme', 'contentSelector' => 'Content Selector', 'navSelector' => 'Navigation Selector', 'nextSelector' => 'Next Selector', 'itemSelector' => 'Item Selector', ); } /** * Grab data and filter prior to passing to table class */ function prepare_items() { global $infinite_scroll; $per_page = 25; $columns = $this->get_columns(); $hidden = array(); $sortable = array(); $this->_column_headers = array($columns, $hidden, $sortable); $data = $infinite_scroll->presets->get_presets(); //only display installed themes $data = array_filter( $data, array( &$infinite_scroll->presets, 'theme_installed' ) ); //merge in themes $themes = $infinite_scroll->presets->getThemes(null); foreach ( $themes as $theme => $theme_data ) { if ( array_key_exists( $theme, $data) ) continue; //check for parent theme's preset, if any if ( $preset = $infinite_scroll->presets->get_preset( $theme ) ) { $data[ $theme ] = $preset; continue; } $themeObj = new stdClass; foreach ( $infinite_scroll->presets->keys as $key ) $themeObj->$key = null; $themeObj->theme = $theme; $data[ $theme ] = $themeObj; } asort( $data ); $current_page = $this->get_pagenum(); $total_items = count($data); $data = array_slice($data, (($current_page-1)*$per_page), $per_page); $this->items = $data; $this->set_pagination_args( array( 'total_items' => $total_items, //WE have to calculate the total number of items 'per_page' => $per_page, //WE have to determine how many items to show on a page 'total_pages' => ceil($total_items/$per_page) //WE have to calculate the total number of pages ) ); } }