From cf14306c2b3f82a81f8d56669a71633b4d4b5fce Mon Sep 17 00:00:00 2001 From: marvin-borner@live.com Date: Mon, 16 Apr 2018 21:09:05 +0200 Subject: Main merge to user management system - files are now at /main/public/ --- .../sprinkles/ConfigManager/src/ConfigManager.php | 40 ++++ .../src/Controller/ConfigManagerController.php | 173 +++++++++++++++ .../src/Database/Migrations/v100/SettingsTable.php | 48 ++++ .../Migrations/v101/SettingsPermissions.php | 70 ++++++ .../ConfigManager/src/Database/Models/Config.php | 70 ++++++ .../src/ServicesProvider/ServicesProvider.php | 31 +++ .../ConfigManager/src/Util/ConfigManager.php | 243 +++++++++++++++++++++ 7 files changed, 675 insertions(+) create mode 100755 main/app/sprinkles/ConfigManager/src/ConfigManager.php create mode 100755 main/app/sprinkles/ConfigManager/src/Controller/ConfigManagerController.php create mode 100755 main/app/sprinkles/ConfigManager/src/Database/Migrations/v100/SettingsTable.php create mode 100755 main/app/sprinkles/ConfigManager/src/Database/Migrations/v101/SettingsPermissions.php create mode 100755 main/app/sprinkles/ConfigManager/src/Database/Models/Config.php create mode 100755 main/app/sprinkles/ConfigManager/src/ServicesProvider/ServicesProvider.php create mode 100755 main/app/sprinkles/ConfigManager/src/Util/ConfigManager.php (limited to 'main/app/sprinkles/ConfigManager/src') diff --git a/main/app/sprinkles/ConfigManager/src/ConfigManager.php b/main/app/sprinkles/ConfigManager/src/ConfigManager.php new file mode 100755 index 0000000..c29cee9 --- /dev/null +++ b/main/app/sprinkles/ConfigManager/src/ConfigManager.php @@ -0,0 +1,40 @@ + ['onAddGlobalMiddleware', 0] + ]; + } + + /** + * Add middleware. + */ + public function onAddGlobalMiddleware(Event $event) + { + $app = $event->getApp(); + $app->add($this->ci->configManager); + } +} diff --git a/main/app/sprinkles/ConfigManager/src/Controller/ConfigManagerController.php b/main/app/sprinkles/ConfigManager/src/Controller/ConfigManagerController.php new file mode 100755 index 0000000..0b5aac0 --- /dev/null +++ b/main/app/sprinkles/ConfigManager/src/Controller/ConfigManagerController.php @@ -0,0 +1,173 @@ +ci = $ci; + $this->manager = new ConfigManager($ci); + } + + /** + * mainList function. + * Used to display a list of all schema with their form + * + * @access public + * @param mixed $request + * @param mixed $response + * @param mixed $args + * @return void + */ + public function displayMain($request, $response, $args){ + + // Access-controlled resource + if (!$this->ci->authorizer->checkAccess($this->ci->currentUser, 'update_site_config')) { + throw new ForbiddenException(); + } + + // Get all the config schemas + $schemas = $this->manager->getAllShemas(); + + // Parse each of them to get it's content + foreach ($schemas as $i => $schemaData) { + + // Set the schemam, the validator and the form + $schema = new RequestSchemaRepository($schemaData['config']); + $validator = new JqueryValidationAdapter($schema, $this->ci->translator); + + // Create the form + $config = $this->ci->config; + $form = new Form($schema, $config); + + // The field names dot syntaxt won't make it across the HTTP POST request. + // Wrap them in a nice `data` array + $form->setFormNamespace("data"); + + // Twig doesn't need the raw thing + unset($schemas[$i]['config']); + + // Add the field and validator so Twig can play with them + $schemas[$i]["fields"] = $form->generate(); + $schemas[$i]["validators"] = $validator->rules('json', true); + + // Add the save url for that schema + $schemas[$i]["formAction"] = $this->ci->router->pathFor('ConfigManager.save', ['schema' => $schemaData['filename']]); + } + + // Time to render the page ! + $this->ci->view->render($response, 'pages/ConfigManager.html.twig', [ + "schemas" => $schemas, + ]); + + } + + /** + * update function. + * Processes the request to save the settings to the db + * + * @access public + * @param mixed $request + * @param mixed $response + * @param mixed $args + * @return void + */ + public function update($request, $response, $args){ + + // Get the alert message stream + $ms = $this->ci->alerts; + + // Access-controlled resource + if (!$this->ci->authorizer->checkAccess($this->ci->currentUser, 'update_site_config')) { + throw new ForbiddenException(); + } + + // Request POST data + $post = $request->getParsedBody(); + + // So we first get the shcema data + $loader = new YamlFileLoader("schema://config/".$args['schema'].".json"); + $schemaData = $loader->load(); + + // We can't pass the file directly to RequestSchema because it's a custom one + // So we create a new empty RequestSchemaRepository and feed it the `config` part of our custom schema + $schema = new RequestSchemaRepository($schemaData['config']); + + // Transform the data + $transformer = new RequestDataTransformer($schema); + $data = $transformer->transform($post['data']); + + // We change the dot notation of our elements to a multidimensionnal array + // This is required for the fields (but not for the schema) because the validator doesn't use the + // dot notation the same way. Sending dot notation field name to the validator will fail. + $dataArray = array(); + foreach ($data as $key => $value) { + array_set($dataArray, $key, $value); + } + + // We validate the data array against the schema + $validator = new ServerSideValidator($schema, $this->ci->translator); + if (!$validator->validate($dataArray)) { + $ms->addValidationErrors($validator); + return $response->withStatus(400); + } + + // The data is now validaded. Instead or switching back the array to dot notation, + // we can use the `$data` that's still intact. The validator doesn't change the data + // Next, update each config + foreach ($data as $key => $value) { + + // We need to access the $schemaData to find if we need to cache this one + $cached = (isset($schemaData['config'][$key]['cached'])) ? $schemaData['config'][$key]['cached'] : true; + + // Set the config using the manager + $this->manager->set($key, $value, $cached); + } + + //Success message! + $ms->addMessageTranslated("success", "SITE.CONFIG.SAVED"); + return $response->withJson([], 200, JSON_PRETTY_PRINT); + } +} diff --git a/main/app/sprinkles/ConfigManager/src/Database/Migrations/v100/SettingsTable.php b/main/app/sprinkles/ConfigManager/src/Database/Migrations/v100/SettingsTable.php new file mode 100755 index 0000000..182dbfb --- /dev/null +++ b/main/app/sprinkles/ConfigManager/src/Database/Migrations/v100/SettingsTable.php @@ -0,0 +1,48 @@ +schema->hasTable('settings')) { + $this->schema->create('settings', function (Blueprint $table) { + $table->increments('id'); + $table->string('key'); + $table->string('value')->nullable(); + $table->boolean('cached')->default(1); + $table->timestamps(); + + $table->engine = 'InnoDB'; + $table->collation = 'utf8_unicode_ci'; + $table->charset = 'utf8'; + }); + } + } + + /** + * {@inheritDoc} + */ + public function down() + { + $this->schema->drop('settings'); + } +} \ No newline at end of file diff --git a/main/app/sprinkles/ConfigManager/src/Database/Migrations/v101/SettingsPermissions.php b/main/app/sprinkles/ConfigManager/src/Database/Migrations/v101/SettingsPermissions.php new file mode 100755 index 0000000..c3928d9 --- /dev/null +++ b/main/app/sprinkles/ConfigManager/src/Database/Migrations/v101/SettingsPermissions.php @@ -0,0 +1,70 @@ +first(); + if ($permissionExist) { + $this->io->warning("\nPermission slug `update_site_config` already exist. Skipping..."); + return; + } + + // Add default permissions + $permission = new Permission([ + 'slug' => 'update_site_config', + 'name' => 'Update site configuration', + 'conditions' => 'always()', + 'description' => 'Edit site configuration from the UI' + ]); + $permission->save(); + + $roleSiteAdmin = Role::where('slug', 'site-admin')->first(); + if ($roleSiteAdmin) { + $roleSiteAdmin->permissions()->attach([ + $permission->id + ]); + } + } + + /** + * {@inheritDoc} + */ + public function down() + { + $permissions = Permission::where('slug', 'update_site_config')->get(); + foreach ($permissions as $permission) { + $permission->delete(); + } + } +} \ No newline at end of file diff --git a/main/app/sprinkles/ConfigManager/src/Database/Models/Config.php b/main/app/sprinkles/ConfigManager/src/Database/Models/Config.php new file mode 100755 index 0000000..53a85ca --- /dev/null +++ b/main/app/sprinkles/ConfigManager/src/Database/Models/Config.php @@ -0,0 +1,70 @@ +db; + return new ConfigManager($c); + }; + } +} diff --git a/main/app/sprinkles/ConfigManager/src/Util/ConfigManager.php b/main/app/sprinkles/ConfigManager/src/Util/ConfigManager.php new file mode 100755 index 0000000..e935487 --- /dev/null +++ b/main/app/sprinkles/ConfigManager/src/Util/ConfigManager.php @@ -0,0 +1,243 @@ +ci = $ci; + } + + /** + * __invoke function. + * Invoke the ConfigManager middleware, merging the db config with the file based one + * + * @access public + * @param \Psr\Http\Message\ServerRequestInterface $request PSR7 request + * @param \Psr\Http\Message\ResponseInterface $response PSR7 response + * @param callable $next Next middleware + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke($request, $response, $next) + { + $this->ci->config->mergeItems(null, $this->fetch()); + return $next($request, $response); + } + + /** + * fetch function. + * Fetch all the config from the db and uses the + * cache container to store most of thoses setting in the cache system + * + * @access public + * @return void + */ + public function fetch() { + + $cache = $this->ci->cache; + + // Case n° 1 we don't have cached content. We load everything + // Case n° 2 we have cached content, pull that and load the non chanched things to it + if (($cached_settings = $cache->get('UF_config')) === null) + { + $settingsCollection = Config::all(); + $settings = $this->collectionToArray($settingsCollection); + + // Save in cache. The settings that are not cached are not included + $cache->forever('UF_config', $this->collectionToArray($settingsCollection, false)); + } + else + { + // We have the cached values, we need to grab the non cached ones + $settingsCollection = Config::where('cached', 0); + $settings = array_merge_recursive($cached_settings, $this->collectionToArray($settingsCollection)); + } + + return $settings; + } + + /** + * delete function. + * Removes a configuration option + * + * @access public + * @param string $key The setting's name + * @return bool Success + */ + public function delete($key) { + + // Get the desired key + if (!$setting = Config::where('key', $key)->first()) { + return false; + } + + // Delete time + $setting->delete(); + + // Remove from current laod + unset($this->ci->config[$key]); + + // Delete cache + if ($setting->cached) + { + $this->ci->cache->forget('UF_config'); + } + + return true; + } + + /** + * set function. + * Sets a setting's value + * + * @access public + * @param string $key The setting's name + * @param string $value The new value + * @param bool $cached (default: true) Whether this variable should be cached or if it + * changes too frequently to be efficiently cached. + * @return bool True if the value was changed, false otherwise + */ + public function set($key, $value, $cached = true) { + return $this->set_atomic($key, false, $value, $cached); + } + + /** + * set_atomic function. + * Sets a setting's value only if the old_value matches the + * current value or the setting does not exist yet. + * + * @access public + * @param string $key The setting's name + * @param string $old_value Current configuration value or false to ignore + * the old value + * @param string $new_value The new value + * @param bool $cached (default: true) Whether this variable should be cached or if it + * changes too frequently to be efficiently cached. + * @return bool True if the value was changed, false otherwise + */ + public function set_atomic($key, $old_value, $new_value, $cached = true) { + + // Get the desired key + $setting = Config::where('key', $key)->first(); + + if ($setting) { + + if ($old_value === false || $setting->value == $old_value) { + + $setting->value = $new_value; + $setting->save(); + + } else { + return false; + } + + } else { + $setting = new Config([ + 'key' => $key, + 'value' => $new_value, + 'cached' => $cached + ]); + $setting->save(); + } + + if ($cached) + { + $this->ci->cache->forget('UF_config'); + } + + $this->ci->config[$key] = $new_value; + return true; + } + + /** + * getAllShemas function. + * Get all the config schemas available + * + * @access public + * @return void + */ + public function getAllShemas() { + + $configSchemas = []; + + $loader = new YamlFileLoader([]); + + // Get all the location where we can find config schemas + $paths = array_reverse($this->ci->locator->findResources('schema://config', true, false)); + + // For every location... + foreach ($paths as $path) { + + // Get a list of all the schemas file + $files_with_path = glob($path . "/*.json"); + + // Load every found files + foreach ($files_with_path as $file) { + + // Load the file content + $schema = $loader->loadFile($file); + + // Get file name + $filename = basename($file, ".json"); + + //inject file name + $schema['filename'] = $filename; + + // Add to list + $configSchemas[$filename] = $schema; + } + } + + return $configSchemas; + } + + + /** + * collectionToArray function. + * This function Expand the db dot notation single level array + * to a multi-dimensional array + * + * @access private + * @param Collection $Collection Eloquent collection + * @return array + */ + private function collectionToArray($Collection, $include_noncached = true) { + $settings_array = array(); + foreach ($Collection as $setting) { + if ($include_noncached || $setting->cached) { + array_set($settings_array, $setting->key, $setting->value); + } + } + return $settings_array; + } +} \ No newline at end of file -- cgit v1.2.3