diff --git a/asset-bundles.json b/asset-bundles.json new file mode 100644 index 0000000..4b5daf6 --- /dev/null +++ b/asset-bundles.json @@ -0,0 +1,9 @@ +{ + "bundle": { + "js/pages/organisations": { + "scripts": [ + "avsdev/js/pages/organisations.js" + ] + } + } +} diff --git a/assets/avsdev/js/pages/organisations.js b/assets/avsdev/js/pages/organisations.js new file mode 100644 index 0000000..0450bd5 --- /dev/null +++ b/assets/avsdev/js/pages/organisations.js @@ -0,0 +1,16 @@ +/** + * Page-specific Javascript file. Should generally be included as a separate asset bundle in your page template. + * example: {{ assets.js('js/pages/sign-in-or-register') | raw }} + * + * This script depends on widgets/organisations.js, uf-table.js, moment.js, handlebars-helpers.js + * + * Target page: /organisations + */ + +$(document).ready(function() { + + $("#widget-organisations").ufTable({ + dataUrl: site.uri.public + "/api/organisations", + useLoadingTransition: site.uf_table.use_loading_transition + }); +}); diff --git a/locale/en_US/messages.php b/locale/en_US/messages.php new file mode 100644 index 0000000..9e6bc00 --- /dev/null +++ b/locale/en_US/messages.php @@ -0,0 +1,22 @@ + [ + 1 => 'Organisation', + 2 => 'Organisations', + + 'PAGE_DESCRIPTION' => 'A listing of the organisations for your site. Provides management tools for editing and deleting organisations.', + ], +]; diff --git a/routes/organisations.php b/routes/organisations.php new file mode 100644 index 0000000..7197159 --- /dev/null +++ b/routes/organisations.php @@ -0,0 +1,24 @@ +group('/organisations', function () { + $this->get('', 'UserFrosting\Sprinkle\Organisations\Controller\OrganisationController:pageList') + ->setName('uri_organisations'); +})->add('authGuard')->add(new NoCache()); + +$app->group('/api/organisations', function () { + $this->get('', 'UserFrosting\Sprinkle\Organisations\Controller\OrganisationController:getList'); +})->add('authGuard')->add(new NoCache()); + +// TODO: add route for accepting members \ No newline at end of file diff --git a/src/Controller/OrganisationController.php b/src/Controller/OrganisationController.php new file mode 100644 index 0000000..1170fea --- /dev/null +++ b/src/Controller/OrganisationController.php @@ -0,0 +1,102 @@ +getQueryParams(); + + /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ + $authorizer = $this->ci->authorizer; + + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ + $currentUser = $this->ci->currentUser; + + // Access-controlled page + if (!$authorizer->checkAccess($currentUser, 'uri_organisations')) { + throw new ForbiddenException(); + } + + /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ + $classMapper = $this->ci->classMapper; + + $sprunje = $classMapper->createInstance('organisation_sprunje', $classMapper, $params); + + // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content. + // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating). + return $sprunje->toResponse($response); + } + + /** + * Renders the organisation listing page. + * + * This page renders a table of groups, with dropdown menus for admin actions for each organisation. + * Actions typically include: edit organisation, delete organisation. + * This page requires authentication. + * + * Request type: GET + * + * @param Request $request + * @param Response $response + * @param array $args + * + * @throws ForbiddenException If user is not authorized to access page + */ + public function pageList(Request $request, Response $response, $args) + { + /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */ + $authorizer = $this->ci->authorizer; + + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ + $currentUser = $this->ci->currentUser; + + // Access-controlled page + if (!$authorizer->checkAccess($currentUser, 'uri_organisations')) { + throw new ForbiddenException(); + } + + return $this->ci->view->render($response, 'pages/organisations.html.twig'); + } +} diff --git a/src/Database/Migrations/v001/OrganisationsTable.php b/src/Database/Migrations/v001/OrganisationsTable.php new file mode 100644 index 0000000..e7da410 --- /dev/null +++ b/src/Database/Migrations/v001/OrganisationsTable.php @@ -0,0 +1,56 @@ +schema->hasTable('organisations')) { + $this->schema->create('organisations', function (Blueprint $table) { + $table->increments('id'); + $table->string('slug'); + $table->string('name'); + $table->text('description')->nullable(); + $table->softDeletes(); + $table->timestamps(); + + $table->engine = 'InnoDB'; + $table->collation = 'utf8_unicode_ci'; + $table->charset = 'utf8'; + $table->unique('slug'); + $table->index('slug'); + }); + } + } + + /** + * {@inheritdoc} + */ + public function down() + { + $this->schema->drop('organisations'); + } +} diff --git a/src/Database/Models/Organisation.php b/src/Database/Models/Organisation.php new file mode 100644 index 0000000..e55ce00 --- /dev/null +++ b/src/Database/Models/Organisation.php @@ -0,0 +1,60 @@ +getPermissions(); + $this->savePermissions($permissions); + + // Add default mappings to permissions + $this->syncPermissionsRole($permissions); + } + + /** + * @return array Permissions to seed + */ + protected function getPermissions() + { + return [ + 'uri_organisations' => new Permission([ + 'slug' => 'uri_organisations', + 'name' => 'Organisation management page', + 'conditions' => 'always()', + 'description' => 'View a page containing a list of organisations.', + ]), + ]; + } + + /** + * Save permissions. + * + * @param array $permissions + */ + protected function savePermissions(array &$permissions) + { + foreach ($permissions as $slug => $permission) { + + // Trying to find if the permission already exist + $existingPermission = Permission::where(['slug' => $permission->slug, 'conditions' => $permission->conditions])->first(); + + // Don't save if already exist, use existing permission reference + // otherwise to re-sync permissions and roles + if ($existingPermission == null) { + $permission->save(); + } else { + $permissions[$slug] = $existingPermission; + } + } + } + + /** + * Sync permissions with default roles. + * + * @param array $permissions + */ + protected function syncPermissionsRole(array $permissions) + { + $roleSiteAdmin = Role::where('slug', 'site-admin')->first(); + if ($roleSiteAdmin) { + $roleSiteAdmin->permissions()->sync([ + $permissions['uri_organisations'], + ], false); + } + } +} diff --git a/src/ServicesProvider/ServicesProvider.php b/src/ServicesProvider/ServicesProvider.php new file mode 100644 index 0000000..ea5b803 --- /dev/null +++ b/src/ServicesProvider/ServicesProvider.php @@ -0,0 +1,44 @@ +extend('classMapper', function ($classMapper, $c) { + $classMapper->setClassMapping('organisation', 'UserFrosting\Sprinkle\Organisations\Database\Models\Organisation'); + $classMapper->setClassMapping('organisation_sprunje', 'UserFrosting\Sprinkle\Organisations\Sprunje\OrganisationSprunje'); + + return $classMapper; + }); + } +} diff --git a/src/Sprunje/OrganisationSprunje.php b/src/Sprunje/OrganisationSprunje.php new file mode 100644 index 0000000..ca17f45 --- /dev/null +++ b/src/Sprunje/OrganisationSprunje.php @@ -0,0 +1,42 @@ +classMapper->createInstance('organisation')->newQuery(); + } +} diff --git a/templates/navigation/sidebar-menu.html.twig b/templates/navigation/sidebar-menu.html.twig new file mode 100644 index 0000000..573bb76 --- /dev/null +++ b/templates/navigation/sidebar-menu.html.twig @@ -0,0 +1,43 @@ +{% block navigation %} + {% if checkAccess('uri_dashboard') %} +
| {{translate('ORGANISATION')}} | +{{translate("DESCRIPTION")}} | +{{translate("ACTIONS")}} | +
|---|