diff --git a/app/plugins/CoreServer/resources/locales/en_US/core_server.po b/app/plugins/CoreServer/resources/locales/en_US/core_server.po index 5a8660f67..efa7ef802 100644 --- a/app/plugins/CoreServer/resources/locales/en_US/core_server.po +++ b/app/plugins/CoreServer/resources/locales/en_US/core_server.po @@ -37,13 +37,18 @@ msgstr "MS SQL Server" msgid "enumeration.RdbmsTypeEnum.MY" msgstr "MySQL" -# XXX Not yet supported -#msgid "enumeration.RdbmsTypeEnum.OR" -#msgstr "Oracle" +msgid "enumeration.RdbmsTypeEnum.OR" +msgstr "Oracle" msgid "enumeration.RdbmsTypeEnum.PG" msgstr "Postgres" +msgid "error.SqlServers.oracle.enabled" +msgstr "Oracle support is not enabled" + +msgid "error.SqlServers.oracle.plugin" +msgstr "OracleClient plugin is not loaded" + msgid "field.SqlServers.databas" msgstr "Database Name" @@ -54,5 +59,11 @@ msgstr "Hostname" msgid "field.SqlServers.password" msgstr "Password" +msgid "field.SqlServers.port" +msgstr "Port" + +msgid "field.SqlServers.port.desc" +msgstr "Specify the port only if a non-standard port number is in use" + msgid "field.SqlServers.type" msgstr "RDBMS Type" diff --git a/app/plugins/CoreServer/src/CoreServerPlugin.php b/app/plugins/CoreServer/src/CoreServerPlugin.php index 6711a5cf7..310c7ccef 100644 --- a/app/plugins/CoreServer/src/CoreServerPlugin.php +++ b/app/plugins/CoreServer/src/CoreServerPlugin.php @@ -26,6 +26,19 @@ class CoreServerPlugin extends BasePlugin */ public function bootstrap(PluginApplicationInterface $app): void { + try { + // Load the OracleDriver plugin if Oracle support is enabled. We require + // the enable flag so we don't have to try loading the plugin on every + // page load when most deployments aren't going to have it. + $oracleEnabled = \Cake\Core\Configure::read('registry.database.oracle.enable'); + + if($oracleEnabled === true) { + $app->addPlugin(\CakeDC\OracleDriver\Plugin::class, ['bootstrap' => true]); + } + } + catch(\Error $e) { + debug($e); + } } /** diff --git a/app/plugins/CoreServer/src/Lib/Enum/RdbmsTypeEnum.php b/app/plugins/CoreServer/src/Lib/Enum/RdbmsTypeEnum.php index a6be23625..92d036f05 100644 --- a/app/plugins/CoreServer/src/Lib/Enum/RdbmsTypeEnum.php +++ b/app/plugins/CoreServer/src/Lib/Enum/RdbmsTypeEnum.php @@ -37,9 +37,7 @@ class RdbmsTypeEnum extends StandardEnum { // This currently aligns with Cake supported servers const MariaDB = 'MA'; const MySQL = 'MY'; -// XXX Oracle requires a community plugin: https://github.com/CakeDC/cakephp-oracle-driver -// We could maybe ship with this? - // const Oracle = 'OR'; + const Oracle = 'OR'; const Postgres = 'PG'; const SQLite = 'LT'; const SqlServer = 'MS'; diff --git a/app/plugins/CoreServer/src/Model/Table/SqlServersTable.php b/app/plugins/CoreServer/src/Model/Table/SqlServersTable.php index 98046d2d0..7d3c9aaa1 100644 --- a/app/plugins/CoreServer/src/Model/Table/SqlServersTable.php +++ b/app/plugins/CoreServer/src/Model/Table/SqlServersTable.php @@ -29,6 +29,7 @@ namespace CoreServer\Model\Table; +use Cake\Core\Plugin; use Cake\Datasource\ConnectionManager; use Cake\ORM\Query; use Cake\ORM\RulesChecker; @@ -93,6 +94,24 @@ public function initialize(array $config): void { ]); } + /** + * Define business rules. + * + * @since COmanage Registry v5.0.0 + * @param RulesChecker $rules RulesChecker object + * @return RulesChecker + */ + + public function buildRules(RulesChecker $rules): RulesChecker { + // This is not an Application Rule per se, but the Oracle plugin must + // be enabled if the Server Type is set to Oracle. + $rules->add([$this, 'ruleOracleEnabled'], + 'oracleEnabled', + ['errorField' => 'type']); + + return $rules; + } + /** * Establish a connection (via Cake's ConnectionManager) to the specified SQL server. * @@ -115,6 +134,7 @@ public function connect(int $serverId, string $name): bool { $dbmap = [ RdbmsTypeEnum::MariaDB => 'Mysql', RdbmsTypeEnum::MySQL => 'Mysql', + RdbmsTypeEnum::Oracle => 'Oracle', RdbmsTypeEnum::Postgres => 'Postgres', RdbmsTypeEnum::SQLite => 'Sqlite', RdbmsTypeEnum::SqlServer => 'Sqlserver' @@ -133,6 +153,34 @@ public function connect(int $serverId, string $name): bool { 'timezone' => 'UTC' ]; + if(!empty($server->sql_server->port) && is_numeric($server->sql_server->port)) { + $dbconfig['port'] = $server->sql_server->port; + } + + if($server->sql_server->type == RdbmsTypeEnum::Oracle) { + $oracleEnabled = \Cake\Core\Configure::read('registry.database.oracle.enable'); + + if($oracleEnabled) { + // We don't test that the plugin is available here, an error should be thrown + // when we try to connect. + + $dbconfig['className'] = 'CakeDC\OracleDriver\Database\OracleConnection'; + $dbconfig['driver'] = 'CakeDC\OracleDriver\Database\Driver\OracleOCI'; # For OCI8 + + // Use 'CakeDC\\OracleDriver\\Database\\Driver\\OraclePDO' for PDO_OCI, but CakeDC + // recommends OCI8 + // The plugin documentation says certain features are enabled at v12, so we hard + // code that version to simplify configuration. As of this writing, Oracle 11g + // is the oldest supported version, but 12c dates back to July 2013, so it seems + // reasonable to require v12 (at least for now). Note Oracle changed their release + // numbers to be based on calendar years, retroactively assigning 18c (12.2.0.2) + // and 19c (12.2.0.3), so this approach should work at least for those versions. + // Since semantic versioning is not being used, it's unclear when backwards + // incompatible changes might be introduced, or if the CakeDC plugin even cares. + $dbconfig['server_version'] = 12; + } + } + // We need to drop the existing configuration before we can reconfigure it ConnectionManager::drop($name); @@ -141,6 +189,33 @@ public function connect(int $serverId, string $name): bool { return true; } + /** + * Application Rule to determine if Oracle is enabled (if selected). + * + * @since COmanage Registyr v5.0.0 + * @param Entity $entity Entity to be validated + * @param array $options Application rule options + * @return mixed true if the Rule check passes, or an error string otherwise + */ + + public function ruleOracleEnabled($entity, $options) { + if($entity->type == RdbmsTypeEnum::Oracle) { + $oracleEnabled = \Cake\Core\Configure::read('registry.database.oracle.enable'); + + if(!$oracleEnabled) { + return __d('core_server', 'error.SqlServers.oracle.enabled'); + } + + $pluginLoaded = Plugin::isLoaded('OracleDriver'); + + if(!$pluginLoaded) { + return __d('core_server', 'error.SqlServers.oracle.plugin'); + } + } + + return true; + } + /** * Set validation rules. * @@ -164,6 +239,11 @@ public function validationDefault(Validator $validator): Validator { $this->registerStringValidation($validator, $schema, 'hostname', true); + $validator->add('port', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->allowEmptyString('port'); + $this->registerStringValidation($validator, $schema, 'databas', true); $this->registerStringValidation($validator, $schema, 'username', false); diff --git a/app/plugins/CoreServer/src/config/plugin.json b/app/plugins/CoreServer/src/config/plugin.json index 876ade57f..769d9aa12 100644 --- a/app/plugins/CoreServer/src/config/plugin.json +++ b/app/plugins/CoreServer/src/config/plugin.json @@ -12,6 +12,7 @@ "server_id": {}, "type": { "type": "string", "size": 2 }, "hostname": { "type": "string", "size": 128 }, + "port": { "type": "integer" }, "databas": { "type": "string", "size": 128 }, "username": { "type": "string", "size": 128 }, "password": { "type": "string", "size": 80 } diff --git a/app/plugins/CoreServer/templates/SqlServers/fields.inc b/app/plugins/CoreServer/templates/SqlServers/fields.inc index aaa97647e..0d0a13d04 100644 --- a/app/plugins/CoreServer/templates/SqlServers/fields.inc +++ b/app/plugins/CoreServer/templates/SqlServers/fields.inc @@ -31,6 +31,8 @@ if($vv_action == 'edit') { print $this->Field->control('hostname'); + print $this->Field->control('port'); + print $this->Field->control('databas'); print $this->Field->control('username');