aboutsummaryrefslogtreecommitdiffhomepage
path: root/main/app/sprinkles/ConfigManager
diff options
context:
space:
mode:
Diffstat (limited to 'main/app/sprinkles/ConfigManager')
-rwxr-xr-xmain/app/sprinkles/ConfigManager/CHANGELOG.md27
-rwxr-xr-xmain/app/sprinkles/ConfigManager/LICENSE21
-rwxr-xr-xmain/app/sprinkles/ConfigManager/README.md56
-rwxr-xr-xmain/app/sprinkles/ConfigManager/asset-bundles.json16
-rwxr-xr-xmain/app/sprinkles/ConfigManager/assets/js/ConfigManager.js92
-rwxr-xr-xmain/app/sprinkles/ConfigManager/composer.json26
-rwxr-xr-xmain/app/sprinkles/ConfigManager/locale/en_US/AdminLTE.php27
-rwxr-xr-xmain/app/sprinkles/ConfigManager/locale/en_US/ConfigManager.php27
-rwxr-xr-xmain/app/sprinkles/ConfigManager/locale/fr_FR/AdminLTE.php27
-rwxr-xr-xmain/app/sprinkles/ConfigManager/locale/fr_FR/ConfigManager.php27
-rwxr-xr-xmain/app/sprinkles/ConfigManager/routes/ConfigManager.php16
-rwxr-xr-xmain/app/sprinkles/ConfigManager/schema/config/AdminLTE.json32
-rwxr-xr-xmain/app/sprinkles/ConfigManager/schema/config/site.json40
-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
-rwxr-xr-xmain/app/sprinkles/ConfigManager/templates/pages/ConfigManager.html.twig64
21 files changed, 1173 insertions, 0 deletions
diff --git a/main/app/sprinkles/ConfigManager/CHANGELOG.md b/main/app/sprinkles/ConfigManager/CHANGELOG.md
new file mode 100755
index 0000000..4248a56
--- /dev/null
+++ b/main/app/sprinkles/ConfigManager/CHANGELOG.md
@@ -0,0 +1,27 @@
+# Change Log
+
+## 2.0.4
+- Fix issue with FormGenerator (for real this time)
+- Bump FormGenerator version
+
+## 2.0.3
+- Fix issue with FormGenerator
+
+## 2.0.2
+- Fix assets bundle issue
+
+## 2.0.1
+- Updated dependencies
+
+## 2.0.0
+Updated for UserFrosting v4.1.x
+
+## 1.0.2
+- Update composer.json
+
+## 1.0.1
+- Added controlled access
+- Added more settings to the default UI
+
+## 1.0.0
+- Initial release
diff --git a/main/app/sprinkles/ConfigManager/LICENSE b/main/app/sprinkles/ConfigManager/LICENSE
new file mode 100755
index 0000000..09386f7
--- /dev/null
+++ b/main/app/sprinkles/ConfigManager/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Louis Charette
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/main/app/sprinkles/ConfigManager/README.md b/main/app/sprinkles/ConfigManager/README.md
new file mode 100755
index 0000000..d90b2fe
--- /dev/null
+++ b/main/app/sprinkles/ConfigManager/README.md
@@ -0,0 +1,56 @@
+# Configuration Manager Sprinkle for [UserFrosting 4](https://www.userfrosting.com)
+
+Configuration Manager sprinkle for [UserFrosting 4](https://www.userfrosting.com). Lets you edit UserFrosting configs from the interface.
+
+> This version only works with UserFrosting 4.1.x !
+
+# Help and Contributing
+
+If you need help using this sprinkle or found any bug, feels free to open an issue or submit a pull request. You can also find me on the [UserFrosting Chat](https://chat.userfrosting.com/) most of the time for direct support.
+
+<a href='https://ko-fi.com/A7052ICP' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://az743702.vo.msecnd.net/cdn/kofi4.png?v=0' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
+
+# Installation
+
+Edit UserFrosting `app/sprinkles.json` file and add the following to the `require` list : `"lcharette/uf_configmanager": "^2.0.0"`. Also add `FormGenerator` and `ConfigManager` to the `base` list. For example:
+
+```
+{
+ "require": {
+ "lcharette/uf_configmanager": "^2.0.0"
+ },
+ "base": [
+ "core",
+ "account",
+ "admin",
+ "FormGenerator",
+ "ConfigManager"
+ ]
+}
+```
+
+Run `composer update` then `php bakery bake` to install the sprinkle.
+
+## Permissions
+The migration will automatically add the `update_site_config` permission to the `Site Administrator` role. To give access to the config UI to another user, simply add the `update_site_config` permission slug to that user role.
+
+## Add link to the menu
+The configuration UI is bound to the the `/settings` route. Simply add a link to this route where you want it. The checkAccess make it so it will appear only for users having the appropriate permission. For example, you can add the following to the sidebar menu :
+
+```
+{% if checkAccess('update_site_config') %}
+<li>
+ <a href="{{site.uri.public}}/settings"><i class="fa fa-gears fa-fw"></i> <span>{{ translate("SITE.CONFIG.MANAGER") }}</span></a>
+</li>
+{% endif %}
+```
+
+## Adding custom config
+
+! TODO
+
+> *NOTE* Only `.json` are accepted. `Yaml` schemas are cannot be used for now.
+
+# Licence
+
+By [Louis Charette](https://github.com/lcharette). Copyright (c) 2017, free to use in personal and commercial software as per the MIT license. \ No newline at end of file
diff --git a/main/app/sprinkles/ConfigManager/asset-bundles.json b/main/app/sprinkles/ConfigManager/asset-bundles.json
new file mode 100755
index 0000000..7550df9
--- /dev/null
+++ b/main/app/sprinkles/ConfigManager/asset-bundles.json
@@ -0,0 +1,16 @@
+{
+ "bundle": {
+ "js/ConfigManager" : {
+ "scripts": [
+ "js/ConfigManager.js"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "scripts": "plain"
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/main/app/sprinkles/ConfigManager/assets/js/ConfigManager.js b/main/app/sprinkles/ConfigManager/assets/js/ConfigManager.js
new file mode 100755
index 0000000..2a298f5
--- /dev/null
+++ b/main/app/sprinkles/ConfigManager/assets/js/ConfigManager.js
@@ -0,0 +1,92 @@
+/*!
+ * UF Config Manager - Config Manager Widget
+ *
+ * @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)
+ */
+
+(function( $ ){
+
+ 'use strict';
+
+ var options = {};
+
+ var methods = {
+ init : function(optionsArg) {
+
+ // Setup options
+ options = $.extend( options, $.fn.ConfigManager.defaultOptions, optionsArg );
+
+ // To use this inside sub-functions
+ var elements = this;
+
+ // Get the currently selected panel from the url anchor and switch to it
+ var hash = window.location.hash.substr(1);
+ if (hash != undefined && hash !== "") {
+ $(elements).hide();
+ $("#"+hash).show();
+
+ // Change the menu
+ $(options.menu).find("li").removeClass('active');
+ $(options.menu).find('a[href="#'+hash+'"]').parent().addClass("active");
+ }
+
+ // Set the menu
+ $(options.menu).find("li > a").click(function () {
+
+ // Change the menu first
+ $(options.menu).find("li").removeClass('active');
+ $(this).parent().addClass("active");
+
+ // Change the displayed forms next
+ $(elements).hide();
+ $("#"+$(this).data('target')).show();
+ });
+
+ // For each element the plugin is called on
+ this.each(function() {
+
+ // To use this inside sub-functions
+ var formPanel = this;
+
+ // ufForm instance. Don't need FormGeneator now
+ $(formPanel).find("form").ufForm({
+ validators: options.validators[ $(formPanel).attr('id') ],
+ msgTarget: $(formPanel).find("form .form-alerts")
+ }).on("submitSuccess.ufForm", function() {
+ // Forward to settings page on success
+ window.location.reload(true);
+ }).on("submitError.ufForm", function() {
+ $(formPanel).find("form .form-alerts").show();
+ });
+
+ });
+ return;
+ }
+ };
+
+ /*
+ * Main plugin function
+ */
+ $.fn.ConfigManager = function(methodOrOptions) {
+ if ( methods[methodOrOptions] ) {
+ return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
+ } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
+ // Default to "init"
+ return methods.init.apply( this, arguments );
+ } else {
+ $.error( 'Method ' + methodOrOptions + ' does not exist on jQuery.ConfigManager' );
+ }
+ };
+
+ /*
+ * Default plugin options
+ */
+ $.fn.ConfigManager.defaultOptions = {
+ menu : $(".configMenu"),
+ validators: {}
+ };
+
+
+})( jQuery ); \ No newline at end of file
diff --git a/main/app/sprinkles/ConfigManager/composer.json b/main/app/sprinkles/ConfigManager/composer.json
new file mode 100755
index 0000000..d524836
--- /dev/null
+++ b/main/app/sprinkles/ConfigManager/composer.json
@@ -0,0 +1,26 @@
+{
+ "name": "lcharette/uf_configmanager",
+ "type": "userfrosting-sprinkle",
+ "description": "This Sprinkle provides a UI for core and custom site settings",
+ "keywords": ["UserFrosting", "Config", "Settings", "Admin Panel"],
+ "homepage": "https://github.com/lcharette/UF_ConfigManager",
+ "license" : "MIT",
+ "authors" : [
+ {
+ "name": "Louis Charette",
+ "homepage": "https://github.com/lcharette"
+ }
+ ],
+ "require": {
+ "php": ">=5.6",
+ "lcharette/uf_formgenerator": "^2.2.10"
+ },
+ "autoload": {
+ "psr-4": {
+ "UserFrosting\\Sprinkle\\ConfigManager\\": "src/"
+ }
+ },
+ "extra": {
+ "installer-name": "ConfigManager"
+ }
+}
diff --git a/main/app/sprinkles/ConfigManager/locale/en_US/AdminLTE.php b/main/app/sprinkles/ConfigManager/locale/en_US/AdminLTE.php
new file mode 100755
index 0000000..bd1d032
--- /dev/null
+++ b/main/app/sprinkles/ConfigManager/locale/en_US/AdminLTE.php
@@ -0,0 +1,27 @@
+<?php
+return [
+ "ADMINLTE" => [
+ "SETTINGS" => [
+ "@TRANSLATION" => "AdminLTE Settings",
+
+ "DESC" => "Settings for the AdminLTE Theme",
+
+ "SKIN" => "Skin color"
+ ],
+
+ "SKIN" => [
+ "BLUE" => "Blue",
+ "BLACK" => "Black",
+ "PURPLE" => "Purple",
+ "GREEN" => "Green",
+ "RED" => "Red",
+ "YELLOW" => "Yellow",
+ "BLUE_LIGHT" => "Blue Light",
+ "BLACK_LIGHT" => "Black Light",
+ "PURPLE_LIGHT" => "Purple Light",
+ "GREEN_LIGHT" => "Green Light",
+ "RED_LIGHT" => "Red Light",
+ "YELLOW_LIGHT" => "Yellow Light"
+ ]
+ ]
+]; \ No newline at end of file
diff --git a/main/app/sprinkles/ConfigManager/locale/en_US/ConfigManager.php b/main/app/sprinkles/ConfigManager/locale/en_US/ConfigManager.php
new file mode 100755
index 0000000..b82f099
--- /dev/null
+++ b/main/app/sprinkles/ConfigManager/locale/en_US/ConfigManager.php
@@ -0,0 +1,27 @@
+<?php
+return [
+ "SITE" => [
+ "CONFIG" => [
+ "@TRANSLATION" => "UserFrosting Settings",
+
+ "DESC" => "Core settings of UserFrosting. See the config file for more configuration options",
+
+ "MANAGER" => "Configuration manager",
+
+ "PAGEDESC" => "This pages allows to edit the global site configuration variables stored in the database",
+
+ "SAVED" => "Changes saved successfully !"
+ ],
+ "TITLE" => [
+ "@TRANSLATION" => "Site title",
+ "REQUIRED" => "The site title is required"
+ ],
+ "REGISTRATION" => [
+ "ENABLED" => "Enabled site registration",
+ "REQUIRE_EMAIL_VERIFICATION" => "Require email verification when registering"
+ ]
+ ],
+ "SETTINGS" => [
+ "DISPLAY_ERROR_DETAILS" => "Display error details"
+ ]
+]; \ No newline at end of file
diff --git a/main/app/sprinkles/ConfigManager/locale/fr_FR/AdminLTE.php b/main/app/sprinkles/ConfigManager/locale/fr_FR/AdminLTE.php
new file mode 100755
index 0000000..39b4614
--- /dev/null
+++ b/main/app/sprinkles/ConfigManager/locale/fr_FR/AdminLTE.php
@@ -0,0 +1,27 @@
+<?php
+return [
+ "ADMINLTE" => [
+ "SETTINGS" => [
+ "@TRANSLATION" => "Paramètres d'AdminLTE",
+
+ "DESC" => "Paramètres du thème AdminLTE",
+
+ "SKIN" => "Couleur du thème"
+ ],
+
+ "SKIN" => [
+ "BLUE" => "Bleu",
+ "BLACK" => "Noir",
+ "PURPLE" => "Violet",
+ "GREEN" => "Vert",
+ "RED" => "Rouge",
+ "YELLOW" => "Jaune",
+ "BLUE_LIGHT" => "Bleu (Light)",
+ "BLACK_LIGHT" => "Noir (Light)",
+ "PURPLE_LIGHT" => "Violet (Light)",
+ "GREEN_LIGHT" => "Vert (Light)",
+ "RED_LIGHT" => "Rouge (Light)",
+ "YELLOW_LIGHT" => "Jaune (Light)"
+ ]
+ ]
+]; \ No newline at end of file
diff --git a/main/app/sprinkles/ConfigManager/locale/fr_FR/ConfigManager.php b/main/app/sprinkles/ConfigManager/locale/fr_FR/ConfigManager.php
new file mode 100755
index 0000000..3f9acff
--- /dev/null
+++ b/main/app/sprinkles/ConfigManager/locale/fr_FR/ConfigManager.php
@@ -0,0 +1,27 @@
+<?php
+return [
+ "SITE" => [
+ "CONFIG" => [
+ "@TRANSLATION" => "Paramètres de UserFrosting",
+
+ "DESC" => "Paramètres principaux de UserFrosting. Voir le fichier config pour plus d'options",
+
+ "MANAGER" => "Gestionnaire des paramètres",
+
+ "PAGEDESC" => "Cette page permet de modifier les paramètres globaux du site enregistrés dans la base de données",
+
+ "SAVED" => "Changements sauvegardés avec succès !"
+ ],
+ "TITLE" => [
+ "@TRANSLATION" => "Titre du site",
+ "REQUIRED" => "Le titre du site est requis"
+ ],
+ "REGISTRATION" => [
+ "ENABLED" => "Activer l'inscription",
+ "REQUIRE_EMAIL_VERIFICATION" => "Exiger une vérification par e-mail lors de l'inscription"
+ ]
+ ],
+ "SETTINGS" => [
+ "DISPLAY_ERROR_DETAILS" => "Afficher le détails des erreurs"
+ ]
+]; \ No newline at end of file
diff --git a/main/app/sprinkles/ConfigManager/routes/ConfigManager.php b/main/app/sprinkles/ConfigManager/routes/ConfigManager.php
new file mode 100755
index 0000000..f17f589
--- /dev/null
+++ b/main/app/sprinkles/ConfigManager/routes/ConfigManager.php
@@ -0,0 +1,16 @@
+<?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)
+ */
+
+$app->group('/settings', function () {
+ $this->get('', 'UserFrosting\Sprinkle\ConfigManager\Controller\ConfigManagerController:displayMain')
+ ->setName('ConfigManager');
+
+ $this->post('/{schema}', 'UserFrosting\Sprinkle\ConfigManager\Controller\ConfigManagerController:update')
+ ->setName('ConfigManager.save');
+}); \ No newline at end of file
diff --git a/main/app/sprinkles/ConfigManager/schema/config/AdminLTE.json b/main/app/sprinkles/ConfigManager/schema/config/AdminLTE.json
new file mode 100755
index 0000000..04e92da
--- /dev/null
+++ b/main/app/sprinkles/ConfigManager/schema/config/AdminLTE.json
@@ -0,0 +1,32 @@
+{
+ "name" : "ADMINLTE.SETTINGS",
+ "desc" : "ADMINLTE.SETTINGS.DESC",
+
+ "config": {
+ "site.AdminLTE.skin" : {
+ "validators" : {
+ "required" : {}
+ },
+ "cached" : true,
+ "form" : {
+ "type" : "select",
+ "label" : "ADMINLTE.SETTINGS.SKIN",
+ "icon" : "",
+ "options" : {
+ "blue" : "ADMINLTE.SKIN.BLUE",
+ "black" : "ADMINLTE.SKIN.BLACK",
+ "purple" : "ADMINLTE.SKIN.PURPLE",
+ "green" : "ADMINLTE.SKIN.GREEN",
+ "red" : "ADMINLTE.SKIN.RED",
+ "yellow" : "ADMINLTE.SKIN.YELLOW",
+ "blue-light" : "ADMINLTE.SKIN.BLUE_LIGHT",
+ "black-light" : "ADMINLTE.SKIN.BLACK_LIGHT",
+ "purple-light" : "ADMINLTE.SKIN.PURPLE_LIGHT",
+ "green-light" : "ADMINLTE.SKIN.GREEN_LIGHT",
+ "red-light" : "ADMINLTE.SKIN.RED_LIGHT",
+ "yellow-light" : "ADMINLTE.SKIN.YELLOW_LIGHT"
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/main/app/sprinkles/ConfigManager/schema/config/site.json b/main/app/sprinkles/ConfigManager/schema/config/site.json
new file mode 100755
index 0000000..2c9f4f0
--- /dev/null
+++ b/main/app/sprinkles/ConfigManager/schema/config/site.json
@@ -0,0 +1,40 @@
+{
+ "name" : "SITE.CONFIG",
+ "desc" : "SITE.CONFIG.DESC",
+
+ "config": {
+ "site.title" : {
+ "validators" : {
+ "required" : {
+ "message" : "SITE.TITLE.REQUIRED"
+ }
+ },
+ "form" : {
+ "type" : "text",
+ "label" : "SITE.TITLE",
+ "icon" : "fa-comment"
+ }
+ },
+ "site.registration.enabled" : {
+ "validators" : {},
+ "form" : {
+ "type" : "checkbox",
+ "label" : "SITE.REGISTRATION.ENABLED"
+ }
+ },
+ "site.registration.require_email_verification" : {
+ "validators" : {},
+ "form" : {
+ "type" : "checkbox",
+ "label" : "SITE.REGISTRATION.REQUIRE_EMAIL_VERIFICATION"
+ }
+ },
+ "settings.displayErrorDetails" : {
+ "validators" : {},
+ "form" : {
+ "type" : "checkbox",
+ "label" : "SETTINGS.DISPLAY_ERROR_DETAILS"
+ }
+ }
+ }
+} \ No newline at end of file
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
diff --git a/main/app/sprinkles/ConfigManager/templates/pages/ConfigManager.html.twig b/main/app/sprinkles/ConfigManager/templates/pages/ConfigManager.html.twig
new file mode 100755
index 0000000..ebfb251
--- /dev/null
+++ b/main/app/sprinkles/ConfigManager/templates/pages/ConfigManager.html.twig
@@ -0,0 +1,64 @@
+{% extends "pages/abstract/dashboard.html.twig" %}
+
+{% block page_title %}{{ translate('SITE.CONFIG.MANAGER') }}{% endblock %}
+{% block page_description %}{{ translate('SITE.CONFIG.PAGEDESC') }}{% endblock %}
+
+{% block body_matter %}
+ <div class="row">
+ <div class="col-lg-3">
+ <div class="box box-primary">
+ <div class="box-header">
+ <h3 class="box-title">Menu</h3>
+ </div>
+ <div class="box-body">
+ <ul id="configMenu" class="nav nav-pills nav-stacked">
+ {% for schema in schemas %}
+ <li role="presentation" {% if loop.first %} class="active"{% endif %}><a href="#configPanel-{{ schema.filename }}" data-target="configPanel-{{ schema.filename }}">{{ translate(schema.name) }}</a></li>
+ {% endfor %}
+ </ul>
+ </div>
+ </div>
+ </div>
+ <div class="col-lg-9">
+ {% for schema in schemas %}
+ <div id="configPanel-{{ schema.filename }}" class="box box-primary" {% if not loop.first %} style="display: none;"{% endif %}>
+ <form method="post" action="{{schema.formAction}}" class="form-horizontal">
+ <div class="box-header">
+ <h3 class="box-title">{{ translate(schema.name) }}</h3>
+ </div>
+ <div class="box-body">
+ {% if schema.desc %}<p>{{ translate(schema.desc) }}</p>{% endif %}
+ {% include "forms/csrf.html.twig" %}
+ <div class="form-alerts"></div>
+ {% include 'FormGenerator/FormGenerator.html.twig' with {fields: schema.fields, 'formLayout': 'horizontal'} %}
+ </div>
+ <div class="box-footer text-center">
+ <button type="reset" class="btn btn-default">{{translate('RESET')}}</button>
+ <button type="submit" class="btn btn-primary js-submit">{{translate('SAVE')}}</button>
+ </div>
+ </form>
+ </div>
+ {% endfor %}
+ </div>
+ </div>
+{% endblock %}
+{% block scripts_page %}
+
+ <script>
+ $(document).ready(function () {
+ $("[id^=configPanel-]").ConfigManager({
+ menu : $("#configMenu"),
+ validators : {
+ {% for schema in schemas %}
+ "configPanel-{{ schema.filename }}" : {{schema.validators | raw}},
+ {% endfor %}
+ }
+ });
+ });
+ </script>
+
+ <!-- Include form widgets JS -->
+ {{ assets.js('js/ConfigManager') | raw }}
+ {{ assets.js('js/form-widgets') | raw }}
+
+{% endblock %}