2005 lines
49 KiB
PHP
2005 lines
49 KiB
PHP
<?php if ( ! defined( 'FW' ) ) {
|
|
die( 'Forbidden' );
|
|
}
|
|
// Useful functions
|
|
|
|
/**
|
|
* Convert to Unix style directory separators
|
|
*/
|
|
function fw_fix_path( $path ) {
|
|
$windows_network_path = isset( $_SERVER['windir'] ) && in_array( substr( $path, 0, 2 ),
|
|
array( '//', '\\\\' ),
|
|
true );
|
|
$fixed_path = untrailingslashit( str_replace( array( '//', '\\' ), array( '/', '/' ), $path ) );
|
|
|
|
if ( empty( $fixed_path ) && ! empty( $path ) ) {
|
|
$fixed_path = '/';
|
|
}
|
|
|
|
if ( $windows_network_path ) {
|
|
$fixed_path = '//' . ltrim( $fixed_path, '/' );
|
|
}
|
|
|
|
return $fixed_path;
|
|
}
|
|
|
|
/**
|
|
* Relative path of the framework customizations directory
|
|
*
|
|
* @param string $append
|
|
*
|
|
* @return string
|
|
*/
|
|
function fw_get_framework_customizations_dir_rel_path( $append = '' ) {
|
|
try {
|
|
$dir = FW_Cache::get( $cache_key = 'fw_customizations_dir_rel_path' );
|
|
} catch ( FW_Cache_Not_Found_Exception $e ) {
|
|
FW_Cache::set(
|
|
$cache_key,
|
|
$dir = apply_filters( 'fw_framework_customizations_dir_rel_path', '/framework-customizations' )
|
|
);
|
|
}
|
|
|
|
return $dir . $append;
|
|
}
|
|
|
|
/** Child theme related functions */
|
|
{
|
|
/**
|
|
* Full path to the child-theme framework customizations directory
|
|
*
|
|
* @param string $rel_path
|
|
*
|
|
* @return null|string
|
|
*/
|
|
function fw_get_stylesheet_customizations_directory( $rel_path = '' ) {
|
|
if ( is_child_theme() ) {
|
|
return get_stylesheet_directory() . fw_get_framework_customizations_dir_rel_path( $rel_path );
|
|
} else {
|
|
// check is_child_theme() before using this function
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* URI to the child-theme framework customizations directory
|
|
*
|
|
* @param string $rel_path
|
|
*
|
|
* @return null|string
|
|
*/
|
|
function fw_get_stylesheet_customizations_directory_uri( $rel_path = '' ) {
|
|
if ( is_child_theme() ) {
|
|
return get_stylesheet_directory_uri() . fw_get_framework_customizations_dir_rel_path( $rel_path );
|
|
} else {
|
|
// check is_child_theme() before using this function
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Parent theme related functions */
|
|
{
|
|
/**
|
|
* Full path to the parent-theme framework customizations directory
|
|
*
|
|
* @param string $rel_path
|
|
*
|
|
* @return string
|
|
*/
|
|
function fw_get_template_customizations_directory( $rel_path = '' ) {
|
|
try {
|
|
$dir = FW_Cache::get( $cache_key = 'fw_template_customizations_dir' );
|
|
} catch ( FW_Cache_Not_Found_Exception $e ) {
|
|
FW_Cache::set(
|
|
$cache_key,
|
|
$dir = get_template_directory() . fw_get_framework_customizations_dir_rel_path()
|
|
);
|
|
}
|
|
|
|
return $dir . $rel_path;
|
|
}
|
|
|
|
/**
|
|
* URI to the parent-theme framework customizations directory
|
|
*
|
|
* @param string $rel_path
|
|
*
|
|
* @return string
|
|
*/
|
|
function fw_get_template_customizations_directory_uri( $rel_path = '' ) {
|
|
try {
|
|
$dir = FW_Cache::get( $cache_key = 'fw_template_customizations_dir_uri' );
|
|
} catch ( FW_Cache_Not_Found_Exception $e ) {
|
|
FW_Cache::set(
|
|
$cache_key,
|
|
$dir = get_template_directory_uri() . fw_get_framework_customizations_dir_rel_path()
|
|
);
|
|
}
|
|
|
|
return $dir . $rel_path;
|
|
}
|
|
}
|
|
|
|
/** Framework related functions */
|
|
{
|
|
/**
|
|
* Full path to the parent-theme/framework directory
|
|
*
|
|
* @param string $rel_path
|
|
*
|
|
* @return string
|
|
*/
|
|
function fw_get_framework_directory( $rel_path = '' ) {
|
|
try {
|
|
$dir = FW_Cache::get( $cache_key = 'fw_framework_dir' );
|
|
} catch ( FW_Cache_Not_Found_Exception $e ) {
|
|
FW_Cache::set(
|
|
$cache_key,
|
|
$dir = apply_filters(
|
|
'fw_framework_directory',
|
|
fw_fix_path( dirname( dirname( __FILE__ ) ) ) // double dirname() to remove '/helpers', use parent dir
|
|
)
|
|
);
|
|
}
|
|
|
|
return $dir . $rel_path;
|
|
}
|
|
|
|
/**
|
|
* URI to the parent-theme/framework directory
|
|
*
|
|
* @param string $rel_path
|
|
*
|
|
* @return string
|
|
*/
|
|
function fw_get_framework_directory_uri( $rel_path = '' ) {
|
|
try {
|
|
$uri = FW_Cache::get( $cache_key = 'fw_framework_dir_uri' );
|
|
} catch ( FW_Cache_Not_Found_Exception $e ) {
|
|
FW_Cache::set(
|
|
$cache_key,
|
|
$uri = apply_filters(
|
|
'fw_framework_directory_uri',
|
|
( $uri = fw_get_path_url( fw_get_framework_directory() ) )
|
|
? $uri
|
|
: get_template_directory_uri() . '/framework'
|
|
)
|
|
);
|
|
}
|
|
|
|
return $uri . $rel_path;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Recursively find a key's value in array
|
|
*
|
|
* @param string $keys 'a/b/c'
|
|
* @param array|object $array_or_object
|
|
* @param null|mixed $default_value
|
|
* @param string $keys_delimiter
|
|
*
|
|
* @return null|mixed
|
|
*/
|
|
function fw_akg( $keys, $array_or_object, $default_value = null, $keys_delimiter = '/' ) {
|
|
if ( ! is_array( $keys ) ) {
|
|
$keys = explode( $keys_delimiter, (string) $keys );
|
|
}
|
|
|
|
$array_or_object = fw_call( $array_or_object );
|
|
|
|
$key_or_property = array_shift( $keys );
|
|
if ( $key_or_property === null ) {
|
|
return fw_call( $default_value );
|
|
}
|
|
|
|
$is_object = is_object( $array_or_object );
|
|
|
|
if ( $is_object ) {
|
|
if ( ! property_exists( $array_or_object, $key_or_property ) ) {
|
|
return fw_call( $default_value );
|
|
}
|
|
} else {
|
|
if ( ! is_array( $array_or_object ) || ! array_key_exists( $key_or_property, $array_or_object ) ) {
|
|
return fw_call( $default_value );
|
|
}
|
|
}
|
|
|
|
if ( isset( $keys[0] ) ) { // not used count() for performance reasons
|
|
if ( $is_object ) {
|
|
return fw_akg( $keys, $array_or_object->{$key_or_property}, $default_value );
|
|
} else {
|
|
return fw_akg( $keys, $array_or_object[ $key_or_property ], $default_value );
|
|
}
|
|
} else {
|
|
if ( $is_object ) {
|
|
return $array_or_object->{$key_or_property};
|
|
} else {
|
|
return $array_or_object[ $key_or_property ];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set (or create if not exists) value for specified key in some array level
|
|
*
|
|
* @param string $keys 'a/b/c', or 'a/b/c/' equivalent to: $arr['a']['b']['c'][] = $val;
|
|
* @param mixed $value
|
|
* @param array|object $array_or_object
|
|
* @param string $keys_delimiter
|
|
*
|
|
* @return array|object
|
|
*/
|
|
function fw_aks( $keys, $value, &$array_or_object, $keys_delimiter = '/' ) {
|
|
if ( ! is_array( $keys ) ) {
|
|
$keys = explode( $keys_delimiter, (string) $keys );
|
|
}
|
|
|
|
$key_or_property = array_shift( $keys );
|
|
if ( $key_or_property === null ) {
|
|
return $array_or_object;
|
|
}
|
|
|
|
$is_object = is_object( $array_or_object );
|
|
|
|
if ( $is_object ) {
|
|
if ( ! property_exists( $array_or_object, $key_or_property )
|
|
|| ! ( is_array( $array_or_object->{$key_or_property} ) || is_object( $array_or_object->{$key_or_property} ) )
|
|
) {
|
|
if ( $key_or_property === '' ) {
|
|
// this happens when use 'empty keys' like: abc/d/e////i/j//foo/
|
|
trigger_error( 'Cannot push value to object like in array ($arr[] = $val)', E_USER_WARNING );
|
|
} else {
|
|
$array_or_object->{$key_or_property} = array();
|
|
}
|
|
}
|
|
} else {
|
|
if ( ! is_array( $array_or_object ) ) {
|
|
$array_or_object = array();
|
|
}
|
|
|
|
if ( ! array_key_exists( $key_or_property,
|
|
$array_or_object ) || ! is_array( $array_or_object[ $key_or_property ] )
|
|
) {
|
|
if ( $key_or_property === '' ) {
|
|
// this happens when use 'empty keys' like: abc.d.e....i.j..foo.
|
|
$array_or_object[] = array();
|
|
|
|
// get auto created key (last)
|
|
end( $array_or_object );
|
|
$key_or_property = key( $array_or_object );
|
|
} else {
|
|
$array_or_object[ $key_or_property ] = array();
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( isset( $keys[0] ) ) { // not used count() for performance reasons
|
|
if ( $is_object ) {
|
|
fw_aks( $keys, $value, $array_or_object->{$key_or_property} );
|
|
} else {
|
|
fw_aks( $keys, $value, $array_or_object[ $key_or_property ] );
|
|
}
|
|
} else {
|
|
if ( $is_object ) {
|
|
$array_or_object->{$key_or_property} = $value;
|
|
} else {
|
|
$array_or_object[ $key_or_property ] = $value;
|
|
}
|
|
}
|
|
|
|
return $array_or_object;
|
|
}
|
|
|
|
/**
|
|
* Unset specified key in some array level
|
|
*
|
|
* @param string $keys 'a/b/c' -> unset($arr['a']['b']['c']);
|
|
* @param array|object $array_or_object
|
|
* @param string $keys_delimiter
|
|
*
|
|
* @return array|object
|
|
*/
|
|
function fw_aku( $keys, &$array_or_object, $keys_delimiter = '/' ) {
|
|
if ( ! is_array( $keys ) ) {
|
|
$keys = explode( $keys_delimiter, (string) $keys );
|
|
}
|
|
|
|
$key_or_property = array_shift( $keys );
|
|
if ( $key_or_property === null || $key_or_property === '' ) {
|
|
return $array_or_object;
|
|
}
|
|
|
|
$is_object = is_object( $array_or_object );
|
|
|
|
if ( $is_object ) {
|
|
if ( ! property_exists( $array_or_object, $key_or_property ) ) {
|
|
return $array_or_object;
|
|
}
|
|
} else {
|
|
if ( ! is_array( $array_or_object ) || ! array_key_exists( $key_or_property, $array_or_object ) ) {
|
|
return $array_or_object;
|
|
}
|
|
}
|
|
|
|
if ( isset( $keys[0] ) ) { // not used count() for performance reasons
|
|
if ( $is_object ) {
|
|
fw_aku( $keys, $array_or_object->{$key_or_property} );
|
|
} else {
|
|
fw_aku( $keys, $array_or_object[ $key_or_property ] );
|
|
}
|
|
} else {
|
|
if ( $is_object ) {
|
|
unset( $array_or_object->{$key_or_property} );
|
|
} else {
|
|
unset( $array_or_object[ $key_or_property ] );
|
|
}
|
|
}
|
|
|
|
return $array_or_object;
|
|
}
|
|
|
|
/**
|
|
* Generate random unique md5
|
|
*/
|
|
function fw_rand_md5() {
|
|
return md5( time() . '-' . uniqid( rand(), true ) . '-' . mt_rand( 1, 1000 ) );
|
|
}
|
|
|
|
function fw_unique_increment() {
|
|
static $i = 0;
|
|
|
|
return ++ $i;
|
|
}
|
|
|
|
/**
|
|
* print_r() alternative
|
|
*
|
|
* @param mixed $value Value to debug
|
|
*/
|
|
function fw_print( $value ) {
|
|
static $first_time = true;
|
|
|
|
if ( $first_time ) {
|
|
ob_start();
|
|
echo '<style type="text/css">
|
|
div.fw_print_r {
|
|
max-height: 500px;
|
|
overflow-y: scroll;
|
|
background: #23282d;
|
|
margin: 10px 30px;
|
|
padding: 0;
|
|
border: 1px solid #F5F5F5;
|
|
border-radius: 3px;
|
|
position: relative;
|
|
z-index: 11111;
|
|
}
|
|
|
|
div.fw_print_r pre {
|
|
color: #78FF5B;
|
|
background: #23282d;
|
|
text-shadow: 1px 1px 0 #000;
|
|
font-family: Consolas, monospace;
|
|
font-size: 12px;
|
|
margin: 0;
|
|
padding: 5px;
|
|
display: block;
|
|
line-height: 16px;
|
|
text-align: left;
|
|
}
|
|
|
|
div.fw_print_r_group {
|
|
background: #f1f1f1;
|
|
margin: 10px 30px;
|
|
padding: 1px;
|
|
border-radius: 5px;
|
|
position: relative;
|
|
z-index: 11110;
|
|
}
|
|
div.fw_print_r_group div.fw_print_r {
|
|
margin: 9px;
|
|
border-width: 0;
|
|
}
|
|
</style>';
|
|
echo str_replace( array( ' ', "\n" ), '', ob_get_clean() );
|
|
|
|
$first_time = false;
|
|
}
|
|
|
|
if ( func_num_args() == 1 ) {
|
|
echo '<div class="fw_print_r"><pre>';
|
|
echo fw_htmlspecialchars( FW_Dumper::dump( $value ) );
|
|
echo '</pre></div>';
|
|
} else {
|
|
echo '<div class="fw_print_r_group">';
|
|
foreach ( func_get_args() as $param ) {
|
|
fw_print( $param );
|
|
}
|
|
echo '</div>';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Alias for fw_print
|
|
*
|
|
* @see fw_print()
|
|
*/
|
|
if ( ! function_exists( 'debug' ) ) {
|
|
function debug() {
|
|
call_user_func_array( 'fw_print', func_get_args() );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate html tag
|
|
*
|
|
* @param string $tag Tag name
|
|
* @param array $attr Tag attributes
|
|
* @param bool|string $end Append closing tag. Also accepts body content
|
|
*
|
|
* @return string The tag's html
|
|
*/
|
|
function fw_html_tag( $tag, $attr = array(), $end = false ) {
|
|
$html = '<' . $tag . ' ' . fw_attr_to_html( $attr );
|
|
|
|
if ( $end === true ) {
|
|
# <script></script>
|
|
$html .= '></' . $tag . '>';
|
|
} else if ( $end === false ) {
|
|
# <br/>
|
|
$html .= '/>';
|
|
} else {
|
|
# <div>content</div>
|
|
$html .= '>' . $end . '</' . $tag . '>';
|
|
}
|
|
|
|
return $html;
|
|
}
|
|
|
|
/**
|
|
* Generate attributes string for html tag
|
|
*
|
|
* @param array $attr_array array('href' => '/', 'title' => 'Test')
|
|
*
|
|
* @return string 'href="/" title="Test"'
|
|
*/
|
|
function fw_attr_to_html( array $attr_array ) {
|
|
$html_attr = '';
|
|
|
|
foreach ( $attr_array as $attr_name => $attr_val ) {
|
|
if ( $attr_val === false ) {
|
|
continue;
|
|
}
|
|
|
|
$html_attr .= $attr_name . '="' . fw_htmlspecialchars( $attr_val ) . '" ';
|
|
}
|
|
|
|
return $html_attr;
|
|
}
|
|
|
|
/**
|
|
* Strip slashes from values, and from keys if magic_quotes_gpc = On
|
|
*/
|
|
function fw_stripslashes_deep_keys( $value ) {
|
|
static $magic_quotes = null;
|
|
if ( $magic_quotes === null ) {
|
|
$magic_quotes = false; //https://www.php.net/manual/en/function.get-magic-quotes-gpc.php - always returns FALSE as of PHP 5.4.0. false fixes https://github.com/ThemeFuse/Unyson/issues/3915
|
|
}
|
|
|
|
if ( is_array( $value ) ) {
|
|
if ( $magic_quotes ) {
|
|
$new_value = array();
|
|
foreach ( $value as $key => $val ) {
|
|
$new_value[ is_string( $key ) ? stripslashes( $key ) : $key ] = fw_stripslashes_deep_keys( $val );
|
|
}
|
|
$value = $new_value;
|
|
unset( $new_value );
|
|
} else {
|
|
$value = array_map( 'fw_stripslashes_deep_keys', $value );
|
|
}
|
|
} elseif ( is_object( $value ) ) {
|
|
$vars = get_object_vars( $value );
|
|
foreach ( $vars as $key => $data ) {
|
|
$value->{$key} = fw_stripslashes_deep_keys( $data );
|
|
}
|
|
} elseif ( is_string( $value ) ) {
|
|
$value = stripslashes( $value );
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Add slashes to values, and to keys if magic_quotes_gpc = On
|
|
*/
|
|
function fw_addslashes_deep_keys( $value ) {
|
|
static $magic_quotes = null;
|
|
if ( $magic_quotes === null ) {
|
|
$magic_quotes = get_magic_quotes_gpc();
|
|
}
|
|
|
|
if ( is_array( $value ) ) {
|
|
if ( $magic_quotes ) {
|
|
$new_value = array();
|
|
foreach ( $value as $key => $value ) {
|
|
$new_value[ is_string( $key ) ? addslashes( $key ) : $key ] = fw_addslashes_deep_keys( $value );
|
|
}
|
|
$value = $new_value;
|
|
unset( $new_value );
|
|
} else {
|
|
$value = array_map( 'fw_addslashes_deep_keys', $value );
|
|
}
|
|
} elseif ( is_object( $value ) ) {
|
|
$vars = get_object_vars( $value );
|
|
foreach ( $vars as $key => $data ) {
|
|
$value->{$key} = fw_addslashes_deep_keys( $data );
|
|
}
|
|
} elseif ( is_string( $value ) ) {
|
|
$value = addslashes( $value );
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Check if current screen pass/match give rules
|
|
*
|
|
* @param array $rules Rules for current screen
|
|
*
|
|
* @return bool
|
|
*/
|
|
function fw_current_screen_match( array $rules ) {
|
|
$available_options = array(
|
|
'action' => true,
|
|
'base' => true,
|
|
'id' => true,
|
|
'is_network' => true,
|
|
'is_user' => true,
|
|
'parent_base' => true,
|
|
'parent_file' => true,
|
|
'post_type' => true,
|
|
'taxonomy' => true,
|
|
);
|
|
|
|
if ( empty( $rules ) ) {
|
|
return true;
|
|
}
|
|
|
|
$rules = array_merge(
|
|
array(
|
|
'exclude' => array(), // array of arrays or array with keys from $available_options
|
|
'only' => array(), // same as in 'exclude'
|
|
),
|
|
$rules
|
|
);
|
|
|
|
if ( empty( $rules['exclude'] ) && empty( $rules['only'] ) ) {
|
|
return true;
|
|
}
|
|
|
|
global $current_screen;
|
|
|
|
if ( gettype( $current_screen ) != 'object' ) {
|
|
return false;
|
|
}
|
|
|
|
// check if current screen passes the "only" rules
|
|
do {
|
|
$only = $rules['only'];
|
|
|
|
if ( empty( $only ) ) {
|
|
break;
|
|
}
|
|
|
|
if ( ! isset( $only[0] ) ) { // if not array of arrays
|
|
$only = array( $only );
|
|
}
|
|
|
|
$found_one = false;
|
|
$counter = 0;
|
|
foreach ( $only as $rule ) {
|
|
if ( ! count( $rule ) ) {
|
|
continue;
|
|
}
|
|
|
|
$match = true;
|
|
|
|
foreach ( $rule as $r_key => $r_val ) {
|
|
if ( ! isset( $available_options[ $r_key ] ) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( gettype( $r_val ) != 'array' ) {
|
|
$r_val = array( $r_val );
|
|
}
|
|
|
|
$counter ++;
|
|
|
|
if ( ! in_array( $current_screen->{$r_key}, $r_val ) ) {
|
|
$match = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( $match ) {
|
|
$found_one = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ! $found_one && $counter ) {
|
|
return false;
|
|
}
|
|
} while ( false );
|
|
|
|
// check if current screen passes the "exclude" rules
|
|
do {
|
|
$exclude = $rules['exclude'];
|
|
|
|
if ( empty( $exclude ) ) {
|
|
break;
|
|
}
|
|
|
|
if ( ! isset( $exclude[0] ) ) { // if not array of arrays
|
|
$exclude = array( $exclude );
|
|
}
|
|
|
|
foreach ( $exclude as $rule ) {
|
|
if ( ! count( $rule ) ) {
|
|
continue;
|
|
}
|
|
|
|
$match = true;
|
|
$counter = 0;
|
|
|
|
foreach ( $rule as $r_key => $r_val ) {
|
|
if ( ! isset( $available_options[ $r_key ] ) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( gettype( $r_val ) != 'array' ) {
|
|
$r_val = array( $r_val );
|
|
}
|
|
|
|
$counter ++;
|
|
|
|
if ( ! in_array( $current_screen->{$r_key}, $r_val ) ) {
|
|
$match = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( $match && $counter ) {
|
|
return false;
|
|
}
|
|
}
|
|
} while ( false );
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Search relative path in child then in parent theme directory and return URI
|
|
*
|
|
* @param string $rel_path '/some/path_to_dir' or '/some/path_to_file.php'
|
|
*
|
|
* @return string URI
|
|
*/
|
|
function fw_locate_theme_path_uri( $rel_path ) {
|
|
if ( is_child_theme() && file_exists( get_stylesheet_directory() . $rel_path ) ) {
|
|
return get_stylesheet_directory_uri() . $rel_path;
|
|
} elseif ( file_exists( get_template_directory() . $rel_path ) ) {
|
|
return get_template_directory_uri() . $rel_path;
|
|
} else {
|
|
return 'about:blank#theme-file-not-found:' . $rel_path;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Search relative path in child then in parent theme directory and return full path
|
|
*
|
|
* @param string $rel_path '/some/path_to_dir' or '/some/path_to_file.php'
|
|
*
|
|
* @return string URI
|
|
*/
|
|
function fw_locate_theme_path( $rel_path ) {
|
|
if ( is_child_theme() && file_exists( get_stylesheet_directory() . $rel_path ) ) {
|
|
return get_stylesheet_directory() . $rel_path;
|
|
} elseif ( file_exists( get_template_directory() . $rel_path ) ) {
|
|
return get_template_directory() . $rel_path;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* There is a theme which does: if (!defined('FW')): function fw_render_view() { ... } endif;
|
|
* It works fine, except in this case
|
|
* https://github.com/ThemeFuse/Unyson/commit/07be8b1f4b50eaf0f1f7e85ea1c6912a0415d241#diff-cf866bf08b8f747e3120221a6b1b07cfR48
|
|
* it throws fatal error because this function here is defined after that
|
|
*/
|
|
if ( ! function_exists( 'fw_render_view' ) ):
|
|
/**
|
|
* Safe render a view and return html
|
|
* In view will be accessible only passed variables
|
|
* Use this function to not include files directly and to not give access to current context variables (like $this)
|
|
*
|
|
* @param string $file_path
|
|
* @param array $view_variables
|
|
* @param bool $return In some cases, for memory saving reasons, you can disable the use of output buffering
|
|
*
|
|
* @return string HTML
|
|
*/
|
|
function fw_render_view( $file_path, $view_variables = array(), $return = true ) {
|
|
|
|
if ( ! is_file( $file_path ) ) {
|
|
return '';
|
|
}
|
|
|
|
extract( $view_variables, EXTR_REFS );
|
|
unset( $view_variables );
|
|
|
|
if ( $return ) {
|
|
ob_start();
|
|
require $file_path;
|
|
|
|
return ob_get_clean();
|
|
} else {
|
|
require $file_path;
|
|
}
|
|
|
|
return '';
|
|
}
|
|
endif;
|
|
|
|
/**
|
|
* Safe load variables from an file
|
|
* Use this function to not include files directly and to not give access to current context variables (like $this)
|
|
*
|
|
* @param string $file_path
|
|
* @param array $_extract_variables Extract these from file array('variable_name' => 'default_value')
|
|
* @param array $_set_variables Set these to be available in file (like variables in view)
|
|
*
|
|
* @return array
|
|
*/
|
|
function fw_get_variables_from_file( $file_path, array $_extract_variables, array $_set_variables = array() ) {
|
|
extract( $_set_variables, EXTR_REFS );
|
|
unset( $_set_variables );
|
|
|
|
require $file_path;
|
|
|
|
foreach ( $_extract_variables as $variable_name => $default_value ) {
|
|
if ( isset( $$variable_name ) ) {
|
|
$_extract_variables[ $variable_name ] = $$variable_name;
|
|
}
|
|
}
|
|
|
|
return $_extract_variables;
|
|
}
|
|
|
|
/**
|
|
* Use this function to not include files directly and to not give access to current context variables (like $this)
|
|
*
|
|
* @param string $file_path
|
|
* @param bool $once
|
|
*
|
|
* @return bool If was included or not
|
|
*/
|
|
function fw_include_file_isolated( $file_path, $once = false ) {
|
|
if ( file_exists( $file_path ) ) {
|
|
if ( (bool) $once ) {
|
|
include_once $file_path;
|
|
} else {
|
|
include $file_path;
|
|
}
|
|
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract only input options (without containers)
|
|
*
|
|
* @param array $options
|
|
*
|
|
* @return array {option_id => option}
|
|
*/
|
|
function fw_extract_only_options( array $options ) {
|
|
$collected = array();
|
|
|
|
fw_collect_options( $collected, $options );
|
|
|
|
return $collected;
|
|
}
|
|
|
|
/**
|
|
* Collect correct options from the first level of the array and group them
|
|
*
|
|
* @param array $collected Will be filled with found correct options
|
|
* @param array $options
|
|
*
|
|
* @deprecated
|
|
* It is deprecated since 2.4 because container types were added and there can be any type of containers
|
|
* but this function is hardcoded only for tab,box,group.
|
|
* Use fw_collect_options()
|
|
*/
|
|
function fw_collect_first_level_options( &$collected, &$options ) {
|
|
if ( empty( $options ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( empty( $collected ) ) {
|
|
$collected['tabs'] =
|
|
$collected['boxes'] =
|
|
$collected['groups'] =
|
|
$collected['options'] =
|
|
$collected['groups_and_options'] =
|
|
$collected['all'] = array();
|
|
}
|
|
|
|
foreach ( $options as $option_id => &$option ) {
|
|
if ( isset( $option['options'] ) ) {
|
|
// this is container for other options
|
|
|
|
switch ( $option['type'] ) {
|
|
case 'tab':
|
|
$collected['tabs'][ $option_id ] =& $option;
|
|
break;
|
|
case 'box':
|
|
$collected['boxes'][ $option_id ] =& $option;
|
|
break;
|
|
case 'group':
|
|
$collected['groups'][ $option_id ] =& $option;
|
|
$collected['groups_and_options'][ $option_id ] =& $option;
|
|
break;
|
|
default:
|
|
trigger_error( 'Invalid option container type: ' . $option['type'], E_USER_WARNING );
|
|
continue 2;
|
|
}
|
|
|
|
$collected['all'][ $option['type'] . ':~:' . $option_id ] = array(
|
|
'type' => $option['type'],
|
|
'id' => $option_id,
|
|
'option' => &$option,
|
|
);
|
|
} elseif (
|
|
is_int( $option_id )
|
|
&&
|
|
is_array( $option )
|
|
&&
|
|
/**
|
|
* make sure the array key was generated automatically
|
|
* and it's not an associative array with numeric keys created like this: $options[1] = array();
|
|
*/
|
|
isset( $options[0] )
|
|
) {
|
|
/**
|
|
* Array "without key" containing options.
|
|
*
|
|
* This happens when options are returned into array from a function:
|
|
* $options = array(
|
|
* 'foo' => array('type' => 'text'),
|
|
* 'bar' => array('type' => 'textarea'),
|
|
*
|
|
* // this is our case
|
|
* // go inside this array and extract the options as they are on the same array level
|
|
* array(
|
|
* 'hello' => array('type' => 'text'),
|
|
* ),
|
|
*
|
|
* // there can be any nested arrays
|
|
* array(
|
|
* array(
|
|
* array(
|
|
* 'h1' => array('type' => 'text'),
|
|
* ),
|
|
* ),
|
|
* ),
|
|
* )
|
|
*/
|
|
fw_collect_first_level_options( $collected, $option );
|
|
} elseif ( isset( $option['type'] ) ) {
|
|
// simple option, last possible level in options array
|
|
$collected['options'][ $option_id ] =& $option;
|
|
$collected['groups_and_options'][ $option_id ] =& $option;
|
|
|
|
$collected['all'][ 'option' . ':~:' . $option_id ] = array(
|
|
'type' => 'option',
|
|
'id' => $option_id,
|
|
'option' => &$option,
|
|
);
|
|
} else {
|
|
trigger_error( 'Invalid option: ' . $option_id, E_USER_WARNING );
|
|
}
|
|
}
|
|
unset( $option );
|
|
}
|
|
|
|
/**
|
|
* @param array $result
|
|
* @param array $options
|
|
* @param array $settings
|
|
* @param array $_recursion_data (private) for internal use
|
|
*/
|
|
function fw_collect_options( &$result, &$options, $settings = array(), $_recursion_data = array() ) {
|
|
static $default_settings = array(
|
|
/**
|
|
* @type bool Wrap the result/collected options in arrays will useful info
|
|
*
|
|
* If true:
|
|
* $result = array(
|
|
* '(container|option):{id}' => array(
|
|
* 'id' => '{id}',
|
|
* 'level' => int, // from which nested level this option is
|
|
* 'group' => 'container|option',
|
|
* 'option' => array(...),
|
|
* )
|
|
* )
|
|
*
|
|
* If false:
|
|
* $result = array(
|
|
* '{id}' => array(...),
|
|
* // Warning: There can be options and containers with the same id (array key will be replaced)
|
|
* )
|
|
*/
|
|
'info_wrapper' => false,
|
|
/**
|
|
* @type int Nested options level limit. For e.g. use 1 to collect only first level. 0 is for unlimited.
|
|
*/
|
|
'limit_level' => 0,
|
|
/**
|
|
* @type false|array('option-type', ...) Empty array will skip all types
|
|
*/
|
|
'limit_option_types' => false,
|
|
/**
|
|
* @type false|array('container-type', ...) Empty array will skip all types
|
|
*/
|
|
'limit_container_types' => array(),
|
|
/**
|
|
* @type int Limit the number of options that will be collected
|
|
*/
|
|
'limit' => 0,
|
|
/**
|
|
* @type callable Executed on each collected option
|
|
* @since 2.6.0
|
|
*/
|
|
'callback' => null,
|
|
);
|
|
|
|
static $access_key = null;
|
|
|
|
if ( empty( $options ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( empty( $_recursion_data ) ) {
|
|
if ( is_null( $access_key ) ) {
|
|
$access_key = new FW_Access_Key( 'fw_collect_options' );
|
|
}
|
|
|
|
$settings = array_merge( $default_settings, $settings );
|
|
|
|
$_recursion_data = array(
|
|
'level' => 1,
|
|
'access_key' => $access_key,
|
|
// todo: maybe add 'parent' => array('id' => '{id}', 'type' => 'container|option') ?
|
|
);
|
|
} elseif ( ! (
|
|
isset( $_recursion_data['access_key'] )
|
|
&&
|
|
( $_recursion_data['access_key'] instanceof FW_Access_Key )
|
|
&&
|
|
( $_recursion_data['access_key']->get_key() === 'fw_collect_options' )
|
|
)
|
|
) {
|
|
trigger_error( 'Call not allowed', E_USER_ERROR );
|
|
}
|
|
|
|
if (
|
|
$settings['limit_level']
|
|
&&
|
|
$_recursion_data['level'] > $settings['limit_level']
|
|
) {
|
|
return;
|
|
}
|
|
|
|
foreach ( $options as $option_id => &$option ) {
|
|
if ( isset( $option['options'] ) ) { // this is a container
|
|
do {
|
|
if (
|
|
is_array( $settings['limit_container_types'] )
|
|
&&
|
|
(
|
|
// Customizer options can contain options with not existing or empty $option['type']
|
|
empty( $option['type'] )
|
|
||
|
|
! in_array( $option['type'], $settings['limit_container_types'] )
|
|
)
|
|
) {
|
|
break;
|
|
}
|
|
|
|
if (
|
|
$settings['limit']
|
|
&&
|
|
count( $result ) >= $settings['limit']
|
|
) {
|
|
return;
|
|
}
|
|
|
|
if ( $settings['info_wrapper'] ) {
|
|
$result[ 'container:' . $option_id ] = array(
|
|
'group' => 'container',
|
|
'id' => $option_id,
|
|
'option' => &$option,
|
|
'level' => $_recursion_data['level'],
|
|
);
|
|
} else {
|
|
$result[ $option_id ] = &$option;
|
|
}
|
|
|
|
if ( $settings['callback'] ) {
|
|
call_user_func_array( $settings['callback'],
|
|
array(
|
|
array(
|
|
'group' => 'container',
|
|
'id' => $option_id,
|
|
'option' => &$option,
|
|
)
|
|
) );
|
|
}
|
|
} while ( false );
|
|
|
|
fw_collect_options(
|
|
$result,
|
|
$option['options'],
|
|
$settings,
|
|
array_merge( $_recursion_data, array( 'level' => $_recursion_data['level'] + 1 ) )
|
|
);
|
|
} elseif (
|
|
is_int( $option_id )
|
|
&&
|
|
is_array( $option )
|
|
&&
|
|
/**
|
|
* make sure the array key was generated automatically
|
|
* and it's not an associative array with numeric keys created like this: $options[1] = array();
|
|
*/
|
|
isset( $options[0] )
|
|
) {
|
|
/**
|
|
* Array "without key" containing options.
|
|
*
|
|
* This happens when options are returned into array from a function:
|
|
* $options = array(
|
|
* 'foo' => array('type' => 'text'),
|
|
* 'bar' => array('type' => 'textarea'),
|
|
*
|
|
* // this is our case
|
|
* // go inside this array and extract the options as they are on the same array level
|
|
* array(
|
|
* 'hello' => array('type' => 'text'),
|
|
* ),
|
|
*
|
|
* // there can be any nested arrays
|
|
* array(
|
|
* array(
|
|
* array(
|
|
* 'h1' => array('type' => 'text'),
|
|
* ),
|
|
* ),
|
|
* ),
|
|
* )
|
|
*/
|
|
fw_collect_options( $result, $option, $settings, $_recursion_data );
|
|
} elseif ( isset( $option['type'] ) ) { // option
|
|
if (
|
|
is_array( $settings['limit_option_types'] )
|
|
&&
|
|
! in_array( $option['type'], $settings['limit_option_types'] )
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
if (
|
|
$settings['limit']
|
|
&&
|
|
count( $result ) >= $settings['limit']
|
|
) {
|
|
return;
|
|
}
|
|
|
|
if ( $settings['info_wrapper'] ) {
|
|
$result[ 'option:' . $option_id ] = array(
|
|
'group' => 'option',
|
|
'id' => $option_id,
|
|
'option' => &$option,
|
|
'level' => $_recursion_data['level'],
|
|
);
|
|
} else {
|
|
$result[ $option_id ] = &$option;
|
|
}
|
|
|
|
if ( $settings['callback'] ) {
|
|
call_user_func_array( $settings['callback'],
|
|
array(
|
|
array(
|
|
'group' => 'option',
|
|
'id' => $option_id,
|
|
'option' => &$option,
|
|
)
|
|
) );
|
|
}
|
|
} else {
|
|
trigger_error( 'Invalid option: ' . $option_id, E_USER_WARNING );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get correct values from input (POST) for given options
|
|
* This values can be saved in db then replaced with $option['value'] for each option
|
|
*
|
|
* @param array $options
|
|
* @param array $input_array
|
|
*
|
|
* @return array Values
|
|
*/
|
|
function fw_get_options_values_from_input( array $options, $input_array = null ) {
|
|
if ( ! is_array( $input_array ) ) {
|
|
$input_array = FW_Request::POST( fw()->backend->get_options_name_attr_prefix() );
|
|
}
|
|
|
|
$values = array();
|
|
|
|
$maybe_new_values = apply_filters(
|
|
'fw:get_options_values_from_input:before',
|
|
null,
|
|
$options, $input_array
|
|
);
|
|
|
|
if ($maybe_new_values) {
|
|
return $maybe_new_values;
|
|
}
|
|
|
|
foreach ( fw_extract_only_options( $options ) as $id => $option ) {
|
|
$values[ $id ] = fw()->backend->option_type( $option['type'] )->get_value_from_input(
|
|
$option,
|
|
isset( $input_array[ $id ] ) ? $input_array[ $id ] : null
|
|
);
|
|
|
|
if ( is_null( $values[ $id ] ) ) {
|
|
// do not save null values
|
|
unset( $values[ $id ] );
|
|
}
|
|
}
|
|
|
|
return $values;
|
|
}
|
|
|
|
/**
|
|
* @param $attr_name
|
|
* @param bool $set_mode
|
|
*
|
|
* @return mixed
|
|
*/
|
|
function fw_html_attr_name_to_array_multi_key( $attr_name, $set_mode = false ) {
|
|
if ( $set_mode ) {
|
|
/**
|
|
* The key will be used to set value in array
|
|
* 'hello[world][]' -> 'hello/world/'
|
|
* $array['hello']['world'][] = $value;
|
|
*/
|
|
$attr_name = str_replace( '[]', '/', $attr_name );
|
|
} else {
|
|
/**
|
|
* The key will be used to get value from array
|
|
* 'hello[world][]' -> 'hello/world'
|
|
* $value = $array['hello']['world'];
|
|
*/
|
|
$attr_name = str_replace( '[]', '', $attr_name );
|
|
}
|
|
|
|
$attr_name = str_replace( '][', '/', $attr_name );
|
|
$attr_name = str_replace( '[', '/', $attr_name );
|
|
$attr_name = str_replace( ']', '', $attr_name );
|
|
|
|
return $attr_name;
|
|
}
|
|
|
|
/**
|
|
* Used when getting some option value from serialized array saved in a custom place
|
|
* and that option is unreachable for standard WordPress filters by other plugins
|
|
* For e.g. that option cannot be translated by plugins, so we pass its value through this function and do the fixes
|
|
*
|
|
* @param $value
|
|
*
|
|
* @return array
|
|
*/
|
|
function fw_prepare_option_value( $value ) {
|
|
if ( empty( $value ) ) {
|
|
return $value;
|
|
}
|
|
|
|
if ( function_exists( 'qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage' ) ) {
|
|
$value = qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage( $value );
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* This function is used in 'save_post' action
|
|
*
|
|
* Used to check if current post save is a regular "Save" button press
|
|
* not a revision, auto-save or something else
|
|
*
|
|
* @param $post_id
|
|
*
|
|
* @return bool
|
|
*
|
|
* @deprecated
|
|
* save_post action happens also happens on Preview, Revision, Auto-save Restore, ...
|
|
* the verifications in this function simplifies too much the save process,
|
|
* the developers should study and understand better how it works
|
|
* and handle different save cases in their scripts using wp functions
|
|
*/
|
|
function fw_is_real_post_save( $post_id ) {
|
|
return ! (
|
|
wp_is_post_revision( $post_id )
|
|
|| wp_is_post_autosave( $post_id )
|
|
|| ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
|
|
|| ( defined( 'DOING_AJAX' ) && DOING_AJAX )
|
|
|| empty( $_POST )
|
|
|| empty( $_POST['post_ID'] )
|
|
|| $_POST['post_ID'] != $post_id
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return Array with Google fonts
|
|
*/
|
|
function fw_get_google_fonts() {
|
|
$cache_key = 'fw_google_fonts';
|
|
|
|
try {
|
|
return FW_Cache::get( $cache_key );
|
|
} catch ( FW_Cache_Not_Found_Exception $e ) {
|
|
$g_fonts = json_decode( fw_get_google_fonts_v2(), true );
|
|
$old_fonts = include( dirname( __FILE__ ) . '/fw-google-fonts.json.php' );
|
|
$fonts = array();
|
|
|
|
foreach ( $g_fonts['items'] as $font ) {
|
|
$fonts[ $font['family'] ] = array(
|
|
'family' => $font['family'],
|
|
'variants' => $font['variants'],
|
|
'position' => isset( $old_fonts[ $font['family'] ] )
|
|
? $old_fonts[ $font['family'] ]['position']
|
|
: 99999
|
|
);
|
|
}
|
|
|
|
$fonts = apply_filters( 'fw_google_fonts', $fonts );
|
|
|
|
FW_Cache::set( $cache_key, $fonts );
|
|
|
|
return $fonts;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return string JSON encoded array with Google fonts
|
|
*/
|
|
function fw_get_google_fonts_v2() {
|
|
$saved_data = get_option( 'fw_google_fonts', false );
|
|
$ttl = 7 * DAY_IN_SECONDS;
|
|
|
|
if (
|
|
false === $saved_data
|
|
||
|
|
( $saved_data['last_update'] + $ttl < time() )
|
|
) {
|
|
$response = wp_remote_get( apply_filters( 'fw_googleapis_webfonts_url',
|
|
'https://google-webfonts-cache.unyson.io/v1/webfonts' ) );
|
|
$body = wp_remote_retrieve_body( $response );
|
|
|
|
if (
|
|
200 === wp_remote_retrieve_response_code( $response )
|
|
&&
|
|
! is_wp_error( $body ) && ! empty( $body )
|
|
) {
|
|
update_option( 'fw_google_fonts',
|
|
array(
|
|
'last_update' => time(),
|
|
'fonts' => $body
|
|
),
|
|
false );
|
|
|
|
return $body;
|
|
} else {
|
|
if ( empty( $saved_data['fonts'] ) ) {
|
|
$saved_data['fonts'] = json_encode( array( 'items' => array() ) );
|
|
}
|
|
|
|
update_option( 'fw_google_fonts',
|
|
array(
|
|
'last_update' => time() - $ttl + MINUTE_IN_SECONDS,
|
|
'fonts' => $saved_data['fonts']
|
|
),
|
|
false );
|
|
}
|
|
}
|
|
|
|
return $saved_data['fonts'];
|
|
}
|
|
|
|
/**
|
|
* @return string Current url
|
|
*/
|
|
function fw_current_url() {
|
|
static $url = null;
|
|
if ( $url === null ) {
|
|
if ( is_multisite() && ! ( defined( 'SUBDOMAIN_INSTALL' ) && SUBDOMAIN_INSTALL ) ) {
|
|
switch_to_blog( 1 );
|
|
$url = get_option( 'home' );
|
|
restore_current_blog();
|
|
} else {
|
|
$url = get_option( 'home' );
|
|
}
|
|
|
|
//Remove the "//" before the domain name
|
|
$url = ltrim( fw_get_url_without_scheme( $url ), '/' );
|
|
|
|
//Remove the ulr subdirectory in case it has one
|
|
$split = explode( '/', $url );
|
|
|
|
//Remove end slash
|
|
$url = rtrim( $split[0], '/' );
|
|
|
|
$url .= '/' . ltrim( fw_akg( 'REQUEST_URI', $_SERVER, '' ), '/' );
|
|
$url = set_url_scheme( '//' . $url ); // https fix
|
|
}
|
|
|
|
return $url;
|
|
}
|
|
|
|
function fw_is_valid_domain_name( $domain_name ) {
|
|
return ( preg_match( "/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name ) // valid chars check
|
|
&& preg_match( "/^.{1,253}$/", $domain_name ) // overall length check
|
|
&& preg_match( "/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name ) ); // length of each label
|
|
}
|
|
|
|
/**
|
|
* Use this id do not want to enter every time same last two parameters
|
|
* Info: Cannot use default parameters because in php 5.2 encoding is not UTF-8 by default
|
|
*
|
|
* @param string $string
|
|
*
|
|
* @return string
|
|
*/
|
|
function fw_htmlspecialchars( $string ) {
|
|
return htmlspecialchars( $string, ENT_QUOTES, 'UTF-8' );
|
|
}
|
|
|
|
/**
|
|
* Check if current user has one capability from the given list
|
|
*
|
|
* @param array $capabilities list of capabilities to check
|
|
* @param mixed $default_value
|
|
*
|
|
* @return string|bool|mixed
|
|
* Return first capability that user can.
|
|
* Else, return default value if it is not null, else return first capability from list.
|
|
* Use default value false to check if user can some of the capabilities
|
|
*/
|
|
function fw_current_user_can( $capabilities, $default_value = null ) {
|
|
if ( is_user_logged_in() ) {
|
|
foreach ( $capabilities as $capability ) {
|
|
if ( current_user_can( $capability ) ) {
|
|
return $capability;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ( $default_value !== null ? fw_call( $default_value ) : array_shift( $capabilities ) );
|
|
}
|
|
|
|
/**
|
|
* Convert number of seconds to 'X {units}'
|
|
*
|
|
* E.g. 123 => '2 minutes'
|
|
* then you can use this string how you want, for e.g. append ' ago' => '2 minutes ago'
|
|
*
|
|
* @param int $seconds
|
|
*
|
|
* @return string
|
|
*/
|
|
function fw_human_time( $seconds ) {
|
|
static $translations = null;
|
|
if ( $translations === null ) {
|
|
$translations = array(
|
|
'year' => __( 'year', 'fw' ),
|
|
'years' => __( 'years', 'fw' ),
|
|
|
|
'month' => __( 'month', 'fw' ),
|
|
'months' => __( 'months', 'fw' ),
|
|
|
|
'week' => __( 'week', 'fw' ),
|
|
'weeks' => __( 'weeks', 'fw' ),
|
|
|
|
'day' => __( 'day', 'fw' ),
|
|
'days' => __( 'days', 'fw' ),
|
|
|
|
'hour' => __( 'hour', 'fw' ),
|
|
'hours' => __( 'hours', 'fw' ),
|
|
|
|
'minute' => __( 'minute', 'fw' ),
|
|
'minutes' => __( 'minutes', 'fw' ),
|
|
|
|
'second' => __( 'second', 'fw' ),
|
|
'seconds' => __( 'seconds', 'fw' ),
|
|
);
|
|
}
|
|
|
|
$tokens = array(
|
|
31536000 => 'year',
|
|
2592000 => 'month',
|
|
604800 => 'week',
|
|
86400 => 'day',
|
|
3600 => 'hour',
|
|
60 => 'minute',
|
|
1 => 'second'
|
|
);
|
|
|
|
foreach ( $tokens as $unit => $translation_key ) {
|
|
if ( $seconds < $unit ) {
|
|
continue;
|
|
}
|
|
|
|
$number_of_units = floor( $seconds / $unit );
|
|
|
|
return $number_of_units . ' ' . $translations[ $translation_key . ( $number_of_units != 1 ? 's' : '' ) ];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert bytes to human readable format
|
|
*
|
|
* @param integer $bytes Size in bytes to convert
|
|
* @param integer $precision
|
|
*
|
|
* @return string
|
|
* @since 2.4.17
|
|
*/
|
|
function fw_human_bytes( $bytes, $precision = 2 ) {
|
|
$kilobyte = 1024;
|
|
$megabyte = $kilobyte * 1024;
|
|
$gigabyte = $megabyte * 1024;
|
|
$terabyte = $gigabyte * 1024;
|
|
|
|
if ( ( $bytes >= 0 ) && ( $bytes < $kilobyte ) ) {
|
|
return $bytes . ' B';
|
|
|
|
} elseif ( ( $bytes >= $kilobyte ) && ( $bytes < $megabyte ) ) {
|
|
return round( $bytes / $kilobyte, $precision ) . ' KB';
|
|
|
|
} elseif ( ( $bytes >= $megabyte ) && ( $bytes < $gigabyte ) ) {
|
|
return round( $bytes / $megabyte, $precision ) . ' MB';
|
|
|
|
} elseif ( ( $bytes >= $gigabyte ) && ( $bytes < $terabyte ) ) {
|
|
return round( $bytes / $gigabyte, $precision ) . ' GB';
|
|
|
|
} elseif ( $bytes >= $terabyte ) {
|
|
return round( $bytes / $terabyte, $precision ) . ' TB';
|
|
} else {
|
|
return $bytes . ' B';
|
|
}
|
|
}
|
|
|
|
function fw_strlen( $string ) {
|
|
if ( function_exists( 'mb_strlen' ) ) {
|
|
return mb_strlen( $string, 'UTF-8' );
|
|
} else {
|
|
return strlen( $string );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If currently is a Post Edit page display/submit
|
|
* @return bool
|
|
*/
|
|
function fw_is_post_edit() {
|
|
static $result = null;
|
|
|
|
if ( $result === null ) {
|
|
$result = false;
|
|
|
|
if ( is_admin() ) {
|
|
if (
|
|
empty( $_POST )
|
|
&&
|
|
isset( $_GET['action'] )
|
|
&&
|
|
$_GET['action'] === 'edit'
|
|
&&
|
|
isset( $_GET['post'] )
|
|
) {
|
|
// Display Edit Post page
|
|
$result = true;
|
|
} elseif (
|
|
isset( $_POST['action'] )
|
|
&&
|
|
$_POST['action'] === 'editpost'
|
|
&&
|
|
isset( $_POST['post_type'] )
|
|
&&
|
|
isset( $_POST['post_ID'] )
|
|
&&
|
|
strpos( wp_get_referer(), 'action=edit' ) !== false
|
|
) {
|
|
// Submit Edit Post page
|
|
$result = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param string $dirname 'foo-bar'
|
|
*
|
|
* @return string 'Foo_Bar'
|
|
*/
|
|
function fw_dirname_to_classname( $dirname ) {
|
|
$class_name = explode( '-', $dirname );
|
|
$class_name = array_map( 'ucfirst', $class_name );
|
|
$class_name = implode( '_', $class_name );
|
|
|
|
return $class_name;
|
|
}
|
|
|
|
/**
|
|
* This function is a wrapper function that set correct width and height for iframes from wp_oembed_get() function
|
|
*
|
|
* @param $url
|
|
* @param array $args
|
|
*
|
|
* @return bool|string
|
|
*/
|
|
function fw_oembed_get( $url, $args = array() ) {
|
|
$html = wp_oembed_get( $url, $args );
|
|
|
|
if ( ! empty( $args['width'] ) and ! empty( $args['height'] ) and class_exists( 'DOMDocument' ) and ! empty( $html ) ) {
|
|
$dom_element = new DOMDocument();
|
|
@$dom_element->loadHTML( $html );
|
|
|
|
if ( $obj = $dom_element->getElementsByTagName( 'iframe' )->item( 0 ) ) {
|
|
$obj->setAttribute( 'width', $args['width'] );
|
|
$obj->setAttribute( 'height', $args['height'] );
|
|
//saveXml instead of SaveHTML for php version compatibility
|
|
$html = $dom_element->saveXML( $obj, LIBXML_NOEMPTYTAG );
|
|
}
|
|
}
|
|
|
|
return $html;
|
|
}
|
|
|
|
/**
|
|
* @var $length
|
|
* @return string
|
|
*
|
|
* Reference
|
|
*
|
|
* Strong cryptography in PHP
|
|
* http://www.zimuel.it/en/strong-cryptography-in-php/
|
|
* > Don't use rand() or mt_rand()
|
|
*/
|
|
function fw_secure_rand( $length ) {
|
|
if ( function_exists( 'openssl_random_pseudo_bytes' ) ) {
|
|
$rnd = openssl_random_pseudo_bytes( $length, $strong );
|
|
if ( $strong ) {
|
|
return $rnd;
|
|
}
|
|
}
|
|
|
|
$sha = '';
|
|
$rnd = '';
|
|
|
|
if ( file_exists( '/dev/urandom' ) ) {
|
|
$fp = fopen( '/dev/urandom', 'rb' );
|
|
if ( $fp ) {
|
|
if ( function_exists( 'stream_set_read_buffer' ) ) {
|
|
stream_set_read_buffer( $fp, 0 );
|
|
}
|
|
$sha = fread( $fp, $length );
|
|
fclose( $fp );
|
|
}
|
|
}
|
|
|
|
for ( $i = 0; $i < $length; $i ++ ) {
|
|
$sha = hash( 'sha256', $sha . mt_rand() );
|
|
$char = mt_rand( 0, 62 );
|
|
$rnd .= chr( hexdec( $sha[ $char ] . $sha[ $char + 1 ] ) );
|
|
}
|
|
|
|
return $rnd;
|
|
}
|
|
|
|
/**
|
|
* Try to make user friendly title from an id
|
|
*
|
|
* @param string $id 'hello-world'
|
|
*
|
|
* @return string 'Hello world'
|
|
*/
|
|
function fw_id_to_title( $id ) {
|
|
// mb_ucfirst()
|
|
if ( function_exists( 'mb_strtoupper' ) && function_exists( 'mb_substr' ) && function_exists( 'mb_strlen' ) ) {
|
|
$id = mb_strtoupper( mb_substr( $id, 0, 1, 'UTF-8' ), 'UTF-8' ) . mb_substr( $id,
|
|
1,
|
|
mb_strlen( $id, 'UTF-8' ),
|
|
'UTF-8' );
|
|
} else {
|
|
$id = strtoupper( substr( $id, 0, 1 ) ) . substr( $id, 1, strlen( $id ) );
|
|
}
|
|
|
|
return str_replace( array( '_', '-' ), ' ', $id );
|
|
}
|
|
|
|
/**
|
|
* Alias
|
|
*
|
|
* @param string $extension_name
|
|
*
|
|
* @return FW_Extension|null
|
|
*/
|
|
function fw_ext( $extension_name ) {
|
|
return fw()->extensions->get( $extension_name );
|
|
}
|
|
|
|
/*
|
|
* Return URI without scheme
|
|
*/
|
|
function fw_get_url_without_scheme( $url ) {
|
|
return preg_replace( '/^[^:]+:\/\//', '//', $url );
|
|
}
|
|
|
|
/**
|
|
* Try to find file path by its uri and read the file contents
|
|
*
|
|
* @param string $file_uri
|
|
*
|
|
* @return bool|string false or string - the file contents
|
|
*/
|
|
function fw_read_file_by_uri( $file_uri ) {
|
|
static $base = null;
|
|
|
|
if ( $base === null ) {
|
|
$base = array();
|
|
$base['dir'] = WP_CONTENT_DIR;
|
|
$base['uri'] = ltrim( content_url(), '/' );
|
|
$base['uri_prefix_regex'] = '/^' . preg_quote( $base['uri'], '/' ) . '/';
|
|
}
|
|
|
|
$file_rel_path = preg_replace( $base['uri_prefix_regex'], '', $file_uri );
|
|
|
|
if ( $base['uri'] === $file_rel_path ) {
|
|
// the file is not inside base dir
|
|
return false;
|
|
}
|
|
|
|
$file_path = $base['dir'] . '/' . $file_rel_path;
|
|
|
|
if ( ! file_exists( $file_path ) ) {
|
|
return false;
|
|
}
|
|
|
|
return file_get_contents( $file_path );
|
|
}
|
|
|
|
/**
|
|
* Make stylesheet contents (portable) independent of directory location
|
|
* For e.g. replace relative paths 'url(img/bg.png)' with full paths 'url(http://site.com/assets/img/bg.png)'
|
|
*
|
|
* @param string $href 'http://.../style.css'
|
|
* @param null|string $contents If not specified, will try to read from $href
|
|
*
|
|
* @return bool|string false - on failure; string - stylesheet contents
|
|
*/
|
|
function fw_make_stylesheet_portable( $href, $contents = null ) {
|
|
if ( is_null( $contents ) ) {
|
|
$contents = fw_read_file_by_uri( $href );
|
|
|
|
if ( $contents === false ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
$dir_uri = dirname( $href );
|
|
|
|
/**
|
|
* Replace relative 'url(img/bg.png)'
|
|
* with full 'url(http://site.com/assets/img/bg.png)'
|
|
*
|
|
* Do not touch if url starts with:
|
|
* - 'https://'
|
|
* - 'http://'
|
|
* - '/' (also matches '//')
|
|
* - '#' (for css property: "behavior: url(#behaveBinObject)")
|
|
* - 'data:'
|
|
*/
|
|
$contents = preg_replace(
|
|
'/url\s*\((?!\s*[\'"]?(?:\/|data\:|\#|(?:https?:)?\/\/))\s*([\'"])?/',
|
|
'url($1' . $dir_uri . '/',
|
|
$contents
|
|
);
|
|
|
|
return $contents;
|
|
}
|
|
|
|
/**
|
|
* Return all images sizes register by add_image_size() merged with
|
|
* WordPress default image sizes.
|
|
* @link https://codex.wordpress.org/Function_Reference/get_intermediate_image_sizes
|
|
*
|
|
* @param string $size
|
|
*
|
|
* @return array|bool
|
|
*/
|
|
function fw_get_image_sizes( $size = '' ) {
|
|
global $_wp_additional_image_sizes;
|
|
|
|
$sizes = array();
|
|
$get_intermediate_image_sizes = get_intermediate_image_sizes();
|
|
|
|
// Create the full array with sizes and crop info
|
|
foreach ( $get_intermediate_image_sizes as $_size ) {
|
|
if ( in_array( $_size, array( 'thumbnail', 'medium', 'large' ) ) ) {
|
|
$sizes[ $_size ]['width'] = get_option( $_size . '_size_w' );
|
|
$sizes[ $_size ]['height'] = get_option( $_size . '_size_h' );
|
|
$sizes[ $_size ]['crop'] = (bool) get_option( $_size . '_crop' );
|
|
} elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {
|
|
$sizes[ $_size ] = array(
|
|
'width' => $_wp_additional_image_sizes[ $_size ]['width'],
|
|
'height' => $_wp_additional_image_sizes[ $_size ]['height'],
|
|
'crop' => $_wp_additional_image_sizes[ $_size ]['crop']
|
|
);
|
|
}
|
|
}
|
|
|
|
// Get only 1 size if found
|
|
if ( $size ) {
|
|
if ( isset( $sizes[ $size ] ) ) {
|
|
return $sizes[ $size ];
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return $sizes;
|
|
}
|
|
|
|
/**
|
|
* @param string $icon A string that is meant to be an icon (an image, a font icon class, or something else)
|
|
* @param array Additional attributes
|
|
*
|
|
* @return string
|
|
*/
|
|
function fw_string_to_icon_html( $icon, array $attributes = array() ) {
|
|
if (
|
|
preg_match( '/\.(png|jpg|jpeg|gif|svg|webp)$/', $icon )
|
|
||
|
|
preg_match( '/^data:image\//', $icon )
|
|
) {
|
|
// http://.../image.png
|
|
$tag = 'img';
|
|
$attr = array(
|
|
'src' => $icon,
|
|
'alt' => 'icon',
|
|
);
|
|
} elseif ( preg_match( '/^[a-zA-Z0-9\-_ ]+$/', $icon ) ) {
|
|
// 'font-icon font-icon-class'
|
|
$tag = 'span';
|
|
$attr = array(
|
|
'class' => trim( $icon ),
|
|
);
|
|
} else {
|
|
// can't detect. maybe it's raw html '<span ...'
|
|
return $icon;
|
|
}
|
|
|
|
foreach ( $attributes as $attr_name => $attr_val ) {
|
|
if ( isset( $attr[ $attr_name ] ) ) {
|
|
if ( $attr_name === 'class' ) {
|
|
$attr[ $attr_name ] .= ' ' . $attr_val;
|
|
} else {
|
|
// ignore. do not overwrite already set attributes
|
|
}
|
|
} else {
|
|
$attr[ $attr_name ] = (string) $attr_val;
|
|
}
|
|
}
|
|
|
|
return fw_html_tag( $tag, $attr );
|
|
}
|
|
|
|
/**
|
|
* @return string|null
|
|
* @since 2.4.10
|
|
*/
|
|
function fw_get_json_last_error_message() {
|
|
switch ( function_exists( 'json_last_error' ) ? json_last_error() : - 1 ) {
|
|
case JSON_ERROR_NONE:
|
|
return null; // __('No errors', 'fw');
|
|
break;
|
|
case JSON_ERROR_DEPTH:
|
|
return __( 'Maximum stack depth exceeded', 'fw' );
|
|
break;
|
|
case JSON_ERROR_STATE_MISMATCH:
|
|
return __( 'Underflow or the modes mismatch', 'fw' );
|
|
break;
|
|
case JSON_ERROR_CTRL_CHAR:
|
|
return __( 'Unexpected control character found', 'fw' );
|
|
break;
|
|
case JSON_ERROR_SYNTAX:
|
|
return __( 'Syntax error, malformed JSON', 'fw' );
|
|
break;
|
|
case JSON_ERROR_UTF8:
|
|
return __( 'Malformed UTF-8 characters, possibly incorrectly encoded', 'fw' );
|
|
break;
|
|
default:
|
|
return __( 'Unknown error', 'fw' );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return mime_types by file extension ex : input : array( 'png', 'jpg', 'jpeg' ) => output : array( 'image/jpeg' ).
|
|
*
|
|
* @param array $type
|
|
*
|
|
* @return array
|
|
*/
|
|
function fw_get_mime_type_by_ext( $type = array() ) {
|
|
$result = array();
|
|
|
|
foreach ( wp_get_mime_types() as $key => $mime_type ) {
|
|
$types = explode( '|', $key );
|
|
foreach ( $type as $item ) {
|
|
if ( in_array( $item, $types ) && ! in_array( $mime_type, $result ) ) {
|
|
$result[] = $mime_type;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Return types from file extensions ex : input array( 'png', 'jpg', 'zip' ) => output : array( 'image', 'archive' ).
|
|
*
|
|
* @see wp_ext2type() function.
|
|
*
|
|
* @param array $ext_array
|
|
*
|
|
* @return array
|
|
*/
|
|
function fw_multi_ext2type( $ext_array = array() ) {
|
|
$result = array();
|
|
|
|
foreach ( $ext_array as $ext ) {
|
|
if ( ! in_array( $type = wp_ext2type( $ext ), $result ) ) {
|
|
$result[] = $type;
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
if ( ! function_exists( 'fw_resize' ) ) {
|
|
function fw_resize( $url, $width = false, $height = false, $crop = false ) {
|
|
$fw_resize = FW_Resize::getInstance();
|
|
$response = $fw_resize->process( $url, $width, $height, $crop );
|
|
|
|
return ( ! is_wp_error( $response ) && ! empty( $response['src'] ) ) ? $response['src'] : $url;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* fw_get_path_url( dirname(__FILE__) .'/test.css' ) --> http://site.url/path/to/test.css
|
|
*
|
|
* @param string $path
|
|
*
|
|
* @return string|null
|
|
* @since 2.6.11
|
|
*/
|
|
function fw_get_path_url( $path ) {
|
|
try {
|
|
$paths_to_urls = FW_Cache::get( $cache_key = 'fw:paths_to_urls' );
|
|
} catch ( FW_Cache_Not_Found_Exception $e ) {
|
|
$wp_upload_dir = wp_upload_dir();
|
|
|
|
$paths_to_urls = array(
|
|
fw_fix_path( WP_PLUGIN_DIR ) => plugins_url(),
|
|
fw_fix_path( get_theme_root() ) => get_theme_root_uri(),
|
|
fw_fix_path( $wp_upload_dir['basedir'] ) => $wp_upload_dir['baseurl'],
|
|
);
|
|
|
|
if ( is_multisite() && WPMU_PLUGIN_DIR ) {
|
|
$paths_to_urls[ fw_fix_path( WPMU_PLUGIN_DIR ) ] = WPMU_PLUGIN_URL;
|
|
}
|
|
|
|
FW_Cache::set( $cache_key, $paths_to_urls );
|
|
}
|
|
|
|
$path = fw_fix_path( $path );
|
|
|
|
foreach ( $paths_to_urls as $_path => $_url ) {
|
|
if ( preg_match( $regex = '/^' . preg_quote( $_path, '/' ) . '($|\/)/', $path ) ) {
|
|
return $_url . '/' . preg_replace( $regex, '', $path );
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param string|array $callback Callback function
|
|
* @param array $args Callback arguments
|
|
* @param bool $cache Whenever you want to cache the function value after it's first call or not
|
|
* Recommend when the function call may require many resources or time (database requests) , or the value is small
|
|
* Not recommended using on very large values
|
|
*
|
|
* @return FW_Callback
|
|
*
|
|
* @since 2.6.14
|
|
*/
|
|
function fw_callback( $callback, array $args = array(), $cache = true ) {
|
|
return new FW_Callback( $callback, $args, $cache );
|
|
}
|
|
|
|
/**
|
|
* In the value is instance of FW_Callback class then it is executed and returns the callback value
|
|
* In other case function returns the provided value
|
|
*
|
|
* @param mixed|FW_Callback $value
|
|
*
|
|
* @return mixed
|
|
*
|
|
* @since 2.6.14
|
|
*/
|
|
function fw_call( $value ) {
|
|
if ( ! fw_is_callback( $value ) ) {
|
|
return $value;
|
|
}
|
|
|
|
return ( is_object( $value ) && get_class( $value ) == 'Closure' )
|
|
? $value()
|
|
: $value->execute();
|
|
}
|
|
|
|
/**
|
|
* Check is the current value is instance of FW_Callback class
|
|
*
|
|
* @param mixed $value
|
|
*
|
|
* @return bool
|
|
*/
|
|
function fw_is_callback( $value ) {
|
|
return $value instanceof FW_Callback || ( is_object( $value ) && get_class( $value ) == 'Closure' );
|
|
}
|
|
|
|
/**
|
|
* Check for command line interface
|
|
*
|
|
* @return bool
|
|
* @since 2.6.16
|
|
*/
|
|
function fw_is_cli() {
|
|
return ( php_sapi_name() === 'cli' ) && defined( 'WP_CLI' );
|
|
}
|