aboutsummaryrefslogtreecommitdiffhomepage
path: root/main/app/sprinkles/ConfigManager/src
diff options
context:
space:
mode:
authormarvin-borner@live.com2018-04-16 21:09:05 +0200
committermarvin-borner@live.com2018-04-16 21:09:05 +0200
commitcf14306c2b3f82a81f8d56669a71633b4d4b5fce (patch)
tree86700651aa180026e89a66064b0364b1e4346f3f /main/app/sprinkles/ConfigManager/src
parent619b01b3615458c4ed78bfaeabb6b1a47cc8ad8b (diff)
Main merge to user management system - files are now at /main/public/
Diffstat (limited to 'main/app/sprinkles/ConfigManager/src')
-rwxr-xr-xmain/app/sprinkles/ConfigManager/src/ConfigManager.php40
-rwxr-xr-xmain/app/sprinkles/ConfigManager/src/Controller/ConfigManagerController.php173
-rwxr-xr-xmain/app/sprinkles/ConfigManager/src/Database/Migrations/v100/SettingsTable.php48
-rwxr-xr-xmain/app/sprinkles/ConfigManager/src/Database/Migrations/v101/SettingsPermissions.php70
-rwxr-xr-xmain/app/sprinkles/ConfigManager/src/Database/Models/Config.php70
-rwxr-xr-xmain/app/sprinkles/ConfigManager/src/ServicesProvider/ServicesProvider.php31
-rwxr-xr-xmain/app/sprinkles/ConfigManager/src/Util/ConfigManager.php243
7 files changed, 675 insertions, 0 deletions
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 @@
+<?php
+/**
+ * UF Config Manager
+ *
+ * @link https://github.com/lcharette/UF_ConfigManager
+ * @copyright Copyright (c) 2016 Louis Charette
+ * @license https://github.com/lcharette/UF_ConfigManager/blob/master/LICENSE (MIT License)
+ */
+namespace UserFrosting\Sprinkle\ConfigManager;
+
+use UserFrosting\System\Sprinkle\Sprinkle;
+use RocketTheme\Toolbox\Event\Event;
+
+/**
+ * ConfigManager class.
+ *
+ * Bootstrapper class for the 'Settings' sprinkle.
+ * @extends Sprinkle
+ */
+class ConfigManager extends Sprinkle
+{
+ /**
+ * Defines which events in the UF lifecycle our Sprinkle should hook into.
+ */
+ public static function getSubscribedEvents()
+ {
+ return [
+ 'onAddGlobalMiddleware' => ['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 @@
+<?php
+/**
+ * UF Config Manager
+ *
+ * @link https://github.com/lcharette/UF_ConfigManager
+ * @copyright Copyright (c) 2016 Louis Charette
+ * @license https://github.com/lcharette/UF_ConfigManager/blob/master/LICENSE (MIT License)
+ */
+namespace UserFrosting\Sprinkle\ConfigManager\Controller;
+
+use Interop\Container\ContainerInterface;
+use UserFrosting\Sprinkle\Core\Facades\Debug;
+use UserFrosting\Sprinkle\ConfigManager\Util\ConfigManager;
+use UserFrosting\Sprinkle\FormGenerator\Form;
+use UserFrosting\Support\Exception\ForbiddenException;
+use UserFrosting\Support\Repository\Loader\YamlFileLoader;
+use UserFrosting\Fortress\RequestSchema;
+use UserFrosting\Fortress\RequestSchema\RequestSchemaRepository;
+use UserFrosting\Fortress\RequestDataTransformer;
+use UserFrosting\Fortress\ServerSideValidator;
+use UserFrosting\Fortress\Adapter\JqueryValidationAdapter;
+
+/**
+ * ConfigManagerController Class
+ *
+ * Controller class for /settings/* URLs. Generate the interface required to modify the sites settings and saving the changes
+ */
+class ConfigManagerController {
+
+ /**
+ * @var ContainerInterface The global container object, which holds all your services.
+ */
+ protected $ci;
+
+ /**
+ * @var ConfigManager Hold the ConfigManager class that handle setting the config and getting the config schema
+ * Note that we don't interact with the `Config` db model directly since it can't handle the cache
+ */
+ protected $manager;
+
+ /**
+ * __construct function.
+ * Create a new ConfigManagerController object.
+ *
+ * @access public
+ * @param ContainerInterface $ci
+ * @return void
+ */
+ public function __construct(ContainerInterface $ci) {
+ $this->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 @@
+<?php
+/**
+ * UF Config Manager
+ *
+ * @link https://github.com/lcharette/UF_ConfigManager
+ * @copyright Copyright (c) 2016 Louis Charette
+ * @license https://github.com/lcharette/UF_ConfigManager/blob/master/LICENSE (MIT License)
+ */
+namespace UserFrosting\Sprinkle\ConfigManager\Database\Migrations\v100;
+
+use UserFrosting\System\Bakery\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Schema\Builder;
+
+/**
+ * Settings table migration
+ * @extends Migration
+ */
+class SettingsTable extends Migration
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function up()
+ {
+ if (!$this->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 @@
+<?php
+/**
+ * UF Config Manager
+ *
+ * @link https://github.com/lcharette/UF_ConfigManager
+ * @copyright Copyright (c) 2016 Louis Charette
+ * @license https://github.com/lcharette/UF_ConfigManager/blob/master/LICENSE (MIT License)
+ */
+namespace UserFrosting\Sprinkle\ConfigManager\Database\Migrations\v101;
+
+use UserFrosting\System\Bakery\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Schema\Builder;
+use UserFrosting\Sprinkle\Account\Database\Models\Permission;
+use UserFrosting\Sprinkle\Account\Database\Models\Role;
+
+/**
+ * Settings permissions migration
+ * @extends Migration
+ */
+class SettingsPermissions extends Migration
+{
+ /**
+ * {@inheritDoc}
+ */
+ public $dependencies = [
+ '\UserFrosting\Sprinkle\Account\Database\Migrations\v400\PermissionsTable',
+ '\UserFrosting\Sprinkle\ConfigManager\Database\Migrations\v100\SettingsTable'
+ ];
+
+ /**
+ * {@inheritDoc}
+ */
+ public function up()
+ {
+ // Check if permission exist
+ $permissionExist = Permission::where('slug', 'update_site_config')->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 @@
+<?php
+/**
+ * UF Settings
+ *
+ * @link https://github.com/lcharette/UF_Settings
+ * @copyright Copyright (c) 2016 Louis Charette
+ * @license
+ */
+namespace UserFrosting\Sprinkle\ConfigManager\Database\Models;
+
+use \Illuminate\Database\Capsule\Manager as Capsule;
+use UserFrosting\Sprinkle\Core\Database\Models\Model;
+
+/**
+ * Settings class.
+ *
+ * @extends Model
+ */
+class Config extends Model {
+
+ /**
+ * @var string The name of the table for the current model.
+ */
+ protected $table = "settings";
+
+ /**
+ * @var array The fields of the table for the current model.
+ */
+ protected $fillable = [
+ "key",
+ "value",
+ "cache"
+ ];
+
+ /**
+ * @var bool Enable timestamps for Users.
+ */
+ public $timestamps = true;
+
+ /**
+ * Create a new Project object.
+ *
+ */
+ public function __construct($properties = [])
+ {
+ parent::__construct($properties);
+ }
+
+ /**
+ * Model's relations
+ * Each of those should be in delete !
+ *
+ */
+
+ /**
+ * Model's parent relation
+ *
+ */
+
+ /**
+ * Delete this group from the database, along with any linked user and authorization rules
+ *
+ */
+ public function delete()
+ {
+ // Delete the main object
+ $result = parent::delete();
+ return $result;
+ }
+} \ No newline at end of file
diff --git a/main/app/sprinkles/ConfigManager/src/ServicesProvider/ServicesProvider.php b/main/app/sprinkles/ConfigManager/src/ServicesProvider/ServicesProvider.php
new file mode 100755
index 0000000..93187b1
--- /dev/null
+++ b/main/app/sprinkles/ConfigManager/src/ServicesProvider/ServicesProvider.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Gaston (http://gaston.bbqsoftwares.com)
+ *
+ * @link https://github.com/lcharette/GASTON
+ * @copyright Copyright (c) 2016 Louis Charette
+ * @license
+ */
+namespace UserFrosting\Sprinkle\ConfigManager\ServicesProvider;
+
+use UserFrosting\Sprinkle\ConfigManager\Util\ConfigManager;
+
+/**
+ * ConfigManagerServicesProvider class.
+ * Registers services for the ConfigManager sprinkle, such as configManager, etc.
+ */
+class ServicesProvider
+{
+ /**
+ * Register configManager services.
+ *
+ * @param Container $container A DI container implementing ArrayAccess and container-interop.
+ */
+ public function register($container)
+ {
+ $container['configManager'] = function ($c) {
+ $c->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 @@
+<?php
+/**
+ * Gaston (http://gaston.bbqsoftwares.com)
+ *
+ * @link https://github.com/lcharette/GASTON
+ * @copyright Copyright (c) 2016 Louis Charette
+ * @license
+ */
+namespace UserFrosting\Sprinkle\ConfigManager\Util;
+
+use UserFrosting\Sprinkle\Core\Facades\Debug;
+use UserFrosting\Sprinkle\ConfigManager\Database\Models\Config;
+use UserFrosting\Support\Exception\FileNotFoundException;
+use UserFrosting\Support\Exception\JsonException;
+use UserFrosting\Support\Repository\Loader\YamlFileLoader;
+use Interop\Container\ContainerInterface;
+
+/**
+ * GastonServicesProvider class.
+ * Registers services for the account sprinkle, such as currentUser, etc.
+ */
+class ConfigManager
+{
+ /**
+ * @var ContainerInterface The global container object, which holds all your services.
+ */
+ protected $ci;
+
+ /**
+ * __construct function.
+ *
+ * @access public
+ * @param ContainerInterface $ci
+ * @return void
+ */
+ public function __construct(ContainerInterface $ci)
+ {
+ $this->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