diff --git a/asset-bundles.json b/asset-bundles.json index 9cc6dd3..fb9306a 100644 --- a/asset-bundles.json +++ b/asset-bundles.json @@ -10,6 +10,17 @@ } } }, + "js/pages/dashboard": { + "scripts": [ + "organisations/js/widgets/organisations.js", + "organisations/js/pages/dashboard.js" + ], + "options": { + "sprinkle": { + "onCollision": "merge" + } + } + }, "js/pages/organisation": { "scripts": [ "userfrosting/js/widgets/users.js", diff --git a/assets/organisations/css/organisations.css b/assets/organisations/css/organisations.css index 9c58d62..13d73bf 100644 --- a/assets/organisations/css/organisations.css +++ b/assets/organisations/css/organisations.css @@ -6,4 +6,16 @@ } .membership-pending { color: #ffd24a !important; +} + +.widget-organisations .widget-user-username, .widget-organisations .widget-user-desc { + margin-left: 0px; +} + +.widget-organisations .widget-user-desc { + text-overflow: ellipsis; + overflow: hidden; + width: 100%; + displaY: inline-block; + white-space: nowrap; } \ No newline at end of file diff --git a/assets/organisations/js/pages/dashboard.js b/assets/organisations/js/pages/dashboard.js new file mode 100644 index 0000000..ac27d51 --- /dev/null +++ b/assets/organisations/js/pages/dashboard.js @@ -0,0 +1,17 @@ +/** + * 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 }} + * + * Target page: /dashboard + */ + +$(document).ready(function() { + // Table of organisations + var organisations = $("#widget-organisations"); + if (organisations.length) { + 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 index 2ca65c8..2192da0 100644 --- a/locale/en_US/messages.php +++ b/locale/en_US/messages.php @@ -17,6 +17,9 @@ return [ 1 => 'Organisation', 2 => 'Organisations', + 'LATEST' => 'Latest Organisations', + 'VIEW_ALL' => 'View all organisations', + 'PAGE_DESCRIPTION' => 'A listing of the organisations for your site. Provides management tools for editing and deleting organisations.', 'DELETED_PAGE_DESCRIPTION' => 'A listing of the deleted organisations for your site. Provides management tools for restoring and permenently deleting organisations.', 'INFO_PAGE' => 'Organisation information page for {{name}}', @@ -164,6 +167,8 @@ return [ 2 => 'Administrators', ], + 'CREATED_ON' => 'Created on', + 'MERGE' => 'Merge', 'MERGE_INTO' => 'Merge into', 'MERGE_CANNOT_UNDONE' => 'This action cannot be undone.', diff --git a/routes/dashboard.php b/routes/dashboard.php new file mode 100644 index 0000000..ef0535e --- /dev/null +++ b/routes/dashboard.php @@ -0,0 +1,18 @@ +group('/dashboard', function () { + $this->get('', 'UserFrosting\Sprinkle\Organisations\Controller\DashboardController:pageDashboard') + ->setName('dashboard'); +})->add('authGuard')->add(new NoCache()); \ No newline at end of file diff --git a/src/Controller/DashboardController.php b/src/Controller/DashboardController.php new file mode 100644 index 0000000..72e1f2b --- /dev/null +++ b/src/Controller/DashboardController.php @@ -0,0 +1,109 @@ +ci->authorizer; + + /** @var \Illuminate\Cache\Repository $cache */ + $cache = $this->ci->cache; + + /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ + $classMapper = $this->ci->classMapper; + + /** @var \UserFrosting\Support\Repository\Repository $config */ + $config = $this->ci->config; + + /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ + $currentUser = $this->ci->currentUser; + + + // Access-controlled page + if (!$authorizer->checkAccess($currentUser, 'uri_dashboard')) { + throw new ForbiddenException(); + } + + // Probably a better way to do this + $users = $classMapper->getClassMapping('user')::orderBy('created_at', 'desc') + ->take(8) + ->get(); + + // Transform the `create_at` date in "x days ago" type of string + $users->transform(function ($item, $key) { + $item->registered = Carbon::parse($item->created_at)->diffForHumans(); + + return $item; + }); + + // Probably a better way to do this + $organisations = $classMapper->getClassMapping('organisation')::orderBy('created_at', 'desc') + ->take(4) + ->get(); + + // Transform the `create_at` date in "x days ago" type of string + $organisations->transform(function ($item, $key) { + $item->registered = Carbon::parse($item->created_at)->diffForHumans(); + + return $item; + }); + + // Get each sprinkle db version + $sprinkles = $this->ci->sprinkleManager->getSprinkleNames(); + + return $this->ci->view->render($response, 'pages/dashboard.html.twig', [ + 'counter' => [ + 'users' => $classMapper->getClassMapping('user')::count(), + 'roles' => $classMapper->getClassMapping('role')::count(), + 'groups' => $classMapper->getClassMapping('group')::count(), + 'organisations' => $classMapper->getClassMapping('organisation')::count(), + ], + 'info' => [ + 'version' => [ + 'UF' => \UserFrosting\VERSION, + 'php' => phpversion(), + 'database' => EnvironmentInfo::database(), + ], + 'database' => [ + 'name' => $config['db.default.database'], + ], + 'environment' => $this->ci->environment, + 'path' => [ + 'project' => \UserFrosting\ROOT_DIR, + ], + ], + 'sprinkles' => $sprinkles, + 'users' => $users, + 'organisations' => $organisations, + ]); + } +} \ No newline at end of file diff --git a/src/Database/Models/Organisation.php b/src/Database/Models/Organisation.php index ab6c345..a33682e 100644 --- a/src/Database/Models/Organisation.php +++ b/src/Database/Models/Organisation.php @@ -135,6 +135,19 @@ class Organisation extends Model implements OrganisationInterface, TokenOwnerInt ->where('flag_admin', true); } + /** + * Get a list of pending members within this organisation. + */ + public function pendingMembers() + { + /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ + $classMapper = static::$ci->classMapper; + + return $this + ->belongsToMany($classMapper->getClassMapping('user'), 'organisation_members', 'organisation_id', 'user_id') + ->where('flag_approved', false); + } + /** * Delete this organisation from the database, along with any linked objects. * diff --git a/src/Sprunje/OrganisationSprunje.php b/src/Sprunje/OrganisationSprunje.php index e009c0c..7f44f9a 100644 --- a/src/Sprunje/OrganisationSprunje.php +++ b/src/Sprunje/OrganisationSprunje.php @@ -33,6 +33,7 @@ class OrganisationSprunje extends Sprunje 'member_count', 'admin_count', 'status', + 'created_at', ]; protected $filterable = [ @@ -42,6 +43,7 @@ class OrganisationSprunje extends Sprunje 'admin_count', 'status', 'info', + 'created_at', ]; protected $excludeForAll = [ @@ -136,4 +138,20 @@ class OrganisationSprunje extends Sprunje return $this; } + + /** + * Sort based on created date. + * + * @param Builder $query + * @param string $direction + * + * @return self + */ + protected function sortCreatedAt($query, $direction) + { + $query->orderBy('organisations.created_at', $direction) + ->orderby('organisations.id', $direction); + + return $this; + } } diff --git a/templates/pages/dashboard.html.twig b/templates/pages/dashboard.html.twig new file mode 100644 index 0000000..0f480da --- /dev/null +++ b/templates/pages/dashboard.html.twig @@ -0,0 +1,345 @@ +{% extends "@admin/pages/dashboard.html.twig" %} + +{% block body_matter %} + {% block info_boxes %} + + {% if hasRole('site-admin') or hasRole('organisations-admin') %} +
+ {% if checkAccess('uri_users') %} + {% block info_box_users %} +
+ +
+ +
+ {{ translate("USER", 2) }} + {{counter.users}} +
+ +
+ +
+
+ + {% endblock %} + {% endif %} + + {% if checkAccess('uri_roles') %} + {% block info_box_roles %} +
+ +
+ +
+ {{ translate("ROLE", 2) }} + {{counter.roles}} +
+ +
+ +
+
+ + {% endblock %} + {% endif %} + + {% if checkAccess('uri_groups') %} + {% block info_box_groups %} +
+ +
+ +
+ {{ translate("GROUP", 2) }} + {{counter.groups}} +
+ +
+ +
+
+ + {% endblock %} + {% endif %} + + {% if checkAccess('uri_organisations') %} + {% block info_box_organisations %} +
+ +
+ +
+ {{ translate("ORGANISATION", 2) }} + {{counter.organisations}} +
+ +
+ +
+
+ + {% endblock %} + {% endif %} +
+ + {% elseif checkAccess('uri_group', { + 'group': current_user.group + }) %} +
+ {% block info_box_group %} +
+
+ +
+

{{current_user.group.name}}

+
+ +
+ +
+ + {% endblock %} + + {% block info_box_group_users %} +
+
+ +
+ {{ translate("USER", 2) }} + {{current_user.group.users.count}} +
+ +
+ +
+ + {% endblock %} +
+ + {% endif %} + {% endblock %} + + +
+ {% if checkAccess('uri_users') or checkAccess('view_system_info') or (hasRole('site-admin') or hasRole('organisations-admin')) %} +
+ {% if checkAccess('uri_users') %} +
+
+ +
+
+

{{translate("USER.LATEST")}}

+
+ +
+ + +
+ + + +
+ +
+ +
+ + {% endif %} + + {% if (hasRole('site-admin') or hasRole('organisations-admin')) and checkAccess('uri_activities') %} + {% block latest_organisations %} +
+
+ +
+
+

{{translate("ORGANISATION.LATEST")}}

+
+ + + + + +
+ +
+ +
+ + {% endblock %} + {% endif %} + + {% if checkAccess('view_system_info') %} +
+
+
+
+

{{translate("SYSTEM_INFO")}}

+
+ +
+
+
{{translate("SYSTEM_INFO.UF_VERSION")}}
+
{{info.version.UF}}
+ +
{{translate("SYSTEM_INFO.PHP_VERSION")}}
+
{{info.version.php}}
+ +
{{translate("SYSTEM_INFO.SERVER")}}
+
{{info.environment.SERVER_SOFTWARE}}
+ +
{{translate("SYSTEM_INFO.DB_VERSION")}}
+
{{info.version.database.type}} {{info.version.database.version}}
+ +
{{translate("SYSTEM_INFO.DB_NAME")}}
+
{{info.database.name}}
+ +
{{translate("SYSTEM_INFO.DIRECTORY")}}
+
{{info.path.project}}
+ +
{{translate("SYSTEM_INFO.URL")}}
+
{{site.uri.public}}
+ +
{{translate("SYSTEM_INFO.SPRINKLES")}}
+
+
    + {% for sprinkle in sprinkles %} +
  • + {{sprinkle}} +
  • + {% endfor %} +
+
+
+
+ + + +
+ +
+ +
+ + {% endif %} +
+ + {% endif %} + + {% if checkAccess('uri_activities') %} +
+
+
+

{{translate('ACTIVITY', 2)}}

+ {% include "tables/table-tool-menu.html.twig" %} +
+
+ {% include "tables/activities.html.twig" with { + "table" : { + "id" : "table-activities", + "columns" : ["user"] + } + } + %} +
+
+
+ {% elseif (hasRole('site-admin') or hasRole('organisations-admin')) %} +
+ {{block('latest_organisations')}} +
+ + {% elseif checkAccess('view_group_field', { + 'group': current_user.group, + 'property': 'users' + }) %} +
+
+
+

{{translate('USER', 2)}}

+ {% include "tables/table-tool-menu.html.twig" %} +
+
+ {% include "tables/users.html.twig" with { + "table" : { + "id" : "table-group-users" + } + } + %} +
+ +
+
+ {% else %} +
+
+ +
+

+ {{translate("WELCOME", { + 'first_name': current_user.first_name + })}} +

+
+
+ User Avatar +
+ +
+ +
+ + {% endif %} +
+ +{% endblock %} diff --git a/templates/tables/organisations.html.twig b/templates/tables/organisations.html.twig index 70e501e..b36a591 100644 --- a/templates/tables/organisations.html.twig +++ b/templates/tables/organisations.html.twig @@ -20,7 +20,9 @@ {{translate('ORGANISATION')}} + {% if 'description' not in table.hidden_columns %} {{translate("DESCRIPTION")}} + {% endif %} {% if hasRole('site-admin') or hasRole('organisations-admin') or (current_user.adminForOrganisations.count > 0) -%} {{translate("STATUS")}} {% endif -%} @@ -28,10 +30,14 @@ {{translate("ORGANISATION.MEMBER_COUNT")}} {{translate("ORGANISATION.ADMIN_COUNT")}} {% endif -%} + {% if 'join' in table.columns %} {{translate("JOIN")}}/{{translate("LEAVE")}} + {% endif %} + {% if 'actions' in table.columns %} {% if hasRole('site-admin') or hasRole('organisations-admin') or (current_user.adminForOrganisations.count > 0) -%} {{translate("ACTIONS")}} {% endif -%} + {% endif %}