7 Commits

12 changed files with 232 additions and 170 deletions

View File

@@ -54,7 +54,10 @@ return [
'MEMBER_COUNT' => '# Members <sub>(excl admins)</sub>',
'ADMIN_COUNT' => '# Admins',
'SELF' => 'My Organisations',
'SELF' => [
1 => 'My Organisation',
2 => 'My Organisations',
],
'MANAGE' => 'Manage Organisations',
'ASSIGN_NEW' => 'Assign to organisation',

View File

@@ -645,8 +645,9 @@ class OrganisationController extends SimpleController
});
if (!$currentUser->isMaster() && $currentUser->roles()->where('slug', 'organisations-admin')->count() == 0) {
$sprunje->extendQuery(function ($query) {
return $query->where('flag_approved', true)
->orWhereNotNull('is_member');
return $query->where(function($query) {
$query->where('flag_approved', true)->orWhereNotNull('is_member');
});
});
}

View File

@@ -1370,6 +1370,20 @@ class OrganisationMembersController extends SimpleController
{
$timeout = $this->ci->config['organisation.membership.timeout'];
$admin_fallback = false;
$recipientsQuery = $organisation->administrators();
if ($recipientsQuery->count() == 0) {
$admin_fallback = true;
$role = $this->ci->classMapper->getClassMapping('role')::where('slug', 'organisations-admin')->with('users')->first();
if ($role->users()->count() == 0) {
$role = $this->ci->classMapper->getClassMapping('role')::where('slug', 'site-admin')->with('users')->first();
}
$recipientsQuery = $role->users();
}
$recipients = $recipientsQuery->get();
// Create and send approval email
$message = new TwigMailMessage($this->ci->view, 'mail/organisation-membership-request.html.twig');
@@ -1379,20 +1393,9 @@ class OrganisationMembersController extends SimpleController
'organisation' => $organisation,
'token' => $token,
'approval_expiration' => ($timeout > 0 ? floor($timeout / 86400) . ' days' : false),
'admin_fallback' => $admin_fallback,
]);
$recipientsQuery = $organisation->administrators();
if ($recipientsQuery->count() == 0) {
$role = $this->ci->classMapper->getClassMapping('role')::where('slug', 'organisations-admin')->with('users')->first();
if ($role->users()->count() == 0) {
$role = $this->ci->classMapper->getClassMapping('role')::where('slug', 'site-admin')->with('users')->first();
}
$recipientsQuery = $role->users();
}
$recipients = $recipientsQuery->get();
foreach($recipients as $recipient) {
$message->addEmailRecipient(new EmailRecipient($recipient->email, $recipient->full_name));
$message->addParams([ 'recipient' => $recipient ]);

View File

@@ -154,6 +154,12 @@ class OrganisationPermissions extends BaseSeed
'conditions' => 'always()',
'description' => 'View the organisation page of any organisation.',
]),
'uri_user' => new Permission([
'slug' => 'uri_user',
'name' => 'View organisation member',
'conditions' => 'can_admin_via_orgs(self.id, user.id)',
'description' => 'View the user page of any member of your organisation.',
]),
'uri_organisation_own' => new Permission([
'slug' => 'uri_organisation',
'name' => 'View own organisation',
@@ -262,6 +268,7 @@ class OrganisationPermissions extends BaseSeed
$permissions['restore_organisation']->id,
$permissions['permenent_delete_organisation']->id,
$permissions['uri_user']->id,
$permissions['uri_organisation']->id,
$permissions['uri_deleted_organisations']->id,
@@ -306,6 +313,7 @@ class OrganisationPermissions extends BaseSeed
$permissions['restore_organisation']->id,
$permissions['permenent_delete_organisation']->id,
$permissions['uri_user']->id,
$permissions['uri_organisation']->id,
$permissions['uri_deleted_organisations']->id,
@@ -340,13 +348,6 @@ class OrganisationPermissions extends BaseSeed
]);
}
$roleAuditer = Role::where('slug', 'auditer')->first();
if ($roleAuditer) {
$roleAuditer->permissions()->syncWithoutDetaching([
Permission::where('slug', 'uri_activities')->first()->id,
]);
}
$roleUser = Role::where('slug', 'user')->first();
if ($roleUser) {
$roleUser->permissions()->syncWithoutDetaching([
@@ -362,9 +363,10 @@ class OrganisationPermissions extends BaseSeed
$permissions['view_organisation_field_own']->id,
$permissions['update_organisation_field_own']->id,
$permissions['uri_organisation_own']->id,
$permissions['uri_organisations']->id,
$permissions['uri_user']->id,
]);
}
}

