Release 2.0.25 (#4066)
* added support for hourly rate column in detail table * allow to register icon in extension * allow to show QR code secret * new translations * bump theme and packages * fix validation for invoice-document-filenames with uppercase character * max upload size 1MB * fix last month in daterange-picker in certain situations, more years in quick-select * remove unused package-versions-deprecated * link preferences from contract warning message * added page_setup page layout * prevent DROP TABLE in addSQL() and replace drop table with schema call * added azuyalabs/yasumi
This commit is contained in:
@@ -1,29 +1,19 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% extends 'page_setup.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
{% set withWorkHourConfiguration = user.hasWorkHourConfiguration() %}
|
||||
|
||||
{% if form is defined %}
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
{{ form_start(form, {'attr': {'class': 'form-reporting', 'id': 'report-form'}}) }}
|
||||
<div class="btn-list w-100">
|
||||
{{ form_widget(form.date) }}
|
||||
{% if form.user is defined %}
|
||||
{{ form_widget(form.user, {'label': false}) }}
|
||||
{% elseif app.user != user %}
|
||||
{{ widgets.username(user) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if not withWorkHourConfiguration %}
|
||||
{{ widgets.alert('info', 'work_times_should.none_configured'|trans) }}
|
||||
{% set warningMsg %}
|
||||
{{ 'work_times_should.none_configured'|trans }}
|
||||
{% if is_granted('contract', user) %}
|
||||
<a href="{{ path('user_profile_contract', {'username': user.getUserIdentifier()}) }}" class="alert-link">{{ 'action.edit'|trans }}</a>
|
||||
{% endif %}
|
||||
{% endset %}
|
||||
{% from '@theme/components/alert.html.twig' import alert %}
|
||||
{{ alert({type: 'info', description: warningMsg|raw}) }}
|
||||
{% endif %}
|
||||
|
||||
{% if summaries|length > 0 %}
|
||||
@@ -299,13 +289,6 @@
|
||||
|
||||
document.addEventListener('kimai.initialized', function() {
|
||||
KimaiReloadPageWidget.create('kimai.workingTimesUpdate', true);
|
||||
|
||||
const contractForm = document.getElementById('report-form');
|
||||
if (contractForm !== null) {
|
||||
contractForm.addEventListener('change', () => {
|
||||
contractForm.submit();
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% import "macros/datatables.html.twig" as tables %}
|
||||
|
||||
{% if dataTable is not defined and page_setup is defined and page_setup.hasDataTable() %}
|
||||
{% set dataTable = page_setup.getDataTable() %}
|
||||
{% endif %}
|
||||
|
||||
{% block main_before %}
|
||||
{{ tables.configuration(dataTable) }}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
{% set showUserColumn = showUserColumn ?? true %}
|
||||
{% set showInternalRate = showInternalRate ?? false %}
|
||||
{% set showRateColumn = showRateColumn ?? is_granted('view_rate_other_timesheet') %}
|
||||
{% set showHourlyRateColumn = showRateColumn and (showHourlyRateColumn ?? false) %}
|
||||
{% set showSummaryHourlyRate = showRateColumn and (showSummaryHourlyRate ?? false) %}
|
||||
{% set showRateBudget = showRateBudget ?? false %}
|
||||
{% set showTimeBudget = showTimeBudget ?? false %}
|
||||
{% set showCustomerSummary = showCustomerSummary ?? true %}
|
||||
{% set showTotalSummary = showTotalSummary ?? true %}
|
||||
{% set showDateTimeShort = showDateTimeShort ?? false %}
|
||||
{% set showSummaryHourlyRate = showSummaryHourlyRate ?? false %}
|
||||
{% set now = create_date('now', app.user) %}
|
||||
{% set decimal = decimal ?? false %}
|
||||
{# this is only triggered, if a user exports from his personal timesheet screen #}
|
||||
@@ -228,6 +229,9 @@ mpdf-->
|
||||
<th>{{ 'user'|trans }}</th>
|
||||
{% endif %}
|
||||
<th>{{ 'description'|trans }}</th>
|
||||
{% if showHourlyRateColumn %}
|
||||
<th class="center">{{ 'hourlyRate'|trans }}</th>
|
||||
{% endif %}
|
||||
<th class="center">{{ 'duration'|trans }}</th>
|
||||
{% if showRateColumn %}
|
||||
{% if showInternalRate %}
|
||||
@@ -241,10 +245,11 @@ mpdf-->
|
||||
{% for entry in entries %}
|
||||
<!-- CONTENT_PART -->
|
||||
{% set duration = duration + entry.duration %}
|
||||
{% set entryCurrency = entry.project.customer.currency %}
|
||||
{% if currency is same as(false) %}
|
||||
{% set currency = entry.project.customer.currency %}
|
||||
{% set currency = entryCurrency %}
|
||||
{% endif %}
|
||||
{% if currency is not same as(entry.project.customer.currency) %}
|
||||
{% if currency is not same as(entryCurrency) %}
|
||||
{% set currency = null %}
|
||||
{% endif %}
|
||||
<tr class="{{ cycle(['odd', 'even'], loop.index0) }}">
|
||||
@@ -266,14 +271,29 @@ mpdf-->
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</td>
|
||||
<td class="duration">{{ entry.duration|duration(decimal) }}</td>
|
||||
{% if showHourlyRateColumn %}
|
||||
<td class="cost">
|
||||
{% if entry.fixedRate %}
|
||||
-
|
||||
{% else %}
|
||||
{{ entry.hourlyRate|money(entryCurrency) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="duration">
|
||||
{% if entry.duration == 0 %}
|
||||
-
|
||||
{% else %}
|
||||
{{ entry.duration|duration(decimal) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if showRateColumn %}
|
||||
{# no check for is_granted('view_rate', entry) because it is only available for timesheets,
|
||||
but maybe missing for other potential export repositories #}
|
||||
{% set rate = rate + entry.rate %}
|
||||
{% set rateInternal = rateInternal + entry.internalRate %}
|
||||
{% set entryRate = entry.rate|money(entry.project.customer.currency) %}
|
||||
{% set entryRateInternal = entry.internalRate|money(entry.project.customer.currency) %}
|
||||
{% set entryRate = entry.rate|money(entryCurrency) %}
|
||||
{% set entryRateInternal = entry.internalRate|money(entryCurrency) %}
|
||||
{% if showInternalRate %}
|
||||
<td class="cost">{{ entryRateInternal }}</td>
|
||||
{% endif %}
|
||||
@@ -287,6 +307,9 @@ mpdf-->
|
||||
{% else %}
|
||||
<td colspan="2">{{ 'sum.total'|trans }}</td>
|
||||
{% endif %}
|
||||
{% if showHourlyRateColumn %}
|
||||
<td class="totals cost"></td>
|
||||
{% endif %}
|
||||
<td class="totals duration">{{ duration|duration(decimal) }}</td>
|
||||
{% if showRateColumn %}
|
||||
{% if showInternalRate %}
|
||||
|
||||
@@ -174,7 +174,7 @@
|
||||
<button type="button" class="btn dropdown-toggle btn-duration-preset" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
|
||||
<div class="dropdown-menu dropdown-menu-start pre-scrollable">
|
||||
{% for title, range in ranges %}
|
||||
<a class="dropdown-item" href="#" data-form-widget="copy-data" data-target="#{{ form.vars.id }}" data-value="{% if range[0] is not null %}{{ range[0]|format_date('short', rangeFormat) }}{{ attr['data-separator'] }}{{ range[1]|format_date('short', rangeFormat) }}{% endif %}" data-event="change keyup">{{ ('daterangepicker.' ~ title)|trans({}, 'daterangepicker') }}</a>
|
||||
<a class="dropdown-item" href="#" data-form-widget="copy-data" data-target="#{{ form.vars.id }}" data-value="{% if range[0] is not null %}{{ range[0]|format_date('short', rangeFormat) }}{{ attr['data-separator'] }}{{ range[1]|format_date('short', rangeFormat) }}{% endif %}" data-event="change keyup">{{ title|trans({}, 'daterangepicker') }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -261,7 +261,6 @@
|
||||
{%- endblock secret_widget %}
|
||||
|
||||
{% block yearpicker_widget -%}
|
||||
{%- set functionName = 'year_' ~ form.vars.id %}
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-left btn-icon" href="#" data-toggle="tooltip" data-placement="top" title="{{ previousYear|date_short }}"
|
||||
data-form-widget="copy-data" data-target="#{{ form.vars.id }}" data-value="{{ previousYear|report_date }}" data-event-bubbles="change">
|
||||
@@ -286,7 +285,6 @@
|
||||
{%- endblock yearpicker_widget %}
|
||||
|
||||
{% block monthpicker_widget -%}
|
||||
{%- set functionName = 'month_' ~ form.vars.id %}
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-left btn-icon" href="#" data-toggle="tooltip" data-placement="top" title="{{ previousMonth|month_name(true) }}"
|
||||
data-form-widget="copy-data" data-target="#{{ form.vars.id }}" data-value="{{ previousMonth|report_date }}" data-event-bubbles="change">
|
||||
@@ -305,7 +303,6 @@
|
||||
{%- endblock monthpicker_widget %}
|
||||
|
||||
{% block weekpicker_widget -%}
|
||||
{%- set functionName = 'week_' ~ form.vars.id %}
|
||||
<div class="btn-group week-picker-btn-group {{ attr.class ?? '' }}">
|
||||
<a class="btn btn-left btn-icon" href="#" data-toggle="tooltip" data-placement="top" title="{{ 'stats.workingTimeWeek'|trans({'%week%': previousWeek|date_format('W')}) }}"
|
||||
data-form-widget="copy-data" data-target="#{{ form.vars.id }}" data-value="{{ previousWeek|report_date }}" data-event-bubbles="change">
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
{{- datatable_column_class(dataTable.getTableName(), column) -}}
|
||||
{% endmacro %}
|
||||
|
||||
{# compare templates/page_setup.html.twig block table_actions #}
|
||||
{% macro actions(dataTable) %}
|
||||
{% set btnClass = '' %}
|
||||
{% set bgClass = '' %}
|
||||
|
||||
28
templates/page_setup.html.twig
Normal file
28
templates/page_setup.html.twig
Normal file
@@ -0,0 +1,28 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
|
||||
{% block table_actions %}
|
||||
{# compare templates/macros/datatables.html.twig macro actions(dataTable) #}
|
||||
{% if page_setup is defined and page_setup.hasPaginationForm() %}
|
||||
<div class="btn-list">
|
||||
{% set form = page_setup.getPaginationForm() %}
|
||||
{{ form_start(form, {'attr': {'onchange': 'this.submit()'}}) }}
|
||||
<div class="btn-list w-100 flex-nowrap">
|
||||
{% if form.date is defined %}
|
||||
{{ form_widget(form.date) }}
|
||||
{% endif %}
|
||||
{% if form.user is defined %}
|
||||
{{ form_widget(form.user) }}
|
||||
{% elseif user is defined and app.user != user %}
|
||||
{{ widgets.username(user) }}
|
||||
{% endif %}
|
||||
{% for field in form %}
|
||||
{% if not field.rendered %}
|
||||
{{ form_widget(field) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -1,5 +1,4 @@
|
||||
{% extends kimai_context.modalRequest ? 'form.html.twig' : 'base.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
|
||||
{% block main %}
|
||||
{{ include(kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig', {
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
{{ 'profile.2fa_intro'|trans }}
|
||||
</p>
|
||||
<p>
|
||||
<img alt="TOTP QR Code" style="max-width: 200px; max-height: 200px;" src="{{ qr_code.dataUri }}" />
|
||||
<img onclick="document.getElementById('totp_secret').classList.toggle('d-none');" data-toggle="tooltip" title="Click to show code" alt="TOTP QR Code" style="max-width: 200px; max-height: 200px;" src="{{ qr_code.dataUri }}" />
|
||||
<span id="totp_secret" style="display: block" class="d-none">{{ secret }}</span>
|
||||
</p>
|
||||
{% if user.totpAuthenticationEnabled %}
|
||||
<div class="row mb-3">
|
||||
|
||||
Reference in New Issue
Block a user