* @link https://TheTempusProject.com * @license https://opensource.org/licenses/MIT [MIT LICENSE] */ namespace TheTempusProject\Classes; use TheTempusProject\Houdini\Classes\Navigation; use TheTempusProject\Houdini\Classes\Filters; use TheTempusProject\TheTempusProject as App; use TheTempusProject\Canary\Bin\Canary as Debug; use TheTempusProject\Bedrock\Classes\Database; class Plugin { public $required_models = []; public $models = []; public $errors = []; // Global Properties public $module; public $initialized = false; public static $installer; public static $db; public static $installFlags = PLUGIN_INSTALL_FLAGS; public static $pluginFolders = []; public static $pluginsAvailable = []; public static $pluginsActive = []; // Basic Required Info public $pluginName = 'Default Plugin Name'; public $pluginAuthor = 'TheTempusProject'; public $pluginWebsite = 'https://TheTempusProject.com'; public $pluginVersion = 0.0; public $pluginDescription = 'The Default Plugin Description'; // Front-end Properties public $admin_links = []; public $main_links = []; public $footer_links = []; public $filters = []; // Install Related public $configName = ''; public $configMatrix = []; public $resourceMatrix = []; public $preferenceMatrix = []; public $permissionMatrix = []; const PLUGIN_FLAG_MAP = [ 'preferences_installed' => 'installPreferences', 'permissions_installed' => 'installPermissions', 'configs_installed' => 'installConfigs', 'models_installed' => 'installModels', 'resources_installed' => 'installResources', ]; public function __construct( $load = false ) { if ( true === $this->initialized && false == $load ) { return; } self::$db = Database::getInstance(); if ( ! isset( self::$installer ) ) { self::$installer = new Installer(); } if ( ! isset( $this->module ) ) { $this->module = self::$installer->getModule( getClassName( $this ) ); } if ( true == $load ) { if ( $this->checkEnabled() ) { $this->loadAdminNav(); $this->loadMainNav(); $this->loadFooterNav(); $this->loadFilters(); } $this->initialized = true; } } public function install( $options ) { Debug::log( 'Installing Plugin: ' . $this->pluginName ); $module_data = []; $errors = []; foreach ( self::PLUGIN_FLAG_MAP as $flag_name => $function_name ) { if ( empty( $options[$flag_name] ) ) { // $module_data[ $flag_name ] = INSTALL_STATUS_SKIPPED; continue; } if ( 'installModels' != $function_name ) { $result = $this->$function_name( $options ); } else { $model_options = $this->convertPluginOptionsToModelOptions( $options ); $result = $this->$function_name( $model_options ); } if ( empty( $result ) ) { $errors[] = ['errorInfo' => get_class($this) . " Failed to execute $flag_name properly."]; $module_data[ $flag_name ] = INSTALL_STATUS_FAIL; continue; } if ( 'installResources' === $function_name ) { $module_data[ $flag_name ] = $result; continue; } $module_data[ $flag_name ] = INSTALL_STATUS_SUCCESS; continue; } return [ $module_data, $errors ]; } public function uninstall( $options ) { Debug::log( 'Uninstalling Plugin: ' . $this->pluginName ); $module_data = []; $errors = []; foreach ( self::PLUGIN_FLAG_MAP as $flag_name => $function_name ) { $function_name = 'un' . $function_name; if ( empty( $options[$flag_name] ) ) { $module_data[ $flag_name ] = INSTALL_STATUS_SKIPPED; continue; } if ( 'installModels' != $function_name ) { $result = $this->$function_name( $options ); } else { $model_options = $this->convertPluginOptionsToModelOptions( $options ); $result = $this->$function_name( $model_options ); } if ( empty( $result ) ) { $errors[] = ['errorInfo' => get_class($this) . " Failed to execute $flag_name properly."]; $module_data[ $flag_name ] = INSTALL_STATUS_FAIL; continue; } if ( 'uninstallResources' === $function_name ) { $module_data[ $flag_name ] = $result; continue; } $module_data[ $flag_name ] = INSTALL_STATUS_UNINSTALLED; continue; } return $errors; } public function installModels( $options ) { $class = get_class($this); $nameArray = explode( '\\', $class ); $name = array_pop( $nameArray ); $directory = PLUGIN_DIRECTORY . lcfirst($name) . DIRECTORY_SEPARATOR . 'models' . DIRECTORY_SEPARATOR; if ( ! file_exists( $directory ) ) { Debug::log( 'models directory is empty' ); return true; } $models = self::$installer->getModelList( $directory ); $error = false; foreach ( $models as $model ) { $result = self::$installer->installModel( $model, $options, true, false ); if ( $result === false ) { $error = true; continue; } } if ( $error ) { return false; } else { return true; } } public function uninstallModels( $options ) { $class = get_class($this); $nameArray = explode( '\\', $class ); $name = array_pop( $nameArray ); $directory = PLUGIN_DIRECTORY . lcfirst($name) . DIRECTORY_SEPARATOR . 'models' . DIRECTORY_SEPARATOR; if ( ! file_exists( $directory ) ) { Debug::log( 'models directory is empty' ); return true; } $models = self::$installer->getModelList( $directory ); $error = false; foreach ( $models as $model ) { $result = self::$installer->uninstallModel( $model, $options, true, false ); if ( $result === false ) { $error = true; continue; } } if ( $error ) { return false; } else { return true; } } public function installPermissions( $options = '' ) { if ( empty( $this->permissionMatrix ) ) { Debug::log( 'permissionMatrix is empty' ); return true; } $perms = new Permissions(); foreach ( $this->permissionMatrix as $name => $details ) { $perms->add( $name, $details ); } return $perms->save( true ); } public function uninstallPermissions( $options = '' ) { if ( empty( $this->permissionMatrix ) ) { Debug::log( 'permissionMatrix is empty' ); return true; } $perms = new Permissions(); foreach ( $this->permissionMatrix as $name => $details ) { $perms->remove( $name, true ); } return true; } public function installConfigs( $options = '' ) { if ( empty( $this->configMatrix ) || empty( $this->configName )) { Debug::log( 'configMatrix is empty' ); return true; } $config = new Config( CONFIG_JSON ); // should have some sort of DELTA functionality and safeguards $config->addCategory( $this->configName ); foreach ( $this->configMatrix as $name => $details ) { $config->add( $this->configName, $name, $details ); } return $config->save(); } public function uninstallConfigs( $options = '' ) { if ( empty( $this->configName ) ) { return true; } $config = new Config( CONFIG_JSON ); return $config->removeCategory( $this->configName, true, true ); } public function installPreferences( $options = '' ) { $prefs = new Preferences(); if ( empty( $this->preferenceMatrix ) ) { Debug::log( 'preferenceMatrix is empty' ); return true; } foreach ( $this->preferenceMatrix as $name => $details ) { $prefs->add( $name, $details ); } return $prefs->save( true ); } public function uninstallPreferences( $options = '' ) { if ( empty( $this->preferenceMatrix ) ) { Debug::log( 'preferenceMatrix is empty' ); return true; } $prefs = new Preferences(); foreach ( $this->preferenceMatrix as $name => $details ) { $prefs->remove( $name, true ); } return $prefs->save( true ); } public function installResources( $options = '' ) { if ( empty( $this->resourceMatrix ) ) { Debug::log( 'resourceMatrix is empty' ); return INSTALL_STATUS_NOT_REQUIRED; } $ids = []; foreach( $this->resourceMatrix as $tableName => $entries ) { foreach ( $entries as $entry ) { foreach ( $entry as $key => $value ) { if ( '{time}' == $value ) { $entry[$key] = time(); } } self::$db->insert( $tableName, $entry ); $id = self::$db->lastId(); if ( $id ) { $ids[] = $id; } } } return $ids; } public function uninstallResources( $options = '' ) { if ( empty( $this->resourceMatrix ) ) { Debug::log( 'resourceMatrix is empty' ); return true; } $ids = $this->module['resources_installed']; $data = []; foreach( $this->resourceMatrix as $tableName => $entries ) { foreach ($ids as $id) { $data[] = self::$db->delete( $tableName, [ 'ID', '=', $id ] ); } } return $data; } /** * Loaders */ public function loadAdminNav() { if ( !empty( $this->admin_links ) ) { foreach( $this->admin_links as $key => $link ) { Navigation::addLink( App::ADMIN_MENU_NAME, $link ); } } } public function loadMainNav() { if ( !empty( $this->main_links ) ) { foreach( $this->main_links as $key => $link ) { Navigation::addLink( App::MAIN_MENU_NAME, $link ); } } } public function loadFooterNav() { if ( !empty( $this->contact_footer_links ) ) { foreach( $this->contact_footer_links as $key => $link ) { Navigation::addLink( App::CONTACT_FOOTER_MENU_NAME, $link ); } } if ( !empty( $this->info_footer_links ) ) { foreach( $this->info_footer_links as $key => $link ) { Navigation::addLink( App::INFO_FOOTER_MENU_NAME, $link ); } } } public function loadFilters() { if ( ! empty( $this->filters ) ) { foreach( $this->filters as $filter ) { Filters::add( $filter['name'], $filter['find'], $filter['replace'], $filter['enabled'] ); } } } public function convertPluginOptionsToModelOptions( $options ) { $data = []; foreach (self::PLUGIN_FLAG_MAP as $pluginValue => $modelValue) { if ( isset( $options[$pluginValue] ) ) { $data[$modelValue] = $options[$pluginValue]; } } return $data; } public static function getPluginDirectories( $forceRefresh = false ) { if ( !empty( self::$pluginFolders ) && true !== $forceRefresh ) { return self::$pluginFolders; } $pluginFolders = []; if ( ! PLUGINS_ENABLED && true !== $forceRefresh ) { Debug::warn('Plugins disabled'); return $pluginFolders; } if ( ! file_exists( PLUGIN_DIRECTORY ) ) { Debug::warn("Plugins folder is missing: $dir"); return $pluginFolders; } // get a list of all plugins in the plugin directory $pluginDirectories = scandir( PLUGIN_DIRECTORY ); array_shift( $pluginDirectories ); // remove the . array_shift( $pluginDirectories ); // remove the .. foreach ( $pluginDirectories as $key => $pluginName ) { $pluginDirectory = PLUGIN_DIRECTORY . $pluginName; if ( is_file( $pluginDirectory ) ) { continue; // skip any files in the main plugin directory if they exist } // get a list of all directories in this plugin directory $pluginFolders[ $pluginName ] = []; $pluginDirectory .= DIRECTORY_SEPARATOR; $pluginDirectoryArray = scandir( $pluginDirectory ); array_shift( $pluginDirectoryArray ); // remove the . array_shift( $pluginDirectoryArray ); // remove the .. // loop over each sub-directory insider plugin directory foreach ( $pluginDirectoryArray as $key => $file ) { $currentFolder = $pluginDirectory . $file . DIRECTORY_SEPARATOR; switch ( $file ) { case 'controllers': case 'config': case 'models': case 'views': case 'templates': break; case 'forms.php': $currentFolder = rtrim( $currentFolder, DIRECTORY_SEPARATOR ); break; case 'plugin.php': $currentFolder = rtrim( $currentFolder, DIRECTORY_SEPARATOR ); break; default: continue 2; // break if we aren't looking for whatever we found } $pluginFolders[ $pluginName ][] = [ $currentFolder => $file ]; } } self::$pluginFolders = $pluginFolders; return self::$pluginFolders; } public static function getActivePlugins( $forceRefresh = false ) { if ( ! empty( self::$pluginsActive ) && true !== $forceRefresh ) { return self::$pluginsActive; } if ( ! isset( self::$installer ) ) { self::$installer = new Installer(); } $out = []; $plugins = self::$installer->getAvailablePlugins( $forceRefresh ); if ( ! empty( $plugins ) ) { foreach ( $plugins as $plugin ) { if ( !isset( $plugin->class_object )) { continue; } $installedPlugin = $plugin->class_object->module; if ( !isset( $installedPlugin['enabled'] ) || !$installedPlugin['enabled'] ) { continue; } $out[] = [ $plugin->name => $installedPlugin['class'], ]; } } self::$pluginsActive = $out; return self::$pluginsActive; } public static function enable( $name, $save = true ) { if ( ! isset( self::$installer ) ) { self::$installer = new Installer(); } $module = self::$installer->getModule( $name ); if ( empty( $module ) ) { Debug::warn( "plugin not found: $name" ); return false; } if ( ! isset( $module['enabled'] ) ) { Debug::error( "plugin enabled not set: $name" ); return false; } $module['enabled'] = true; return self::$installer->setModule( $name, $module, $save ); } public static function disable( $name, $save = true ) { if ( ! isset( self::$installer ) ) { self::$installer = new Installer(); } $module = self::$installer->getModule( $name ); if ( empty($module) ) { Debug::warn( "plugin not found: $name" ); return false; } if ( ! isset( $module['enabled'] ) ) { Debug::error( "plugin not enabled: $name" ); return false; } $module['enabled'] = false; return self::$installer->setModule( $name, $module, $save ); } public function checkEnabled() { $name = ucfirst( strtolower( $this->pluginName ) ); if ( isset( $this->module['enabled'] ) ) { return $this->module['enabled']; } Debug::warn( "install not found: {$this->pluginName}" ); return false; } }