View File

@@ -43,11 +43,6 @@ class OrganisationRoles extends BaseSeed
'name' => 'Organisations Administrator',
'description' => 'This role is meant for "organisation administrators", who can basically do anything related to organisations and their members.',
]),
new Role([
'slug' => 'auditer',
'name' => 'Audit Viewer',
'description' => 'This role is meant for "auditers", who are allowed to view the activity log.',
]),
];
}
}

View File

@@ -83,6 +83,48 @@ class OrganisationSprunje extends Sprunje
return $this;
}
/**
* Filter by name (case insensitive).
*
* @param Builder $query
* @param mixed $value
*
* @return self
*/
protected function filterName($query, $value)
{
// Split value on separator for OR queries
$values = explode($this->orSeparator, $value);
$query->where(function ($query) use ($values) {
foreach ($values as $value) {
$query->orWhereRaw('LOWER(name) LIKE ?', [ '%' . strtolower($value) . '%' ]);
}
});
return $this;
}
/**
* Filter by description (case insensitive).
*
* @param Builder $query
* @param mixed $value
*
* @return self
*/
protected function filterDescription($query, $value)
{
// Split value on separator for OR queries
$values = explode($this->orSeparator, $value);
$query->where(function ($query) use ($values) {
foreach ($values as $value) {
$query->orWhereRaw('LOWER(description) LIKE ?', [ '%' . strtolower($value) . '%' ]);
}
});
return $this;
}
/**
* Return a list of possible user statuses.
*
@@ -117,28 +159,6 @@ class OrganisationSprunje extends Sprunje
return $this;
}
/**
* Filter LIKE name OR description. (for user organisations modal)
*
* @param Builder $query
* @param mixed $value
*
* @return self
*/
protected function filterInfo($query, $value)
{
// Split value on separator for OR queries
$values = explode($this->orSeparator, $value);
$query->where(function ($query) use ($values) {
foreach ($values as $value) {
$query->orLike('name', $value)
->orLike('description', $value);
}
});
return $this;
}
/**
* Sort based on created date.
*

View File

@@ -3,6 +3,12 @@
{% endblock %}
{% block body %}
{% if admin_fallback %}
<hr style="color: #990000" />
<h3 style="color: #990000">You are receiving this email as an organisation/site administrator!</h3>
<p style="color: #990000">This email has been sent to you in lieu of <b>{{organisation.name}}</b> as there are currently no approved administrators for the organisation.</p>
<hr style="color: #990000" />
{% endif %}
<p>
Dear {{recipient.first_name}},
</p>

View File

@@ -1,25 +1,37 @@
{% extends "@admin/navigation/sidebar-menu.html.twig" %}
{% block navigation %}
{{ parent() }}
{% block organisations_nav %}
{% if checkAccess('uri_organisations') %}
<li>
<a href="{{site.uri.public}}/organisations"><i class="fas fa-sitemap fa-fw"></i> <span>{{ translate("ORGANISATION", 2) }}</span></a>
</li>
{% endif %}
{% if current_user.organisations.count() > 0 %}
<li>
<a href="#" data-toggle="collapse" data-target="#submenu-organisations" aria-expanded="false"><i class="fa fa-fw fa-sitemap"></i> <span>{{ translate("ORGANISATION.SELF") }}</span> <i class="fa fa-fw pull-right fa-angle-down"></i></a>
<ul id="submenu-organisations" class="collapsable collapse" aria-expanded="false" style="height: 1px;">
{% for organisation in current_user.organisations %}
{% if organisationConfig.membership.single_membership == false %}
<li>
<a href="#" data-toggle="collapse" data-target="#submenu-organisations" aria-expanded="false"><i class="fa fa-fw fa-sitemap"></i> <span>{{ translate("ORGANISATION.SELF") }}</span> <i class="fa fa-fw pull-right fa-angle-down"></i></a>
<ul id="submenu-organisations" class="collapsable collapse" aria-expanded="false" style="height: 1px;">
{% for organisation in current_user.organisations %}
{% if organisation.flag_approved or isOrganisationRegistrant(organisation) %}
<li>
<a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}"><i class="fas fa-angle-double-right"></i> <span>{{organisation.name}}</span></a>
</li>
{% endif %}
{% endfor %}
</ul>
</li>
{% else %}
{% set organisation = current_user.organisations.0 %}
{% if organisation.flag_approved or isOrganisationRegistrant(organisation) %}
<li>
<a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}"><i class="fas fa-angle-double-right"></i> <span>{{organisation.name}}</span></a>
<a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}"><i class="fas fa-sitemap fa-fw"></i> <span>{{ translate("ORGANISATION.SELF") }}</span></a>
</li>
{% endif %}
{% endfor %}
</ul>
</li>
{% endif %}
{% endif %}
{% endblock %}
{% block navigation %}
{{ parent() }}
{{ block('organisations_nav') }}
{% endblock %}

View File

@@ -84,12 +84,60 @@
{% endblock %}
{% block latest_organisations %}
{% if (hasRole('site-admin') or hasRole('organisations-admin')) %}
<div class="row">
<div class="col-sm-12">
<!-- ORGANISTIONS LIST -->
<div class="box box-info">
<div class="box-header with-border">
<h3 class="box-title">{{translate("ORGANISATION.LATEST")}}</h3>
</div>
<!-- /.box-header -->
<div class="box-body no-padding clearfix">
{% for organisation in organisations %}
<div class="col-sm-6 col-xs-12">
<div class="box box-widget widget-user-2 widget-organisations">
<div class="widget-user-header bg-green">
<h3 class="widget-user-username">{{organisation.name}}</h3>
<h5 class="widget-user-desc">{{organisation.description}}</h5>
</div>
<div class="box-footer no-padding">
<ul class="nav nav-stacked">
<li><a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}">{{translate("CREATED_ON")}} <span class="pull-right badge {% if organisation.flag_approved %}bg-green{% else %}bg-yellow{% endif %}">{{organisation.created_at}}</span></a></li>
<li><a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}">{{translate("MEMBER", 2)}} <span class="pull-right badge bg-aqua">{{organisation.members.count}}</span></a></li>
<li><a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}">{{translate("ADMIN", 2)}} <span class="pull-right badge bg-aqua">{{organisation.administrators.count}}</span></a></li>
<li><a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}">{{translate("PENDING", 2)}} <span class="pull-right badge {% if organisation.pendingMembers.count == 0 %}bg-green{% else %}bg-yellow{% endif %}">{{organisation.pendingMembers.count}}</span></a></li>
</ul>
</div>
</div>
</div>
{% endfor %}
<!-- /.organistions-list -->
</div>
<!-- /.box-body -->
<div class="box-footer text-center">
<a href="{{site.uri.public}}/organisations" class="uppercase">{{translate("ORGANISATION.VIEW_ALL")}}</a>
</div>
<!-- /.box-footer -->
</div>
<!--/.box -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
{% endif %}
{% endblock %}
{% set empty_dashboard = true %}
{% block main_panels %}
<div class="row">
{% if
checkAccess('uri_users') or
checkAccess('view_system_info') or
checkAccess('uri_activities') or
checkAccess('view_group_field', {
'group': current_user.group,
'property': 'users'
@@ -97,115 +145,67 @@
hasRole('site-admin') or
hasRole('organisations-admin')
%}
{% set empty_dashboard = false %}
<div class="row">
{% block left_panels %}
{% if checkAccess('uri_users') or checkAccess('view_system_info') or (hasRole('site-admin') or hasRole('organisations-admin')) %}
<div class="col-md-6 col-sm-12 col-xs-12">
{% block latest_users %}{{ parent() }}{% endblock %}
<div class="col-md-6 col-sm-12 col-xs-12">
{{ block("latest_users") }}
{% block latest_organisations %}
{% if (hasRole('site-admin') or hasRole('organisations-admin')) and checkAccess('uri_activities') %}
<div class="row">
<div class="col-sm-12">
<!-- ORGANISTIONS LIST -->
<div class="box box-info">
<div class="box-header with-border">
<h3 class="box-title">{{translate("ORGANISATION.LATEST")}}</h3>
</div>
<!-- /.box-header -->
<div class="box-body no-padding clearfix">
{% for organisation in organisations %}
<div class="col-sm-6 col-xs-12">
<div class="box box-widget widget-user-2 widget-organisations">
<div class="widget-user-header bg-green">
<h3 class="widget-user-username">{{organisation.name}}</h3>
<h5 class="widget-user-desc">{{organisation.description}}</h5>
</div>
<div class="box-footer no-padding">
<ul class="nav nav-stacked">
<li><a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}">{{translate("CREATED_ON")}} <span class="pull-right badge {% if organisation.flag_approved %}bg-green{% else %}bg-yellow{% endif %}">{{organisation.created_at}}</span></a></li>
<li><a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}">{{translate("MEMBER", 2)}} <span class="pull-right badge bg-aqua">{{organisation.members.count}}</span></a></li>
<li><a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}">{{translate("ADMIN", 2)}} <span class="pull-right badge bg-aqua">{{organisation.administrators.count}}</span></a></li>
<li><a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}">{{translate("PENDING", 2)}} <span class="pull-right badge {% if organisation.pendingMembers.count == 0 %}bg-green{% else %}bg-yellow{% endif %}">{{organisation.pendingMembers.count}}</span></a></li>
</ul>
</div>
</div>
</div>
{% endfor %}
<!-- /.organistions-list -->
</div>
<!-- /.box-body -->
<div class="box-footer text-center">
<a href="{{site.uri.public}}/organisations" class="uppercase">{{translate("ORGANISATION.VIEW_ALL")}}</a>
</div>
<!-- /.box-footer -->
</div>
<!--/.box -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
{% endif %}
{% endblock %}
{% block system_info %}{{ parent() }}{% endblock %}
</div>
<!-- /.col -->
{% endif %}
{{ block("group_users_summary") }}
</div>
<!-- /.col -->
{% endblock %}
{% block right_panels %}
{% block activities_summary %}{{ parent() }}{% endblock %}
{% block organisations_summary %}
{% if ((hasRole('site-admin') or hasRole('organisations-admin'))) and not checkAccess('uri_activities') %}
<div class="col-md-6 col-sm-12 col-xs-12">
<!-- ORGANISTIONS LIST -->
<div class="box box-info">
<div class="box-header with-border">
<h3 class="box-title">{{translate("ORGANISATION.LATEST")}}</h3>
</div>
<!-- /.box-header -->
<div class="box-body no-padding clearfix">
{% for organisation in organisations %}
<div class="col-sm-6 col-xs-12">
<div class="box box-widget widget-user-2 widget-organisations">
<div class="widget-user-header bg-green">
<h3 class="widget-user-username">{{organisation.name}}</h3>
<h5 class="widget-user-desc">{{organisation.description}}</h5>
</div>
<div class="box-footer no-padding">
<ul class="nav nav-stacked">
<li><a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}">{{translate("CREATED_ON")}} <span class="pull-right badge {% if organisation.flag_approved %}bg-green{% else %}bg-yellow{% endif %}">{{organisation.created_at}}</span></a></li>
<li><a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}">{{translate("MEMBER", 2)}} <span class="pull-right badge bg-aqua">{{organisation.members.count}}</span></a></li>
<li><a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}">{{translate("ADMIN", 2)}} <span class="pull-right badge bg-aqua">{{organisation.administrators.count}}</span></a></li>
<li><a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}">{{translate("PENDING", 2)}} <span class="pull-right badge {% if organisation.pendingMembers.count == 0 %}bg-green{% else %}bg-yellow{% endif %}">{{organisation.pendingMembers.count}}</span></a></li>
</ul>
</div>
</div>
</div>
{% endfor %}
<!-- /.organistions-list -->
</div>
<!-- /.box-body -->
<div class="box-footer text-center">
<a href="{{site.uri.public}}/organisations" class="uppercase">{{translate("ORGANISATION.VIEW_ALL")}}</a>
</div>
<!-- /.box-footer -->
</div>
<!--/.box -->
</div>
{% endif %}
{% endblock %}
{% block group_users_summary %}{{ parent() }}{% endblock %}
<div class="col-md-6 col-sm-12 col-xs-12">
{{ block("latest_organisations") }}
</div>
{% endblock %}
{% else %}
{% block user_welcome %}{{ parent() }}{% endblock %}
{% endif %}
</div>
<!-- /.row -->
{% endif %}
{% if checkAccess('uri_activities') %}
{% set empty_dashboard = false %}
<div class="row">
{% if checkAccess('uri_activities') %}
<div class="col-md-12 col-sm-12 col-xs-12">
<div id="widget-activities" class="box box-primary">
<div class="box-header">
<h3 class="box-title"><i class="fas fa-tasks fa-fw"></i> {{translate('ACTIVITY', 2)}}</h3>
{% include "tables/table-tool-menu.html.twig" %}
</div>
<div class="box-body">
{% include "tables/activities.html.twig" with {
"table" : {
"id" : "table-activities",
"columns" : ["user"]
}
}
%}
</div>
</div>
</div>
{% endif %}
</div>
<!-- /.row -->
{% endif %}
{% if
checkAccess('view_system_info') or
hasRole('site-admin')
%}
{% set empty_dashboard = false %}
<div class="row">
<div class="col-md-6 col-sm-12 col-xs-12">
{{ block("system_info") }}
</div>
<!-- /.col -->
</div>
<!-- /.row -->
{% endif %}
{% if empty_dashboard == true %}
{{ block("user_welcome") }}
{% endif %}
{% endblock %}

View File

@@ -1,5 +1,6 @@
{% extends "tables/users.html.twig" %}
{% use 'tables/partials/organisation-members/column-info.html.twig' %}
{% use 'tables/partials/organisation-members/column-status.html.twig' %}
{% use 'tables/partials/organisation-members/column-actions.html.twig' %}

View File

@@ -19,9 +19,9 @@
<table id="{{table.id}}" class="tablesorter table table-bordered table-hover table-striped" data-sortlist="{{table.sortlist}}">
<thead>
<tr>
<th class="sorter-metatext" data-column-name="name" data-column-template="#{{table.id}}-column-info" data-priority="1">{{translate('ORGANISATION')}} <i class="fas fa-sort"></i></th>
<th class="sorter-metatext filter-metatext" data-column-name="name" data-column-template="#{{table.id}}-column-info" data-priority="1">{{translate('ORGANISATION')}} <i class="fas fa-sort"></i></th>
{% if 'description' not in table.hidden_columns %}
<th class="sorter-metatext" data-column-name="description" data-column-template="#{{table.id}}-column-description" data-priority="2">{{translate("DESCRIPTION")}} <i class="fas fa-sort"></i></th>
<th class="sorter-metatext filter-metatext" data-column-name="description" data-column-template="#{{table.id}}-column-description" data-priority="2">{{translate("DESCRIPTION")}} <i class="fas fa-sort"></i></th>
{% endif %}
{% if hasRole('site-admin') or hasRole('organisations-admin') or (current_user.adminForOrganisations.count > 0) -%}
<th class="filter-select filter-metatext" data-column-name="status" data-column-template="#{{table.id}}-column-status" data-priority="2">{{translate("STATUS")}} <i class="fas fa-sort"></i></th>

View File

@@ -0,0 +1,19 @@
{% block table_cell_template_info %}
<script id="{{table.id}}-column-info" type="text/x-handlebars-template">
{%- verbatim %}
<td data-text="{{row.last_name}}">
<strong>
{% endverbatim -%}{% if isOrganisationAdmin(organisation) %}{%- verbatim %}
<a href="{{site.uri.public}}/users/u/{{row.user_name}}">{{row.first_name}} {{row.last_name}} ({{row.user_name}})</a>
{% endverbatim -%}{% else %}{%- verbatim %}
{{row.first_name}} {{row.last_name}} ({{row.user_name}})
{% endverbatim -%}{% endif %}{%- verbatim %}
</strong>
<div class="js-copy-container">
<span class="js-copy-target">{{row.email}}</span>
<button class="btn btn-xs uf-copy-trigger js-copy-trigger"><i class="fas fa-copy"></i></button>
</div>
</td>
{% endverbatim -%}
</script>
{% endblock %}