replaced delete confirm dialog with modal (#638)

This commit is contained in:
Kevin Papst
2019-03-12 23:24:22 +01:00
committed by GitHub
parent 46655ad462
commit f4d53e9006
43 changed files with 303 additions and 109 deletions

View File

@@ -44,19 +44,12 @@ $(function() {
// auto hide success message after x seconds, as they are just meant as quick feedback and // auto hide success message after x seconds, as they are just meant as quick feedback and
// not as a permanent source of information // not as a permanent source of information
if ($.kimai.settings['alertSuccessAutoHide'] > 0) {
setTimeout( setTimeout(
function() { function() {
$('div.alert-success').alert('close'); $('div.alert-success').alert('close');
}, },
$.kimai.settings['alertSuccessAutoHide'] 5000
); );
}
// ask before a delete call is executed
$('body').on('click', 'a.btn-trash', function (event) {
return confirm($.kimai.settings['confirmDelete']);
});
// compound field in toolbar // compound field in toolbar
this.activateDateRangePicker('.content-wrapper'); this.activateDateRangePicker('.content-wrapper');
@@ -107,6 +100,7 @@ $(function() {
}); });
}, },
reloadDatatableWithToolbarFilter: function() { reloadDatatableWithToolbarFilter: function() {
// TODO check if toolbar form is present, if not, reload current URL
var $form = $('.toolbar form'); var $form = $('.toolbar form');
var loading = '<div class="overlay"><i class="fas fa-sync fa-spin"></i></div>'; var loading = '<div class="overlay"><i class="fas fa-sync fa-spin"></i></div>';
$('section.content').append(loading); $('section.content').append(loading);
@@ -230,6 +224,8 @@ $(function() {
// load new form from given content // load new form from given content
if ($(html).find('#form_modal .modal-content').length > 0 ) { if ($(html).find('#form_modal .modal-content').length > 0 ) {
// switch classes, in case the modal type changed
$('#remote_form_modal').attr('class', $(html).find('#form_modal').attr('class'));
// TODO cleanup widgets before replacing the content? // TODO cleanup widgets before replacing the content?
$('#remote_form_modal .modal-content').replaceWith( $('#remote_form_modal .modal-content').replaceWith(
$(html).find('#form_modal .modal-content') $(html).find('#form_modal .modal-content')
@@ -312,8 +308,6 @@ $(function() {
// default values // default values
$.kimai.defaults = { $.kimai.defaults = {
locale: 'en', locale: 'en',
alertSuccessAutoHide: 5000,
confirmDelete: 'Really delete?',
today: 'Today', today: 'Today',
yesterday: 'Yesterday', yesterday: 'Yesterday',
apply: 'Apply', apply: 'Apply',

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
{ {
"build/app.js": "./app.js?689ff5ff33eeac75779e", "build/app.js": "./app.js?2adac3fa8d223ed843ed",
"build/app.css": "./app.css?d1c5aa3942706f7bffb181e234e112de", "build/app.css": "./app.css?d1c5aa3942706f7bffb181e234e112de",
"build/images/blue@2x.png": "./images/blue@2x.png?2694acfd", "build/images/blue@2x.png": "./images/blue@2x.png?2694acfd",
"build/images/blue.png": "./images/blue.png?96f8a905", "build/images/blue.png": "./images/blue.png?96f8a905",

View File

@@ -603,7 +603,7 @@ class KimaiImporterCommand extends Command
->setFax($oldCustomer['fax']) ->setFax($oldCustomer['fax'])
->setHomepage($oldCustomer['homepage']) ->setHomepage($oldCustomer['homepage'])
->setMobile($oldCustomer['mobile']) ->setMobile($oldCustomer['mobile'])
->setMail($oldCustomer['mail']) ->setEmail($oldCustomer['mail'])
->setPhone($oldCustomer['phone']) ->setPhone($oldCustomer['phone'])
->setContact($oldCustomer['contact']) ->setContact($oldCustomer['contact'])
->setAddress($oldCustomer['street'] . PHP_EOL . $oldCustomer['zipcode'] . ' ' . $oldCustomer['city']) ->setAddress($oldCustomer['street'] . PHP_EOL . $oldCustomer['zipcode'] . ' ' . $oldCustomer['city'])

View File

@@ -135,12 +135,12 @@ class ActivityController extends AbstractController
$deleteForm->handleRequest($request); $deleteForm->handleRequest($request);
if (0 == $stats->getRecordAmount() || ($deleteForm->isSubmitted() && $deleteForm->isValid())) { if ($deleteForm->isSubmitted() && $deleteForm->isValid()) {
try { try {
$this->getRepository()->deleteActivity($activity, $deleteForm->get('activity')->getData()); $this->getRepository()->deleteActivity($activity, $deleteForm->get('activity')->getData());
$this->flashSuccess('action.delete.success'); $this->flashSuccess('action.delete.success');
} catch (ORMException $ex) { } catch (ORMException $ex) {
$this->flashError('action.delete.error'); $this->flashError('action.delete.error', ['%reason%' => $ex->getMessage()]);
} }
return $this->redirectToRoute('admin_activity'); return $this->redirectToRoute('admin_activity');

View File

@@ -146,12 +146,12 @@ class CustomerController extends AbstractController
$deleteForm->handleRequest($request); $deleteForm->handleRequest($request);
if (0 == $stats->getRecordAmount() || ($deleteForm->isSubmitted() && $deleteForm->isValid())) { if ($deleteForm->isSubmitted() && $deleteForm->isValid()) {
try { try {
$this->getRepository()->deleteCustomer($customer, $deleteForm->get('customer')->getData()); $this->getRepository()->deleteCustomer($customer, $deleteForm->get('customer')->getData());
$this->flashSuccess('action.delete.success'); $this->flashSuccess('action.delete.success');
} catch (ORMException $ex) { } catch (ORMException $ex) {
$this->flashError('action.delete.error'); $this->flashError('action.delete.error', ['%reason%' => $ex->getMessage()]);
} }
return $this->redirectToRoute('admin_customer'); return $this->redirectToRoute('admin_customer');

View File

@@ -133,12 +133,12 @@ class ProjectController extends AbstractController
$deleteForm->handleRequest($request); $deleteForm->handleRequest($request);
if (0 == $stats->getRecordAmount() || ($deleteForm->isSubmitted() && $deleteForm->isValid())) { if ($deleteForm->isSubmitted() && $deleteForm->isValid()) {
try { try {
$this->getRepository()->deleteProject($project, $deleteForm->get('project')->getData()); $this->getRepository()->deleteProject($project, $deleteForm->get('project')->getData());
$this->flashSuccess('action.delete.success'); $this->flashSuccess('action.delete.success');
} catch (ORMException $ex) { } catch (ORMException $ex) {
$this->flashError('action.delete.error'); $this->flashError('action.delete.error', ['%reason%' => $ex->getMessage()]);
} }
return $this->redirectToRoute('admin_project'); return $this->redirectToRoute('admin_project');

View File

@@ -13,6 +13,7 @@ use App\Entity\Timesheet;
use App\Form\TimesheetEditForm; use App\Form\TimesheetEditForm;
use App\Form\Toolbar\TimesheetToolbarForm; use App\Form\Toolbar\TimesheetToolbarForm;
use App\Repository\Query\TimesheetQuery; use App\Repository\Query\TimesheetQuery;
use Doctrine\ORM\ORMException;
use Pagerfanta\Pagerfanta; use Pagerfanta\Pagerfanta;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -228,17 +229,28 @@ class TimesheetController extends AbstractController
*/ */
public function deleteAction(Timesheet $entry, Request $request) public function deleteAction(Timesheet $entry, Request $request)
{ {
try { $deleteForm = $this->createFormBuilder()
$entityManager = $this->getDoctrine()->getManager(); ->setAction($this->generateUrl('timesheet_delete', ['id' => $entry->getId()]))
$entityManager->remove($entry); ->setMethod('POST')
$entityManager->flush(); ->getForm();
$deleteForm->handleRequest($request);
if ($deleteForm->isSubmitted() && $deleteForm->isValid()) {
try {
$this->getRepository()->delete($entry);
$this->flashSuccess('action.delete.success'); $this->flashSuccess('action.delete.success');
} catch (\Exception $ex) { } catch (ORMException $ex) {
$this->flashError('action.delete.error', ['%reason%' => $ex->getMessage()]); $this->flashError('action.delete.error', ['%reason%' => $ex->getMessage()]);
} }
return $this->redirectToRoute('timesheet_paginated', ['page' => $request->get('page', 1)]); return $this->redirectToRoute('timesheet');
}
return $this->render('timesheet/delete.html.twig', [
'timesheet' => $entry,
'form' => $deleteForm->createView(),
]);
} }
/** /**

View File

@@ -13,6 +13,7 @@ use App\Entity\Timesheet;
use App\Form\TimesheetEditForm; use App\Form\TimesheetEditForm;
use App\Form\Toolbar\TimesheetToolbarForm; use App\Form\Toolbar\TimesheetToolbarForm;
use App\Repository\Query\TimesheetQuery; use App\Repository\Query\TimesheetQuery;
use Doctrine\ORM\ORMException;
use Pagerfanta\Pagerfanta; use Pagerfanta\Pagerfanta;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -152,17 +153,28 @@ class TimesheetTeamController extends AbstractController
*/ */
public function deleteAction(Timesheet $entry, Request $request) public function deleteAction(Timesheet $entry, Request $request)
{ {
try { $deleteForm = $this->createFormBuilder()
$entityManager = $this->getDoctrine()->getManager(); ->setAction($this->generateUrl('admin_timesheet_delete', ['id' => $entry->getId()]))
$entityManager->remove($entry); ->setMethod('POST')
$entityManager->flush(); ->getForm();
$deleteForm->handleRequest($request);
if ($deleteForm->isSubmitted() && $deleteForm->isValid()) {
try {
$this->getRepository()->delete($entry);
$this->flashSuccess('action.delete.success'); $this->flashSuccess('action.delete.success');
} catch (\Exception $ex) { } catch (ORMException $ex) {
$this->flashError('action.delete.error', ['%reason%' => $ex->getMessage()]); $this->flashError('action.delete.error', ['%reason%' => $ex->getMessage()]);
} }
return $this->redirectToRoute('admin_timesheet_paginated', ['page' => $request->get('page', 1)]); return $this->redirectToRoute('admin_timesheet');
}
return $this->render('timesheet-team/delete.html.twig', [
'timesheet' => $entry,
'form' => $deleteForm->createView(),
]);
} }
/** /**

View File

@@ -147,7 +147,7 @@ class UserController extends AbstractController
$deleteForm->handleRequest($request); $deleteForm->handleRequest($request);
if (0 == $stats->getRecordsTotal() || ($deleteForm->isSubmitted() && $deleteForm->isValid())) { if ($deleteForm->isSubmitted() && $deleteForm->isValid()) {
$entityManager = $this->getDoctrine()->getManager(); $entityManager = $this->getDoctrine()->getManager();
$entityManager->remove($userToDelete); $entityManager->remove($userToDelete);
$entityManager->flush(); $entityManager->flush();

View File

@@ -82,6 +82,7 @@ class ThemeEvent extends Event
public function setPayload($payload) public function setPayload($payload)
{ {
$this->payload = $payload; $this->payload = $payload;
return $this; return $this;
} }
} }

View File

@@ -30,6 +30,18 @@ class TimesheetRepository extends AbstractRepository
public const STATS_QUERY_ACTIVE = 'active'; public const STATS_QUERY_ACTIVE = 'active';
public const STATS_QUERY_MONTHLY = 'monthly'; public const STATS_QUERY_MONTHLY = 'monthly';
/**
* @param Timesheet $timesheet
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
*/
public function delete(Timesheet $timesheet)
{
$entityManager = $this->getEntityManager();
$entityManager->remove($timesheet);
$entityManager->flush();
}
/** /**
* @param Timesheet $timesheet * @param Timesheet $timesheet
* @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\ORMException

View File

@@ -79,5 +79,4 @@ class EventExtensions extends AbstractExtension
return $themeEvent; return $themeEvent;
} }
} }

View File

@@ -1,6 +1,4 @@
{% extends 'base.html.twig' %} {% extends app.request.xmlHttpRequest ? 'form.html.twig' : 'base.html.twig' %}
{% import "macros/widgets.html.twig" as widgets %}
{% import "macros/datatables.html.twig" as tables %}
{% import "macros/actions.html.twig" as actions %} {% import "macros/actions.html.twig" as actions %}
{% block page_title %}{{ 'admin_activity.title'|trans }}{% endblock %} {% block page_title %}{{ 'admin_activity.title'|trans }}{% endblock %}
@@ -9,6 +7,8 @@
{% block main %} {% block main %}
{% set inUse = (stats.recordAmount > 0) %}
{% set params = { {% set params = {
'%activity%': '<strong>' ~ activity.name ~ '</strong>', '%activity%': '<strong>' ~ activity.name ~ '</strong>',
'%project%': '<strong>-</strong>', '%project%': '<strong>-</strong>',
@@ -23,9 +23,11 @@
'%customer%': '<strong>' ~ activity.project.customer.name ~ '</strong>', '%customer%': '<strong>' ~ activity.project.customer.name ~ '</strong>',
}) %} }) %}
{% endif %} {% endif %}
{{ include('default/_form_delete.html.twig', {
{{ include(app.request.xmlHttpRequest ? 'default/_form_delete_modal.html.twig' : 'default/_form_delete.html.twig', {
'message': "admin_activity.delete_confirm"|trans(params)|raw, 'message': "admin_activity.delete_confirm"|trans(params)|raw,
'form': form, 'form': form,
'used': inUse,
'back': path('admin_activity') 'back': path('admin_activity')
}) }} }) }}

View File

@@ -113,7 +113,6 @@
$(document).ready(function () { $(document).ready(function () {
$.kimai.init({ $.kimai.init({
locale: '{{ app.request.locale }}', locale: '{{ app.request.locale }}',
confirmDelete: '{{ 'confirm.delete'|trans }}',
apply: '{{ 'daterangepicker.apply'|trans({}, 'daterangepicker') }}', apply: '{{ 'daterangepicker.apply'|trans({}, 'daterangepicker') }}',
cancel: '{{ 'daterangepicker.cancel'|trans({}, 'daterangepicker') }}', cancel: '{{ 'daterangepicker.cancel'|trans({}, 'daterangepicker') }}',
today: '{{ 'daterangepicker.today'|trans({}, 'daterangepicker') }}', today: '{{ 'daterangepicker.today'|trans({}, 'daterangepicker') }}',

View File

@@ -1,6 +1,4 @@
{% extends 'base.html.twig' %} {% extends app.request.xmlHttpRequest ? 'form.html.twig' : 'base.html.twig' %}
{% import "macros/widgets.html.twig" as widgets %}
{% import "macros/datatables.html.twig" as tables %}
{% import "macros/actions.html.twig" as actions %} {% import "macros/actions.html.twig" as actions %}
{% block page_title %}{{ 'admin_customer.title'|trans }}{% endblock %} {% block page_title %}{{ 'admin_customer.title'|trans }}{% endblock %}
@@ -9,6 +7,8 @@
{% block main %} {% block main %}
{% set inUse = (stats.recordAmount > 0) %}
{% set params = { {% set params = {
'%activity%': '<strong>' ~ stats.activityAmount ~ '</strong>', '%activity%': '<strong>' ~ stats.activityAmount ~ '</strong>',
'%project%': '<strong>' ~ stats.projectAmount ~ '</strong>', '%project%': '<strong>' ~ stats.projectAmount ~ '</strong>',
@@ -17,9 +17,10 @@
'%duration%': '<strong>' ~ stats.recordDuration|duration ~ '</strong>' '%duration%': '<strong>' ~ stats.recordDuration|duration ~ '</strong>'
} %} } %}
{{ include('default/_form_delete.html.twig', { {{ include(app.request.xmlHttpRequest ? 'default/_form_delete_modal.html.twig' : 'default/_form_delete.html.twig', {
'message': "admin_customer.delete_confirm"|trans(params)|raw, 'message': "admin_customer.delete_confirm"|trans(params)|raw,
'form': form, 'form': form,
'used': inUse,
'back': path('admin_customer') 'back': path('admin_customer')
}) }} }) }}

View File

@@ -0,0 +1,24 @@
{% embed 'embeds/modal.html.twig' %}
{% block modal_id %}form_modal{% endblock %}
{% block modal_class %}{% if used %}modal-danger{% endif %}{% endblock %}
{% block modal_before %}{{ form_start(form) }}{% endblock %}
{% block modal_title %}
{{ title|default('confirm.delete'|trans) }}
{% endblock %}
{% block modal_body %}
{% if used is same as (false) %}
{{ 'delete.not_in_use'|trans }}
<div class="hidden">
{{ form_widget(form) }}
</div>
{% else %}
<p>{{ message|default('confirm.delete_message'|trans)|raw }}</p>
{{ form_widget(form) }}
{% endif %}
{% endblock %}
{% block modal_footer %}
<button type="button" class="btn btn-default btn-cancel" data-dismiss="modal">{{ 'action.close'|trans }}</button>
<button type="submit" class="btn {% if used %}btn-outline{% else %}btn-primary{% endif %} modal-form-save" data-loading-text="{{ 'action.delete'|trans }}..." id="{{ block('modal_id') }}_save">{{ 'action.delete'|trans }}</button>
{% endblock %}
{% block modal_end %}{{ form_end(form) }}{% endblock %}
{% endembed %}

View File

@@ -12,7 +12,7 @@
{% endblock %} {% endblock %}
{% block modal_footer %} {% block modal_footer %}
<button type="button" class="btn btn-default btn-cancel" data-dismiss="modal">{{ 'action.close'|trans }}</button> <button type="button" class="btn btn-default btn-cancel" data-dismiss="modal">{{ 'action.close'|trans }}</button>
<button type="submit" class="btn btn-primary modal-form-save" data-loading-text="Loading..." id="{{ block('modal_id') }}_save">{{ 'action.save'|trans }}</button> <button type="submit" class="btn btn-primary modal-form-save" data-loading-text="{{ 'action.save'|trans }}..." id="{{ block('modal_id') }}_save">{{ 'action.save'|trans }}</button>
{% endblock %} {% endblock %}
{% block modal_end %}{{ form_end(form) }}{% endblock %} {% block modal_end %}{{ form_end(form) }}{% endblock %}
{% endembed %} {% endembed %}

View File

@@ -1,4 +1,4 @@
<div class="modal fade" id="{{ block('modal_id') }}" tabindex="-1" role="dialog" aria-labelledby="{{ block('modal_id') }}_label"> <div class="modal{% if block('modal_class') is defined %} {{ block('modal_class') }}{% endif %} fade" id="{{ block('modal_id') }}" tabindex="-1" role="dialog" aria-labelledby="{{ block('modal_id') }}_label">
<div class="modal-dialog modal-lg" role="document"> <div class="modal-dialog modal-lg" role="document">
<div class="modal-content"> <div class="modal-content">
{% if block('modal_before') is defined %}{{ block('modal_before') }}{% endif %} {% if block('modal_before') is defined %}{{ block('modal_before') }}{% endif %}

View File

@@ -19,8 +19,8 @@
{% if is_granted('edit', activity) %} {% if is_granted('edit', activity) %}
{% set actions = actions|merge({'edit': path('admin_activity_edit', {'id': activity.id})}) %} {% set actions = actions|merge({'edit': path('admin_activity_edit', {'id': activity.id})}) %}
{% endif %} {% endif %}
{% if is_granted('delete', activity) %} {% if view == 'index' and is_granted('delete', activity) %}
{% set actions = actions|merge({'trash': path('admin_activity_delete', {'id': activity.id})}) %} {% set actions = actions|merge({'trash': {'url': path('admin_activity_delete', {'id': activity.id}), 'class': 'modal-ajax-form'}}) %}
{% endif %} {% endif %}
{% endif %} {% endif %}
@@ -59,8 +59,8 @@
{% if is_granted('view_activity') %} {% if is_granted('view_activity') %}
{% set actions = actions|merge({'activity': path('admin_activity', {'customer': project.customer.id, 'project': project.id})}) %} {% set actions = actions|merge({'activity': path('admin_activity', {'customer': project.customer.id, 'project': project.id})}) %}
{% endif %} {% endif %}
{% if is_granted('delete', project) %} {% if view == 'index' and is_granted('delete', project) %}
{% set actions = actions|merge({'trash': path('admin_project_delete', {'id': project.id})}) %} {% set actions = actions|merge({'trash': {'url': path('admin_project_delete', {'id': project.id}), 'class': 'modal-ajax-form'}}) %}
{% endif %} {% endif %}
{% endif %} {% endif %}
@@ -99,8 +99,8 @@
{% if is_granted('view_project') %} {% if is_granted('view_project') %}
{% set actions = actions|merge({'project': path('admin_project', {'customer': customer.id})}) %} {% set actions = actions|merge({'project': path('admin_project', {'customer': customer.id})}) %}
{% endif %} {% endif %}
{% if is_granted('delete', customer) %} {% if view == 'index' and is_granted('delete', customer) %}
{% set actions = actions|merge({'trash': path('admin_customer_delete', {'id': customer.id})}) %} {% set actions = actions|merge({'trash': {'url': path('admin_customer_delete', {'id': customer.id}), 'class': 'modal-ajax-form'}}) %}
{% endif %} {% endif %}
{% endif %} {% endif %}
@@ -156,8 +156,8 @@
{% set actions = actions|merge({'edit': {'url': path('timesheet_edit', {'id': timesheet.id}), 'class': class}}) %} {% set actions = actions|merge({'edit': {'url': path('timesheet_edit', {'id': timesheet.id}), 'class': class}}) %}
{% endif %} {% endif %}
{% if is_granted('delete', timesheet) %} {% if view == 'index' and is_granted('delete', timesheet) %}
{% set actions = actions|merge({'trash': path('timesheet_delete', {'id' : timesheet.id})}) %} {% set actions = actions|merge({'trash': {'url': path('timesheet_delete', {'id' : timesheet.id}), 'class': 'modal-ajax-form'}}) %}
{% endif %} {% endif %}
{% endif %} {% endif %}
@@ -197,6 +197,7 @@
{% if not timesheet.end and is_granted('stop', timesheet) %} {% if not timesheet.end and is_granted('stop', timesheet) %}
{% set actions = actions|merge({'stop': path('admin_timesheet_stop', {'id' : timesheet.id})}) %} {% set actions = actions|merge({'stop': path('admin_timesheet_stop', {'id' : timesheet.id})}) %}
{% endif %} {% endif %}
{% if is_granted('edit', timesheet) %} {% if is_granted('edit', timesheet) %}
{% set class = '' %} {% set class = '' %}
{% if view != 'edit' %} {% if view != 'edit' %}
@@ -204,8 +205,9 @@
{% endif %} {% endif %}
{% set actions = actions|merge({'edit': {'url': path('admin_timesheet_edit', {'id': timesheet.id}), 'class': class}}) %} {% set actions = actions|merge({'edit': {'url': path('admin_timesheet_edit', {'id': timesheet.id}), 'class': class}}) %}
{% endif %} {% endif %}
{% if is_granted('delete', timesheet) %}
{% set actions = actions|merge({'trash': path('admin_timesheet_delete', {'id' : timesheet.id})}) %} {% if view == 'index' and is_granted('delete', timesheet) %}
{% set actions = actions|merge({'trash': {'url': path('admin_timesheet_delete', {'id' : timesheet.id}), 'class': 'modal-ajax-form'}}) %}
{% endif %} {% endif %}
{% endif %} {% endif %}

View File

@@ -1,6 +1,4 @@
{% extends 'base.html.twig' %} {% extends app.request.xmlHttpRequest ? 'form.html.twig' : 'base.html.twig' %}
{% import "macros/widgets.html.twig" as widgets %}
{% import "macros/datatables.html.twig" as tables %}
{% import "macros/actions.html.twig" as actions %} {% import "macros/actions.html.twig" as actions %}
{% block page_title %}{{ 'admin_project.title'|trans }}{% endblock %} {% block page_title %}{{ 'admin_project.title'|trans }}{% endblock %}
@@ -9,6 +7,8 @@
{% block main %} {% block main %}
{% set inUse = (stats.recordAmount > 0) %}
{% set params = { {% set params = {
'%project%': '<strong>' ~ project.name ~ '</strong>', '%project%': '<strong>' ~ project.name ~ '</strong>',
'%customer%': '<strong>' ~ project.customer.name ~ '</strong>', '%customer%': '<strong>' ~ project.customer.name ~ '</strong>',
@@ -17,9 +17,10 @@
'%duration%': '<strong>' ~ stats.recordDuration|duration ~ '</strong>' '%duration%': '<strong>' ~ stats.recordDuration|duration ~ '</strong>'
} %} } %}
{{ include('default/_form_delete.html.twig', { {{ include(app.request.xmlHttpRequest ? 'default/_form_delete_modal.html.twig' : 'default/_form_delete.html.twig', {
'message': "admin_project.delete_confirm"|trans(params)|raw, 'message': "admin_project.delete_confirm"|trans(params)|raw,
'form': form, 'form': form,
'used': inUse,
'back': path('admin_project') 'back': path('admin_project')
}) }} }) }}

View File

@@ -51,7 +51,7 @@
<i class="{{ 'help'|icon }} text-gray"></i> <i class="{{ 'help'|icon }} text-gray"></i>
</div> </div>
<div class="menu-info"> <div class="menu-info">
<h4 class="control-sidebar-subheading">{{ 'help.title'|trans }}</h4> <h4 class="control-sidebar-subheading">{{ 'home.help'|trans({}, 'sidebar') }}</h4>
</div> </div>
</a> </a>
</li> </li>

View File

@@ -0,0 +1,17 @@
{% extends app.request.xmlHttpRequest ? 'form.html.twig' : 'base.html.twig' %}
{% import "macros/actions.html.twig" as actions %}
{% block page_title %}{{ 'admin_timesheet.title'|trans }}{% endblock %}
{% block page_subtitle %}{{ 'admin_timesheet.subtitle'|trans }}{% endblock %}
{% block page_actions %}{{ actions.timesheet_team(timesheet, 'delete') }}{% endblock %}
{% block main %}
{{ include(app.request.xmlHttpRequest ? 'default/_form_delete_modal.html.twig' : 'default/_form_delete.html.twig', {
'message': "delete.not_in_use"|trans|raw,
'form': form,
'used': false,
'back': path('admin_timesheet')
}) }}
{% endblock %}

View File

@@ -0,0 +1,17 @@
{% extends app.request.xmlHttpRequest ? 'form.html.twig' : 'base.html.twig' %}
{% import "macros/actions.html.twig" as actions %}
{% block page_title %}{{ 'timesheet.title'|trans }}{% endblock %}
{% block page_subtitle %}{{ 'timesheet.subtitle'|trans }}{% endblock %}
{% block page_actions %}{{ actions.timesheet(timesheet, 'delete') }}{% endblock %}
{% block main %}
{{ include(app.request.xmlHttpRequest ? 'default/_form_delete_modal.html.twig' : 'default/_form_delete.html.twig', {
'message': "delete.not_in_use"|trans|raw,
'form': form,
'used': false,
'back': path('timesheet')
}) }}
{% endblock %}

View File

@@ -1,4 +1,4 @@
{% extends 'base.html.twig' %} {% extends app.request.xmlHttpRequest ? 'form.html.twig' : 'base.html.twig' %}
{% import "macros/widgets.html.twig" as widgets %} {% import "macros/widgets.html.twig" as widgets %}
{% import "macros/datatables.html.twig" as tables %} {% import "macros/datatables.html.twig" as tables %}
@@ -7,16 +7,19 @@
{% block main %} {% block main %}
{% set inUse = (stats.recordsTotal > 0) %}
{% set params = { {% set params = {
'%user%': '<strong>' ~ widgets.username(user) ~ '</strong>', '%user%': '<strong>' ~ widgets.username(user) ~ '</strong>',
'%records%': '<strong>' ~ stats.recordsTotal ~ '</strong>', '%records%': '<strong>' ~ stats.recordsTotal ~ '</strong>',
'%duration%': '<strong>' ~ stats.durationTotal|duration ~ '</strong>' '%duration%': '<strong>' ~ stats.durationTotal|duration ~ '</strong>'
} %} } %}
{{ include('default/_form_delete.html.twig', { {{ include(app.request.xmlHttpRequest ? 'default/_form_delete_modal.html.twig' : 'default/_form_delete.html.twig', {
'message': "admin_user.delete_confirm"|trans(params)|raw, 'message': "admin_user.delete_confirm"|trans(params)|raw,
'form': form, 'form': form,
'back': path('admin_activity') 'used': inUse,
'back': path('admin_user')
}) }} }) }}
{% endblock %} {% endblock %}

View File

@@ -58,7 +58,7 @@
{% set actionButtons = actionButtons|merge({'timesheet': path('admin_timesheet', {'user' : entry.id})}) %} {% set actionButtons = actionButtons|merge({'timesheet': path('admin_timesheet', {'user' : entry.id})}) %}
{% endif %} {% endif %}
{% if is_granted('delete', entry) %} {% if is_granted('delete', entry) %}
{% set actionButtons = actionButtons|merge({'trash': path('admin_user_delete', {'id': entry.id})}) %} {% set actionButtons = actionButtons|merge({'trash': {'url': path('admin_user_delete', {'id': entry.id}), 'class': 'modal-ajax-form'}}) %}
{% endif %} {% endif %}
{{ widgets.button_group(actionButtons) }} {{ widgets.button_group(actionButtons) }}
</td> </td>

View File

@@ -9,13 +9,11 @@
namespace App\Tests\Controller; namespace App\Tests\Controller;
use App\Entity\Activity;
use App\Entity\Timesheet; use App\Entity\Timesheet;
use App\Entity\User; use App\Entity\User;
use App\Tests\DataFixtures\ActivityFixtures; use App\Tests\DataFixtures\ActivityFixtures;
use App\Tests\DataFixtures\ProjectFixtures; use App\Tests\DataFixtures\ProjectFixtures;
use App\Tests\DataFixtures\TimesheetFixtures; use App\Tests\DataFixtures\TimesheetFixtures;
use Gedmo\Loggable\Entity\LogEntry;
/** /**
* @coversDefaultClass \App\Controller\ActivityController * @coversDefaultClass \App\Controller\ActivityController
@@ -122,7 +120,12 @@ class ActivityControllerTest extends ControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful()); $this->assertTrue($client->getResponse()->isSuccessful());
$this->request($client, '/admin/activity/1/delete'); $this->request($client, '/admin/activity/1/delete');
$this->assertIsRedirect($client, $this->createUrl('/admin/activity/'));
$this->assertTrue($client->getResponse()->isSuccessful());
$form = $client->getCrawler()->filter('form[name=form]')->form();
$this->assertStringEndsWith($this->createUrl('/admin/activity/1/delete'), $form->getUri());
$client->submit($form);
$client->followRedirect(); $client->followRedirect();
$this->assertHasFlashDeleteSuccess($client); $this->assertHasFlashDeleteSuccess($client);
$this->assertHasNoEntriesWithFilter($client); $this->assertHasNoEntriesWithFilter($client);

View File

@@ -9,12 +9,10 @@
namespace App\Tests\Controller; namespace App\Tests\Controller;
use App\Entity\Customer;
use App\Entity\Timesheet; use App\Entity\Timesheet;
use App\Entity\User; use App\Entity\User;
use App\Tests\DataFixtures\CustomerFixtures; use App\Tests\DataFixtures\CustomerFixtures;
use App\Tests\DataFixtures\TimesheetFixtures; use App\Tests\DataFixtures\TimesheetFixtures;
use Gedmo\Loggable\Entity\LogEntry;
/** /**
* @coversDefaultClass \App\Controller\CustomerController * @coversDefaultClass \App\Controller\CustomerController
@@ -92,9 +90,12 @@ class CustomerControllerTest extends ControllerBaseTest
$this->request($client, '/admin/customer/2/edit'); $this->request($client, '/admin/customer/2/edit');
$this->assertTrue($client->getResponse()->isSuccessful()); $this->assertTrue($client->getResponse()->isSuccessful());
$this->request($client, '/admin/customer/2/delete'); $this->request($client, '/admin/customer/2/delete');
$this->assertIsRedirect($client, $this->createUrl('/admin/customer/')); $this->assertTrue($client->getResponse()->isSuccessful());
$form = $client->getCrawler()->filter('form[name=form]')->form();
$this->assertStringEndsWith($this->createUrl('/admin/customer/2/delete'), $form->getUri());
$client->submit($form);
$client->followRedirect(); $client->followRedirect();
$this->assertHasDataTable($client); $this->assertHasDataTable($client);

View File

@@ -9,13 +9,11 @@
namespace App\Tests\Controller; namespace App\Tests\Controller;
use App\Entity\Project;
use App\Entity\Timesheet; use App\Entity\Timesheet;
use App\Entity\User; use App\Entity\User;
use App\Tests\DataFixtures\CustomerFixtures; use App\Tests\DataFixtures\CustomerFixtures;
use App\Tests\DataFixtures\ProjectFixtures; use App\Tests\DataFixtures\ProjectFixtures;
use App\Tests\DataFixtures\TimesheetFixtures; use App\Tests\DataFixtures\TimesheetFixtures;
use Gedmo\Loggable\Entity\LogEntry;
/** /**
* @coversDefaultClass \App\Controller\ProjectController * @coversDefaultClass \App\Controller\ProjectController
@@ -116,9 +114,13 @@ class ProjectControllerTest extends ControllerBaseTest
$this->request($client, '/admin/project/2/edit'); $this->request($client, '/admin/project/2/edit');
$this->assertTrue($client->getResponse()->isSuccessful()); $this->assertTrue($client->getResponse()->isSuccessful());
$this->request($client, '/admin/project/2/delete'); $this->request($client, '/admin/project/2/delete');
$this->assertIsRedirect($client, $this->createUrl('/admin/project/')); $this->assertTrue($client->getResponse()->isSuccessful());
$form = $client->getCrawler()->filter('form[name=form]')->form();
$this->assertStringEndsWith($this->createUrl('/admin/project/2/delete'), $form->getUri());
$client->submit($form);
$client->followRedirect(); $client->followRedirect();
$this->assertHasDataTable($client); $this->assertHasDataTable($client);
$this->assertHasFlashSuccess($client); $this->assertHasFlashSuccess($client);

View File

@@ -157,10 +157,16 @@ class TimesheetControllerTest extends ControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful()); $this->assertTrue($client->getResponse()->isSuccessful());
$this->request($client, '/timesheet/1/delete'); $this->request($client, '/timesheet/1/delete');
$this->assertIsRedirect($client, $this->createUrl('/timesheet/page/1'));
$this->assertTrue($client->getResponse()->isSuccessful());
$form = $client->getCrawler()->filter('form[name=form]')->form();
$this->assertStringEndsWith($this->createUrl('/timesheet/1/delete'), $form->getUri());
$client->submit($form);
$client->followRedirect(); $client->followRedirect();
$this->assertTrue($client->getResponse()->isSuccessful()); $this->assertTrue($client->getResponse()->isSuccessful());
$this->assertHasFlashSuccess($client); $this->assertHasFlashDeleteSuccess($client);
$this->assertHasDataTable($client);
$this->request($client, '/timesheet/1/edit'); $this->request($client, '/timesheet/1/edit');
$this->assertFalse($client->getResponse()->isSuccessful()); $this->assertFalse($client->getResponse()->isSuccessful());

View File

@@ -14,7 +14,6 @@ use App\Entity\User;
use App\Form\Type\DateRangeType; use App\Form\Type\DateRangeType;
use App\Tests\Controller\ControllerBaseTest; use App\Tests\Controller\ControllerBaseTest;
use App\Tests\DataFixtures\TimesheetFixtures; use App\Tests\DataFixtures\TimesheetFixtures;
use Gedmo\Loggable\Entity\LogEntry;
/** /**
* @coversDefaultClass \App\Controller\TimesheetTeamController * @coversDefaultClass \App\Controller\TimesheetTeamController
@@ -147,21 +146,6 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
$this->assertNull($timesheet->getFixedRate()); $this->assertNull($timesheet->getFixedRate());
} }
public function testDeleteActionIsNotAllowedForTeamlead()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$fixture = new TimesheetFixtures();
$fixture->setAmount(10);
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
$fixture->setStartDate('2017-05-01');
$this->importFixture($em, $fixture);
$this->request($client, '/team/timesheet/1/delete');
$this->assertFalse($client->getResponse()->isSuccessful());
}
public function testDeleteAction() public function testDeleteAction()
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN); $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
@@ -177,10 +161,16 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful()); $this->assertTrue($client->getResponse()->isSuccessful());
$this->request($client, '/team/timesheet/1/delete'); $this->request($client, '/team/timesheet/1/delete');
$this->assertIsRedirect($client, $this->createUrl('/team/timesheet/page/1'));
$this->assertTrue($client->getResponse()->isSuccessful());
$form = $client->getCrawler()->filter('form[name=form]')->form();
$this->assertStringEndsWith($this->createUrl('/team/timesheet/1/delete'), $form->getUri());
$client->submit($form);
$client->followRedirect(); $client->followRedirect();
$this->assertTrue($client->getResponse()->isSuccessful()); $this->assertTrue($client->getResponse()->isSuccessful());
$this->assertHasFlashSuccess($client); $this->assertHasFlashDeleteSuccess($client);
$this->assertHasDataTable($client);
$this->request($client, '/team/timesheet/1/edit'); $this->request($client, '/team/timesheet/1/edit');
$this->assertFalse($client->getResponse()->isSuccessful()); $this->assertFalse($client->getResponse()->isSuccessful());

View File

@@ -9,7 +9,9 @@
namespace App\Tests\Controller; namespace App\Tests\Controller;
use App\Entity\Timesheet;
use App\Entity\User; use App\Entity\User;
use App\Tests\DataFixtures\TimesheetFixtures;
/** /**
* @coversDefaultClass \App\Controller\UserController * @coversDefaultClass \App\Controller\UserController
@@ -86,6 +88,60 @@ class UserControllerTest extends ControllerBaseTest
$this->assertEquals(1, $form->get('user_create[create_more]')->getValue()); $this->assertEquals(1, $form->get('user_create[create_more]')->getValue());
} }
public function testDeleteAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$this->request($client, '/admin/user/4/delete');
$this->assertTrue($client->getResponse()->isSuccessful());
$form = $client->getCrawler()->filter('form[name=form]')->form();
$this->assertStringEndsWith($this->createUrl('/admin/user/4/delete'), $form->getUri());
$client->submit($form);
$client->followRedirect();
$this->assertHasDataTable($client);
$this->assertHasFlashSuccess($client);
$this->request($client, '/admin/user/4/edit');
$this->assertFalse($client->getResponse()->isSuccessful());
}
public function testDeleteActionWithTimesheetEntries()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$user = $this->getUserByRole($em, User::ROLE_USER);
$fixture = new TimesheetFixtures();
$fixture->setUser($user);
$fixture->setAmount(10);
$this->importFixture($em, $fixture);
$timesheets = $em->getRepository(Timesheet::class)->findAll();
$this->assertEquals(10, count($timesheets));
$this->request($client, '/admin/user/' . $user->getId() . '/delete');
$this->assertTrue($client->getResponse()->isSuccessful());
$form = $client->getCrawler()->filter('form[name=form]')->form();
$this->assertStringEndsWith($this->createUrl('/admin/user/' . $user->getId() . '/delete'), $form->getUri());
$client->submit($form);
$this->assertIsRedirect($client, $this->createUrl('/admin/user/'));
$client->followRedirect();
$this->assertHasFlashDeleteSuccess($client);
// SQLIte does not necessarly support onCascade delete, so these timesheet will stay after deletion
// $em->clear();
// $timesheets = $em->getRepository(Timesheet::class)->findAll();
// $this->assertEquals(0, count($timesheets));
$this->request($client, '/admin/user/' . $user->getId() . '/edit');
$this->assertFalse($client->getResponse()->isSuccessful());
}
/** /**
* @dataProvider getValidationTestData * @dataProvider getValidationTestData
*/ */

View File

@@ -39,9 +39,11 @@
</trans-unit> </trans-unit>
<trans-unit id="confirm.delete_message"> <trans-unit id="confirm.delete_message">
<source>confirm.delete_message</source> <source>confirm.delete_message</source>
<target> <target>Sie müssen das Löschen erneut bestätigen, da verknüpfte Objekte existieren welche ebenfalls mit gelöscht werden.</target>
Sie müssen das Löschen erneut bestätigen, da verknüpfte Objekte existieren welche ebenfalls mit gelöscht werden. </trans-unit>
</target> <trans-unit id="delete.not_in_use">
<source>delete.not_in_use</source>
<target>Dieses Element kann sicher gelöscht werden.</target>
</trans-unit> </trans-unit>
<!-- <!--

View File

@@ -39,9 +39,11 @@
</trans-unit> </trans-unit>
<trans-unit id="confirm.delete_message"> <trans-unit id="confirm.delete_message">
<source>confirm.delete_message</source> <source>confirm.delete_message</source>
<target> <target>You have to confirm the deletion, as there are linked objects which will be deleted as well.</target>
You have to confirm the deletion, as there are linked objects which will be deleted as well. </trans-unit>
</target> <trans-unit id="delete.not_in_use">
<source>delete.not_in_use</source>
<target>This item can be safely deleted.</target>
</trans-unit> </trans-unit>
<!-- <!--

View File

@@ -9,6 +9,10 @@
لمساعدتي في تحسين Kimai, الرجاء إرسال رسالتك على موقع الويب المرتبط. سواء كان لديك أسئلة أو للتبليغ عن خطأ أو عطل أو أفكار للتحسينات ، فإن تعليقاتك ذات قيمة! لمساعدتي في تحسين Kimai, الرجاء إرسال رسالتك على موقع الويب المرتبط. سواء كان لديك أسئلة أو للتبليغ عن خطأ أو عطل أو أفكار للتحسينات ، فإن تعليقاتك ذات قيمة!
]]></target> ]]></target>
</trans-unit> </trans-unit>
<trans-unit id="home.help">
<source>home.help</source>
<target>مساعدة</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -19,6 +19,10 @@
<source>home.website</source> <source>home.website</source>
<target>Homepage</target> <target>Homepage</target>
</trans-unit> </trans-unit>
<trans-unit id="home.help">
<source>home.help</source>
<target>Hilfe</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -19,6 +19,10 @@
<source>home.website</source> <source>home.website</source>
<target>Homepage</target> <target>Homepage</target>
</trans-unit> </trans-unit>
<trans-unit id="home.help">
<source>home.help</source>
<target>Help</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -11,6 +11,10 @@
Para ayudar a mejorar Kimai, por favor enviar sus comentarios al sitio. Sus comentarios, ideas para mejorar o reportes de errores son valiosos y agradezco el tiempo para enviárnoslos! Para ayudar a mejorar Kimai, por favor enviar sus comentarios al sitio. Sus comentarios, ideas para mejorar o reportes de errores son valiosos y agradezco el tiempo para enviárnoslos!
]]></target> ]]></target>
</trans-unit> </trans-unit>
<trans-unit id="home.help">
<source>home.help</source>
<target>Ayuda</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -11,6 +11,10 @@
Pour m'aider à améliorer Kimai, merci de poster votre message sur le site lié. Que vous ayez des questions, des messages ou des idées d'améliorations, vos retours sont précieux ! Pour m'aider à améliorer Kimai, merci de poster votre message sur le site lié. Que vous ayez des questions, des messages ou des idées d'améliorations, vos retours sont précieux !
]]></target> ]]></target>
</trans-unit> </trans-unit>
<trans-unit id="home.help">
<source>home.help</source>
<target>Aide</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -21,6 +21,10 @@
<source>home.website</source> <source>home.website</source>
<target>Weboldal</target> <target>Weboldal</target>
</trans-unit> </trans-unit>
<trans-unit id="home.help">
<source>home.help</source>
<target>Súgó</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -11,6 +11,10 @@
To help me improve Kimai, please send your message on the linked website. Whether you have questions, error messages or ideas for enhancements, your feedback is valuable! To help me improve Kimai, please send your message on the linked website. Whether you have questions, error messages or ideas for enhancements, your feedback is valuable!
]]></target> ]]></target>
</trans-unit> </trans-unit>
<trans-unit id="home.help">
<source>home.help</source>
<target>Aiuto</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -19,6 +19,10 @@
<source>home.website</source> <source>home.website</source>
<target>Homepage</target> <target>Homepage</target>
</trans-unit> </trans-unit>
<trans-unit id="home.help">
<source>home.help</source>
<target>Ajuda</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -11,6 +11,10 @@
Для помощи в усовершенствовании Kimai, пожалуйства, отправляйте свои сообщения на страницу под ссылкой. Приветствуются любые вопросы, идеи по разработке, в частности на тему дополнительных функций, а также сообщения об ошибках. Нам очень важна Ваша обратная связь! Для помощи в усовершенствовании Kimai, пожалуйства, отправляйте свои сообщения на страницу под ссылкой. Приветствуются любые вопросы, идеи по разработке, в частности на тему дополнительных функций, а также сообщения об ошибках. Нам очень важна Ваша обратная связь!
]]></target> ]]></target>
</trans-unit> </trans-unit>
<trans-unit id="home.help">
<source>home.help</source>
<target>Помощь</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>