summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.markdown3
-rw-r--r--app/Controller/Base.php6
-rw-r--r--app/Controller/Hourlyrate.php89
-rw-r--r--app/Controller/Subtask.php20
-rw-r--r--app/Controller/Timetable.php39
-rw-r--r--app/Controller/Timetableday.php88
-rw-r--r--app/Controller/Timetableextra.php16
-rw-r--r--app/Controller/Timetableoff.php107
-rw-r--r--app/Controller/Timetableweek.php99
-rw-r--r--app/Controller/User.php8
-rw-r--r--app/Core/Helper.php49
-rw-r--r--app/Locale/da_DK/translations.php36
-rw-r--r--app/Locale/de_DE/translations.php36
-rw-r--r--app/Locale/es_ES/translations.php36
-rw-r--r--app/Locale/fi_FI/translations.php36
-rw-r--r--app/Locale/fr_FR/translations.php36
-rw-r--r--app/Locale/hu_HU/translations.php36
-rw-r--r--app/Locale/it_IT/translations.php36
-rw-r--r--app/Locale/ja_JP/translations.php36
-rw-r--r--app/Locale/pl_PL/translations.php36
-rw-r--r--app/Locale/pt_BR/translations.php36
-rw-r--r--app/Locale/ru_RU/translations.php36
-rw-r--r--app/Locale/sv_SE/translations.php36
-rw-r--r--app/Locale/th_TH/translations.php36
-rw-r--r--app/Locale/tr_TR/translations.php776
-rw-r--r--app/Locale/zh_CN/translations.php36
-rw-r--r--app/Model/Acl.php1
-rw-r--r--app/Model/Config.php23
-rw-r--r--app/Model/HourlyRate.php103
-rw-r--r--app/Model/Subtask.php84
-rw-r--r--app/Model/Swimlane.php16
-rw-r--r--app/Model/Timetable.php194
-rw-r--r--app/Model/TimetableDay.php87
-rw-r--r--app/Model/TimetableExtra.php22
-rw-r--r--app/Model/TimetableOff.php125
-rw-r--r--app/Model/TimetableWeek.php91
-rw-r--r--app/Schema/Mysql.php83
-rw-r--r--app/Schema/Postgres.php78
-rw-r--r--app/Schema/Sqlite.php80
-rw-r--r--app/ServiceProvider/ClassProvider.php6
-rw-r--r--app/ServiceProvider/DatabaseProvider.php2
-rw-r--r--app/Template/app/projects.php2
-rw-r--r--app/Template/board/edit.php2
-rw-r--r--app/Template/board/filters.php6
-rw-r--r--app/Template/board/swimlane.php2
-rw-r--r--app/Template/hourlyrate/index.php46
-rw-r--r--app/Template/hourlyrate/remove.php13
-rw-r--r--app/Template/layout.php4
-rw-r--r--app/Template/project/index.php2
-rw-r--r--app/Template/project/show.php2
-rw-r--r--app/Template/subtask/create.php2
-rw-r--r--app/Template/subtask/edit.php2
-rw-r--r--app/Template/subtask/show.php12
-rw-r--r--app/Template/task/show.php2
-rw-r--r--app/Template/timetable/index.php44
-rw-r--r--app/Template/timetable_day/index.php45
-rw-r--r--app/Template/timetable_day/remove.php13
-rw-r--r--app/Template/timetable_extra/index.php56
-rw-r--r--app/Template/timetable_extra/remove.php13
-rw-r--r--app/Template/timetable_off/index.php56
-rw-r--r--app/Template/timetable_off/remove.php13
-rw-r--r--app/Template/timetable_week/index.php46
-rw-r--r--app/Template/timetable_week/remove.php13
-rw-r--r--app/Template/user/sidebar.php8
-rw-r--r--app/constants.php1
-rw-r--r--assets/css/app.css4
-rw-r--r--assets/css/src/table.css4
-rw-r--r--assets/js/app.js12
-rw-r--r--assets/js/src/base.js3
-rw-r--r--assets/js/src/board.js9
-rw-r--r--composer.lock8
-rw-r--r--config.default.php3
-rw-r--r--docs/api-json-rpc.markdown115
-rw-r--r--docs/faq.markdown5
-rw-r--r--jsonrpc.php6
-rwxr-xr-xscripts/make-assets.sh2
-rw-r--r--tests/functionals/ApiTest.php10
-rw-r--r--tests/units/HourlyRate.php43
-rw-r--r--tests/units/SubtaskTest.php75
-rw-r--r--tests/units/TimetableTest.php130
80 files changed, 3587 insertions, 46 deletions
diff --git a/README.markdown b/README.markdown
index 7fe2c58d..866ccb7d 100644
--- a/README.markdown
+++ b/README.markdown
@@ -33,7 +33,7 @@ Features
- Host anywhere (shared hosting, VPS, Raspberry Pi or localhost)
- No external dependencies
- **Super easy setup**, copy and paste files and you are done!
-- Translated in 15 languages (Brazilian, Chinese, Danish, English, Finnish, French, German, Hungarian, Italian, Japanese, Polish, Russian, Spanish, Swedish, Thai)
+- Translated in 16 languages (Brazilian, Chinese, Danish, English, Finnish, French, German, Hungarian, Italian, Japanese, Polish, Russian, Spanish, Swedish, Thai, Turkish)
Known bugs and feature requests
-------------------------------
@@ -172,6 +172,7 @@ Contributors:
- [Cmer](https://github.com/chncsu)
- [Colin Williams](https://github.com/crwilliams)
- [Crash5](https://github.com/crash5)
+- [Creador30](https://github.com/creador30)
- [Cynthia Pereira](https://github.com/cynthiapereira)
- [David-Norris](https://github.com/David-Norris)
- [Esteban Monge](https://github.com/EstebanMonge)
diff --git a/app/Controller/Base.php b/app/Controller/Base.php
index d949048d..ec202c06 100644
--- a/app/Controller/Base.php
+++ b/app/Controller/Base.php
@@ -34,6 +34,7 @@ use Symfony\Component\EventDispatcher\Event;
* @property \Model\Config $config
* @property \Model\DateParser $dateParser
* @property \Model\File $file
+ * @property \Model\HourlyRate $hourlyRate
* @property \Model\LastLogin $lastLogin
* @property \Model\Notification $notification
* @property \Model\Project $project
@@ -56,6 +57,11 @@ use Symfony\Component\EventDispatcher\Event;
* @property \Model\TaskPosition $taskPosition
* @property \Model\TaskPermission $taskPermission
* @property \Model\TaskStatus $taskStatus
+ * @property \Model\Timetable $timetable
+ * @property \Model\TimetableDay $timetableDay
+ * @property \Model\TimetableWeek $timetableWeek
+ * @property \Model\TimetableExtra $timetableExtra
+ * @property \Model\TimetableOff $timetableOff
* @property \Model\TaskValidator $taskValidator
* @property \Model\TaskLink $taskLink
* @property \Model\CommentHistory $commentHistory
diff --git a/app/Controller/Hourlyrate.php b/app/Controller/Hourlyrate.php
new file mode 100644
index 00000000..8d96e5ca
--- /dev/null
+++ b/app/Controller/Hourlyrate.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Controller;
+
+/**
+ * Hourly Rate controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class Hourlyrate extends User
+{
+ /**
+ * Display rate and form
+ *
+ * @access public
+ */
+ public function index(array $values = array(), array $errors = array())
+ {
+ $user = $this->getUser();
+
+ $this->response->html($this->layout('hourlyrate/index', array(
+ 'rates' => $this->hourlyRate->getAllByUser($user['id']),
+ 'currencies_list' => $this->config->getCurrencies(),
+ 'values' => $values + array('user_id' => $user['id']),
+ 'errors' => $errors,
+ 'user' => $user,
+ )));
+ }
+
+ /**
+ * Validate and save a new rate
+ *
+ * @access public
+ */
+ public function save()
+ {
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->hourlyRate->validateCreation($values);
+
+ if ($valid) {
+
+ if ($this->hourlyRate->create($values['user_id'], $values['rate'], $values['currency'], $values['date_effective'])) {
+ $this->session->flash(t('Hourly rate created successfully.'));
+ $this->response->redirect($this->helper->url('hourlyrate', 'index', array('user_id' => $values['user_id'])));
+ }
+ else {
+ $this->session->flashError(t('Unable to save the hourly rate.'));
+ }
+ }
+
+ $this->index($values, $errors);
+ }
+
+ /**
+ * Confirmation dialag box to remove a row
+ *
+ * @access public
+ */
+ public function confirm()
+ {
+ $user = $this->getUser();
+
+ $this->response->html($this->layout('hourlyrate/remove', array(
+ 'rate_id' => $this->request->getIntegerParam('rate_id'),
+ 'user' => $user,
+ )));
+ }
+
+ /**
+ * Remove a row
+ *
+ * @access public
+ */
+ public function remove()
+ {
+ $this->checkCSRFParam();
+ $user = $this->getUser();
+
+ if ($this->hourlyRate->remove($this->request->getIntegerParam('rate_id'))) {
+ $this->session->flash(t('Rate removed successfully.'));
+ }
+ else {
+ $this->session->flash(t('Unable to remove this rate.'));
+ }
+
+ $this->response->redirect($this->helper->url('hourlyrate', 'index', array('user_id' => $user['id'])));
+ }
+}
diff --git a/app/Controller/Subtask.php b/app/Controller/Subtask.php
index c7ec00d1..385785a1 100644
--- a/app/Controller/Subtask.php
+++ b/app/Controller/Subtask.php
@@ -185,7 +185,7 @@ class Subtask extends Base
if ($redirect === 'board') {
$this->session['has_subtask_inprogress'] = $this->subtask->hasSubtaskInProgress($this->userSession->getId());
-
+
$this->response->html($this->template->render('board/subtasks', array(
'subtasks' => $this->subtask->getAll($task['id']),
'task' => $task,
@@ -259,4 +259,22 @@ class Subtask extends Base
$this->response->redirect($this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
}
}
+
+ /**
+ * Move subtask position
+ *
+ * @access public
+ */
+ public function movePosition()
+ {
+ $this->checkCSRFParam();
+ $project_id = $this->request->getIntegerParam('project_id');
+ $task_id = $this->request->getIntegerParam('task_id');
+ $subtask_id = $this->request->getIntegerParam('subtask_id');
+ $direction = $this->request->getStringParam('direction');
+ $method = $direction === 'up' ? 'moveUp' : 'moveDown';
+
+ $this->subtask->$method($task_id, $subtask_id);
+ $this->response->redirect($this->helper->url('task', 'show', array('project_id' => $project_id, 'task_id' => $task_id)).'#subtasks');
+ }
}
diff --git a/app/Controller/Timetable.php b/app/Controller/Timetable.php
new file mode 100644
index 00000000..65edb44c
--- /dev/null
+++ b/app/Controller/Timetable.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Controller;
+
+use DateTime;
+
+/**
+ * Timetable controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class Timetable extends User
+{
+ /**
+ * Display timetable for the user
+ *
+ * @access public
+ */
+ public function index()
+ {
+ $user = $this->getUser();
+ $from = $this->request->getStringParam('from', date('Y-m-d'));
+ $to = $this->request->getStringParam('to', date('Y-m-d', strtotime('next week')));
+ $timetable = $this->timetable->calculate($user['id'], new DateTime($from), new DateTime($to));
+
+ $this->response->html($this->layout('timetable/index', array(
+ 'user' => $user,
+ 'timetable' => $timetable,
+ 'values' => array(
+ 'from' => $from,
+ 'to' => $to,
+ 'controller' => 'timetable',
+ 'action' => 'index',
+ 'user_id' => $user['id'],
+ ),
+ )));
+ }
+}
diff --git a/app/Controller/Timetableday.php b/app/Controller/Timetableday.php
new file mode 100644
index 00000000..eea44ae1
--- /dev/null
+++ b/app/Controller/Timetableday.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace Controller;
+
+/**
+ * Day Timetable controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class Timetableday extends User
+{
+ /**
+ * Display timetable for the user
+ *
+ * @access public
+ */
+ public function index(array $values = array(), array $errors = array())
+ {
+ $user = $this->getUser();
+
+ $this->response->html($this->layout('timetable_day/index', array(
+ 'timetable' => $this->timetableDay->getByUser($user['id']),
+ 'values' => $values + array('user_id' => $user['id']),
+ 'errors' => $errors,
+ 'user' => $user,
+ )));
+ }
+
+ /**
+ * Validate and save
+ *
+ * @access public
+ */
+ public function save()
+ {
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->timetableDay->validateCreation($values);
+
+ if ($valid) {
+
+ if ($this->timetableDay->create($values['user_id'], $values['start'], $values['end'])) {
+ $this->session->flash(t('Time slot created successfully.'));
+ $this->response->redirect($this->helper->url('timetableday', 'index', array('user_id' => $values['user_id'])));
+ }
+ else {
+ $this->session->flashError(t('Unable to save this time slot.'));
+ }
+ }
+
+ $this->index($values, $errors);
+ }
+
+ /**
+ * Confirmation dialag box to remove a row
+ *
+ * @access public
+ */
+ public function confirm()
+ {
+ $user = $this->getUser();
+
+ $this->response->html($this->layout('timetable_day/remove', array(
+ 'slot_id' => $this->request->getIntegerParam('slot_id'),
+ 'user' => $user,
+ )));
+ }
+
+ /**
+ * Remove a row
+ *
+ * @access public
+ */
+ public function remove()
+ {
+ $this->checkCSRFParam();
+ $user = $this->getUser();
+
+ if ($this->timetableDay->remove($this->request->getIntegerParam('slot_id'))) {
+ $this->session->flash(t('Time slot removed successfully.'));
+ }
+ else {
+ $this->session->flash(t('Unable to remove this time slot.'));
+ }
+
+ $this->response->redirect($this->helper->url('timetableday', 'index', array('user_id' => $user['id'])));
+ }
+}
diff --git a/app/Controller/Timetableextra.php b/app/Controller/Timetableextra.php
new file mode 100644
index 00000000..c362bd1a
--- /dev/null
+++ b/app/Controller/Timetableextra.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Controller;
+
+/**
+ * Over-time Timetable controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class Timetableextra extends TimetableOff
+{
+ protected $model = 'timetableExtra';
+ protected $controller_url = 'timetableextra';
+ protected $template_dir = 'timetable_extra';
+}
diff --git a/app/Controller/Timetableoff.php b/app/Controller/Timetableoff.php
new file mode 100644
index 00000000..19a6fab1
--- /dev/null
+++ b/app/Controller/Timetableoff.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace Controller;
+
+/**
+ * Time-off Timetable controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class Timetableoff extends User
+{
+ protected $model = 'timetableOff';
+ protected $controller_url = 'timetableoff';
+ protected $template_dir = 'timetable_off';
+
+ /**
+ * Display timetable for the user
+ *
+ * @access public
+ */
+ public function index(array $values = array(), array $errors = array())
+ {
+ $user = $this->getUser();
+
+ $paginator = $this->paginator
+ ->setUrl($this->controller_url, 'index', array('user_id' => $user['id']))
+ ->setMax(10)
+ ->setOrder('date')
+ ->setDirection('desc')
+ ->setQuery($this->{$this->model}->getUserQuery($user['id']))
+ ->calculate();
+
+ $this->response->html($this->layout($this->template_dir.'/index', array(
+ 'values' => $values + array('user_id' => $user['id']),
+ 'errors' => $errors,
+ 'paginator' => $paginator,
+ 'user' => $user,
+ )));
+ }
+
+ /**
+ * Validate and save
+ *
+ * @access public
+ */
+ public function save()
+ {
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->{$this->model}->validateCreation($values);
+
+ if ($valid) {
+
+ if ($this->{$this->model}->create(
+ $values['user_id'],
+ $values['date'],
+ isset($values['all_day']) && $values['all_day'] == 1,
+ $values['start'],
+ $values['end'],
+ $values['comment'])) {
+
+ $this->session->flash(t('Time slot created successfully.'));
+ $this->response->redirect($this->helper->url($this->controller_url, 'index', array('user_id' => $values['user_id'])));
+ }
+ else {
+ $this->session->flashError(t('Unable to save this time slot.'));
+ }
+ }
+
+ $this->index($values, $errors);
+ }
+
+ /**
+ * Confirmation dialag box to remove a row
+ *
+ * @access public
+ */
+ public function confirm()
+ {
+ $user = $this->getUser();
+
+ $this->response->html($this->layout($this->template_dir.'/remove', array(
+ 'slot_id' => $this->request->getIntegerParam('slot_id'),
+ 'user' => $user,
+ )));
+ }
+
+ /**
+ * Remove a row
+ *
+ * @access public
+ */
+ public function remove()
+ {
+ $this->checkCSRFParam();
+ $user = $this->getUser();
+
+ if ($this->{$this->model}->remove($this->request->getIntegerParam('slot_id'))) {
+ $this->session->flash(t('Time slot removed successfully.'));
+ }
+ else {
+ $this->session->flash(t('Unable to remove this time slot.'));
+ }
+
+ $this->response->redirect($this->helper->url($this->controller_url, 'index', array('user_id' => $user['id'])));
+ }
+}
diff --git a/app/Controller/Timetableweek.php b/app/Controller/Timetableweek.php
new file mode 100644
index 00000000..829f4402
--- /dev/null
+++ b/app/Controller/Timetableweek.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Controller;
+
+/**
+ * Week Timetable controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class Timetableweek extends User
+{
+ /**
+ * Display timetable for the user
+ *
+ * @access public
+ */
+ public function index(array $values = array(), array $errors = array())
+ {
+ $user = $this->getUser();
+
+ if (empty($values)) {
+
+ $day = $this->timetableDay->getByUser($user['id']);
+
+ $values = array(
+ 'user_id' => $user['id'],
+ 'start' => isset($day[0]['start']) ? $day[0]['start'] : null,
+ 'end' => isset($day[0]['end']) ? $day[0]['end'] : null,
+ );
+ }
+
+ $this->response->html($this->layout('timetable_week/index', array(
+ 'timetable' => $this->timetableWeek->getByUser($user['id']),
+ 'values' => $values,
+ 'errors' => $errors,
+ 'user' => $user,
+ )));
+ }
+
+ /**
+ * Validate and save
+ *
+ * @access public
+ */
+ public function save()
+ {
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->timetableWeek->validateCreation($values);
+
+ if ($valid) {
+
+ if ($this->timetableWeek->create($values['user_id'], $values['day'], $values['start'], $values['end'])) {
+ $this->session->flash(t('Time slot created successfully.'));
+ $this->response->redirect($this->helper->url('timetableweek', 'index', array('user_id' => $values['user_id'])));
+ }
+ else {
+ $this->session->flashError(t('Unable to save this time slot.'));
+ }
+ }
+
+ $this->index($values, $errors);
+ }
+
+ /**
+ * Confirmation dialag box to remove a row
+ *
+ * @access public
+ */
+ public function confirm()
+ {
+ $user = $this->getUser();
+
+ $this->response->html($this->layout('timetable_week/remove', array(
+ 'slot_id' => $this->request->getIntegerParam('slot_id'),
+ 'user' => $user,
+ )));
+ }
+
+ /**
+ * Remove a row
+ *
+ * @access public
+ */
+ public function remove()
+ {
+ $this->checkCSRFParam();
+ $user = $this->getUser();
+
+ if ($this->timetableWeek->remove($this->request->getIntegerParam('slot_id'))) {
+ $this->session->flash(t('Time slot removed successfully.'));
+ }
+ else {
+ $this->session->flash(t('Unable to remove this time slot.'));
+ }
+
+ $this->response->redirect($this->helper->url('timetableweek', 'index', array('user_id' => $user['id'])));
+ }
+}
diff --git a/app/Controller/User.php b/app/Controller/User.php
index decdb646..46d0214d 100644
--- a/app/Controller/User.php
+++ b/app/Controller/User.php
@@ -69,12 +69,12 @@ class User extends Base
/**
* Common layout for user views
*
- * @access private
+ * @access protected
* @param string $template Template name
* @param array $params Template parameters
* @return string
*/
- private function layout($template, array $params)
+ protected function layout($template, array $params)
{
$content = $this->template->render($template, $params);
$params['user_content_for_layout'] = $content;
@@ -90,10 +90,10 @@ class User extends Base
/**
* Common method to get the user
*
- * @access private
+ * @access protected
* @return array
*/
- private function getUser()
+ protected function getUser()
{
$user = $this->user->getById($this->request->getIntegerParam('user_id'));
diff --git a/app/Core/Helper.php b/app/Core/Helper.php
index 01ebb08f..78267feb 100644
--- a/app/Core/Helper.php
+++ b/app/Core/Helper.php
@@ -677,4 +677,53 @@ class Helper
array('task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'], 'redirect' => $redirect)
);
}
+
+ /**
+ * Get all hours for day
+ *
+ * @access public
+ * @return array
+ */
+ public function getDayHours()
+ {
+ $values = array();
+
+ foreach (range(0, 23) as $hour) {
+ foreach (array(0, 30) as $minute) {
+ $time = sprintf('%02d:%02d', $hour, $minute);
+ $values[$time] = $time;
+ }
+ }
+
+ return $values;
+ }
+
+ /**
+ * Get all days of a week
+ *
+ * @access public
+ * @return array
+ */
+ public function getWeekDays()
+ {
+ $values = array();
+
+ foreach (range(1, 7) as $day) {
+ $values[$day] = $this->getWeekDay($day);
+ }
+
+ return $values;
+ }
+
+ /**
+ * Get the localized day name from the day number
+ *
+ * @access public
+ * @param integer $day Day number
+ * @return string
+ */
+ public function getWeekDay($day)
+ {
+ return dt('%A', strtotime('next Monday +'.($day - 1).' days'));
+ }
}
diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php
index a378bec6..f31bfa08 100644
--- a/app/Locale/da_DK/translations.php
+++ b/app/Locale/da_DK/translations.php
@@ -1,6 +1,8 @@
<?php
return array(
+ // 'number.decimals_separator' => '',
+ // 'number.thousands_separator' => '',
'None' => 'Ingen',
'edit' => 'rediger',
'Edit' => 'Rediger',
@@ -737,4 +739,38 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
+ // 'No results match:' => '',
+ // 'Remove hourly rate' => '',
+ // 'Do you really want to remove this hourly rate?' => '',
+ // 'Hourly rates' => '',
+ // 'Hourly rate' => '',
+ // 'Currency' => '',
+ // 'Effective date' => '',
+ // 'Add new rate' => '',
+ // 'Rate removed successfully.' => '',
+ // 'Unable to remove this rate.' => '',
+ // 'Unable to save the hourly rate.' => '',
+ // 'Hourly rate created successfully.' => '',
+ // 'Start time' => '',
+ // 'End time' => '',
+ // 'Comment' => '',
+ // 'All day' => '',
+ // 'Day' => '',
+ // 'Manage timetable' => '',
+ // 'Overtime timetable' => '',
+ // 'Time off timetable' => '',
+ // 'Timetable' => '',
+ // 'Work timetable' => '',
+ // 'Week timetable' => '',
+ // 'Day timetable' => '',
+ // 'From' => '',
+ // 'To' => '',
+ // 'Time slot created successfully.' => '',
+ // 'Unable to save this time slot.' => '',
+ // 'Time slot removed successfully.' => '',
+ // 'Unable to remove this time slot.' => '',
+ // 'Do you really want to remove this time slot?' => '',
+ // 'Remove time slot' => '',
+ // 'Add new time slot' => '',
+ // 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
);
diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php
index 5652e3ba..ec1d20ca 100644
--- a/app/Locale/de_DE/translations.php
+++ b/app/Locale/de_DE/translations.php
@@ -1,6 +1,8 @@
<?php
return array(
+ // 'number.decimals_separator' => '',
+ // 'number.thousands_separator' => '',
'None' => 'Keines',
'edit' => 'Bearbeiten',
'Edit' => 'Bearbeiten',
@@ -737,4 +739,38 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
+ // 'No results match:' => '',
+ // 'Remove hourly rate' => '',
+ // 'Do you really want to remove this hourly rate?' => '',
+ // 'Hourly rates' => '',
+ // 'Hourly rate' => '',
+ // 'Currency' => '',
+ // 'Effective date' => '',
+ // 'Add new rate' => '',
+ // 'Rate removed successfully.' => '',
+ // 'Unable to remove this rate.' => '',
+ // 'Unable to save the hourly rate.' => '',
+ // 'Hourly rate created successfully.' => '',
+ // 'Start time' => '',
+ // 'End time' => '',
+ // 'Comment' => '',
+ // 'All day' => '',
+ // 'Day' => '',
+ // 'Manage timetable' => '',
+ // 'Overtime timetable' => '',
+ // 'Time off timetable' => '',
+ // 'Timetable' => '',
+ // 'Work timetable' => '',
+ // 'Week timetable' => '',
+ // 'Day timetable' => '',
+ // 'From' => '',
+ // 'To' => '',
+ // 'Time slot created successfully.' => '',
+ // 'Unable to save this time slot.' => '',
+ // 'Time slot removed successfully.' => '',
+ // 'Unable to remove this time slot.' => '',
+ // 'Do you really want to remove this time slot?' => '',
+ // 'Remove time slot' => '',
+ // 'Add new time slot' => '',
+ // 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
);
diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php
index 15071483..aaacd7f8 100644
--- a/app/Locale/es_ES/translations.php
+++ b/app/Locale/es_ES/translations.php
@@ -1,6 +1,8 @@
<?php
return array(
+ // 'number.decimals_separator' => '',
+ // 'number.thousands_separator' => '',
'None' => 'Ninguno',
'edit' => 'modificar',
'Edit' => 'Modificar',
@@ -737,4 +739,38 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
+ // 'No results match:' => '',
+ // 'Remove hourly rate' => '',
+ // 'Do you really want to remove this hourly rate?' => '',
+ // 'Hourly rates' => '',
+ // 'Hourly rate' => '',
+ // 'Currency' => '',
+ // 'Effective date' => '',
+ // 'Add new rate' => '',
+ // 'Rate removed successfully.' => '',
+ // 'Unable to remove this rate.' => '',
+ // 'Unable to save the hourly rate.' => '',
+ // 'Hourly rate created successfully.' => '',
+ // 'Start time' => '',
+ // 'End time' => '',
+ // 'Comment' => '',
+ // 'All day' => '',
+ // 'Day' => '',
+ // 'Manage timetable' => '',
+ // 'Overtime timetable' => '',
+ // 'Time off timetable' => '',
+ // 'Timetable' => '',
+ // 'Work timetable' => '',
+ // 'Week timetable' => '',
+ // 'Day timetable' => '',
+ // 'From' => '',
+ // 'To' => '',
+ // 'Time slot created successfully.' => '',
+ // 'Unable to save this time slot.' => '',
+ // 'Time slot removed successfully.' => '',
+ // 'Unable to remove this time slot.' => '',
+ // 'Do you really want to remove this time slot?' => '',
+ // 'Remove time slot' => '',
+ // 'Add new time slot' => '',
+ // 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
);
diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php
index e9342e80..bd3a7d68 100644
--- a/app/Locale/fi_FI/translations.php
+++ b/app/Locale/fi_FI/translations.php
@@ -1,6 +1,8 @@
<?php
return array(
+ // 'number.decimals_separator' => '',
+ // 'number.thousands_separator' => '',
'None' => 'Ei mikään',
'edit' => 'muokkaa',
'Edit' => 'Muokkaa',
@@ -737,4 +739,38 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
+ // 'No results match:' => '',
+ // 'Remove hourly rate' => '',
+ // 'Do you really want to remove this hourly rate?' => '',
+ // 'Hourly rates' => '',
+ // 'Hourly rate' => '',
+ // 'Currency' => '',
+ // 'Effective date' => '',
+ // 'Add new rate' => '',
+ // 'Rate removed successfully.' => '',
+ // 'Unable to remove this rate.' => '',
+ // 'Unable to save the hourly rate.' => '',
+ // 'Hourly rate created successfully.' => '',
+ // 'Start time' => '',
+ // 'End time' => '',
+ // 'Comment' => '',
+ // 'All day' => '',
+ // 'Day' => '',
+ // 'Manage timetable' => '',
+ // 'Overtime timetable' => '',
+ // 'Time off timetable' => '',
+ // 'Timetable' => '',
+ // 'Work timetable' => '',
+ // 'Week timetable' => '',
+ // 'Day timetable' => '',
+ // 'From' => '',
+ // 'To' => '',
+ // 'Time slot created successfully.' => '',
+ // 'Unable to save this time slot.' => '',
+ // 'Time slot removed successfully.' => '',
+ // 'Unable to remove this time slot.' => '',
+ // 'Do you really want to remove this time slot?' => '',
+ // 'Remove time slot' => '',
+ // 'Add new time slot' => '',
+ // 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
);
diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php
index 9a1d5f57..df1d8428 100644
--- a/app/Locale/fr_FR/translations.php
+++ b/app/Locale/fr_FR/translations.php
@@ -1,6 +1,8 @@
<?php
return array(
+ 'number.decimals_separator' => ',',
+ 'number.thousands_separator' => ' ',
'None' => 'Aucun',
'edit' => 'modifier',
'Edit' => 'Modifier',
@@ -739,4 +741,38 @@ return array(
'Compact view' => 'Vue compacte',
'Horizontal scrolling' => 'Défilement horizontal',
'Compact/wide view' => 'Basculer entre la vue compacte et étendue',
+ 'No results match:' => 'Aucun résultat :',
+ 'Remove hourly rate' => 'Supprimer un taux horaire',
+ 'Do you really want to remove this hourly rate?' => 'Voulez-vous vraiment supprimer ce taux horaire ?',
+ 'Hourly rates' => 'Taux horaires',
+ 'Hourly rate' => 'Taux horaire',
+ 'Currency' => 'Devise',
+ 'Effective date' => 'Date d\'effet',
+ 'Add new rate' => 'Ajouter un nouveau taux horaire',
+ 'Rate removed successfully.' => 'Taux horaire supprimé avec succès.',
+ 'Unable to remove this rate.' => 'Impossible de supprimer ce taux horaire.',
+ 'Unable to save the hourly rate.' => 'Impossible de sauvegarder ce taux horaire.',
+ 'Hourly rate created successfully.' => 'Taux horaire créé avec succès.',
+ 'Start time' => 'Date de début',
+ 'End time' => 'Date de fin',
+ 'Comment' => 'Commentaire',
+ 'All day' => 'Toute la journée',
+ 'Day' => 'Jour',
+ 'Manage timetable' => 'Gérer les horaires',
+ 'Overtime timetable' => 'Heures supplémentaires',
+ 'Time off timetable' => 'Heures d\'absences',
+ 'Timetable' => 'Horaires',
+ 'Work timetable' => 'Horaires travaillés',
+ 'Week timetable' => 'Horaires de la semaine',
+ 'Day timetable' => 'Horaire d\'une journée',
+ 'From' => 'Depuis',
+ 'To' => 'À',
+ 'Time slot created successfully.' => 'Créneau horaire créé avec succès.',
+ 'Unable to save this time slot.' => 'Impossible de sauvegarder ce créneau horaire.',
+ 'Time slot removed successfully.' => 'Créneau horaire supprimé avec succès.',
+ 'Unable to remove this time slot.' => 'Impossible de supprimer ce créneau horaire.',
+ 'Do you really want to remove this time slot?' => 'Voulez-vous vraiment supprimer ce créneau horaire ?',
+ 'Remove time slot' => 'Supprimer un créneau horaire',
+ 'Add new time slot' => 'Ajouter un créneau horaire',
+ 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Ces horaires sont utilisés lorsque la case « Toute la journée » est cochée pour les heures d\'absences ou supplémentaires programmées.',
);
diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php
index ef057f70..fed84522 100644
--- a/app/Locale/hu_HU/translations.php
+++ b/app/Locale/hu_HU/translations.php
@@ -1,6 +1,8 @@
<?php
return array(
+ // 'number.decimals_separator' => '',
+ // 'number.thousands_separator' => '',
'None' => 'Nincs',
'edit' => 'szerkesztés',
'Edit' => 'Szerkesztés',
@@ -737,4 +739,38 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
+ // 'No results match:' => '',
+ // 'Remove hourly rate' => '',
+ // 'Do you really want to remove this hourly rate?' => '',
+ // 'Hourly rates' => '',
+ // 'Hourly rate' => '',
+ // 'Currency' => '',
+ // 'Effective date' => '',
+ // 'Add new rate' => '',
+ // 'Rate removed successfully.' => '',
+ // 'Unable to remove this rate.' => '',
+ // 'Unable to save the hourly rate.' => '',
+ // 'Hourly rate created successfully.' => '',
+ // 'Start time' => '',
+ // 'End time' => '',
+ // 'Comment' => '',
+ // 'All day' => '',
+ // 'Day' => '',
+ // 'Manage timetable' => '',
+ // 'Overtime timetable' => '',
+ // 'Time off timetable' => '',
+ // 'Timetable' => '',
+ // 'Work timetable' => '',
+ // 'Week timetable' => '',
+ // 'Day timetable' => '',
+ // 'From' => '',
+ // 'To' => '',
+ // 'Time slot created successfully.' => '',
+ // 'Unable to save this time slot.' => '',
+ // 'Time slot removed successfully.' => '',
+ // 'Unable to remove this time slot.' => '',
+ // 'Do you really want to remove this time slot?' => '',
+ // 'Remove time slot' => '',
+ // 'Add new time slot' => '',
+ // 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
);
diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php
index 3154ad17..d6c57371 100644
--- a/app/Locale/it_IT/translations.php
+++ b/app/Locale/it_IT/translations.php
@@ -1,6 +1,8 @@
<?php
return array(
+ // 'number.decimals_separator' => '',
+ // 'number.thousands_separator' => '',
'None' => 'Nessuno',
'edit' => 'modificare',
'Edit' => 'Modificare',
@@ -737,4 +739,38 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
+ // 'No results match:' => '',
+ // 'Remove hourly rate' => '',
+ // 'Do you really want to remove this hourly rate?' => '',
+ // 'Hourly rates' => '',
+ // 'Hourly rate' => '',
+ // 'Currency' => '',
+ // 'Effective date' => '',
+ // 'Add new rate' => '',
+ // 'Rate removed successfully.' => '',
+ // 'Unable to remove this rate.' => '',
+ // 'Unable to save the hourly rate.' => '',
+ // 'Hourly rate created successfully.' => '',
+ // 'Start time' => '',
+ // 'End time' => '',
+ // 'Comment' => '',
+ // 'All day' => '',
+ // 'Day' => '',
+ // 'Manage timetable' => '',
+ // 'Overtime timetable' => '',
+ // 'Time off timetable' => '',
+ // 'Timetable' => '',
+ // 'Work timetable' => '',
+ // 'Week timetable' => '',
+ // 'Day timetable' => '',
+ // 'From' => '',
+ // 'To' => '',
+ // 'Time slot created successfully.' => '',
+ // 'Unable to save this time slot.' => '',
+ // 'Time slot removed successfully.' => '',
+ // 'Unable to remove this time slot.' => '',
+ // 'Do you really want to remove this time slot?' => '',
+ // 'Remove time slot' => '',
+ // 'Add new time slot' => '',
+ // 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
);
diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php
index d55227f2..dd727d0e 100644
--- a/app/Locale/ja_JP/translations.php
+++ b/app/Locale/ja_JP/translations.php
@@ -1,6 +1,8 @@
<?php
return array(
+ // 'number.decimals_separator' => '',
+ // 'number.thousands_separator' => '',
'None' => 'なし',
'edit' => '変更',
'Edit' => '変更',
@@ -737,4 +739,38 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
+ // 'No results match:' => '',
+ // 'Remove hourly rate' => '',
+ // 'Do you really want to remove this hourly rate?' => '',
+ // 'Hourly rates' => '',
+ // 'Hourly rate' => '',
+ // 'Currency' => '',
+ // 'Effective date' => '',
+ // 'Add new rate' => '',
+ // 'Rate removed successfully.' => '',
+ // 'Unable to remove this rate.' => '',
+ // 'Unable to save the hourly rate.' => '',
+ // 'Hourly rate created successfully.' => '',
+ // 'Start time' => '',
+ // 'End time' => '',
+ // 'Comment' => '',
+ // 'All day' => '',
+ // 'Day' => '',
+ // 'Manage timetable' => '',
+ // 'Overtime timetable' => '',
+ // 'Time off timetable' => '',
+ // 'Timetable' => '',
+ // 'Work timetable' => '',
+ // 'Week timetable' => '',
+ // 'Day timetable' => '',
+ // 'From' => '',
+ // 'To' => '',
+ // 'Time slot created successfully.' => '',
+ // 'Unable to save this time slot.' => '',
+ // 'Time slot removed successfully.' => '',
+ // 'Unable to remove this time slot.' => '',
+ // 'Do you really want to remove this time slot?' => '',
+ // 'Remove time slot' => '',
+ // 'Add new time slot' => '',
+ // 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
);
diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php
index 6eac4005..deb5a280 100644
--- a/app/Locale/pl_PL/translations.php
+++ b/app/Locale/pl_PL/translations.php
@@ -1,6 +1,8 @@
<?php
return array(
+ // 'number.decimals_separator' => '',
+ // 'number.thousands_separator' => '',
'None' => 'Brak',
'edit' => 'edytuj',
'Edit' => 'Edytuj',
@@ -737,4 +739,38 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
+ // 'No results match:' => '',
+ // 'Remove hourly rate' => '',
+ // 'Do you really want to remove this hourly rate?' => '',
+ // 'Hourly rates' => '',
+ // 'Hourly rate' => '',
+ // 'Currency' => '',
+ // 'Effective date' => '',
+ // 'Add new rate' => '',
+ // 'Rate removed successfully.' => '',
+ // 'Unable to remove this rate.' => '',
+ // 'Unable to save the hourly rate.' => '',
+ // 'Hourly rate created successfully.' => '',
+ // 'Start time' => '',
+ // 'End time' => '',
+ // 'Comment' => '',
+ // 'All day' => '',
+ // 'Day' => '',
+ // 'Manage timetable' => '',
+ // 'Overtime timetable' => '',
+ // 'Time off timetable' => '',
+ // 'Timetable' => '',
+ // 'Work timetable' => '',
+ // 'Week timetable' => '',
+ // 'Day timetable' => '',
+ // 'From' => '',
+ // 'To' => '',
+ // 'Time slot created successfully.' => '',
+ // 'Unable to save this time slot.' => '',
+ // 'Time slot removed successfully.' => '',
+ // 'Unable to remove this time slot.' => '',
+ // 'Do you really want to remove this time slot?' => '',
+ // 'Remove time slot' => '',
+ // 'Add new time slot' => '',
+ // 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
);
diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php
index 1791f74f..f81bc675 100644
--- a/app/Locale/pt_BR/translations.php
+++ b/app/Locale/pt_BR/translations.php
@@ -1,6 +1,8 @@
<?php
return array(
+ // 'number.decimals_separator' => '',
+ // 'number.thousands_separator' => '',
'None' => 'Nenhum',
'edit' => 'editar',
'Edit' => 'Editar',
@@ -737,4 +739,38 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
+ // 'No results match:' => '',
+ // 'Remove hourly rate' => '',
+ // 'Do you really want to remove this hourly rate?' => '',
+ // 'Hourly rates' => '',
+ // 'Hourly rate' => '',
+ // 'Currency' => '',
+ // 'Effective date' => '',
+ // 'Add new rate' => '',
+ // 'Rate removed successfully.' => '',
+ // 'Unable to remove this rate.' => '',
+ // 'Unable to save the hourly rate.' => '',
+ // 'Hourly rate created successfully.' => '',
+ // 'Start time' => '',
+ // 'End time' => '',
+ // 'Comment' => '',
+ // 'All day' => '',
+ // 'Day' => '',
+ // 'Manage timetable' => '',
+ // 'Overtime timetable' => '',
+ // 'Time off timetable' => '',
+ // 'Timetable' => '',
+ // 'Work timetable' => '',
+ // 'Week timetable' => '',
+ // 'Day timetable' => '',
+ // 'From' => '',
+ // 'To' => '',
+ // 'Time slot created successfully.' => '',
+ // 'Unable to save this time slot.' => '',
+ // 'Time slot removed successfully.' => '',
+ // 'Unable to remove this time slot.' => '',
+ // 'Do you really want to remove this time slot?' => '',
+ // 'Remove time slot' => '',
+ // 'Add new time slot' => '',
+ // 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
);
diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php
index 2fdf6e50..a4caa545 100644
--- a/app/Locale/ru_RU/translations.php
+++ b/app/Locale/ru_RU/translations.php
@@ -1,6 +1,8 @@
<?php
return array(
+ // 'number.decimals_separator' => '',
+ // 'number.thousands_separator' => '',
'None' => 'Отсутствует',
'edit' => 'изменить',
'Edit' => 'Изменить',
@@ -737,4 +739,38 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
+ // 'No results match:' => '',
+ // 'Remove hourly rate' => '',
+ // 'Do you really want to remove this hourly rate?' => '',
+ // 'Hourly rates' => '',
+ // 'Hourly rate' => '',
+ // 'Currency' => '',
+ // 'Effective date' => '',
+ // 'Add new rate' => '',
+ // 'Rate removed successfully.' => '',
+ // 'Unable to remove this rate.' => '',
+ // 'Unable to save the hourly rate.' => '',
+ // 'Hourly rate created successfully.' => '',
+ // 'Start time' => '',
+ // 'End time' => '',
+ // 'Comment' => '',
+ // 'All day' => '',
+ // 'Day' => '',
+ // 'Manage timetable' => '',
+ // 'Overtime timetable' => '',
+ // 'Time off timetable' => '',
+ // 'Timetable' => '',
+ // 'Work timetable' => '',
+ // 'Week timetable' => '',
+ // 'Day timetable' => '',
+ // 'From' => '',
+ // 'To' => '',
+ // 'Time slot created successfully.' => '',
+ // 'Unable to save this time slot.' => '',
+ // 'Time slot removed successfully.' => '',
+ // 'Unable to remove this time slot.' => '',
+ // 'Do you really want to remove this time slot?' => '',
+ // 'Remove time slot' => '',
+ // 'Add new time slot' => '',
+ // 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
);
diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php
index bfc8d714..47ac8b26 100644
--- a/app/Locale/sv_SE/translations.php
+++ b/app/Locale/sv_SE/translations.php
@@ -1,6 +1,8 @@
<?php
return array(
+ // 'number.decimals_separator' => '',
+ // 'number.thousands_separator' => '',
'None' => 'Ingen',
'edit' => 'redigera',
'Edit' => 'Redigera',
@@ -737,4 +739,38 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
+ // 'No results match:' => '',
+ // 'Remove hourly rate' => '',
+ // 'Do you really want to remove this hourly rate?' => '',
+ // 'Hourly rates' => '',
+ // 'Hourly rate' => '',
+ // 'Currency' => '',
+ // 'Effective date' => '',
+ // 'Add new rate' => '',
+ // 'Rate removed successfully.' => '',
+ // 'Unable to remove this rate.' => '',
+ // 'Unable to save the hourly rate.' => '',
+ // 'Hourly rate created successfully.' => '',
+ // 'Start time' => '',
+ // 'End time' => '',
+ // 'Comment' => '',
+ // 'All day' => '',
+ // 'Day' => '',
+ // 'Manage timetable' => '',
+ // 'Overtime timetable' => '',
+ // 'Time off timetable' => '',
+ // 'Timetable' => '',
+ // 'Work timetable' => '',
+ // 'Week timetable' => '',
+ // 'Day timetable' => '',
+ // 'From' => '',
+ // 'To' => '',
+ // 'Time slot created successfully.' => '',
+ // 'Unable to save this time slot.' => '',
+ // 'Time slot removed successfully.' => '',
+ // 'Unable to remove this time slot.' => '',
+ // 'Do you really want to remove this time slot?' => '',
+ // 'Remove time slot' => '',
+ // 'Add new time slot' => '',
+ // 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
);
diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php
index 04e8dcf8..43395db5 100644
--- a/app/Locale/th_TH/translations.php
+++ b/app/Locale/th_TH/translations.php
@@ -1,6 +1,8 @@
<?php
return array(
+ // 'number.decimals_separator' => '',
+ // 'number.thousands_separator' => '',
'None' => 'ไม่มี',
'edit' => 'แก้ไข',
'Edit' => 'แก้ไข',
@@ -737,4 +739,38 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
+ // 'No results match:' => '',
+ // 'Remove hourly rate' => '',
+ // 'Do you really want to remove this hourly rate?' => '',
+ // 'Hourly rates' => '',
+ // 'Hourly rate' => '',
+ // 'Currency' => '',
+ // 'Effective date' => '',
+ // 'Add new rate' => '',
+ // 'Rate removed successfully.' => '',
+ // 'Unable to remove this rate.' => '',
+ // 'Unable to save the hourly rate.' => '',
+ // 'Hourly rate created successfully.' => '',
+ // 'Start time' => '',
+ // 'End time' => '',
+ // 'Comment' => '',
+ // 'All day' => '',
+ // 'Day' => '',
+ // 'Manage timetable' => '',
+ // 'Overtime timetable' => '',
+ // 'Time off timetable' => '',
+ // 'Timetable' => '',
+ // 'Work timetable' => '',
+ // 'Week timetable' => '',
+ // 'Day timetable' => '',
+ // 'From' => '',
+ // 'To' => '',
+ // 'Time slot created successfully.' => '',
+ // 'Unable to save this time slot.' => '',
+ // 'Time slot removed successfully.' => '',
+ // 'Unable to remove this time slot.' => '',
+ // 'Do you really want to remove this time slot?' => '',
+ // 'Remove time slot' => '',
+ // 'Add new time slot' => '',
+ // 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
);
diff --git a/app/Locale/tr_TR/translations.php b/app/Locale/tr_TR/translations.php
new file mode 100644
index 00000000..e2d4e72c
--- /dev/null
+++ b/app/Locale/tr_TR/translations.php
@@ -0,0 +1,776 @@
+<?php
+
+return array(
+ // 'number.decimals_separator' => '',
+ // 'number.thousands_separator' => '',
+ 'None' => 'Hiçbiri',
+ 'edit' => 'düzenle',
+ 'Edit' => 'Düzenle',
+ 'remove' => 'sil',
+ 'Remove' => 'Sil',
+ 'Update' => 'Güncelle',
+ 'Yes' => 'Evet',
+ 'No' => 'Hayır',
+ 'cancel' => 'İptal',
+ 'or' => 'veya',
+ 'Yellow' => 'Sarı',
+ 'Blue' => 'Mavi',
+ 'Green' => 'Yeşil',
+ 'Purple' => 'Mor',
+ 'Red' => 'Kırmızı',
+ 'Orange' => 'Turuncu',
+ 'Grey' => 'Gri',
+ 'Save' => 'Kaydet',
+ 'Login' => 'Giriş',
+ 'Official website:' => 'Resmi internet sitesi:',
+ 'Unassigned' => 'Atanmamış',
+ 'View this task' => 'Bu görevi görüntüle',
+ 'Remove user' => 'Kullanıcıyı kaldır',
+ 'Do you really want to remove this user: "%s"?' => 'Bu kullanıcıyı gerçekten silmek istiyor musunuz: "%s"?',
+ 'New user' => 'Yeni kullanıcı',
+ 'All users' => 'Tüm kullanıcılar',
+ 'Username' => 'Kullanıcı adı',
+ 'Password' => 'Şifre',
+ // 'Default project' => '',
+ 'Administrator' => 'Yönetici',
+ 'Sign in' => 'Giriş yap',
+ 'Users' => 'Kullanıcılar',
+ 'No user' => 'Kullanıcı yok',
+ 'Forbidden' => 'Yasak',
+ 'Access Forbidden' => 'Erişim yasak',
+ 'Only administrators can access to this page.' => 'Bu sayfaya yalnızca yöneticiler erişebilir.',
+ 'Edit user' => 'Kullanıcıyı düzenle',
+ 'Logout' => 'Çıkış yap',
+ 'Bad username or password' => 'Hatalı kullanıcı adı veya şifre',
+ 'users' => 'kullanıcılar',
+ 'projects' => 'projeler',
+ 'Edit project' => 'Projeyi düzenle',
+ 'Name' => 'İsim',
+ 'Activated' => 'Aktif',
+ 'Projects' => 'Projeler',
+ 'No project' => 'Proje yok',
+ 'Project' => 'Proje',
+ 'Status' => 'Durum',
+ 'Tasks' => 'Görevler',
+ 'Board' => 'Tablo',
+ 'Actions' => 'İşlemler',
+ 'Inactive' => 'Aktif değil',
+ 'Active' => 'Aktif',
+ 'Column %d' => 'Sütun %d',
+ 'Add this column' => 'Bu sütunu ekle',
+ '%d tasks on the board' => '%d görev bu tabloda',
+ '%d tasks in total' => '%d görev toplam',
+ 'Unable to update this board.' => 'Bu tablo güncellenemiyor.',
+ 'Edit board' => 'Tabloyu düzenle',
+ 'Disable' => 'Devre dışı bırak',
+ 'Enable' => 'Etkinleştir',
+ 'New project' => 'Yeni proje',
+ 'Do you really want to remove this project: "%s"?' => 'Bu projeyi gerçekten silmek istiyor musunuz: "%s"?',
+ 'Remove project' => 'Projeyi sil',
+ 'Boards' => 'Tablolar',
+ 'Edit the board for "%s"' => 'Tabloyu "%s" için güncelle',
+ 'All projects' => 'Tüm projeler',
+ 'Change columns' => 'Sütunları değiştir',
+ 'Add a new column' => 'Yeni sütun ekle',
+ 'Title' => 'Başlık',
+ 'Add Column' => 'Sütun ekle',
+ 'Project "%s"' => 'Proje "%s"',
+ 'Nobody assigned' => 'Kullanıcı atanmamış',
+ 'Assigned to %s' => '%s kullanıcısına atanmış',
+ 'Remove a column' => 'Bir sütunu sil',
+ 'Remove a column from a board' => 'Tablodan bir sütunu sil',
+ 'Unable to remove this column.' => 'Bu sütun silinemiyor.',
+ 'Do you really want to remove this column: "%s"?' => 'Bu sütunu gerçekten silmek istiyor musunuz: "%s"?',
+ 'This action will REMOVE ALL TASKS associated to this column!' => 'Bu komut sütun içindeki TÜM GÖREVLERİ silecek!',
+ 'Settings' => 'Ayarlar',
+ 'Application settings' => 'Uygulama ayarları',
+ 'Language' => 'Dil',
+ // 'Webhook token:' => '',
+ 'API token:' => 'API Token:',
+ 'More information' => 'Daha fazla bilgi',
+ 'Database size:' => 'Veritabanı boyutu :',
+ 'Download the database' => 'Veritabanını indir',
+ 'Optimize the database' => 'Veritabanını optimize et',
+ '(VACUUM command)' => '(VACUUM komutu)',
+ '(Gzip compressed Sqlite file)' => '(Gzip ile sıkıştırılmış Sqlite dosyası)',
+ 'User settings' => 'Kullanıcı ayarları',
+ 'My default project:' => 'Benim varsayılan projem:',
+ 'Close a task' => 'Bir görevi kapat',
+ 'Do you really want to close this task: "%s"?' => 'Bu görevi gerçekten kapatmak istiyor musunuz: "%s"?',
+ 'Edit a task' => 'Bir görevi düzenle',
+ 'Column' => 'Sütun',
+ 'Color' => 'Renk',
+ 'Assignee' => 'Atanan',
+ 'Create another task' => 'Başka bir görev oluştur',
+ 'New task' => 'Nouvelle tâche',
+ 'Open a task' => 'Bir görevi aç',
+ 'Do you really want to open this task: "%s"?' => 'Bu görevi gerçekten açmak istiyor musunuz: "%s"?',
+ 'Back to the board' => 'Tabloya dön',
+ // 'Created on %B %e, %Y at %k:%M %p' => '',
+ 'There is nobody assigned' => 'Kimse atanmamış',
+ 'Column on the board:' => 'Tablodaki sütun:',
+ 'Status is open' => 'Açık durumda',
+ 'Status is closed' => 'Kapalı durumda',
+ 'Close this task' => 'Görevi kapat',
+ 'Open this task' => 'Görevi aç',
+ 'There is no description.' => 'Açıklama yok.',
+ 'Add a new task' => 'Yeni görev ekle',
+ 'The username is required' => 'Kullanıcı adı gerekli',
+ 'The maximum length is %d characters' => 'Maksimum uzunluk %d karakterdir',
+ 'The minimum length is %d characters' => 'Minimum uzunluk %d karakterdir',
+ 'The password is required' => 'Şifre gerekli',
+ 'This value must be an integer' => 'Bu değer bir rakam olmak zorunda',
+ 'The username must be unique' => 'Kullanıcı adı daha önceden var',
+ 'The username must be alphanumeric' => 'Kullanıcı adı alfanumerik olmalı (geçersiz karakter var)',
+ 'The user id is required' => 'Kullanıcı kodu gerekli',
+ 'Passwords don\'t match' => 'Şifreler uyuşmuyor',
+ 'The confirmation is required' => 'Onay gerekli',
+ 'The column is required' => 'Sütun gerekli',
+ 'The project is required' => 'Proje gerekli',
+ 'The color is required' => 'Renk gerekli',
+ 'The id is required' => 'Kod gerekli',
+ 'The project id is required' => 'Proje kodu gerekli',
+ 'The project name is required' => 'Proje adı gerekli',
+ 'This project must be unique' => 'Bu projenin tekil olması gerekli',
+ 'The title is required' => 'Başlık gerekli',
+ 'The language is required' => 'Dil seçimi gerekli',
+ 'There is no active project, the first step is to create a new project.' => 'Aktif bir proje yok. İlk aşama yeni bir proje oluşturmak olmalı.',
+ 'Settings saved successfully.' => 'Ayarlar başarıyla kaydedildi.',
+ 'Unable to save your settings.' => 'Ayarlarınız kaydedilemedi.',
+ 'Database optimization done.' => 'Veritabanı optimizasyonu tamamlandı.',
+ 'Your project have been created successfully.' => 'Projeniz başarıyla oluşturuldu.',
+ 'Unable to create your project.' => 'Proje oluşturulamadı.',
+ 'Project updated successfully.' => 'Proje başarıyla güncellendi.',
+ 'Unable to update this project.' => 'Bu proje güncellenemedi.',
+ 'Unable to remove this project.' => 'Bu proje silinemedi.',
+ 'Project removed successfully.' => 'Proje başarıyla silindi.',
+ 'Project activated successfully.' => 'Proje başarıyla aktive edildi.',
+ 'Unable to activate this project.' => 'Bu proje aktive edilemedi.',
+ 'Project disabled successfully.' => 'Proje devre dışı bırakıldı.',
+ 'Unable to disable this project.' => 'Bu proje devre dışı bırakılamadı.',
+ 'Unable to open this task.' => 'Bu görev açılamıyor.',
+ 'Task opened successfully.' => 'Görev başarıyla açıldı.',
+ 'Unable to close this task.' => 'Bu görev kapatılamıyor.',
+ 'Task closed successfully.' => 'Görev başarıyla kapatıldı.',
+ 'Unable to update your task.' => 'Görev güncellenemiyor.',
+ 'Task updated successfully.' => 'Görev başarıyla güncellendi.',
+ 'Unable to create your task.' => 'Görev oluşturulamadı.',
+ 'Task created successfully.' => 'Görev başarıyla oluşturuldu.',
+ 'User created successfully.' => 'Kullanıcı başarıyla oluşturuldu',
+ 'Unable to create your user.' => 'Kullanıcı oluşturulamıyor.',
+ 'User updated successfully.' => 'Kullanıcı başarıyla güncellendi.',
+ 'Unable to update your user.' => 'Kullanıcı güncellenemiyor.',
+ 'User removed successfully.' => 'Kullanıcı silindi.',
+ 'Unable to remove this user.' => 'Bu kullanıcı silinemiyor.',
+ 'Board updated successfully.' => 'Tablo başarıyla güncellendi.',
+ 'Ready' => 'Hazır',
+ 'Backlog' => 'Bekleme listesi',
+ 'Work in progress' => 'İşlemde',
+ 'Done' => 'Tamamlandı',
+ 'Application version:' => 'Uygulama versiyonu:',
+ // 'Completed on %B %e, %Y at %k:%M %p' => '',
+ // '%B %e, %Y at %k:%M %p' => '',
+ 'Date created' => 'Oluşturulma tarihi',
+ 'Date completed' => 'Tamamlanma tarihi',
+ 'Id' => 'Kod',
+ 'No task' => 'Görev yok',
+ 'Completed tasks' => 'Tamamlanan görevler',
+ 'List of projects' => 'Proje listesi',
+ 'Completed tasks for "%s"' => '"%s" için tamamlanan görevler',
+ '%d closed tasks' => '%d kapatılmış görevler',
+ // 'No task for this project' => '',
+ 'Public link' => 'Dışa açık link',
+ 'There is no column in your project!' => 'Projenizde hiç sütun yok',
+ 'Change assignee' => 'Atanmış Kullanıcıyı değiştir',
+ 'Change assignee for the task "%s"' => '"%s" görevi için atanmış kullanıcıyı değiştir',
+ 'Timezone' => 'Saat dilimi',
+ // 'Sorry, I didn\'t find this information in my database!' => '',
+ 'Page not found' => 'Sayfa bulunamadı',
+ 'Complexity' => 'Zorluk seviyesi',
+ 'limit' => 'limit',
+ 'Task limit' => 'Görev limiti',
+ 'Task count' => 'Görev sayısı',
+ 'This value must be greater than %d' => 'Bu değer %d den büyük olmalı',
+ 'Edit project access list' => 'Proje erişim listesini düzenle',
+ 'Edit users access' => 'Kullanıcı erişim haklarını düzenle',
+ 'Allow this user' => 'Bu kullanıcıya izin ver',
+ 'Only those users have access to this project:' => 'Bu projeye yalnızca şu kullanıcılar erişebilir:',
+ 'Don\'t forget that administrators have access to everything.' => 'Dikkat: Yöneticilerin herşeye erişimi olduğunu unutmayın!',
+ 'Revoke' => 'Iptal et',
+ 'List of authorized users' => 'Yetkili kullanıcıların listesi',
+ 'User' => 'Kullanıcı',
+ 'Nobody have access to this project.' => 'Bu projeye kimsenin erişimi yok.',
+ 'You are not allowed to access to this project.' => 'Bu projeye giriş yetkiniz yok.',
+ 'Comments' => 'Yorumlar',
+ 'Post comment' => 'Yorum ekle',
+ 'Write your text in Markdown' => 'Yazınızı Markdown ile yazın',
+ 'Leave a comment' => 'Bir yorum ekle',
+ 'Comment is required' => 'Yorum gerekli',
+ 'Leave a description' => 'Açıklama ekleyin',
+ 'Comment added successfully.' => 'Yorum eklendi',
+ 'Unable to create your comment.' => 'Yorumunuz oluşturulamadı',
+ 'The description is required' => 'Açıklama gerekli',
+ 'Edit this task' => 'Bu görevi değiştir',
+ 'Due Date' => 'Termin',
+ 'Invalid date' => 'Geçersiz tarihi',
+ // 'Must be done before %B %e, %Y' => '',
+ '%B %e, %Y' => '%d %B %Y',
+ '%b %e, %Y' => '%d/%m/%Y',
+ 'Automatic actions' => 'Otomatik işlemler',
+ 'Your automatic action have been created successfully.' => 'Otomatik işlem başarıyla oluşturuldu',
+ 'Unable to create your automatic action.' => 'Otomatik işleminiz oluşturulamadı',
+ 'Remove an action' => 'Bir işlemi sil',
+ 'Unable to remove this action.' => 'Bu işlem silinemedi',
+ 'Action removed successfully.' => 'İşlem başarıyla silindi',
+ 'Automatic actions for the project "%s"' => '"%s" projesi için otomatik işlemler',
+ 'Defined actions' => 'Tanımlanan işlemler',
+ 'Add an action' => 'İşlem ekle',
+ 'Event name' => 'Durum adı',
+ 'Action name' => 'İşlem adı',
+ 'Action parameters' => 'İşlem parametreleri',
+ 'Action' => 'İşlem',
+ 'Event' => 'Durum',
+ 'When the selected event occurs execute the corresponding action.' => 'Seçilen durum oluştuğunda ilgili eylemi gerçekleştir.',
+ 'Next step' => 'Sonraki adım',
+ 'Define action parameters' => 'İşlem parametrelerini düzenle',
+ 'Save this action' => 'Bu işlemi kaydet',
+ 'Do you really want to remove this action: "%s"?' => 'Bu işlemi silmek istediğinize emin misiniz: "%s"?',
+ 'Remove an automatic action' => 'Bir otomatik işlemi sil',
+ 'Close the task' => 'Görevi kapat',
+ 'Assign the task to a specific user' => 'Görevi bir kullanıcıya ata',
+ 'Assign the task to the person who does the action' => 'Görevi, işlemi gerçekleştiren kullanıcıya ata',
+ 'Duplicate the task to another project' => 'Görevi bir başka projeye kopyala',
+ 'Move a task to another column' => 'Bir görevi başka bir sütuna taşı',
+ 'Move a task to another position in the same column' => 'Bir görevin aynı sütunda yerini değiştir',
+ 'Task modification' => 'Görev düzenleme',
+ 'Task creation' => 'Görev oluşturma',
+ 'Open a closed task' => 'Kapalı bir görevi aç',
+ 'Closing a task' => 'Bir görev kapatılıyor',
+ 'Assign a color to a specific user' => 'Bir kullanıcıya renk tanımla',
+ 'Column title' => 'Sütun başlığı',
+ 'Position' => 'Pozisyon',
+ 'Move Up' => 'Yukarı taşı',
+ 'Move Down' => 'Aşağı taşı',
+ 'Duplicate to another project' => 'Başka bir projeye kopyala',
+ 'Duplicate' => 'Kopya oluştur',
+ 'link' => 'link',
+ 'Update this comment' => 'Bu yorumu güncelle',
+ 'Comment updated successfully.' => 'Yorum güncellendi.',
+ 'Unable to update your comment.' => 'Yorum güncellenemedi.',
+ 'Remove a comment' => 'Bir yorumu sil',
+ 'Comment removed successfully.' => 'Yorum silindi.',
+ 'Unable to remove this comment.' => 'Bu yorum silinemiyor.',
+ 'Do you really want to remove this comment?' => 'Bu yorumu silmek istediğinize emin misiniz?',
+ 'Only administrators or the creator of the comment can access to this page.' => 'Bu sayfaya yalnızca yorum sahibi ve yöneticiler erişebilir.',
+ 'Details' => 'Detaylar',
+ 'Current password for the user "%s"' => 'Kullanıcı için mevcut şifre "%s"',
+ 'The current password is required' => 'Mevcut şifre gerekli',
+ 'Wrong password' => 'Yanlış Şifre',
+ 'Reset all tokens' => 'Tüm fişleri sıfırla',
+ 'All tokens have been regenerated.' => 'Tüm fişler yeniden oluşturuldu.',
+ 'Unknown' => 'Bilinmeyen',
+ 'Last logins' => 'Son kullanıcı girişleri',
+ 'Login date' => 'Giriş tarihi',
+ 'Authentication method' => 'Doğrulama yöntemi',
+ 'IP address' => 'IP adresi',
+ 'User agent' => 'Kullanıcı sistemi',
+ 'Persistent connections' => 'Kalıcı bağlantılar',
+ // 'No session.' => '',
+ 'Expiration date' => 'Geçerlilik sonu',
+ 'Remember Me' => 'Beni hatırla',
+ 'Creation date' => 'Oluşturulma tarihi',
+ 'Filter by user' => 'Kullanıcıya göre filtrele',
+ 'Filter by due date' => 'Termine göre filtrele',
+ 'Everybody' => 'Herkes',
+ 'Open' => 'Açık',
+ 'Closed' => 'Kapalı',
+ 'Search' => 'Ara',
+ 'Nothing found.' => 'Hiçbir şey bulunamadı',
+ 'Search in the project "%s"' => '"%s" Projesinde ara',
+ 'Due date' => 'Termin',
+ 'Others formats accepted: %s and %s' => 'Diğer kabul edilen formatlar: %s ve %s',
+ 'Description' => 'Açıklama',
+ '%d comments' => '%d yorumlar',
+ '%d comment' => '%d yorum',
+ 'Email address invalid' => 'E-Posta adresi geçersiz',
+ 'Your Google Account is not linked anymore to your profile.' => 'Google hesabınız artık profilinize bağlı değil',
+ 'Unable to unlink your Google Account.' => 'Google hesabınızla bağ koparılamadı',
+ 'Google authentication failed' => 'Google hesap doğrulaması başarısız',
+ 'Unable to link your Google Account.' => 'Google hesabınızla bağ oluşturulamadı',
+ 'Your Google Account is linked to your profile successfully.' => 'Google hesabınız profilinize başarıyla bağlandı',
+ 'Email' => 'E-Posta',
+ 'Link my Google Account' => 'Google hesabımla bağ oluştur',
+ 'Unlink my Google Account' => 'Google hesabımla bağı kaldır',
+ 'Login with my Google Account' => 'Google hesabımla giriş yap',
+ 'Project not found.' => 'Proje bulunamadı',
+ 'Task #%d' => 'Görev #%d',
+ 'Task removed successfully.' => 'Görev silindi',
+ 'Unable to remove this task.' => 'Görev silinemiyor',
+ 'Remove a task' => 'Bir görevi sil',
+ 'Do you really want to remove this task: "%s"?' => 'Bu görevi silmek istediğinize emin misiniz: "%s"?',
+ 'Assign automatically a color based on a category' => 'Kategoriye göre otomatik renk ata',
+ 'Assign automatically a category based on a color' => 'Rengine göre otomatik kategori ata',
+ 'Task creation or modification' => 'Görev oluşturma veya değiştirme',
+ 'Category' => 'Kategori',
+ 'Category:' => 'Kategori:',
+ 'Categories' => 'Kategoriler',
+ 'Category not found.' => 'Kategori bulunamadı',
+ 'Your category have been created successfully.' => 'Kategori oluşturuldu',
+ 'Unable to create your category.' => 'Kategori oluşturulamadı',
+ 'Your category have been updated successfully.' => 'Kategori başarıyla güncellendi',
+ 'Unable to update your category.' => 'Kategori güncellenemedi',
+ 'Remove a category' => 'Bir kategoriyi sil',
+ 'Category removed successfully.' => 'Kategori silindi',
+ 'Unable to remove this category.' => 'Bu kategori silinemedi',
+ 'Category modification for the project "%s"' => '"%s" projesi için kategori değiştirme',
+ 'Category Name' => 'Kategori adı',
+ 'Categories for the project "%s"' => '"%s" Projesi için kategoriler',
+ 'Add a new category' => 'Yeni kategori ekle',
+ 'Do you really want to remove this category: "%s"?' => 'Bu kategoriyi silmek istediğinize emin misiniz: "%s"?',
+ 'Filter by category' => 'Kategoriye göre filtrele',
+ 'All categories' => 'Tüm kategoriler',
+ 'No category' => 'Kategori Yok',
+ 'The name is required' => 'İsim gerekli',
+ 'Remove a file' => 'Dosya sil',
+ 'Unable to remove this file.' => 'Dosya silinemedi',
+ 'File removed successfully.' => 'Dosya silindi',
+ 'Attach a document' => 'Dosya ekle',
+ 'Do you really want to remove this file: "%s"?' => 'Bu dosyayı silmek istediğinize emin misiniz: "%s"?',
+ 'open' => 'aç',
+ 'Attachments' => 'Ekler',
+ 'Edit the task' => 'Görevi değiştir',
+ 'Edit the description' => 'Açıklamayı değiştir',
+ 'Add a comment' => 'Yorum ekle',
+ 'Edit a comment' => 'Yorum değiştir',
+ 'Summary' => 'Özet',
+ 'Time tracking' => 'Zaman takibi',
+ 'Estimate:' => 'Tahmini:',
+ 'Spent:' => 'Harcanan:',
+ 'Do you really want to remove this sub-task?' => 'Bu alt görevi silmek istediğinize emin misiniz',
+ 'Remaining:' => 'Kalan',
+ 'hours' => 'saat',
+ 'spent' => 'harcanan',
+ 'estimated' => 'tahmini',
+ 'Sub-Tasks' => 'Alt Görev',
+ 'Add a sub-task' => 'Alt görev ekle',
+ // 'Original estimate' => '',
+ 'Create another sub-task' => 'Başka bir alt görev daha oluştur',
+ // 'Time spent' => '',
+ 'Edit a sub-task' => 'Alt görev düzenle',
+ 'Remove a sub-task' => 'Alt görev sil',
+ 'The time must be a numeric value' => 'Zaman alfanumerik bir değer olmalı',
+ 'Todo' => 'Yapılacaklar',
+ 'In progress' => 'İşlemde',
+ 'Sub-task removed successfully.' => 'Alt görev silindi',
+ 'Unable to remove this sub-task.' => 'Alt görev silinemedi',
+ 'Sub-task updated successfully.' => 'Alt görev güncellendi',
+ 'Unable to update your sub-task.' => 'Alt görev güncellenemiyor',
+ 'Unable to create your sub-task.' => 'Alt görev oluşturulamadı',
+ 'Sub-task added successfully.' => 'Alt görev başarıyla eklendii',
+ 'Maximum size: ' => 'Maksimum boyutu',
+ 'Unable to upload the file.' => 'Karşıya yükleme başarısız',
+ 'Display another project' => 'Başka bir proje göster',
+ 'Your GitHub account was successfully linked to your profile.' => 'GitHub Hesabınız Profilinize bağlandı.',
+ 'Unable to link your GitHub Account.' => 'GitHub hesabınızla bağ oluşturulamadı.',
+ // 'GitHub authentication failed' => '',
+ // 'Your GitHub account is no longer linked to your profile.' => '',
+ // 'Unable to unlink your GitHub Account.' => '',
+ // 'Login with my GitHub Account' => '',
+ // 'Link my GitHub Account' => '',
+ // 'Unlink my GitHub Account' => '',
+ 'Created by %s' => '%s tarafından oluşturuldu',
+ 'Last modified on %B %e, %Y at %k:%M %p' => 'Son değişiklik tarihi %d.%m.%Y, saati %H:%M',
+ 'Tasks Export' => 'Görevleri dışa aktar',
+ 'Tasks exportation for "%s"' => '"%s" için görevleri dışa aktar',
+ 'Start Date' => 'Başlangıç tarihi',
+ 'End Date' => 'Bitiş tarihi',
+ 'Execute' => 'Gerçekleştir',
+ 'Task Id' => 'Görev No',
+ 'Creator' => 'Oluşturan',
+ 'Modification date' => 'Değişiklik tarihi',
+ 'Completion date' => 'Tamamlanma tarihi',
+ // 'Webhook URL for task creation' => '',
+ // 'Webhook URL for task modification' => '',
+ 'Clone' => 'Kopya oluştur',
+ 'Clone Project' => 'Projenin kopyasını oluştur',
+ 'Project cloned successfully.' => 'Proje kopyası başarıyla oluşturuldu.',
+ 'Unable to clone this project.' => 'Proje kopyası oluşturulamadı.',
+ 'Email notifications' => 'E-Posta bilgilendirmesi',
+ 'Enable email notifications' => 'E-Posta bilgilendirmesini aç',
+ 'Task position:' => 'Görev pozisyonu',
+ 'The task #%d have been opened.' => '#%d numaralı görev açıldı.',
+ 'The task #%d have been closed.' => '#%d numaralı görev kapatıldı.',
+ 'Sub-task updated' => 'Alt görev güncellendi',
+ 'Title:' => 'Başlık',
+ 'Status:' => 'Durum',
+ 'Assignee:' => 'Sorumlu:',
+ 'Time tracking:' => 'Zaman takibi',
+ 'New sub-task' => 'Yeni alt görev',
+ 'New attachment added "%s"' => 'Yeni dosya "%s" eklendi.',
+ 'Comment updated' => 'Yorum güncellendi',
+ 'New comment posted by %s' => '%s tarafından yeni yorum eklendi',
+ 'List of due tasks for the project "%s"' => '"%s" projesi için ilgili görevlerin listesi',
+ 'New attachment' => 'Yeni dosya eki',
+ 'New comment' => 'Yeni yorum',
+ 'New subtask' => 'Yeni alt görev',
+ 'Subtask updated' => 'Alt görev güncellendi',
+ 'Task updated' => 'Görev güncellendi',
+ 'Task closed' => 'Görev kapatıldı',
+ 'Task opened' => 'Görev açıldı',
+ '[%s][Due tasks]' => '[%s][İlgili görevler]',
+ '[Kanboard] Notification' => '[Kanboard] Bildirim',
+ 'I want to receive notifications only for those projects:' => 'Yalnızca bu projelerle ilgili bildirim almak istiyorum:',
+ 'view the task on Kanboard' => 'bu görevi Kanboard\'da göster',
+ 'Public access' => 'Dışa açık erişim',
+ 'Category management' => 'Kategori yönetimi',
+ 'User management' => 'Kullanıcı yönetimi',
+ 'Active tasks' => 'Aktif görevler',
+ 'Disable public access' => 'Dışa açık erişimi kapat',
+ 'Enable public access' => 'Dışa açık erişimi aç',
+ 'Active projects' => 'Aktif projeler',
+ 'Inactive projects' => 'Aktif olmayan projeler',
+ 'Public access disabled' => 'Dışa açık erişim kapatıldı',
+ 'Do you really want to disable this project: "%s"?' => 'Bu projeyi devre dışı bırakmak istediğinize emin misiniz?: "%s"',
+ 'Do you really want to duplicate this project: "%s"?' => 'Bu projenin kopyasını oluşturmak istediğinize emin misiniz?: "%s"',
+ 'Do you really want to enable this project: "%s"?' => 'Bu projeyi aktive etmek istediğinize emin misiniz?: "%s"',
+ 'Project activation' => 'Proje aktivasyonu',
+ 'Move the task to another project' => 'Görevi başka projeye taşı',
+ 'Move to another project' => 'Başka projeye taşı',
+ 'Do you really want to duplicate this task?' => 'Bu görevin kopyasını oluşturmak istediğinize emin misiniz?',
+ 'Duplicate a task' => 'Görevin kopyasını oluştur',
+ 'External accounts' => 'Dış hesaplar',
+ 'Account type' => 'Hesap türü',
+ 'Local' => 'Yerel',
+ 'Remote' => 'Uzak',
+ 'Enabled' => 'Etkinleştirildi',
+ 'Disabled' => 'Devre dışı bırakıldı',
+ 'Google account linked' => 'Google hesabıyla bağlı',
+ 'Github account linked' => 'Github hesabıyla bağlı',
+ 'Username:' => 'Kullanıcı adı',
+ 'Name:' => 'Ad',
+ 'Email:' => 'E-Posta',
+ 'Default project:' => 'Varsayılan Proje:',
+ 'Notifications:' => 'Bildirimler:',
+ 'Notifications' => 'Bildirimler',
+ 'Group:' => 'Grup',
+ 'Regular user' => 'Varsayılan kullanıcı',
+ 'Account type:' => 'Hesap türü:',
+ 'Edit profile' => 'Profili değiştir',
+ 'Change password' => 'Şifre değiştir',
+ 'Password modification' => 'Şifre değişimi',
+ 'External authentications' => 'Dış kimlik doğrulamaları',
+ 'Google Account' => 'Google hesabı',
+ 'Github Account' => 'Github hesabı',
+ 'Never connected.' => 'Hiç bağlanmamış.',
+ 'No account linked.' => 'Bağlanmış hesap yok.',
+ 'Account linked.' => 'Hesap bağlandı',
+ 'No external authentication enabled.' => 'Dış kimlik doğrulama kapalı.',
+ 'Password modified successfully.' => 'Şifre başarıyla değiştirildi.',
+ 'Unable to change the password.' => 'Şifre değiştirilemedi.',
+ 'Change category for the task "%s"' => '"%s" görevi için kategori değiştirme',
+ 'Change category' => 'Kategori değiştirme',
+ '%s updated the task %s' => '%s kullanıcısı %s görevini güncelledi',
+ '%s opened the task %s' => '%s kullanıcısı %s görevini açtı',
+ '%s moved the task %s to the position #%d in the column "%s"' => '%s kullanıcısı %s görevini #%d pozisyonu "%s" sütununa taşıdı',
+ '%s moved the task %s to the column "%s"' => '%s kullanıcısı %s görevini "%s" sütununa taşıdı',
+ '%s created the task %s' => '%s kullanıcısı %s görevini oluşturdu',
+ '%s closed the task %s' => '%s kullanıcısı %s görevini kapattı',
+ '%s created a subtask for the task %s' => '%s kullanıcısı %s görevi için bir alt görev oluşturdu',
+ '%s updated a subtask for the task %s' => '%s kullanıcısı %s görevinin bir alt görevini güncelledi',
+ 'Assigned to %s with an estimate of %s/%sh' => '%s kullanıcısına tahmini %s/%s saat tamamlanma süresi ile atanmış',
+ 'Not assigned, estimate of %sh' => 'Kimseye atanmamış, tahmini süre %s saat',
+ '%s updated a comment on the task %s' => '%s kullanıcısı %s görevinde bir yorumu güncelledi',
+ '%s commented the task %s' => '%s kullanıcısı %s görevine yorum ekledi',
+ '%s\'s activity' => '%s\'in aktivitesi',
+ 'No activity.' => 'Aktivite yok.',
+ 'RSS feed' => 'RSS kaynağı',
+ '%s updated a comment on the task #%d' => '%s kullanıcısı #%d nolu görevde bir yorumu güncelledi',
+ '%s commented on the task #%d' => '%s kullanıcısı #%d nolu göreve yorum ekledi',
+ '%s updated a subtask for the task #%d' => '%s kullanıcısı #%d nolu görevin bir alt görevini güncelledi',
+ '%s created a subtask for the task #%d' => '%s kullanıcısı #%d nolu göreve bir alt görev ekledi',
+ '%s updated the task #%d' => '%s kullanıcısı #%d nolu görevi güncelledi',
+ '%s created the task #%d' => '%s kullanıcısı #%d nolu görevi oluşturdu',
+ '%s closed the task #%d' => '%s kullanıcısı #%d nolu görevi kapattı',
+ '%s open the task #%d' => '%s kullanıcısı #%d nolu görevi açtı',
+ '%s moved the task #%d to the column "%s"' => '%s kullanıcısı #%d nolu görevi "%s" sütununa taşıdı',
+ '%s moved the task #%d to the position %d in the column "%s"' => '%s kullanıcısı #%d nolu görevi %d pozisyonu "%s" sütununa taşıdı',
+ 'Activity' => 'Aktivite',
+ 'Default values are "%s"' => 'Varsayılan değerler "%s"',
+ 'Default columns for new projects (Comma-separated)' => 'Yeni projeler için varsayılan sütunlar (virgül ile ayrılmış)',
+ 'Task assignee change' => 'Göreve atanan kullanıcı değişikliği',
+ '%s change the assignee of the task #%d to %s' => '%s kullanıcısı #%d nolu görevin sorumlusunu %s olarak değiştirdi',
+ '%s changed the assignee of the task %s to %s' => '%s kullanıcısı %s görevinin sorumlusunu %s olarak değiştirdi',
+ 'Column Change' => 'Sütun değişikliği',
+ 'Position Change' => 'Konum değişikliği',
+ 'Assignee Change' => 'Sorumlu değişikliği',
+ 'New password for the user "%s"' => '"%s" kullanıcısı için yeni şifre',
+ 'Choose an event' => 'Bir durum seçin',
+ // 'Github commit received' => '',
+ // 'Github issue opened' => '',
+ // 'Github issue closed' => '',
+ // 'Github issue reopened' => '',
+ // 'Github issue assignee change' => '',
+ // 'Github issue label change' => '',
+ 'Create a task from an external provider' => 'Dış sağlayıcı ile bir görev oluştur',
+ 'Change the assignee based on an external username' => 'Dış kaynaklı kullanıcı adı ile göreve atananı değiştir',
+ 'Change the category based on an external label' => 'Dış kaynaklı bir etiket ile kategori değiştir',
+ 'Reference' => 'Referans',
+ 'Reference: %s' => 'Referans: %s',
+ 'Label' => 'Etiket',
+ 'Database' => 'Veri bankası',
+ 'About' => 'Hakkında',
+ 'Database driver:' => 'Veri bankası sürücüsü',
+ 'Board settings' => 'Tablo ayarları',
+ 'URL and token' => 'URL veya Token',
+ 'Webhook settings' => 'Webhook ayarları',
+ 'URL for task creation:' => 'Görev oluşturma için URL',
+ 'Reset token' => 'Reset Token',
+ 'API endpoint:' => 'API endpoint',
+ 'Refresh interval for private board' => 'Özel tablolar için yenileme sıklığı',
+ 'Refresh interval for public board' => 'Dışa açık tablolar için yenileme sıklığı',
+ 'Task highlight period' => 'Görevi öne çıkarma süresi',
+ 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Bir görevin yeni değiştirilmiş sayılması için süre (saniye olarak) (Bu özelliği iptal etmek için 0, varsayılan değer 2 gün)',
+ 'Frequency in second (60 seconds by default)' => 'Saniye olarak frekans (varsayılan 60 saniye)',
+ 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Saniye olarak frekans (Bu özelliği iptal etmek için 0, varsayılan değer 10 saniye)',
+ 'Application URL' => 'Uygulama URL',
+ 'Example: http://example.kanboard.net/ (used by email notifications)' => 'Örneğin: http://example.kanboard.net/ (E-posta bildirimleri için kullanılıyor)',
+ 'Token regenerated.' => 'Token yeniden oluşturuldu.',
+ 'Date format' => 'Tarih formatı',
+ 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO formatı her zaman kabul edilir, örneğin: "%s" ve "%s"',
+ 'New private project' => 'Yeni özel proje',
+ 'This project is private' => 'Bu proje özel',
+ 'Type here to create a new sub-task' => 'Yeni bir alt görev oluşturmak için buraya yazın',
+ 'Add' => 'Ekle',
+ 'Estimated time: %s hours' => 'Tahmini süre: %s Saat',
+ 'Time spent: %s hours' => 'Kullanılan süre: %s Saat',
+ 'Started on %B %e, %Y' => '%B %e %Y tarihinde başlatıldı',
+ 'Start date' => 'Başlangıç tarihi',
+ 'Time estimated' => 'Tahmini süre',
+ 'There is nothing assigned to you.' => 'Size atanan hiçbir şey yok.',
+ 'My tasks' => 'Görevlerim',
+ 'Activity stream' => 'Güncel olay akışı',
+ 'Dashboard' => 'Anasayfa',
+ 'Confirmation' => 'Onay',
+ 'Allow everybody to access to this project' => 'Bu projeye herkesin erişimine izin ver',
+ 'Everybody have access to this project.' => 'Bu projeye herkesin erişimi var.',
+ 'Webhooks' => 'Webhooks',
+ 'API' => 'API',
+ 'Integration' => 'Entegrasyon',
+ 'Github webhooks' => 'Github Webhook',
+ 'Help on Github webhooks' => 'Github Webhooks hakkında yardım',
+ 'Create a comment from an external provider' => 'Dış sağlayıcı ile bir yorum oluştur',
+ 'Github issue comment created' => 'Github hata yorumu oluşturuldu',
+ 'Configure' => 'Ayarla',
+ 'Project management' => 'Proje yönetimi',
+ 'My projects' => 'Projelerim',
+ 'Columns' => 'Sütunlar',
+ 'Task' => 'Görev',
+ 'Your are not member of any project.' => 'Hiç bir projenin üyesi değilsiniz.',
+ 'Percentage' => 'Yüzde',
+ 'Number of tasks' => 'Görev sayısı',
+ 'Task distribution' => 'Görev dağılımı',
+ 'Reportings' => 'Raporlar',
+ 'Task repartition for "%s"' => '"%s" için görev dağılımı',
+ 'Analytics' => 'Analiz',
+ 'Subtask' => 'Alt görev',
+ 'My subtasks' => 'Alt görevlerim',
+ 'User repartition' => 'Kullanıcı dağılımı',
+ 'User repartition for "%s"' => '"%s" için kullanıcı dağılımı',
+ 'Clone this project' => 'Projenin kopyasını oluştur',
+ 'Column removed successfully.' => 'Sütun başarıyla kaldırıldı.',
+ 'Edit Project' => 'Projeyi düzenle',
+ 'Github Issue' => 'Github Issue',
+ 'Not enough data to show the graph.' => 'Grafik gösterimi için yeterli veri yok.',
+ 'Previous' => 'Önceki',
+ 'The id must be an integer' => 'ID bir tamsayı olmalı',
+ 'The project id must be an integer' => 'Proje numarası bir tam sayı olmalı',
+ 'The status must be an integer' => 'Durum bir tam sayı olmalı',
+ 'The subtask id is required' => 'Alt görev numarası gerekli',
+ 'The subtask id must be an integer' => 'Alt görev numarası bir tam sayı olmalı',
+ 'The task id is required' => 'Görev numarası gerekli',
+ 'The task id must be an integer' => 'Görev numarası bir tam sayı olmalı',
+ 'The user id must be an integer' => 'Kullanıcı numarası bir tam sayı olmalı',
+ 'This value is required' => 'Bu değer gerekli',
+ 'This value must be numeric' => 'Bu değer sayı olmalı',
+ 'Unable to create this task.' => 'Bu görev oluşturulamıyor.',
+ 'Cumulative flow diagram' => 'Kümülatif akış diyagramı',
+ 'Cumulative flow diagram for "%s"' => '"%s" için kümülatif akış diyagramı',
+ 'Daily project summary' => 'Günlük proje özeti',
+ 'Daily project summary export' => 'Günlük proje özetini dışa aktar',
+ 'Daily project summary export for "%s"' => '"%s" için günlük proje özetinin dışa',
+ 'Exports' => 'Dışa aktarımlar',
+ 'This export contains the number of tasks per column grouped per day.' => 'Bu dışa aktarım günlük gruplanmış olarak her sütundaki görev sayısını içerir.',
+ 'Nothing to preview...' => 'Önizleme yapılacak bir şey yok ...',
+ 'Preview' => 'Önizleme',
+ 'Write' => 'Değiştir',
+ 'Active swimlanes' => 'Aktif Kulvar',
+ 'Add a new swimlane' => 'Yeni bir Kulvar ekle',
+ 'Change default swimlane' => 'Varsayılan Kulvarı değiştir',
+ 'Default swimlane' => 'Varsayılan Kulvar',
+ 'Do you really want to remove this swimlane: "%s"?' => 'Bu Kulvarı silmek istediğinize emin misiniz?: "%s"?',
+ 'Inactive swimlanes' => 'Pasif Kulvarlar',
+ 'Set project manager' => 'Proje yöneticisi olarak ata',
+ 'Set project member' => 'Proje üyesi olarak ata',
+ 'Remove a swimlane' => 'Kulvarı sil',
+ 'Rename' => 'Yeniden adlandır',
+ 'Show default swimlane' => 'Varsayılan Kulvarı göster',
+ 'Swimlane modification for the project "%s"' => '"% s" Projesi için Kulvar değişikliği',
+ 'Swimlane not found.' => 'Kulvar bulunamadı',
+ 'Swimlane removed successfully.' => 'Kulvar başarıyla kaldırıldı.',
+ 'Swimlanes' => 'Kulvarlar',
+ 'Swimlane updated successfully.' => 'Kulvar başarıyla güncellendi.',
+ 'The default swimlane have been updated successfully.' => 'Varsayılan Kulvarlar başarıyla güncellendi.',
+ 'Unable to create your swimlane.' => 'Bu Kulvarı oluşturmak mümkün değil.',
+ 'Unable to remove this swimlane.' => 'Bu Kulvarı silmek mümkün değil.',
+ 'Unable to update this swimlane.' => 'Bu Kulvarı değiştirmek mümkün değil.',
+ 'Your swimlane have been created successfully.' => 'Kulvar başarıyla oluşturuldu.',
+ 'Example: "Bug, Feature Request, Improvement"' => 'Örnek: "Sorun, Özellik talebi, İyileştirme"',
+ 'Default categories for new projects (Comma-separated)' => 'Yeni projeler için varsayılan kategoriler (Virgül ile ayrılmış)',
+ // 'Gitlab commit received' => '',
+ // 'Gitlab issue opened' => '',
+ // 'Gitlab issue closed' => '',
+ // 'Gitlab webhooks' => '',
+ // 'Help on Gitlab webhooks' => '',
+ 'Integrations' => 'Entegrasyon',
+ 'Integration with third-party services' => 'Dış servislerle entegrasyon',
+ 'Role for this project' => 'Bu proje için rol',
+ 'Project manager' => 'Proje Yöneticisi',
+ 'Project member' => 'Proje Üyesi',
+ 'A project manager can change the settings of the project and have more privileges than a standard user.' => 'Bir Proje Yöneticisi proje ayarlarını değiştirebilir ve bir üyeden daha fazla yetkiye sahiptir.',
+ // 'Gitlab Issue' => '',
+ 'Subtask Id' => 'Alt görev No:',
+ 'Subtasks' => 'Alt görevler',
+ 'Subtasks Export' => 'Alt görevleri dışa aktar',
+ 'Subtasks exportation for "%s"' => '"%s" için alt görevleri dışa aktarımı',
+ 'Task Title' => 'Görev Başlığı',
+ 'Untitled' => 'Başlıksız',
+ 'Application default' => 'Uygulama varsayılanları',
+ 'Language:' => 'Dil:',
+ 'Timezone:' => 'Saat dilimi:',
+ 'All columns' => 'Tüm sütunlar',
+ 'Calendar for "%s"' => '"%s" için takvim',
+ 'Filter by column' => 'Sütuna göre filtrele',
+ 'Filter by status' => 'Duruma göre filtrele',
+ 'Calendar' => 'Takvim',
+ 'Next' => 'Sonraki',
+ '#%d' => '#%d',
+ 'Filter by color' => 'Renklere göre filtrele',
+ 'Filter by swimlane' => 'Kulvara göre filtrele',
+ 'All swimlanes' => 'Tüm Kulvarlar',
+ 'All colors' => 'Tüm Renkler',
+ 'All status' => 'Tüm Durumlar',
+ 'Add a comment logging moving the task between columns' => 'Sütun değiştiğinde kayıt olarak yorum ekle',
+ 'Moved to column %s' => '%s Sütununa taşındı',
+ 'Change description' => 'Açıklamayı değiştir',
+ 'User dashboard' => 'Kullanıcı Anasayfası',
+ 'Allow only one subtask in progress at the same time for a user' => 'Bir kullanıcı için aynı anda yalnızca bir alt göreve izin ver',
+ 'Edit column "%s"' => '"%s" sütununu değiştir',
+ 'Enable time tracking for subtasks' => 'Alt görevler için zaman takibini etkinleştir',
+ 'Select the new status of the subtask: "%s"' => '"%s" alt görevi için yeni durum seçin.',
+ 'Subtask timesheet' => 'Alt görev için zaman takip tablosu',
+ 'There is nothing to show.' => 'Gösterilecek hiçbir şey yok.',
+ 'Time Tracking' => 'Zaman takibi',
+ 'You already have one subtask in progress' => 'Zaten işlemde olan bir alt görev var',
+ 'Which parts of the project do you want to duplicate?' => 'Projenin hangi kısımlarının kopyasını oluşturmak istiyorsunuz?',
+ 'Change dashboard view' => 'Anasayfa görünümünü değiştir',
+ 'Show/hide activities' => 'Aktiviteleri göster/gizle',
+ 'Show/hide projects' => 'Projeleri göster/gizle',
+ 'Show/hide subtasks' => 'Alt görevleri göster/gizle',
+ 'Show/hide tasks' => 'Görevleri göster/gizle',
+ 'Disable login form' => 'Giriş formunu devre dışı bırak',
+ 'Show/hide calendar' => 'Takvimi göster/gizle',
+ 'User calendar' => 'Kullanıcı takvimi',
+ 'Bitbucket commit received' => 'Bitbucket commit alındı',
+ 'Bitbucket webhooks' => 'Bitbucket webhooks',
+ 'Help on Bitbucket webhooks' => 'Bitbucket webhooks için yardım',
+ 'Start' => 'Başlangıç',
+ 'End' => 'Son',
+ 'Task age in days' => 'Görev yaşı gün olarak',
+ 'Days in this column' => 'Bu sütunda geçirilen gün',
+ '%dd' => '%dG',
+ 'Add a link' => 'Link ekle',
+ 'Add a new link' => 'Yeni link ekle',
+ 'Do you really want to remove this link: "%s"?' => '"%s" linkini gerçekten silmek istiyor musunuz?',
+ 'Do you really want to remove this link with task #%d?' => '#%d numaralı görev ile linki gerçekten silmek istiyor musunuz?',
+ 'Field required' => 'Bu alan gerekli',
+ 'Link added successfully.' => 'Link başarıyla eklendi.',
+ 'Link updated successfully.' => 'Link başarıyla güncellendi.',
+ 'Link removed successfully.' => 'Link başarıyla silindi.',
+ 'Link labels' => 'Link etiketleri',
+ 'Link modification' => 'Link değiştirme',
+ 'Links' => 'Links',
+ 'Link settings' => 'Link ayarları',
+ 'Opposite label' => 'Zıt etiket',
+ 'Remove a link' => 'Bir link silmek',
+ 'Task\'s links' => 'Görevin linkleri',
+ 'The labels must be different' => 'Etiketler farklı olmalı',
+ 'There is no link.' => 'Hiç bir bağ yok',
+ 'This label must be unique' => 'Bu etiket tek olmalı',
+ 'Unable to create your link.' => 'Link oluşturulamadı.',
+ 'Unable to update your link.' => 'Link güncellenemiyor.',
+ 'Unable to remove this link.' => 'Link kaldırılamıyor',
+ 'relates to' => 'şununla ilgili',
+ 'blocks' => 'şunu engelliyor',
+ 'is blocked by' => 'şunun tarafından engelleniyor',
+ 'duplicates' => 'şunun kopyasını oluşturuyor',
+ 'is duplicated by' => 'şunun tarafından kopyası oluşturuluyor',
+ 'is a child of' => 'şunun astı',
+ 'is a parent of' => 'şunun üstü',
+ 'targets milestone' => 'şu kilometre taşını hedefliyor',
+ 'is a milestone of' => 'şunun için bir kilometre taşı',
+ 'fixes' => 'düzeltiyor',
+ 'is fixed by' => 'şunun tarafından düzeltildi',
+ 'This task' => 'Bu görev',
+ '<1h' => '<1s',
+ '%dh' => '%ds',
+ // '%b %e' => '',
+ 'Expand tasks' => 'Görevleri genişlet',
+ 'Collapse tasks' => 'Görevleri daralt',
+ 'Expand/collapse tasks' => 'Görevleri genişlet/daralt',
+ 'Close dialog box' => 'İletiyi kapat',
+ 'Submit a form' => 'Formu gönder',
+ 'Board view' => 'Tablo görünümü',
+ 'Keyboard shortcuts' => 'Klavye kısayolları',
+ 'Open board switcher' => 'Tablo seçim listesini aç',
+ 'Application' => 'Uygulama',
+ 'Filter recently updated' => 'Son güncellenenleri göster',
+ 'since %B %e, %Y at %k:%M %p' => '%B %e, %Y saat %k:%M %p\'den beri',
+ 'More filters' => 'Daha fazla filtre',
+ 'Compact view' => 'Ekrana sığdır',
+ 'Horizontal scrolling' => 'Geniş görünüm',
+ 'Compact/wide view' => 'Ekrana sığdır / Geniş görünüm',
+ // 'No results match:' => '',
+ // 'Remove hourly rate' => '',
+ // 'Do you really want to remove this hourly rate?' => '',
+ // 'Hourly rates' => '',
+ // 'Hourly rate' => '',
+ // 'Currency' => '',
+ // 'Effective date' => '',
+ // 'Add new rate' => '',
+ // 'Rate removed successfully.' => '',
+ // 'Unable to remove this rate.' => '',
+ // 'Unable to save the hourly rate.' => '',
+ // 'Hourly rate created successfully.' => '',
+ // 'Start time' => '',
+ // 'End time' => '',
+ // 'Comment' => '',
+ // 'All day' => '',
+ // 'Day' => '',
+ // 'Manage timetable' => '',
+ // 'Overtime timetable' => '',
+ // 'Time off timetable' => '',
+ // 'Timetable' => '',
+ // 'Work timetable' => '',
+ // 'Week timetable' => '',
+ // 'Day timetable' => '',
+ // 'From' => '',
+ // 'To' => '',
+ // 'Time slot created successfully.' => '',
+ // 'Unable to save this time slot.' => '',
+ // 'Time slot removed successfully.' => '',
+ // 'Unable to remove this time slot.' => '',
+ // 'Do you really want to remove this time slot?' => '',
+ // 'Remove time slot' => '',
+ // 'Add new time slot' => '',
+ // 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
+);
diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php
index cb36250b..f1bbd87c 100644
--- a/app/Locale/zh_CN/translations.php
+++ b/app/Locale/zh_CN/translations.php
@@ -1,6 +1,8 @@
<?php
return array(
+ // 'number.decimals_separator' => '',
+ // 'number.thousands_separator' => '',
'None' => '无',
'edit' => '编辑',
'Edit' => '编辑',
@@ -737,4 +739,38 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
+ // 'No results match:' => '',
+ // 'Remove hourly rate' => '',
+ // 'Do you really want to remove this hourly rate?' => '',
+ // 'Hourly rates' => '',
+ // 'Hourly rate' => '',
+ // 'Currency' => '',
+ // 'Effective date' => '',
+ // 'Add new rate' => '',
+ // 'Rate removed successfully.' => '',
+ // 'Unable to remove this rate.' => '',
+ // 'Unable to save the hourly rate.' => '',
+ // 'Hourly rate created successfully.' => '',
+ // 'Start time' => '',
+ // 'End time' => '',
+ // 'Comment' => '',
+ // 'All day' => '',
+ // 'Day' => '',
+ // 'Manage timetable' => '',
+ // 'Overtime timetable' => '',
+ // 'Time off timetable' => '',
+ // 'Timetable' => '',
+ // 'Work timetable' => '',
+ // 'Week timetable' => '',
+ // 'Day timetable' => '',
+ // 'From' => '',
+ // 'To' => '',
+ // 'Time slot created successfully.' => '',
+ // 'Unable to save this time slot.' => '',
+ // 'Time slot removed successfully.' => '',
+ // 'Unable to remove this time slot.' => '',
+ // 'Do you really want to remove this time slot?' => '',
+ // 'Remove time slot' => '',
+ // 'Add new time slot' => '',
+ // 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
);
diff --git a/app/Model/Acl.php b/app/Model/Acl.php
index 9fc81747..56938f9d 100644
--- a/app/Model/Acl.php
+++ b/app/Model/Acl.php
@@ -70,6 +70,7 @@ class Acl extends Base
'config' => '*',
'link' => '*',
'project' => array('remove'),
+ 'hourlyrate' => '*',
);
/**
diff --git a/app/Model/Config.php b/app/Model/Config.php
index 48640f4e..a2db25e9 100644
--- a/app/Model/Config.php
+++ b/app/Model/Config.php
@@ -22,6 +22,27 @@ class Config extends Base
const TABLE = 'settings';
/**
+ * Get available currencies
+ *
+ * @access public
+ * @return array
+ */
+ public function getCurrencies()
+ {
+ return array(
+ 'USD' => t('USD - US Dollar'),
+ 'EUR' => t('EUR - Euro'),
+ 'GBP' => t('GBP - British Pound'),
+ 'CHF' => t('CHF - Swiss Francs'),
+ 'CAD' => t('CAD - Canadian Dollar'),
+ 'AUD' => t('AUD - Australian Dollar'),
+ 'NZD' => t('NZD - New Zealand Dollar'),
+ 'INR' => t('INR - Indian Rupee'),
+ 'JPY' => t('JPY - Japanese Yen'),
+ );
+ }
+
+ /**
* Get available timezones
*
* @access public
@@ -63,6 +84,7 @@ class Config extends Base
'ru_RU' => 'Русский',
'fi_FI' => 'Suomi',
'sv_SE' => 'Svenska',
+ 'tr_TR' => 'Türkçe',
'zh_CN' => '中文(简体)',
'ja_JP' => '日本語',
'th_TH' => 'ไทย',
@@ -99,6 +121,7 @@ class Config extends Base
'zh_CN' => 'zh-cn',
'ja_JP' => 'ja',
'th_TH' => 'th',
+ 'tr_TR' => 'tr',
);
$lang = $this->getCurrentLanguage();
diff --git a/app/Model/HourlyRate.php b/app/Model/HourlyRate.php
new file mode 100644
index 00000000..c2ce3eaf
--- /dev/null
+++ b/app/Model/HourlyRate.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace Model;
+
+use SimpleValidator\Validator;
+use SimpleValidator\Validators;
+
+/**
+ * Hourly Rate
+ *
+ * @package model
+ * @author Frederic Guillot
+ */
+class HourlyRate extends Base
+{
+ /**
+ * SQL table name
+ *
+ * @var string
+ */
+ const TABLE = 'hourly_rates';
+
+ /**
+ * Get all rates for a given user
+ *
+ * @access public
+ * @param integer $user_id User id
+ * @return array
+ */
+ public function getAllByUser($user_id)
+ {
+ return $this->db->table(self::TABLE)->eq('user_id', $user_id)->desc('date_effective')->findAll();
+ }
+
+ /**
+ * Get current rate for a given user
+ *
+ * @access public
+ * @param integer $user_id User id
+ * @return float
+ */
+ public function getCurrentRate($user_id)
+ {
+ return $this->db->table(self::TABLE)->eq('user_id', $user_id)->desc('date_effective')->findOneColumn('rate') ?: 0;
+ }
+
+ /**
+ * Add a new rate in the database
+ *
+ * @access public
+ * @param integer $user_id User id
+ * @param float $rate Hourly rate
+ * @param string $currency Currency code
+ * @param string $date ISO8601 date format
+ * @return boolean|integer
+ */
+ public function create($user_id, $rate, $currency, $date)
+ {
+ $values = array(
+ 'user_id' => $user_id,
+ 'rate' => $rate,
+ 'currency' => $currency,
+ 'date_effective' => $this->dateParser->removeTimeFromTimestamp($this->dateParser->getTimestamp($date)),
+ );
+
+ return $this->persist(self::TABLE, $values);
+ }
+
+ /**
+ * Remove a specific rate
+ *
+ * @access public
+ * @param integer $rate_id
+ * @return boolean
+ */
+ public function remove($rate_id)
+ {
+ return $this->db->table(self::TABLE)->eq('id', $rate_id)->remove();
+ }
+
+ /**
+ * Validate creation
+ *
+ * @access public
+ * @param array $values Form values
+ * @return array $valid, $errors [0] = Success or not, [1] = List of errors
+ */
+ public function validateCreation(array $values)
+ {
+ $v = new Validator($values, array(
+ new Validators\Required('user_id', t('Field required')),
+ new Validators\Required('rate', t('Field required')),
+ new Validators\Numeric('rate', t('This value must be numeric')),
+ new Validators\Required('date_effective', t('Field required')),
+ new Validators\Required('currency', t('Field required')),
+ ));
+
+ return array(
+ $v->execute(),
+ $v->getErrors()
+ );
+ }
+}
diff --git a/app/Model/Subtask.php b/app/Model/Subtask.php
index aa4a6f81..e33373e0 100644
--- a/app/Model/Subtask.php
+++ b/app/Model/Subtask.php
@@ -122,7 +122,7 @@ class Subtask extends Base
->eq('task_id', $task_id)
->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name')
->join(User::TABLE, 'id', 'user_id')
- ->asc(self::TABLE.'.id')
+ ->asc(self::TABLE.'.position')
->filter(array($this, 'addStatusName'))
->findAll();
}
@@ -164,6 +164,22 @@ class Subtask extends Base
}
/**
+ * Get the position of the last column for a given project
+ *
+ * @access public
+ * @param integer $task_id Task id
+ * @return integer
+ */
+ public function getLastPosition($task_id)
+ {
+ return (int) $this->db
+ ->table(self::TABLE)
+ ->eq('task_id', $task_id)
+ ->desc('position')
+ ->findOneColumn('position');
+ }
+
+ /**
* Create a new subtask
*
* @access public
@@ -173,6 +189,8 @@ class Subtask extends Base
public function create(array $values)
{
$this->prepare($values);
+ $values['position'] = $this->getLastPosition($values['task_id']) + 1;
+
$subtask_id = $this->persist(self::TABLE, $values);
if ($subtask_id) {
@@ -209,6 +227,64 @@ class Subtask extends Base
}
/**
+ * Move a subtask down, increment the position value
+ *
+ * @access public
+ * @param integer $task_id
+ * @param integer $subtask_id
+ * @return boolean
+ */
+ public function moveDown($task_id, $subtask_id)
+ {
+ $subtasks = $this->db->hashtable(self::TABLE)->eq('task_id', $task_id)->asc('position')->getAll('id', 'position');
+ $positions = array_flip($subtasks);
+
+ if (isset($subtasks[$subtask_id]) && $subtasks[$subtask_id] < count($subtasks)) {
+
+ $position = ++$subtasks[$subtask_id];
+ $subtasks[$positions[$position]]--;
+
+ $this->db->startTransaction();
+ $this->db->table(self::TABLE)->eq('id', $subtask_id)->update(array('position' => $position));
+ $this->db->table(self::TABLE)->eq('id', $positions[$position])->update(array('position' => $subtasks[$positions[$position]]));
+ $this->db->closeTransaction();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Move a subtask up, decrement the position value
+ *
+ * @access public
+ * @param integer $task_id
+ * @param integer $subtask_id
+ * @return boolean
+ */
+ public function moveUp($task_id, $subtask_id)
+ {
+ $subtasks = $this->db->hashtable(self::TABLE)->eq('task_id', $task_id)->asc('position')->getAll('id', 'position');
+ $positions = array_flip($subtasks);
+
+ if (isset($subtasks[$subtask_id]) && $subtasks[$subtask_id] > 1) {
+
+ $position = --$subtasks[$subtask_id];
+ $subtasks[$positions[$position]]++;
+
+ $this->db->startTransaction();
+ $this->db->table(self::TABLE)->eq('id', $subtask_id)->update(array('position' => $position));
+ $this->db->table(self::TABLE)->eq('id', $positions[$position])->update(array('position' => $subtasks[$positions[$position]]));
+ $this->db->closeTransaction();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
* Change the status of subtask
*
* Todo -> In progress -> Done -> Todo -> etc...
@@ -286,9 +362,9 @@ class Subtask extends Base
return $this->db->transaction(function ($db) use ($src_task_id, $dst_task_id) {
$subtasks = $db->table(Subtask::TABLE)
- ->columns('title', 'time_estimated')
+ ->columns('title', 'time_estimated', 'position')
->eq('task_id', $src_task_id)
- ->asc('id') // Explicit sorting for postgresql
+ ->asc('position')
->findAll();
foreach ($subtasks as &$subtask) {
@@ -380,7 +456,7 @@ class Subtask extends Base
return array(
new Validators\Integer('id', t('The subtask id must be an integer')),
new Validators\Integer('task_id', t('The task id must be an integer')),
- new Validators\MaxLength('title', t('The maximum length is %d characters', 100), 100),
+ new Validators\MaxLength('title', t('The maximum length is %d characters', 255), 255),
new Validators\Integer('user_id', t('The user id must be an integer')),
new Validators\Integer('status', t('The status must be an integer')),
new Validators\Numeric('time_estimated', t('The time must be a numeric value')),
diff --git a/app/Model/Swimlane.php b/app/Model/Swimlane.php
index c9bc43e1..8f417fca 100644
--- a/app/Model/Swimlane.php
+++ b/app/Model/Swimlane.php
@@ -75,6 +75,22 @@ class Swimlane extends Base
}
/**
+ * Get a swimlane by the project and the name
+ *
+ * @access public
+ * @param integer $project_id Project id
+ * @param string $name Swimlane name
+ * @return array
+ */
+ public function getByName($project_id, $name)
+ {
+ return $this->db->table(self::TABLE)
+ ->eq('project_id', $project_id)
+ ->eq('name', $name)
+ ->findAll();
+ }
+
+ /**
* Get default swimlane properties
*
* @access public
diff --git a/app/Model/Timetable.php b/app/Model/Timetable.php
new file mode 100644
index 00000000..eb37cefd
--- /dev/null
+++ b/app/Model/Timetable.php
@@ -0,0 +1,194 @@
+<?php
+
+namespace Model;
+
+use DateTime;
+use DateInterval;
+
+/**
+ * Timetable
+ *
+ * @package model
+ * @author Frederic Guillot
+ */
+class Timetable extends Base
+{
+ /**
+ * User time slots
+ *
+ * @access private
+ * @var array
+ */
+ private $day;
+ private $week;
+ private $overtime;
+ private $timeoff;
+
+ /**
+ * Get the timetable for a user for a given date range
+ *
+ * @access public
+ * @param integer $user_id
+ * @param \DateTime $start
+ * @param \DateTime $end
+ * @return array
+ */
+ public function calculate($user_id, DateTime $start, DateTime $end)
+ {
+ $timetable = array();
+
+ $this->day = $this->timetableDay->getByUser($user_id);
+ $this->week = $this->timetableWeek->getByUser($user_id);
+ $this->overtime = $this->timetableExtra->getByUserAndDate($user_id, $start->format('Y-m-d'), $end->format('Y-m-d'));
+ $this->timeoff = $this->timetableOff->getByUserAndDate($user_id, $start->format('Y-m-d'), $end->format('Y-m-d'));
+
+ for ($today = clone($start); $today <= $end; $today->add(new DateInterval('P1D'))) {
+ $week_day = $today->format('N');
+ $timetable = array_merge($timetable, $this->getWeekSlots($today, $week_day));
+ $timetable = array_merge($timetable, $this->getOvertimeSlots($today, $week_day));
+ }
+
+ return $timetable;
+ }
+
+ /**
+ * Return worked time slots for the given day
+ *
+ * @access public
+ * @param \DateTime $today
+ * @param string $week_day
+ * @return array
+ */
+ public function getWeekSlots(DateTime $today, $week_day)
+ {
+ $slots = array();
+ $dayoff = $this->getDayOff($today);
+
+ if (! empty($dayoff) && $dayoff['all_day'] == 1) {
+ return array();
+ }
+
+ foreach ($this->week as $slot) {
+ if ($week_day == $slot['day']) {
+ $slots = array_merge($slots, $this->getDayWorkSlots($slot, $dayoff, $today));
+ }
+ }
+
+ return $slots;
+ }
+
+ /**
+ * Get the overtime time slots for the given day
+ *
+ * @access public
+ * @param \DateTime $today
+ * @param string $week_day
+ * @return array
+ */
+ public function getOvertimeSlots(DateTime $today, $week_day)
+ {
+ $slots = array();
+
+ foreach ($this->overtime as $slot) {
+
+ $day = new DateTime($slot['date']);
+
+ if ($week_day == $day->format('N')) {
+
+ if ($slot['all_day'] == 1) {
+ $slots = array_merge($slots, $this->getDaySlots($today));
+ }
+ else {
+ $slots[] = $this->getTimeSlot($slot, $day);
+ }
+ }
+ }
+
+ return $slots;
+ }
+
+ /**
+ * Get worked time slots and remove time off
+ *
+ * @access public
+ * @param array $slot
+ * @param array $dayoff
+ * @param \DateTime $today
+ * @return array
+ */
+ public function getDayWorkSlots(array $slot, array $dayoff, DateTime $today)
+ {
+ $slots = array();
+
+ if (! empty($dayoff) && $dayoff['start'] < $slot['end']) {
+
+ if ($dayoff['start'] > $slot['start']) {
+ $slots[] = $this->getTimeSlot(array('end' => $dayoff['start']) + $slot, $today);
+ }
+
+ if ($dayoff['end'] < $slot['end']) {
+ $slots[] = $this->getTimeSlot(array('start' => $dayoff['end']) + $slot, $today);
+ }
+ }
+ else {
+ $slots[] = $this->getTimeSlot($slot, $today);
+ }
+
+ return $slots;
+ }
+
+ /**
+ * Get regular day work time slots
+ *
+ * @access public
+ * @param \DateTime $today
+ * @return array
+ */
+ public function getDaySlots(DateTime $today)
+ {
+ $slots = array();
+
+ foreach ($this->day as $day) {
+ $slots[] = $this->getTimeSlot($day, $today);
+ }
+
+ return $slots;
+ }
+
+ /**
+ * Get the start and end time slot for a given day
+ *
+ * @access public
+ * @param array $slot
+ * @param \DateTime $today
+ * @return array
+ */
+ public function getTimeSlot(array $slot, DateTime $today)
+ {
+ $date = $today->format('Y-m-d');
+
+ return array(
+ new DateTime($date.' '.$slot['start']),
+ new DateTime($date.' '.$slot['end']),
+ );
+ }
+
+ /**
+ * Return day off time slot
+ *
+ * @access public
+ * @param \DateTime $today
+ * @return array
+ */
+ public function getDayOff(DateTime $today)
+ {
+ foreach ($this->timeoff as $day) {
+
+ if ($day['date'] === $today->format('Y-m-d')) {
+ return $day;
+ }
+ }
+
+ return array();
+ }
+}
diff --git a/app/Model/TimetableDay.php b/app/Model/TimetableDay.php
new file mode 100644
index 00000000..0c7bf20b
--- /dev/null
+++ b/app/Model/TimetableDay.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace Model;
+
+use SimpleValidator\Validator;
+use SimpleValidator\Validators;
+
+/**
+ * Timetable Workweek
+ *
+ * @package model
+ * @author Frederic Guillot
+ */
+class TimetableDay extends Base
+{
+ /**
+ * SQL table name
+ *
+ * @var string
+ */
+ const TABLE = 'timetable_day';
+
+ /**
+ * Get the timetable for a given user
+ *
+ * @access public
+ * @param integer $user_id User id
+ * @return array
+ */
+ public function getByUser($user_id)
+ {
+ return $this->db->table(self::TABLE)->eq('user_id', $user_id)->asc('start')->findAll();
+ }
+
+ /**
+ * Add a new time slot in the database
+ *
+ * @access public
+ * @param integer $user_id User id
+ * @param string $start Start hour (24h format)
+ * @param string $end End hour (24h format)
+ * @return boolean|integer
+ */
+ public function create($user_id, $start, $end)
+ {
+ $values = array(
+ 'user_id' => $user_id,
+ 'start' => $start,
+ 'end' => $end,
+ );
+
+ return $this->persist(self::TABLE, $values);
+ }
+
+ /**
+ * Remove a specific time slot
+ *
+ * @access public
+ * @param integer $slot_id
+ * @return boolean
+ */
+ public function remove($slot_id)
+ {
+ return $this->db->table(self::TABLE)->eq('id', $slot_id)->remove();
+ }
+
+ /**
+ * Validate creation
+ *
+ * @access public
+ * @param array $values Form values
+ * @return array $valid, $errors [0] = Success or not, [1] = List of errors
+ */
+ public function validateCreation(array $values)
+ {
+ $v = new Validator($values, array(
+ new Validators\Required('user_id', t('Field required')),
+ new Validators\Required('start', t('Field required')),
+ new Validators\Required('end', t('Field required')),
+ ));
+
+ return array(
+ $v->execute(),
+ $v->getErrors()
+ );
+ }
+}
diff --git a/app/Model/TimetableExtra.php b/app/Model/TimetableExtra.php
new file mode 100644
index 00000000..48db662d
--- /dev/null
+++ b/app/Model/TimetableExtra.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Model;
+
+use SimpleValidator\Validator;
+use SimpleValidator\Validators;
+
+/**
+ * Timetable over-time
+ *
+ * @package model
+ * @author Frederic Guillot
+ */
+class TimetableExtra extends TimetableOff
+{
+ /**
+ * SQL table name
+ *
+ * @var string
+ */
+ const TABLE = 'timetable_extra';
+}
diff --git a/app/Model/TimetableOff.php b/app/Model/TimetableOff.php
new file mode 100644
index 00000000..aa064f05
--- /dev/null
+++ b/app/Model/TimetableOff.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Model;
+
+use SimpleValidator\Validator;
+use SimpleValidator\Validators;
+
+/**
+ * Timetable time off
+ *
+ * @package model
+ * @author Frederic Guillot
+ */
+class TimetableOff extends Base
+{
+ /**
+ * SQL table name
+ *
+ * @var string
+ */
+ const TABLE = 'timetable_off';
+
+ /**
+ * Get query to fetch everything (pagination)
+ *
+ * @access public
+ * @param integer $user_id User id
+ * @return \PicoDb\Table
+ */
+ public function getUserQuery($user_id)
+ {
+ return $this->db->table(static::TABLE)->eq('user_id', $user_id);
+ }
+
+ /**
+ * Get the timetable for a given user
+ *
+ * @access public
+ * @param integer $user_id User id
+ * @return array
+ */
+ public function getByUser($user_id)
+ {
+ return $this->db->table(static::TABLE)->eq('user_id', $user_id)->desc('date')->asc('start')->findAll();
+ }
+
+ /**
+ * Get the timetable for a given user
+ *
+ * @access public
+ * @param integer $user_id User id
+ * @param string $start_date
+ * @param string $end_date
+ * @return array
+ */
+ public function getByUserAndDate($user_id, $start_date, $end_date)
+ {
+ return $this->db->table(static::TABLE)
+ ->eq('user_id', $user_id)
+ ->gte('date', $start_date)
+ ->lte('date', $end_date)
+ ->desc('date')
+ ->asc('start')
+ ->findAll();
+ }
+
+ /**
+ * Add a new time slot in the database
+ *
+ * @access public
+ * @param integer $user_id User id
+ * @param string $date Day (ISO8601 format)
+ * @param boolean $all_day All day flag
+ * @param float $start Start hour (24h format)
+ * @param float $end End hour (24h format)
+ * @param string $comment
+ * @return boolean|integer
+ */
+ public function create($user_id, $date, $all_day, $start = '', $end = '', $comment = '')
+ {
+ $values = array(
+ 'user_id' => $user_id,
+ 'date' => $date,
+ 'all_day' => $all_day,
+ 'start' => $all_day ? '' : $start,
+ 'end' => $all_day ? '' : $end,
+ 'comment' => $comment,
+ );
+
+ return $this->persist(static::TABLE, $values);
+ }
+
+ /**
+ * Remove a specific time slot
+ *
+ * @access public
+ * @param integer $slot_id
+ * @return boolean
+ */
+ public function remove($slot_id)
+ {
+ return $this->db->table(static::TABLE)->eq('id', $slot_id)->remove();
+ }
+
+ /**
+ * Validate creation
+ *
+ * @access public
+ * @param array $values Form values
+ * @return array $valid, $errors [0] = Success or not, [1] = List of errors
+ */
+ public function validateCreation(array $values)
+ {
+ $v = new Validator($values, array(
+ new Validators\Required('user_id', t('Field required')),
+ new Validators\Required('date', t('Field required')),
+ new Validators\Numeric('all_day', t('This value must be numeric')),
+ ));
+
+ return array(
+ $v->execute(),
+ $v->getErrors()
+ );
+ }
+}
diff --git a/app/Model/TimetableWeek.php b/app/Model/TimetableWeek.php
new file mode 100644
index 00000000..b22b3b7e
--- /dev/null
+++ b/app/Model/TimetableWeek.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace Model;
+
+use SimpleValidator\Validator;
+use SimpleValidator\Validators;
+
+/**
+ * Timetable Workweek
+ *
+ * @package model
+ * @author Frederic Guillot
+ */
+class TimetableWeek extends Base
+{
+ /**
+ * SQL table name
+ *
+ * @var string
+ */
+ const TABLE = 'timetable_week';
+
+ /**
+ * Get the timetable for a given user
+ *
+ * @access public
+ * @param integer $user_id User id
+ * @return array
+ */
+ public function getByUser($user_id)
+ {
+ return $this->db->table(self::TABLE)->eq('user_id', $user_id)->asc('day')->asc('start')->findAll();
+ }
+
+ /**
+ * Add a new time slot in the database
+ *
+ * @access public
+ * @param integer $user_id User id
+ * @param string $day Day of the week (ISO-8601)
+ * @param string $start Start hour (24h format)
+ * @param string $end End hour (24h format)
+ * @return boolean|integer
+ */
+ public function create($user_id, $day, $start, $end)
+ {
+ $values = array(
+ 'user_id' => $user_id,
+ 'day' => $day,
+ 'start' => $start,
+ 'end' => $end,
+ );
+
+ return $this->persist(self::TABLE, $values);
+ }
+
+ /**
+ * Remove a specific time slot
+ *
+ * @access public
+ * @param integer $slot_id
+ * @return boolean
+ */
+ public function remove($slot_id)
+ {
+ return $this->db->table(self::TABLE)->eq('id', $slot_id)->remove();
+ }
+
+ /**
+ * Validate creation
+ *
+ * @access public
+ * @param array $values Form values
+ * @return array $valid, $errors [0] = Success or not, [1] = List of errors
+ */
+ public function validateCreation(array $values)
+ {
+ $v = new Validator($values, array(
+ new Validators\Required('user_id', t('Field required')),
+ new Validators\Required('day', t('Field required')),
+ new Validators\Numeric('day', t('This value must be numeric')),
+ new Validators\Required('start', t('Field required')),
+ new Validators\Required('end', t('Field required')),
+ ));
+
+ return array(
+ $v->execute(),
+ $v->getErrors()
+ );
+ }
+}
diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php
index 1c5cdc3a..44ca7fd4 100644
--- a/app/Schema/Mysql.php
+++ b/app/Schema/Mysql.php
@@ -6,7 +6,88 @@ use PDO;
use Core\Security;
use Model\Link;
-const VERSION = 48;
+const VERSION = 51;
+
+function version_51($pdo)
+{
+ $pdo->exec('CREATE TABLE timetable_day (
+ id INT NOT NULL AUTO_INCREMENT,
+ user_id INT NOT NULL,
+ start VARCHAR(5) NOT NULL,
+ end VARCHAR(5) NOT NULL,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
+ PRIMARY KEY(id)
+ ) ENGINE=InnoDB CHARSET=utf8');
+
+ $pdo->exec('CREATE TABLE timetable_week (
+ id INT NOT NULL AUTO_INCREMENT,
+ user_id INTEGER NOT NULL,
+ day INT NOT NULL,
+ start VARCHAR(5) NOT NULL,
+ end VARCHAR(5) NOT NULL,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
+ PRIMARY KEY(id)
+ ) ENGINE=InnoDB CHARSET=utf8');
+
+ $pdo->exec('CREATE TABLE timetable_off (
+ id INT NOT NULL AUTO_INCREMENT,
+ user_id INT NOT NULL,
+ date VARCHAR(10) NOT NULL,
+ all_day TINYINT(1) DEFAULT 0,
+ start VARCHAR(5) DEFAULT 0,
+ end VARCHAR(5) DEFAULT 0,
+ comment TEXT,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
+ PRIMARY KEY(id)
+ ) ENGINE=InnoDB CHARSET=utf8');
+
+ $pdo->exec('CREATE TABLE timetable_extra (
+ id INT NOT NULL AUTO_INCREMENT,
+ user_id INT NOT NULL,
+ date VARCHAR(10) NOT NULL,
+ all_day TINYINT(1) DEFAULT 0,
+ start VARCHAR(5) DEFAULT 0,
+ end VARCHAR(5) DEFAULT 0,
+ comment TEXT,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
+ PRIMARY KEY(id)
+ ) ENGINE=InnoDB CHARSET=utf8');
+}
+
+function version_50($pdo)
+{
+ $pdo->exec("CREATE TABLE hourly_rates (
+ id INT NOT NULL AUTO_INCREMENT,
+ user_id INT NOT NULL,
+ rate FLOAT DEFAULT 0,
+ date_effective INTEGER NOT NULL,
+ currency TEXT NOT NULL,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
+ PRIMARY KEY(id)
+ ) ENGINE=InnoDB CHARSET=utf8");
+}
+
+function version_49($pdo)
+{
+ $pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1');
+
+ $task_id = 0;
+ $urq = $pdo->prepare('UPDATE subtasks SET position=? WHERE id=?');
+
+ $rq = $pdo->prepare('SELECT * FROM subtasks ORDER BY task_id, id ASC');
+ $rq->execute();
+
+ foreach ($rq->fetchAll(PDO::FETCH_ASSOC) as $subtask) {
+
+ if ($task_id != $subtask['task_id']) {
+ $position = 1;
+ $task_id = $subtask['task_id'];
+ }
+
+ $urq->execute(array($position, $subtask['id']));
+ $position++;
+ }
+}
function version_48($pdo)
{
diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php
index aa3f60f7..fe01b1e9 100644
--- a/app/Schema/Postgres.php
+++ b/app/Schema/Postgres.php
@@ -6,7 +6,83 @@ use PDO;
use Core\Security;
use Model\Link;
-const VERSION = 29;
+const VERSION = 32;
+
+function version_32($pdo)
+{
+ $pdo->exec('CREATE TABLE timetable_day (
+ "id" SERIAL PRIMARY KEY,
+ "user_id" INTEGER NOT NULL,
+ "start" VARCHAR(5) NOT NULL,
+ "end" VARCHAR(5) NOT NULL,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
+ )');
+
+ $pdo->exec('CREATE TABLE timetable_week (
+ "id" SERIAL PRIMARY KEY,
+ "user_id" INTEGER NOT NULL,
+ "day" INTEGER NOT NULL,
+ "start" VARCHAR(5) NOT NULL,
+ "end" VARCHAR(5) NOT NULL,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
+ )');
+
+ $pdo->exec('CREATE TABLE timetable_off (
+ "id" SERIAL PRIMARY KEY,
+ "user_id" INTEGER NOT NULL,
+ "date" VARCHAR(10) NOT NULL,
+ "all_day" BOOLEAN DEFAULT \'0\',
+ "start" VARCHAR(5) DEFAULT 0,
+ "end" VARCHAR(5) DEFAULT 0,
+ "comment" TEXT,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
+ )');
+
+ $pdo->exec('CREATE TABLE timetable_extra (
+ "id" SERIAL PRIMARY KEY,
+ "user_id" INTEGER NOT NULL,
+ "date" VARCHAR(10) NOT NULL,
+ "all_day" BOOLEAN DEFAULT \'0\',
+ "start" VARCHAR(5) DEFAULT 0,
+ "end" VARCHAR(5) DEFAULT 0,
+ "comment" TEXT,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
+ )');
+}
+
+function version_31($pdo)
+{
+ $pdo->exec("CREATE TABLE hourly_rates (
+ id SERIAL PRIMARY KEY,
+ user_id INTEGER NOT NULL,
+ rate REAL DEFAULT 0,
+ date_effective INTEGER NOT NULL,
+ currency TEXT NOT NULL,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
+ )");
+}
+
+function version_30($pdo)
+{
+ $pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1');
+
+ $task_id = 0;
+ $urq = $pdo->prepare('UPDATE subtasks SET position=? WHERE id=?');
+
+ $rq = $pdo->prepare('SELECT * FROM subtasks ORDER BY task_id, id ASC');
+ $rq->execute();
+
+ foreach ($rq->fetchAll(PDO::FETCH_ASSOC) as $subtask) {
+
+ if ($task_id != $subtask['task_id']) {
+ $position = 1;
+ $task_id = $subtask['task_id'];
+ }
+
+ $urq->execute(array($position, $subtask['id']));
+ $position++;
+ }
+}
function version_29($pdo)
{
diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php
index 6fc2d07e..eaa5c734 100644
--- a/app/Schema/Sqlite.php
+++ b/app/Schema/Sqlite.php
@@ -6,7 +6,85 @@ use Core\Security;
use PDO;
use Model\Link;
-const VERSION = 47;
+const VERSION = 50;
+
+function version_50($pdo)
+{
+ $pdo->exec('CREATE TABLE timetable_day (
+ "id" INTEGER PRIMARY KEY,
+ "user_id" INTEGER NOT NULL,
+ "start" TEXT NOT NULL,
+ "end" TEXT NOT NULL,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
+ )');
+
+ $pdo->exec('CREATE TABLE timetable_week (
+ "id" INTEGER PRIMARY KEY,
+ "user_id" INTEGER NOT NULL,
+ "day" INTEGER NOT NULL,
+ "start" TEXT NOT NULL,
+ "end" TEXT NOT NULL,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
+ )');
+
+ $pdo->exec('CREATE TABLE timetable_off (
+ "id" INTEGER PRIMARY KEY,
+ "user_id" INTEGER NOT NULL,
+ "date" TEXT NOT NULL,
+ "all_day" INTEGER DEFAULT 0,
+ "start" TEXT DEFAULT 0,
+ "end" TEXT DEFAULT 0,
+ "comment" TEXT,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
+ )');
+
+ $pdo->exec('CREATE TABLE timetable_extra (
+ "id" INTEGER PRIMARY KEY,
+ "user_id" INTEGER NOT NULL,
+ "date" TEXT NOT NULL,
+ "all_day" INTEGER DEFAULT 0,
+ "start" TEXT DEFAULT 0,
+ "end" TEXT DEFAULT 0,
+ "comment" TEXT,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
+ )');
+}
+
+function version_49($pdo)
+{
+ $pdo->exec("CREATE TABLE hourly_rates (
+ id INTEGER PRIMARY KEY,
+ user_id INTEGER NOT NULL,
+ rate REAL DEFAULT 0,
+ date_effective INTEGER NOT NULL,
+ currency TEXT NOT NULL,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
+ )");
+}
+
+function version_48($pdo)
+{
+ $pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1');
+
+ // Migrate all subtasks position
+
+ $task_id = 0;
+ $urq = $pdo->prepare('UPDATE subtasks SET position=? WHERE id=?');
+
+ $rq = $pdo->prepare('SELECT * FROM subtasks ORDER BY task_id, id ASC');
+ $rq->execute();
+
+ foreach ($rq->fetchAll(PDO::FETCH_ASSOC) as $subtask) {
+
+ if ($task_id != $subtask['task_id']) {
+ $position = 1;
+ $task_id = $subtask['task_id'];
+ }
+
+ $urq->execute(array($position, $subtask['id']));
+ $position++;
+ }
+}
function version_47($pdo)
{
diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php
index 213972ed..a94bb745 100644
--- a/app/ServiceProvider/ClassProvider.php
+++ b/app/ServiceProvider/ClassProvider.php
@@ -23,6 +23,7 @@ class ClassProvider implements ServiceProviderInterface
'Config',
'DateParser',
'File',
+ 'HourlyRate',
'LastLogin',
'Link',
'Notification',
@@ -48,6 +49,11 @@ class ClassProvider implements ServiceProviderInterface
'TaskPosition',
'TaskStatus',
'TaskValidator',
+ 'Timetable',
+ 'TimetableDay',
+ 'TimetableWeek',
+ 'TimetableOff',
+ 'TimetableExtra',
'TimeTracking',
'User',
'UserSession',
diff --git a/app/ServiceProvider/DatabaseProvider.php b/app/ServiceProvider/DatabaseProvider.php
index 4218f5ff..7ee35d5f 100644
--- a/app/ServiceProvider/DatabaseProvider.php
+++ b/app/ServiceProvider/DatabaseProvider.php
@@ -79,6 +79,7 @@ class DatabaseProvider implements ServiceProviderInterface
'password' => DB_PASSWORD,
'database' => DB_NAME,
'charset' => 'utf8',
+ 'port' => DB_PORT,
));
}
@@ -97,6 +98,7 @@ class DatabaseProvider implements ServiceProviderInterface
'username' => DB_USERNAME,
'password' => DB_PASSWORD,
'database' => DB_NAME,
+ 'port' => DB_PORT,
));
}
}
diff --git a/app/Template/app/projects.php b/app/Template/app/projects.php
index 92675729..b2744644 100644
--- a/app/Template/app/projects.php
+++ b/app/Template/app/projects.php
@@ -22,7 +22,7 @@
<?= $this->a($this->e($project['name']), 'board', 'show', array('project_id' => $project['id'])) ?>
<?php if (! empty($project['description'])): ?>
- <span class="column-tooltip" title='<?= $this->markdown($project['description']) ?>'>
+ <span class="column-tooltip" title='<?= $this->e($this->markdown($project['description'])) ?>'>
<i class="fa fa-info-circle"></i>
</span>
<?php endif ?>
diff --git a/app/Template/board/edit.php b/app/Template/board/edit.php
index cf0c28e6..a6df1000 100644
--- a/app/Template/board/edit.php
+++ b/app/Template/board/edit.php
@@ -13,7 +13,7 @@
<tr>
<td class="column-60"><?= $this->e($column['title']) ?>
<?php if (! empty($column['description'])): ?>
- <span class="column-tooltip" title='<?= $this->markdown($column['description']) ?>'>
+ <span class="column-tooltip" title='<?= $this->e($this->markdown($column['description'])) ?>'>
<i class="fa fa-info-circle"></i>
</span>
<?php endif ?>
diff --git a/app/Template/board/filters.php b/app/Template/board/filters.php
index fc5bfc34..2dd559b9 100644
--- a/app/Template/board/filters.php
+++ b/app/Template/board/filters.php
@@ -57,13 +57,13 @@
</span>
</li>
<li>
- <?= $this->formSelect('user_id', $users, array(), array(), array('data-placeholder="'.t('Filter by user').'"'), 'apply-filters chosen-select') ?>
+ <?= $this->formSelect('user_id', $users, array(), array(), array('data-placeholder="'.t('Filter by user').'"', 'data-notfound="'.t('No results match:').'"'), 'apply-filters chosen-select') ?>
</li>
<li>
- <?= $this->formSelect('category_id', $categories, array(), array(), array('data-placeholder="'.t('Filter by category').'"'), 'apply-filters chosen-select') ?>
+ <?= $this->formSelect('category_id', $categories, array(), array(), array('data-placeholder="'.t('Filter by category').'"', 'data-notfound="'.t('No results match:').'"'), 'apply-filters chosen-select') ?>
</li>
<li>
- <select id="more-filters" multiple data-placeholder="<?= t('More filters') ?>" class="apply-filters chosen-select hide-mobile">
+ <select id="more-filters" multiple data-placeholder="<?= t('More filters') ?>" data-notfound="<?= t('No results match:') ?>" class="apply-filters chosen-select hide-mobile">
<option value=""></option>
<option value="filter-due-date"><?= t('Filter by due date') ?></option>
<option value="filter-recent"><?= t('Filter recently updated') ?></option>
diff --git a/app/Template/board/swimlane.php b/app/Template/board/swimlane.php
index 4be92e58..744610ab 100644
--- a/app/Template/board/swimlane.php
+++ b/app/Template/board/swimlane.php
@@ -28,7 +28,7 @@
<?= $this->e($column['title']) ?>
<?php if (! empty($column['description'])): ?>
- <span class="column-tooltip pull-right" title='<?= $this->markdown($column['description']) ?>'>
+ <span class="column-tooltip pull-right" title='<?= $this->e($this->markdown($column['description'])) ?>'>
<i class="fa fa-info-circle"></i>
</span>
<?php endif ?>
diff --git a/app/Template/hourlyrate/index.php b/app/Template/hourlyrate/index.php
new file mode 100644
index 00000000..9d0b77c8
--- /dev/null
+++ b/app/Template/hourlyrate/index.php
@@ -0,0 +1,46 @@
+<div class="page-header">
+ <h2><?= t('Hourly rates') ?></h2>
+</div>
+
+<?php if (! empty($rates)): ?>
+
+<table>
+ <tr>
+ <th><?= t('Hourly rate') ?></th>
+ <th><?= t('Currency') ?></th>
+ <th><?= t('Effective date') ?></th>
+ <th><?= t('Action') ?></th>
+ </tr>
+ <?php foreach ($rates as $rate): ?>
+ <tr>
+ <td><?= n($rate['rate']) ?></td>
+ <td><?= $rate['currency'] ?></td>
+ <td><?= dt('%b %e, %Y', $rate['date_effective']) ?></td>
+ <td>
+ <?= $this->a(t('Remove'), 'hourlyrate', 'confirm', array('user_id' => $user['id'], 'rate_id' => $rate['id'])) ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+</table>
+
+<h3><?= t('Add new rate') ?></h3>
+<?php endif ?>
+
+<form method="post" action="<?= $this->u('hourlyrate', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off">
+
+ <?= $this->formHidden('user_id', $values) ?>
+ <?= $this->formCsrf() ?>
+
+ <?= $this->formLabel(t('Hourly rate'), 'rate') ?>
+ <?= $this->formText('rate', $values, $errors, array('required'), 'form-numeric') ?>
+
+ <?= $this->formLabel(t('Currency'), 'currency') ?>
+ <?= $this->formSelect('currency', $currencies_list, $values, $errors, array('required')) ?>
+
+ <?= $this->formLabel(t('Effective date'), 'date_effective') ?>
+ <?= $this->formText('date_effective', $values, $errors, array('required'), 'form-date') ?>
+
+ <div class="form-actions">
+ <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
+ </div>
+</form>
diff --git a/app/Template/hourlyrate/remove.php b/app/Template/hourlyrate/remove.php
new file mode 100644
index 00000000..7f22728e
--- /dev/null
+++ b/app/Template/hourlyrate/remove.php
@@ -0,0 +1,13 @@
+<div class="page-header">
+ <h2><?= t('Remove hourly rate') ?></h2>
+</div>
+
+<div class="confirm">
+ <p class="alert alert-info"><?= t('Do you really want to remove this hourly rate?') ?></p>
+
+ <div class="form-actions">
+ <?= $this->a(t('Yes'), 'hourlyrate', 'remove', array('user_id' => $user['id'], 'rate_id' => $rate_id), true, 'btn btn-red') ?>
+ <?= t('or') ?>
+ <?= $this->a(t('cancel'), 'hourlyrate', 'index', array('user_id' => $user['id'])) ?>
+ </div>
+</div> \ No newline at end of file
diff --git a/app/Template/layout.php b/app/Template/layout.php
index 6c88d33b..9290a21e 100644
--- a/app/Template/layout.php
+++ b/app/Template/layout.php
@@ -37,7 +37,7 @@
<nav>
<h1><?= $this->a('K<span>B</span>', 'app', 'index', array(), false, 'logo', t('Dashboard')).' '.$this->summary($this->e($title)) ?>
<?php if (! empty($description)): ?>
- <span class="column-tooltip" title='<?= $this->markdown($description) ?>'>
+ <span class="column-tooltip" title='<?= $this->e($this->markdown($description)) ?>'>
<i class="fa fa-info-circle"></i>
</span>
<?php endif ?>
@@ -45,7 +45,7 @@
<ul>
<?php if (isset($board_selector) && ! empty($board_selector)): ?>
<li>
- <select id="board-selector" data-placeholder="<?= t('Display another project') ?>" data-board-url="<?= $this->u('board', 'show', array('project_id' => 'PROJECT_ID')) ?>">
+ <select id="board-selector" data-notfound="<?= t('No results match:') ?>" data-placeholder="<?= t('Display another project') ?>" data-board-url="<?= $this->u('board', 'show', array('project_id' => 'PROJECT_ID')) ?>">
<option value=""></option>
<?php foreach($board_selector as $board_id => $board_name): ?>
<option value="<?= $board_id ?>"><?= $this->e($board_name) ?></option>
diff --git a/app/Template/project/index.php b/app/Template/project/index.php
index 9a0f641c..05a7d955 100644
--- a/app/Template/project/index.php
+++ b/app/Template/project/index.php
@@ -42,7 +42,7 @@
<?= $this->a($this->e($project['name']), 'project', 'show', array('project_id' => $project['id'])) ?>
<?php if (! empty($project['description'])): ?>
- <span class="column-tooltip" title='<?= $this->markdown($project['description']) ?>'>
+ <span class="column-tooltip" title='<?= $this->e($this->markdown($project['description'])) ?>'>
<i class="fa fa-info-circle"></i>
</span>
<?php endif ?>
diff --git a/app/Template/project/show.php b/app/Template/project/show.php
index 4ad710a8..9c6cd1a8 100644
--- a/app/Template/project/show.php
+++ b/app/Template/project/show.php
@@ -50,7 +50,7 @@
<td>
<?= $this->e($column['title']) ?>
<?php if (! empty($column['description'])): ?>
- <span class="column-tooltip" title='<?= $this->markdown($column['description']) ?>'>
+ <span class="column-tooltip" title='<?= $this->e($this->markdown($column['description'])) ?>'>
<i class="fa fa-info-circle"></i>
</span>
<?php endif ?>
diff --git a/app/Template/subtask/create.php b/app/Template/subtask/create.php
index be7fc311..8c5aae11 100644
--- a/app/Template/subtask/create.php
+++ b/app/Template/subtask/create.php
@@ -9,7 +9,7 @@
<?= $this->formHidden('task_id', $values) ?>
<?= $this->formLabel(t('Title'), 'title') ?>
- <?= $this->formText('title', $values, $errors, array('required', 'autofocus', 'maxlength="50"')) ?><br/>
+ <?= $this->formText('title', $values, $errors, array('required', 'autofocus', 'maxlength="255"')) ?><br/>
<?= $this->formLabel(t('Assignee'), 'user_id') ?>
<?= $this->formSelect('user_id', $users_list, $values, $errors) ?><br/>
diff --git a/app/Template/subtask/edit.php b/app/Template/subtask/edit.php
index f34d9532..3058ff44 100644
--- a/app/Template/subtask/edit.php
+++ b/app/Template/subtask/edit.php
@@ -10,7 +10,7 @@
<?= $this->formHidden('task_id', $values) ?>
<?= $this->formLabel(t('Title'), 'title') ?>
- <?= $this->formText('title', $values, $errors, array('required', 'autofocus', 'maxlength="50"')) ?><br/>
+ <?= $this->formText('title', $values, $errors, array('required', 'autofocus', 'maxlength="255"')) ?><br/>
<?= $this->formLabel(t('Assignee'), 'user_id') ?>
<?= $this->formSelect('user_id', $users_list, $values, $errors) ?><br/>
diff --git a/app/Template/subtask/show.php b/app/Template/subtask/show.php
index 1d55d1ee..c7ac652a 100644
--- a/app/Template/subtask/show.php
+++ b/app/Template/subtask/show.php
@@ -20,7 +20,7 @@
<?php if (! isset($not_editable)): ?>
<?= $this->toggleSubtaskStatus($subtask, 'task') ?>
<?php else: ?>
- <?= $this->render('subtask/icons', array('subtask' => $subtask)) . $this->e($subtask['status_name']) ?>
+ <?= $this->render('subtask/icons', array('subtask' => $subtask)) . $this->e($subtask['title']) ?>
<?php endif ?>
</td>
<td>
@@ -40,6 +40,16 @@
<?php if (! isset($not_editable)): ?>
<td>
<ul>
+ <?php if ($subtask['position'] > 1): ?>
+ <li>
+ <?= $this->a(t('Move Up'), 'subtask', 'movePosition', array('project_id' => $project['id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'], 'direction' => 'up'), true) ?>
+ </li>
+ <?php endif ?>
+ <?php if ($subtask['position'] != 0 && $subtask['position'] != count($subtasks)): ?>
+ <li>
+ <?= $this->a(t('Move Down'), 'subtask', 'movePosition', array('project_id' => $project['id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'], 'direction' => 'down'), true) ?>
+ </li>
+ <?php endif ?>
<li>
<?= $this->a(t('Edit'), 'subtask', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>
</li>
diff --git a/app/Template/task/show.php b/app/Template/task/show.php
index 45485e53..ec930715 100644
--- a/app/Template/task/show.php
+++ b/app/Template/task/show.php
@@ -2,7 +2,7 @@
<?= $this->render('task/time', array('task' => $task, 'values' => $values, 'date_format' => $date_format, 'date_formats' => $date_formats)) ?>
<?= $this->render('task/show_description', array('task' => $task)) ?>
<?= $this->render('tasklink/show', array('task' => $task, 'links' => $links)) ?>
-<?= $this->render('subtask/show', array('task' => $task, 'subtasks' => $subtasks)) ?>
+<?= $this->render('subtask/show', array('task' => $task, 'subtasks' => $subtasks, 'project' => $project)) ?>
<?= $this->render('task/timesheet', array('task' => $task)) ?>
<?= $this->render('file/show', array('task' => $task, 'files' => $files, 'images' => $images)) ?>
<?= $this->render('task/comments', array('task' => $task, 'comments' => $comments, 'project' => $project)) ?>
diff --git a/app/Template/timetable/index.php b/app/Template/timetable/index.php
new file mode 100644
index 00000000..27cbe39c
--- /dev/null
+++ b/app/Template/timetable/index.php
@@ -0,0 +1,44 @@
+<div class="page-header">
+ <h2><?= t('Timetable') ?></h2>
+ <ul>
+ <li><?= $this->a(t('Day timetable'), 'timetableday', 'index', array('user_id' => $user['id'])) ?></li>
+ <li><?= $this->a(t('Week timetable'), 'timetableweek', 'index', array('user_id' => $user['id'])) ?></li>
+ <li><?= $this->a(t('Time off timetable'), 'timetableoff', 'index', array('user_id' => $user['id'])) ?></li>
+ <li><?= $this->a(t('Overtime timetable'), 'timetableextra', 'index', array('user_id' => $user['id'])) ?></li>
+ </ul>
+</div>
+
+<form method="get" action="?" autocomplete="off" class="form-inline">
+
+ <?= $this->formHidden('controller', $values) ?>
+ <?= $this->formHidden('action', $values) ?>
+ <?= $this->formHidden('user_id', $values) ?>
+
+ <?= $this->formLabel(t('From'), 'from') ?>
+ <?= $this->formText('from', $values, array(), array(), 'form-date') ?>
+
+ <?= $this->formLabel(t('To'), 'to') ?>
+ <?= $this->formText('to', $values, array(), array(), 'form-date') ?>
+
+ <input type="submit" value="<?= t('Execute') ?>" class="btn btn-blue"/>
+</form>
+
+<?php if (! empty($timetable)): ?>
+<hr/>
+<h3><?= t('Work timetable') ?></h3>
+<table class="table-fixed table-stripped">
+ <tr>
+ <th><?= t('Day') ?></th>
+ <th><?= t('Start') ?></th>
+ <th><?= t('End') ?></th>
+ </tr>
+ <?php foreach ($timetable as $slot): ?>
+ <tr>
+ <td><?= dt('%B %e, %Y', $slot[0]->getTimestamp()) ?></td>
+ <td><?= dt('%k:%M %p', $slot[0]->getTimestamp()) ?></td>
+ <td><?= dt('%k:%M %p', $slot[1]->getTimestamp()) ?></td>
+ </tr>
+ <?php endforeach ?>
+</table>
+
+<?php endif ?> \ No newline at end of file
diff --git a/app/Template/timetable_day/index.php b/app/Template/timetable_day/index.php
new file mode 100644
index 00000000..50aca602
--- /dev/null
+++ b/app/Template/timetable_day/index.php
@@ -0,0 +1,45 @@
+<div class="page-header">
+ <h2><?= t('Day timetable') ?></h2>
+</div>
+
+<?php if (! empty($timetable)): ?>
+
+<table class="table-fixed table-stripped">
+ <tr>
+ <th><?= t('Start time') ?></th>
+ <th><?= t('End time') ?></th>
+ <th><?= t('Action') ?></th>
+ </tr>
+ <?php foreach ($timetable as $slot): ?>
+ <tr>
+ <td><?= $slot['start'] ?></td>
+ <td><?= $slot['end'] ?></td>
+ <td>
+ <?= $this->a(t('Remove'), 'timetableday', 'confirm', array('user_id' => $user['id'], 'slot_id' => $slot['id'])) ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+</table>
+
+<h3><?= t('Add new time slot') ?></h3>
+<?php endif ?>
+
+<form method="post" action="<?= $this->u('timetableday', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off">
+
+ <?= $this->formHidden('user_id', $values) ?>
+ <?= $this->formCsrf() ?>
+
+ <?= $this->formLabel(t('Start time'), 'start') ?>
+ <?= $this->formSelect('start', $this->getDayHours(), $values, $errors) ?>
+
+ <?= $this->formLabel(t('End time'), 'end') ?>
+ <?= $this->formSelect('end', $this->getDayHours(), $values, $errors) ?>
+
+ <div class="form-actions">
+ <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
+ </div>
+</form>
+
+<p class="alert alert-info">
+ <?= t('This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.') ?>
+</p> \ No newline at end of file
diff --git a/app/Template/timetable_day/remove.php b/app/Template/timetable_day/remove.php
new file mode 100644
index 00000000..b3ee8775
--- /dev/null
+++ b/app/Template/timetable_day/remove.php
@@ -0,0 +1,13 @@
+<div class="page-header">
+ <h2><?= t('Remove time slot') ?></h2>
+</div>
+
+<div class="confirm">
+ <p class="alert alert-info"><?= t('Do you really want to remove this time slot?') ?></p>
+
+ <div class="form-actions">
+ <?= $this->a(t('Yes'), 'timetableday', 'remove', array('user_id' => $user['id'], 'slot_id' => $slot_id), true, 'btn btn-red') ?>
+ <?= t('or') ?>
+ <?= $this->a(t('cancel'), 'timetableday', 'index', array('user_id' => $user['id'])) ?>
+ </div>
+</div> \ No newline at end of file
diff --git a/app/Template/timetable_extra/index.php b/app/Template/timetable_extra/index.php
new file mode 100644
index 00000000..a0a55bec
--- /dev/null
+++ b/app/Template/timetable_extra/index.php
@@ -0,0 +1,56 @@
+<div class="page-header">
+ <h2><?= t('Overtime timetable') ?></h2>
+</div>
+
+<?php if (! $paginator->isEmpty()): ?>
+
+<table class="table-fixed table-stripped">
+ <tr>
+ <th><?= $paginator->order(t('Day'), 'Day') ?></th>
+ <th><?= $paginator->order(t('All day'), 'all_day') ?></th>
+ <th><?= $paginator->order(t('Start time'), 'start') ?></th>
+ <th><?= $paginator->order(t('End time'), 'end') ?></th>
+ <th class="column-40"><?= t('Comment') ?></th>
+ <th><?= t('Action') ?></th>
+ </tr>
+ <?php foreach ($paginator->getCollection() as $slot): ?>
+ <tr>
+ <td><?= $slot['date'] ?></td>
+ <td><?= $slot['all_day'] == 1 ? t('Yes') : t('No') ?></td>
+ <td><?= $slot['start'] ?></td>
+ <td><?= $slot['end'] ?></td>
+ <td><?= $this->e($slot['comment']) ?></td>
+ <td>
+ <?= $this->a(t('Remove'), 'timetableextra', 'confirm', array('user_id' => $user['id'], 'slot_id' => $slot['id'])) ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+</table>
+
+<?= $paginator ?>
+
+<?php endif ?>
+
+<form method="post" action="<?= $this->u('timetableextra', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off">
+
+ <?= $this->formHidden('user_id', $values) ?>
+ <?= $this->formCsrf() ?>
+
+ <?= $this->formLabel(t('Day'), 'date') ?>
+ <?= $this->formText('date', $values, $errors, array('required'), 'form-date') ?>
+
+ <?= $this->formCheckbox('all_day', t('All day'), 1) ?>
+
+ <?= $this->formLabel(t('Start time'), 'start') ?>
+ <?= $this->formSelect('start', $this->getDayHours(), $values, $errors) ?>
+
+ <?= $this->formLabel(t('End time'), 'end') ?>
+ <?= $this->formSelect('end', $this->getDayHours(), $values, $errors) ?>
+
+ <?= $this->formLabel(t('Comment'), 'comment') ?>
+ <?= $this->formText('comment', $values, $errors) ?>
+
+ <div class="form-actions">
+ <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
+ </div>
+</form> \ No newline at end of file
diff --git a/app/Template/timetable_extra/remove.php b/app/Template/timetable_extra/remove.php
new file mode 100644
index 00000000..d8dc5b3b
--- /dev/null
+++ b/app/Template/timetable_extra/remove.php
@@ -0,0 +1,13 @@
+<div class="page-header">
+ <h2><?= t('Remove time slot') ?></h2>
+</div>
+
+<div class="confirm">
+ <p class="alert alert-info"><?= t('Do you really want to remove this time slot?') ?></p>
+
+ <div class="form-actions">
+ <?= $this->a(t('Yes'), 'timetableextra', 'remove', array('user_id' => $user['id'], 'slot_id' => $slot_id), true, 'btn btn-red') ?>
+ <?= t('or') ?>
+ <?= $this->a(t('cancel'), 'timetableextra', 'index', array('user_id' => $user['id'])) ?>
+ </div>
+</div> \ No newline at end of file
diff --git a/app/Template/timetable_off/index.php b/app/Template/timetable_off/index.php
new file mode 100644
index 00000000..f35d331e
--- /dev/null
+++ b/app/Template/timetable_off/index.php
@@ -0,0 +1,56 @@
+<div class="page-header">
+ <h2><?= t('Time off timetable') ?></h2>
+</div>
+
+<?php if (! $paginator->isEmpty()): ?>
+
+<table class="table-fixed table-stripped">
+ <tr>
+ <th><?= $paginator->order(t('Day'), 'Day') ?></th>
+ <th><?= $paginator->order(t('All day'), 'all_day') ?></th>
+ <th><?= $paginator->order(t('Start time'), 'start') ?></th>
+ <th><?= $paginator->order(t('End time'), 'end') ?></th>
+ <th class="column-40"><?= t('Comment') ?></th>
+ <th><?= t('Action') ?></th>
+ </tr>
+ <?php foreach ($paginator->getCollection() as $slot): ?>
+ <tr>
+ <td><?= $slot['date'] ?></td>
+ <td><?= $slot['all_day'] == 1 ? t('Yes') : t('No') ?></td>
+ <td><?= $slot['start'] ?></td>
+ <td><?= $slot['end'] ?></td>
+ <td><?= $this->e($slot['comment']) ?></td>
+ <td>
+ <?= $this->a(t('Remove'), 'timetableoff', 'confirm', array('user_id' => $user['id'], 'slot_id' => $slot['id'])) ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+</table>
+
+<?= $paginator ?>
+
+<?php endif ?>
+
+<form method="post" action="<?= $this->u('timetableoff', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off">
+
+ <?= $this->formHidden('user_id', $values) ?>
+ <?= $this->formCsrf() ?>
+
+ <?= $this->formLabel(t('Day'), 'date') ?>
+ <?= $this->formText('date', $values, $errors, array('required'), 'form-date') ?>
+
+ <?= $this->formCheckbox('all_day', t('All day'), 1) ?>
+
+ <?= $this->formLabel(t('Start time'), 'start') ?>
+ <?= $this->formSelect('start', $this->getDayHours(), $values, $errors) ?>
+
+ <?= $this->formLabel(t('End time'), 'end') ?>
+ <?= $this->formSelect('end', $this->getDayHours(), $values, $errors) ?>
+
+ <?= $this->formLabel(t('Comment'), 'comment') ?>
+ <?= $this->formText('comment', $values, $errors) ?>
+
+ <div class="form-actions">
+ <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
+ </div>
+</form> \ No newline at end of file
diff --git a/app/Template/timetable_off/remove.php b/app/Template/timetable_off/remove.php
new file mode 100644
index 00000000..64863781
--- /dev/null
+++ b/app/Template/timetable_off/remove.php
@@ -0,0 +1,13 @@
+<div class="page-header">
+ <h2><?= t('Remove time slot') ?></h2>
+</div>
+
+<div class="confirm">
+ <p class="alert alert-info"><?= t('Do you really want to remove this time slot?') ?></p>
+
+ <div class="form-actions">
+ <?= $this->a(t('Yes'), 'timetableoff', 'remove', array('user_id' => $user['id'], 'slot_id' => $slot_id), true, 'btn btn-red') ?>
+ <?= t('or') ?>
+ <?= $this->a(t('cancel'), 'timetableoff', 'index', array('user_id' => $user['id'])) ?>
+ </div>
+</div> \ No newline at end of file
diff --git a/app/Template/timetable_week/index.php b/app/Template/timetable_week/index.php
new file mode 100644
index 00000000..8fb51909
--- /dev/null
+++ b/app/Template/timetable_week/index.php
@@ -0,0 +1,46 @@
+<div class="page-header">
+ <h2><?= t('Week timetable') ?></h2>
+</div>
+
+<?php if (! empty($timetable)): ?>
+
+<table class="table-fixed table-stripped">
+ <tr>
+ <th><?= t('Day') ?></th>
+ <th><?= t('Start time') ?></th>
+ <th><?= t('End time') ?></th>
+ <th><?= t('Action') ?></th>
+ </tr>
+ <?php foreach ($timetable as $slot): ?>
+ <tr>
+ <td><?= $this->getWeekDay($slot['day']) ?></td>
+ <td><?= $slot['start'] ?></td>
+ <td><?= $slot['end'] ?></td>
+ <td>
+ <?= $this->a(t('Remove'), 'timetableweek', 'confirm', array('user_id' => $user['id'], 'slot_id' => $slot['id'])) ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+</table>
+
+<h3><?= t('Add new time slot') ?></h3>
+<?php endif ?>
+
+<form method="post" action="<?= $this->u('timetableweek', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off">
+
+ <?= $this->formHidden('user_id', $values) ?>
+ <?= $this->formCsrf() ?>
+
+ <?= $this->formLabel(t('Day'), 'day') ?>
+ <?= $this->formSelect('day', $this->getWeekDays(), $values, $errors) ?>
+
+ <?= $this->formLabel(t('Start time'), 'start') ?>
+ <?= $this->formSelect('start', $this->getDayHours(), $values, $errors) ?>
+
+ <?= $this->formLabel(t('End time'), 'end') ?>
+ <?= $this->formSelect('end', $this->getDayHours(), $values, $errors) ?>
+
+ <div class="form-actions">
+ <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
+ </div>
+</form> \ No newline at end of file
diff --git a/app/Template/timetable_week/remove.php b/app/Template/timetable_week/remove.php
new file mode 100644
index 00000000..f8eb2bbe
--- /dev/null
+++ b/app/Template/timetable_week/remove.php
@@ -0,0 +1,13 @@
+<div class="page-header">
+ <h2><?= t('Remove time slot') ?></h2>
+</div>
+
+<div class="confirm">
+ <p class="alert alert-info"><?= t('Do you really want to remove this time slot?') ?></p>
+
+ <div class="form-actions">
+ <?= $this->a(t('Yes'), 'timetableweek', 'remove', array('user_id' => $user['id'], 'slot_id' => $slot_id), true, 'btn btn-red') ?>
+ <?= t('or') ?>
+ <?= $this->a(t('cancel'), 'timetableweek', 'index', array('user_id' => $user['id'])) ?>
+ </div>
+</div> \ No newline at end of file
diff --git a/app/Template/user/sidebar.php b/app/Template/user/sidebar.php
index e41851a9..1af10c1d 100644
--- a/app/Template/user/sidebar.php
+++ b/app/Template/user/sidebar.php
@@ -32,7 +32,7 @@
<?= $this->a(t('Time tracking'), 'user', 'timesheet', array('user_id' => $user['id'])) ?>
</li>
<?php endif ?>
-
+
<?php if ($this->userSession->isAdmin()): ?>
<li>
<?= $this->a(t('User dashboard'), 'app', 'dashboard', array('user_id' => $user['id'])) ?>
@@ -40,6 +40,12 @@
<li>
<?= $this->a(t('User calendar'), 'user', 'calendar', array('user_id' => $user['id'])) ?>
</li>
+ <li>
+ <?= $this->a(t('Hourly rates'), 'hourlyrate', 'index', array('user_id' => $user['id'])) ?>
+ </li>
+ <li>
+ <?= $this->a(t('Manage timetable'), 'timetable', 'index', array('user_id' => $user['id'])) ?>
+ </li>
<?php endif ?>
<?php if ($this->userSession->isAdmin() && ! $this->userSession->isCurrentUser($user['id'])): ?>
diff --git a/app/constants.php b/app/constants.php
index 39ab5470..63902b4d 100644
--- a/app/constants.php
+++ b/app/constants.php
@@ -21,6 +21,7 @@ defined('DB_USERNAME') or define('DB_USERNAME', 'root');
defined('DB_PASSWORD') or define('DB_PASSWORD', '');
defined('DB_HOSTNAME') or define('DB_HOSTNAME', 'localhost');
defined('DB_NAME') or define('DB_NAME', 'kanboard');
+defined('DB_PORT') or define('DB_PORT', null);
// LDAP configuration
defined('LDAP_AUTH') or define('LDAP_AUTH', false);
diff --git a/assets/css/app.css b/assets/css/app.css
index 57ec7f9c..4d2f4075 100644
--- a/assets/css/app.css
+++ b/assets/css/app.css
@@ -152,6 +152,10 @@ th a:hover {
text-overflow: ellipsis;
}
+.table-stripped tr:nth-child(odd) td {
+ background: #fefefe;
+}
+
.column-5 {
width: 5%;
}
diff --git a/assets/css/src/table.css b/assets/css/src/table.css
index 9bc0c712..ea788623 100644
--- a/assets/css/src/table.css
+++ b/assets/css/src/table.css
@@ -58,6 +58,10 @@ th a:hover {
text-overflow: ellipsis;
}
+.table-stripped tr:nth-child(odd) td {
+ background: #fefefe;
+}
+
.column-5 {
width: 5%;
}
diff --git a/assets/js/app.js b/assets/js/app.js
index 0dd38eeb..6e6f7f34 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -140,7 +140,7 @@ var Kanboard=function(){jQuery(document).ready(function(){Kanboard.Init()});retu
$("#popover-content").click(function(a){a.stopPropagation()});$(".close-popover").click(function(a){a.preventDefault();$("#popover-container").remove()});Mousetrap.bind("esc",function(){$("#popover-container").remove()});c&&c()})},IsVisible:function(){var a="";"undefined"!==typeof document.hidden?a="visibilityState":"undefined"!==typeof document.mozHidden?a="mozVisibilityState":"undefined"!==typeof document.msHidden?a="msVisibilityState":"undefined"!==typeof document.webkitHidden&&(a="webkitVisibilityState");
return""!=a?"visible"==document[a]:!0},SetStorageItem:function(a,c){"undefined"!==typeof Storage&&localStorage.setItem(a,c)},GetStorageItem:function(a){return"undefined"!==typeof Storage?localStorage.getItem(a):""},MarkdownPreview:function(a){a.preventDefault();var c=$(this),b=$(this).closest("ul"),d=$(".write-area"),e=$(".preview-area"),f=$("textarea");$.ajax({url:"?controller=app&action=preview",contentType:"application/json",type:"POST",processData:!1,dataType:"html",data:JSON.stringify({text:f.val()})}).done(function(a){b.find("li").removeClass("form-tab-selected");
c.parent().addClass("form-tab-selected");e.find(".markdown").html(a);e.css("height",f.css("height"));e.css("width",f.css("width"));d.hide();e.show()})},MarkdownWriter:function(a){a.preventDefault();$(this).closest("ul").find("li").removeClass("form-tab-selected");$(this).parent().addClass("form-tab-selected");$(".write-area").show();$(".preview-area").hide()},CheckSession:function(){$(".form-login").length||$.ajax({cache:!1,url:$("body").data("status-url"),statusCode:{401:function(){window.location=
-$("body").data("login-url")}}})},Init:function(){$("#board-selector").chosen({width:180});$("#board-selector").change(function(){window.location=$(this).attr("data-board-url").replace(/PROJECT_ID/g,$(this).val())});window.setInterval(Kanboard.CheckSession,6E4);Mousetrap.bindGlobal("mod+enter",function(){$("form").submit()});Mousetrap.bind("b",function(a){a.preventDefault();$("#board-selector").trigger("chosen:open")});$(".column-tooltip").tooltip({content:function(){return'<div class="markdown">'+
+$("body").data("login-url")}}})},Init:function(){$("#board-selector").chosen({width:180,no_results_text:$("#board-selector").data("notfound")});$("#board-selector").change(function(){window.location=$(this).attr("data-board-url").replace(/PROJECT_ID/g,$(this).val())});window.setInterval(Kanboard.CheckSession,6E4);Mousetrap.bindGlobal("mod+enter",function(){$("form").submit()});Mousetrap.bind("b",function(a){a.preventDefault();$("#board-selector").trigger("chosen:open")});$(".column-tooltip").tooltip({content:function(){return'<div class="markdown">'+
$(this).attr("title")+"</div>"},position:{my:"left-20 top",at:"center bottom+9",using:function(a,c){$(this).css(a);var b=c.target.left+c.target.width/2-c.element.left-20;$("<div>").addClass("tooltip-arrow").addClass(c.vertical).addClass(0==b?"align-left":"align-right").appendTo(this)}}});$.datepicker.setDefaults($.datepicker.regional[$("body").data("js-lang")]);Kanboard.InitAfterAjax()},InitAfterAjax:function(){$(document).on("click",".popover",Kanboard.Popover);$(".form-date").datepicker({showOtherMonths:!0,
selectOtherMonths:!0,dateFormat:"yy-mm-dd",constrainInput:!1});$("#markdown-preview").click(Kanboard.MarkdownPreview);$("#markdown-write").click(Kanboard.MarkdownWriter);$(".auto-select").focus(function(){$(this).select()});$(".dropit-submenu").hide();$(".dropdown").not(".dropit").dropit({triggerParentEl:"span"})}}}();
Kanboard.Board=function(){function a(a){a.preventDefault();a.stopPropagation();Kanboard.Popover(a,Kanboard.InitAfterAjax)}function c(){Mousetrap.bind("n",function(){Kanboard.OpenPopover($("#board").data("task-creation-url"),Kanboard.InitAfterAjax)});Mousetrap.bind("s",function(){"expanded"===(Kanboard.GetStorageItem(d())||"expanded")?(e(),Kanboard.SetStorageItem(d(),"collapsed")):(f(),Kanboard.SetStorageItem(d(),"expanded"))});Mousetrap.bind("c",function(){p()})}function b(){$(".filter-expand-link").click(function(a){a.preventDefault();
@@ -151,10 +151,10 @@ c.of=$(b);a.position(c);$("#tooltip-subtasks a").not(".popover").click(function(
var b=this;setTimeout(function(){$(".ui-tooltip:hover").length||$(b).tooltip("close")},100)});var b=parseInt($("#board").attr("data-check-interval"));0<b&&(m=window.setInterval(n,1E3*b))}function q(a,b,c,d){clearInterval(m);$.ajax({cache:!1,url:$("#board").attr("data-save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:a,column_id:b,swimlane_id:d,position:c}),success:function(a){$("#board-container").remove();$("#main").append(a);Kanboard.InitAfterAjax();
k();h();g();l()}})}function n(){Kanboard.IsVisible()&&$.ajax({cache:!1,url:$("#board").attr("data-check-url"),statusCode:{200:function(a){$("#board-container").remove();$("#main").append(a);Kanboard.InitAfterAjax();clearInterval(m);k();h();g();l()}}})}function h(){var a=$("#form-user_id").val(),b=$("#form-category_id").val(),c=$("#more-filters option[value=filter-due-date]").is(":selected"),d=$("#more-filters option[value=filter-recent]").is(":selected"),e=$("#board").data("project-id");$("[data-task-id]").each(function(e,
g){var k=g.getAttribute("data-owner-id"),f=g.getAttribute("data-due-date"),n=g.getAttribute("data-category-id"),h=$(g).hasClass("task-board-recent");g.style.display=k!=a&&-1!=a?"none":"block";!c||""!=f&&"0"!=f||(g.style.display="none");n!=b&&-1!=b&&(g.style.display="none");d&&!h&&(g.style.display="none")});Kanboard.SetStorageItem("board_filter_"+e+"_form-user_id",a);Kanboard.SetStorageItem("board_filter_"+e+"_form-category_id",b);Kanboard.SetStorageItem("board_filter_"+e+"_filter-due-date",~~c);Kanboard.SetStorageItem("board_filter_"+
-e+"_filter-recent",~~d)}function t(){var a=$("#board").data("project-id");$("#form-user_id").chosen({width:"180px"});$("#form-category_id").chosen({width:"200px"});$("#more-filters").chosen({width:"30%"});$(".apply-filters").change(function(a){h()});$("#form-user_id").val(Kanboard.GetStorageItem("board_filter_"+a+"_form-user_id")||-1);$("#form-user_id").trigger("chosen:updated");$("#form-category_id").val(Kanboard.GetStorageItem("board_filter_"+a+"_form-category_id")||-1);$("#form-category_id").trigger("chosen:updated");
-+Kanboard.GetStorageItem("board_filter_"+a+"_filter-due-date")&&$("#more-filters option[value=filter-due-date]").attr("selected",!0);+Kanboard.GetStorageItem("board_filter_"+a+"_filter-recent")&&$("#more-filters option[value=filter-recent]").attr("selected",!0);$("#more-filters").trigger("chosen:updated");h()}function u(){jQuery(document).on("click",".filter-toggle-scrolling",function(a){a.preventDefault();p()});l()}function p(){var a=Kanboard.GetStorageItem("horizontal_scroll")||1;Kanboard.SetStorageItem("horizontal_scroll",
-0==a?1:0);l()}function l(){0==Kanboard.GetStorageItem("horizontal_scroll")?($(".filter-wide").show(),$(".filter-compact").hide(),$("#board-container").removeClass("board-container-wide").addClass("board-container-compact"),$("#board th,#board td").removeClass("board-column-wide").addClass("board-column-compact")):($(".filter-wide").hide(),$(".filter-compact").show(),$("#board-container").removeClass("board-container-compact").addClass("board-container-wide"),$("#board th,#board td").removeClass("board-column-compact").addClass("board-column-wide"))}
-var m=null;jQuery(document).ready(function(){Kanboard.Exists("board")&&(k(),t(),b(),u(),c())})}();
+e+"_filter-recent",~~d)}function t(){var a=$("#board").data("project-id");$("#form-user_id").chosen({width:"180px",no_results_text:$("#form-user_id").data("notfound")});$("#form-category_id").chosen({width:"200px",no_results_text:$("#form-category_id").data("notfound")});$("#more-filters").chosen({width:"30%",no_results_text:$("#more-filters").data("notfound")});$(".apply-filters").change(function(a){h()});$("#form-user_id").val(Kanboard.GetStorageItem("board_filter_"+a+"_form-user_id")||-1);$("#form-user_id").trigger("chosen:updated");
+$("#form-category_id").val(Kanboard.GetStorageItem("board_filter_"+a+"_form-category_id")||-1);$("#form-category_id").trigger("chosen:updated");+Kanboard.GetStorageItem("board_filter_"+a+"_filter-due-date")&&$("#more-filters option[value=filter-due-date]").attr("selected",!0);+Kanboard.GetStorageItem("board_filter_"+a+"_filter-recent")&&$("#more-filters option[value=filter-recent]").attr("selected",!0);$("#more-filters").trigger("chosen:updated");h()}function u(){jQuery(document).on("click",".filter-toggle-scrolling",
+function(a){a.preventDefault();p()});l()}function p(){var a=Kanboard.GetStorageItem("horizontal_scroll")||1;Kanboard.SetStorageItem("horizontal_scroll",0==a?1:0);l()}function l(){0==Kanboard.GetStorageItem("horizontal_scroll")?($(".filter-wide").show(),$(".filter-compact").hide(),$("#board-container").removeClass("board-container-wide").addClass("board-container-compact"),$("#board th,#board td").removeClass("board-column-wide").addClass("board-column-compact")):($(".filter-wide").hide(),$(".filter-compact").show(),
+$("#board-container").removeClass("board-container-compact").addClass("board-container-wide"),$("#board th,#board td").removeClass("board-column-compact").addClass("board-column-wide"))}var m=null;jQuery(document).ready(function(){Kanboard.Exists("board")&&(k(),t(),b(),u(),c())})}();
Kanboard.Calendar=function(){function a(a){var b=$("#calendar").data("save-url")||$("#user-calendar").data("save-url");$.ajax({cache:!1,url:b,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:a.id,date_due:a.start.format()})})}function c(){var a=$("#user-calendar"),b=a.data("check-url"),c={start:a.fullCalendar("getView").start.format(),end:a.fullCalendar("getView").end.format(),user_id:a.data("user-id")},d;for(d in c)b+="&"+d+"="+c[d];$.getJSON(b,function(b){a.fullCalendar("removeEvents");
a.fullCalendar("addEventSource",b);a.fullCalendar("rerenderEvents")})}function b(a){var b=$("#calendar"),c=b.data("check-url"),d={start:b.fullCalendar("getView").start.format(),end:b.fullCalendar("getView").end.format()};jQuery.extend(d,a);for(var e in d)c+="&"+e+"="+d[e];$.getJSON(c,function(a){b.fullCalendar("removeEvents");b.fullCalendar("addEventSource",a);b.fullCalendar("rerenderEvents")})}function d(){var a=Kanboard.GetStorageItem(f);if(""!==a){var a=JSON.parse(a),c;for(c in a)$("select[name="+
c+"]").val(a[c])}b(a||{});$(".calendar-filter").change(e)}function e(){var a={};$(".calendar-filter").each(function(){a[$(this).attr("name")]=$(this).val()});Kanboard.SetStorageItem(f,JSON.stringify(a));b(a)}var f="";jQuery(document).ready(function(){Kanboard.Exists("calendar")?(f="calendar_filters_"+$("#calendar").data("project-id"),$("#calendar").fullCalendar({lang:$("body").data("js-lang"),editable:!0,eventLimit:!0,defaultView:"month",header:{left:"prev,next today",center:"title",right:"month,agendaWeek,agendaDay"},
@@ -169,4 +169,4 @@ b=$(this).data("swimlane-id");if(-1<c().indexOf(b)){var d="hidden_swimlanes_"+$(
JSON.stringify(e)),a(b)})}();
Kanboard.Dashboard=function(){function a(){0<$(".dashboard-right-column > div:visible").size()?$(".dashboard-left-column").removeClass("dashboard-single-column"):$(".dashboard-left-column").addClass("dashboard-single-column");0<$(".dashboard-left-column > div:visible").size()?$(".dashboard-right-column").removeClass("dashboard-single-column"):$(".dashboard-right-column").addClass("dashboard-single-column")}jQuery(document).ready(function(){var c=Kanboard.GetStorageItem("dashboard_view");if(c){var c=
JSON.parse(c),b;for(b in c)$("#dashboard-"+b).toggle(c[b]);a()}});jQuery(document).on("click",".dashboard-toggle",function(c){c.preventDefault();$("#dashboard-"+$(this).data("toggle")).toggle();a();c=["projects","tasks","subtasks","activities","calendar"];for(var b={},d=0;d<c.length;d++)b[c[d]]=$("#dashboard-"+c[d]).is(":visible");Kanboard.SetStorageItem("dashboard_view",JSON.stringify(b))})}();
-(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){(e.defineLocale||e.lang).call(e,"da",{months:"januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"søn_man_tir_ons_tor_fre_lør".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd [d.] D. MMMM YYYY LT"},calendar:{sameDay:"[I dag kl.] LT",nextDay:"[I morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[I går kl.] LT",lastWeek:"[sidste] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"få sekunder",m:"et minut",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dage",M:"en måned",MM:"%d måneder",y:"et år",yy:"%d år"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),t.fullCalendar.datepickerLang("da","da",{closeText:"Luk",prevText:"&#x3C;Forrige",nextText:"Næste&#x3E;",currentText:"Idag",monthNames:["Januar","Februar","Marts","April","Maj","Juni","Juli","August","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNames:["Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag"],dayNamesShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør"],dayNamesMin:["Sø","Ma","Ti","On","To","Fr","Lø"],weekHeader:"Uge",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("da",{defaultButtonText:{month:"Måned",week:"Uge",day:"Dag",list:"Agenda"},allDayText:"Hele dagen",eventLimitText:"flere"})});(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){function n(t,e,n){var i={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[t+" Tage",t+" Tagen"],M:["ein Monat","einem Monat"],MM:[t+" Monate",t+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[t+" Jahre",t+" Jahren"]};return e?i[n][0]:i[n][1]}(e.defineLocale||e.lang).call(e,"de",{months:"Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Heute um] LT [Uhr]",sameElse:"L",nextDay:"[Morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[Gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:n,mm:"%d Minuten",h:n,hh:"%d Stunden",d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),t.fullCalendar.datepickerLang("de","de",{closeText:"Schließen",prevText:"&#x3C;Zurück",nextText:"Vor&#x3E;",currentText:"Heute",monthNames:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],dayNamesShort:["So","Mo","Di","Mi","Do","Fr","Sa"],dayNamesMin:["So","Mo","Di","Mi","Do","Fr","Sa"],weekHeader:"KW",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("de",{defaultButtonText:{month:"Monat",week:"Woche",day:"Tag",list:"Terminübersicht"},allDayText:"Ganztägig",eventLimitText:function(t){return"+ weitere "+t}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){var n="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),i="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_");(t.defineLocale||t.lang).call(t,"es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,t){return/-MMM-/.test(t)?i[e.month()]:n[e.month()]},weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"Do_Lu_Ma_Mi_Ju_Vi_Sá".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY LT",LLLL:"dddd, D [de] MMMM [de] YYYY LT"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("es","es",{closeText:"Cerrar",prevText:"&#x3C;Ant",nextText:"Sig&#x3E;",currentText:"Hoy",monthNames:["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],monthNamesShort:["ene","feb","mar","abr","may","jun","jul","ago","sep","oct","nov","dic"],dayNames:["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],dayNamesShort:["dom","lun","mar","mié","jue","vie","sáb"],dayNamesMin:["D","L","M","X","J","V","S"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("es",{defaultButtonText:{month:"Mes",week:"Semana",day:"Día",list:"Agenda"},allDayHtml:"Todo<br/>el día",eventLimitText:"más"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t,n,r){var s="";switch(n){case"s":return r?"muutaman sekunnin":"muutama sekunti";case"m":return r?"minuutin":"minuutti";case"mm":s=r?"minuutin":"minuuttia";break;case"h":return r?"tunnin":"tunti";case"hh":s=r?"tunnin":"tuntia";break;case"d":return r?"päivän":"päivä";case"dd":s=r?"päivän":"päivää";break;case"M":return r?"kuukauden":"kuukausi";case"MM":s=r?"kuukauden":"kuukautta";break;case"y":return r?"vuoden":"vuosi";case"yy":s=r?"vuoden":"vuotta"}return s=i(e,r)+" "+s}function i(e,t){return 10>e?t?s[e]:r[e]:e}var r="nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän".split(" "),s=["nolla","yhden","kahden","kolmen","neljän","viiden","kuuden",r[7],r[8],r[9]];(t.defineLocale||t.lang).call(t,"fi",{months:"tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"),monthsShort:"tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu".split("_"),weekdays:"sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"),weekdaysShort:"su_ma_ti_ke_to_pe_la".split("_"),weekdaysMin:"su_ma_ti_ke_to_pe_la".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"Do MMMM[ta] YYYY",LLL:"Do MMMM[ta] YYYY, [klo] LT",LLLL:"dddd, Do MMMM[ta] YYYY, [klo] LT",l:"D.M.YYYY",ll:"Do MMM YYYY",lll:"Do MMM YYYY, [klo] LT",llll:"ddd, Do MMM YYYY, [klo] LT"},calendar:{sameDay:"[tänään] [klo] LT",nextDay:"[huomenna] [klo] LT",nextWeek:"dddd [klo] LT",lastDay:"[eilen] [klo] LT",lastWeek:"[viime] dddd[na] [klo] LT",sameElse:"L"},relativeTime:{future:"%s päästä",past:"%s sitten",s:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("fi","fi",{closeText:"Sulje",prevText:"&#xAB;Edellinen",nextText:"Seuraava&#xBB;",currentText:"Tänään",monthNames:["Tammikuu","Helmikuu","Maaliskuu","Huhtikuu","Toukokuu","Kesäkuu","Heinäkuu","Elokuu","Syyskuu","Lokakuu","Marraskuu","Joulukuu"],monthNamesShort:["Tammi","Helmi","Maalis","Huhti","Touko","Kesä","Heinä","Elo","Syys","Loka","Marras","Joulu"],dayNamesShort:["Su","Ma","Ti","Ke","To","Pe","La"],dayNames:["Sunnuntai","Maanantai","Tiistai","Keskiviikko","Torstai","Perjantai","Lauantai"],dayNamesMin:["Su","Ma","Ti","Ke","To","Pe","La"],weekHeader:"Vk",dateFormat:"d.m.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("fi",{defaultButtonText:{month:"Kuukausi",week:"Viikko",day:"Päivä",list:"Tapahtumat"},allDayText:"Koko päivä",eventLimitText:"lisää"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"fr",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Aujourd'hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinalParse:/\d{1,2}(er|)/,ordinal:function(e){return e+(1===e?"er":"")},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("fr","fr",{closeText:"Fermer",prevText:"Précédent",nextText:"Suivant",currentText:"Aujourd'hui",monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthNamesShort:["janv.","févr.","mars","avr.","mai","juin","juil.","août","sept.","oct.","nov.","déc."],dayNames:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],dayNamesShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],dayNamesMin:["D","L","M","M","J","V","S"],weekHeader:"Sem.",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("fr",{defaultButtonText:{month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},allDayHtml:"Toute la<br/>journée",eventLimitText:"en plus"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t,n,r){var i=e;switch(n){case"s":return r||t?"néhány másodperc":"néhány másodperce";case"m":return"egy"+(r||t?" perc":" perce");case"mm":return i+(r||t?" perc":" perce");case"h":return"egy"+(r||t?" óra":" órája");case"hh":return i+(r||t?" óra":" órája");case"d":return"egy"+(r||t?" nap":" napja");case"dd":return i+(r||t?" nap":" napja");case"M":return"egy"+(r||t?" hónap":" hónapja");case"MM":return i+(r||t?" hónap":" hónapja");case"y":return"egy"+(r||t?" év":" éve");case"yy":return i+(r||t?" év":" éve")}return""}function r(e){return(e?"":"[múlt] ")+"["+i[this.day()]+"] LT[-kor]"}var i="vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton".split(" ");(t.defineLocale||t.lang).call(t,"hu",{months:"január_február_március_április_május_június_július_augusztus_szeptember_október_november_december".split("_"),monthsShort:"jan_feb_márc_ápr_máj_jún_júl_aug_szept_okt_nov_dec".split("_"),weekdays:"vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat".split("_"),weekdaysShort:"vas_hét_kedd_sze_csüt_pén_szo".split("_"),weekdaysMin:"v_h_k_sze_cs_p_szo".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"YYYY.MM.DD.",LL:"YYYY. MMMM D.",LLL:"YYYY. MMMM D., LT",LLLL:"YYYY. MMMM D., dddd LT"},meridiemParse:/de|du/i,isPM:function(e){return"u"===e.charAt(1).toLowerCase()},meridiem:function(e,t,n){return 12>e?n===!0?"de":"DE":n===!0?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return r.call(this,!0)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return r.call(this,!1)},sameElse:"L"},relativeTime:{future:"%s múlva",past:"%s",s:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("hu","hu",{closeText:"bezár",prevText:"vissza",nextText:"előre",currentText:"ma",monthNames:["Január","Február","Március","Április","Május","Június","Július","Augusztus","Szeptember","Október","November","December"],monthNamesShort:["Jan","Feb","Már","Ápr","Máj","Jún","Júl","Aug","Szep","Okt","Nov","Dec"],dayNames:["Vasárnap","Hétfő","Kedd","Szerda","Csütörtök","Péntek","Szombat"],dayNamesShort:["Vas","Hét","Ked","Sze","Csü","Pén","Szo"],dayNamesMin:["V","H","K","Sze","Cs","P","Szo"],weekHeader:"Hét",dateFormat:"yy.mm.dd.",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:""}),e.fullCalendar.lang("hu",{defaultButtonText:{month:"Hónap",week:"Hét",day:"Nap",list:"Napló"},allDayText:"Egész nap",eventLimitText:"további"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"it",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"Domenica_Lunedì_Martedì_Mercoledì_Giovedì_Venerdì_Sabato".split("_"),weekdaysShort:"Dom_Lun_Mar_Mer_Gio_Ven_Sab".split("_"),weekdaysMin:"D_L_Ma_Me_G_V_S".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Oggi alle] LT",nextDay:"[Domani alle] LT",nextWeek:"dddd [alle] LT",lastDay:"[Ieri alle] LT",lastWeek:function(){switch(this.day()){case 0:return"[la scorsa] dddd [alle] LT";default:return"[lo scorso] dddd [alle] LT"}},sameElse:"L"},relativeTime:{future:function(e){return(/^[0-9].+$/.test(e)?"tra":"in")+" "+e},past:"%s fa",s:"alcuni secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("it","it",{closeText:"Chiudi",prevText:"&#x3C;Prec",nextText:"Succ&#x3E;",currentText:"Oggi",monthNames:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],monthNamesShort:["Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic"],dayNames:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"],dayNamesShort:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],dayNamesMin:["Do","Lu","Ma","Me","Gi","Ve","Sa"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("it",{defaultButtonText:{month:"Mese",week:"Settimana",day:"Giorno",list:"Agenda"},allDayHtml:"Tutto il<br/>giorno",eventLimitText:function(e){return"+altri "+e}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"ja",{months:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日".split("_"),weekdaysShort:"日_月_火_水_木_金_土".split("_"),weekdaysMin:"日_月_火_水_木_金_土".split("_"),longDateFormat:{LT:"Ah時m分",LTS:"LTs秒",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日LT",LLLL:"YYYY年M月D日LT dddd"},meridiemParse:/午前|午後/i,isPM:function(e){return"午後"===e},meridiem:function(e){return 12>e?"午前":"午後"},calendar:{sameDay:"[今日] LT",nextDay:"[明日] LT",nextWeek:"[来週]dddd LT",lastDay:"[昨日] LT",lastWeek:"[前週]dddd LT",sameElse:"L"},relativeTime:{future:"%s後",past:"%s前",s:"数秒",m:"1分",mm:"%d分",h:"1時間",hh:"%d時間",d:"1日",dd:"%d日",M:"1ヶ月",MM:"%dヶ月",y:"1年",yy:"%d年"}}),e.fullCalendar.datepickerLang("ja","ja",{closeText:"閉じる",prevText:"&#x3C;前",nextText:"次&#x3E;",currentText:"今日",monthNames:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],monthNamesShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],dayNames:["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"],dayNamesShort:["日","月","火","水","木","金","土"],dayNamesMin:["日","月","火","水","木","金","土"],weekHeader:"週",dateFormat:"yy/mm/dd",firstDay:0,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"年"}),e.fullCalendar.lang("ja",{defaultButtonText:{month:"月",week:"週",day:"日",list:"予定リスト"},allDayText:"終日",eventLimitText:function(e){return"他 "+e+" 件"}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e){return 5>e%10&&e%10>1&&1!==~~(e/10)%10}function i(e,t,i){var r=e+" ";switch(i){case"m":return t?"minuta":"minutę";case"mm":return r+(n(e)?"minuty":"minut");case"h":return t?"godzina":"godzinę";case"hh":return r+(n(e)?"godziny":"godzin");case"MM":return r+(n(e)?"miesiące":"miesięcy");case"yy":return r+(n(e)?"lata":"lat")}}var r="styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"),a="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_");(t.defineLocale||t.lang).call(t,"pl",{months:function(e,t){return/D MMMM/.test(t)?a[e.month()]:r[e.month()]},monthsShort:"sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),weekdays:"niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),weekdaysShort:"nie_pon_wt_śr_czw_pt_sb".split("_"),weekdaysMin:"N_Pn_Wt_Śr_Cz_Pt_So".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Dziś o] LT",nextDay:"[Jutro o] LT",nextWeek:"[W] dddd [o] LT",lastDay:"[Wczoraj o] LT",lastWeek:function(){switch(this.day()){case 0:return"[W zeszłą niedzielę o] LT";case 3:return"[W zeszłą środę o] LT";case 6:return"[W zeszłą sobotę o] LT";default:return"[W zeszły] dddd [o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"%s temu",s:"kilka sekund",m:i,mm:i,h:i,hh:i,d:"1 dzień",dd:"%d dni",M:"miesiąc",MM:i,y:"rok",yy:i},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("pl","pl",{closeText:"Zamknij",prevText:"&#x3C;Poprzedni",nextText:"Następny&#x3E;",currentText:"Dziś",monthNames:["Styczeń","Luty","Marzec","Kwiecień","Maj","Czerwiec","Lipiec","Sierpień","Wrzesień","Październik","Listopad","Grudzień"],monthNamesShort:["Sty","Lu","Mar","Kw","Maj","Cze","Lip","Sie","Wrz","Pa","Lis","Gru"],dayNames:["Niedziela","Poniedziałek","Wtorek","Środa","Czwartek","Piątek","Sobota"],dayNamesShort:["Nie","Pn","Wt","Śr","Czw","Pt","So"],dayNamesMin:["N","Pn","Wt","Śr","Cz","Pt","So"],weekHeader:"Tydz",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("pl",{defaultButtonText:{month:"Miesiąc",week:"Tydzień",day:"Dzień",list:"Plan dnia"},allDayText:"Cały dzień",eventLimitText:"więcej"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"pt-br",{months:"janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado".split("_"),weekdaysShort:"dom_seg_ter_qua_qui_sex_sáb".split("_"),weekdaysMin:"dom_2ª_3ª_4ª_5ª_6ª_sáb".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY [às] LT",LLLL:"dddd, D [de] MMMM [de] YYYY [às] LT"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"%s atrás",s:"segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinalParse:/\d{1,2}º/,ordinal:"%dº"}),e.fullCalendar.datepickerLang("pt-br","pt-BR",{closeText:"Fechar",prevText:"&#x3C;Anterior",nextText:"Próximo&#x3E;",currentText:"Hoje",monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayNamesMin:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("pt-br",{defaultButtonText:{month:"Mês",week:"Semana",day:"Dia",list:"Compromissos"},allDayText:"dia inteiro",eventLimitText:function(e){return"mais +"+e}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t){var n=e.split("_");return 1===t%10&&11!==t%100?n[0]:t%10>=2&&4>=t%10&&(10>t%100||t%100>=20)?n[1]:n[2]}function i(e,t,i){var a={mm:t?"минута_минуты_минут":"минуту_минуты_минут",hh:"час_часа_часов",dd:"день_дня_дней",MM:"месяц_месяца_месяцев",yy:"год_года_лет"};return"m"===i?t?"минута":"минуту":e+" "+n(a[i],+e)}function a(e,t){var n={nominative:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"),accusative:"января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря".split("_")},i=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(t)?"accusative":"nominative";return n[i][e.month()]}function r(e,t){var n={nominative:"янв_фев_март_апр_май_июнь_июль_авг_сен_окт_ноя_дек".split("_"),accusative:"янв_фев_мар_апр_мая_июня_июля_авг_сен_окт_ноя_дек".split("_")},i=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(t)?"accusative":"nominative";return n[i][e.month()]}function s(e,t){var n={nominative:"воскресенье_понедельник_вторник_среда_четверг_пятница_суббота".split("_"),accusative:"воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу".split("_")},i=/\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?\] ?dddd/.test(t)?"accusative":"nominative";return n[i][e.day()]}(t.defineLocale||t.lang).call(t,"ru",{months:a,monthsShort:r,weekdays:s,weekdaysShort:"вс_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"вс_пн_вт_ср_чт_пт_сб".split("_"),monthsParse:[/^янв/i,/^фев/i,/^мар/i,/^апр/i,/^ма[й|я]/i,/^июн/i,/^июл/i,/^авг/i,/^сен/i,/^окт/i,/^ноя/i,/^дек/i],longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., LT",LLLL:"dddd, D MMMM YYYY г., LT"},calendar:{sameDay:"[Сегодня в] LT",nextDay:"[Завтра в] LT",lastDay:"[Вчера в] LT",nextWeek:function(){return 2===this.day()?"[Во] dddd [в] LT":"[В] dddd [в] LT"},lastWeek:function(e){if(e.week()===this.week())return 2===this.day()?"[Во] dddd [в] LT":"[В] dddd [в] LT";switch(this.day()){case 0:return"[В прошлое] dddd [в] LT";case 1:case 2:case 4:return"[В прошлый] dddd [в] LT";case 3:case 5:case 6:return"[В прошлую] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"через %s",past:"%s назад",s:"несколько секунд",m:i,mm:i,h:"час",hh:i,d:"день",dd:i,M:"месяц",MM:i,y:"год",yy:i},meridiemParse:/ночи|утра|дня|вечера/i,isPM:function(e){return/^(дня|вечера)$/.test(e)},meridiem:function(e){return 4>e?"ночи":12>e?"утра":17>e?"дня":"вечера"},ordinalParse:/\d{1,2}-(й|го|я)/,ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":return e+"-й";case"D":return e+"-го";case"w":case"W":return e+"-я";default:return e}},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("ru","ru",{closeText:"Закрыть",prevText:"&#x3C;Пред",nextText:"След&#x3E;",currentText:"Сегодня",monthNames:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthNamesShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],dayNames:["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"],dayNamesShort:["вск","пнд","втр","срд","чтв","птн","сбт"],dayNamesMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],weekHeader:"Нед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("ru",{defaultButtonText:{month:"Месяц",week:"Неделя",day:"День",list:"Повестка дня"},allDayText:"Весь день",eventLimitText:function(e){return"+ ещё "+e}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"sv",{months:"januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag".split("_"),weekdaysShort:"sön_mån_tis_ons_tor_fre_lör".split("_"),weekdaysMin:"sö_må_ti_on_to_fr_lö".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Idag] LT",nextDay:"[Imorgon] LT",lastDay:"[Igår] LT",nextWeek:"dddd LT",lastWeek:"[Förra] dddd[en] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"för %s sedan",s:"några sekunder",m:"en minut",mm:"%d minuter",h:"en timme",hh:"%d timmar",d:"en dag",dd:"%d dagar",M:"en månad",MM:"%d månader",y:"ett år",yy:"%d år"},ordinalParse:/\d{1,2}(e|a)/,ordinal:function(e){var t=e%10,n=1===~~(e%100/10)?"e":1===t?"a":2===t?"a":3===t?"e":"e";return e+n},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("sv","sv",{closeText:"Stäng",prevText:"&#xAB;Förra",nextText:"Nästa&#xBB;",currentText:"Idag",monthNames:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNamesShort:["Sön","Mån","Tis","Ons","Tor","Fre","Lör"],dayNames:["Söndag","Måndag","Tisdag","Onsdag","Torsdag","Fredag","Lördag"],dayNamesMin:["Sö","Må","Ti","On","To","Fr","Lö"],weekHeader:"Ve",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("sv",{defaultButtonText:{month:"Månad",week:"Vecka",day:"Dag",list:"Program"},allDayText:"Heldag",eventLimitText:"till"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"th",{months:"มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม".split("_"),monthsShort:"มกรา_กุมภา_มีนา_เมษา_พฤษภา_มิถุนา_กรกฎา_สิงหา_กันยา_ตุลา_พฤศจิกา_ธันวา".split("_"),weekdays:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์".split("_"),weekdaysShort:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์".split("_"),weekdaysMin:"อา._จ._อ._พ._พฤ._ศ._ส.".split("_"),longDateFormat:{LT:"H นาฬิกา m นาที",LTS:"LT s วินาที",L:"YYYY/MM/DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY เวลา LT",LLLL:"วันddddที่ D MMMM YYYY เวลา LT"},meridiemParse:/ก่อนเที่ยง|หลังเที่ยง/,isPM:function(e){return"หลังเที่ยง"===e},meridiem:function(e){return 12>e?"ก่อนเที่ยง":"หลังเที่ยง"},calendar:{sameDay:"[วันนี้ เวลา] LT",nextDay:"[พรุ่งนี้ เวลา] LT",nextWeek:"dddd[หน้า เวลา] LT",lastDay:"[เมื่อวานนี้ เวลา] LT",lastWeek:"[วัน]dddd[ที่แล้ว เวลา] LT",sameElse:"L"},relativeTime:{future:"อีก %s",past:"%sที่แล้ว",s:"ไม่กี่วินาที",m:"1 นาที",mm:"%d นาที",h:"1 ชั่วโมง",hh:"%d ชั่วโมง",d:"1 วัน",dd:"%d วัน",M:"1 เดือน",MM:"%d เดือน",y:"1 ปี",yy:"%d ปี"}}),e.fullCalendar.datepickerLang("th","th",{closeText:"ปิด",prevText:"&#xAB;&#xA0;ย้อน",nextText:"ถัดไป&#xA0;&#xBB;",currentText:"วันนี้",monthNames:["มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม"],monthNamesShort:["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."],dayNames:["อาทิตย์","จันทร์","อังคาร","พุธ","พฤหัสบดี","ศุกร์","เสาร์"],dayNamesShort:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],dayNamesMin:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("th",{defaultButtonText:{month:"เดือน",week:"สัปดาห์",day:"วัน",list:"แผนงาน"},allDayText:"ตลอดวัน",eventLimitText:"เพิ่มเติม"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"zh-cn",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"周日_周一_周二_周三_周四_周五_周六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"Ah点mm",LTS:"Ah点m分s秒",L:"YYYY-MM-DD",LL:"YYYY年MMMD日",LLL:"YYYY年MMMD日LT",LLLL:"YYYY年MMMD日ddddLT",l:"YYYY-MM-DD",ll:"YYYY年MMMD日",lll:"YYYY年MMMD日LT",llll:"YYYY年MMMD日ddddLT"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(e,t){return 12===e&&(e=0),"凌晨"===t||"早上"===t||"上午"===t?e:"下午"===t||"晚上"===t?e+12:e>=11?e:e+12},meridiem:function(e,t){var n=100*e+t;return 600>n?"凌晨":900>n?"早上":1130>n?"上午":1230>n?"中午":1800>n?"下午":"晚上"},calendar:{sameDay:function(){return 0===this.minutes()?"[今天]Ah[点整]":"[今天]LT"},nextDay:function(){return 0===this.minutes()?"[明天]Ah[点整]":"[明天]LT"},lastDay:function(){return 0===this.minutes()?"[昨天]Ah[点整]":"[昨天]LT"},nextWeek:function(){var e,n;return e=t().startOf("week"),n=this.unix()-e.unix()>=604800?"[下]":"[本]",0===this.minutes()?n+"dddAh点整":n+"dddAh点mm"},lastWeek:function(){var e,n;return e=t().startOf("week"),n=this.unix()<e.unix()?"[上]":"[本]",0===this.minutes()?n+"dddAh点整":n+"dddAh点mm"},sameElse:"LL"},ordinalParse:/\d{1,2}(日|月|周)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"周";default:return e}},relativeTime:{future:"%s内",past:"%s前",s:"几秒",m:"1分钟",mm:"%d分钟",h:"1小时",hh:"%d小时",d:"1天",dd:"%d天",M:"1个月",MM:"%d个月",y:"1年",yy:"%d年"},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("zh-cn","zh-CN",{closeText:"关闭",prevText:"&#x3C;上月",nextText:"下月&#x3E;",currentText:"今天",monthNames:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthNamesShort:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周六"],dayNamesMin:["日","一","二","三","四","五","六"],weekHeader:"周",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"年"}),e.fullCalendar.lang("zh-cn",{defaultButtonText:{month:"月",week:"周",day:"日",list:"日程"},allDayText:"全天",eventLimitText:function(e){return"另外 "+e+" 个"}})}); \ No newline at end of file
+(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){(e.defineLocale||e.lang).call(e,"da",{months:"januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"søn_man_tir_ons_tor_fre_lør".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd [d.] D. MMMM YYYY LT"},calendar:{sameDay:"[I dag kl.] LT",nextDay:"[I morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[I går kl.] LT",lastWeek:"[sidste] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"få sekunder",m:"et minut",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dage",M:"en måned",MM:"%d måneder",y:"et år",yy:"%d år"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),t.fullCalendar.datepickerLang("da","da",{closeText:"Luk",prevText:"&#x3C;Forrige",nextText:"Næste&#x3E;",currentText:"Idag",monthNames:["Januar","Februar","Marts","April","Maj","Juni","Juli","August","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNames:["Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag"],dayNamesShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør"],dayNamesMin:["Sø","Ma","Ti","On","To","Fr","Lø"],weekHeader:"Uge",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("da",{defaultButtonText:{month:"Måned",week:"Uge",day:"Dag",list:"Agenda"},allDayText:"Hele dagen",eventLimitText:"flere"})});(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){function n(t,e,n){var i={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[t+" Tage",t+" Tagen"],M:["ein Monat","einem Monat"],MM:[t+" Monate",t+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[t+" Jahre",t+" Jahren"]};return e?i[n][0]:i[n][1]}(e.defineLocale||e.lang).call(e,"de",{months:"Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Heute um] LT [Uhr]",sameElse:"L",nextDay:"[Morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[Gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:n,mm:"%d Minuten",h:n,hh:"%d Stunden",d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),t.fullCalendar.datepickerLang("de","de",{closeText:"Schließen",prevText:"&#x3C;Zurück",nextText:"Vor&#x3E;",currentText:"Heute",monthNames:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],dayNamesShort:["So","Mo","Di","Mi","Do","Fr","Sa"],dayNamesMin:["So","Mo","Di","Mi","Do","Fr","Sa"],weekHeader:"KW",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("de",{defaultButtonText:{month:"Monat",week:"Woche",day:"Tag",list:"Terminübersicht"},allDayText:"Ganztägig",eventLimitText:function(t){return"+ weitere "+t}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){var n="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),i="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_");(t.defineLocale||t.lang).call(t,"es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,t){return/-MMM-/.test(t)?i[e.month()]:n[e.month()]},weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"Do_Lu_Ma_Mi_Ju_Vi_Sá".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY LT",LLLL:"dddd, D [de] MMMM [de] YYYY LT"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("es","es",{closeText:"Cerrar",prevText:"&#x3C;Ant",nextText:"Sig&#x3E;",currentText:"Hoy",monthNames:["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],monthNamesShort:["ene","feb","mar","abr","may","jun","jul","ago","sep","oct","nov","dic"],dayNames:["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],dayNamesShort:["dom","lun","mar","mié","jue","vie","sáb"],dayNamesMin:["D","L","M","X","J","V","S"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("es",{defaultButtonText:{month:"Mes",week:"Semana",day:"Día",list:"Agenda"},allDayHtml:"Todo<br/>el día",eventLimitText:"más"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t,n,r){var s="";switch(n){case"s":return r?"muutaman sekunnin":"muutama sekunti";case"m":return r?"minuutin":"minuutti";case"mm":s=r?"minuutin":"minuuttia";break;case"h":return r?"tunnin":"tunti";case"hh":s=r?"tunnin":"tuntia";break;case"d":return r?"päivän":"päivä";case"dd":s=r?"päivän":"päivää";break;case"M":return r?"kuukauden":"kuukausi";case"MM":s=r?"kuukauden":"kuukautta";break;case"y":return r?"vuoden":"vuosi";case"yy":s=r?"vuoden":"vuotta"}return s=i(e,r)+" "+s}function i(e,t){return 10>e?t?s[e]:r[e]:e}var r="nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän".split(" "),s=["nolla","yhden","kahden","kolmen","neljän","viiden","kuuden",r[7],r[8],r[9]];(t.defineLocale||t.lang).call(t,"fi",{months:"tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"),monthsShort:"tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu".split("_"),weekdays:"sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"),weekdaysShort:"su_ma_ti_ke_to_pe_la".split("_"),weekdaysMin:"su_ma_ti_ke_to_pe_la".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"Do MMMM[ta] YYYY",LLL:"Do MMMM[ta] YYYY, [klo] LT",LLLL:"dddd, Do MMMM[ta] YYYY, [klo] LT",l:"D.M.YYYY",ll:"Do MMM YYYY",lll:"Do MMM YYYY, [klo] LT",llll:"ddd, Do MMM YYYY, [klo] LT"},calendar:{sameDay:"[tänään] [klo] LT",nextDay:"[huomenna] [klo] LT",nextWeek:"dddd [klo] LT",lastDay:"[eilen] [klo] LT",lastWeek:"[viime] dddd[na] [klo] LT",sameElse:"L"},relativeTime:{future:"%s päästä",past:"%s sitten",s:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("fi","fi",{closeText:"Sulje",prevText:"&#xAB;Edellinen",nextText:"Seuraava&#xBB;",currentText:"Tänään",monthNames:["Tammikuu","Helmikuu","Maaliskuu","Huhtikuu","Toukokuu","Kesäkuu","Heinäkuu","Elokuu","Syyskuu","Lokakuu","Marraskuu","Joulukuu"],monthNamesShort:["Tammi","Helmi","Maalis","Huhti","Touko","Kesä","Heinä","Elo","Syys","Loka","Marras","Joulu"],dayNamesShort:["Su","Ma","Ti","Ke","To","Pe","La"],dayNames:["Sunnuntai","Maanantai","Tiistai","Keskiviikko","Torstai","Perjantai","Lauantai"],dayNamesMin:["Su","Ma","Ti","Ke","To","Pe","La"],weekHeader:"Vk",dateFormat:"d.m.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("fi",{defaultButtonText:{month:"Kuukausi",week:"Viikko",day:"Päivä",list:"Tapahtumat"},allDayText:"Koko päivä",eventLimitText:"lisää"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"fr",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Aujourd'hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinalParse:/\d{1,2}(er|)/,ordinal:function(e){return e+(1===e?"er":"")},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("fr","fr",{closeText:"Fermer",prevText:"Précédent",nextText:"Suivant",currentText:"Aujourd'hui",monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthNamesShort:["janv.","févr.","mars","avr.","mai","juin","juil.","août","sept.","oct.","nov.","déc."],dayNames:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],dayNamesShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],dayNamesMin:["D","L","M","M","J","V","S"],weekHeader:"Sem.",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("fr",{defaultButtonText:{month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},allDayHtml:"Toute la<br/>journée",eventLimitText:"en plus"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t,n,r){var i=e;switch(n){case"s":return r||t?"néhány másodperc":"néhány másodperce";case"m":return"egy"+(r||t?" perc":" perce");case"mm":return i+(r||t?" perc":" perce");case"h":return"egy"+(r||t?" óra":" órája");case"hh":return i+(r||t?" óra":" órája");case"d":return"egy"+(r||t?" nap":" napja");case"dd":return i+(r||t?" nap":" napja");case"M":return"egy"+(r||t?" hónap":" hónapja");case"MM":return i+(r||t?" hónap":" hónapja");case"y":return"egy"+(r||t?" év":" éve");case"yy":return i+(r||t?" év":" éve")}return""}function r(e){return(e?"":"[múlt] ")+"["+i[this.day()]+"] LT[-kor]"}var i="vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton".split(" ");(t.defineLocale||t.lang).call(t,"hu",{months:"január_február_március_április_május_június_július_augusztus_szeptember_október_november_december".split("_"),monthsShort:"jan_feb_márc_ápr_máj_jún_júl_aug_szept_okt_nov_dec".split("_"),weekdays:"vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat".split("_"),weekdaysShort:"vas_hét_kedd_sze_csüt_pén_szo".split("_"),weekdaysMin:"v_h_k_sze_cs_p_szo".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"YYYY.MM.DD.",LL:"YYYY. MMMM D.",LLL:"YYYY. MMMM D., LT",LLLL:"YYYY. MMMM D., dddd LT"},meridiemParse:/de|du/i,isPM:function(e){return"u"===e.charAt(1).toLowerCase()},meridiem:function(e,t,n){return 12>e?n===!0?"de":"DE":n===!0?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return r.call(this,!0)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return r.call(this,!1)},sameElse:"L"},relativeTime:{future:"%s múlva",past:"%s",s:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("hu","hu",{closeText:"bezár",prevText:"vissza",nextText:"előre",currentText:"ma",monthNames:["Január","Február","Március","Április","Május","Június","Július","Augusztus","Szeptember","Október","November","December"],monthNamesShort:["Jan","Feb","Már","Ápr","Máj","Jún","Júl","Aug","Szep","Okt","Nov","Dec"],dayNames:["Vasárnap","Hétfő","Kedd","Szerda","Csütörtök","Péntek","Szombat"],dayNamesShort:["Vas","Hét","Ked","Sze","Csü","Pén","Szo"],dayNamesMin:["V","H","K","Sze","Cs","P","Szo"],weekHeader:"Hét",dateFormat:"yy.mm.dd.",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:""}),e.fullCalendar.lang("hu",{defaultButtonText:{month:"Hónap",week:"Hét",day:"Nap",list:"Napló"},allDayText:"Egész nap",eventLimitText:"további"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"it",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"Domenica_Lunedì_Martedì_Mercoledì_Giovedì_Venerdì_Sabato".split("_"),weekdaysShort:"Dom_Lun_Mar_Mer_Gio_Ven_Sab".split("_"),weekdaysMin:"D_L_Ma_Me_G_V_S".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Oggi alle] LT",nextDay:"[Domani alle] LT",nextWeek:"dddd [alle] LT",lastDay:"[Ieri alle] LT",lastWeek:function(){switch(this.day()){case 0:return"[la scorsa] dddd [alle] LT";default:return"[lo scorso] dddd [alle] LT"}},sameElse:"L"},relativeTime:{future:function(e){return(/^[0-9].+$/.test(e)?"tra":"in")+" "+e},past:"%s fa",s:"alcuni secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("it","it",{closeText:"Chiudi",prevText:"&#x3C;Prec",nextText:"Succ&#x3E;",currentText:"Oggi",monthNames:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],monthNamesShort:["Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic"],dayNames:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"],dayNamesShort:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],dayNamesMin:["Do","Lu","Ma","Me","Gi","Ve","Sa"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("it",{defaultButtonText:{month:"Mese",week:"Settimana",day:"Giorno",list:"Agenda"},allDayHtml:"Tutto il<br/>giorno",eventLimitText:function(e){return"+altri "+e}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"ja",{months:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日".split("_"),weekdaysShort:"日_月_火_水_木_金_土".split("_"),weekdaysMin:"日_月_火_水_木_金_土".split("_"),longDateFormat:{LT:"Ah時m分",LTS:"LTs秒",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日LT",LLLL:"YYYY年M月D日LT dddd"},meridiemParse:/午前|午後/i,isPM:function(e){return"午後"===e},meridiem:function(e){return 12>e?"午前":"午後"},calendar:{sameDay:"[今日] LT",nextDay:"[明日] LT",nextWeek:"[来週]dddd LT",lastDay:"[昨日] LT",lastWeek:"[前週]dddd LT",sameElse:"L"},relativeTime:{future:"%s後",past:"%s前",s:"数秒",m:"1分",mm:"%d分",h:"1時間",hh:"%d時間",d:"1日",dd:"%d日",M:"1ヶ月",MM:"%dヶ月",y:"1年",yy:"%d年"}}),e.fullCalendar.datepickerLang("ja","ja",{closeText:"閉じる",prevText:"&#x3C;前",nextText:"次&#x3E;",currentText:"今日",monthNames:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],monthNamesShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],dayNames:["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"],dayNamesShort:["日","月","火","水","木","金","土"],dayNamesMin:["日","月","火","水","木","金","土"],weekHeader:"週",dateFormat:"yy/mm/dd",firstDay:0,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"年"}),e.fullCalendar.lang("ja",{defaultButtonText:{month:"月",week:"週",day:"日",list:"予定リスト"},allDayText:"終日",eventLimitText:function(e){return"他 "+e+" 件"}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e){return 5>e%10&&e%10>1&&1!==~~(e/10)%10}function i(e,t,i){var r=e+" ";switch(i){case"m":return t?"minuta":"minutę";case"mm":return r+(n(e)?"minuty":"minut");case"h":return t?"godzina":"godzinę";case"hh":return r+(n(e)?"godziny":"godzin");case"MM":return r+(n(e)?"miesiące":"miesięcy");case"yy":return r+(n(e)?"lata":"lat")}}var r="styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"),a="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_");(t.defineLocale||t.lang).call(t,"pl",{months:function(e,t){return/D MMMM/.test(t)?a[e.month()]:r[e.month()]},monthsShort:"sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),weekdays:"niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),weekdaysShort:"nie_pon_wt_śr_czw_pt_sb".split("_"),weekdaysMin:"N_Pn_Wt_Śr_Cz_Pt_So".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Dziś o] LT",nextDay:"[Jutro o] LT",nextWeek:"[W] dddd [o] LT",lastDay:"[Wczoraj o] LT",lastWeek:function(){switch(this.day()){case 0:return"[W zeszłą niedzielę o] LT";case 3:return"[W zeszłą środę o] LT";case 6:return"[W zeszłą sobotę o] LT";default:return"[W zeszły] dddd [o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"%s temu",s:"kilka sekund",m:i,mm:i,h:i,hh:i,d:"1 dzień",dd:"%d dni",M:"miesiąc",MM:i,y:"rok",yy:i},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("pl","pl",{closeText:"Zamknij",prevText:"&#x3C;Poprzedni",nextText:"Następny&#x3E;",currentText:"Dziś",monthNames:["Styczeń","Luty","Marzec","Kwiecień","Maj","Czerwiec","Lipiec","Sierpień","Wrzesień","Październik","Listopad","Grudzień"],monthNamesShort:["Sty","Lu","Mar","Kw","Maj","Cze","Lip","Sie","Wrz","Pa","Lis","Gru"],dayNames:["Niedziela","Poniedziałek","Wtorek","Środa","Czwartek","Piątek","Sobota"],dayNamesShort:["Nie","Pn","Wt","Śr","Czw","Pt","So"],dayNamesMin:["N","Pn","Wt","Śr","Cz","Pt","So"],weekHeader:"Tydz",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("pl",{defaultButtonText:{month:"Miesiąc",week:"Tydzień",day:"Dzień",list:"Plan dnia"},allDayText:"Cały dzień",eventLimitText:"więcej"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"pt-br",{months:"janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado".split("_"),weekdaysShort:"dom_seg_ter_qua_qui_sex_sáb".split("_"),weekdaysMin:"dom_2ª_3ª_4ª_5ª_6ª_sáb".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY [às] LT",LLLL:"dddd, D [de] MMMM [de] YYYY [às] LT"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"%s atrás",s:"segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinalParse:/\d{1,2}º/,ordinal:"%dº"}),e.fullCalendar.datepickerLang("pt-br","pt-BR",{closeText:"Fechar",prevText:"&#x3C;Anterior",nextText:"Próximo&#x3E;",currentText:"Hoje",monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayNamesMin:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("pt-br",{defaultButtonText:{month:"Mês",week:"Semana",day:"Dia",list:"Compromissos"},allDayText:"dia inteiro",eventLimitText:function(e){return"mais +"+e}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t){var n=e.split("_");return 1===t%10&&11!==t%100?n[0]:t%10>=2&&4>=t%10&&(10>t%100||t%100>=20)?n[1]:n[2]}function i(e,t,i){var a={mm:t?"минута_минуты_минут":"минуту_минуты_минут",hh:"час_часа_часов",dd:"день_дня_дней",MM:"месяц_месяца_месяцев",yy:"год_года_лет"};return"m"===i?t?"минута":"минуту":e+" "+n(a[i],+e)}function a(e,t){var n={nominative:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"),accusative:"января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря".split("_")},i=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(t)?"accusative":"nominative";return n[i][e.month()]}function r(e,t){var n={nominative:"янв_фев_март_апр_май_июнь_июль_авг_сен_окт_ноя_дек".split("_"),accusative:"янв_фев_мар_апр_мая_июня_июля_авг_сен_окт_ноя_дек".split("_")},i=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(t)?"accusative":"nominative";return n[i][e.month()]}function s(e,t){var n={nominative:"воскресенье_понедельник_вторник_среда_четверг_пятница_суббота".split("_"),accusative:"воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу".split("_")},i=/\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?\] ?dddd/.test(t)?"accusative":"nominative";return n[i][e.day()]}(t.defineLocale||t.lang).call(t,"ru",{months:a,monthsShort:r,weekdays:s,weekdaysShort:"вс_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"вс_пн_вт_ср_чт_пт_сб".split("_"),monthsParse:[/^янв/i,/^фев/i,/^мар/i,/^апр/i,/^ма[й|я]/i,/^июн/i,/^июл/i,/^авг/i,/^сен/i,/^окт/i,/^ноя/i,/^дек/i],longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., LT",LLLL:"dddd, D MMMM YYYY г., LT"},calendar:{sameDay:"[Сегодня в] LT",nextDay:"[Завтра в] LT",lastDay:"[Вчера в] LT",nextWeek:function(){return 2===this.day()?"[Во] dddd [в] LT":"[В] dddd [в] LT"},lastWeek:function(e){if(e.week()===this.week())return 2===this.day()?"[Во] dddd [в] LT":"[В] dddd [в] LT";switch(this.day()){case 0:return"[В прошлое] dddd [в] LT";case 1:case 2:case 4:return"[В прошлый] dddd [в] LT";case 3:case 5:case 6:return"[В прошлую] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"через %s",past:"%s назад",s:"несколько секунд",m:i,mm:i,h:"час",hh:i,d:"день",dd:i,M:"месяц",MM:i,y:"год",yy:i},meridiemParse:/ночи|утра|дня|вечера/i,isPM:function(e){return/^(дня|вечера)$/.test(e)},meridiem:function(e){return 4>e?"ночи":12>e?"утра":17>e?"дня":"вечера"},ordinalParse:/\d{1,2}-(й|го|я)/,ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":return e+"-й";case"D":return e+"-го";case"w":case"W":return e+"-я";default:return e}},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("ru","ru",{closeText:"Закрыть",prevText:"&#x3C;Пред",nextText:"След&#x3E;",currentText:"Сегодня",monthNames:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthNamesShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],dayNames:["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"],dayNamesShort:["вск","пнд","втр","срд","чтв","птн","сбт"],dayNamesMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],weekHeader:"Нед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("ru",{defaultButtonText:{month:"Месяц",week:"Неделя",day:"День",list:"Повестка дня"},allDayText:"Весь день",eventLimitText:function(e){return"+ ещё "+e}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"sv",{months:"januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag".split("_"),weekdaysShort:"sön_mån_tis_ons_tor_fre_lör".split("_"),weekdaysMin:"sö_må_ti_on_to_fr_lö".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Idag] LT",nextDay:"[Imorgon] LT",lastDay:"[Igår] LT",nextWeek:"dddd LT",lastWeek:"[Förra] dddd[en] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"för %s sedan",s:"några sekunder",m:"en minut",mm:"%d minuter",h:"en timme",hh:"%d timmar",d:"en dag",dd:"%d dagar",M:"en månad",MM:"%d månader",y:"ett år",yy:"%d år"},ordinalParse:/\d{1,2}(e|a)/,ordinal:function(e){var t=e%10,n=1===~~(e%100/10)?"e":1===t?"a":2===t?"a":3===t?"e":"e";return e+n},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("sv","sv",{closeText:"Stäng",prevText:"&#xAB;Förra",nextText:"Nästa&#xBB;",currentText:"Idag",monthNames:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNamesShort:["Sön","Mån","Tis","Ons","Tor","Fre","Lör"],dayNames:["Söndag","Måndag","Tisdag","Onsdag","Torsdag","Fredag","Lördag"],dayNamesMin:["Sö","Må","Ti","On","To","Fr","Lö"],weekHeader:"Ve",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("sv",{defaultButtonText:{month:"Månad",week:"Vecka",day:"Dag",list:"Program"},allDayText:"Heldag",eventLimitText:"till"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"th",{months:"มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม".split("_"),monthsShort:"มกรา_กุมภา_มีนา_เมษา_พฤษภา_มิถุนา_กรกฎา_สิงหา_กันยา_ตุลา_พฤศจิกา_ธันวา".split("_"),weekdays:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์".split("_"),weekdaysShort:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์".split("_"),weekdaysMin:"อา._จ._อ._พ._พฤ._ศ._ส.".split("_"),longDateFormat:{LT:"H นาฬิกา m นาที",LTS:"LT s วินาที",L:"YYYY/MM/DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY เวลา LT",LLLL:"วันddddที่ D MMMM YYYY เวลา LT"},meridiemParse:/ก่อนเที่ยง|หลังเที่ยง/,isPM:function(e){return"หลังเที่ยง"===e},meridiem:function(e){return 12>e?"ก่อนเที่ยง":"หลังเที่ยง"},calendar:{sameDay:"[วันนี้ เวลา] LT",nextDay:"[พรุ่งนี้ เวลา] LT",nextWeek:"dddd[หน้า เวลา] LT",lastDay:"[เมื่อวานนี้ เวลา] LT",lastWeek:"[วัน]dddd[ที่แล้ว เวลา] LT",sameElse:"L"},relativeTime:{future:"อีก %s",past:"%sที่แล้ว",s:"ไม่กี่วินาที",m:"1 นาที",mm:"%d นาที",h:"1 ชั่วโมง",hh:"%d ชั่วโมง",d:"1 วัน",dd:"%d วัน",M:"1 เดือน",MM:"%d เดือน",y:"1 ปี",yy:"%d ปี"}}),e.fullCalendar.datepickerLang("th","th",{closeText:"ปิด",prevText:"&#xAB;&#xA0;ย้อน",nextText:"ถัดไป&#xA0;&#xBB;",currentText:"วันนี้",monthNames:["มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม"],monthNamesShort:["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."],dayNames:["อาทิตย์","จันทร์","อังคาร","พุธ","พฤหัสบดี","ศุกร์","เสาร์"],dayNamesShort:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],dayNamesMin:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("th",{defaultButtonText:{month:"เดือน",week:"สัปดาห์",day:"วัน",list:"แผนงาน"},allDayText:"ตลอดวัน",eventLimitText:"เพิ่มเติม"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"zh-cn",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"周日_周一_周二_周三_周四_周五_周六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"Ah点mm",LTS:"Ah点m分s秒",L:"YYYY-MM-DD",LL:"YYYY年MMMD日",LLL:"YYYY年MMMD日LT",LLLL:"YYYY年MMMD日ddddLT",l:"YYYY-MM-DD",ll:"YYYY年MMMD日",lll:"YYYY年MMMD日LT",llll:"YYYY年MMMD日ddddLT"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(e,t){return 12===e&&(e=0),"凌晨"===t||"早上"===t||"上午"===t?e:"下午"===t||"晚上"===t?e+12:e>=11?e:e+12},meridiem:function(e,t){var n=100*e+t;return 600>n?"凌晨":900>n?"早上":1130>n?"上午":1230>n?"中午":1800>n?"下午":"晚上"},calendar:{sameDay:function(){return 0===this.minutes()?"[今天]Ah[点整]":"[今天]LT"},nextDay:function(){return 0===this.minutes()?"[明天]Ah[点整]":"[明天]LT"},lastDay:function(){return 0===this.minutes()?"[昨天]Ah[点整]":"[昨天]LT"},nextWeek:function(){var e,n;return e=t().startOf("week"),n=this.unix()-e.unix()>=604800?"[下]":"[本]",0===this.minutes()?n+"dddAh点整":n+"dddAh点mm"},lastWeek:function(){var e,n;return e=t().startOf("week"),n=this.unix()<e.unix()?"[上]":"[本]",0===this.minutes()?n+"dddAh点整":n+"dddAh点mm"},sameElse:"LL"},ordinalParse:/\d{1,2}(日|月|周)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"周";default:return e}},relativeTime:{future:"%s内",past:"%s前",s:"几秒",m:"1分钟",mm:"%d分钟",h:"1小时",hh:"%d小时",d:"1天",dd:"%d天",M:"1个月",MM:"%d个月",y:"1年",yy:"%d年"},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("zh-cn","zh-CN",{closeText:"关闭",prevText:"&#x3C;上月",nextText:"下月&#x3E;",currentText:"今天",monthNames:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthNamesShort:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周六"],dayNamesMin:["日","一","二","三","四","五","六"],weekHeader:"周",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"年"}),e.fullCalendar.lang("zh-cn",{defaultButtonText:{month:"月",week:"周",day:"日",list:"日程"},allDayText:"全天",eventLimitText:function(e){return"另外 "+e+" 个"}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){var n={1:"'inci",5:"'inci",8:"'inci",70:"'inci",80:"'inci",2:"'nci",7:"'nci",20:"'nci",50:"'nci",3:"'üncü",4:"'üncü",100:"'üncü",6:"'ncı",9:"'uncu",10:"'uncu",30:"'uncu",60:"'ıncı",90:"'ıncı"};(t.defineLocale||t.lang).call(t,"tr",{months:"Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eylül_Ekim_Kasım_Aralık".split("_"),monthsShort:"Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara".split("_"),weekdays:"Pazar_Pazartesi_Salı_Çarşamba_Perşembe_Cuma_Cumartesi".split("_"),weekdaysShort:"Paz_Pts_Sal_Çar_Per_Cum_Cts".split("_"),weekdaysMin:"Pz_Pt_Sa_Ça_Pe_Cu_Ct".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[bugün saat] LT",nextDay:"[yarın saat] LT",nextWeek:"[haftaya] dddd [saat] LT",lastDay:"[dün] LT",lastWeek:"[geçen hafta] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s önce",s:"birkaç saniye",m:"bir dakika",mm:"%d dakika",h:"bir saat",hh:"%d saat",d:"bir gün",dd:"%d gün",M:"bir ay",MM:"%d ay",y:"bir yıl",yy:"%d yıl"},ordinalParse:/\d{1,2}'(inci|nci|üncü|ncı|uncu|ıncı)/,ordinal:function(e){if(0===e)return e+"'ıncı";var t=e%10,a=e%100-t,r=e>=100?100:null;return e+(n[t]||n[a]||n[r])},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("tr","tr",{closeText:"kapat",prevText:"&#x3C;geri",nextText:"ileri&#x3e",currentText:"bugün",monthNames:["Ocak","Şubat","Mart","Nisan","Mayıs","Haziran","Temmuz","Ağustos","Eylül","Ekim","Kasım","Aralık"],monthNamesShort:["Oca","Şub","Mar","Nis","May","Haz","Tem","Ağu","Eyl","Eki","Kas","Ara"],dayNames:["Pazar","Pazartesi","Salı","Çarşamba","Perşembe","Cuma","Cumartesi"],dayNamesShort:["Pz","Pt","Sa","Ça","Pe","Cu","Ct"],dayNamesMin:["Pz","Pt","Sa","Ça","Pe","Cu","Ct"],weekHeader:"Hf",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("tr",{defaultButtonText:{next:"ileri",month:"Ay",week:"Hafta",day:"Gün",list:"Ajanda"},allDayText:"Tüm gün",eventLimitText:"daha fazla"})}); \ No newline at end of file
diff --git a/assets/js/src/base.js b/assets/js/src/base.js
index e844ed27..6e92eb31 100644
--- a/assets/js/src/base.js
+++ b/assets/js/src/base.js
@@ -168,7 +168,8 @@ var Kanboard = (function() {
// Project select box
$("#board-selector").chosen({
- width: 180
+ width: 180,
+ no_results_text: $("#board-selector").data("notfound")
});
$("#board-selector").change(function() {
diff --git a/assets/js/src/board.js b/assets/js/src/board.js
index 7f856ce7..878cb98e 100644
--- a/assets/js/src/board.js
+++ b/assets/js/src/board.js
@@ -322,15 +322,18 @@ Kanboard.Board = (function() {
var projectId = $('#board').data('project-id');
$("#form-user_id").chosen({
- width: "180px"
+ width: "180px",
+ no_results_text: $("#form-user_id").data("notfound")
});
$("#form-category_id").chosen({
- width: "200px"
+ width: "200px",
+ no_results_text: $("#form-category_id").data("notfound")
});
$("#more-filters").chosen({
- width: "30%"
+ width: "30%",
+ no_results_text: $("#more-filters").data("notfound")
});
$(".apply-filters").change(function(e) {
diff --git a/composer.lock b/composer.lock
index ff209d51..ef61d8cc 100644
--- a/composer.lock
+++ b/composer.lock
@@ -88,12 +88,12 @@
"source": {
"type": "git",
"url": "https://github.com/fguillot/picoDb.git",
- "reference": "88d9af320a4eee57691474b3ba9c84d6a882135c"
+ "reference": "3bc7a6ccdaaa675bc90610f7fe8c1dd6044d2a9e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/fguillot/picoDb/zipball/88d9af320a4eee57691474b3ba9c84d6a882135c",
- "reference": "88d9af320a4eee57691474b3ba9c84d6a882135c",
+ "url": "https://api.github.com/repos/fguillot/picoDb/zipball/3bc7a6ccdaaa675bc90610f7fe8c1dd6044d2a9e",
+ "reference": "3bc7a6ccdaaa675bc90610f7fe8c1dd6044d2a9e",
"shasum": ""
},
"require": {
@@ -117,7 +117,7 @@
],
"description": "Minimalist database query builder",
"homepage": "https://github.com/fguillot/picoDb",
- "time": "2015-02-15 21:17:50"
+ "time": "2015-03-06 02:33:25"
},
{
"name": "fguillot/simple-validator",
diff --git a/config.default.php b/config.default.php
index ab3e3b8c..e508a84d 100644
--- a/config.default.php
+++ b/config.default.php
@@ -40,6 +40,9 @@ define('DB_HOSTNAME', 'localhost');
// Mysql/Postgres database name
define('DB_NAME', 'kanboard');
+// Mysql/Postgres custom port (null = default port)
+define('DB_PORT', null);
+
// Enable LDAP authentication (false by default)
define('LDAP_AUTH', false);
diff --git a/docs/api-json-rpc.markdown b/docs/api-json-rpc.markdown
index 0440da0c..dcf92ea8 100644
--- a/docs/api-json-rpc.markdown
+++ b/docs/api-json-rpc.markdown
@@ -144,6 +144,32 @@ Array
Procedures
----------
+### getVersion
+
+- Purpose: **Get the application version**
+- Parameters: none
+- Result: **version** (Example: 1.0.12, master)
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getVersion",
+ "id": 1661138292
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1661138292,
+ "result": "1.0.13"
+}
+```
+
### getTimezone
- Purpose: **Get the application timezone**
@@ -1019,6 +1045,95 @@ Response example:
}
```
+### getAllSwimlanes
+
+- Purpose: **Get the list of all swimlanes of a project**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **swimlane properties**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAllSwimlanes",
+ "id": 1242049935,
+ "params": [
+ 2
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1242049935,
+ "result": [
+ {
+ "id": "0",
+ "name": "Default"
+ },
+ {
+ "id": "3",
+ "name": "Version 1.0",
+ "is_active": "0",
+ "position": 1,
+ "project_id": 2
+ },
+ {
+ "id": "2",
+ "name": "Version 7.0",
+ "is_active": "1",
+ "position": 2,
+ "project_id": 2
+ }
+ ]
+}
+```
+
+### getSwimlane
+
+- Purpose: **Get the a swimlane**
+- Parameters:
+ - **project_id** (integer, required)
+ - **name** (string, required)
+- Result on success: **swimlane properties**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getSwimlane",
+ "id": 1242049935,
+ "params": [
+ 2,
+ "Version 1.0"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1242049935,
+ "result": {
+ "id": "3",
+ "name": "Version 1.0",
+ "is_active": "0",
+ "position": 2,
+ "project_id": 2
+ }
+}
+```
+
### moveSwimlaneUp
- Purpose: **Move up the swimlane position**
diff --git a/docs/faq.markdown b/docs/faq.markdown
index 0b7a3562..68bc686f 100644
--- a/docs/faq.markdown
+++ b/docs/faq.markdown
@@ -91,3 +91,8 @@ How to install Kanboard on Yunohost?
[YunoHost](https://yunohost.org/) is a server operating system aiming to make self-hosting accessible to everyone.
There is a [package to install Kanboard on Yunohost easily](https://github.com/mbugeia/kanboard_ynh).
+
+
+Are there some tutorials about Kanboard in other languages?
+------------------------------------
+ - [German article series about Kanboard](http://demaya.de/wp/2014/07/kanboard-eine-jira-alternative-im-detail-installation/)
diff --git a/jsonrpc.php b/jsonrpc.php
index a2106010..afc1ed18 100644
--- a/jsonrpc.php
+++ b/jsonrpc.php
@@ -68,6 +68,8 @@ $server->bind('removeColumn', $container['board'], 'removeColumn');
* Swimlane procedures
*/
$server->bind('getSwimlanes', $container['swimlane'], 'getSwimlanes');
+$server->bind('getAllSwimlanes', $container['swimlane'], 'getAll');
+$server->bind('getSwimlane', $container['swimlane'], 'getByName');
$server->bind('addSwimlane', $container['swimlane'], 'create');
$server->bind('updateSwimlane', $container['swimlane'], 'rename');
$server->bind('removeSwimlane', $container['swimlane'], 'remove');
@@ -418,6 +420,10 @@ $server->register('getTimezone', function() use ($container) {
return $container['config']->get('application_timezone');
});
+$server->register('getVersion', function() use ($container) {
+ return APP_VERSION;
+});
+
/**
* Parse incoming requests
*/
diff --git a/scripts/make-assets.sh b/scripts/make-assets.sh
index d9562ee0..6fe867bb 100755
--- a/scripts/make-assets.sh
+++ b/scripts/make-assets.sh
@@ -5,7 +5,7 @@ vendor_css="jquery-ui.min chosen.min fullcalendar.min font-awesome.min"
app_js="base board calendar analytic task swimlane dashboard"
vendor_js="jquery-1.11.1.min jquery-ui.min jquery.ui.touch-punch.min chosen.jquery.min dropit.min moment.min fullcalendar.min mousetrap.min mousetrap-global-bind.min app.min"
-lang_js="da de es fi fr hu it ja pl pt-br ru sv th zh-cn"
+lang_js="da de es fi fr hu it ja pl pt-br ru sv th zh-cn tr"
function merge_css {
diff --git a/tests/functionals/ApiTest.php b/tests/functionals/ApiTest.php
index f778d1ca..9fdfd1ba 100644
--- a/tests/functionals/ApiTest.php
+++ b/tests/functionals/ApiTest.php
@@ -39,7 +39,7 @@ class Api extends PHPUnit_Framework_TestCase
{
$this->client = new JsonRPC\Client(API_URL);
$this->client->authentication('jsonrpc', API_KEY);
- //$this->client->debug = true;
+ // $this->client->debug = true;
}
private function getTaskId()
@@ -53,8 +53,12 @@ class Api extends PHPUnit_Framework_TestCase
public function testGetTimezone()
{
- $timezone = $this->client->getTimezone();
- $this->assertEquals('Europe/Paris', $timezone);
+ $this->assertEquals('Europe/Paris', $this->client->getTimezone());
+ }
+
+ public function testGetVersion()
+ {
+ $this->assertEquals('master', $this->client->getVersion());
}
public function testRemoveAll()
diff --git a/tests/units/HourlyRate.php b/tests/units/HourlyRate.php
new file mode 100644
index 00000000..5daf0446
--- /dev/null
+++ b/tests/units/HourlyRate.php
@@ -0,0 +1,43 @@
+<?php
+
+require_once __DIR__.'/Base.php';
+
+use Model\User;
+use Model\HourlyRate;
+
+class HourlyRateTest extends Base
+{
+ public function testCreation()
+ {
+ $hr = new HourlyRate($this->container);
+ $this->assertEquals(1, $hr->create(1, 32.4, 'EUR', '2015-01-01'));
+ $this->assertEquals(2, $hr->create(1, 42, 'CAD', '2015-02-01'));
+
+ $rates = $hr->getAllByUser(0);
+ $this->assertEmpty($rates);
+
+ $rates = $hr->getAllByUser(1);
+ $this->assertNotEmpty($rates);
+ $this->assertCount(2, $rates);
+
+ $this->assertEquals(42, $rates[0]['rate']);
+ $this->assertEquals('CAD', $rates[0]['currency']);
+ $this->assertEquals('2015-02-01', date('Y-m-d', $rates[0]['date_effective']));
+
+ $this->assertEquals(32.4, $rates[1]['rate']);
+ $this->assertEquals('EUR', $rates[1]['currency']);
+ $this->assertEquals('2015-01-01', date('Y-m-d', $rates[1]['date_effective']));
+
+ $this->assertEquals(0, $hr->getCurrentRate(0));
+ $this->assertEquals(42, $hr->getCurrentRate(1));
+
+ $this->assertTrue($hr->remove(2));
+ $this->assertEquals(32.4, $hr->getCurrentRate(1));
+
+ $this->assertTrue($hr->remove(1));
+ $this->assertEquals(0, $hr->getCurrentRate(1));
+
+ $rates = $hr->getAllByUser(1);
+ $this->assertEmpty($rates);
+ }
+}
diff --git a/tests/units/SubtaskTest.php b/tests/units/SubtaskTest.php
index 62475186..eb1a3fd3 100644
--- a/tests/units/SubtaskTest.php
+++ b/tests/units/SubtaskTest.php
@@ -11,6 +11,78 @@ use Model\User;
class SubTaskTest extends Base
{
+ public function testMoveUp()
+ {
+ $tc = new TaskCreation($this->container);
+ $s = new Subtask($this->container);
+ $p = new Project($this->container);
+
+ $this->assertEquals(1, $p->create(array('name' => 'test1')));
+ $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1)));
+
+ $this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1)));
+ $this->assertEquals(2, $s->create(array('title' => 'subtask #2', 'task_id' => 1)));
+ $this->assertEquals(3, $s->create(array('title' => 'subtask #3', 'task_id' => 1)));
+
+ $subtask = $s->getById(1);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(1, $subtask['position']);
+
+ $subtask = $s->getById(2);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(2, $subtask['position']);
+
+ $subtask = $s->getById(3);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(3, $subtask['position']);
+
+ $this->assertTrue($s->moveUp(1, 2));
+
+ $subtask = $s->getById(1);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(2, $subtask['position']);
+
+ $subtask = $s->getById(2);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(1, $subtask['position']);
+
+ $subtask = $s->getById(3);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(3, $subtask['position']);
+
+ $this->assertFalse($s->moveUp(1, 2));
+ }
+
+ public function testMoveDown()
+ {
+ $tc = new TaskCreation($this->container);
+ $s = new Subtask($this->container);
+ $p = new Project($this->container);
+
+ $this->assertEquals(1, $p->create(array('name' => 'test1')));
+ $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1)));
+
+ $this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1)));
+ $this->assertEquals(2, $s->create(array('title' => 'subtask #2', 'task_id' => 1)));
+ $this->assertEquals(3, $s->create(array('title' => 'subtask #3', 'task_id' => 1)));
+
+ $this->assertTrue($s->moveDown(1, 1));
+
+ $subtask = $s->getById(1);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(2, $subtask['position']);
+
+ $subtask = $s->getById(2);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(1, $subtask['position']);
+
+ $subtask = $s->getById(3);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(3, $subtask['position']);
+
+ $this->assertFalse($s->moveDown(1, 3));
+ }
+
public function testDuplicate()
{
$tc = new TaskCreation($this->container);
@@ -53,5 +125,8 @@ class SubTaskTest extends Base
$this->assertEquals(0, $subtasks[0]['user_id']);
$this->assertEquals(0, $subtasks[1]['user_id']);
+
+ $this->assertEquals(1, $subtasks[0]['position']);
+ $this->assertEquals(2, $subtasks[1]['position']);
}
}
diff --git a/tests/units/TimetableTest.php b/tests/units/TimetableTest.php
new file mode 100644
index 00000000..4b4dcc83
--- /dev/null
+++ b/tests/units/TimetableTest.php
@@ -0,0 +1,130 @@
+<?php
+
+require_once __DIR__.'/Base.php';
+
+use Model\User;
+use Model\Timetable;
+use Model\TimetableDay;
+use Model\TimetableWeek;
+use Model\TimetableOff;
+use Model\TimetableExtra;
+
+class TimetableTest extends Base
+{
+ public function testCalculateWorkDays()
+ {
+ $w = new TimetableWeek($this->container);
+ $t = new Timetable($this->container);
+
+ $this->assertNotFalse($w->create(1, 1, '09:30', '12:00'));
+ $this->assertNotFalse($w->create(1, 1, '13:00', '17:00'));
+ $this->assertNotFalse($w->create(1, 2, '09:30', '12:00'));
+ $this->assertNotFalse($w->create(1, 2, '13:00', '17:00'));
+
+ $monday = new DateTime('next Monday');
+
+ $timetable = $t->calculate(1, $monday, new DateTime('next Monday + 6 days'));
+ $this->assertNotEmpty($timetable);
+ $this->assertCount(4, $timetable);
+
+ $this->assertEquals($monday->format('Y-m-d').' 09:30', $timetable[0][0]->format('Y-m-d H:i'));
+ $this->assertEquals($monday->format('Y-m-d').' 12:00', $timetable[0][1]->format('Y-m-d H:i'));
+ $this->assertEquals($monday->format('Y-m-d').' 13:00', $timetable[1][0]->format('Y-m-d H:i'));
+ $this->assertEquals($monday->format('Y-m-d').' 17:00', $timetable[1][1]->format('Y-m-d H:i'));
+
+ $this->assertEquals($monday->add(new DateInterval('P1D'))->format('Y-m-d').' 09:30', $timetable[2][0]->format('Y-m-d H:i'));
+ $this->assertEquals($monday->format('Y-m-d').' 12:00', $timetable[2][1]->format('Y-m-d H:i'));
+ $this->assertEquals($monday->format('Y-m-d').' 13:00', $timetable[3][0]->format('Y-m-d H:i'));
+ $this->assertEquals($monday->format('Y-m-d').' 17:00', $timetable[3][1]->format('Y-m-d H:i'));
+ }
+
+ public function testCalculateOverTime()
+ {
+ $d = new TimetableDay($this->container);
+ $w = new TimetableWeek($this->container);
+ $e = new TimetableExtra($this->container);
+ $t = new Timetable($this->container);
+
+ $monday = new DateTime('next Monday');
+ $tuesday = new DateTime('next Monday + 1 day');
+ $friday = new DateTime('next Monday + 4 days');
+
+ $this->assertNotFalse($d->create(1, '08:00', '12:00'));
+ $this->assertNotFalse($d->create(1, '14:00', '18:00'));
+
+ $this->assertNotFalse($w->create(1, 1, '09:30', '12:00'));
+ $this->assertNotFalse($w->create(1, 1, '13:00', '17:00'));
+ $this->assertNotFalse($w->create(1, 2, '09:30', '12:00'));
+ $this->assertNotFalse($w->create(1, 2, '13:00', '17:00'));
+
+ $this->assertNotFalse($e->create(1, $tuesday->format('Y-m-d'), 0, '17:00', '22:00'));
+ $this->assertNotFalse($e->create(1, $friday->format('Y-m-d'), 1));
+
+ $timetable = $t->calculate(1, $monday, new DateTime('next Monday + 6 days'));
+ $this->assertNotEmpty($timetable);
+ $this->assertCount(7, $timetable);
+
+ $this->assertEquals($monday->format('Y-m-d').' 09:30', $timetable[0][0]->format('Y-m-d H:i'));
+ $this->assertEquals($monday->format('Y-m-d').' 12:00', $timetable[0][1]->format('Y-m-d H:i'));
+ $this->assertEquals($monday->format('Y-m-d').' 13:00', $timetable[1][0]->format('Y-m-d H:i'));
+ $this->assertEquals($monday->format('Y-m-d').' 17:00', $timetable[1][1]->format('Y-m-d H:i'));
+
+ $this->assertEquals($tuesday->format('Y-m-d').' 09:30', $timetable[2][0]->format('Y-m-d H:i'));
+ $this->assertEquals($tuesday->format('Y-m-d').' 12:00', $timetable[2][1]->format('Y-m-d H:i'));
+ $this->assertEquals($tuesday->format('Y-m-d').' 13:00', $timetable[3][0]->format('Y-m-d H:i'));
+ $this->assertEquals($tuesday->format('Y-m-d').' 17:00', $timetable[3][1]->format('Y-m-d H:i'));
+
+ $this->assertEquals($tuesday->format('Y-m-d').' 17:00', $timetable[4][0]->format('Y-m-d H:i'));
+ $this->assertEquals($tuesday->format('Y-m-d').' 22:00', $timetable[4][1]->format('Y-m-d H:i'));
+
+ $this->assertEquals($friday->format('Y-m-d').' 08:00', $timetable[5][0]->format('Y-m-d H:i'));
+ $this->assertEquals($friday->format('Y-m-d').' 12:00', $timetable[5][1]->format('Y-m-d H:i'));
+
+ $this->assertEquals($friday->format('Y-m-d').' 14:00', $timetable[6][0]->format('Y-m-d H:i'));
+ $this->assertEquals($friday->format('Y-m-d').' 18:00', $timetable[6][1]->format('Y-m-d H:i'));
+ }
+
+ public function testCalculateTimeOff()
+ {
+ $d = new TimetableDay($this->container);
+ $w = new TimetableWeek($this->container);
+ $o = new TimetableOff($this->container);
+ $t = new Timetable($this->container);
+
+ $monday = new DateTime('next Monday');
+ $tuesday = new DateTime('next Monday + 1 day');
+ $friday = new DateTime('next Monday + 4 days');
+
+ $this->assertNotFalse($d->create(1, '08:00', '12:00'));
+ $this->assertNotFalse($d->create(1, '14:00', '18:00'));
+
+ $this->assertNotFalse($w->create(1, 1, '09:30', '12:00'));
+ $this->assertNotFalse($w->create(1, 1, '13:00', '17:00'));
+ $this->assertNotFalse($w->create(1, 2, '09:30', '12:00'));
+ $this->assertNotFalse($w->create(1, 2, '13:00', '17:00'));
+ $this->assertNotFalse($w->create(1, 5, '09:30', '12:00'));
+ $this->assertNotFalse($w->create(1, 5, '13:00', '17:00'));
+
+ $this->assertNotFalse($o->create(1, $tuesday->format('Y-m-d'), 0, '14:00', '15:00'));
+ $this->assertNotFalse($o->create(1, $monday->format('Y-m-d'), 1));
+
+ $timetable = $t->calculate(1, $monday, new DateTime('next Monday + 6 days'));
+ $this->assertNotEmpty($timetable);
+ $this->assertCount(5, $timetable);
+
+ $this->assertEquals($tuesday->format('Y-m-d').' 09:30', $timetable[0][0]->format('Y-m-d H:i'));
+ $this->assertEquals($tuesday->format('Y-m-d').' 12:00', $timetable[0][1]->format('Y-m-d H:i'));
+
+ $this->assertEquals($tuesday->format('Y-m-d').' 13:00', $timetable[1][0]->format('Y-m-d H:i'));
+ $this->assertEquals($tuesday->format('Y-m-d').' 14:00', $timetable[1][1]->format('Y-m-d H:i'));
+
+ $this->assertEquals($tuesday->format('Y-m-d').' 15:00', $timetable[2][0]->format('Y-m-d H:i'));
+ $this->assertEquals($tuesday->format('Y-m-d').' 17:00', $timetable[2][1]->format('Y-m-d H:i'));
+
+ $this->assertEquals($friday->format('Y-m-d').' 09:30', $timetable[3][0]->format('Y-m-d H:i'));
+ $this->assertEquals($friday->format('Y-m-d').' 12:00', $timetable[3][1]->format('Y-m-d H:i'));
+
+ $this->assertEquals($friday->format('Y-m-d').' 13:00', $timetable[4][0]->format('Y-m-d H:i'));
+ $this->assertEquals($friday->format('Y-m-d').' 17:00', $timetable[4][1]->format('Y-m-d H:i'));
+ }
+}