Allow users to see their own organisation pages
This commit is contained in:
@@ -1,5 +1,20 @@
|
||||
{
|
||||
"bundle": {
|
||||
"js/admin": {
|
||||
"scripts": [
|
||||
"vendor/moment/moment.js",
|
||||
"userfrosting/js/handlebars-helpers.js",
|
||||
"vendor/tablesorter/dist/js/jquery.tablesorter.js",
|
||||
"vendor/tablesorter/dist/js/jquery.tablesorter.widgets.js",
|
||||
"userfrosting/js/tablesorter/widget-sort2Hash.js",
|
||||
"vendor/tablesorter/dist/js/widgets/widget-columnSelector.min.js",
|
||||
"vendor/tablesorter/dist/js/widgets/widget-reflow.min.js",
|
||||
"vendor/tablesorter/dist/js/widgets/widget-pager.min.js",
|
||||
"userfrosting/js/query-string.js",
|
||||
"userfrosting/js/uf-table.js",
|
||||
"avsdev/js/sidebar.js"
|
||||
]
|
||||
},
|
||||
"js/pages/organisation": {
|
||||
"scripts": [
|
||||
"userfrosting/js/widgets/users.js",
|
||||
|
||||
23
assets/avsdev/js/sidebar.js
Normal file
23
assets/avsdev/js/sidebar.js
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
$(document).ready(function() {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
$('.sidebar-menu .collapse, .sidebar-menu .collapsing').on('hide.bs.collapse', function() {
|
||||
$(this).prev().find('.fa').eq(1).removeClass('fa-angle-right').addClass('fa-angle-down');
|
||||
var states = JSON.parse(localStorage.getItem('sidebar-states') || {});
|
||||
states[this.id] = 0;
|
||||
localStorage.setItem('sidebar-states', JSON.stringify(states));
|
||||
});
|
||||
$('.sidebar-menu .collapse, sidebar-menu .collapsing').on('show.bs.collapse', function() {
|
||||
$(this).prev().find('.fa').eq(1).removeClass('fa-angle-down').addClass('fa-angle-right');
|
||||
var states = JSON.parse(localStorage.getItem('sidebar-states') || {});
|
||||
states[this.id] = 1;
|
||||
localStorage.setItem('sidebar-states', JSON.stringify(states));
|
||||
});
|
||||
|
||||
var states = JSON.parse(localStorage.getItem('sidebar-states') || {});
|
||||
Object.getOwnPropertyNames(states).forEach((elid) => {
|
||||
if (states[elid] === 1) {
|
||||
$('#' + elid).collapse('show');
|
||||
}
|
||||
});
|
||||
})
|
||||
@@ -36,9 +36,11 @@ return [
|
||||
'DELETE_YES' => 'Yes, delete organisation',
|
||||
'DELETION_SUCCESSFUL' => 'Successfully deleted organisation <strong>{{name}}</strong>',
|
||||
|
||||
'MEMBER_COUNT' => '# Members <sub>(excl admins)</sub>',
|
||||
'MEMBER_COUNT' => '# Members <sub>(excl admins)</sub>',
|
||||
'ADMIN_COUNT' => '# Admins',
|
||||
|
||||
'SELF' => 'My Organisations',
|
||||
|
||||
'NAME' => [
|
||||
1 => 'Organisation name',
|
||||
|
||||
|
||||
@@ -74,10 +74,7 @@ class OrganisationMembersController extends SimpleController
|
||||
|
||||
$sprunje = $classMapper->createInstance('user_sprunje', $classMapper, $params);
|
||||
$sprunje->extendQuery(function ($query) use ($classMapper, $organisation) {
|
||||
return $query
|
||||
->join('organisation_members', function($join) use($organisation) {
|
||||
$join->on('organisation_members.user_id', '=', 'users.id')->where('organisation_id', $organisation->id);
|
||||
});
|
||||
return $query->where('organisation_id', $organisation->id);
|
||||
});
|
||||
|
||||
// Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
|
||||
|
||||
76
src/Database/Models/User.php
Normal file
76
src/Database/Models/User.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* AVSDev UF Organisations (https://avsdev.uk)
|
||||
*
|
||||
* @link https://git.avsdev.uk/avsdev/sprinkle-organisations
|
||||
* @license https://git.avsdev.uk/avsdev/sprinkle-organisations/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||
*/
|
||||
|
||||
namespace UserFrosting\Sprinkle\Organisations\Database\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface;
|
||||
use UserFrosting\Sprinkle\Account\Database\Models\User as UFUser;
|
||||
use UserFrosting\Sprinkle\Account\Facades\Password;
|
||||
use UserFrosting\Sprinkle\Core\Database\Models\Model;
|
||||
use UserFrosting\Sprinkle\Core\Facades\Debug;
|
||||
|
||||
/**
|
||||
* User Class.
|
||||
*
|
||||
* Extends the UF User object by adding organisation membership
|
||||
*
|
||||
* @author Craig Williams (https://avsdev.uk)
|
||||
*/
|
||||
class User extends UFUser
|
||||
{
|
||||
/**
|
||||
* Get all organisations this user is a member of.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function organisations()
|
||||
{
|
||||
/** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
|
||||
$classMapper = static::$ci->classMapper;
|
||||
|
||||
return $this->belongsToMany($classMapper->getClassMapping('organisation'), 'organisation_members', 'user_id', 'organisation_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete this member from the database, along with any links to organisations.
|
||||
*
|
||||
* @param bool $hardDelete Set to true to completely remove the member and all associated objects.
|
||||
*
|
||||
* @return bool true if the deletion was successful, false otherwise.
|
||||
*/
|
||||
public function delete($hardDelete = false)
|
||||
{
|
||||
/** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
|
||||
$classMapper = static::$ci->classMapper;
|
||||
|
||||
if ($hardDelete) {
|
||||
// TODO: remove user from organisations?
|
||||
}
|
||||
|
||||
return parent::delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins the user's organisations directly, so we can do things like sort, search, paginate, etc.
|
||||
*
|
||||
* @param Builder $query
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function scopeJoinOrganisations($query)
|
||||
{
|
||||
$query = $query->leftJoin('organisation_members', 'organisation_members.user_id', '=', 'users.id');
|
||||
$query = $query->leftJoin('organisations', 'organisations.id', '=', 'organisation_members.organisation_id');
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ class OrganisationPermissions extends BaseSeed
|
||||
public function run()
|
||||
{
|
||||
// We require the default roles
|
||||
Seeder::execute('DefaultPermissions');
|
||||
Seeder::execute('DefaultRoles');
|
||||
Seeder::execute('OrganisationRoles');
|
||||
|
||||
@@ -56,6 +57,12 @@ class OrganisationPermissions extends BaseSeed
|
||||
'conditions' => "in(property,['name','slug','description','members'])",
|
||||
'description' => 'View certain properties of any organisation.',
|
||||
]),
|
||||
'view_organisation_field_own' => new Permission([
|
||||
'slug' => 'view_organisation_field',
|
||||
'name' => 'View own organisation',
|
||||
'conditions' => "is_organisation_member(self.id,organisation.id) & in(property,['name','slug','description','members'])",
|
||||
'description' => 'View certain properties of own organisation.',
|
||||
]),
|
||||
'update_organisation_field' => new Permission([
|
||||
'slug' => 'update_organisation_field',
|
||||
'name' => 'Edit organisation',
|
||||
@@ -80,6 +87,12 @@ class OrganisationPermissions extends BaseSeed
|
||||
'conditions' => 'always()',
|
||||
'description' => 'View the organisation page of any organisation.',
|
||||
]),
|
||||
'uri_organisation_own' => new Permission([
|
||||
'slug' => 'uri_organisation',
|
||||
'name' => 'View own organisation',
|
||||
'conditions' => 'is_organisation_member(self.id,organisation.id)',
|
||||
'description' => 'View the organisation page of an organisation you are a member of.',
|
||||
]),
|
||||
'uri_organisations' => new Permission([
|
||||
'slug' => 'uri_organisations',
|
||||
'name' => 'Organisation management page',
|
||||
@@ -144,6 +157,13 @@ class OrganisationPermissions extends BaseSeed
|
||||
$permissions['uri_organisation']->id,
|
||||
]);
|
||||
}
|
||||
|
||||
$roleUser = Role::where('slug', 'user')->first();
|
||||
if ($roleUser) {
|
||||
$roleUser->permissions()->syncWithoutDetaching([
|
||||
$permissions['uri_organisation_own']->id,
|
||||
$permissions['view_organisation_field_own']->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace UserFrosting\Sprinkle\Organisations\ServicesProvider;
|
||||
|
||||
use Illuminate\Database\Capsule\Manager as Capsule;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
@@ -38,10 +39,49 @@ class ServicesProvider
|
||||
$container->extend('classMapper', function ($classMapper, $c) {
|
||||
$classMapper->setClassMapping('organisation', 'UserFrosting\Sprinkle\Organisations\Database\Models\Organisation');
|
||||
$classMapper->setClassMapping('organisation_sprunje', 'UserFrosting\Sprinkle\Organisations\Sprunje\OrganisationSprunje');
|
||||
$classMapper->setClassMapping('user', 'UserFrosting\Sprinkle\Organisations\Database\Models\User');
|
||||
$classMapper->setClassMapping('user_sprunje', 'UserFrosting\Sprinkle\Organisations\Sprunje\UserSprunje');
|
||||
|
||||
return $classMapper;
|
||||
});
|
||||
|
||||
/*
|
||||
* Extend the 'authorizer' service to add extra access condition callbacks.
|
||||
*
|
||||
* @return \UserFrosting\Sprinkle\Core\Util\ClassMapper
|
||||
*/
|
||||
$container->extend('authorizer', function ($authorizer, $c) {
|
||||
/*
|
||||
* Check if all $user is a member of $organisation.
|
||||
*
|
||||
* @param int $user_id the id of the requesting user (normally currentUser->id).
|
||||
* @param int $organisation_id the id of the target organisation.
|
||||
* @return bool true if $user is a member of $organisation.
|
||||
*/
|
||||
$authorizer->addCallback('is_organisation_member', function ($user_id, $organisation_id) {
|
||||
return Capsule::table('organisation_members')
|
||||
->where('user_id', $user_id)
|
||||
->where('organisation_id', $organisation_id)
|
||||
->count() > 0;
|
||||
});
|
||||
|
||||
/*
|
||||
* Check if all $user is an administrator of $organisation.
|
||||
*
|
||||
* @param int $user_id the id of the requesting user (normally currentUser->id).
|
||||
* @param int $organisation_id the id of the target organisation.
|
||||
* @return bool true if $user is an administrator of $organisation.
|
||||
*/
|
||||
$authorizer->addCallback('is_organisation_admin', function ($user_id, $organisation_id) {
|
||||
return Capsule::table('organisation_members')
|
||||
->where('user_id', $user_id)
|
||||
->where('organisation_id', $organisation_id)
|
||||
->where('flag_admin', true)
|
||||
->count() > 0;
|
||||
});
|
||||
|
||||
return $authorizer;
|
||||
});
|
||||
|
||||
/*
|
||||
* Returns a callback that handles merging any organisation objects.
|
||||
@@ -79,6 +119,5 @@ class ServicesProvider
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
85
src/Sprunje/UserSprunje.php
Normal file
85
src/Sprunje/UserSprunje.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* AVSDev UF Organisations (https://avsdev.uk)
|
||||
*
|
||||
* @link https://git.avsdev.uk/avsdev/sprinkle-organisations
|
||||
* @license https://git.avsdev.uk/avsdev/sprinkle-organisations/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||
*/
|
||||
|
||||
namespace UserFrosting\Sprinkle\Organisations\Sprunje;
|
||||
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
use UserFrosting\Sprinkle\Core\Facades\Translator;
|
||||
use UserFrosting\Sprinkle\Core\Sprunje\Sprunje;
|
||||
use UserFrosting\Sprinkle\Admin\Sprunje\UserSprunje as UFUserSprunje;
|
||||
|
||||
/**
|
||||
* UserSprunje.
|
||||
*
|
||||
* Extends Sprunje for the users API.
|
||||
*
|
||||
* @author Craig Williams (https://avsdev.uk)
|
||||
*/
|
||||
class UserSprunje extends UFUserSprunje
|
||||
{
|
||||
protected $filterable = [
|
||||
'name',
|
||||
'organisations',
|
||||
'last_activity',
|
||||
'status',
|
||||
];
|
||||
protected $sortable = [
|
||||
'name',
|
||||
'organisations',
|
||||
'last_activity',
|
||||
'status',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function baseQuery()
|
||||
{
|
||||
$query = parent::baseQuery();
|
||||
|
||||
// Join user's organisations
|
||||
return $query->joinOrganisations()->with('organisations');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort based on organisations names.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function sortOrganisations($query, $direction)
|
||||
{
|
||||
$query->orderBy('organisations.name', $direction);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter LIKE the last activity description.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function filterOrganisations($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('organisations.name', $value);
|
||||
}
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -40,4 +40,16 @@
|
||||
<a href="{{site.uri.public}}/organisations"><i class="fas fa-users 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" class="" aria-expanded="false"><i class="fa fa-fw fa-users"></i> {{ translate("ORGANISATION.SELF") }} <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 %}
|
||||
<li>
|
||||
<a href="{{site.uri.public}}/organisations/o/{{organisation.slug}}"><i class="fas fa-angle-double-right"></i> <span>{{organisation.name}}</span></a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user