array('WP_STATELESS_MEDIA_MODE', 'ephemeral'), 'body_rewrite' => array('WP_STATELESS_MEDIA_BODY_REWRITE', 'false'), 'body_rewrite_types' => array('WP_STATELESS_MEDIA_BODY_REWRITE_TYPES', 'jpg jpeg png gif pdf'), 'bucket' => array('WP_STATELESS_MEDIA_BUCKET', ''), 'root_dir' => array('WP_STATELESS_MEDIA_ROOT_DIR', ['/%date_year/date_month%/', '/sites/%site_id%/%date_year/date_month%/']), 'key_json' => array('WP_STATELESS_MEDIA_JSON_KEY', ''), 'cache_control' => array('WP_STATELESS_MEDIA_CACHE_CONTROL', ''), 'delete_remote' => array('WP_STATELESS_MEDIA_DELETE_REMOTE', 'true'), 'custom_domain' => array('WP_STATELESS_MEDIA_CUSTOM_DOMAIN', ''), 'organize_media' => array('', 'true'), 'hashify_file_name' => array(['WP_STATELESS_MEDIA_HASH_FILENAME' => 'WP_STATELESS_MEDIA_CACHE_BUSTING'], 'false'), ); private $network_only_settings = array( 'hide_settings_panel' => array('WP_STATELESS_MEDIA_HIDE_SETTINGS_PANEL', false), 'hide_setup_assistant' => array('WP_STATELESS_MEDIA_HIDE_SETUP_ASSISTANT', false), ); private $strings = array( 'network' => 'Currently configured via Network Settings.', 'constant' => 'Currently configured via a constant.', 'environment' => 'Currently configured via an environment variable.', ); /** * * Settings constructor. * @param null $bootstrap */ public function __construct($bootstrap = null) { $this->bootstrap = $bootstrap ? $bootstrap : ud_get_stateless_media(); /* Add 'Settings' link for SM plugin on plugins page. */ $_basename = plugin_basename( $this->bootstrap->boot_file ); parent::__construct( array( 'store' => 'options', 'format' => 'json', 'data' => array( 'sm' => array() ) )); // Setting sm variable $this->refresh(); $this->set('page_url.stateless_setup', $this->bootstrap->get_settings_page_url('?page=stateless-setup')); $this->set('page_url.stateless_settings', $this->bootstrap->get_settings_page_url('?page=stateless-settings')); /** Register options */ add_action( 'init', array( $this, 'init' ), 3 ); // apply wildcard to root dir. add_filter( 'wp_stateless_handle_root_dir', array( $this, 'root_dir_wildcards' ), 10, 3); // Parse root dir by wildcards add_filter( 'wp_stateless_unhandle_root_dir', array( $this, 'parse_root_dir_wildcards' ), 10, 3); $site_url = parse_url( site_url() ); $site_url['path'] = isset($site_url['path']) ? $site_url['path'] : ''; $this->wildcards = array( 'sites' => [ 'sites', __("sites", $this->bootstrap->domain), __("Sites uses for multisite.", $this->bootstrap->domain), ], '%site_id%' => [ get_current_blog_id(), __("site id", $this->bootstrap->domain), __("Site ID, for example 1.", $this->bootstrap->domain), ], '%site_url%' => [ trim( $site_url['host'] . $site_url['path'], '/ ' ), __("site url", $this->bootstrap->domain), __("Site URL, for example example.com/site-1.", $this->bootstrap->domain), ], '%site_url_host%' => [ trim( $site_url['host'], '/ ' ), __("host name", $this->bootstrap->domain), __("Host name, for example example.com.", $this->bootstrap->domain), ], '%site_url_path%' => [ trim( $site_url['path'], '/ ' ), __("site path", $this->bootstrap->domain), __("Site path, for example site-1.", $this->bootstrap->domain), ], '%date_year/date_month%' => [ date('Y').'/'.date('m'), __("year and monthnum", $this->bootstrap->domain), __("The year of the post, four digits, for example 2004. Month of the year, for example 05", $this->bootstrap->domain), "\d{4}\/\d{2}" ] ); } /** * Init */ public function init(){ $this->save_media_settings(); add_action('admin_menu', array( $this, 'admin_menu' )); /** * Manage specific Network Settings */ if( is_network_admin() ) { add_action( 'network_admin_menu', array( $this, 'network_admin_menu' )); } } /** * Refresh settings */ public function refresh() { $this->set( "sm.readonly", []); $google_app_key_file = getenv('GOOGLE_APPLICATION_CREDENTIALS') ?: getenv('GOOGLE_APPLICATION_CREDENTIALS'); foreach ($this->settings as $option => $array) { $value = ''; $_option = 'sm_' . $option; $constant = $array[0]; // Constant name $default = is_array($array[1]) ? $array[1] : array($array[1], $array[1]); // Default value // Getting settings $value = get_option($_option, $default[0]); if ($option == 'body_rewrite_types' && empty($value) && !is_multisite()) { $value = $default[0]; } if ($option == 'hashify_file_name' && in_array($this->get("sm.mode"), array( 'stateless', 'ephemeral' ) )) { $value = true; } // If constant is set then override by constant if(is_array($constant)){ foreach($constant as $old_const => $new_const){ if(defined($new_const)){ $value = constant($new_const); $this->set( "sm.readonly.{$option}", "constant" ); break; } if(is_string($old_const) && defined($old_const)){ $value = constant($old_const); $this->bootstrap->errors->add( array( 'key' => $new_const, 'title' => sprintf( __( "%s: Deprecated Notice (%s)", $this->bootstrap->domain ), $this->bootstrap->name, $new_const ), 'message' => sprintf(__("%s constant is deprecated, please use %s instead.", $this->bootstrap->domain), $old_const, $new_const), ), 'notice' ); $this->set( "sm.readonly.{$option}", "constant" ); break; } } } elseif(defined($constant)){ $value = constant($constant); $this->set( "sm.readonly.{$option}", "constant" ); } // Getting network settings if(is_multisite() && $option != 'organize_media' && !$this->get( "sm.readonly.{$option}")){ $network = get_site_option( $_option, $default[1] ); // If network settings available then override by network settings. if($network || is_network_admin()){ $value = $network; if(!is_network_admin()) $this->set( "sm.readonly.{$option}", "network" ); } } // Converting to string true false for angular. if(is_bool($value)){ $value = $value === true ? "true" : "false"; } $this->set( "sm.$option", $value); } // Network only settings, to hide settings page foreach ($this->network_only_settings as $option => $array) { $value = ''; $_option = 'sm_' . $option; $constant = $array[0]; // Constant name $default = $array[1]; // Default value // If constant is set then override by constant if(is_array($constant)){ foreach($constant as $old_const => $new_const){ if(defined($new_const)){ $value = constant($new_const); break; } if(is_string($old_const) && defined($old_const)){ $value = constant($old_const); trigger_error(__(sprintf("%s constant is deprecated, please use %s instead.", $old_const, $new_const)), E_USER_WARNING); break; } } } elseif(defined($constant)){ $value = constant($constant); } // Getting network settings elseif(is_multisite()){ $value = get_site_option( $_option, $default ); } // Converting to string true false for angular. if(is_bool($value)){ $value = $value === true ? "true" : "false"; } $this->set( "sm.$option", $value); } /** * JSON key file path */ /* Use constant value for JSON key file path, if set. */ if (defined('WP_STATELESS_MEDIA_KEY_FILE_PATH') || $google_app_key_file !== false) { /* Maybe fix the path to p12 file. */ $key_file_path = (defined('WP_STATELESS_MEDIA_KEY_FILE_PATH')) ? WP_STATELESS_MEDIA_KEY_FILE_PATH : $google_app_key_file; if( !empty( $key_file_path ) ) { $upload_dir = wp_upload_dir(); /* Check if file exists */ switch( true ) { /* Determine if default path is correct */ case (file_exists($key_file_path)): /* Path is correct. Do nothing */ break; /* Look using WP root. */ case (file_exists( ABSPATH . $key_file_path ) ): $key_file_path = ABSPATH . $key_file_path; break; /* Look in wp-content dir */ case (file_exists( WP_CONTENT_DIR . $key_file_path ) ): $key_file_path = WP_CONTENT_DIR . $key_file_path; break; /* Look in uploads dir */ case (file_exists( wp_normalize_path( $upload_dir[ 'basedir' ] ) . '/' . $key_file_path ) ): $key_file_path = wp_normalize_path( $upload_dir[ 'basedir' ] ) . '/' . $key_file_path; break; /* Look using Plugin root */ case (file_exists($this->bootstrap->path( $key_file_path, 'dir') ) ): $key_file_path = $this->bootstrap->path( $key_file_path, 'dir' ); break; } if(is_readable($key_file_path)) { $this->set( 'sm.key_json', file_get_contents($key_file_path) ); if(defined('WP_STATELESS_MEDIA_KEY_FILE_PATH')) $this->set( "sm.readonly.key_json", "constant" ); else $this->set("sm.readonly.key_json", "environment"); } } } $this->set( 'sm.strings', $this->strings ); } /** * Remove settings * @param bool $network */ public function reset($network = false) { foreach ($this->settings as $option => $array) { if($option == 'organize_media') continue; $_option = 'sm_' . $option; if($network && current_user_can('manage_network')){ delete_site_option($_option); delete_option($_option); } else{ delete_option($_option); } } foreach ($this->network_only_settings as $option => $array) { $_option = 'sm_' . $option; if($network && current_user_can('manage_network')){ delete_site_option($_option); delete_option($_option); } } $this->set('sm', []); $this->refresh(); } /** * Replacing wildcards with real values * @param $root_dir * @param bool $regex * @param array $current_values * @return mixed|null|string|string[] * */ public function root_dir_wildcards( $root_dir, $regex = false, $current_values = [] ) { $not_allowed_char = '/[^A-Za-z0-9\/_.]/'; $wildcards = apply_filters('wp_stateless_root_dir_wildcard', $this->wildcards); if($regex){ $root_dir = preg_quote($root_dir); $not_allowed_char = '/[^A-Za-z0-9\/_.\.\-\\\\{}]/'; } if ( is_array( $wildcards ) && !empty( $wildcards ) ) { foreach ($wildcards as $wildcard => $values) { if (!empty($wildcard)) { $replace = $values[0]; if($regex){ $replace = isset($values[3]) ? $values[3] : preg_quote($values[0]); } if ( isset($current_values[$wildcard]) ) { $replace = $current_values[$wildcard]; } $root_dir = str_replace($wildcard, $replace, $root_dir); } } } //removing all special chars except slash $root_dir = preg_replace($not_allowed_char, '', $root_dir); $root_dir = preg_replace('/(\/+)/', '/', $root_dir); $root_dir = trim( $root_dir, '/ ' ); // Remove any forward slash and empty space. return $root_dir; } /** * Parse path by wildcards and return array ('wildcard' => 'value') * The perpose of this filter is to return Y/M or other dynamic fields from the file path. * For now only Y/M is dynamic. We will get it via using regex. * @param $path * @return array */ public function parse_root_dir_wildcards ( $path ) { $result = []; /** * removing GS host from path */ $gs_url = $this->bootstrap->get_gs_host(); if( 0 === strpos( $path, $gs_url . '/' ) ) { $path = substr( $path, strlen( $gs_url . '/' ) ); } /** * removing filename and last slash */ $path = untrailingslashit( str_replace(basename($path), '', $path) ); $wildcards = apply_filters('wp_stateless_root_dir_wildcard', $this->wildcards); /** * Checking if a wildcard have regex field in it. * Then return the matching value using regex. */ foreach ($wildcards as $key => $value) { if(isset($value[3])){ if(preg_match("@" . $value[3] . "@", $path, $matches)){ $result[$key] = $matches[0]; } } } return $result; } /** * Add menu options */ public function admin_menu() { $key_json = $this->get('sm.key_json'); if($this->get('sm.hide_setup_assistant') != 'true' && empty($key_json) ){ $this->setup_wizard_ui = add_media_page( __( 'Stateless Setup', $this->bootstrap->domain ), __( 'Stateless Setup', $this->bootstrap->domain ), 'manage_options', 'stateless-setup', array($this, 'setup_wizard_interface') ); } if($this->get('sm.hide_settings_panel') != 'true'){ $this->stateless_settings = add_media_page( __( 'Stateless Settings', $this->bootstrap->domain ), __( 'Stateless Settings', $this->bootstrap->domain ), 'manage_options', 'stateless-settings', array($this, 'settings_interface') ); } } /** * Add menu options * @param $slug */ public function network_admin_menu($slug) { $this->setup_wizard_ui = add_submenu_page( 'settings.php', __( 'Stateless Setup', $this->bootstrap->domain ), __( 'Stateless Setup', $this->bootstrap->domain ), 'manage_options', 'stateless-setup', array($this, 'setup_wizard_interface') ); $this->stateless_settings = add_submenu_page( 'settings.php', __( 'Stateless Settings', $this->bootstrap->domain ), __( 'Stateless Settings', $this->bootstrap->domain ), 'manage_options', 'stateless-settings', array($this, 'settings_interface') ); } /** * Draw interface */ public function settings_interface() { $wildcards = apply_filters('wp_stateless_root_dir_wildcard', $this->wildcards); $wildcard_year_month = '%date_year/date_month%'; $root_dir = $this->get( 'sm.root_dir' ); $use_year_month = (strpos($root_dir, $wildcard_year_month) !== false) ?: false; /** * removing year/month wildcard */ if ($use_year_month) { $root_dir = str_replace($wildcard_year_month, '%YM%', $root_dir); } /** * preparing array with wildcards */ $root_dir_values = explode('/', $root_dir); /** * adding year/month wildcard */ if ($use_year_month) { if ( !empty($root_dir_values) ) { foreach( $root_dir_values as $k=>$root_dir_value ) { if ( $root_dir_value == '%YM%' ) { $root_dir_values[$k] = $wildcard_year_month; } } } else { $root_dir_values[] = $wildcard_year_month; } } /** * first slash */ array_unshift($root_dir_values , '/'); /** * removing empty values */ $root_dir_values = array_filter($root_dir_values); /** * merging user's wildcards with default values */ if (!empty($root_dir_values)) { $wildcards = array_merge(array_flip($root_dir_values), $wildcards); } include $this->bootstrap->path( '/static/views/settings_interface.php', 'dir' ); } /** * Draw interface */ public function regenerate_interface() { include $this->bootstrap->path( '/static/views/regenerate_interface.php', 'dir' ); } /** * Draw interface */ public function setup_wizard_interface() { include ud_get_stateless_media()->path( '/static/views/setup_wizard_interface.php', 'dir' ); } /** * Handles saving SM data. * * @author alim@UD */ public function save_media_settings(){ if(isset($_POST['action']) && $_POST['action'] == 'stateless_settings' && wp_verify_nonce( $_POST['_smnonce'], 'wp-stateless-settings' )){ $settings = apply_filters('stateless::settings::save', $_POST['sm']); $root_dir_value = false; foreach ( $settings as $name => $value ) { /** * root_dir settings */ if ( 'root_dir' == $name && is_array($value) ) { //managed in WP-Stateless settings (via Bucket Folder control) if ( in_array('%date_year/date_month%', $value)) { update_option( 'uploads_use_yearmonth_folders', '1' ); } else { update_option( 'uploads_use_yearmonth_folders', '0' ); } /** * preparing path from tags */ $value = implode('/', $value); $root_dir_value = true; } $option = 'sm_'. $name; if($name == 'organize_media'){ $option = 'uploads_use_yearmonth_folders'; } elseif($name == 'key_json'){ $value = stripslashes($value); } // Be sure to cleanup values before saving $value = trim($value); if(is_network_admin()){ update_site_option( $option, $value ); } else{ update_option( $option, $value ); } } if ( !$root_dir_value ) { if(is_network_admin()){ update_site_option( 'sm_root_dir', '' ); } else{ update_option( 'sm_root_dir', '' ); } } $this->bootstrap->flush_transients(); $this->refresh(); } } /** * Wrapper for setting value. * @param string $key * @param bool $value * @param bool $bypass_validation * @return \UsabilityDynamics\Settings */ public function set( $key = '', $value = false, $bypass_validation = false ) { return parent::set( $key, $value, $bypass_validation ); } } } }