user profile, editing and more statistics
This commit is contained in:
@@ -26,19 +26,19 @@ class AppKernel extends Kernel
|
|||||||
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
|
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
|
||||||
new Symfony\Bundle\TwigBundle\TwigBundle(),
|
new Symfony\Bundle\TwigBundle\TwigBundle(),
|
||||||
new Symfony\Bundle\MonologBundle\MonologBundle(),
|
new Symfony\Bundle\MonologBundle\MonologBundle(),
|
||||||
new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
|
|
||||||
new Symfony\Bundle\AsseticBundle\AsseticBundle(),
|
|
||||||
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
|
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
|
||||||
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
|
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
|
||||||
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
|
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
|
||||||
new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(),
|
new Avanzu\AdminThemeBundle\AvanzuAdminThemeBundle(),
|
||||||
new AppBundle\AppBundle(),
|
new AppBundle\AppBundle(),
|
||||||
new TimesheetBundle\TimesheetBundle(),
|
new TimesheetBundle\TimesheetBundle(),
|
||||||
new Avanzu\AdminThemeBundle\AvanzuAdminThemeBundle(),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Bundles only used for development or unit and functional tests
|
// Bundles only used for development or unit and functional tests
|
||||||
if (in_array($this->getEnvironment(), ['dev', 'test'])) {
|
if (in_array($this->getEnvironment(), ['dev', 'test'])) {
|
||||||
|
//$bundles[] = new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle();
|
||||||
|
//$bundles[] = new Symfony\Bundle\AsseticBundle\AsseticBundle();
|
||||||
|
$bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();
|
||||||
$bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
|
$bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
|
||||||
$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
|
$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
|
||||||
$bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
|
$bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- User Account: style can be found in dropdown.less -->
|
{# User Account: style can be found in dropdown.less #}
|
||||||
{% import "AvanzuAdminThemeBundle:layout:macros.html.twig" as macro %}
|
{% import "AvanzuAdminThemeBundle:layout:macros.html.twig" as macro %}
|
||||||
<li class="dropdown user user-menu">
|
<li class="dropdown user user-menu">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
<li class="user-header">
|
<li class="user-header">
|
||||||
{{ macro.avatar(user.avatar, user.username) }}
|
{{ macro.avatar(user.avatar, user.username) }}
|
||||||
<p>
|
<p>
|
||||||
{{user.name}} - {{user.title}}
|
{{user.name}}
|
||||||
<small>{{ 'Member since %date%'|trans({'%date%': user.memberSince|date('m.Y') }, 'AvanzuAdminTheme') }}</small>
|
<small>{{user.title}}</small>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
{#
|
{#
|
||||||
@@ -28,10 +28,10 @@
|
|||||||
<!-- Menu Footer-->
|
<!-- Menu Footer-->
|
||||||
<li class="user-footer">
|
<li class="user-footer">
|
||||||
<div class="pull-left">
|
<div class="pull-left">
|
||||||
<a href="{{ path('profile'|route_alias, {'ident' : user.identifier}) }}" class="btn btn-default btn-flat">{{'Profile'|trans({}, 'AvanzuAdminTheme')}}</a>
|
<a href="{{ path('user_profile', {'username' : user.username}) }}" class="btn btn-default btn-flat">{{ 'label.user_profile'|trans }}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="{{ logout_path() }}" class="btn btn-default btn-flat">{{ 'Sign out'|trans({}, 'AvanzuAdminTheme') }}</a>
|
<a href="{{ logout_path() }}" class="btn btn-default btn-flat">{{ 'label.logout'|trans }}</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
{# Tell the browser to be responsive to screen width #}
|
||||||
|
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
||||||
|
{# Bootstrap 3.3.6 #}
|
||||||
|
<link rel="stylesheet" href="{{ asset('theme/bootstrap/css/bootstrap.min.css') }}">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
{# Ionicons #}
|
||||||
|
{#<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css">#}
|
||||||
|
<!-- Theme style -->
|
||||||
|
<link rel="stylesheet" href="{{ asset('theme/dist/css/AdminLTE.min.css') }}">
|
||||||
|
{# AdminLTE Skins. We have chosen the skin-blue for this starter page. However, you can choose
|
||||||
|
any other skin. Make sure you apply the skin class to the body tag so the changes take effect. #}
|
||||||
|
<link rel="stylesheet" href="{{ asset("theme/dist/css/skins/"~avanzu_admin_context.options.skin~".min.css") }}">
|
||||||
|
{# HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries.
|
||||||
|
WARNING: Respond.js doesn't work if you view the page via file:// #}
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||||
|
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||||
|
<![endif]-->
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
|
||||||
<file source-language="en" target-language="de" datatype="plaintext" original="not.available">
|
|
||||||
<body>
|
|
||||||
<trans-unit id="Member since %date%">
|
|
||||||
<source>Member since %date%</source>
|
|
||||||
<target>Mitglied seit %date%</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="Profile">
|
|
||||||
<source>Profile</source>
|
|
||||||
<target>Profil</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="Sign out">
|
|
||||||
<source>Sign out</source>
|
|
||||||
<target>Abmelden</target>
|
|
||||||
</trans-unit>
|
|
||||||
</body>
|
|
||||||
</file>
|
|
||||||
</xliff>
|
|
||||||
@@ -25,10 +25,22 @@
|
|||||||
<source>security.label.password</source>
|
<source>security.label.password</source>
|
||||||
<target>Passwort</target>
|
<target>Passwort</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="security.label.password_repeat">
|
||||||
|
<source>security.label.password_repeat</source>
|
||||||
|
<target>Passwort wiederholen</target>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="security.action.sign_in">
|
<trans-unit id="security.action.sign_in">
|
||||||
<source>security.action.sign_in</source>
|
<source>security.action.sign_in</source>
|
||||||
<target>Los geht's</target>
|
<target>Los geht's</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="label.logout">
|
||||||
|
<source>label.logout</source>
|
||||||
|
<target>Abmelden</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="label.user_profile">
|
||||||
|
<source>label.user_profile</source>
|
||||||
|
<target>Mein Profil</target>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Menu / Navbar items
|
Menu / Navbar items
|
||||||
@@ -173,6 +185,46 @@
|
|||||||
<source>label.project</source>
|
<source>label.project</source>
|
||||||
<target>Projekt</target>
|
<target>Projekt</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="label.hourly_rate">
|
||||||
|
<source>label.hourly_rate</source>
|
||||||
|
<target>Stundenlohn</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="label.hours">
|
||||||
|
<source>label.hours</source>
|
||||||
|
<target>Stunden</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="label.rate">
|
||||||
|
<source>label.rate</source>
|
||||||
|
<target>Umsatz</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="label.language">
|
||||||
|
<source>label.language</source>
|
||||||
|
<target>Sprache</target>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Buttons & Actions
|
||||||
|
-->
|
||||||
|
<trans-unit id="label.actions">
|
||||||
|
<source>label.actions</source>
|
||||||
|
<target>Aktionen</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="action.edit">
|
||||||
|
<source>action.edit</source>
|
||||||
|
<target>Bearbeiten</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="action.delete">
|
||||||
|
<source>action.delete</source>
|
||||||
|
<target>Löschen</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="action.save">
|
||||||
|
<source>action.save</source>
|
||||||
|
<target>Speichern</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="action.updated_successfully">
|
||||||
|
<source>action.updated_successfully</source>
|
||||||
|
<target>Änderungen erfolgreich gespeichert</target>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Dashboard
|
Dashboard
|
||||||
@@ -213,6 +265,10 @@
|
|||||||
<source>badge.invisible</source>
|
<source>badge.invisible</source>
|
||||||
<target>Nein</target>
|
<target>Nein</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="label.toggle_dropdown">
|
||||||
|
<source>label.toggle_dropdown</source>
|
||||||
|
<target>Menü anzeigen</target>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Timesheet
|
Timesheet
|
||||||
@@ -237,6 +293,30 @@
|
|||||||
<source>profile.subtitle</source>
|
<source>profile.subtitle</source>
|
||||||
<target>Verwalte Deine Einstellungen</target>
|
<target>Verwalte Deine Einstellungen</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="profile.about_me">
|
||||||
|
<source>profile.about_me</source>
|
||||||
|
<target>Über mich</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="profile.first_entry">
|
||||||
|
<source>profile.first_entry</source>
|
||||||
|
<target>Arbeitet seit</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="profile.tab_monthly">
|
||||||
|
<source>profile.tab_monthly</source>
|
||||||
|
<target>Jahresstatistik</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="profile.avatar">
|
||||||
|
<source>profile.avatar</source>
|
||||||
|
<target>Profilbild</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="profile.settings">
|
||||||
|
<source>profile.settings</source>
|
||||||
|
<target>Einstellungen</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="profile.password">
|
||||||
|
<source>profile.password</source>
|
||||||
|
<target>Passwort</target>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Admin: Timesheet
|
Admin: Timesheet
|
||||||
@@ -285,6 +365,14 @@
|
|||||||
<source>admin_user.subtitle</source>
|
<source>admin_user.subtitle</source>
|
||||||
<target>Alle registrierten Nutzer</target>
|
<target>Alle registrierten Nutzer</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="edit_profile.title">
|
||||||
|
<source>edit_profile.title</source>
|
||||||
|
<target>Profil editieren</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="edit_profile.subtitle">
|
||||||
|
<source>edit_profile.subtitle</source>
|
||||||
|
<target>Einstellungen von %username% ändern</target>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="label.alias">
|
<trans-unit id="label.alias">
|
||||||
<source>label.alias</source>
|
<source>label.alias</source>
|
||||||
<target>Name</target>
|
<target>Name</target>
|
||||||
@@ -346,6 +434,57 @@
|
|||||||
<target>Umsatz total</target>
|
<target>Umsatz total</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Month names
|
||||||
|
-->
|
||||||
|
<trans-unit id="month.1">
|
||||||
|
<source>month.1</source>
|
||||||
|
<target>Januar</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="month.2">
|
||||||
|
<source>month.2</source>
|
||||||
|
<target>Februar</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="month.3">
|
||||||
|
<source>month.3</source>
|
||||||
|
<target>März</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="month.4">
|
||||||
|
<source>month.4</source>
|
||||||
|
<target>April</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="month.5">
|
||||||
|
<source>month.5</source>
|
||||||
|
<target>Mai</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="month.6">
|
||||||
|
<source>month.6</source>
|
||||||
|
<target>Juni</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="month.7">
|
||||||
|
<source>month.7</source>
|
||||||
|
<target>Juli</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="month.8">
|
||||||
|
<source>month.8</source>
|
||||||
|
<target>August</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="month.9">
|
||||||
|
<source>month.9</source>
|
||||||
|
<target>September</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="month.10">
|
||||||
|
<source>month.10</source>
|
||||||
|
<target>Oktober</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="month.11">
|
||||||
|
<source>month.11</source>
|
||||||
|
<target>November</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="month.12">
|
||||||
|
<source>month.12</source>
|
||||||
|
<target>Dezember</target>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|||||||
@@ -16,17 +16,24 @@
|
|||||||
'label.title': 'graduation-cap',
|
'label.title': 'graduation-cap',
|
||||||
'label.active': 'lock',
|
'label.active': 'lock',
|
||||||
'label.roles': 'users',
|
'label.roles': 'users',
|
||||||
|
'label.actions': 'pencil-square-o',
|
||||||
}) }}
|
}) }}
|
||||||
|
|
||||||
{% for entry in entries %}
|
{% for entry in entries %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ entry.id }}</td>
|
<td>{{ entry.id }}</td>
|
||||||
<td>{{ entry.username }}</td>
|
<td><a href="{{ path('user_profile', {'username' : entry.username}) }}">{{ entry.username }}</a></td>
|
||||||
<td>{{ entry.alias }}</td>
|
<td>{{ entry.alias }}</td>
|
||||||
<td>{{ entry.email }}</td>
|
<td>{{ entry.email }}</td>
|
||||||
<td>{{ entry.title }}</td>
|
<td>{{ entry.title }}</td>
|
||||||
<td>{{ widgets.label_visible(entry.active) }}</td>
|
<td>{{ widgets.label_visible(entry.active) }}</td>
|
||||||
<td>{{ entry.roles|join(',')|trans }}</td>
|
<td>{{ entry.roles|join(',')|trans }}</td>
|
||||||
|
<td>
|
||||||
|
{{ widgets.button_group({
|
||||||
|
'edit': path('user_profile', {'username' : entry.username}),
|
||||||
|
'trash': path('user_profile_delete', {'username' : entry.username})
|
||||||
|
}) }}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|||||||
13
app/Resources/views/admin/user/_form.html.twig
Normal file
13
app/Resources/views/admin/user/_form.html.twig
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<div class="box box-primary">
|
||||||
|
<div class="box-header with-border">
|
||||||
|
<h3 class="box-title">{{ user.username }}</h3>
|
||||||
|
</div>
|
||||||
|
{{ form_start(form) }}
|
||||||
|
<div class="box-body">
|
||||||
|
{{ form_widget(form) }}
|
||||||
|
</div>
|
||||||
|
<div class="box-footer">
|
||||||
|
<input type="submit" value="{{ 'action.save'|trans }}" class="btn btn-primary" />
|
||||||
|
</div>
|
||||||
|
{{ form_end(form) }}
|
||||||
|
</div>
|
||||||
13
app/Resources/views/admin/user/edit.html.twig
Normal file
13
app/Resources/views/admin/user/edit.html.twig
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block page_title %}{{ 'edit_profile.title'|trans }}{% endblock %}
|
||||||
|
{% block page_subtitle %}{{ 'edit_profile.subtitle'|trans({'%username%': user.username}) }}{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{{ include('default/_flash_messages.html.twig') }}
|
||||||
|
|
||||||
|
{{ include('admin/user/_form.html.twig', {
|
||||||
|
user: user,
|
||||||
|
form: form,
|
||||||
|
}, with_context = false) }}
|
||||||
|
{% endblock %}
|
||||||
@@ -38,11 +38,14 @@
|
|||||||
|
|
||||||
{% block avanzu_head %}
|
{% block avanzu_head %}
|
||||||
<link rel="stylesheet" href="{{ asset('css/kimai.css') }}">
|
<link rel="stylesheet" href="{{ asset('css/kimai.css') }}">
|
||||||
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
|
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
|
||||||
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
|
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
|
||||||
|
<script src="{{ asset('theme/bootstrap/js/bootstrap.min.js') }}"></script>
|
||||||
<script src="{{ asset('js/kimai.js') }}"></script>
|
<script src="{{ asset('js/kimai.js') }}"></script>
|
||||||
|
<script src="{{ asset('js/Chart.min.js') }}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
$('.dropdown-toggle').dropdown();
|
||||||
$(document).kimai({imagePath: '{{ asset('images') }}'});
|
$(document).kimai({imagePath: '{{ asset('images') }}'});
|
||||||
$(document).kimai('ticktac', 'a#ticktac');
|
$(document).kimai('ticktac', 'a#ticktac');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
{# FIXME Übersetzungen hinzufügen #}
|
{# FIXME Übersetzungen hinzufügen #}
|
||||||
|
|
||||||
{# blue / yellow / purple / green / black #}
|
{# blue / yellow / purple / green / black #}
|
||||||
{# bar-chart / line-chart / calendar / clock-o #}
|
{# bar-chart / line-chart / calendar / clock-o #}
|
||||||
{#
|
{#
|
||||||
@@ -30,16 +31,16 @@
|
|||||||
{{ widgets.page_header('dashboard.you') }}
|
{{ widgets.page_header('dashboard.you') }}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||||
{{ widgets.info_box_counter('stats.durationThisMonth', timesheetUser.durationThisMonth|duration(true), 'hourglass-o', 'blue') }}
|
{{ widgets.info_box_counter('stats.durationThisMonth', timesheetUser.durationThisMonth|duration(true), 'hourglass-o', 'green') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||||
{{ widgets.info_box_counter('stats.amountThisMonth', timesheetUser.amountThisMonth|money, 'money', 'green') }}
|
{{ widgets.info_box_counter('stats.amountThisMonth', timesheetUser.amountThisMonth|money, 'money', 'blue') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||||
{{ widgets.info_box_counter('stats.durationTotal', timesheetUser.durationTotal|duration(true), 'hourglass-o', 'yellow') }}
|
{{ widgets.info_box_counter('stats.durationTotal', timesheetUser.durationTotal|duration(true), 'hourglass-o', 'red') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||||
{{ widgets.info_box_counter('stats.amountTotal', timesheetUser.amountTotal|money, 'money', 'red') }}
|
{{ widgets.info_box_counter('stats.amountTotal', timesheetUser.amountTotal|money, 'money', 'yellow') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -65,13 +66,13 @@
|
|||||||
{% if is_granted('ROLE_TEAMLEAD') %}
|
{% if is_granted('ROLE_TEAMLEAD') %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||||
{{ widgets.info_box_counter('Anzahl Benutzer', user.totalAmount, 'users', 'blue') }}
|
{{ widgets.info_box_counter('Anzahl Benutzer', user.totalAmount, 'users', 'red') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||||
{{ widgets.info_box_counter('Aktive Benutzer diesen Monat', timesheetGlobal.activeThisMonth, 'users', 'yellow') }}
|
{{ widgets.info_box_counter('Aktive Benutzer diesen Monat', timesheetGlobal.activeThisMonth, 'users', 'yellow') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||||
{{ widgets.info_box_counter('Aktive Benutzer jemals', timesheetGlobal.activeTotal, 'users', 'red') }}
|
{{ widgets.info_box_counter('Aktive Benutzer jemals', timesheetGlobal.activeTotal, 'users', 'blue') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||||
{{ widgets.info_box_counter('Momentan aktiv', timesheetGlobal.activeCurrently, 'users', 'green') }}
|
{{ widgets.info_box_counter('Momentan aktiv', timesheetGlobal.activeCurrently, 'users', 'green') }}
|
||||||
|
|||||||
@@ -89,3 +89,33 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro button_group_dropdown(title, actions) %}
|
||||||
|
<div class="btn-group">
|
||||||
|
<button type="button" class="btn btn-default">{{ title|trans }}</button>
|
||||||
|
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||||
|
<span class="caret"></span>
|
||||||
|
<span class="sr-only">{{ 'label.toggle_dropdown'|trans }}</span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" role="menu">
|
||||||
|
{% for url, entry in actions %}
|
||||||
|
<li><a href="{{ url }}">{{ entry|trans }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro button_group(actions) %}
|
||||||
|
{% import _self as macro %}
|
||||||
|
<div class="btn-group">
|
||||||
|
{% for icon, url in actions %}
|
||||||
|
{{ macro.button_action(icon, url) }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro button_action(icon, url) %}
|
||||||
|
<a href="{{ url }}" class="btn btn-default">
|
||||||
|
<i class="fa fa-{{ icon }}"></i>
|
||||||
|
</a>
|
||||||
|
{% endmacro %}
|
||||||
|
|||||||
@@ -5,17 +5,165 @@
|
|||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% import _self as widgets %}
|
{% import _self as widgets %}
|
||||||
|
|
||||||
|
{{ include('default/_flash_messages.html.twig') }}
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-sm-12 col-xs-12">
|
<div class="col-md-3">
|
||||||
{{ widgets.profile_list(user, settings, 'aqua-active') }}
|
{{ widgets.profile_box(user, stats) }}
|
||||||
|
{{ widgets.profile_infos(user, stats) }}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-9">
|
||||||
|
|
||||||
|
<div class="nav-tabs-custom">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li {% if tab == "charts" %}class="active"{% endif %}><a href="#charts" data-toggle="tab" aria-expanded="true">{{ 'profile.tab_monthly'|trans }}</a></li>
|
||||||
|
<li {% if tab == "profile" %}class="active"{% endif %}><a href="#profile" data-toggle="tab" aria-expanded="false">{{ 'profile.settings'|trans }}</a></li>
|
||||||
|
<li {% if tab == "password" %}class="active"{% endif %}><a href="#password" data-toggle="tab" aria-expanded="false">{{ 'profile.password'|trans }}</a></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-pane {% if tab == "charts" %}active{% endif %}" id="charts">
|
||||||
|
<script type="text/javascript">
|
||||||
|
var barChartOptions = {
|
||||||
|
scaleBeginAtZero: true,
|
||||||
|
scaleShowGridLines: true,
|
||||||
|
scaleGridLineColor: "rgba(0,0,0,.05)",
|
||||||
|
scaleGridLineWidth: 1,
|
||||||
|
scaleShowHorizontalLines: true,
|
||||||
|
scaleShowVerticalLines: true,
|
||||||
|
barShowStroke: true,
|
||||||
|
barStrokeWidth: 2,
|
||||||
|
barValueSpacing: 5,
|
||||||
|
barDatasetSpacing: 1,
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: true,
|
||||||
|
tooltipTemplate: "{% raw %}<%= value %>{% endraw %} {{ 'label.hours'|trans }}",
|
||||||
|
};
|
||||||
|
var barChartLabels = [
|
||||||
|
{% for i in 1..11 %}
|
||||||
|
"{{ ('month.' ~i)|trans }}",
|
||||||
|
{% endfor %}
|
||||||
|
"{{ 'month.12'|trans }}"
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% for year,yearStat in years %}
|
||||||
|
<h2>{{ year }}</h2>
|
||||||
|
<div class="chart">
|
||||||
|
<canvas id="barChart{{ year }}" style="height: 230px;"></canvas>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var barChartData{{ year }} = {
|
||||||
|
labels: barChartLabels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
{# label: '# hours',#}
|
||||||
|
fillColor: "#00a65a",
|
||||||
|
strokeColor: "#00a65a",
|
||||||
|
pointColor: "#00a65a",
|
||||||
|
pointStrokeColor: "#c1c7d1",
|
||||||
|
pointHighlightFill: "#fff",
|
||||||
|
pointHighlightStroke: "rgba(220,220,220,1)",
|
||||||
|
data: [
|
||||||
|
{% for i in 1..11 %}
|
||||||
|
{{ (yearStat.month(i).totalDuration / 3600)|round }},
|
||||||
|
{% endfor %}
|
||||||
|
{{ (yearStat.month(12).totalDuration / 3600)|round }}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
{% if tab == "charts" %}
|
||||||
|
$(function () {
|
||||||
|
var barChartCanvas{{ year }} = $("#barChart{{ year }}").get(0).getContext("2d");
|
||||||
|
var barChart = new Chart(barChartCanvas{{ year }});
|
||||||
|
barChart.Bar(barChartData{{ year }}, barChartOptions);
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
|
$('a[href="#charts"]').on('shown.bs.tab', function(e){
|
||||||
|
var barChartCanvas{{ year }} = $("#barChart{{ year }}").get(0).getContext("2d");
|
||||||
|
var barChart = new Chart(barChartCanvas{{ year }});
|
||||||
|
barChart.Bar(barChartData{{ year }}, barChartOptions);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane {% if tab == "profile" %}active{% endif %}" id="profile">
|
||||||
|
{{ form_start(form) }}
|
||||||
|
{{ form_widget(form) }}
|
||||||
|
<input type="submit" value="{{ 'action.save'|trans }}" class="btn btn-primary" />
|
||||||
|
{{ form_end(form) }}
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane {% if tab == "password" %}active{% endif %}" id="password">
|
||||||
|
{{ form_start(form_password) }}
|
||||||
|
{{ form_widget(form_password) }}
|
||||||
|
<input type="submit" value="{{ 'action.save'|trans }}" class="btn btn-primary" />
|
||||||
|
{{ form_end(form_password) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-sm-12 col-xs-12">
|
|
||||||
{{ widgets.profile_box(user, stats, 'aqua-active') }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% macro profile_list(user, items, color) %}
|
{% macro profile_infos(user, stats) %}
|
||||||
|
<div class="box box-primary">
|
||||||
|
<div class="box-header with-border">
|
||||||
|
<h3 class="box-title">{{ 'profile.about_me'|trans }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<ul class="nav nav-stacked">
|
||||||
|
{# colors = purple, blue, aqua, red, green #}
|
||||||
|
{# FIXME hourly rate must be dynamic <li><a href="#">{{ 'label.hourly_rate'|trans }} <span class="pull-right badge bg-blue">70</span></a></li> #}
|
||||||
|
<li><a href="#">{{ 'label.id'|trans }} <span class="pull-right badge bg-aqua">{{ user.id }}</span></a></li>
|
||||||
|
<li><a href="#">{{ 'label.username'|trans }} <span class="pull-right badge bg-blue">{{ user.username }}</span></a></li>
|
||||||
|
<li><a href="#">{{ 'label.language'|trans }} <span class="pull-right badge bg-green">{{ user.language }}</span></a></li>
|
||||||
|
<li><a href="#">{{ 'profile.first_entry'|trans }} <span class="pull-right badge bg-purple">{{ stats.firstEntry|date }}</span></a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro profile_box(user, stats) %}
|
||||||
|
{% import "AvanzuAdminThemeBundle:layout:macros.html.twig" as macro %}
|
||||||
|
|
||||||
|
<div class="box box-primary">
|
||||||
|
<div class="box-body box-profile">
|
||||||
|
{{ macro.avatar(user.avatar, user.username, 'profile-user-img img-responsive img-circle') }}
|
||||||
|
<h3 class="profile-username text-center">{{ user.alias }}</h3>
|
||||||
|
|
||||||
|
<p class="text-muted text-center">{{ user.title }}</p>
|
||||||
|
|
||||||
|
<ul class="list-group list-group-unbordered">
|
||||||
|
|
||||||
|
<li class="list-group-item">
|
||||||
|
<b>{{ 'stats.durationThisMonth'|trans }}</b> <a class="pull-right">{{ stats.durationThisMonth|duration }}</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="list-group-item">
|
||||||
|
<b>{{ 'stats.amountThisMonth'|trans }}</b> <a class="pull-right">{{ stats.amountThisMonth|money }}</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="list-group-item">
|
||||||
|
<b>{{ 'stats.durationTotal'|trans }}</b> <a class="pull-right">{{ stats.durationTotal|duration }}</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="list-group-item">
|
||||||
|
<b>{{ 'stats.amountTotal'|trans }}</b> <a class="pull-right">{{ stats.amountTotal|money }}</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{# <a href="#" class="btn btn-primary btn-block"><b>Follow</b></a> #}
|
||||||
|
</div>
|
||||||
|
<!-- /.box-body -->
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{# -------------------------------- UNUSED FOR NOW -------------------------------- #}
|
||||||
|
{% macro profile_list_unused(user, items, color) %}
|
||||||
{% import "AvanzuAdminThemeBundle:layout:macros.html.twig" as macro %}
|
{% import "AvanzuAdminThemeBundle:layout:macros.html.twig" as macro %}
|
||||||
<div class="box box-widget widget-user-2">
|
<div class="box box-widget widget-user-2">
|
||||||
<!-- Add the bg color to the header using any of the bg-* classes -->
|
<!-- Add the bg color to the header using any of the bg-* classes -->
|
||||||
@@ -37,11 +185,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro profile_box(user, stats, color) %}
|
{# -------------------------------- UNUSED FOR NOW -------------------------------- #}
|
||||||
|
{% macro profile_box_unused(user, stats, color) %}
|
||||||
{% import "AvanzuAdminThemeBundle:layout:macros.html.twig" as macro %}
|
{% import "AvanzuAdminThemeBundle:layout:macros.html.twig" as macro %}
|
||||||
|
|
||||||
<div class="box box-widget widget-user">
|
<div class="box box-widget widget-user">
|
||||||
<!-- Add the bg color to the header using any of the bg-* classes -->
|
|
||||||
<div class="widget-user-header bg-{{ color|default(kimai_context.box_color) }}">
|
<div class="widget-user-header bg-{{ color|default(kimai_context.box_color) }}">
|
||||||
<h3 class="widget-user-username">{{ user.alias }} ({{ user.username }})</h3>
|
<h3 class="widget-user-username">{{ user.alias }} ({{ user.username }})</h3>
|
||||||
<h5 class="widget-user-desc">{{ user.title }}</h5>
|
<h5 class="widget-user-desc">{{ user.title }}</h5>
|
||||||
|
|||||||
@@ -64,16 +64,16 @@ twig:
|
|||||||
table_icons: false
|
table_icons: false
|
||||||
|
|
||||||
# Assetic Configuration (used for managing web assets: CSS, JavaScript, Sass, etc.)
|
# Assetic Configuration (used for managing web assets: CSS, JavaScript, Sass, etc.)
|
||||||
assetic:
|
#assetic:
|
||||||
debug: "%kernel.debug%"
|
# debug: "%kernel.debug%"
|
||||||
use_controller: false
|
# use_controller: false
|
||||||
bundles: [ ]
|
# bundles: [ ]
|
||||||
filters:
|
# filters:
|
||||||
cssrewrite: ~
|
# cssrewrite: ~
|
||||||
jsqueeze: ~
|
# jsqueeze: ~
|
||||||
scssphp:
|
# scssphp:
|
||||||
# the formatter must be the FQCN (don't use the 'compressed' value)
|
# # the formatter must be the FQCN (don't use the 'compressed' value)
|
||||||
formatter: "Leafo\\ScssPhp\\Formatter\\Compressed"
|
# formatter: "Leafo\\ScssPhp\\Formatter\\Compressed"
|
||||||
|
|
||||||
# Doctrine Configuration (used to access databases and manipulate their information)
|
# Doctrine Configuration (used to access databases and manipulate their information)
|
||||||
doctrine:
|
doctrine:
|
||||||
@@ -85,12 +85,12 @@ doctrine:
|
|||||||
auto_mapping: true
|
auto_mapping: true
|
||||||
|
|
||||||
# Swiftmailer Configuration (used to send emails)
|
# Swiftmailer Configuration (used to send emails)
|
||||||
swiftmailer:
|
#swiftmailer:
|
||||||
transport: "%mailer_transport%"
|
# transport: "%mailer_transport%"
|
||||||
host: "%mailer_host%"
|
# host: "%mailer_host%"
|
||||||
username: "%mailer_user%"
|
# username: "%mailer_user%"
|
||||||
password: "%mailer_password%"
|
# password: "%mailer_password%"
|
||||||
spool: { type: memory }
|
# spool: { type: memory }
|
||||||
|
|
||||||
avanzu_admin_theme:
|
avanzu_admin_theme:
|
||||||
use_twig : true
|
use_twig : true
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ monolog:
|
|||||||
# type: chromephp
|
# type: chromephp
|
||||||
# level: info
|
# level: info
|
||||||
|
|
||||||
assetic:
|
#assetic:
|
||||||
use_controller: true
|
# use_controller: true
|
||||||
|
|
||||||
#swiftmailer:
|
#swiftmailer:
|
||||||
# delivery_address: me@example.com
|
# delivery_address: me@example.com
|
||||||
|
|||||||
86
app/config/mysql.yml
Normal file
86
app/config/mysql.yml
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
doctrine:
|
||||||
|
orm:
|
||||||
|
dql:
|
||||||
|
datetime_functions:
|
||||||
|
convert_tz: DoctrineExtensions\Query\Mysql\ConvertTz
|
||||||
|
date: DoctrineExtensions\Query\Mysql\Date
|
||||||
|
date_format: DoctrineExtensions\Query\Mysql\DateFormat
|
||||||
|
dateadd: DoctrineExtensions\Query\Mysql\DateAdd
|
||||||
|
datesub: DoctrineExtensions\Query\Mysql\DateSub
|
||||||
|
datediff: DoctrineExtensions\Query\Mysql\DateDiff
|
||||||
|
day: DoctrineExtensions\Query\Mysql\Day
|
||||||
|
dayname: DoctrineExtensions\Query\Mysql\DayName
|
||||||
|
dayofweek: DoctrineExtensions\Query\Mysql\DayOfWeek
|
||||||
|
dayofyear: DoctrineExtensions\Query\Mysql\DayOfYear
|
||||||
|
div: DoctrineExtensions\Query\Mysql\Div
|
||||||
|
from_unixtime: DoctrineExtensions\Query\Mysql\FromUnixtime
|
||||||
|
last_day: DoctrineExtensions\Query\Mysql\LastDay
|
||||||
|
minute: DoctrineExtensions\Query\Mysql\Minute
|
||||||
|
now: DoctrineExtensions\Query\Mysql\Now
|
||||||
|
month: DoctrineExtensions\Query\Mysql\Month
|
||||||
|
monthname: DoctrineExtensions\Query\Mysql\MonthName
|
||||||
|
second: DoctrineExtensions\Query\Mysql\Second
|
||||||
|
strtodate: DoctrineExtensions\Query\Mysql\StrToDate
|
||||||
|
time: DoctrineExtensions\Query\Mysql\Time
|
||||||
|
timediff: DoctrineExtensions\Query\Mysql\TimeDiff
|
||||||
|
timestampadd: DoctrineExtensions\Query\Mysql\TimestampAdd
|
||||||
|
timestampdiff: DoctrineExtensions\Query\Mysql\TimestampDiff
|
||||||
|
timetosec: DoctrineExtensions\Query\Mysql\TimeToSec
|
||||||
|
week: DoctrineExtensions\Query\Mysql\Week
|
||||||
|
weekday: DoctrineExtensions\Query\Mysql\WeekDay
|
||||||
|
year: DoctrineExtensions\Query\Mysql\Year
|
||||||
|
yearweek: DoctrineExtensions\Query\Mysql\YearWeek
|
||||||
|
unix_timestamp: DoctrineExtensions\Query\Mysql\UnixTimestamp
|
||||||
|
|
||||||
|
numeric_functions:
|
||||||
|
acos: DoctrineExtensions\Query\Mysql\Acos
|
||||||
|
asin: DoctrineExtensions\Query\Mysql\Asin
|
||||||
|
atan2: DoctrineExtensions\Query\Mysql\Atan2
|
||||||
|
atan: DoctrineExtensions\Query\Mysql\Atan
|
||||||
|
bit_count: DoctrineExtensions\Query\Mysql\BitCount
|
||||||
|
bit_xor: DoctrineExtensions\Query\Mysql\BitXor
|
||||||
|
ceil: DoctrineExtensions\Query\Mysql\Ceil
|
||||||
|
cos: DoctrineExtensions\Query\Mysql\Cos
|
||||||
|
cot: DoctrineExtensions\Query\Mysql\Cot
|
||||||
|
floor: DoctrineExtensions\Query\Mysql\Floor
|
||||||
|
hour: DoctrineExtensions\Query\Mysql\Hour
|
||||||
|
pi: DoctrineExtensions\Query\Mysql\Pi
|
||||||
|
power: DoctrineExtensions\Query\Mysql\Power
|
||||||
|
quarter: DoctrineExtensions\Query\Mysql\Quarter
|
||||||
|
rand: DoctrineExtensions\Query\Mysql\Rand
|
||||||
|
round: DoctrineExtensions\Query\Mysql\Round
|
||||||
|
stddev: DoctrineExtensions\Query\Mysql\StdDev
|
||||||
|
sin: DoctrineExtensions\Query\Mysql\Sin
|
||||||
|
std: DoctrineExtensions\Query\Mysql\Std
|
||||||
|
tan: DoctrineExtensions\Query\Mysql\Tan
|
||||||
|
|
||||||
|
string_functions:
|
||||||
|
ascii: DoctrineExtensions\Query\Mysql\Ascii
|
||||||
|
binary: DoctrineExtensions\Query\Mysql\Binary
|
||||||
|
char_length: DoctrineExtensions\Query\Mysql\CharLength
|
||||||
|
concat_ws: DoctrineExtensions\Query\Mysql\ConcatWs
|
||||||
|
countif: DoctrineExtensions\Query\Mysql\CountIf
|
||||||
|
crc32: DoctrineExtensions\Query\Mysql\Crc32
|
||||||
|
degrees: DoctrineExtensions\Query\Mysql\Degrees
|
||||||
|
field: DoctrineExtensions\Query\Mysql\Field
|
||||||
|
find_in_set: DoctrineExtensions\Query\Mysql\FindInSet
|
||||||
|
greatest: DoctrineExtensions\Query\Mysql\Greatest
|
||||||
|
group_concat: DoctrineExtensions\Query\Mysql\GroupConcat
|
||||||
|
ifelse: DoctrineExtensions\Query\Mysql\IfElse
|
||||||
|
ifnull: DoctrineExtensions\Query\Mysql\IfNull
|
||||||
|
least: DoctrineExtensions\Query\Mysql\Least
|
||||||
|
lpad: DoctrineExtensions\Query\Mysql\Lpad
|
||||||
|
match_against: DoctrineExtensions\Query\Mysql\MatchAgainst
|
||||||
|
md5: DoctrineExtensions\Query\Mysql\Md5
|
||||||
|
nullif: DoctrineExtensions\Query\Mysql\NullIf
|
||||||
|
radians: DoctrineExtensions\Query\Mysql\Radians
|
||||||
|
regexp: DoctrineExtensions\Query\Mysql\Regexp
|
||||||
|
replace: DoctrineExtensions\Query\Mysql\Replace
|
||||||
|
rpad: DoctrineExtensions\Query\Mysql\Rpad
|
||||||
|
sha1: DoctrineExtensions\Query\Mysql\Sha1
|
||||||
|
sha2: DoctrineExtensions\Query\Mysql\Sha2
|
||||||
|
soundex: DoctrineExtensions\Query\Mysql\Soundex
|
||||||
|
substring_index: DoctrineExtensions\Query\Mysql\SubstringIndex
|
||||||
|
uuid_short: DoctrineExtensions\Query\Mysql\UuidShort
|
||||||
|
hex: DoctrineExtensions\Query\Mysql\Hex
|
||||||
|
unhex: DoctrineExtensions\Query\Mysql\Unhex
|
||||||
@@ -4,6 +4,9 @@
|
|||||||
# See http://symfony.com/doc/current/best_practices/configuration.html#canonical-parameters
|
# See http://symfony.com/doc/current/best_practices/configuration.html#canonical-parameters
|
||||||
parameters:
|
parameters:
|
||||||
|
|
||||||
|
# used to load additional doctrine functions (possible values: mysql, sqlite)
|
||||||
|
database_engine: sqlite
|
||||||
|
|
||||||
# Define the database connection string
|
# Define the database connection string
|
||||||
database_url: 'sqlite:///%kernel.root_dir%/data/timesheet.sqlite'
|
database_url: 'sqlite:///%kernel.root_dir%/data/timesheet.sqlite'
|
||||||
# database_url: 'mysql://root:pass@127.0.0.1:3306/timesheet'
|
# database_url: 'mysql://root:pass@127.0.0.1:3306/timesheet'
|
||||||
|
|||||||
@@ -34,6 +34,6 @@ homepage:
|
|||||||
permanent: true
|
permanent: true
|
||||||
|
|
||||||
avanzu_admin_profile:
|
avanzu_admin_profile:
|
||||||
path: /{_locale}/profile/{ident}/
|
path: /{_locale}/profile/{username}
|
||||||
options:
|
options:
|
||||||
avanzu_admin_route: profile
|
avanzu_admin_route: profile
|
||||||
|
|||||||
50
app/config/sqlite.yml
Normal file
50
app/config/sqlite.yml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
doctrine:
|
||||||
|
orm:
|
||||||
|
dql:
|
||||||
|
datetime_functions:
|
||||||
|
date: DoctrineExtensions\Query\Sqlite\Date
|
||||||
|
date_format: DoctrineExtensions\Query\Sqlite\DateFormat
|
||||||
|
#dateadd: DoctrineExtensions\Query\Sqlite\DateAdd
|
||||||
|
#datediff: DoctrineExtensions\Query\Sqlite\DateDiff
|
||||||
|
day: DoctrineExtensions\Query\Sqlite\Day
|
||||||
|
dayname: DoctrineExtensions\Query\Sqlite\DayName
|
||||||
|
minute: DoctrineExtensions\Query\Sqlite\Minute
|
||||||
|
#month: DoctrineExtensions\Query\Sqlite\Month
|
||||||
|
#monthname: DoctrineExtensions\Query\Sqlite\MonthName
|
||||||
|
second: DoctrineExtensions\Query\Sqlite\Second
|
||||||
|
month: DoctrineExtensions\Query\Sqlite\Month
|
||||||
|
strftime: DoctrineExtensions\Query\Sqlite\StrfTime
|
||||||
|
#strtodate: DoctrineExtensions\Query\Sqlite\StrToDate
|
||||||
|
#time: DoctrineExtensions\Query\Sqlite\Time
|
||||||
|
#timestampadd: DoctrineExtensions\Query\Sqlite\TimestampAdd
|
||||||
|
#timestampdiff: DoctrineExtensions\Query\Sqlite\TimestampDiff
|
||||||
|
week: DoctrineExtensions\Query\Sqlite\Week
|
||||||
|
weekday: DoctrineExtensions\Query\Sqlite\WeekDay
|
||||||
|
year: DoctrineExtensions\Query\Sqlite\Year
|
||||||
|
|
||||||
|
|
||||||
|
string_functions:
|
||||||
|
# binary: DoctrineExtensions\Query\Sqlite\Binary
|
||||||
|
# char_length: DoctrineExtensions\Query\Sqlite\CharLength
|
||||||
|
concat_ws: DoctrineExtensions\Query\Sqlite\ConcatWs
|
||||||
|
# countif: DoctrineExtensions\Query\Sqlite\CountIf
|
||||||
|
# crc32: DoctrineExtensions\Query\Sqlite\Crc32
|
||||||
|
# degrees: DoctrineExtensions\Query\Sqlite\Degrees
|
||||||
|
# field: DoctrineExtensions\Query\Sqlite\Field
|
||||||
|
# find_in_set: DoctrineExtensions\Query\Sqlite\FindInSet
|
||||||
|
# group_concat: DoctrineExtensions\Query\Sqlite\GroupConcat
|
||||||
|
# ifelse: DoctrineExtensions\Query\Sqlite\IfElse
|
||||||
|
ifnull: DoctrineExtensions\Query\Sqlite\IfNull
|
||||||
|
# match_against: DoctrineExtensions\Query\Sqlite\MatchAgainst
|
||||||
|
# md5: DoctrineExtensions\Query\Sqlite\Md5
|
||||||
|
# nullif: DoctrineExtensions\Query\Sqlite\NullIf
|
||||||
|
# radians: DoctrineExtensions\Query\Sqlite\Radians
|
||||||
|
# regexp: DoctrineExtensions\Query\Sqlite\Regexp
|
||||||
|
replace: DoctrineExtensions\Query\Sqlite\Replace
|
||||||
|
# sha1: DoctrineExtensions\Query\Sqlite\Sha1
|
||||||
|
# sha2: DoctrineExtensions\Query\Sqlite\Sha2
|
||||||
|
# soundex: DoctrineExtensions\Query\Sqlite\Soundex
|
||||||
|
# uuid_short: DoctrineExtensions\Query\Sqlite\UuidShort
|
||||||
|
|
||||||
|
numeric_functions:
|
||||||
|
round: DoctrineExtensions\Query\Sqlite\Round
|
||||||
@@ -31,7 +31,8 @@
|
|||||||
"symfony/symfony": "^3.1",
|
"symfony/symfony": "^3.1",
|
||||||
"twig/extensions": "^1.3",
|
"twig/extensions": "^1.3",
|
||||||
"white-october/pagerfanta-bundle": "^1.0",
|
"white-october/pagerfanta-bundle": "^1.0",
|
||||||
"avanzu/admin-theme-bundle": "dev-feature/2.0"
|
"avanzu/admin-theme-bundle": "dev-feature/2.0",
|
||||||
|
"beberlei/DoctrineExtensions": "^1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"sensio/generator-bundle": "~3.0",
|
"sensio/generator-bundle": "~3.0",
|
||||||
|
|||||||
66
composer.lock
generated
66
composer.lock
generated
@@ -4,8 +4,8 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"hash": "4987a4da3f291b8e0bb1b3af06a5cfa6",
|
"hash": "d5aa01779571420f9166199f60fa2a17",
|
||||||
"content-hash": "287d1663444888a10331136287c4269b",
|
"content-hash": "20dda7356ab7c685ada168dd7e9b7593",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "almasaeed2010/adminlte",
|
"name": "almasaeed2010/adminlte",
|
||||||
@@ -93,6 +93,68 @@
|
|||||||
"description": "Admin Theme based on the AdminLTE Template for easy integration into symfony",
|
"description": "Admin Theme based on the AdminLTE Template for easy integration into symfony",
|
||||||
"time": "2016-08-26 08:28:19"
|
"time": "2016-08-26 08:28:19"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "beberlei/DoctrineExtensions",
|
||||||
|
"version": "v1.0.11",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/beberlei/DoctrineExtensions.git",
|
||||||
|
"reference": "6dbe65e469e8bdcfe165ea16636b7049af479a24"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/beberlei/DoctrineExtensions/zipball/6dbe65e469e8bdcfe165ea16636b7049af479a24",
|
||||||
|
"reference": "6dbe65e469e8bdcfe165ea16636b7049af479a24",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"doctrine/orm": "~2.1",
|
||||||
|
"nesbot/carbon": "*",
|
||||||
|
"phpunit/phpunit": "~4.5",
|
||||||
|
"symfony/yaml": "~2.6",
|
||||||
|
"zf1/zend-date": "~1.12",
|
||||||
|
"zf1/zend-registry": "~1.12"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"nesbot/carbon": "Alternative to DateTime",
|
||||||
|
"zf1/zend-date": "Alternative to DateTime"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.0-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"DoctrineExtensions\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Benjamin Eberlei",
|
||||||
|
"email": "kontakt@beberlei.de"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Steve Lacey",
|
||||||
|
"email": "steve@stevelacey.net"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A set of extensions to Doctrine 2 that add support for additional query functions available in MySQL and Oracle.",
|
||||||
|
"keywords": [
|
||||||
|
"database",
|
||||||
|
"doctrine",
|
||||||
|
"orm"
|
||||||
|
],
|
||||||
|
"time": "2016-07-06 17:12:30"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/annotations",
|
"name": "doctrine/annotations",
|
||||||
"version": "v1.2.7",
|
"version": "v1.2.7",
|
||||||
|
|||||||
@@ -11,6 +11,10 @@
|
|||||||
|
|
||||||
namespace AppBundle;
|
namespace AppBundle;
|
||||||
|
|
||||||
|
use AppBundle\DependencyInjection\AppBundleExtension;
|
||||||
|
use AppBundle\DependencyInjection\CompilerPass;
|
||||||
|
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,7 +22,15 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
|
|||||||
*
|
*
|
||||||
* @see http://symfony.com/doc/current/cookbook/bundles/best_practices.html
|
* @see http://symfony.com/doc/current/cookbook/bundles/best_practices.html
|
||||||
* @see http://symfony.com/doc/current/best_practices/business-logic.html
|
* @see http://symfony.com/doc/current/best_practices/business-logic.html
|
||||||
|
*
|
||||||
|
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||||
*/
|
*/
|
||||||
class AppBundle extends Bundle
|
class AppBundle extends Bundle
|
||||||
{
|
{
|
||||||
|
public function build(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
parent::build($container);
|
||||||
|
|
||||||
|
$container->addCompilerPass(new CompilerPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,12 +11,22 @@
|
|||||||
|
|
||||||
namespace AppBundle\Controller;
|
namespace AppBundle\Controller;
|
||||||
|
|
||||||
|
use AppBundle\Entity\User;
|
||||||
|
use AppBundle\Form\UserEditType;
|
||||||
|
use AppBundle\Form\UserPasswordType;
|
||||||
|
use AppBundle\Repository\UserRepository;
|
||||||
|
use Symfony\Component\Form\Form;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
use TimesheetBundle\Entity\Timesheet;
|
use TimesheetBundle\Entity\Timesheet;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||||
|
use TimesheetBundle\Model\TimesheetStatistic;
|
||||||
|
use TimesheetBundle\Repository\TimesheetRepository;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User profile controller
|
* User profile controller
|
||||||
@@ -29,45 +39,189 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
|||||||
class ProfileController extends Controller
|
class ProfileController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @Route("/", defaults={"ident": null}, name="user_profile")
|
* @Route("/{username}", name="user_profile")
|
||||||
* @Route("/{ident}/", requirements={"ident": "[a-zA-Z0-9\-].*"}, name="user_profile_ident")
|
|
||||||
* @Method("GET")
|
* @Method("GET")
|
||||||
*/
|
*/
|
||||||
public function indexAction($ident)
|
public function indexAction($username)
|
||||||
{
|
{
|
||||||
$user = $this->getUser();
|
$user = $this->getUserByUsername($username);
|
||||||
|
|
||||||
$isAdmin = false;
|
return $this->getProfileView($user);
|
||||||
if ($isAdmin) {
|
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
* @param Form $editForm
|
||||||
|
* @param Form $pwdForm
|
||||||
|
* @param string $tab
|
||||||
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
|
*/
|
||||||
|
protected function getProfileView(User $user, Form $editForm = null, Form $pwdForm = null, $tab = 'charts')
|
||||||
|
{
|
||||||
|
/* @var $timesheetRepo TimesheetRepository */
|
||||||
$timesheetRepo = $this->getDoctrine()->getRepository(Timesheet::class);
|
$timesheetRepo = $this->getDoctrine()->getRepository(Timesheet::class);
|
||||||
$userStats = $timesheetRepo->getUserStatistics($user);
|
$userStats = $timesheetRepo->getUserStatistics($user);
|
||||||
|
$monthlyStats = $timesheetRepo->getMonthlyStats($user);
|
||||||
|
|
||||||
// FIXME fetch values dynamically and add trans filter to macros
|
$editForm = $editForm !== null ? $editForm : $this->createEditForm($user);
|
||||||
$items = [
|
$pwdForm = $pwdForm !== null ? $pwdForm : $this->createPasswordForm($user);
|
||||||
[
|
|
||||||
'title' => 'Stundenlohn',
|
|
||||||
'url' => '#',
|
|
||||||
'color' => 'blue', // aqua, red, green
|
|
||||||
'value' => 70
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'title' => 'Sprache',
|
|
||||||
'url' => '#',
|
|
||||||
'color' => 'green', // aqua, red, green
|
|
||||||
'value' => 'deutsch'
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
return $this->render(
|
return $this->render(
|
||||||
'user/profile.html.twig',
|
'user/profile.html.twig',
|
||||||
[
|
[
|
||||||
'user' => $this->getUser(),
|
'tab' => $tab,
|
||||||
'settings' => $items,
|
'user' => $user,
|
||||||
'stats' => $userStats
|
'stats' => $userStats,
|
||||||
|
'years' => $monthlyStats,
|
||||||
|
'form' => $editForm->createView(),
|
||||||
|
'form_password' => $pwdForm->createView(),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/{username}/edit", name="user_profile_edit")
|
||||||
|
* @Method({"GET", "POST"})
|
||||||
|
*/
|
||||||
|
public function editAction($username, Request $request)
|
||||||
|
{
|
||||||
|
$user = $this->getUserByUsername($username);
|
||||||
|
$editForm = $this->createEditForm($user);
|
||||||
|
|
||||||
|
$editForm->handleRequest($request);
|
||||||
|
|
||||||
|
if ($editForm->isSubmitted() && $editForm->isValid()) {
|
||||||
|
$entityManager = $this->getDoctrine()->getManager();
|
||||||
|
$entityManager->persist($user);
|
||||||
|
$entityManager->flush();
|
||||||
|
|
||||||
|
$this->addFlash('success', 'action.updated_successfully');
|
||||||
|
|
||||||
|
return $this->redirectToRoute(
|
||||||
|
'user_profile', ['username' => $user->getUsername()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getProfileView($user, $editForm, null, 'profile');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/{username}/password", name="user_profile_password")
|
||||||
|
* @Method({"GET", "POST"})
|
||||||
|
*/
|
||||||
|
public function passwordAction($username, Request $request)
|
||||||
|
{
|
||||||
|
$user = $this->getUserByUsername($username);
|
||||||
|
$pwdForm = $this->createPasswordForm($user);
|
||||||
|
|
||||||
|
$pwdForm->handleRequest($request);
|
||||||
|
|
||||||
|
if ($pwdForm->isSubmitted() && $pwdForm->isValid()) {
|
||||||
|
$password = $this->get('security.password_encoder')
|
||||||
|
->encodePassword($user, $user->getPlainPassword());
|
||||||
|
$user->setPassword($password);
|
||||||
|
|
||||||
|
$entityManager = $this->getDoctrine()->getManager();
|
||||||
|
$entityManager->persist($user);
|
||||||
|
$entityManager->flush();
|
||||||
|
|
||||||
|
$this->addFlash('success', 'action.updated_successfully');
|
||||||
|
|
||||||
|
return $this->redirectToRoute(
|
||||||
|
'user_profile', ['username' => $user->getUsername()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getProfileView($user, null, $pwdForm, 'password');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIXME
|
||||||
|
* @Route("/{username}/delete", name="user_profile_delete")
|
||||||
|
* @Method({"GET", "POST"})
|
||||||
|
*/
|
||||||
|
public function deleteAction($username, Request $request)
|
||||||
|
{
|
||||||
|
$user = $this->getUserByUsername($username);
|
||||||
|
$deleteForm = $this->createDeleteForm($user);
|
||||||
|
|
||||||
|
throw new \Exception('Delete not implemented yet');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $username
|
||||||
|
* @return User
|
||||||
|
* @throws NotFoundHttpException
|
||||||
|
*/
|
||||||
|
protected function getUserByUsername($username)
|
||||||
|
{
|
||||||
|
$user = $this->getUser();
|
||||||
|
|
||||||
|
// access to own profile always allowed
|
||||||
|
if (null === $username) {
|
||||||
|
$username = $user->getUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($username !== $user->getUsername()) {
|
||||||
|
$this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'Unable to access this page'); // TODO translation
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($username !== $user->getUsername()) {
|
||||||
|
/* @var $userRepo UserRepository */
|
||||||
|
$userRepo = $this->getDoctrine()->getRepository(User::class);
|
||||||
|
$user = $userRepo->findByUsername($username);
|
||||||
|
if (null === $user) {
|
||||||
|
throw new NotFoundHttpException('User "'.$username.'" does not exist');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
* @return \Symfony\Component\Form\Form
|
||||||
|
*/
|
||||||
|
private function createEditForm(User $user)
|
||||||
|
{
|
||||||
|
return $this->createForm(
|
||||||
|
UserEditType::class,
|
||||||
|
$user,
|
||||||
|
[
|
||||||
|
'action' => $this->generateUrl('user_profile_edit', ['username' => $user->getUsername()]),
|
||||||
|
'method' => 'POST'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
* @return \Symfony\Component\Form\Form
|
||||||
|
*/
|
||||||
|
private function createPasswordForm(User $user)
|
||||||
|
{
|
||||||
|
return $this->createForm(
|
||||||
|
UserPasswordType::class,
|
||||||
|
$user,
|
||||||
|
[
|
||||||
|
'validation_groups' => array('passwordUpdate'),
|
||||||
|
'action' => $this->generateUrl('user_profile_password', ['username' => $user->getUsername()]),
|
||||||
|
'method' => 'POST'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
*
|
||||||
|
* @return \Symfony\Component\Form\Form The form
|
||||||
|
*/
|
||||||
|
private function createDeleteForm(User $user)
|
||||||
|
{
|
||||||
|
return $this->createFormBuilder()
|
||||||
|
->setAction($this->generateUrl('user_profile_delete', ['username' => $user->getUsername()]))
|
||||||
|
->setMethod('DELETE')
|
||||||
|
->getForm()
|
||||||
|
;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,47 +83,6 @@ class LoadFixtures implements FixtureInterface, ContainerAwareInterface
|
|||||||
$manager->flush();
|
$manager->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* FIXME CAN BE REMOVED
|
|
||||||
*
|
|
||||||
* @param ObjectManager $manager
|
|
||||||
*/
|
|
||||||
private function loadPosts(ObjectManager $manager)
|
|
||||||
{
|
|
||||||
$authors = [
|
|
||||||
'anna_admin@example.com',
|
|
||||||
'tony_teamlead@example.com',
|
|
||||||
'clara_customer@example.com',
|
|
||||||
'john_user@example.com'
|
|
||||||
];
|
|
||||||
foreach (range(1, 30) as $i) {
|
|
||||||
$post = new Post();
|
|
||||||
|
|
||||||
$post->setTitle($this->getRandomPostTitle());
|
|
||||||
$post->setSummary($this->getRandomPostSummary());
|
|
||||||
$post->setSlug($this->container->get('slugger')->slugify($post->getTitle()));
|
|
||||||
$post->setContent($this->getPostContent());
|
|
||||||
$post->setAuthorEmail($authors[array_rand($authors, 1)]);
|
|
||||||
$post->setPublishedAt(new \DateTime('now - '.$i.'days'));
|
|
||||||
|
|
||||||
foreach (range(1, 5) as $j) {
|
|
||||||
$comment = new Comment();
|
|
||||||
|
|
||||||
$comment->setAuthorEmail($authors[array_rand($authors, 1)]);
|
|
||||||
$comment->setPublishedAt(new \DateTime('now + '.($i + $j).'seconds'));
|
|
||||||
$comment->setContent($this->getRandomCommentContent());
|
|
||||||
$comment->setPost($post);
|
|
||||||
|
|
||||||
$manager->persist($comment);
|
|
||||||
$post->addComment($comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
$manager->persist($post);
|
|
||||||
}
|
|
||||||
|
|
||||||
$manager->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
|||||||
34
src/AppBundle/DependencyInjection/AppExtension.php
Normal file
34
src/AppBundle/DependencyInjection/AppExtension.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai package.
|
||||||
|
*
|
||||||
|
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace AppBundle\DependencyInjection;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||||
|
use \Doctrine\Common\ClassLoader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main extension class
|
||||||
|
*
|
||||||
|
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*/
|
||||||
|
class AppExtension extends Extension
|
||||||
|
{
|
||||||
|
|
||||||
|
public function load(array $configs, ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$rootDir = realpath($container->getParameter('kernel.root_dir'));
|
||||||
|
$extensionsDir = realpath($rootDir . '/../vendor/beberlei/DoctrineExtensions/src/');
|
||||||
|
|
||||||
|
$classLoader = new ClassLoader('DoctrineExtensions', $extensionsDir);
|
||||||
|
$classLoader->register();
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/AppBundle/DependencyInjection/CompilerPass.php
Normal file
60
src/AppBundle/DependencyInjection/CompilerPass.php
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai package.
|
||||||
|
*
|
||||||
|
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace AppBundle\DependencyInjection;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Configuration
|
||||||
|
*
|
||||||
|
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*/
|
||||||
|
class CompilerPass implements CompilerPassInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ContainerBuilder $container
|
||||||
|
*/
|
||||||
|
public function process(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$engine = $container->getParameter('database_engine');
|
||||||
|
if (null === $engine) {
|
||||||
|
throw new ParameterNotFoundException('database_engine');
|
||||||
|
}
|
||||||
|
|
||||||
|
$ormConfigDef = $container->getDefinition('doctrine.orm.default_configuration');
|
||||||
|
|
||||||
|
$configDir = realpath($container->getParameter('kernel.root_dir') . '/config/');
|
||||||
|
$config = Yaml::parse(file_get_contents($configDir . '/' . $engine . '.yml'));
|
||||||
|
|
||||||
|
if (!isset($config['doctrine']['orm']['dql'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = $config['doctrine']['orm']['dql'];
|
||||||
|
|
||||||
|
if (!empty($sql)) {
|
||||||
|
foreach ($sql['string_functions'] as $name => $function) {
|
||||||
|
$ormConfigDef->addMethodCall('addCustomStringFunction', array($name, $function));
|
||||||
|
}
|
||||||
|
foreach ($sql['numeric_functions'] as $name => $function) {
|
||||||
|
$ormConfigDef->addMethodCall('addCustomNumericFunction', array($name, $function));
|
||||||
|
}
|
||||||
|
foreach ($sql['datetime_functions'] as $name => $function) {
|
||||||
|
$ormConfigDef->addMethodCall('addCustomDatetimeFunction', array($name, $function));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ namespace AppBundle\Entity;
|
|||||||
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User
|
* User
|
||||||
@@ -28,6 +28,8 @@ class User implements UserInterface
|
|||||||
* @var string
|
* @var string
|
||||||
*
|
*
|
||||||
* @ORM\Column(name="name", type="string", length=160, nullable=false, unique=true)
|
* @ORM\Column(name="name", type="string", length=160, nullable=false, unique=true)
|
||||||
|
* @Assert\NotBlank()
|
||||||
|
* @Assert\Length(min=6, max=160)
|
||||||
*/
|
*/
|
||||||
private $username;
|
private $username;
|
||||||
|
|
||||||
@@ -35,6 +37,8 @@ class User implements UserInterface
|
|||||||
* @var string
|
* @var string
|
||||||
*
|
*
|
||||||
* @ORM\Column(name="mail", type="string", length=160, nullable=false, unique=true)
|
* @ORM\Column(name="mail", type="string", length=160, nullable=false, unique=true)
|
||||||
|
* @Assert\NotBlank()
|
||||||
|
* @Assert\Email()
|
||||||
*/
|
*/
|
||||||
private $email;
|
private $email;
|
||||||
|
|
||||||
@@ -45,6 +49,14 @@ class User implements UserInterface
|
|||||||
*/
|
*/
|
||||||
private $password;
|
private $password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*
|
||||||
|
* @Assert\NotBlank(groups={"registration", "passwordUpdate"})
|
||||||
|
* @Assert\Length(min=6, max=4096, groups={"registration", "passwordUpdate"})
|
||||||
|
*/
|
||||||
|
private $plainPassword;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*
|
*
|
||||||
@@ -52,6 +64,13 @@ class User implements UserInterface
|
|||||||
*/
|
*/
|
||||||
private $alias;
|
private $alias;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*
|
||||||
|
* @ORM\Column(name="language", type="string", length=5, nullable=true)
|
||||||
|
*/
|
||||||
|
private $language = 'de';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var boolean
|
* @var boolean
|
||||||
*
|
*
|
||||||
@@ -62,16 +81,9 @@ class User implements UserInterface
|
|||||||
/**
|
/**
|
||||||
* @var \DateTime
|
* @var \DateTime
|
||||||
*
|
*
|
||||||
* @ORM\Column(name="start_timeframe", type="datetime", nullable=true)
|
* @ORM\Column(name="registration_date", type="datetime", nullable=true)
|
||||||
*/
|
*/
|
||||||
private $timeframeBegin;
|
private $registeredAt;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \DateTime
|
|
||||||
*
|
|
||||||
* @ORM\Column(name="end_timeframe", type="datetime", nullable=true)
|
|
||||||
*/
|
|
||||||
private $timeframeEnd;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
@@ -94,6 +106,14 @@ class User implements UserInterface
|
|||||||
*/
|
*/
|
||||||
private $roles = [];
|
private $roles = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->registeredAt = new \DateTime();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
@@ -103,35 +123,22 @@ class User implements UserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return \DateTime
|
||||||
*/
|
*/
|
||||||
public function getTimeframeBegin()
|
public function getRegisteredAt()
|
||||||
{
|
{
|
||||||
return $this->timeframeBegin;
|
return $this->registeredAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $timeframeBegin
|
* @param $registeredAt
|
||||||
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setTimeframeBegin($timeframeBegin)
|
public function setRegisteredAt($registeredAt)
|
||||||
{
|
{
|
||||||
$this->timeframeBegin = $timeframeBegin;
|
$this->registeredAt = $registeredAt;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return $this;
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getTimeframeEnd()
|
|
||||||
{
|
|
||||||
return $this->timeframeEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $timeframeEnd
|
|
||||||
*/
|
|
||||||
public function setTimeframeEnd($timeframeEnd)
|
|
||||||
{
|
|
||||||
$this->timeframeEnd = $timeframeEnd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -193,6 +200,28 @@ class User implements UserInterface
|
|||||||
return $this->password;
|
return $this->password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only for form editing, you don't need this method!
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPlainPassword()
|
||||||
|
{
|
||||||
|
return $this->plainPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only for form editing, you don't need this method!
|
||||||
|
*
|
||||||
|
* @param string $password
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setPlainPassword($password)
|
||||||
|
{
|
||||||
|
$this->plainPassword = $password;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
@@ -266,6 +295,22 @@ class User implements UserInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getLanguage()
|
||||||
|
{
|
||||||
|
return $this->language;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $language
|
||||||
|
*/
|
||||||
|
public function setLanguage($language)
|
||||||
|
{
|
||||||
|
$this->language = $language;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the roles or permissions granted to the user for security.
|
* Returns the roles or permissions granted to the user for security.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -29,11 +29,18 @@ class NavbarShowUserListener
|
|||||||
*/
|
*/
|
||||||
protected $storage;
|
protected $storage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NavbarShowUserListener constructor.
|
||||||
|
* @param TokenStorageInterface $tokenStorage
|
||||||
|
*/
|
||||||
public function __construct(TokenStorageInterface $tokenStorage)
|
public function __construct(TokenStorageInterface $tokenStorage)
|
||||||
{
|
{
|
||||||
$this->storage = $tokenStorage;
|
$this->storage = $tokenStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ShowUserEvent $event
|
||||||
|
*/
|
||||||
public function onShowUser(ShowUserEvent $event)
|
public function onShowUser(ShowUserEvent $event)
|
||||||
{
|
{
|
||||||
/* @var $myUser User */
|
/* @var $myUser User */
|
||||||
@@ -51,8 +58,7 @@ class NavbarShowUserListener
|
|||||||
->setIsOnline(true)
|
->setIsOnline(true)
|
||||||
->setTitle($myUser->getTitle())
|
->setTitle($myUser->getTitle())
|
||||||
->setAvatar($myUser->getAvatar())
|
->setAvatar($myUser->getAvatar())
|
||||||
->setMemberSince(new \DateTime()) // FIXME add column to entity
|
->setMemberSince(new \DateTime());
|
||||||
;
|
|
||||||
|
|
||||||
$event->setUser($user);
|
$event->setUser($user);
|
||||||
}
|
}
|
||||||
|
|||||||
67
src/AppBundle/Form/UserEditType.php
Normal file
67
src/AppBundle/Form/UserEditType.php
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai package.
|
||||||
|
*
|
||||||
|
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace AppBundle\Form;
|
||||||
|
|
||||||
|
use AppBundle\Entity\User;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\LanguageType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the form used to create and manipulate Users.
|
||||||
|
*
|
||||||
|
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*/
|
||||||
|
class UserEditType extends AbstractType
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
// string - length 160
|
||||||
|
->add('alias', null, [
|
||||||
|
'label' => 'label.alias',
|
||||||
|
])
|
||||||
|
// string - length 50
|
||||||
|
->add('title', null, [
|
||||||
|
//'attr' => ['autofocus' => true],
|
||||||
|
'label' => 'label.title',
|
||||||
|
])
|
||||||
|
// string - length 160
|
||||||
|
->add('email', null, [
|
||||||
|
'label' => 'label.email',
|
||||||
|
])
|
||||||
|
// string - length 5
|
||||||
|
->add('language', LanguageType::class, [
|
||||||
|
'label' => 'label.language',
|
||||||
|
'choices' => array('Deutsch' => 'de') // FIXME translation
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => User::class,
|
||||||
|
'csrf_protection' => true,
|
||||||
|
'csrf_field_name' => '_token',
|
||||||
|
'csrf_token_id' => 'edit_user_profile',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
55
src/AppBundle/Form/UserPasswordType.php
Normal file
55
src/AppBundle/Form/UserPasswordType.php
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai package.
|
||||||
|
*
|
||||||
|
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace AppBundle\Form;
|
||||||
|
|
||||||
|
use AppBundle\Entity\User;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the form used to create and manipulate Users.
|
||||||
|
*
|
||||||
|
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*/
|
||||||
|
class UserPasswordType extends AbstractType
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('plainPassword', RepeatedType::class, [
|
||||||
|
'type' => PasswordType::class,
|
||||||
|
'first_options' => array('label' => 'security.label.password'),
|
||||||
|
'second_options' => array('label' => 'security.label.password_repeat'),
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => User::class,
|
||||||
|
'csrf_protection' => true,
|
||||||
|
'csrf_field_name' => '_token',
|
||||||
|
'csrf_token_id' => 'edit_user_password',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,7 +57,7 @@ class UserRepository extends EntityRepository
|
|||||||
|
|
||||||
public function findByUsername($username)
|
public function findByUsername($username)
|
||||||
{
|
{
|
||||||
return $this->findOneBy(['']);
|
return $this->findOneBy(['username' => $username]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
86
src/TimesheetBundle/Model/Statistic/Month.php
Normal file
86
src/TimesheetBundle/Model/Statistic/Month.php
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai package.
|
||||||
|
*
|
||||||
|
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace TimesheetBundle\Model\Statistic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yearly statistics
|
||||||
|
*
|
||||||
|
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*/
|
||||||
|
class Month
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $month;
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $totalDuration = 0;
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $totalRate = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Month constructor.
|
||||||
|
* @param string $month
|
||||||
|
*/
|
||||||
|
public function __construct($month)
|
||||||
|
{
|
||||||
|
$this->month = $month;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getMonth()
|
||||||
|
{
|
||||||
|
return $this->month;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getTotalDuration()
|
||||||
|
{
|
||||||
|
return $this->totalDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $totalDuration
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setTotalDuration($totalDuration)
|
||||||
|
{
|
||||||
|
$this->totalDuration = $totalDuration;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getTotalRate()
|
||||||
|
{
|
||||||
|
return $this->totalRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $totalRate
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setTotalRate($totalRate)
|
||||||
|
{
|
||||||
|
$this->totalRate = $totalRate;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
78
src/TimesheetBundle/Model/Statistic/Year.php
Normal file
78
src/TimesheetBundle/Model/Statistic/Year.php
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai package.
|
||||||
|
*
|
||||||
|
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace TimesheetBundle\Model\Statistic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yearly statistics
|
||||||
|
*
|
||||||
|
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*/
|
||||||
|
class Year
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $year;
|
||||||
|
/**
|
||||||
|
* @var Month[]
|
||||||
|
*/
|
||||||
|
protected $months;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Year constructor.
|
||||||
|
* @param string $year
|
||||||
|
*/
|
||||||
|
public function __construct($year)
|
||||||
|
{
|
||||||
|
$this->year = $year;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getYear()
|
||||||
|
{
|
||||||
|
return $this->year;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Month $month
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setMonth(Month $month)
|
||||||
|
{
|
||||||
|
$this->months[(int)$month->getMonth()] = $month;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $month
|
||||||
|
* @return null|Month
|
||||||
|
*/
|
||||||
|
public function getMonth($month)
|
||||||
|
{
|
||||||
|
if (isset($this->months[$month])) {
|
||||||
|
return $this->months[$month];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Month[]
|
||||||
|
*/
|
||||||
|
public function getMonths()
|
||||||
|
{
|
||||||
|
return array_values($this->months);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
namespace TimesheetBundle\Model;
|
namespace TimesheetBundle\Model;
|
||||||
|
|
||||||
|
use \DateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timesheet statistics
|
* Timesheet statistics
|
||||||
*
|
*
|
||||||
@@ -34,6 +36,10 @@ class TimesheetStatistic
|
|||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
protected $amountTotal = 0;
|
protected $amountTotal = 0;
|
||||||
|
/**
|
||||||
|
* @var \DateTime
|
||||||
|
*/
|
||||||
|
protected $firstEntry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return int
|
* @return int
|
||||||
@@ -98,4 +104,20 @@ class TimesheetStatistic
|
|||||||
{
|
{
|
||||||
$this->amountThisMonth = $amountThisMonth;
|
$this->amountThisMonth = $amountThisMonth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return DateTime
|
||||||
|
*/
|
||||||
|
public function getFirstEntry()
|
||||||
|
{
|
||||||
|
return $this->firstEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DateTime $firstEntry
|
||||||
|
*/
|
||||||
|
public function setFirstEntry(DateTime $firstEntry)
|
||||||
|
{
|
||||||
|
$this->firstEntry = $firstEntry;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ use Doctrine\ORM\Query;
|
|||||||
use Doctrine\DBAL\Types\Type;
|
use Doctrine\DBAL\Types\Type;
|
||||||
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
||||||
use Pagerfanta\Pagerfanta;
|
use Pagerfanta\Pagerfanta;
|
||||||
|
use TimesheetBundle\Model\Statistic\Month;
|
||||||
|
use TimesheetBundle\Model\Statistic\Year;
|
||||||
use TimesheetBundle\Model\TimesheetGlobalStatistic;
|
use TimesheetBundle\Model\TimesheetGlobalStatistic;
|
||||||
use TimesheetBundle\Model\TimesheetStatistic;
|
use TimesheetBundle\Model\TimesheetStatistic;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
@@ -30,13 +32,29 @@ use DateTime;
|
|||||||
class TimesheetRepository extends EntityRepository
|
class TimesheetRepository extends EntityRepository
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $select
|
||||||
|
* @param User|null $user
|
||||||
|
* @return \Doctrine\ORM\QueryBuilder
|
||||||
|
*/
|
||||||
protected function queryThisMonth($select, User $user = null)
|
protected function queryThisMonth($select, User $user = null)
|
||||||
{
|
{
|
||||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
|
||||||
|
|
||||||
// FIXME
|
|
||||||
$end = new DateTime();
|
$end = new DateTime();
|
||||||
$begin = $end->sub(new \DateInterval("P30D"));
|
$begin = $end->sub(new \DateInterval("P30D")); // FIXME last month is not last 30 days
|
||||||
|
|
||||||
|
return $this->queryTimeRange($select, $begin, $end, $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $select
|
||||||
|
* @param DateTime $begin
|
||||||
|
* @param DateTime $end
|
||||||
|
* @param User|null $user
|
||||||
|
* @return \Doctrine\ORM\QueryBuilder
|
||||||
|
*/
|
||||||
|
protected function queryTimeRange($select, DateTime $begin, DateTime $end, User $user = null)
|
||||||
|
{
|
||||||
|
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||||
|
|
||||||
$qb->select($select)
|
$qb->select($select)
|
||||||
->from('TimesheetBundle:Timesheet', 't')
|
->from('TimesheetBundle:Timesheet', 't')
|
||||||
@@ -52,6 +70,7 @@ class TimesheetRepository extends EntityRepository
|
|||||||
|
|
||||||
return $qb;
|
return $qb;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch statistic data for one user.
|
* Fetch statistic data for one user.
|
||||||
*
|
*
|
||||||
@@ -74,16 +93,67 @@ class TimesheetRepository extends EntityRepository
|
|||||||
$durationMonth = $this->queryThisMonth('SUM(t.duration)', $user)
|
$durationMonth = $this->queryThisMonth('SUM(t.duration)', $user)
|
||||||
->getQuery()
|
->getQuery()
|
||||||
->getSingleScalarResult();
|
->getSingleScalarResult();
|
||||||
|
$firstEntry = $this->getEntityManager()
|
||||||
|
->createQuery('SELECT MIN(t.begin) FROM TimesheetBundle:Timesheet t WHERE t.user = :user')
|
||||||
|
->setParameter('user', $user)
|
||||||
|
->getSingleScalarResult();
|
||||||
|
|
||||||
$stats = new TimesheetStatistic();
|
$stats = new TimesheetStatistic();
|
||||||
$stats->setAmountTotal($rateTotal);
|
$stats->setAmountTotal($rateTotal);
|
||||||
$stats->setDurationTotal($durationTotal);
|
$stats->setDurationTotal($durationTotal);
|
||||||
$stats->setAmountThisMonth($amountMonth);
|
$stats->setAmountThisMonth($amountMonth);
|
||||||
$stats->setDurationThisMonth($durationMonth);
|
$stats->setDurationThisMonth($durationMonth);
|
||||||
|
$stats->setFirstEntry(new DateTime($firstEntry));
|
||||||
|
|
||||||
return $stats;
|
return $stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of Year statistics.
|
||||||
|
*
|
||||||
|
* @param User|null $user
|
||||||
|
* @return Year[]
|
||||||
|
*/
|
||||||
|
public function getMonthlyStats(User $user = null)
|
||||||
|
{
|
||||||
|
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||||
|
|
||||||
|
$qb->select('SUM(t.rate) as totalRate, SUM(t.duration) as totalDuration, MONTH(t.begin) as month, YEAR(t.begin) as year')
|
||||||
|
->from('TimesheetBundle:Timesheet', 't')
|
||||||
|
->where($qb->expr()->gt('t.begin', '0'))
|
||||||
|
->andWhere($qb->expr()->isNotNull('t.end'))
|
||||||
|
->orderBy('year', 'DESC')
|
||||||
|
->addOrderBy('month', 'ASC')
|
||||||
|
->groupBy('year')
|
||||||
|
->addGroupBy('month');
|
||||||
|
|
||||||
|
if (null !== $user) {
|
||||||
|
$qb->where('t.user = :user')
|
||||||
|
->setParameter('user', $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
$years = [];
|
||||||
|
foreach($qb->getQuery()->execute() as $statRow) {
|
||||||
|
$curYear = $statRow['year'];
|
||||||
|
|
||||||
|
if (!isset($years[$curYear])) {
|
||||||
|
$year = new Year($curYear);
|
||||||
|
for ($i = 1; $i < 13; $i++) {
|
||||||
|
$month = $i < 10 ? '0' . $i : (string)$i;
|
||||||
|
$year->setMonth(new Month($month));
|
||||||
|
}
|
||||||
|
$years[$curYear] = $year;
|
||||||
|
}
|
||||||
|
|
||||||
|
$month = new Month($statRow['month']);
|
||||||
|
$month->setTotalDuration($statRow['totalDuration'])
|
||||||
|
->setTotalRate($statRow['totalRate']);
|
||||||
|
$years[$curYear]->setMonth($month);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $years;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch statistic data for all user.
|
* Fetch statistic data for all user.
|
||||||
*
|
*
|
||||||
@@ -133,8 +203,8 @@ class TimesheetRepository extends EntityRepository
|
|||||||
|
|
||||||
$qb->select('t')
|
$qb->select('t')
|
||||||
->from('TimesheetBundle:Timesheet', 't')
|
->from('TimesheetBundle:Timesheet', 't')
|
||||||
->where('t.begin > 0')
|
->where($qb->expr()->gt('t.begin', '0'))
|
||||||
->andWhere('t.end is NULL');
|
->andWhere($qb->expr()->isNull('t.end'));
|
||||||
|
|
||||||
$params = [];
|
$params = [];
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
'label.project': 'book',
|
'label.project': 'book',
|
||||||
'label.comment': 'comment-o',
|
'label.comment': 'comment-o',
|
||||||
'label.visible': 'eye',
|
'label.visible': 'eye',
|
||||||
|
'label.actions': 'pencil-square-o',
|
||||||
}) }}
|
}) }}
|
||||||
|
|
||||||
{% for entry in entries %}
|
{% for entry in entries %}
|
||||||
@@ -22,6 +23,9 @@
|
|||||||
<td>{{ entry.project }}</td>
|
<td>{{ entry.project }}</td>
|
||||||
<td>{{ entry.comment }}</td>
|
<td>{{ entry.comment }}</td>
|
||||||
<td>{{ widgets.label_visible(entry.visible) }}</td>
|
<td>{{ widgets.label_visible(entry.visible) }}</td>
|
||||||
|
<td>
|
||||||
|
{{ widgets.button_group({'edit': '#', 'trash': '#'}) }}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
'label.activity': 'tasks',
|
'label.activity': 'tasks',
|
||||||
'label.budget': 'credit-card',
|
'label.budget': 'credit-card',
|
||||||
'label.visible': 'eye',
|
'label.visible': 'eye',
|
||||||
|
'label.actions': 'pencil-square-o',
|
||||||
}) }}
|
}) }}
|
||||||
|
|
||||||
{% for entry in entries %}
|
{% for entry in entries %}
|
||||||
@@ -24,6 +25,9 @@
|
|||||||
<td>{{ widgets.badge_counter(entry.activities.count) }}</td>
|
<td>{{ widgets.badge_counter(entry.activities.count) }}</td>
|
||||||
<td>{{ entry.budget|money}}</td>
|
<td>{{ entry.budget|money}}</td>
|
||||||
<td>{{ widgets.label_visible(entry.visible) }}</td>
|
<td>{{ widgets.label_visible(entry.visible) }}</td>
|
||||||
|
<td>
|
||||||
|
{{ widgets.button_group({'edit': '#', 'trash': '#'}) }}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +1,45 @@
|
|||||||
{% extends 'base.html.twig' %}
|
{% extends 'base.html.twig' %}
|
||||||
{% import "macros/datatables.html.twig" as widgets %}
|
{% import "macros/widgets.html.twig" as widgets %}
|
||||||
|
{% import "macros/datatables.html.twig" as datatables %}
|
||||||
|
|
||||||
{% 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 main %}
|
{% block main %}
|
||||||
{% if entries.count > 0 %}
|
{% if entries.count > 0 %}
|
||||||
{{ widgets.data_table_header({
|
{{ datatables.data_table_header({
|
||||||
'label.date': 'calendar',
|
'label.date': 'calendar',
|
||||||
'label.starttime': 'hourglass-start',
|
'label.starttime': 'hourglass-start',
|
||||||
'label.endtime': 'hourglass-end',
|
'label.endtime': 'hourglass-end',
|
||||||
'label.duration': 'clock-o',
|
'label.duration': 'clock-o',
|
||||||
|
'label.rate': 'money',
|
||||||
'label.username': 'user',
|
'label.username': 'user',
|
||||||
'label.description': 'pencil-square-o',
|
'label.description': 'comment-o',
|
||||||
|
'label.actions': 'pencil-square-o',
|
||||||
}) }}
|
}) }}
|
||||||
|
|
||||||
{% for entry in entries %}
|
{% for entry in entries %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ entry.begin|date(kimai_context.date_1) }}</td>
|
<td>{{ entry.begin|date(kimai_context.date_1) }}</td>
|
||||||
<td>{{ entry.begin|date("H:i") }}</td>
|
<td>{{ entry.begin|date("H:i") }}</td>
|
||||||
|
{% if entry.end %}
|
||||||
<td>{{ entry.end|date("H:i") }}</td>
|
<td>{{ entry.end|date("H:i") }}</td>
|
||||||
<td>{{ entry.duration|duration }}</td>
|
<td>{{ entry.duration|duration }}</td>
|
||||||
<td>{{ entry.user.username }}</td>
|
<td>{{ entry.rate|money }}</td>
|
||||||
|
{% else %}
|
||||||
|
<td>‐</td>
|
||||||
|
<td>‐</td>
|
||||||
|
<td>‐</td>
|
||||||
|
{% endif %}
|
||||||
|
<td><a href="{{ path('profile'|route_alias, {'username' : entry.user.username}) }}">{{ entry.user.username }}</a></td>
|
||||||
<td>{{ entry.description }}</td>
|
<td>{{ entry.description }}</td>
|
||||||
|
<td>
|
||||||
|
{{ widgets.button_group({'edit': '#', 'trash': '#'}) }}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{{ widgets.data_table_footer(entries, 'admin_timesheet_paginated') }}
|
{{ datatables.data_table_footer(entries, 'admin_timesheet_paginated') }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ widgets.callout('warning', 'error.no_entries_found') }}
|
{{ widgets.callout('warning', 'error.no_entries_found') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -1,30 +1,43 @@
|
|||||||
{% extends 'base.html.twig' %}
|
{% extends 'base.html.twig' %}
|
||||||
{% import "macros/datatables.html.twig" as widgets %}
|
{% import "macros/widgets.html.twig" as widgets %}
|
||||||
|
{% import "macros/datatables.html.twig" as datatables %}
|
||||||
|
|
||||||
{% block page_title %}{{ 'timesheet.title'|trans }}{% endblock %}
|
{% block page_title %}{{ 'timesheet.title'|trans }}{% endblock %}
|
||||||
{% block page_subtitle %}{{ 'timesheet.subtitle'|trans }}{% endblock %}
|
{% block page_subtitle %}{{ 'timesheet.subtitle'|trans }}{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% if entries.count > 0 %}
|
{% if entries.count > 0 %}
|
||||||
{{ widgets.data_table_header({
|
{{ datatables.data_table_header({
|
||||||
'label.date': 'calendar',
|
'label.date': 'calendar',
|
||||||
'label.starttime': 'hourglass-start',
|
'label.starttime': 'hourglass-start',
|
||||||
'label.endtime': 'hourglass-end',
|
'label.endtime': 'hourglass-end',
|
||||||
'label.duration': 'clock-o',
|
'label.duration': 'clock-o',
|
||||||
'label.description': 'pencil-square-o',
|
'label.rate': 'money',
|
||||||
|
'label.description': 'comment-o',
|
||||||
|
'label.actions': 'pencil-square-o',
|
||||||
}) }}
|
}) }}
|
||||||
|
|
||||||
{% for entry in entries %}
|
{% for entry in entries %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ entry.begin|date(kimai_context.date_1) }}</td>
|
<td>{{ entry.begin|date(kimai_context.date_1) }}</td>
|
||||||
<td>{{ entry.begin|date("H:i") }}</td>
|
<td>{{ entry.begin|date("H:i") }}</td>
|
||||||
|
{% if entry.end %}
|
||||||
<td>{{ entry.end|date("H:i") }}</td>
|
<td>{{ entry.end|date("H:i") }}</td>
|
||||||
<td>{{ entry.duration|duration }}</td>
|
<td>{{ entry.duration|duration }}</td>
|
||||||
|
<td>{{ entry.rate|money }}</td>
|
||||||
|
{% else %}
|
||||||
|
<td>‐</td>
|
||||||
|
<td>‐</td>
|
||||||
|
<td>‐</td>
|
||||||
|
{% endif %}
|
||||||
<td>{{ entry.description }}</td>
|
<td>{{ entry.description }}</td>
|
||||||
|
<td>
|
||||||
|
{{ widgets.button_group({'repeat': '#', 'edit': '#', 'trash': '#'}) }}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{{ widgets.data_table_footer(entries, 'timesheet_paginated') }}
|
{{ datatables.data_table_footer(entries, 'timesheet_paginated') }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ widgets.callout('warning', 'error.no_entries_found') }}
|
{{ widgets.callout('warning', 'error.no_entries_found') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
namespace TimesheetBundle;
|
namespace TimesheetBundle;
|
||||||
|
|
||||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
use TimesheetBundle\EventListener\Menu as MenuListener;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class defines the Bundle for all Timesheet related topics
|
* This class defines the Bundle for all Timesheet related topics
|
||||||
@@ -27,8 +26,5 @@ class TimesheetBundle extends Bundle
|
|||||||
*/
|
*/
|
||||||
public function boot()
|
public function boot()
|
||||||
{
|
{
|
||||||
$listener = new MenuListener();
|
|
||||||
/* @var $dispatcher \Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher */
|
|
||||||
$dispatcher = $this->container->get('event_dispatcher');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
width: 30px;
|
width: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-panel>.info {
|
||||||
|
left: 40px;
|
||||||
|
padding-top: 0px;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
#ticktac img {
|
#ticktac img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
11
web/js/Chart.min.js
vendored
Executable file
11
web/js/Chart.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
@@ -10,7 +10,6 @@ $(document).ready(function() {
|
|||||||
},
|
},
|
||||||
hook: function(name) {
|
hook: function(name) {
|
||||||
alert('Hook: ' + name);
|
alert('Hook: ' + name);
|
||||||
console.log($.fn.kimai.settings);
|
|
||||||
},
|
},
|
||||||
subscribe: function() {
|
subscribe: function() {
|
||||||
pubSub.on.apply(pubSub, arguments);
|
pubSub.on.apply(pubSub, arguments);
|
||||||
@@ -31,9 +30,6 @@ $(document).ready(function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$.fn.kimai = function(options) {
|
$.fn.kimai = function(options) {
|
||||||
console.log('#');
|
|
||||||
console.log(options);
|
|
||||||
console.log('#');
|
|
||||||
if (methods[options]) {
|
if (methods[options]) {
|
||||||
return methods[options].apply( this, Array.prototype.slice.call(arguments, 1));
|
return methods[options].apply( this, Array.prototype.slice.call(arguments, 1));
|
||||||
} else if (typeof options === 'object') {
|
} else if (typeof options === 'object') {
|
||||||
|
|||||||
Reference in New Issue
Block a user