";
return false;
}
/**
*
* Backwards compatibility - see http://php.net/manual/en/function.str-getcsv.php
*
* @param $input
* @param string $delimiter
* @param string $enclosure
* @param string $escape
*
* @return array
*/
function str_getcsv( $input, $delimiter = ',', $enclosure = '"', $escape = '\\' ) {
$fp = fopen( 'php://memory', 'r+' );
fputs( $fp, $input );
rewind( $fp );
$data = fgetcsv( $fp, null, $delimiter, $enclosure ); // $escape only got added in 5.3.0
fclose( $fp );
return $data;
}
/**
*
* Helper function to convert csv in key/value pair format to an associative array.
*
* @param $csv
*
* @return array
*/
function csv_to_array( $csv ) {
$args = array();
if ( version_compare( PHP_VERSION, '5.3', '>=' ) ) {
$v = $this->str_getcsv( $csv );
} else {
$v = str_getcsv( $csv ); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.str_getcsvFound
}
$size = count( $v );
if ( is_array( $v ) && isset( $v[0] ) && $size >= 2 ) {
for ( $i = 0; $i < $size; $i += 2 ) {
$args[ $v[ $i ] ] = $v[ $i + 1 ];
}
}
return $args;
}
/** Allow modules to use WP Filesystem if available and desired, fall back to PHP filesystem access otherwise.
*
* @param string $method
* @param bool $form_fields
* @param string $url
* @param bool $error
*
* @return bool
*/
function use_wp_filesystem( $method = '', $form_fields = false, $url = '', $error = false ) {
if ( empty( $method ) ) {
$this->credentials = request_filesystem_credentials( $url );
} else {
$this->credentials = request_filesystem_credentials( $url, $method, $error, false, $form_fields );
}
return $this->credentials;
}
/**
* Wrapper function to get filesystem object.
*/
function get_filesystem_object() {
$cred = get_transient( 'aioseop_fs_credentials' );
if ( ! empty( $cred ) ) {
$this->credentials = $cred;
}
if ( function_exists( 'WP_Filesystem' ) && WP_Filesystem( $this->credentials ) ) {
global $wp_filesystem;
return $wp_filesystem;
} else {
require_once( ABSPATH . 'wp-admin/includes/template.php' );
require_once( ABSPATH . 'wp-admin/includes/screen.php' );
require_once( ABSPATH . 'wp-admin/includes/file.php' );
if ( ! WP_Filesystem( $this->credentials ) ) {
$this->use_wp_filesystem();
}
if ( ! empty( $this->credentials ) ) {
set_transient( 'aioseop_fs_credentials', $this->credentials, 10800 );
}
global $wp_filesystem;
if ( is_object( $wp_filesystem ) ) {
return $wp_filesystem;
}
}
return false;
}
/**
* See if a file exists using WP Filesystem.
*
* @param string $filename
*
* @return bool
*/
function file_exists( $filename ) {
$wpfs = $this->get_filesystem_object();
if ( is_object( $wpfs ) ) {
return $wpfs->exists( $filename );
}
return $wpfs;
}
/**
* See if the directory entry is a file using WP Filesystem.
*
* @param $filename
*
* @return bool
*/
function is_file( $filename ) {
$wpfs = $this->get_filesystem_object();
if ( is_object( $wpfs ) ) {
return $wpfs->is_file( $filename );
}
return $wpfs;
}
/**
* List files in a directory using WP Filesystem.
*
* @param $path
*
* @return array|bool
*/
function scandir( $path ) {
$wpfs = $this->get_filesystem_object();
if ( is_object( $wpfs ) ) {
$dirlist = $wpfs->dirlist( $path );
if ( empty( $dirlist ) ) {
return $dirlist;
}
return array_keys( $dirlist );
}
return $wpfs;
}
/**
* Load a file through WP Filesystem; implement basic support for offset and maxlen.
*
* @param $filename
* @param bool $use_include_path
* @param null $context
* @param int $offset
* @param int $maxlen
*
* @return bool|mixed
*/
function load_file( $filename, $use_include_path = false, $context = null, $offset = - 1, $maxlen = - 1 ) {
$wpfs = $this->get_filesystem_object();
if ( is_object( $wpfs ) ) {
if ( ! $wpfs->exists( $filename ) ) {
return false;
}
if ( ( $offset > 0 ) || ( $maxlen >= 0 ) ) {
if ( 0 === $maxlen ) {
return '';
}
if ( 0 > $offset ) {
$offset = 0;
}
$file = $wpfs->get_contents( $filename );
if ( ! is_string( $file ) || empty( $file ) ) {
return $file;
}
if ( 0 > $maxlen ) {
return $this->substr( $file, $offset );
} else {
return $this->substr( $file, $offset, $maxlen );
}
} else {
return $wpfs->get_contents( $filename );
}
}
return false;
}
/**
* Save a file through WP Filesystem.
*
* @param string $filename
*
* @param $contents
*
* @return bool
*/
function save_file( $filename, $contents ) {
/* translators: %s is a placeholder and will be replaced with the name of the relevant file. */
$failed_str = sprintf( __( 'Failed to write file %s!', 'all-in-one-seo-pack' ) . "\n", $filename );
/* translators: %s is a placeholder and will be replaced with the name of the relevant file. */
$readonly_str = sprintf( __( 'File %s isn\'t writable!', 'all-in-one-seo-pack' ) . "\n", $filename );
$wpfs = $this->get_filesystem_object();
if ( is_object( $wpfs ) ) {
$file_exists = $wpfs->exists( $filename );
if ( ! $file_exists || $wpfs->is_writable( $filename ) ) {
if ( $wpfs->put_contents( $filename, $contents ) === false ) {
return $this->output_error( $failed_str );
}
} else {
return $this->output_error( $readonly_str );
}
return true;
}
return false;
}
/**
* Delete a file through WP Filesystem.
*
* @param string $filename
*
* @return bool
*/
function delete_file( $filename ) {
$wpfs = $this->get_filesystem_object();
if ( is_object( $wpfs ) ) {
if ( $wpfs->exists( $filename ) ) {
if ( $wpfs->delete( $filename ) === false ) {
/* translators: %s is a placeholder and will be replaced with the name of the relevant file. */
$this->output_error( sprintf( __( 'Failed to delete file %s!', 'all-in-one-seo-pack' ) . "\n", $filename ) );
} else {
return true;
}
} else {
/* translators: %s is a placeholder and will be replaced with the name of the relevant file. */
$this->output_error( sprintf( __( "File %s doesn't exist!", 'all-in-one-seo-pack' ) . "\n", $filename ) );
}
}
return false;
}
/**
* Rename a file through WP Filesystem.
*
* @param string $filename
* @param string $newname
*
* @return bool
*/
function rename_file( $filename, $newname ) {
$wpfs = $this->get_filesystem_object();
if ( is_object( $wpfs ) ) {
$file_exists = $wpfs->exists( $filename );
$newfile_exists = $wpfs->exists( $newname );
if ( $file_exists && ! $newfile_exists ) {
if ( $wpfs->move( $filename, $newname ) === false ) {
/* translators: %s is a placeholder and will be replaced with the name of the relevant file. */
$this->output_error( sprintf( __( 'Failed to rename file %s!', 'all-in-one-seo-pack' ) . "\n", $filename ) );
} else {
return true;
}
} else {
if ( ! $file_exists ) {
/* translators: %s is a placeholder and will be replaced with the name of the relevant file. */
$this->output_error( sprintf( __( "File %s doesn't exist!", 'all-in-one-seo-pack' ) . "\n", $filename ) );
} elseif ( $newfile_exists ) {
/* translators: %s is a placeholder and will be replaced with the name of the relevant file. */
$this->output_error( sprintf( __( 'File %s already exists!', 'all-in-one-seo-pack' ) . "\n", $newname ) );
}
}
}
return false;
}
/**
* Load multiple files.
*
* @param $options
* @param $opts
* @param $prefix
*
* @return mixed
*/
function load_files( $options, $opts, $prefix ) {
foreach ( $opts as $opt => $file ) {
$opt = $prefix . $opt;
$file = ABSPATH . $file;
$contents = $this->load_file( $file );
if ( false !== $contents ) {
$options[ $opt ] = $contents;
}
}
return $options;
}
/**
* Save multiple files.
*
* @param $opts
* @param $prefix
*/
function save_files( $opts, $prefix ) {
foreach ( $opts as $opt => $file ) {
$opt = $prefix . $opt;
if ( isset( $_POST[ $opt ] ) ) {
$output = stripslashes_deep( $_POST[ $opt ] );
$file = ABSPATH . $file;
$this->save_file( $file, $output );
}
}
}
/**
* Delete multiple files.
*
* @param $opts
*/
function delete_files( $opts ) {
foreach ( $opts as $opt => $file ) {
$file = ABSPATH . $file;
$this->delete_file( $file );
}
}
/**
* Returns available social seo images.
*
* @since 2.4 #1079 Fixes array_flip warning on opengraph module.
*
* @param array $options Plugin/module options.
* @param object $p Post.
*
* @return array
*/
function get_all_images_by_type( $options = null, $p = null ) {
$img = array();
if ( empty( $img ) ) {
$size = apply_filters( 'post_thumbnail_size', 'large' );
global $aioseop_options, $wp_query, $aioseop_opengraph;
if ( null === $p ) {
global $post;
} else {
$post = $p;
}
$count = 1;
if ( ! empty( $post ) ) {
if ( ! is_object( $post ) ) {
$post = get_post( $post );
}
if ( is_object( $post ) && function_exists( 'get_post_thumbnail_id' ) ) {
if ( 'attachment' == $post->post_type ) {
$post_thumbnail_id = $post->ID;
} else {
$post_thumbnail_id = get_post_thumbnail_id( $post->ID );
}
if ( ! empty( $post_thumbnail_id ) ) {
$image = wp_get_attachment_image_src( $post_thumbnail_id, $size );
if ( is_array( $image ) ) {
$img[] = array(
'type' => 'featured',
'id' => $post_thumbnail_id,
'link' => $image[0],
);
}
}
}
$post_id = $post->ID;
$p = $post;
$w = $wp_query;
$meta_key = '';
if ( is_array( $options ) && isset( $options['meta_key'] ) ) {
$meta_key = $options['meta_key'];
}
if ( ! empty( $meta_key ) && ! empty( $post ) ) {
$image = $this->get_the_image_by_meta_key(
array(
'post_id' => $post->ID,
'meta_key' => explode( ',', $meta_key ),
)
);
if ( ! empty( $image ) ) {
$img[] = array(
'type' => 'meta_key',
'id' => $meta_key,
'link' => $image,
);
}
}
if ( '' != ! $post->post_modified_gmt ) {
$wp_query = new WP_Query(
array(
'p' => $post_id,
'post_type' => $post->post_type,
)
);
}
if ( 'page' == $post->post_type ) {
$wp_query->is_page = true;
} elseif ( 'attachment' == $post->post_type ) {
$wp_query->is_attachment = true;
} else {
$wp_query->is_single = true;
}
if ( 'page' == get_option( 'show_on_front' ) && get_option( 'page_for_posts' ) == $post->ID ) {
$wp_query->is_home = true;
}
$args['options']['type'] = 'html';
$args['options']['nowrap'] = false;
$args['options']['save'] = false;
$wp_query->queried_object = $post;
$attachments = get_children(
array(
'post_parent' => $post->ID,
'post_status' => 'inherit',
'post_type' => 'attachment',
'post_mime_type' => 'image',
'order' => 'ASC',
'orderby' => 'menu_order ID',
)
);
if ( ! empty( $attachments ) ) {
foreach ( $attachments as $id => $attachment ) {
$image = wp_get_attachment_image_src( $id, $size );
if ( is_array( $image ) ) {
$img[] = array(
'type' => 'attachment',
'id' => $id,
'link' => $image[0],
);
}
}
}
$matches = array();
preg_match_all( '||i', get_post_field( 'post_content', $post->ID ), $matches );
if ( isset( $matches ) && ! empty( $matches[1] ) && ! empty( $matches[1][0] ) ) {
foreach ( $matches[1] as $i => $m ) {
$img[] = array(
'type' => 'post_content',
'id' => 'post' . $count ++,
'link' => $m,
);
}
}
wp_reset_postdata();
$wp_query = $w;
$post = $p;
}
}
return $img;
}
/**
* Get All Images
*
* @since ?
*
* @param null $options
* @param null $p
* @return array
*/
function get_all_images( $options = null, $p = null ) {
$img = $this->get_all_images_by_type( $options, $p );
$legacy = array();
foreach ( $img as $k => $v ) {
$v['link'] = set_url_scheme( $v['link'] );
if ( 'featured' == $v['type'] ) {
$legacy[ $v['link'] ] = 1;
} else {
$legacy[ $v['link'] ] = $v['id'];
}
}
return $legacy;
}
/**
* Thanks to Justin Tadlock for the original get-the-image code - http://themehybrid.com/plugins/get-the-image **
*
* @param null $options
* @param null $p
*
* @return bool|mixed|string
*/
function get_the_image( $options = null, $p = null ) {
if ( null === $p ) {
global $post;
} else {
$post = $p;
}
$meta_key = '';
if ( is_array( $options ) && isset( $options['meta_key'] ) ) {
$meta_key = $options['meta_key'];
}
if ( ! empty( $meta_key ) && ! empty( $post ) ) {
$meta_key = explode( ',', $meta_key );
$image = $this->get_the_image_by_meta_key(
array(
'post_id' => $post->ID,
'meta_key' => $meta_key,
)
);
}
if ( empty( $image ) ) {
$image = $this->get_the_image_by_post_thumbnail( $post );
}
if ( empty( $image ) ) {
$image = $this->get_the_image_by_attachment( $post );
}
if ( empty( $image ) ) {
$image = $this->get_the_image_by_scan( $post );
}
if ( empty( $image ) ) {
$image = $this->get_the_image_by_default( $post );
}
return $image;
}
/**
* Get the Image by Default
*
* @since ?
*
* @param null $p
* @return string
*/
function get_the_image_by_default( $p = null ) {
return '';
}
/**
* Get the Image by Meta Key
*
* @since ?
*
* @param array $args
* @return bool|mixed
*/
function get_the_image_by_meta_key( $args = array() ) {
/* If $meta_key is not an array. */
if ( ! is_array( $args['meta_key'] ) ) {
$args['meta_key'] = array( $args['meta_key'] );
}
/* Loop through each of the given meta keys. */
foreach ( $args['meta_key'] as $meta_key ) {
/* Get the image URL by the current meta key in the loop. */
$image = get_post_meta( $args['post_id'], $meta_key, true );
/* If a custom key value has been given for one of the keys, return the image URL. */
if ( ! empty( $image ) ) {
return $image;
}
}
return false;
}
/**
* Get the Image by Post Thumbnail
*
* @since ?
* @since 2.4.13 Fixes when content is taxonomy.
*
* @param null $p
* @return bool
*/
function get_the_image_by_post_thumbnail( $p = null ) {
if ( null === $p ) {
global $post;
} else {
$post = $p;
}
if ( is_category() || is_tag() || is_tax() ) {
return false;
}
$post_thumbnail_id = null;
if ( function_exists( 'get_post_thumbnail_id' ) ) {
$post_thumbnail_id = get_post_thumbnail_id( $post->ID );
}
if ( empty( $post_thumbnail_id ) ) {
return false;
}
// Check if someone is using built-in WP filter.
$size = apply_filters( 'aioseop_thumbnail_size', apply_filters( 'post_thumbnail_size', 'large' ) );
$image = wp_get_attachment_image_src( $post_thumbnail_id, $size );
return $image[0];
}
/**
* Get the Image by Attachment
*
* @since ?
*
* @param null $p
* @return bool
*/
function get_the_image_by_attachment( $p = null ) {
if ( null === $p ) {
global $post;
} else {
$post = $p;
}
$attachments = get_children(
array(
'post_parent' => $post->ID,
'post_status' => 'inherit',
'post_type' => 'attachment',
'post_mime_type' => 'image',
'order' => 'ASC',
'orderby' => 'menu_order ID',
)
);
if ( empty( $attachments ) && 'attachment' == get_post_type( $post->ID ) ) {
$size = apply_filters( 'aioseop_attachment_size', 'large' );
$image = wp_get_attachment_image_src( $post->ID, $size );
}
/* If no attachments or image is found, return false. */
if ( empty( $attachments ) && empty( $image ) ) {
return false;
}
/* Set the default iterator to 0. */
$i = 0;
/* Loop through each attachment. Once the $order_of_image (default is '1') is reached, break the loop. */
foreach ( $attachments as $id => $attachment ) {
if ( 1 == ++ $i ) {
$size = apply_filters( 'aioseop_attachment_size', 'large' );
$image = wp_get_attachment_image_src( $id, $size );
$alt = trim( strip_tags( get_post_field( 'post_excerpt', $id ) ) );
break;
}
}
/* Return the image URL. */
return $image[0];
}
/**
* Get the Image by Scan
*
* @since ?
*
* @param null $p
* @return bool
*/
function get_the_image_by_scan( $p = null ) {
if ( null === $p ) {
global $post;
} else {
$post = $p;
}
/* Search the post's content for the tag and get its URL. */
preg_match_all( '||i', get_post_field( 'post_content', $post->ID ), $matches );
/* If there is a match for the image, return its URL. */
if ( isset( $matches ) && ! empty( $matches[1][0] ) ) {
return $matches[1][0];
}
return false;
}
/**
* Load scripts and styles for metaboxes.
* edit-tags exists only for pre 4.5 support... remove when we drop 4.5 support.
* Also, that check and others should be pulled out into their own functions.
*
* @todo is it possible to migrate this to \All_in_One_SEO_Pack_Module::add_page_hooks? Or refactor? Both function about the same.
*
* @since 2.4.14 Added term as screen base.
*/
function enqueue_metabox_scripts() {
$screen = '';
if ( function_exists( 'get_current_screen' ) ) {
$screen = get_current_screen();
}
$bail = false;
if ( empty( $screen ) ) {
$bail = true;
}
if ( true != $bail ) {
if ( ( 'post' != $screen->base ) && ( 'term' != $screen->base ) && ( 'edit-tags' != $screen->base ) && ( 'toplevel_page_shopp-products' != $screen->base ) ) {
$bail = true;
}
}
$prefix = $this->get_prefix();
$bail = apply_filters( $prefix . 'bail_on_enqueue', $bail, $screen );
if ( $bail ) {
return;
}
$this->form = 'post';
if ( 'term' == $screen->base || 'edit-tags' == $screen->base ) {
$this->form = 'edittag';
}
if ( 'toplevel_page_shopp-products' == $screen->base ) {
$this->form = 'product';
}
$this->form = apply_filters( $prefix . 'set_form_on_enqueue', $this->form, $screen );
foreach ( $this->locations as $k => $v ) {
if ( 'metabox' === $v['type'] && isset( $v['display'] ) && ! empty( $v['display'] ) ) {
$enqueue_scripts = false;
$enqueue_scripts =
(
( 'toplevel_page_shopp-products' == $screen->base ) &&
in_array( 'shopp_product', $v['display'] )
) ||
in_array( $screen->post_type, $v['display'] ) ||
'edit-category' == $screen->base ||
'edit-post_tag' == $screen->base ||
'term' == $screen->base;
$enqueue_scripts = apply_filters( $prefix . 'enqueue_metabox_scripts', $enqueue_scripts, $screen, $v );
if ( $enqueue_scripts ) {
add_filter( 'aioseop_localize_script_data', array( $this, 'localize_script_data' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ), 20 );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_styles' ), 20 );
}
}
}
}
/**
* Load styles for module.
*
* Add hook in \All_in_One_SEO_Pack_Module::enqueue_metabox_scripts - Bails adding hook if not on target valid screen.
* Add hook in \All_in_One_SEO_Pack_Module::add_page_hooks - Function itself is hooked based on the screen_id/page.
*
* @since 2.9
* @since 3.0 Added jQuery UI CSS missing from WP. #1850
*
* @see 'admin_enqueue_scripts' hook
* @link https://developer.wordpress.org/reference/hooks/admin_enqueue_scripts/
* @uses wp_scripts() Gets the Instance of WP Scripts.
* @link https://developer.wordpress.org/reference/functions/wp_scripts/
*
* @param string $hook_suffix
*/
function admin_enqueue_styles( $hook_suffix ) {
wp_enqueue_style( 'thickbox' );
if ( ! empty( $this->pointers ) ) {
wp_enqueue_style( 'wp-pointer' );
}
wp_enqueue_style( 'aioseop-module-style', AIOSEOP_PLUGIN_URL . 'css/modules/aioseop_module.css', array(), AIOSEOP_VERSION );
if ( function_exists( 'is_rtl' ) && is_rtl() ) {
wp_enqueue_style( 'aioseop-module-style-rtl', AIOSEOP_PLUGIN_URL . 'css/modules/aioseop_module-rtl.css', array( 'aioseop-module-style' ), AIOSEOP_VERSION );
}
if ( ! wp_style_is( 'aioseop-jquery-ui', 'registered' ) && ! wp_style_is( 'aioseop-jquery-ui', 'enqueued' ) ) {
wp_enqueue_style(
'aioseop-jquery-ui',
AIOSEOP_PLUGIN_URL . 'css/aioseop-jquery-ui.css',
array(),
AIOSEOP_VERSION
);
}
}
/**
* Admin Enqueue Scripts
*
* Hook function to enqueue scripts and localize data to scripts.
*
* Add hook in \All_in_One_SEO_Pack_Module::enqueue_metabox_scripts - Bails adding hook if not on target valid screen.
* Add hook in \All_in_One_SEO_Pack_Module::add_page_hooks - Function itself is hooked based on the screen_id/page.
*
* @since ?
* @since 2.3.12.3 Add missing wp_enqueue_media.
* @since 2.9 Switch to admin_enqueue_scripts; both the hook and function name.
* @since 3.0 Add enqueue footer JS for jQuery UI Compatibility. #1850
*
* @see 'admin_enqueue_scripts' hook
* @link https://developer.wordpress.org/reference/hooks/admin_enqueue_scripts/
* @global WP_Post $post Used to set the post ID in wp_enqueue_media().
*
* @param string $hook_suffix
*/
public function admin_enqueue_scripts( $hook_suffix ) {
wp_enqueue_script( 'sack' );
wp_enqueue_script( 'jquery' );
wp_enqueue_script( 'jquery-ui-tabs' );
wp_enqueue_script( 'media-upload' );
wp_enqueue_script( 'thickbox' );
wp_enqueue_script( 'common' );
wp_enqueue_script( 'wp-lists' );
wp_enqueue_script( 'postbox' );
if ( ! empty( $this->pointers ) ) {
wp_enqueue_script(
'wp-pointer',
false,
array( 'jquery' )
);
}
global $post;
if ( ! empty( $post->ID ) ) {
wp_enqueue_media( array( 'post' => $post->ID ) );
} else {
wp_enqueue_media();
}
$helper_dep = array(
'jquery',
'jquery-ui-core',
'jquery-ui-widget',
'jquery-ui-position',
'jquery-ui-tooltip',
);
// AIOSEOP Script enqueue.
wp_enqueue_script(
'aioseop-module-script',
AIOSEOP_PLUGIN_URL . 'js/modules/aioseop_module.js',
array(),
AIOSEOP_VERSION
);
wp_enqueue_script(
'aioseop-helper-js',
AIOSEOP_PLUGIN_URL . 'js/aioseop-helper.js',
$helper_dep,
AIOSEOP_VERSION,
true
);
// Localize aiosp_data in JS.
if ( ! empty( $this->script_data ) ) {
aioseop_localize_script_data();
}
}
/**
* Localize Script Data
*
* @since ?
*
* @param $data
* @return array
*/
function localize_script_data( $data ) {
if ( ! is_array( $data ) ) {
$data = array( 0 => $data );
}
if ( empty( $this->script_data ) ) {
$this->script_data = array();
}
if ( ! empty( $this->pointers ) ) {
$this->script_data['pointers'] = $this->pointers;
}
if ( empty( $data[0]['condshow'] ) ) {
$data[0]['condshow'] = array();
}
if ( empty( $this->script_data['condshow'] ) ) {
$this->script_data['condshow'] = array();
}
$condshow = $this->script_data['condshow'];
$data[0]['condshow'] = array_merge( $data[0]['condshow'], $condshow );
unset( $this->script_data['condshow'] );
$data[0] = array_merge( $this->script_data, $data[0] );
$this->script_data['condshow'] = $condshow;
return $data;
}
/**
* Override this to run code at the beginning of the settings page.
*/
function settings_page_init() {
}
/**
* Filter out admin pointers that have already been clicked.
*/
function filter_pointers() {
if ( ! empty( $this->pointers ) ) {
$dismissed = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
foreach ( $dismissed as $d ) {
if ( isset( $this->pointers[ $d ] ) ) {
unset( $this->pointers[ $d ] );
}
}
}
}
/**
* Add basic hooks when on the module's page.
*/
function add_page_hooks() {
$hookname = current_filter();
if ( $this->strpos( $hookname, 'load-' ) === 0 ) {
$this->pagehook = $this->substr( $hookname, 5 );
}
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_styles' ) );
add_filter( 'aioseop_localize_script_data', array( $this, 'localize_script_data' ) );
add_action( $this->prefix . 'settings_header', array( $this, 'display_tabs' ) );
}
/**
* Get Admin Links
*
* @since ?
*
* @return array
*/
function get_admin_links() {
if ( ! empty( $this->menu_name ) ) {
$name = $this->menu_name;
} else {
$name = $this->name;
}
$hookname = plugin_basename( $this->file );
$links = array();
$url = '';
if ( function_exists( 'menu_page_url' ) ) {
$url = menu_page_url( $hookname, 0 );
}
if ( empty( $url ) ) {
$url = esc_url( admin_url( 'admin.php?page=' . $hookname ) );
}
if ( null === $this->locations ) {
array_unshift(
$links,
array(
'parent' => AIOSEOP_PLUGIN_DIRNAME,
'title' => $name,
'id' => $hookname,
'href' => $url,
'order' => $this->menu_order(),
)
);
} else {
foreach ( $this->locations as $k => $v ) {
if ( 'settings' === $v['type'] ) {
if ( 'default' === $k ) {
array_unshift(
$links,
array(
'parent' => AIOSEOP_PLUGIN_DIRNAME,
'title' => $name,
'id' => $hookname,
'href' => $url,
'order' => $this->menu_order(),
)
);
} else {
if ( ! empty( $v['menu_name'] ) ) {
$name = $v['menu_name'];
} else {
$name = $v['name'];
}
array_unshift(
$links,
array(
'parent' => AIOSEOP_PLUGIN_DIRNAME,
'title' => $name,
'id' => $this->get_prefix( $k ) . $k,
'href' => esc_url( admin_url( 'admin.php?page=' . $this->get_prefix( $k ) . $k ) ),
'order' => $this->menu_order(),
)
);
}
}
}
}
return $links;
}
function add_admin_bar_submenu() {
global $aioseop_admin_menu, $wp_admin_bar;
if ( $aioseop_admin_menu ) {
$links = $this->get_admin_links();
if ( ! empty( $links ) ) {
foreach ( $links as $l ) {
$wp_admin_bar->add_menu( $l );
}
}
}
}
/**
* Collect metabox data together for tabbed metaboxes.
*
* @param $args
*
* @return array
*/
function filter_return_metaboxes( $args ) {
return array_merge( $args, $this->post_metaboxes );
}
/** Add submenu for module, call page hooks, set up metaboxes.
*
* @param $parent_slug
*
* @return bool
*/
function add_menu( $parent_slug ) {
if ( ! empty( $this->menu_name ) ) {
$name = $this->menu_name;
} else {
$name = $this->name;
}
if ( null === $this->locations ) {
$hookname = add_submenu_page(
$parent_slug,
$name,
$name,
apply_filters( 'manage_aiosp', 'aiosp_manage_seo' ),
plugin_basename( $this->file ),
array(
$this,
'display_settings_page',
)
);
add_action( "load-{$hookname}", array( $this, 'add_page_hooks' ) );
return true;
}
foreach ( $this->locations as $k => $v ) {
if ( 'settings' === $v['type'] ) {
if ( 'default' === $k ) {
if ( ! empty( $this->menu_name ) ) {
$name = $this->menu_name;
} else {
$name = $this->name;
}
$hookname = add_submenu_page(
$parent_slug,
$name,
$name,
apply_filters( 'manage_aiosp', 'aiosp_manage_seo' ),
plugin_basename( $this->file ),
array(
$this,
'display_settings_page',
)
);
} else {
if ( ! empty( $v['menu_name'] ) ) {
$name = $v['menu_name'];
} else {
$name = $v['name'];
}
$hookname = add_submenu_page(
$parent_slug,
$name,
$name,
apply_filters( 'manage_aiosp', 'aiosp_manage_seo' ),
$this->get_prefix( $k ) . $k,
array(
$this,
"display_settings_page_$k",
)
);
}
add_action( "load-{$hookname}", array( $this, 'add_page_hooks' ) );
} elseif ( 'metabox' === $v['type'] ) {
// hack -- make sure this runs anyhow, for now -- pdb.
$this->setting_options( $k );
$this->toggle_save_post_hooks( true );
if ( isset( $v['display'] ) && ! empty( $v['display'] ) ) {
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_metabox_scripts' ), 5 );
if ( $this->tabbed_metaboxes ) {
add_filter( 'aioseop_add_post_metabox', array( $this, 'filter_return_metaboxes' ) );
}
foreach ( $v['display'] as $posttype ) {
$v['location'] = $k;
$v['posttype'] = $posttype;
if ( post_type_exists( $posttype ) ) {
// Metabox priority/context on edit post screen.
$v['context'] = apply_filters( 'aioseop_post_metabox_context', 'normal' );
$v['priority'] = apply_filters( 'aioseop_post_metabox_priority', 'high' );
}
if ( false !== strpos( $posttype, 'edit-' ) ) {
// Metabox priority/context on edit taxonomy screen.
$v['context'] = 'advanced';
$v['priority'] = 'default';
}
// Metabox priority for everything else.
if ( ! isset( $v['context'] ) ) {
$v['context'] = 'advanced';
}
if ( ! isset( $v['priority'] ) ) {
$v['priority'] = 'default';
}
if ( $this->tabbed_metaboxes ) {
$this->post_metaboxes[] = array(
'id' => $v['prefix'] . $k,
'title' => $v['name'],
'callback' => array( $this, 'display_metabox' ),
'post_type' => $posttype,
'context' => $v['context'],
'priority' => $v['priority'],
'callback_args' => $v,
);
} else {
$title = $v['name'];
if ( $title != $this->plugin_name ) {
$title = $this->plugin_name . ' - ' . $title;
}
if ( ! empty( $v['help_link'] ) ) {
$title .= "" .
/* translators: This string is used as an action link which users can click on to view the relevant documentation on our website. */
__( 'Help', 'all-in-one-seo-pack' ) . '';
}
add_meta_box(
$v['prefix'] . $k,
$title,
array(
$this,
'display_metabox',
),
$posttype,
$v['context'],
$v['priority'],
$v
);
}
}
}
}
}
}
/**
* Adds or removes hooks that could be called while editing a post.
*
* TODO: Review if all these hooks are really required (save_post should be enough vs. edit_post and publish_post).
*/
private function toggle_save_post_hooks( $add ) {
if ( $add ) {
add_action( 'edit_post', array( $this, 'save_post_data' ) );
add_action( 'publish_post', array( $this, 'save_post_data' ) );
add_action( 'add_attachment', array( $this, 'save_post_data' ) );
add_action( 'edit_attachment', array( $this, 'save_post_data' ) );
add_action( 'save_post', array( $this, 'save_post_data' ) );
add_action( 'edit_page_form', array( $this, 'save_post_data' ) );
} else {
remove_action( 'edit_post', array( $this, 'save_post_data' ) );
remove_action( 'publish_post', array( $this, 'save_post_data' ) );
remove_action( 'add_attachment', array( $this, 'save_post_data' ) );
remove_action( 'edit_attachment', array( $this, 'save_post_data' ) );
remove_action( 'save_post', array( $this, 'save_post_data' ) );
remove_action( 'edit_page_form', array( $this, 'save_post_data' ) );
}
}
/**
* Update postmeta for metabox.
*
* @param $post_id
*/
function save_post_data( $post_id ) {
$this->toggle_save_post_hooks( false );
if ( null !== $this->locations ) {
foreach ( $this->locations as $k => $v ) {
if ( isset( $v['type'] ) && ( 'metabox' === $v['type'] ) ) {
$opts = $this->default_options( $k );
$options = array();
foreach ( $opts as $l => $o ) {
if ( isset( $_POST[ $l ] ) ) {
$options[ $l ] = stripslashes_deep( $_POST[ $l ] );
$options[ $l ] = esc_attr( $options[ $l ] );
}
}
$prefix = $this->get_prefix( $k );
$options = apply_filters( $prefix . 'filter_metabox_options', $options, $k, $post_id );
update_post_meta( $post_id, '_' . $prefix . $k, $options );
}
}
}
$this->toggle_save_post_hooks( true );
}
/**
* Outputs radio buttons, checkboxes, selects, multiselects, handles groups.
*
* @param $args
*
* @return string
*/
function do_multi_input( $args ) {
$options = $args['options'];
$value = $args['value'];
$name = $args['name'];
$attr = $args['attr'];
$buf1 = '';
$type = $options['type'];
$strings = array(
'block' => "\n",
'group' => "\t\n",
'item' => "\t\n",
'item_args' => array( 'sel', 'v', 'subopt' ),
'selected' => 'selected ',
);
if ( ( 'radio' === $type ) || ( 'checkbox' === $type ) ) {
$strings = array(
'block' => "%s\n",
'group' => "\t%s \n%s\n",
'item' => "\t\n",
'item_args' => array( 'sel', 'name', 'v', 'attr', 'subopt' ),
'selected' => 'checked ',
);
}
$setsel = $strings['selected'];
if ( isset( $options['initial_options'] ) && is_array( $options['initial_options'] ) ) {
foreach ( $options['initial_options'] as $l => $option ) {
$option_check = strip_tags( is_array( $option ) ? implode( ' ', $option ) : $option );
if ( empty( $l ) && empty( $option_check ) ) {
continue;
}
$is_group = is_array( $option );
if ( ! $is_group ) {
$option = array( $l => $option );
}
$buf2 = '';
foreach ( $option as $v => $subopt ) {
$sel = '';
$is_arr = is_array( $value );
if ( is_string( $v ) || is_string( $value ) ) {
if ( is_string( $value ) ) {
$cmp = ! strcmp( $v, $value );
} else {
$cmp = ! strcmp( $v, '' );
}
// $cmp = !strcmp( (string)$v, (string)$value );
} else {
$cmp = ( $value == $v );
}
if ( ( ! $is_arr && $cmp ) || ( $is_arr && in_array( $v, $value ) ) ) {
$sel = $setsel;
}
$item_arr = array();
foreach ( $strings['item_args'] as $arg ) {
$item_arr[] = $$arg;
}
$buf2 .= vsprintf( $strings['item'], $item_arr );
}
if ( $is_group ) {
$buf1 .= sprintf( $strings['group'], $l, $buf2 );
} else {
$buf1 .= $buf2;
}
}
$buf1 = sprintf( $strings['block'], $buf1 );
}
return $buf1;
}
/**
* Get Option HTML
*
* Outputs a setting item for settings pages and metaboxes.
*
* @since ?
* @since 2.12 Add 'input' to allowed tags with 'html'. #2157
*
* @param array $args {
* Contains the admin option element values and attributes for rendering.
*
* @type string $attr The HTML element's attributes to render within the element.
* @type string $name THE HTML element's name attribute. Used with form input elements.
* @type string $prefix Optional. The AIOSEOP Module prefix.
* @type string $value The HTML element's value attribute.
* @type array $options {
* Arguments used for this function/method operations and rendering.
*
* @type string $class Optional. The HTML element's class attribute. This is used if
* `$options['count']` is not empty.
* @type int $cols Optional. Character count length of column.
* @type boolean $count Optional. Determines whether to add the character count for SEO.
* @type string $count_desc Optional. The description/help text to rend to the admin.
* @type string $name Optional. Used within the description/help text when it's for character count.
* @type boolean $required Optional. Determines whether to require a value in the input element.
* @type int $rows Optional. Number of rows to multiply with cols.
* @type string $type Which Switch Case (HTML element) to use.
* }
* }
* @return string
*/
function get_option_html( $args ) {
static $n = 0;
$options = $args['options'];
$value = $args['value'];
$name = $args['name'];
$attr = $args['attr'];
$prefix = isset( $args['prefix'] ) ? $args['prefix'] : '';
if ( 'custom' == $options['type'] ) {
return apply_filters( "{$prefix}output_option", '', $args );
}
if ( in_array(
$options['type'],
array(
'multiselect',
'select',
'multicheckbox',
'radio',
'checkbox',
'textarea',
'text',
'submit',
'hidden',
'date',
)
) && is_string( $value )
) {
$value = esc_attr( $value );
}
$buf = '';
$onload = '';
if ( ! empty( $options['count'] ) ) {
$n ++;
$classes = isset( $options['class'] ) ? $options['class'] : '';
$classes .= ' aioseop_count_chars';
$attr .= " class='{$classes}' data-length-field='{$prefix}length$n'";
}
if ( isset( $opts['id'] ) ) {
$attr .= " id=\"{$opts['id']}\" ";
}
if ( isset( $options['required'] ) && true === $options['required'] ) {
$attr .= ' required';
}
switch ( $options['type'] ) {
case 'multiselect':
$attr .= ' MULTIPLE';
$args['attr'] = $attr;
$name = "{$name}[]";
$args['name'] = $name;
// fall through.
case 'select':
$buf .= $this->do_multi_input( $args );
break;
case 'multicheckbox':
$name = "{$name}[]";
$args['name'] = $name;
$args['options']['type'] = 'checkbox';
$options['type'] = 'checkbox';
// fall through.
case 'radio':
$buf .= $this->do_multi_input( $args );
break;
case 'checkbox':
if ( $value ) {
$attr .= ' CHECKED';
}
$buf .= "\n";
break;
case 'textarea':
// #1363: prevent characters like ampersand in title and description (in social meta module) from getting changed to &
if ( in_array( $name, array( 'aiosp_opengraph_hometitle', 'aiosp_opengraph_description' ), true ) ) {
$value = htmlspecialchars_decode( $value, ENT_QUOTES );
}
$buf .= "";
break;
case 'image':
$buf .= '' .
"\n";
break;
case 'html':
$allowed_tags = wp_kses_allowed_html( 'post' );
$allowed_tags['input'] = array(
'name' => true,
'type' => true,
'value' => true,
'class' => true,
'placeholder' => true,
);
$buf .= wp_kses( $value, $allowed_tags );
break;
case 'esc_html':
$buf .= '
' . esc_html( $value ) . "
\n";
break;
case 'date':
// firefox and IE < 11 do not have support for HTML5 date, so we will fall back to the datepicker.
wp_enqueue_script( 'jquery-ui-datepicker' );
// fall through.
default:
$buf .= "\n";
}
// TODO Maybe Change/Add a function for SEO character count.
if ( ! empty( $options['count'] ) ) {
$size = 60;
if ( isset( $options['size'] ) ) {
$size = $options['size'];
} elseif ( isset( $options['rows'] ) && isset( $options['cols'] ) ) {
$size = $options['rows'] * $options['cols'];
}
if ( isset( $options['count_desc'] ) ) {
$count_desc = $options['count_desc'];
} else {
/* translators: %1$s and %2$s are placeholders and should not be translated. %1$s is replaced with a number, %2$s is replaced with the name of an meta tag field (e.g; "Title", "Description", etc.). */
$count_desc = __( ' characters. Most search engines use a maximum of %1$s chars for the %2$s.', 'all-in-one-seo-pack' );
}
$buf .= " "
. sprintf( $count_desc, $size, trim( $this->strtolower( $options['name'] ), ':' ) );
if ( ! empty( $onload ) ) {
$buf .= "";
}
}
return $buf;
}
/**
* Format a row for an option on a settings page.
*
* @since ?
* @since 3.0 Added Helper Class for jQuery Tooltips. #1850
*
* @param $name
* @param $opts
* @param $args
*
* @return string
*/
function get_option_row( $name, $opts, $args ) {
$label_text = '';
$input_attr = '';
$id_attr = '';
require_once( AIOSEOP_PLUGIN_DIR . 'admin/class-aioseop-helper.php' );
$info = new AIOSEOP_Helper( get_class( $this ) );
$align = 'right';
if ( 'top' == $opts['label'] ) {
$align = 'left';
}
if ( isset( $opts['id'] ) ) {
$id_attr .= " id=\"{$opts['id']}_div\" ";
}
if ( 'none' != $opts['label'] ) {
$tmp_help_text = $info->get_help_text( $name );
if ( isset( $tmp_help_text ) && ! empty( $tmp_help_text ) ) {
$display_help = '';
$help_text = sprintf( $display_help, $info->get_help_text( $name ), $opts['name'] );
} else {
$help_text = $opts['name'];
}
// TODO Possible remove text align.
// Currently aligns to the right when everything is being aligned to the left; which is usually a workaround.
$display_label_format = '%s';
$label_text = sprintf( $display_label_format, $align, $help_text );
} else {
$input_attr .= ' aioseop_no_label ';
}
if ( 'top' == $opts['label'] ) {
$label_text .= "