summaryrefslogtreecommitdiff
path: root/app/Template/user_view
diff options
context:
space:
mode:
authorFrederic Guillot <fred@kanboard.net>2016-05-16 20:55:21 -0400
committerFrederic Guillot <fred@kanboard.net>2016-05-16 20:55:21 -0400
commit4514bc1d4b4abff23902e46da76e70f13a3647eb (patch)
treefb4f03d47f49bd8acc6fbae943ac44ac58df9540 /app/Template/user_view
parentabdf6f97800e7e838b5841820815385b183bab67 (diff)
Improve user controllers and views
Diffstat (limited to 'app/Template/user_view')
-rw-r--r--app/Template/user_view/authentication.php27
-rw-r--r--app/Template/user_view/edit.php35
-rw-r--r--app/Template/user_view/external.php11
-rw-r--r--app/Template/user_view/integrations.php13
-rw-r--r--app/Template/user_view/last.php24
-rw-r--r--app/Template/user_view/layout.php19
-rw-r--r--app/Template/user_view/notifications.php26
-rw-r--r--app/Template/user_view/password.php26
-rw-r--r--app/Template/user_view/password_reset.php26
-rw-r--r--app/Template/user_view/profile.php9
-rw-r--r--app/Template/user_view/sessions.php26
-rw-r--r--app/Template/user_view/share.php15
-rw-r--r--app/Template/user_view/show.php40
-rw-r--r--app/Template/user_view/sidebar.php83
-rw-r--r--app/Template/user_view/timesheet.php29
15 files changed, 409 insertions, 0 deletions
diff --git a/app/Template/user_view/authentication.php b/app/Template/user_view/authentication.php
new file mode 100644
index 00000000..44643388
--- /dev/null
+++ b/app/Template/user_view/authentication.php
@@ -0,0 +1,27 @@
+<div class="page-header">
+ <h2><?= t('Edit Authentication') ?></h2>
+</div>
+<form method="post" action="<?= $this->url->href('UserViewController', 'authentication', array('user_id' => $user['id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+
+ <?= $this->form->hidden('id', $values) ?>
+ <?= $this->form->hidden('username', $values) ?>
+
+ <?= $this->hook->render('template:user:authentication:form', array('values' => $values, 'errors' => $errors, 'user' => $user)) ?>
+
+ <?= $this->form->checkbox('is_ldap_user', t('Remote user'), 1, isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1) ?>
+ <?= $this->form->checkbox('disable_login_form', t('Disallow login form'), 1, isset($values['disable_login_form']) && $values['disable_login_form'] == 1) ?>
+
+ <div class="form-actions">
+ <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'UserViewController', 'show', array('user_id' => $user['id'])) ?>
+ </div>
+
+ <div class="alert alert-info">
+ <ul>
+ <li><?= t('Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.') ?></li>
+ <li><?= t('If you check the box "Disallow login form", credentials entered in the login form will be ignored.') ?></li>
+ </ul>
+ </div>
+</form>
diff --git a/app/Template/user_view/edit.php b/app/Template/user_view/edit.php
new file mode 100644
index 00000000..18947905
--- /dev/null
+++ b/app/Template/user_view/edit.php
@@ -0,0 +1,35 @@
+<div class="page-header">
+ <h2><?= t('Edit user') ?></h2>
+</div>
+<form method="post" action="<?= $this->url->href('UserViewController', 'edit', array('user_id' => $user['id'])) ?>" autocomplete="off">
+
+ <?= $this->form->csrf() ?>
+
+ <?= $this->form->hidden('id', $values) ?>
+
+ <?= $this->form->label(t('Username'), 'username') ?>
+ <?= $this->form->text('username', $values, $errors, array('required', isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1 ? 'readonly' : '', 'maxlength="50"')) ?>
+
+ <?= $this->form->label(t('Name'), 'name') ?>
+ <?= $this->form->text('name', $values, $errors) ?>
+
+ <?= $this->form->label(t('Email'), 'email') ?>
+ <?= $this->form->email('email', $values, $errors) ?>
+
+ <?= $this->form->label(t('Timezone'), 'timezone') ?>
+ <?= $this->form->select('timezone', $timezones, $values, $errors) ?>
+
+ <?= $this->form->label(t('Language'), 'language') ?>
+ <?= $this->form->select('language', $languages, $values, $errors) ?>
+
+ <?php if ($this->user->isAdmin()): ?>
+ <?= $this->form->label(t('Role'), 'role') ?>
+ <?= $this->form->select('role', $roles, $values, $errors) ?>
+ <?php endif ?>
+
+ <div class="form-actions">
+ <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'UserViewController', 'show', array('user_id' => $user['id'])) ?>
+ </div>
+</form>
diff --git a/app/Template/user_view/external.php b/app/Template/user_view/external.php
new file mode 100644
index 00000000..22c25af2
--- /dev/null
+++ b/app/Template/user_view/external.php
@@ -0,0 +1,11 @@
+<div class="page-header">
+ <h2><?= t('External authentications') ?></h2>
+</div>
+
+<?php $html = $this->hook->render('template:user:external', array('user' => $user)) ?>
+
+<?php if (empty($html)): ?>
+ <p class="alert"><?= t('No external authentication enabled.') ?></p>
+<?php else: ?>
+ <?= $html ?>
+<?php endif ?>
diff --git a/app/Template/user_view/integrations.php b/app/Template/user_view/integrations.php
new file mode 100644
index 00000000..4a237346
--- /dev/null
+++ b/app/Template/user_view/integrations.php
@@ -0,0 +1,13 @@
+<div class="page-header">
+ <h2><?= t('Integrations') ?></h2>
+</div>
+
+<form method="post" action="<?= $this->url->href('UserViewController', 'integrations', array('user_id' => $user['id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?php $hooks = $this->hook->render('template:user:integrations', array('values' => $values)) ?>
+ <?php if (! empty($hooks)): ?>
+ <?= $hooks ?>
+ <?php else: ?>
+ <p class="alert"><?= t('No external integration registered.') ?></p>
+ <?php endif ?>
+</form>
diff --git a/app/Template/user_view/last.php b/app/Template/user_view/last.php
new file mode 100644
index 00000000..3de4d5e2
--- /dev/null
+++ b/app/Template/user_view/last.php
@@ -0,0 +1,24 @@
+<div class="page-header">
+ <h2><?= t('Last logins') ?></h2>
+</div>
+
+<?php if (empty($last_logins)): ?>
+ <p class="alert"><?= t('Never connected.') ?></p>
+<?php else: ?>
+ <table class="table-small table-fixed">
+ <tr>
+ <th class="column-20"><?= t('Login date') ?></th>
+ <th class="column-15"><?= t('Authentication method') ?></th>
+ <th class="column-15"><?= t('IP address') ?></th>
+ <th><?= t('User agent') ?></th>
+ </tr>
+ <?php foreach ($last_logins as $login): ?>
+ <tr>
+ <td><?= $this->dt->datetime($login['date_creation']) ?></td>
+ <td><?= $this->text->e($login['auth_type']) ?></td>
+ <td><?= $this->text->e($login['ip']) ?></td>
+ <td><?= $this->text->e($login['user_agent']) ?></td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+<?php endif ?> \ No newline at end of file
diff --git a/app/Template/user_view/layout.php b/app/Template/user_view/layout.php
new file mode 100644
index 00000000..c3604b99
--- /dev/null
+++ b/app/Template/user_view/layout.php
@@ -0,0 +1,19 @@
+<section id="main">
+ <div class="page-header">
+ <?php if ($this->user->hasAccess('UserCreationController', 'show')): ?>
+ <ul>
+ <li><i class="fa fa-user fa-fw"></i><?= $this->url->link(t('All users'), 'UserListController', 'show') ?></li>
+ <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New local user'), 'UserCreationController', 'show', array(), false, 'popover') ?></li>
+ <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New remote user'), 'UserCreationController', 'show', array('remote' => 1), false, 'popover') ?></li>
+ <li><i class="fa fa-upload fa-fw"></i><?= $this->url->link(t('Import'), 'UserImportController', 'show', array(), false, 'popover') ?></li>
+ <li><i class="fa fa-users fa-fw"></i><?= $this->url->link(t('View all groups'), 'GroupListController', 'index') ?></li>
+ </ul>
+ <?php endif ?>
+ </div>
+ <section class="sidebar-container" id="user-section">
+ <?= $this->render('user_view/sidebar', array('user' => $user)) ?>
+ <div class="sidebar-content">
+ <?= $content_for_sublayout ?>
+ </div>
+ </section>
+</section>
diff --git a/app/Template/user_view/notifications.php b/app/Template/user_view/notifications.php
new file mode 100644
index 00000000..84ca1282
--- /dev/null
+++ b/app/Template/user_view/notifications.php
@@ -0,0 +1,26 @@
+<div class="page-header">
+ <h2><?= t('Notifications') ?></h2>
+</div>
+
+<form method="post" action="<?= $this->url->href('UserViewController', 'notifications', array('user_id' => $user['id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+
+ <h4><?= t('Notification methods:') ?></h4>
+ <?= $this->form->checkboxes('notification_types', $types, $notifications) ?>
+
+ <hr>
+ <h4><?= t('I want to receive notifications for:') ?></h4>
+ <?= $this->form->radios('notifications_filter', $filters, $notifications) ?>
+
+ <hr>
+ <?php if (! empty($projects)): ?>
+ <h4><?= t('I want to receive notifications only for those projects:') ?></h4>
+ <?= $this->form->checkboxes('notification_projects', $projects, $notifications) ?>
+ <?php endif ?>
+
+ <div class="form-actions">
+ <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'UserViewController', 'show', array('user_id' => $user['id'])) ?>
+ </div>
+</form>
diff --git a/app/Template/user_view/password.php b/app/Template/user_view/password.php
new file mode 100644
index 00000000..32ff9d5c
--- /dev/null
+++ b/app/Template/user_view/password.php
@@ -0,0 +1,26 @@
+<div class="page-header">
+ <h2><?= t('Password modification') ?></h2>
+</div>
+
+<form method="post" action="<?= $this->url->href('UserViewController', 'password', array('user_id' => $user['id'])) ?>" autocomplete="off">
+
+ <?= $this->form->hidden('id', $values) ?>
+ <?= $this->form->csrf() ?>
+
+ <div class="alert alert-error">
+ <?= $this->form->label(t('Current password for the user "%s"', $this->user->getFullname()), 'current_password') ?>
+ <?= $this->form->password('current_password', $values, $errors) ?>
+ </div>
+
+ <?= $this->form->label(t('New password for the user "%s"', $this->user->getFullname($user)), 'password') ?>
+ <?= $this->form->password('password', $values, $errors) ?>
+
+ <?= $this->form->label(t('Confirmation'), 'confirmation') ?>
+ <?= $this->form->password('confirmation', $values, $errors) ?>
+
+ <div class="form-actions">
+ <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'UserViewController', 'show', array('user_id' => $user['id'])) ?>
+ </div>
+</form>
diff --git a/app/Template/user_view/password_reset.php b/app/Template/user_view/password_reset.php
new file mode 100644
index 00000000..1371ce11
--- /dev/null
+++ b/app/Template/user_view/password_reset.php
@@ -0,0 +1,26 @@
+<div class="page-header">
+ <h2><?= t('Last Password Reset') ?></h2>
+</div>
+
+<?php if (empty($tokens)): ?>
+ <p class="alert"><?= t('The password has never been reinitialized.') ?></p>
+<?php else: ?>
+ <table class="table-small table-fixed">
+ <tr>
+ <th class="column-20"><?= t('Creation') ?></th>
+ <th class="column-20"><?= t('Expiration') ?></th>
+ <th class="column-5"><?= t('Active') ?></th>
+ <th class="column-15"><?= t('IP address') ?></th>
+ <th><?= t('User agent') ?></th>
+ </tr>
+ <?php foreach ($tokens as $token): ?>
+ <tr>
+ <td><?= $this->dt->datetime($token['date_creation']) ?></td>
+ <td><?= $this->dt->datetime($token['date_expiration']) ?></td>
+ <td><?= $token['is_active'] == 0 ? t('No') : t('Yes') ?></td>
+ <td><?= $this->text->e($token['ip']) ?></td>
+ <td><?= $this->text->e($token['user_agent']) ?></td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+<?php endif ?> \ No newline at end of file
diff --git a/app/Template/user_view/profile.php b/app/Template/user_view/profile.php
new file mode 100644
index 00000000..9c9d3282
--- /dev/null
+++ b/app/Template/user_view/profile.php
@@ -0,0 +1,9 @@
+<section id="main">
+ <br>
+ <?= $this->avatar->render($user['id'], $user['username'], $user['name'], $user['email'], $user['avatar_path']) ?>
+ <ul class="listing">
+ <li><?= t('Username:') ?> <strong><?= $this->text->e($user['username']) ?></strong></li>
+ <li><?= t('Name:') ?> <strong><?= $this->text->e($user['name']) ?: t('None') ?></strong></li>
+ <li><?= t('Email:') ?> <strong><?= $this->text->e($user['email']) ?: t('None') ?></strong></li>
+ </ul>
+</section> \ No newline at end of file
diff --git a/app/Template/user_view/sessions.php b/app/Template/user_view/sessions.php
new file mode 100644
index 00000000..eda3ef7f
--- /dev/null
+++ b/app/Template/user_view/sessions.php
@@ -0,0 +1,26 @@
+<div class="page-header">
+ <h2><?= t('Persistent connections') ?></h2>
+</div>
+
+<?php if (empty($sessions)): ?>
+ <p class="alert"><?= t('No session.') ?></p>
+<?php else: ?>
+ <table class="table-small table-fixed">
+ <tr>
+ <th class="column-20"><?= t('Creation date') ?></th>
+ <th class="column-20"><?= t('Expiration date') ?></th>
+ <th class="column-15"><?= t('IP address') ?></th>
+ <th><?= t('User agent') ?></th>
+ <th class="column-10"><?= t('Action') ?></th>
+ </tr>
+ <?php foreach ($sessions as $session): ?>
+ <tr>
+ <td><?= $this->dt->datetime($session['date_creation']) ?></td>
+ <td><?= $this->dt->datetime($session['expiration']) ?></td>
+ <td><?= $this->text->e($session['ip']) ?></td>
+ <td><?= $this->text->e($session['user_agent']) ?></td>
+ <td><?= $this->url->link(t('Remove'), 'UserViewController', 'removeSession', array('user_id' => $user['id'], 'id' => $session['id']), true) ?></td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+<?php endif ?>
diff --git a/app/Template/user_view/share.php b/app/Template/user_view/share.php
new file mode 100644
index 00000000..9ef150e8
--- /dev/null
+++ b/app/Template/user_view/share.php
@@ -0,0 +1,15 @@
+<div class="page-header">
+ <h2><?= t('Public access') ?></h2>
+</div>
+
+<?php if (! empty($user['token'])): ?>
+ <div class="listing">
+ <ul class="no-bullet">
+ <li><strong><i class="fa fa-rss-square"></i> <?= $this->url->link(t('RSS feed'), 'feed', 'user', array('token' => $user['token']), false, '', '', true) ?></strong></li>
+ <li><strong><i class="fa fa-calendar"></i> <?= $this->url->link(t('iCal feed'), 'ical', 'user', array('token' => $user['token']), false, '', '', true) ?></strong></li>
+ </ul>
+ </div>
+ <?= $this->url->link(t('Disable public access'), 'UserViewController', 'share', array('user_id' => $user['id'], 'switch' => 'disable'), true, 'btn btn-red') ?>
+<?php else: ?>
+ <?= $this->url->link(t('Enable public access'), 'UserViewController', 'share', array('user_id' => $user['id'], 'switch' => 'enable'), true, 'btn btn-blue') ?>
+<?php endif ?>
diff --git a/app/Template/user_view/show.php b/app/Template/user_view/show.php
new file mode 100644
index 00000000..df0affb8
--- /dev/null
+++ b/app/Template/user_view/show.php
@@ -0,0 +1,40 @@
+<div class="page-header">
+ <h2><?= t('Summary') ?></h2>
+</div>
+<ul class="listing">
+ <li><?= t('Username:') ?> <strong><?= $this->text->e($user['username']) ?></strong></li>
+ <li><?= t('Name:') ?> <strong><?= $this->text->e($user['name']) ?: t('None') ?></strong></li>
+ <li><?= t('Email:') ?> <strong><?= $this->text->e($user['email']) ?: t('None') ?></strong></li>
+ <li><?= t('Status:') ?> <strong><?= $user['is_active'] ? t('Active') : t('Inactive') ?></strong></li>
+</ul>
+
+<div class="page-header">
+ <h2><?= t('Security') ?></h2>
+</div>
+<ul class="listing">
+ <li><?= t('Role:') ?> <strong><?= $this->user->getRoleName($user['role']) ?></strong></li>
+ <li><?= t('Account type:') ?> <strong><?= $user['is_ldap_user'] ? t('Remote') : t('Local') ?></strong></li>
+ <li><?= $user['twofactor_activated'] == 1 ? t('Two factor authentication enabled') : t('Two factor authentication disabled') ?></li>
+</ul>
+
+<div class="page-header">
+ <h2><?= t('Preferences') ?></h2>
+</div>
+<ul class="listing">
+ <li><?= t('Timezone:') ?> <strong><?= $this->text->in($user['timezone'], $timezones) ?></strong></li>
+ <li><?= t('Language:') ?> <strong><?= $this->text->in($user['language'], $languages) ?></strong></li>
+ <li><?= t('Notifications:') ?> <strong><?= $user['notifications_enabled'] == 1 ? t('Enabled') : t('Disabled') ?></strong></li>
+</ul>
+
+<?php if (! empty($user['token'])): ?>
+ <div class="page-header">
+ <h2><?= t('Public access') ?></h2>
+ </div>
+
+ <div class="listing">
+ <ul class="no-bullet">
+ <li><strong><i class="fa fa-rss-square"></i> <?= $this->url->link(t('RSS feed'), 'feed', 'user', array('token' => $user['token']), false, '', '', true) ?></strong></li>
+ <li><strong><i class="fa fa-calendar"></i> <?= $this->url->link(t('iCal feed'), 'ical', 'user', array('token' => $user['token']), false, '', '', true) ?></strong></li>
+ </ul>
+ </div>
+<?php endif ?>
diff --git a/app/Template/user_view/sidebar.php b/app/Template/user_view/sidebar.php
new file mode 100644
index 00000000..9a25df16
--- /dev/null
+++ b/app/Template/user_view/sidebar.php
@@ -0,0 +1,83 @@
+<div class="sidebar">
+ <h2><?= t('Information') ?></h2>
+ <ul>
+ <?php if ($this->user->hasAccess('UserViewController', 'show')): ?>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'show') ?>>
+ <?= $this->url->link(t('Summary'), 'UserViewController', 'show', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
+ <?php if ($this->user->isAdmin()): ?>
+ <li>
+ <?= $this->url->link(t('User dashboard'), 'DashboardController', 'show', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
+ <?php if ($this->user->isAdmin() || $this->user->isCurrentUser($user['id'])): ?>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'timesheet') ?>>
+ <?= $this->url->link(t('Time tracking'), 'UserViewController', 'timesheet', array('user_id' => $user['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'lastLogin') ?>>
+ <?= $this->url->link(t('Last logins'), 'UserViewController', 'lastLogin', array('user_id' => $user['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'sessions') ?>>
+ <?= $this->url->link(t('Persistent connections'), 'UserViewController', 'sessions', array('user_id' => $user['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'passwordReset') ?>>
+ <?= $this->url->link(t('Password reset history'), 'UserViewController', 'passwordReset', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
+
+ <?= $this->hook->render('template:user:sidebar:information', array('user' => $user)) ?>
+ </ul>
+
+ <h2><?= t('Actions') ?></h2>
+ <ul>
+ <?php if ($this->user->isAdmin() || $this->user->isCurrentUser($user['id'])): ?>
+
+ <?php if ($this->user->hasAccess('UserViewController', 'edit')): ?>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'edit') ?>>
+ <?= $this->url->link(t('Edit profile'), 'UserViewController', 'edit', array('user_id' => $user['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('AvatarFile') ?>>
+ <?= $this->url->link(t('Avatar'), 'AvatarFile', 'show', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
+
+ <?php if ($user['is_ldap_user'] == 0): ?>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'password') ?>>
+ <?= $this->url->link(t('Change password'), 'UserViewController', 'password', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
+
+ <?php if ($this->user->isCurrentUser($user['id'])): ?>
+ <li <?= $this->app->checkMenuSelection('twofactor', 'index') ?>>
+ <?= $this->url->link(t('Two factor authentication'), 'twofactor', 'index', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php elseif ($this->user->hasAccess('twofactor', 'disable') && $user['twofactor_activated'] == 1): ?>
+ <li <?= $this->app->checkMenuSelection('twofactor', 'disable') ?>>
+ <?= $this->url->link(t('Two factor authentication'), 'twofactor', 'disable', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
+
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'share') ?>>
+ <?= $this->url->link(t('Public access'), 'UserViewController', 'share', array('user_id' => $user['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'notifications') ?>>
+ <?= $this->url->link(t('Notifications'), 'UserViewController', 'notifications', array('user_id' => $user['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'external') ?>>
+ <?= $this->url->link(t('External accounts'), 'UserViewController', 'external', array('user_id' => $user['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'integrations') ?>>
+ <?= $this->url->link(t('Integrations'), 'UserViewController', 'integrations', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
+
+ <?php if ($this->user->hasAccess('UserViewController', 'authentication')): ?>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'authentication') ?>>
+ <?= $this->url->link(t('Edit Authentication'), 'UserViewController', 'authentication', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
+
+ <?= $this->hook->render('template:user:sidebar:actions', array('user' => $user)) ?>
+ </ul>
+</div>
diff --git a/app/Template/user_view/timesheet.php b/app/Template/user_view/timesheet.php
new file mode 100644
index 00000000..92ebafb5
--- /dev/null
+++ b/app/Template/user_view/timesheet.php
@@ -0,0 +1,29 @@
+<div class="page-header">
+ <h2><?= t('Time Tracking') ?></h2>
+</div>
+
+<h3><?= t('Subtask timesheet') ?></h3>
+<?php if ($subtask_paginator->isEmpty()): ?>
+ <p class="alert"><?= t('There is nothing to show.') ?></p>
+<?php else: ?>
+ <table class="table-fixed">
+ <tr>
+ <th class="column-25"><?= $subtask_paginator->order(t('Task'), 'task_title') ?></th>
+ <th class="column-25"><?= $subtask_paginator->order(t('Subtask'), 'subtask_title') ?></th>
+ <th class="column-20"><?= $subtask_paginator->order(t('Start'), 'start') ?></th>
+ <th class="column-20"><?= $subtask_paginator->order(t('End'), 'end') ?></th>
+ <th class="column-10"><?= $subtask_paginator->order(t('Time spent'), 'time_spent') ?></th>
+ </tr>
+ <?php foreach ($subtask_paginator->getCollection() as $record): ?>
+ <tr>
+ <td><?= $this->url->link($this->text->e($record['task_title']), 'task', 'show', array('project_id' => $record['project_id'], 'task_id' => $record['task_id'])) ?></td>
+ <td><?= $this->url->link($this->text->e($record['subtask_title']), 'task', 'show', array('project_id' => $record['project_id'], 'task_id' => $record['task_id'])) ?></td>
+ <td><?= $this->dt->datetime($record['start']) ?></td>
+ <td><?= $this->dt->datetime($record['end']) ?></td>
+ <td><?= n($record['time_spent']).' '.t('hours') ?></td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+
+ <?= $subtask_paginator ?>
+<?php endif ?> \ No newline at end of file