export team timesheets (#508)
This commit is contained in:
@@ -62,7 +62,7 @@ kimai:
|
|||||||
INVOICE: [view_invoice,create_invoice]
|
INVOICE: [view_invoice,create_invoice]
|
||||||
INVOICE_TEMPLATE: [view_invoice_template,create_invoice_template,edit_invoice_template,delete_invoice_template]
|
INVOICE_TEMPLATE: [view_invoice_template,create_invoice_template,edit_invoice_template,delete_invoice_template]
|
||||||
TIMESHEET: [view_own_timesheet,start_own_timesheet,stop_own_timesheet,create_own_timesheet,edit_own_timesheet,export_own_timesheet,delete_own_timesheet]
|
TIMESHEET: [view_own_timesheet,start_own_timesheet,stop_own_timesheet,create_own_timesheet,edit_own_timesheet,export_own_timesheet,delete_own_timesheet]
|
||||||
TIMESHEET_OTHER: [view_other_timesheet,start_other_timesheet,stop_other_timesheet,create_other_timesheet,edit_other_timesheet,delete_other_timesheet]
|
TIMESHEET_OTHER: [view_other_timesheet,start_other_timesheet,stop_other_timesheet,create_other_timesheet,edit_other_timesheet,export_other_timesheet,delete_other_timesheet]
|
||||||
PROFILE: [view_own_profile,edit_own_profile,password_own_profile,preferences_own_profile,api-token_own_profile]
|
PROFILE: [view_own_profile,edit_own_profile,password_own_profile,preferences_own_profile,api-token_own_profile]
|
||||||
PROFILE_OTHER: [view_other_profile,edit_other_profile,delete_other_profile,password_other_profile,roles_other_profile,preferences_other_profile,api-token_other_profile]
|
PROFILE_OTHER: [view_other_profile,edit_other_profile,delete_other_profile,password_other_profile,roles_other_profile,preferences_other_profile,api-token_other_profile]
|
||||||
USER: [view_user,create_user,delete_user]
|
USER: [view_user,create_user,delete_user]
|
||||||
|
|||||||
@@ -78,6 +78,39 @@ class TimesheetController extends AbstractController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route(path="/export", name="admin_timesheet_export", methods={"GET"})
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
|
*/
|
||||||
|
public function exportAction(Request $request)
|
||||||
|
{
|
||||||
|
$query = new TimesheetQuery();
|
||||||
|
$query->setOrder(TimesheetQuery::ORDER_ASC);
|
||||||
|
|
||||||
|
$form = $this->getToolbarForm($query);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
/** @var TimesheetQuery $query */
|
||||||
|
$query = $form->getData();
|
||||||
|
if (null !== $query->getBegin()) {
|
||||||
|
$query->getBegin()->setTime(0, 0, 0);
|
||||||
|
}
|
||||||
|
if (null !== $query->getEnd()) {
|
||||||
|
$query->getEnd()->setTime(23, 59, 59);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @var $entries Pagerfanta */
|
||||||
|
$entries = $this->getRepository()->findByQuery($query);
|
||||||
|
|
||||||
|
return $this->render('admin/timesheet_export.html.twig', [
|
||||||
|
'entries' => $entries,
|
||||||
|
'query' => $query,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Route(path="/{id}/stop", name="admin_timesheet_stop", methods={"GET"})
|
* @Route(path="/{id}/stop", name="admin_timesheet_stop", methods={"GET"})
|
||||||
* @Security("is_granted('stop', entry)")
|
* @Security("is_granted('stop', entry)")
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ class TimesheetController extends AbstractController
|
|||||||
public function exportAction(Request $request)
|
public function exportAction(Request $request)
|
||||||
{
|
{
|
||||||
$query = new TimesheetQuery();
|
$query = new TimesheetQuery();
|
||||||
|
$query->setOrder(TimesheetQuery::ORDER_ASC);
|
||||||
|
|
||||||
$form = $this->getToolbarForm($query);
|
$form = $this->getToolbarForm($query);
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class TimesheetVoter extends AbstractVoter
|
|||||||
public const STOP = 'stop';
|
public const STOP = 'stop';
|
||||||
public const EDIT = 'edit';
|
public const EDIT = 'edit';
|
||||||
public const DELETE = 'delete';
|
public const DELETE = 'delete';
|
||||||
|
public const EXPORT = 'export';
|
||||||
public const VIEW_RATE = 'view_rate';
|
public const VIEW_RATE = 'view_rate';
|
||||||
public const EDIT_RATE = 'edit_rate';
|
public const EDIT_RATE = 'edit_rate';
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ class TimesheetVoter extends AbstractVoter
|
|||||||
self::STOP,
|
self::STOP,
|
||||||
self::EDIT,
|
self::EDIT,
|
||||||
self::DELETE,
|
self::DELETE,
|
||||||
|
self::EXPORT,
|
||||||
self::VIEW_RATE,
|
self::VIEW_RATE,
|
||||||
self::EDIT_RATE,
|
self::EDIT_RATE,
|
||||||
];
|
];
|
||||||
@@ -89,6 +91,7 @@ class TimesheetVoter extends AbstractVoter
|
|||||||
case self::STOP:
|
case self::STOP:
|
||||||
case self::EDIT:
|
case self::EDIT:
|
||||||
case self::DELETE:
|
case self::DELETE:
|
||||||
|
case self::EXPORT:
|
||||||
$permission .= $attribute;
|
$permission .= $attribute;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,11 @@
|
|||||||
{% block page_title %}{{ 'admin_timesheet.title'|trans }}{% endblock %}
|
{% block page_title %}{{ 'admin_timesheet.title'|trans }}{% endblock %}
|
||||||
{% block page_subtitle %}{{ 'admin_timesheet.subtitle'|trans }}{% endblock %}
|
{% block page_subtitle %}{{ 'admin_timesheet.subtitle'|trans }}{% endblock %}
|
||||||
{% block page_actions %}
|
{% block page_actions %}
|
||||||
{% set actions = {'filter': '#collapseTimesheetAdmin', 'visibility': '#modal_timesheet_admin'} %}
|
{% set actions = {'filter': '#collapseTimesheetAdmin'} %}
|
||||||
|
{% if is_granted('export_own_timesheet') %}
|
||||||
|
{% set actions = actions|merge({'download': 'onclick:return exportTimesheet()'}) %}
|
||||||
|
{% endif %}
|
||||||
|
{% set actions = actions|merge({'visibility': '#modal_timesheet_admin'}) %}
|
||||||
{% if is_granted('create_other_timesheet') %}
|
{% if is_granted('create_other_timesheet') %}
|
||||||
{% set actions = actions|merge({'create': path('admin_timesheet_create')}) %}
|
{% set actions = actions|merge({'create': path('admin_timesheet_create')}) %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -98,3 +102,17 @@
|
|||||||
|
|
||||||
{{ tables.data_table_footer(entries, 'admin_timesheet_paginated') }}
|
{{ tables.data_table_footer(entries, 'admin_timesheet_paginated') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block javascripts %}
|
||||||
|
{{ parent() }}
|
||||||
|
<script type="text/javascript">
|
||||||
|
function exportTimesheet() {
|
||||||
|
var form = $("div.toolbar form.navbar-form");
|
||||||
|
var prevAction = form.attr('action');
|
||||||
|
form.attr('target', '_blank').attr('action', '{{ path('admin_timesheet_export') }}');
|
||||||
|
form.submit();
|
||||||
|
form.removeAttr('target').attr('action', prevAction);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
78
templates/admin/timesheet_export.html.twig
Normal file
78
templates/admin/timesheet_export.html.twig
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
{% import "macros/widgets.html.twig" as widgets %}
|
||||||
|
{% extends 'invoice/layout.html.twig' %}
|
||||||
|
|
||||||
|
{% block invoice %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<h2 class="page-header">
|
||||||
|
<span contenteditable="true">
|
||||||
|
{% if query.begin is not empty and query.end is not empty %}
|
||||||
|
{% if query.begin|date('m') != query.end|date('m') or query.begin|date('Y') != query.end|date('Y') %}
|
||||||
|
{{ query.begin|date_short }} - {{ query.end|date_short }}
|
||||||
|
{% elseif query.end is not empty %}
|
||||||
|
{{ query.end|month_name|trans }} {{ query.end|date('Y') }}
|
||||||
|
{% elseif query.begin is not empty %}
|
||||||
|
{{ query.begin|month_name|trans }} {{ query.begin|date('Y') }}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% if query.user is not empty %}
|
||||||
|
: {{ widgets.username(query.user) }}
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 table-responsive">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{{ 'label.date'|trans }}</th>
|
||||||
|
{% if query.user is empty %}
|
||||||
|
<th>{{ 'label.username'|trans }}</th>
|
||||||
|
{% endif %}
|
||||||
|
<th>{{ 'label.description'|trans }}</th>
|
||||||
|
<th>{{ 'label.hours'|trans }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% set timeWorked = 0 %}
|
||||||
|
{% for entry in entries %}
|
||||||
|
{% set timeWorked = timeWorked + entry.duration %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ entry.begin|date_short }}</td>
|
||||||
|
{% if query.user is empty %}
|
||||||
|
<td>{{ widgets.username(entry.user) }}</td>
|
||||||
|
{% endif %}
|
||||||
|
<td>
|
||||||
|
{% if entry.description is not empty %}
|
||||||
|
<div>
|
||||||
|
{{ entry.description|desc2html }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<span class="small">
|
||||||
|
{{ 'label.activity'|trans }}: {{ entry.activity.name }} |
|
||||||
|
{{ 'label.project'|trans }}: {{ entry.project.name }} |
|
||||||
|
{{ 'label.customer'|trans }}: {{ entry.project.customer.name }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>{{ entry.duration|duration }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>{{ 'invoice.total_working_time'|trans }}</th>
|
||||||
|
<th>{{ timeWorked|duration }}</th>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block print_button %}{% endblock %}
|
||||||
@@ -44,12 +44,20 @@ class ActivityControllerTest extends ControllerBaseTest
|
|||||||
$this->assertNull($form->get('activity_edit_form[create_more]')->getValue());
|
$this->assertNull($form->get('activity_edit_form[create_more]')->getValue());
|
||||||
$client->submit($form, [
|
$client->submit($form, [
|
||||||
'activity_edit_form' => [
|
'activity_edit_form' => [
|
||||||
'name' => 'Test 2',
|
'name' => 'An AcTiVitY Name',
|
||||||
|
'project' => '1',
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
$this->assertIsRedirect($client, $this->createUrl('/admin/activity/'));
|
$this->assertIsRedirect($client, $this->createUrl('/admin/activity/'));
|
||||||
$client->followRedirect();
|
$client->followRedirect();
|
||||||
$this->assertHasDataTable($client);
|
$this->assertHasDataTable($client);
|
||||||
|
|
||||||
|
$this->request($client, '/admin/activity/2/edit');
|
||||||
|
$editForm = $client->getCrawler()->filter('form[name=activity_edit_form]')->form();
|
||||||
|
$this->assertEquals('An AcTiVitY Name', $editForm->get('activity_edit_form[name]')->getValue());
|
||||||
|
// make sure customer and project are pre-selected for none global activities
|
||||||
|
$this->assertEquals('1', $editForm->get('activity_edit_form[project]')->getValue());
|
||||||
|
$this->assertEquals('1', $editForm->get('activity_edit_form[customer]')->getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCreateActionWithCreateMore()
|
public function testCreateActionWithCreateMore()
|
||||||
@@ -101,6 +109,9 @@ class ActivityControllerTest extends ControllerBaseTest
|
|||||||
$this->request($client, '/admin/activity/1/edit');
|
$this->request($client, '/admin/activity/1/edit');
|
||||||
$editForm = $client->getCrawler()->filter('form[name=activity_edit_form]')->form();
|
$editForm = $client->getCrawler()->filter('form[name=activity_edit_form]')->form();
|
||||||
$this->assertEquals('Test 2', $editForm->get('activity_edit_form[name]')->getValue());
|
$this->assertEquals('Test 2', $editForm->get('activity_edit_form[name]')->getValue());
|
||||||
|
// make sure no customer or project is pre-selected for global activities
|
||||||
|
$this->assertEquals('', $editForm->get('activity_edit_form[customer]')->getValue());
|
||||||
|
$this->assertEquals('', $editForm->get('activity_edit_form[project]')->getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDeleteAction()
|
public function testDeleteAction()
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
|||||||
$this->assertHasDataTable($client);
|
$this->assertHasDataTable($client);
|
||||||
|
|
||||||
$result = $client->getCrawler()->filter('div.breadcrumb div.box-tools div.btn-group a.btn');
|
$result = $client->getCrawler()->filter('div.breadcrumb div.box-tools div.btn-group a.btn');
|
||||||
$this->assertEquals(3, count($result));
|
$this->assertEquals(4, count($result));
|
||||||
|
|
||||||
foreach ($result as $item) {
|
foreach ($result as $item) {
|
||||||
$this->assertEquals('btn btn-default', $item->getAttribute('class'));
|
$this->assertEquals('btn btn-default', $item->getAttribute('class'));
|
||||||
@@ -71,6 +71,44 @@ class TimesheetControllerTest extends ControllerBaseTest
|
|||||||
// TODO more assertions
|
// TODO more assertions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testExportAction()
|
||||||
|
{
|
||||||
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||||
|
|
||||||
|
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
|
||||||
|
$fixture = new TimesheetFixtures();
|
||||||
|
$fixture->setAmount(7);
|
||||||
|
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
|
||||||
|
$fixture->setStartDate(new \DateTime('-10 days'));
|
||||||
|
$this->importFixture($em, $fixture);
|
||||||
|
$fixture = new TimesheetFixtures();
|
||||||
|
$fixture->setAmount(3);
|
||||||
|
$fixture->setUser($this->getUserByRole($em, User::ROLE_TEAMLEAD));
|
||||||
|
$fixture->setStartDate(new \DateTime('-10 days'));
|
||||||
|
$this->importFixture($em, $fixture);
|
||||||
|
|
||||||
|
$this->request($client, '/team/timesheet/');
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
|
$form = $client->getCrawler()->filter('form.navbar-form')->form();
|
||||||
|
$form->getFormNode()->setAttribute('action', $this->createUrl('/team/timesheet/export'));
|
||||||
|
$client->submit($form, [
|
||||||
|
'state' => 1,
|
||||||
|
'pageSize' => 25,
|
||||||
|
'begin' => (new \DateTime('-10 days'))->format('Y-m-d'),
|
||||||
|
'end' => (new \DateTime())->format('Y-m-d'),
|
||||||
|
'customer' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
|
$node = $client->getCrawler()->filter('body');
|
||||||
|
$this->assertEquals('invoice_print', $node->getNode(0)->getAttribute('class'));
|
||||||
|
|
||||||
|
$result = $node->filter('section.invoice table.table tbody tr');
|
||||||
|
$this->assertEquals(10, count($result));
|
||||||
|
}
|
||||||
|
|
||||||
public function testCreateAction()
|
public function testCreateAction()
|
||||||
{
|
{
|
||||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ abstract class AbstractVoterTest extends TestCase
|
|||||||
$invoice = ['view_invoice', 'create_invoice'];
|
$invoice = ['view_invoice', 'create_invoice'];
|
||||||
$invoiceTemplate = ['view_invoice_template', 'create_invoice_template', 'edit_invoice_template', 'delete_invoice_template'];
|
$invoiceTemplate = ['view_invoice_template', 'create_invoice_template', 'edit_invoice_template', 'delete_invoice_template'];
|
||||||
$timesheet = ['view_own_timesheet', 'start_own_timesheet', 'stop_own_timesheet', 'create_own_timesheet', 'edit_own_timesheet', 'export_own_timesheet', 'delete_own_timesheet'];
|
$timesheet = ['view_own_timesheet', 'start_own_timesheet', 'stop_own_timesheet', 'create_own_timesheet', 'edit_own_timesheet', 'export_own_timesheet', 'delete_own_timesheet'];
|
||||||
$timesheetOthers = ['view_other_timesheet', 'start_other_timesheet', 'stop_other_timesheet', 'create_other_timesheet', 'edit_other_timesheet', 'delete_other_timesheet'];
|
$timesheetOthers = ['view_other_timesheet', 'start_other_timesheet', 'stop_other_timesheet', 'create_other_timesheet', 'edit_other_timesheet', 'export_other_timesheet', 'delete_other_timesheet'];
|
||||||
$profile = ['view_own_profile', 'edit_own_profile', 'password_own_profile', 'preferences_own_profile', 'api-token_own_profile'];
|
$profile = ['view_own_profile', 'edit_own_profile', 'password_own_profile', 'preferences_own_profile', 'api-token_own_profile'];
|
||||||
$profileOther = ['view_other_profile', 'edit_other_profile', 'delete_other_profile', 'password_other_profile', 'roles_other_profile', 'preferences_other_profile', 'api-token_other_profile'];
|
$profileOther = ['view_other_profile', 'edit_other_profile', 'delete_other_profile', 'password_other_profile', 'roles_other_profile', 'preferences_other_profile', 'api-token_other_profile'];
|
||||||
$user = ['view_user', 'create_user', 'delete_user'];
|
$user = ['view_user', 'create_user', 'delete_user'];
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ class TimesheetVoterTest extends AbstractVoterTest
|
|||||||
yield [$timeEntry[0], $timeEntry[1], 'stop', $result];
|
yield [$timeEntry[0], $timeEntry[1], 'stop', $result];
|
||||||
yield [$timeEntry[0], $timeEntry[1], 'edit', $result];
|
yield [$timeEntry[0], $timeEntry[1], 'edit', $result];
|
||||||
yield [$timeEntry[0], $timeEntry[1], 'delete', $result];
|
yield [$timeEntry[0], $timeEntry[1], 'delete', $result];
|
||||||
|
yield [$timeEntry[0], $timeEntry[1], 'export', $result];
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = VoterInterface::ACCESS_DENIED;
|
$result = VoterInterface::ACCESS_DENIED;
|
||||||
@@ -73,6 +74,7 @@ class TimesheetVoterTest extends AbstractVoterTest
|
|||||||
yield [$timeEntry[0], $timeEntry[1], 'stop', $result];
|
yield [$timeEntry[0], $timeEntry[1], 'stop', $result];
|
||||||
yield [$timeEntry[0], $timeEntry[1], 'edit', $result];
|
yield [$timeEntry[0], $timeEntry[1], 'edit', $result];
|
||||||
yield [$timeEntry[0], $timeEntry[1], 'delete', $result];
|
yield [$timeEntry[0], $timeEntry[1], 'delete', $result];
|
||||||
|
yield [$timeEntry[0], $timeEntry[1], 'export', $result];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,30 +71,31 @@ The permission-names were chosen to be self-explanatory. In the hope that it wor
|
|||||||
| stop_own_timesheet | TIMESHEET | | - |
|
| stop_own_timesheet | TIMESHEET | | - |
|
||||||
| create_own_timesheet | TIMESHEET | X | - |
|
| create_own_timesheet | TIMESHEET | X | - |
|
||||||
| edit_own_timesheet | TIMESHEET | | - |
|
| edit_own_timesheet | TIMESHEET | | - |
|
||||||
| export_own_timesheet | TIMESHEET | | - |
|
| export_own_timesheet | TIMESHEET | | export your own timesheet in the timesheet panel |
|
||||||
| delete_own_timesheet | TIMESHEET | | - |
|
| delete_own_timesheet | TIMESHEET | | - |
|
||||||
| view_other_timesheet | TIMESHEET_OTHER | | allows access to the complete timesheet view |
|
| view_other_timesheet | TIMESHEET_OTHER | | allows access to the complete timesheet view |
|
||||||
| start_other_timesheet | TIMESHEET_OTHER | | - |
|
| start_other_timesheet | TIMESHEET_OTHER | | - |
|
||||||
| stop_other_timesheet | TIMESHEET_OTHER | | - |
|
| stop_other_timesheet | TIMESHEET_OTHER | | - |
|
||||||
| create_other_timesheet | TIMESHEET_OTHER | | - |
|
| create_other_timesheet | TIMESHEET_OTHER | | - |
|
||||||
| edit_other_timesheet | TIMESHEET_OTHER | | - |
|
| edit_other_timesheet | TIMESHEET_OTHER | | - |
|
||||||
|
| export_other_timesheet | TIMESHEET | | export timesheet in the timesheet admin panel |
|
||||||
| delete_other_timesheet | TIMESHEET_OTHER | | - |
|
| delete_other_timesheet | TIMESHEET_OTHER | | - |
|
||||||
| view_rate_own_timesheet | RATE | | - |
|
| view_rate_own_timesheet | RATE | | - |
|
||||||
| edit_rate_own_timesheet | RATE | | - |
|
| edit_rate_own_timesheet | RATE | | - |
|
||||||
| view_rate_other_timesheet | RATE_OTHER | | - |
|
| view_rate_other_timesheet | RATE_OTHER | | - |
|
||||||
| edit_rate_other_timesheet | RATE_OTHER | | - |
|
| edit_rate_other_timesheet | RATE_OTHER | | - |
|
||||||
| view_own_profile | PROFILE | | Allows access to the own profile view. Without this permission, users cannot access any of their profile settings or passwords ... |
|
| view_own_profile | PROFILE | | allows access to the own profile view - without this permission, users cannot access any of their profile settings or passwords ... |
|
||||||
| edit_own_profile | PROFILE | | - |
|
| edit_own_profile | PROFILE | | grants access to edit the own profile |
|
||||||
| delete_own_profile | PROFILE | | - |
|
| delete_own_profile | PROFILE | | grants access to delete the own profile |
|
||||||
| password_own_profile | PROFILE | | - |
|
| password_own_profile | PROFILE | | grants access to change the own password |
|
||||||
| roles_own_profile | PROFILE | | - |
|
| roles_own_profile | PROFILE | | SECURITY ALERT: grants access to the own roles |
|
||||||
| preferences_own_profile | PROFILE | | - |
|
| preferences_own_profile | PROFILE | | grants access to the own preferences |
|
||||||
| api-token_own_profile | PROFILE | | - |
|
| api-token_own_profile | PROFILE | | grants access to change the own API token |
|
||||||
| view_other_profile | PROFILE_OTHER | | - |
|
| view_other_profile | PROFILE_OTHER | | - |
|
||||||
| edit_other_profile | PROFILE_OTHER | | - |
|
| edit_other_profile | PROFILE_OTHER | | - |
|
||||||
| delete_other_profile | PROFILE_OTHER | | - |
|
| delete_other_profile | PROFILE_OTHER | | - |
|
||||||
| password_other_profile | PROFILE_OTHER | | allows to change the password for another user |
|
| password_other_profile | PROFILE_OTHER | | allows to change the password for another user |
|
||||||
| roles_other_profile | PROFILE_OTHER | | allows to change roles for other users |
|
| roles_other_profile | PROFILE_OTHER | | SECURITY ALERT: allows to change roles for other users |
|
||||||
| preferences_other_profile | PROFILE_OTHER | | allows to change the preferences for another user |
|
| preferences_other_profile | PROFILE_OTHER | | allows to change the preferences for another user |
|
||||||
| api-token_other_profile | PROFILE_OTHER | | allows to set the API login token for other users |
|
| api-token_other_profile | PROFILE_OTHER | | allows to set the API login token for other users |
|
||||||
| hourly-rate_own_profile | - | | allows to edit the own user specific hourly rate |
|
| hourly-rate_own_profile | - | | allows to edit the own user specific hourly rate |
|
||||||
|
|||||||
Reference in New Issue
Block a user