errors = new Errors( array_merge( $args, array(
'type' => $this->type
) ) );
//** Determine if Composer autoloader is included and modules classes are up to date */
$this->composer_dependencies();
//** Determine if plugin/theme requires or recommends another plugin(s) */
$this->plugins_dependencies();
// Maybe run install or upgrade processes.
$this->maybe_run_upgrade_process();
//** Set install/upgrade pages if needed */
$this->define_splash_pages();
//** Maybe need to show UD splash page. Used static functions intentionaly. */
if ( !has_action( 'admin_init', array( Dashboard::get_instance(), 'maybe_ud_splash_page' ) ) ) {
add_action( 'admin_init', array( Dashboard::get_instance(), 'maybe_ud_splash_page' ) );
}
if ( !has_action( 'admin_menu', array( Dashboard::get_instance(), 'add_ud_splash_page') ) ) {
add_action( 'admin_menu', array( Dashboard::get_instance(), 'add_ud_splash_page') );
}
add_action( 'wp_ajax_ud_bootstrap_dismiss_notice', array( $this, 'ud_bootstrap_dismiss_notice' ) );
}
/**
* Initialize application.
* Redeclare the method in final class!
*
* @author peshkov@UD
*/
public function init() {}
/**
* Determine if errors exist
* Just wrapper.
*/
public function has_errors() {
return $this->errors->has_errors();
}
/**
* @param string $key
* @param mixed $value
*
* @author peshkov@UD
* @return \UsabilityDynamics\Settings
*/
public function set( $key = null, $value = null ) {
if( !is_object( $this->settings ) || !is_callable( array( $this->settings, 'set' ) ) ) {
return false;
}
return $this->settings->set( $key, $value );
}
/**
* @param string $key
* @param mixed $default
*
* @author peshkov@UD
* @return \UsabilityDynamics\type
*/
public function get( $key = null, $default = null ) {
if( !is_object( $this->settings ) || !is_callable( array( $this->settings, 'get' ) ) ) {
return $default;
}
return $this->settings->get( $key, $default );
}
/**
* Returns specific schema from composer.json file.
*
* @param string $file Path to file
* @author peshkov@UD
* @return mixed array or false
*/
public function get_schema( $key = '' ) {
if( $this->schema === null ) {
if( !empty( $this->schema_path ) && file_exists( $this->schema_path ) ) {
$this->schema = (array)\UsabilityDynamics\Utility::l10n_localize( json_decode( file_get_contents( $this->schema_path ), true ), (array)$this->get_localization() );
}
}
//** Break if composer.json does not exist */
if( !is_array( $this->schema ) ) {
return false;
}
//** Resolve dot-notated key. */
if( strpos( $key, '.' ) ) {
$current = $this->schema;
$p = strtok( $key, '.' );
while( $p !== false ) {
if( !isset( $current[ $p ] ) ) {
return false;
}
$current = $current[ $p ];
$p = strtok( '.' );
}
return $current;
}
//** Get default key */
else {
return isset( $this->schema[ $key ] ) ? $this->schema[ $key ] : false;
}
}
/**
* Return localization's list.
*
* Example:
* If schema contains l10n.{key} values:
*
* { 'config': 'l10n.hello_world' }
*
* the current function should return something below:
*
* return array(
* 'hello_world' => __( 'Hello World', $this->domain ),
* );
*
* @author peshkov@UD
* @return array
*/
public function get_localization() {
return array();
}
/**
* Determine if product is just installed or upgraded
* and run install/upgrade processes
*
* @author peshkov@UD
*/
protected function maybe_run_upgrade_process() {
//** Determine what to show depending on version installed */
$version = get_option($this->slug . '-current-version', 0);
$this->old_version = $version;
//** Just installed */
if (!$version) {
/* Run Install handlers */
add_action( 'plugins_loaded', array( $this, '_run_install_process' ), 0 );
}
//** Upgraded */
elseif (version_compare($version, $this->args['version']) == -1) {
/* Run Upgrade handlers */
add_action( 'plugins_loaded', array( $this, '_run_upgrade_process' ), 0 );
}
// Need to save current version on plugins_loaded action,
// unless _run_install_process and _run_upgrade_process not get called.
add_action( 'plugins_loaded', array( $this, 'save_version_no' ), 100 );
}
/**
* Saving version no to database.
*
*/
public function save_version_no($value=''){
update_option( $this->slug . '-current-version', $this->args['version'] );
}
/**
* Installation Handler
* Internal method. Use run_install_process() instead
*/
public function _run_install_process() {
/* Delete 'Install/Upgrade' notice 'dismissed' information */
delete_option( sanitize_key( 'dismiss_' . $this->slug . '_' . str_replace( '.', '_', $this->args['version'] ) . '_notice' ) );
/* Delete 'Bootstrap' notice 'dismissed' information */
delete_option( 'dismissed_notice_' . sanitize_key( $this->name ) );
$this->run_install_process();
}
/**
* Upgrade Handler
* Internal method. Use run_upgrade_process() instead
*/
public function _run_upgrade_process() {
/* Delete 'Install/Upgrade' notice 'dismissed' information */
delete_option( sanitize_key( 'dismiss_' . $this->slug . '_' . str_replace( '.', '_', $this->args['version'] ) . '_notice' ) );
/* Delete 'Bootstrap' notice 'dismissed' information */
delete_option( 'dismissed_notice_' . sanitize_key( $this->name ) );
$this->run_upgrade_process();
}
/**
* Run Install Process.
*
* Re-define the function in child.
*/
public function run_install_process() {}
/**
* Run Upgrade Process.
*
* Re-define the function in child.
*/
public function run_upgrade_process() {}
/**
* Define splash pages for plugins if needed
* And Renders Admin Notice about installed product
*
* @return boolean
* @author korotkov@UD
* @author peshkov@UD
*/
public function define_splash_pages() {
//** If not defined in schemas or not determined - skip */
if ( !$splashes = $this->get_schema( 'extra.splashes' ) ) {
return false;
}
foreach( (array)$splashes as $splash => $shortpath ) {
$path = $this->type == 'theme' ? get_template_directory() . '/' . ltrim( $shortpath, '/\\' ) : $this->path( $shortpath, 'dir' );
if( !file_exists( $path ) ) {
unset( $splashes[ $splash ] );
}
}
//** If no splash templates files or missed 'install' splash - skip */
if( empty( $splashes ) || !isset( $splashes[ 'install' ] ) ) {
return false;
}
$page = false;
//** Determine what to show depending on version installed */
$version = get_option( $this->slug . '-splash-version', 0 );
//** Just installed */
if( !$version ) {
$page = 'install';
}
//** Upgraded */
elseif ( version_compare( $version, $this->args['version'] ) == -1 ) {
if( isset( $splashes[ 'upgrade' ] ) ) {
$page = 'upgrade';
} else {
$page = 'install';
}
}
//** In other case do not do this */
else {
/**
* Maybe Render Install Notice
* about current instance
*/
$option = sanitize_key( 'dismiss_' . $this->slug . '_' . str_replace( '.', '_', $this->args['version'] ) . '_notice' );
if(
isset( $_REQUEST[ 'page' ] ) &&
$_REQUEST[ 'page' ] == Dashboard::get_instance()->page_slug &&
isset( $_REQUEST[ 'slug' ] ) &&
$_REQUEST[ 'slug' ] == $this->slug
) {
/** Dismiss Admin Notice */
if( isset( $_REQUEST[ 'dismiss' ] ) ) {
update_option( $option, array(
'slug' => $this->slug,
'type' => $this->type,
'version' => $this->args['version']
) );
if( !function_exists( 'wp_redirect' ) ) {
require_once( ABSPATH . 'wp-includes/pluggable.php' );
}
if( !empty( $_SERVER[ 'HTTP_REFERER' ] ) ) {
wp_redirect( $_SERVER[ 'HTTP_REFERER' ] );
} else {
wp_redirect( admin_url( 'plugins.php' ) );
}
exit;
}
/** Add information about product to Dashboard page */
else {
if( isset( $splashes[ 'upgrade' ] ) ) {
$page = 'upgrade';
} else {
$page = 'install';
}
}
}
if( !get_option( $option ) ) {
add_action( 'admin_notices', array( $this, 'render_upgrade_notice' ), 1 );
}
if( empty( $page ) ) {
return false;
}
}
$content = $this->root_path . ltrim( $splashes[$page], '/\\' );
//** Abort if no files exist */
if ( !file_exists( $content ) ) {
return false;
}
//** Push data to temp transient */
$_current_pages_to_show = get_transient( Dashboard::get_instance()->transient_key );
//** If empty - create */
if ( !$_current_pages_to_show ) {
set_transient( Dashboard::get_instance()->transient_key, array(
$this->slug => array(
'name' => $this->name,
'content' => $content,
'version' => $this->args['version']
)
), 30 );
}
//** If not empty - update */
else {
$_current_pages_to_show[$this->slug] = array(
'name' => $this->name,
'content' => $content,
'version' => $this->args['version']
);
set_transient( Dashboard::get_instance()->transient_key, $_current_pages_to_show, 30 );
}
set_transient( Dashboard::get_instance()->need_splash_key, Dashboard::get_instance()->transient_key, 30 );
}
/**
* Renders Upgrade Notice.
*
*/
public function render_upgrade_notice() {
if( $this->type == 'theme' ) {
if( !current_user_can( 'switch_themes' ) ) {
return;
}
$icon = file_exists( get_template_directory() . '/static/images/icon.png' ) ? get_template_directory_uri() . '/static/images/icon.png' : false;
} else {
if( !current_user_can( 'activate_plugins' ) ) {
return;
}
$icon = file_exists( $this->path( 'static/images/icon.png', 'dir' ) ) ? $this->path( 'static/images/icon.png', 'url' ) : false;
}
ob_start();
$vars = apply_filters( 'ud::bootstrap::upgrade_notice::vars', array(
'content' => false,
'icon' => $icon,
'name' => $this->name,
'type' => $this->type,
'slug' => $this->slug,
'version' => $this->args['version'],
'dashboard_link' => admin_url( 'index.php?page='. Dashboard::get_instance()->page_slug . '&slug=' . $this->slug ),
'dismiss_link' => admin_url( 'index.php?page='. Dashboard::get_instance()->page_slug . '&slug=' . $this->slug . '&dismiss=1' ),
'home_link' => !empty( $this->schema[ 'homepage' ] ) ? $this->schema[ 'homepage' ] : false,
) );
extract( $vars );
$content = ob_get_clean();
echo apply_filters( 'ud::bootstrap::upgrade_notice::template', $content, $this->slug, $vars );
}
/**
* Check plugins requirements
*
* @author peshkov@UD
*/
public function check_plugins_requirements() {
//** Determine if we have TGMA Plugin Activation initialized. */
$is_tgma = $this->is_tgma;
if( $is_tgma ) {
$tgma = TGM_Plugin_Activation::get_instance();
//** Maybe get TGMPA notices. */
$notices = $tgma->notices( get_class( $this ) );
if( !empty( $notices[ 'messages' ] ) && is_array( $notices[ 'messages' ] ) ) {
$error_links = false;
$message_links = false;
foreach( $notices[ 'messages' ] as $m ) {
if( $m[ 'type' ] == 'error' ) $error_links = true;
elseif( $m[ 'type' ] == 'message' ) $message_links = true;
$this->errors->add( $m[ 'value' ], $m[ 'type' ] );
}
//** Maybe add footer action links to errors and|or notices block. */
if( !empty( $notices[ 'links' ] ) && is_array( $notices[ 'links' ] ) ) {
foreach( $notices[ 'links' ] as $type => $links ) {
foreach( $links as $link ) {
$this->errors->add_action_link( $link, $type );
}
}
}
}
}
}
/**
* Maybe determines if Composer autoloader is included and modules classes are up to date
*
* @author peshkov@UD
*/
private function composer_dependencies() {
$dependencies = $this->get_schema( 'extra.schemas.dependencies.modules' );
if( !empty( $dependencies ) && is_array( $dependencies ) ) {
foreach( $dependencies as $module => $classes ) {
if( !empty( $classes ) && is_array( $classes ) ) {
foreach( $classes as $class => $v ) {
if( !class_exists( $class ) ) {
$this->errors->add( sprintf( __( 'Module %s is not installed or the version is old, class %s does not exist.', $this->domain ), $module, $class ) );
continue;
}
if ( '*' != trim( $v ) && ( !property_exists( $class, 'version' ) || $class::$version < $v ) ) {
$this->errors->add( sprintf( __( 'Module %s should be updated to the latest version, class %s must have version %s or higher.', $this->domain ), $module, $class, $v ) );
}
}
}
}
}
}
/**
* Determine if plugin/theme requires or recommends another plugin(s)
*
* @author peshkov@UD
*/
private function plugins_dependencies() {
/**
* Dependencies must be checked before plugins_loaded hook to prevent issues!
*
* The current condition fixes incorrect behaviour on custom 'Install Plugins' page
* after activation plugin which has own dependencies.
*
* The condition belongs to WordPress 4.3 and higher.
*/
if( did_action( 'plugins_loaded' ) && $this->type == 'plugin' ) {
return;
}
$plugins = $this->get_schema( 'extra.schemas.dependencies.plugins' );
if( !empty( $plugins ) && is_array( $plugins ) ) {
$tgma = TGM_Plugin_Activation::get_instance();
foreach( $plugins as $plugin ) {
$plugin[ '_referrer' ] = get_class( $this );
$plugin[ '_referrer_name' ] = $this->name;
$tgma->register( $plugin );
}
$this->is_tgma = true;
}
}
/**
* Defines License Client if 'licenses' schema is set
*
* @author peshkov@UD
*/
protected function define_license_client() {
//** Break if we already have errors to prevent fatal ones. */
if( $this->has_errors() ) {
return false;
}
//** Be sure we have licenses scheme to continue */
$schema = $this->get_schema( 'extra.schemas.licenses.client' );
if( !$schema ) {
return false;
}
//** Licenses Manager */
if( !class_exists( '\UsabilityDynamics\UD_API\Bootstrap' ) ) {
$this->errors->add( __( 'Class \UsabilityDynamics\UD_API\Bootstrap does not exist. Be sure all required plugins and (or) composer modules installed and activated.', $this->domain ) );
return false;
}
$args = $this->args;
$args = array_merge( $args, array(
'type' => $this->type,
'name' => $this->name,
'slug' => $this->slug,
'referrer_slug' => $this->slug,
'domain' => $this->domain,
'errors_callback' => array( $this->errors, 'add' ),
), $schema );
if( empty( $args[ 'screen' ] ) ) {
$this->errors->add( __( 'Licenses client can not be activated due to invalid \'licenses\' schema.', $this->domain ) );
}
$this->client = new \UsabilityDynamics\UD_API\Bootstrap( $args );
}
/**
* Defines License Manager if 'license' schema is set
*
* @author peshkov@UD
*/
public function define_license_manager() {
//** Break if we already have errors to prevent fatal ones. */
if( $this->has_errors() ) {
return false;
}
//** Be sure we have license scheme to continue */
$schema = $this->get_schema( 'extra.schemas.licenses.product' );
if( !$schema ) {
return false;
}
if( empty( $schema[ 'product_id' ] ) || ( empty( $schema[ 'referrer' ] ) && $this->type !== 'theme' ) ) {
$this->errors->add( __( 'Product requires license, but product ID and (or) referrer is undefined. Please, be sure, that license schema has all required data.', $this->domain ), 'message' );
}
$schema = array_merge( (array)$schema, array(
'type' => $this->type,
'name' => $this->name,
'boot_file' => $this->boot_file,
'errors_callback' => array( $this->errors, 'add' )
) );
//** Licenses Manager */
if( !class_exists( '\UsabilityDynamics\UD_API\Manager' ) ) {
//$this->errors->add( __( 'Class \UsabilityDynamics\UD_API\Manager does not exist. Be sure all required plugins installed and activated.', $this->domain ), 'message' );
return false;
}
$this->license_manager = new \UsabilityDynamics\UD_API\Manager( $schema );
return true;
}
public function ud_bootstrap_dismiss_notice() {
$response = array(
'success' => '0',
'error' => __( 'There was an error in request.', $this->domain ),
);
$error = false;
if( empty( $_POST['key'] ) ||
empty( $_POST['slug'] ) ||
empty( $_POST['type'] ) ||
empty( $_POST['version'] )
) {
$response['error'] = __( 'Invalid values', $this->domain );
$error = true;
}
if ( ! $error && update_option( ( $_POST['key'] ), array(
'slug' => $_POST['slug'],
'type' => $_POST['type'],
'version' => $_POST['version'],
) ) ) {
$response['success'] = '1';
}
wp_send_json( $response );
}
}
}
}