diff options
280 files changed, 7962 insertions, 4805 deletions
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 3542d84c..118a837d 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -5,9 +5,11 @@ Original author and main developer: Frédéric Guillot (fguillot) Contributors: +- [85pando](https://github.com/85pando) - Alex Butum - [Aleix Pol](https://github.com/aleixpol) - [Ally Raza](https://github.com/alirz23) +- [Angystardust](https://github.com/angystardust) - [Anjar Febrianto](https://github.com/Lasut) - [Ashbike](https://github.com/ashbike) - [Ashish Kulkarni](https://github.com/ashkulz) @@ -21,6 +23,7 @@ Contributors: - [Crash5](https://github.com/crash5) - [Creador30](https://github.com/creador30) - [Cynthia Pereira](https://github.com/cynthiapereira) +- [d4rk5eed](https://github.com/d4rk5eed) - [Damian](https://github.com/dromek) - [Daniel Raknes](https://github.com/danielraknes) - [David-Norris](https://github.com/David-Norris) @@ -40,6 +43,7 @@ Contributors: - [Jan Dittrich](https://github.com/jdittrich) - [Janne Mäntyharju](https://github.com/JanneMantyharju) - [Jean-François Magnier](https://github.com/lefakir) +- [Jeff Guillou](https://github.com/jf-guillou) - [Jesusaplsoft](https://github.com/jesusaplsoft) - [Jesús Marín](https://github.com/alu0100502114) - [Jules Verhaeren](https://github.com/julesverhaeren) @@ -91,6 +95,7 @@ Contributors: - [Sebastien Pacilly](https://github.com/spacilly) - [Sebastian Reese](https://github.com/ReeseSebastian) - [Semyon Novikov](https://github.com/semka) +- [StavrosKa](https://github.com/StavrosKa) - [Sylvain Veyrié](https://github.com/turb) - [Thomas Lutz](https://github.com/phoen1x) - [Timo](https://github.com/BlueTeck) @@ -98,10 +103,12 @@ Contributors: - [Tomáš Votruba](https://github.com/TomasVotruba) - [Toomyem](https://github.com/Toomyem) - [Tony G. Bolaño](https://github.com/tonybolanyo) +- [Trapulo](https://github.com/Trapulo) - [Torsten](https://github.com/misterfu) - [Troloo](https://github.com/troloo) - [Typz](https://github.com/Typz) - [Vedovator](https://github.com/vedovator) +- [Vitaliy S. Orlov](https://github.com/orlov0562) - [Vladimir Babin](https://github.com/Chiliec) - [Yannick Ihmels](https://github.com/ihmels) - [Ybarc](https://github.com/ybarc) @@ -1,10 +1,38 @@ Version 1.0.25 (unreleased) -------------- +Breaking changes: + +* Core functionalities moved to external plugins: + - Google Auth: https://github.com/kanboard/plugin-google-auth + - Github Auth: https://github.com/kanboard/plugin-github-auth + - Gitlab Auth: https://github.com/kanboard/plugin-gitlab-auth + New features: +* When creating a new project, have the possibility to select another project to duplicate +* Add a "Me" button to assignee form element +* Add external links for tasks with plugin api * Add project owner (Directly Responsible Individual) * Add configurable task priority +* Add Greek translation +* Add automatic actions to close tasks with no activity +* Add automatic actions to send an email when there is no activity on a task +* Regroup all daily background tasks in one command: "cronjob" + +Improvements: + +* Do not refresh the whole page when changing subtask status (work in progress) +* Add dropdown menu with inline popup for all task actions +* Change sidebar style +* Change task summary layout +* Use inline popup for subtasks modification +* Move homepage menus to the user dropdown +* Have a new task assigned to the creator by default instead of "no assignee" +* Show progress for task links in board tooltips +* Simplify code to handle ajax popover and redirects +* Simplify layout and templates generation +* Move task form elements to Task helper Version 1.0.24 -------------- @@ -1,12 +1,12 @@ BUILD_DIR = /tmp -CSS_APP = $(addprefix assets/css/src/, $(addsuffix .css, base links title table form button alert tooltip header board task comment subtask markdown listing activity dashboard pagination popover confirm sidebar responsive dropdown screenshot filters gantt)) +CSS_APP = $(addprefix assets/css/src/, $(addsuffix .css, base links title table form button alert tooltip header board task comment subtask markdown listing activity dashboard pagination popover confirm sidebar responsive dropdown screenshot filters gantt project)) CSS_PRINT = $(addprefix assets/css/src/, $(addsuffix .css, print links table board task comment subtask markdown)) CSS_VENDOR = $(addprefix assets/css/vendor/, $(addsuffix .css, jquery-ui.min jquery-ui-timepicker-addon.min chosen.min fullcalendar.min font-awesome.min c3.min)) -JS_APP = $(addprefix assets/js/src/, $(addsuffix .js, Popover Dropdown Tooltip Markdown Sidebar Search App Screenshot Calendar Board Swimlane Gantt Task Project TaskRepartitionChart UserRepartitionChart CumulativeFlowDiagram BurndownChart AvgTimeColumnChart TaskTimeColumnChart LeadCycleTimeChart CompareHoursColumnChart Router)) +JS_APP = $(addprefix assets/js/src/, $(addsuffix .js, Popover Dropdown Tooltip Markdown Search App Screenshot Calendar Board Swimlane Gantt Task Project TaskRepartitionChart UserRepartitionChart CumulativeFlowDiagram BurndownChart AvgTimeColumnChart TaskTimeColumnChart LeadCycleTimeChart CompareHoursColumnChart Router)) JS_VENDOR = $(addprefix assets/js/vendor/, $(addsuffix .js, jquery-1.11.3.min jquery-ui.min jquery-ui-timepicker-addon.min jquery.ui.touch-punch.min chosen.jquery.min moment.min fullcalendar.min mousetrap.min mousetrap-global-bind.min jquery.textcomplete)) -JS_LANG = $(addprefix assets/js/vendor/lang/, $(addsuffix .js, cs da de es fi fr hu id it ja nl nb pl pt pt-br ru sv sr th tr zh-cn)) +JS_LANG = $(addprefix assets/js/vendor/lang/, $(addsuffix .js, cs da de es el fi fr hu id it ja nl nb pl pt pt-br ru sv sr th tr zh-cn)) all: css js diff --git a/app/Action/Base.php b/app/Action/Base.php index efc52f04..e8449d0c 100644 --- a/app/Action/Base.php +++ b/app/Action/Base.php @@ -125,7 +125,7 @@ abstract class Base extends \Kanboard\Core\Base $params[] = $key.'='.var_export($value, true); } - return $this->getName().'('.implode('|', $params).'])'; + return $this->getName().'('.implode('|', $params).')'; } /** diff --git a/app/Action/TaskCloseNoActivity.php b/app/Action/TaskCloseNoActivity.php new file mode 100644 index 00000000..59f7f56a --- /dev/null +++ b/app/Action/TaskCloseNoActivity.php @@ -0,0 +1,95 @@ +<?php + +namespace Kanboard\Action; + +use Kanboard\Model\Task; + +/** + * Close automatically a task after when inactive + * + * @package action + * @author Frederic Guillot + */ +class TaskCloseNoActivity extends Base +{ + /** + * Get automatic action description + * + * @access public + * @return string + */ + public function getDescription() + { + return t('Close a task when there is no activity'); + } + + /** + * Get the list of compatible events + * + * @access public + * @return array + */ + public function getCompatibleEvents() + { + return array(Task::EVENT_DAILY_CRONJOB); + } + + /** + * Get the required parameter for the action (defined by the user) + * + * @access public + * @return array + */ + public function getActionRequiredParameters() + { + return array( + 'duration' => t('Duration in days') + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array('tasks'); + } + + /** + * Execute the action (close the task) + * + * @access public + * @param array $data Event data dictionary + * @return bool True if the action was executed or false when not executed + */ + public function doAction(array $data) + { + $results = array(); + $max = $this->getParam('duration') * 86400; + + foreach ($data['tasks'] as $task) { + $duration = time() - $task['date_modification']; + + if ($duration > $max) { + $results[] = $this->taskStatus->close($task['id']); + } + } + + return in_array(true, $results, true); + } + + /** + * Check if the event data meet the action condition + * + * @access public + * @param array $data Event data dictionary + * @return bool + */ + public function hasRequiredCondition(array $data) + { + return count($data['tasks']) > 0; + } +} diff --git a/app/Action/TaskEmailNoActivity.php b/app/Action/TaskEmailNoActivity.php new file mode 100644 index 00000000..c5d7a797 --- /dev/null +++ b/app/Action/TaskEmailNoActivity.php @@ -0,0 +1,124 @@ +<?php + +namespace Kanboard\Action; + +use Kanboard\Model\Task; + +/** + * Email a task with no activity + * + * @package action + * @author Frederic Guillot + */ +class TaskEmailNoActivity extends Base +{ + /** + * Get automatic action description + * + * @access public + * @return string + */ + public function getDescription() + { + return t('Send email when there is no activity on a task'); + } + + /** + * Get the list of compatible events + * + * @access public + * @return array + */ + public function getCompatibleEvents() + { + return array( + Task::EVENT_DAILY_CRONJOB, + ); + } + + /** + * Get the required parameter for the action (defined by the user) + * + * @access public + * @return array + */ + public function getActionRequiredParameters() + { + return array( + 'user_id' => t('User that will receive the email'), + 'subject' => t('Email subject'), + 'duration' => t('Duration in days'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array('tasks'); + } + + /** + * Check if the event data meet the action condition + * + * @access public + * @param array $data Event data dictionary + * @return bool + */ + public function hasRequiredCondition(array $data) + { + return count($data['tasks']) > 0; + } + + /** + * Execute the action (move the task to another column) + * + * @access public + * @param array $data Event data dictionary + * @return bool True if the action was executed or false when not executed + */ + public function doAction(array $data) + { + $results = array(); + $max = $this->getParam('duration') * 86400; + $user = $this->user->getById($this->getParam('user_id')); + + if (! empty($user['email'])) { + foreach ($data['tasks'] as $task) { + $duration = time() - $task['date_modification']; + + if ($duration > $max) { + $results[] = $this->sendEmail($task['id'], $user); + } + } + } + + return in_array(true, $results, true); + } + + /** + * Send email + * + * @access private + * @param integer $task_id + * @param array $user + * @return boolean + */ + private function sendEmail($task_id, array $user) + { + $task = $this->taskFinder->getDetails($task_id); + + $this->emailClient->send( + $user['email'], + $user['name'] ?: $user['username'], + $this->getParam('subject'), + $this->template->render('notification/task_create', array('task' => $task, 'application_url' => $this->config->get('application_url'))) + ); + + return true; + } +} diff --git a/app/Api/User.php b/app/Api/User.php index 63c222fe..9f26615d 100644 --- a/app/Api/User.php +++ b/app/Api/User.php @@ -21,6 +21,11 @@ class User extends \Kanboard\Core\Base return $this->user->getById($user_id); } + public function getUserByName($username) + { + return $this->user->getByUsername($username); + } + public function getAllUsers() { return $this->user->getAll(); diff --git a/app/Auth/GithubAuth.php b/app/Auth/GithubAuth.php deleted file mode 100644 index 83699581..00000000 --- a/app/Auth/GithubAuth.php +++ /dev/null @@ -1,143 +0,0 @@ -<?php - -namespace Kanboard\Auth; - -use Kanboard\Core\Base; -use Kanboard\Core\Security\OAuthAuthenticationProviderInterface; -use Kanboard\User\GithubUserProvider; - -/** - * Github Authentication Provider - * - * @package auth - * @author Frederic Guillot - */ -class GithubAuth extends Base implements OAuthAuthenticationProviderInterface -{ - /** - * User properties - * - * @access protected - * @var \Kanboard\User\GithubUserProvider - */ - protected $userInfo = null; - - /** - * OAuth2 instance - * - * @access protected - * @var \Kanboard\Core\Http\OAuth2 - */ - protected $service; - - /** - * OAuth2 code - * - * @access protected - * @var string - */ - protected $code = ''; - - /** - * Get authentication provider name - * - * @access public - * @return string - */ - public function getName() - { - return 'Github'; - } - - /** - * Authenticate the user - * - * @access public - * @return boolean - */ - public function authenticate() - { - $profile = $this->getProfile(); - - if (! empty($profile)) { - $this->userInfo = new GithubUserProvider($profile); - return true; - } - - return false; - } - - /** - * Set Code - * - * @access public - * @param string $code - * @return GithubAuth - */ - public function setCode($code) - { - $this->code = $code; - return $this; - } - - /** - * Get user object - * - * @access public - * @return GithubUserProvider - */ - public function getUser() - { - return $this->userInfo; - } - - /** - * Get configured OAuth2 service - * - * @access public - * @return \Kanboard\Core\Http\OAuth2 - */ - public function getService() - { - if (empty($this->service)) { - $this->service = $this->oauth->createService( - GITHUB_CLIENT_ID, - GITHUB_CLIENT_SECRET, - $this->helper->url->to('oauth', 'github', array(), '', true), - GITHUB_OAUTH_AUTHORIZE_URL, - GITHUB_OAUTH_TOKEN_URL, - array() - ); - } - - return $this->service; - } - - /** - * Get Github profile - * - * @access public - * @return array - */ - public function getProfile() - { - $this->getService()->getAccessToken($this->code); - - return $this->httpClient->getJson( - GITHUB_API_URL.'user', - array($this->getService()->getAuthorizationHeader()) - ); - } - - /** - * Unlink user - * - * @access public - * @param integer $userId - * @return bool - */ - public function unlink($userId) - { - return $this->user->update(array('id' => $userId, 'github_id' => '')); - } -} diff --git a/app/Auth/GitlabAuth.php b/app/Auth/GitlabAuth.php deleted file mode 100644 index c0a2cf9b..00000000 --- a/app/Auth/GitlabAuth.php +++ /dev/null @@ -1,143 +0,0 @@ -<?php - -namespace Kanboard\Auth; - -use Kanboard\Core\Base; -use Kanboard\Core\Security\OAuthAuthenticationProviderInterface; -use Kanboard\User\GitlabUserProvider; - -/** - * Gitlab Authentication Provider - * - * @package auth - * @author Frederic Guillot - */ -class GitlabAuth extends Base implements OAuthAuthenticationProviderInterface -{ - /** - * User properties - * - * @access private - * @var \Kanboard\User\GitlabUserProvider - */ - private $userInfo = null; - - /** - * OAuth2 instance - * - * @access protected - * @var \Kanboard\Core\Http\OAuth2 - */ - protected $service; - - /** - * OAuth2 code - * - * @access protected - * @var string - */ - protected $code = ''; - - /** - * Get authentication provider name - * - * @access public - * @return string - */ - public function getName() - { - return 'Gitlab'; - } - - /** - * Authenticate the user - * - * @access public - * @return boolean - */ - public function authenticate() - { - $profile = $this->getProfile(); - - if (! empty($profile)) { - $this->userInfo = new GitlabUserProvider($profile); - return true; - } - - return false; - } - - /** - * Set Code - * - * @access public - * @param string $code - * @return GitlabAuth - */ - public function setCode($code) - { - $this->code = $code; - return $this; - } - - /** - * Get user object - * - * @access public - * @return GitlabUserProvider - */ - public function getUser() - { - return $this->userInfo; - } - - /** - * Get configured OAuth2 service - * - * @access public - * @return \Kanboard\Core\Http\OAuth2 - */ - public function getService() - { - if (empty($this->service)) { - $this->service = $this->oauth->createService( - GITLAB_CLIENT_ID, - GITLAB_CLIENT_SECRET, - $this->helper->url->to('oauth', 'gitlab', array(), '', true), - GITLAB_OAUTH_AUTHORIZE_URL, - GITLAB_OAUTH_TOKEN_URL, - array() - ); - } - - return $this->service; - } - - /** - * Get Gitlab profile - * - * @access public - * @return array - */ - public function getProfile() - { - $this->getService()->getAccessToken($this->code); - - return $this->httpClient->getJson( - GITLAB_API_URL.'user', - array($this->getService()->getAuthorizationHeader()) - ); - } - - /** - * Unlink user - * - * @access public - * @param integer $userId - * @return bool - */ - public function unlink($userId) - { - return $this->user->update(array('id' => $userId, 'gitlab_id' => '')); - } -} diff --git a/app/Auth/GoogleAuth.php b/app/Auth/GoogleAuth.php deleted file mode 100644 index 6eacf0b0..00000000 --- a/app/Auth/GoogleAuth.php +++ /dev/null @@ -1,143 +0,0 @@ -<?php - -namespace Kanboard\Auth; - -use Kanboard\Core\Base; -use Kanboard\Core\Security\OAuthAuthenticationProviderInterface; -use Kanboard\User\GoogleUserProvider; - -/** - * Google Authentication Provider - * - * @package auth - * @author Frederic Guillot - */ -class GoogleAuth extends Base implements OAuthAuthenticationProviderInterface -{ - /** - * User properties - * - * @access protected - * @var \Kanboard\User\GoogleUserProvider - */ - protected $userInfo = null; - - /** - * OAuth2 instance - * - * @access protected - * @var \Kanboard\Core\Http\OAuth2 - */ - protected $service; - - /** - * OAuth2 code - * - * @access protected - * @var string - */ - protected $code = ''; - - /** - * Get authentication provider name - * - * @access public - * @return string - */ - public function getName() - { - return 'Google'; - } - - /** - * Authenticate the user - * - * @access public - * @return boolean - */ - public function authenticate() - { - $profile = $this->getProfile(); - - if (! empty($profile)) { - $this->userInfo = new GoogleUserProvider($profile); - return true; - } - - return false; - } - - /** - * Set Code - * - * @access public - * @param string $code - * @return GoogleAuth - */ - public function setCode($code) - { - $this->code = $code; - return $this; - } - - /** - * Get user object - * - * @access public - * @return GoogleUserProvider - */ - public function getUser() - { - return $this->userInfo; - } - - /** - * Get configured OAuth2 service - * - * @access public - * @return \Kanboard\Core\Http\OAuth2 - */ - public function getService() - { - if (empty($this->service)) { - $this->service = $this->oauth->createService( - GOOGLE_CLIENT_ID, - GOOGLE_CLIENT_SECRET, - $this->helper->url->to('oauth', 'google', array(), '', true), - 'https://accounts.google.com/o/oauth2/auth', - 'https://accounts.google.com/o/oauth2/token', - array('https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile') - ); - } - - return $this->service; - } - - /** - * Get Google profile - * - * @access public - * @return array - */ - public function getProfile() - { - $this->getService()->getAccessToken($this->code); - - return $this->httpClient->getJson( - 'https://www.googleapis.com/oauth2/v1/userinfo', - array($this->getService()->getAuthorizationHeader()) - ); - } - - /** - * Unlink user - * - * @access public - * @param integer $userId - * @return bool - */ - public function unlink($userId) - { - return $this->user->update(array('id' => $userId, 'google_id' => '')); - } -} diff --git a/app/Console/Base.php b/app/Console/Base.php index 4c5caf73..ac89207d 100644 --- a/app/Console/Base.php +++ b/app/Console/Base.php @@ -11,18 +11,19 @@ use Symfony\Component\Console\Command\Command; * @package console * @author Frederic Guillot * - * @property \Kanboard\Model\Notification $notification - * @property \Kanboard\Model\Project $project - * @property \Kanboard\Model\ProjectPermission $projectPermission - * @property \Kanboard\Model\ProjectAnalytic $projectAnalytic - * @property \Kanboard\Model\ProjectDailyColumnStats $projectDailyColumnStats - * @property \Kanboard\Model\ProjectDailyStats $projectDailyStats - * @property \Kanboard\Model\SubtaskExport $subtaskExport - * @property \Kanboard\Model\OverdueNotification $overdueNotification - * @property \Kanboard\Model\Task $task - * @property \Kanboard\Model\TaskExport $taskExport - * @property \Kanboard\Model\TaskFinder $taskFinder - * @property \Kanboard\Model\Transition $transition + * @property \Kanboard\Model\Notification $notification + * @property \Kanboard\Model\Project $project + * @property \Kanboard\Model\ProjectPermission $projectPermission + * @property \Kanboard\Model\ProjectAnalytic $projectAnalytic + * @property \Kanboard\Model\ProjectDailyColumnStats $projectDailyColumnStats + * @property \Kanboard\Model\ProjectDailyStats $projectDailyStats + * @property \Kanboard\Model\SubtaskExport $subtaskExport + * @property \Kanboard\Model\OverdueNotification $overdueNotification + * @property \Kanboard\Model\Task $task + * @property \Kanboard\Model\TaskExport $taskExport + * @property \Kanboard\Model\TaskFinder $taskFinder + * @property \Kanboard\Model\Transition $transition + * @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher */ abstract class Base extends Command { diff --git a/app/Console/Cronjob.php b/app/Console/Cronjob.php new file mode 100644 index 00000000..3a5c5596 --- /dev/null +++ b/app/Console/Cronjob.php @@ -0,0 +1,32 @@ +<?php + +namespace Kanboard\Console; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\NullOutput; + +class Cronjob extends Base +{ + private $commands = array( + 'projects:daily-stats', + 'notification:overdue-tasks', + 'trigger:tasks', + ); + + protected function configure() + { + $this + ->setName('cronjob') + ->setDescription('Execute daily cronjob'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + foreach ($this->commands as $command) { + $job = $this->getApplication()->find($command); + $job->run(new ArrayInput(array('command' => $command)), new NullOutput()); + } + } +} diff --git a/app/Console/TaskTrigger.php b/app/Console/TaskTrigger.php new file mode 100644 index 00000000..8d707211 --- /dev/null +++ b/app/Console/TaskTrigger.php @@ -0,0 +1,51 @@ +<?php + +namespace Kanboard\Console; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Kanboard\Model\Task; +use Kanboard\Event\TaskListEvent; + +class TaskTrigger extends Base +{ + protected function configure() + { + $this + ->setName('trigger:tasks') + ->setDescription('Trigger scheduler event for all tasks'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + foreach ($this->getProjectIds() as $project_id) { + $tasks = $this->taskFinder->getAll($project_id); + $nb_tasks = count($tasks); + + if ($nb_tasks > 0) { + $output->writeln('Trigger task event: project_id='.$project_id.', nb_tasks='.$nb_tasks); + $this->sendEvent($tasks, $project_id); + } + } + } + + private function getProjectIds() + { + $listeners = $this->dispatcher->getListeners(Task::EVENT_DAILY_CRONJOB); + $project_ids = array(); + + foreach ($listeners as $listener) { + $project_ids[] = $listener[0]->getProjectId(); + } + + return array_unique($project_ids); + } + + private function sendEvent(array &$tasks, $project_id) + { + $event = new TaskListEvent(array('project_id' => $project_id)); + $event->setTasks($tasks); + + $this->dispatcher->dispatch(Task::EVENT_DAILY_CRONJOB, $event); + } +} diff --git a/app/Controller/Action.php b/app/Controller/Action.php index 645b53b7..482a210b 100644 --- a/app/Controller/Action.php +++ b/app/Controller/Action.php @@ -20,7 +20,7 @@ class Action extends Base $project = $this->getProject(); $actions = $this->action->getAllByProject($project['id']); - $this->response->html($this->projectLayout('action/index', array( + $this->response->html($this->helper->layout->project('action/index', array( 'values' => array('project_id' => $project['id']), 'project' => $project, 'actions' => $actions, @@ -51,7 +51,7 @@ class Action extends Base $this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id']))); } - $this->response->html($this->projectLayout('action/event', array( + $this->response->html($this->helper->layout->project('action/event', array( 'values' => $values, 'project' => $project, 'events' => $this->actionManager->getCompatibleEvents($values['action_name']), @@ -83,7 +83,7 @@ class Action extends Base $projects_list = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()); unset($projects_list[$project['id']]); - $this->response->html($this->projectLayout('action/params', array( + $this->response->html($this->helper->layout->project('action/params', array( 'values' => $values, 'action_params' => $action_params, 'columns_list' => $this->board->getColumnsList($project['id']), @@ -138,7 +138,7 @@ class Action extends Base { $project = $this->getProject(); - $this->response->html($this->projectLayout('action/remove', array( + $this->response->html($this->helper->layout->project('action/remove', array( 'action' => $this->action->getById($this->request->getIntegerParam('action_id')), 'available_events' => $this->eventManager->getAll(), 'available_actions' => $this->actionManager->getAvailableActions(), diff --git a/app/Controller/Activity.php b/app/Controller/Activity.php index 38658345..db520ebe 100644 --- a/app/Controller/Activity.php +++ b/app/Controller/Activity.php @@ -19,8 +19,7 @@ class Activity extends Base { $project = $this->getProject(); - $this->response->html($this->template->layout('activity/project', array( - 'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()), + $this->response->html($this->helper->layout->app('activity/project', array( 'events' => $this->projectActivity->getProject($project['id']), 'project' => $project, 'title' => t('%s\'s activity', $project['name']) @@ -36,7 +35,7 @@ class Activity extends Base { $task = $this->getTask(); - $this->response->html($this->taskLayout('activity/task', array( + $this->response->html($this->helper->layout->task('activity/task', array( 'title' => $task['title'], 'task' => $task, 'events' => $this->projectActivity->getTask($task['id']), diff --git a/app/Controller/Analytic.php b/app/Controller/Analytic.php index d203fb8e..e7ab9ebc 100644 --- a/app/Controller/Analytic.php +++ b/app/Controller/Analytic.php @@ -1,6 +1,7 @@ <?php namespace Kanboard\Controller; + use Kanboard\Model\Task as TaskModel; /** @@ -12,22 +13,6 @@ use Kanboard\Model\Task as TaskModel; class Analytic extends Base { /** - * Common layout for analytic views - * - * @access private - * @param string $template Template name - * @param array $params Template parameters - * @return string - */ - private function layout($template, array $params) - { - $params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()); - $params['content_for_sublayout'] = $this->template->render($template, $params); - - return $this->template->layout('analytic/layout', $params); - } - - /** * Show average Lead and Cycle time * * @access public @@ -37,7 +22,7 @@ class Analytic extends Base $project = $this->getProject(); list($from, $to) = $this->getDates(); - $this->response->html($this->layout('analytic/lead_cycle_time', array( + $this->response->html($this->helper->layout->analytic('analytic/lead_cycle_time', array( 'values' => array( 'from' => $from, 'to' => $to, @@ -69,7 +54,7 @@ class Analytic extends Base ->setQuery($query) ->calculate(); - $this->response->html($this->layout('analytic/compare_hours', array( + $this->response->html($this->helper->layout->analytic('analytic/compare_hours', array( 'project' => $project, 'paginator' => $paginator, 'metrics' => $this->estimatedTimeComparisonAnalytic->build($project['id']), @@ -86,7 +71,7 @@ class Analytic extends Base { $project = $this->getProject(); - $this->response->html($this->layout('analytic/avg_time_columns', array( + $this->response->html($this->helper->layout->analytic('analytic/avg_time_columns', array( 'project' => $project, 'metrics' => $this->averageTimeSpentColumnAnalytic->build($project['id']), 'title' => t('Average time spent into each column for "%s"', $project['name']), @@ -102,7 +87,7 @@ class Analytic extends Base { $project = $this->getProject(); - $this->response->html($this->layout('analytic/tasks', array( + $this->response->html($this->helper->layout->analytic('analytic/tasks', array( 'project' => $project, 'metrics' => $this->taskDistributionAnalytic->build($project['id']), 'title' => t('Task repartition for "%s"', $project['name']), @@ -118,7 +103,7 @@ class Analytic extends Base { $project = $this->getProject(); - $this->response->html($this->layout('analytic/users', array( + $this->response->html($this->helper->layout->analytic('analytic/users', array( 'project' => $project, 'metrics' => $this->userDistributionAnalytic->build($project['id']), 'title' => t('User repartition for "%s"', $project['name']), @@ -160,7 +145,7 @@ class Analytic extends Base $display_graph = $this->projectDailyColumnStats->countDays($project['id'], $from, $to) >= 2; - $this->response->html($this->layout($template, array( + $this->response->html($this->helper->layout->analytic($template, array( 'values' => array( 'from' => $from, 'to' => $to, diff --git a/app/Controller/App.php b/app/Controller/App.php index bdd7fbcf..1ce74506 100644 --- a/app/Controller/App.php +++ b/app/Controller/App.php @@ -13,22 +13,6 @@ use Kanboard\Model\Subtask as SubtaskModel; class App extends Base { /** - * Common layout for dashboard views - * - * @access private - * @param string $template Template name - * @param array $params Template parameters - * @return string - */ - private function layout($template, array $params) - { - $params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()); - $params['content_for_sublayout'] = $this->template->render($template, $params); - - return $this->template->layout('app/layout', $params); - } - - /** * Get project pagination * * @access private @@ -101,7 +85,7 @@ class App extends Base { $user = $this->getUser(); - $this->response->html($this->layout('app/overview', array( + $this->response->html($this->helper->layout->dashboard('app/overview', array( 'title' => t('Dashboard'), 'project_paginator' => $this->getProjectPaginator($user['id'], 'index', 10), 'task_paginator' => $this->getTaskPaginator($user['id'], 'index', 10), @@ -119,7 +103,7 @@ class App extends Base { $user = $this->getUser(); - $this->response->html($this->layout('app/tasks', array( + $this->response->html($this->helper->layout->dashboard('app/tasks', array( 'title' => t('My tasks'), 'paginator' => $this->getTaskPaginator($user['id'], 'tasks', 50), 'user' => $user, @@ -135,7 +119,7 @@ class App extends Base { $user = $this->getUser(); - $this->response->html($this->layout('app/subtasks', array( + $this->response->html($this->helper->layout->dashboard('app/subtasks', array( 'title' => t('My subtasks'), 'paginator' => $this->getSubtaskPaginator($user['id'], 'subtasks', 50), 'user' => $user, @@ -151,7 +135,7 @@ class App extends Base { $user = $this->getUser(); - $this->response->html($this->layout('app/projects', array( + $this->response->html($this->helper->layout->dashboard('app/projects', array( 'title' => t('My projects'), 'paginator' => $this->getProjectPaginator($user['id'], 'projects', 25), 'user' => $user, @@ -167,7 +151,7 @@ class App extends Base { $user = $this->getUser(); - $this->response->html($this->layout('app/activity', array( + $this->response->html($this->helper->layout->dashboard('app/activity', array( 'title' => t('My activity stream'), 'events' => $this->projectActivity->getProjects($this->projectPermission->getActiveProjectIds($user['id']), 100), 'user' => $user, @@ -181,7 +165,7 @@ class App extends Base */ public function calendar() { - $this->response->html($this->layout('app/calendar', array( + $this->response->html($this->helper->layout->dashboard('app/calendar', array( 'title' => t('My calendar'), 'user' => $this->getUser(), ))); @@ -196,7 +180,7 @@ class App extends Base { $user = $this->getUser(); - $this->response->html($this->layout('app/notifications', array( + $this->response->html($this->helper->layout->dashboard('app/notifications', array( 'title' => t('My notifications'), 'notifications' => $this->userUnreadNotification->getAll($user['id']), 'user' => $user, diff --git a/app/Controller/Auth.php b/app/Controller/Auth.php index 5284e126..fef7f0e3 100644 --- a/app/Controller/Auth.php +++ b/app/Controller/Auth.php @@ -21,7 +21,7 @@ class Auth extends Base $this->response->redirect($this->helper->url->to('app', 'index')); } - $this->response->html($this->template->layout('auth/index', array( + $this->response->html($this->helper->layout->app('auth/index', array( 'captcha' => ! empty($values['username']) && $this->userLocking->hasCaptcha($values['username']), 'errors' => $errors, 'values' => $values, diff --git a/app/Controller/Base.php b/app/Controller/Base.php index efeab31e..c55ad9ad 100644 --- a/app/Controller/Base.php +++ b/app/Controller/Base.php @@ -131,7 +131,7 @@ abstract class Base extends \Kanboard\Core\Base */ protected function notfound($no_layout = false) { - $this->response->html($this->template->layout('app/notfound', array( + $this->response->html($this->helper->layout->app('app/notfound', array( 'title' => t('Page not found'), 'no_layout' => $no_layout, ))); @@ -149,7 +149,7 @@ abstract class Base extends \Kanboard\Core\Base $this->response->text('Access Forbidden', 403); } - $this->response->html($this->template->layout('app/forbidden', array( + $this->response->html($this->helper->layout->app('app/forbidden', array( 'title' => t('Access Forbidden'), 'no_layout' => $no_layout, ))); @@ -180,43 +180,6 @@ abstract class Base extends \Kanboard\Core\Base } /** - * Common layout for task views - * - * @access protected - * @param string $template Template name - * @param array $params Template parameters - * @return string - */ - protected function taskLayout($template, array $params) - { - $content = $this->template->render($template, $params); - $params['task_content_for_layout'] = $content; - $params['title'] = $params['task']['project_name'].' > '.$params['task']['title']; - $params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()); - - return $this->template->layout('task/layout', $params); - } - - /** - * Common layout for project views - * - * @access protected - * @param string $template Template name - * @param array $params Template parameters - * @return string - */ - protected function projectLayout($template, array $params, $sidebar_template = 'project/sidebar') - { - $content = $this->template->render($template, $params); - $params['project_content_for_layout'] = $content; - $params['title'] = $params['project']['name'] === $params['title'] ? $params['title'] : $params['project']['name'].' > '.$params['title']; - $params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()); - $params['sidebar_template'] = $sidebar_template; - - return $this->template->layout('project/layout', $params); - } - - /** * Common method to get a task for task views * * @access protected @@ -251,8 +214,7 @@ abstract class Base extends \Kanboard\Core\Base $project = $this->project->getByIdWithOwner($project_id); if (empty($project)) { - $this->flash->failure(t('Project not found.')); - $this->response->redirect($this->helper->url->to('project', 'index')); + $this->notfound(); } return $project; @@ -280,6 +242,23 @@ abstract class Base extends \Kanboard\Core\Base } /** + * Get the current subtask + * + * @access protected + * @return array + */ + protected function getSubtask() + { + $subtask = $this->subtask->getById($this->request->getIntegerParam('subtask_id')); + + if (empty($subtask)) { + $this->notfound(); + } + + return $subtask; + } + + /** * Common method to get project filters * * @access protected @@ -319,7 +298,8 @@ abstract class Base extends \Kanboard\Core\Base * @param array &$project * @return string */ - protected function getProjectDescription(array &$project) { + protected function getProjectDescription(array &$project) + { if ($project['owner_id'] > 0) { $description = t('Project owner: ').'**'.$this->template->e($project['owner_name'] ?: $project['owner_username']).'**'.PHP_EOL.PHP_EOL; diff --git a/app/Controller/Board.php b/app/Controller/Board.php index f64de69a..199f1703 100644 --- a/app/Controller/Board.php +++ b/app/Controller/Board.php @@ -27,7 +27,7 @@ class Board extends Base } // Display the board with a specific layout - $this->response->html($this->template->layout('board/view_public', array( + $this->response->html($this->helper->layout->app('board/view_public', array( 'project' => $project, 'swimlanes' => $this->board->getBoard($project['id']), 'title' => $project['name'], @@ -49,7 +49,7 @@ class Board extends Base { $params = $this->getProjectFilters('board', 'show'); - $this->response->html($this->template->layout('board/view_private', array( + $this->response->html($this->helper->layout->app('board/view_private', array( 'categories_list' => $this->category->getList($params['project']['id'], false), 'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false), 'custom_filters_list' => $this->customFilter->getAll($params['project']['id'], $this->userSession->getId()), diff --git a/app/Controller/BoardTooltip.php b/app/Controller/BoardTooltip.php index ed58a2f2..06f4d729 100644 --- a/app/Controller/BoardTooltip.php +++ b/app/Controller/BoardTooltip.php @@ -19,7 +19,21 @@ class BoardTooltip extends Base { $task = $this->getTask(); $this->response->html($this->template->render('board/tooltip_tasklinks', array( - 'links' => $this->taskLink->getAll($task['id']), + 'links' => $this->taskLink->getAllGroupedByLabel($task['id']), + 'task' => $task, + ))); + } + + /** + * Get links on mouseover + * + * @access public + */ + public function externallinks() + { + $task = $this->getTask(); + $this->response->html($this->template->render('board/tooltip_external_links', array( + 'links' => $this->taskExternalLink->getAll($task['id']), 'task' => $task, ))); } diff --git a/app/Controller/Calendar.php b/app/Controller/Calendar.php index 67a402d3..a0a25e41 100644 --- a/app/Controller/Calendar.php +++ b/app/Controller/Calendar.php @@ -20,7 +20,7 @@ class Calendar extends Base */ public function show() { - $this->response->html($this->template->layout('calendar/show', array( + $this->response->html($this->helper->layout->app('calendar/show', array( 'check_interval' => $this->config->get('board_private_refresh_interval'), ) + $this->getProjectFilters('calendar', 'show'))); } diff --git a/app/Controller/Category.php b/app/Controller/Category.php index a0af4139..258a3b78 100644 --- a/app/Controller/Category.php +++ b/app/Controller/Category.php @@ -38,7 +38,7 @@ class Category extends Base { $project = $this->getProject(); - $this->response->html($this->projectLayout('category/index', array( + $this->response->html($this->helper->layout->project('category/index', array( 'categories' => $this->category->getList($project['id'], false), 'values' => $values + array('project_id' => $project['id']), 'errors' => $errors, @@ -81,7 +81,7 @@ class Category extends Base $project = $this->getProject(); $category = $this->getCategory($project['id']); - $this->response->html($this->projectLayout('category/edit', array( + $this->response->html($this->helper->layout->project('category/edit', array( 'values' => empty($values) ? $category : $values, 'errors' => $errors, 'project' => $project, @@ -123,7 +123,7 @@ class Category extends Base $project = $this->getProject(); $category = $this->getCategory($project['id']); - $this->response->html($this->projectLayout('category/remove', array( + $this->response->html($this->helper->layout->project('category/remove', array( 'project' => $project, 'category' => $category, 'title' => t('Remove a category') diff --git a/app/Controller/Column.php b/app/Controller/Column.php index 1ce575d7..3201c549 100644 --- a/app/Controller/Column.php +++ b/app/Controller/Column.php @@ -26,7 +26,7 @@ class Column extends Base $values['task_limit['.$column['id'].']'] = $column['task_limit'] ?: null; } - $this->response->html($this->projectLayout('column/index', array( + $this->response->html($this->helper->layout->project('column/index', array( 'errors' => $errors, 'values' => $values + array('project_id' => $project['id']), 'columns' => $columns, @@ -75,7 +75,7 @@ class Column extends Base $project = $this->getProject(); $column = $this->board->getColumn($this->request->getIntegerParam('column_id')); - $this->response->html($this->projectLayout('column/edit', array( + $this->response->html($this->helper->layout->project('column/edit', array( 'errors' => $errors, 'values' => $values ?: $column, 'project' => $project, @@ -136,7 +136,7 @@ class Column extends Base { $project = $this->getProject(); - $this->response->html($this->projectLayout('column/remove', array( + $this->response->html($this->helper->layout->project('column/remove', array( 'column' => $this->board->getColumn($this->request->getIntegerParam('column_id')), 'project' => $project, 'title' => t('Remove a column from a board') diff --git a/app/Controller/Comment.php b/app/Controller/Comment.php index a608dd1c..da3213e0 100644 --- a/app/Controller/Comment.php +++ b/app/Controller/Comment.php @@ -21,13 +21,11 @@ class Comment extends Base $comment = $this->comment->getById($this->request->getIntegerParam('comment_id')); if (empty($comment)) { - $this->notfound(); + return $this->notfound(); } if (! $this->userSession->isAdmin() && $comment['user_id'] != $this->userSession->getId()) { - $this->response->html($this->template->layout('comment/forbidden', array( - 'title' => t('Access Forbidden') - ))); + return $this->forbidden(); } return $comment; @@ -41,7 +39,6 @@ class Comment extends Base public function create(array $values = array(), array $errors = array()) { $task = $this->getTask(); - $ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax'); if (empty($values)) { $values = array( @@ -50,16 +47,7 @@ class Comment extends Base ); } - if ($ajax) { - $this->response->html($this->template->render('comment/create', array( - 'values' => $values, - 'errors' => $errors, - 'task' => $task, - 'ajax' => $ajax, - ))); - } - - $this->response->html($this->taskLayout('comment/create', array( + $this->response->html($this->helper->layout->task('comment/create', array( 'values' => $values, 'errors' => $errors, 'task' => $task, @@ -76,7 +64,6 @@ class Comment extends Base { $task = $this->getTask(); $values = $this->request->getValues(); - $ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax'); list($valid, $errors) = $this->commentValidator->validateCreation($values); @@ -87,11 +74,7 @@ class Comment extends Base $this->flash->failure(t('Unable to create your comment.')); } - if ($ajax) { - $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); - } - - $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments')); + return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'), true); } $this->create($values, $errors); @@ -107,7 +90,7 @@ class Comment extends Base $task = $this->getTask(); $comment = $this->getComment(); - $this->response->html($this->taskLayout('comment/edit', array( + $this->response->html($this->helper->layout->task('comment/edit', array( 'values' => empty($values) ? $comment : $values, 'errors' => $errors, 'comment' => $comment, @@ -124,7 +107,7 @@ class Comment extends Base public function update() { $task = $this->getTask(); - $comment = $this->getComment(); + $this->getComment(); $values = $this->request->getValues(); list($valid, $errors) = $this->commentValidator->validateModification($values); @@ -136,7 +119,7 @@ class Comment extends Base $this->flash->failure(t('Unable to update your comment.')); } - $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comment-'.$comment['id'])); + return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), false); } $this->edit($values, $errors); @@ -152,7 +135,7 @@ class Comment extends Base $task = $this->getTask(); $comment = $this->getComment(); - $this->response->html($this->taskLayout('comment/remove', array( + $this->response->html($this->helper->layout->task('comment/remove', array( 'comment' => $comment, 'task' => $task, 'title' => t('Remove a comment') diff --git a/app/Controller/Config.php b/app/Controller/Config.php index 4aee8553..53f7cdb6 100644 --- a/app/Controller/Config.php +++ b/app/Controller/Config.php @@ -11,24 +11,6 @@ namespace Kanboard\Controller; class Config extends Base { /** - * Common layout for config views - * - * @access private - * @param string $template Template name - * @param array $params Template parameters - * @return string - */ - private function layout($template, array $params) - { - $params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()); - $params['values'] = $this->config->getAll(); - $params['errors'] = array(); - $params['config_content_for_layout'] = $this->template->render($template, $params); - - return $this->template->layout('config/layout', $params); - } - - /** * Common method between pages * * @access private @@ -72,7 +54,7 @@ class Config extends Base */ public function index() { - $this->response->html($this->layout('config/about', array( + $this->response->html($this->helper->layout->config('config/about', array( 'db_size' => $this->config->getDatabaseSize(), 'title' => t('Settings').' > '.t('About'), ))); @@ -85,7 +67,7 @@ class Config extends Base */ public function plugins() { - $this->response->html($this->layout('config/plugins', array( + $this->response->html($this->helper->layout->config('config/plugins', array( 'plugins' => $this->pluginLoader->plugins, 'title' => t('Settings').' > '.t('Plugins'), ))); @@ -100,7 +82,7 @@ class Config extends Base { $this->common('application'); - $this->response->html($this->layout('config/application', array( + $this->response->html($this->helper->layout->config('config/application', array( 'languages' => $this->config->getLanguages(), 'timezones' => $this->config->getTimezones(), 'date_formats' => $this->dateParser->getAvailableFormats(), @@ -117,7 +99,7 @@ class Config extends Base { $this->common('project'); - $this->response->html($this->layout('config/project', array( + $this->response->html($this->helper->layout->config('config/project', array( 'colors' => $this->color->getList(), 'default_columns' => implode(', ', $this->board->getDefaultColumns()), 'title' => t('Settings').' > '.t('Project settings'), @@ -133,7 +115,7 @@ class Config extends Base { $this->common('board'); - $this->response->html($this->layout('config/board', array( + $this->response->html($this->helper->layout->config('config/board', array( 'title' => t('Settings').' > '.t('Board settings'), ))); } @@ -147,7 +129,7 @@ class Config extends Base { $this->common('calendar'); - $this->response->html($this->layout('config/calendar', array( + $this->response->html($this->helper->layout->config('config/calendar', array( 'title' => t('Settings').' > '.t('Calendar settings'), ))); } @@ -161,7 +143,7 @@ class Config extends Base { $this->common('integrations'); - $this->response->html($this->layout('config/integrations', array( + $this->response->html($this->helper->layout->config('config/integrations', array( 'title' => t('Settings').' > '.t('Integrations'), ))); } @@ -175,7 +157,7 @@ class Config extends Base { $this->common('webhook'); - $this->response->html($this->layout('config/webhook', array( + $this->response->html($this->helper->layout->config('config/webhook', array( 'title' => t('Settings').' > '.t('Webhook settings'), ))); } @@ -187,7 +169,7 @@ class Config extends Base */ public function api() { - $this->response->html($this->layout('config/api', array( + $this->response->html($this->helper->layout->config('config/api', array( 'title' => t('Settings').' > '.t('API'), ))); } diff --git a/app/Controller/Currency.php b/app/Controller/Currency.php index 42e404f8..ecaa9834 100644 --- a/app/Controller/Currency.php +++ b/app/Controller/Currency.php @@ -11,29 +11,13 @@ namespace Kanboard\Controller; class Currency extends Base { /** - * Common layout for config views - * - * @access private - * @param string $template Template name - * @param array $params Template parameters - * @return string - */ - private function layout($template, array $params) - { - $params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()); - $params['config_content_for_layout'] = $this->template->render($template, $params); - - return $this->template->layout('config/layout', $params); - } - - /** * Display all currency rates and form * * @access public */ public function index(array $values = array(), array $errors = array()) { - $this->response->html($this->layout('currency/index', array( + $this->response->html($this->helper->layout->config('currency/index', array( 'config_values' => array('application_currency' => $this->config->get('application_currency')), 'values' => $values, 'errors' => $errors, diff --git a/app/Controller/Customfilter.php b/app/Controller/Customfilter.php index 1b43f1d0..da7eb77b 100644 --- a/app/Controller/Customfilter.php +++ b/app/Controller/Customfilter.php @@ -21,7 +21,7 @@ class Customfilter extends Base { $project = $this->getProject(); - $this->response->html($this->projectLayout('custom_filter/index', array( + $this->response->html($this->helper->layout->project('custom_filter/index', array( 'values' => $values + array('project_id' => $project['id']), 'errors' => $errors, 'project' => $project, @@ -90,7 +90,7 @@ class Customfilter extends Base $this->checkPermission($project, $filter); - $this->response->html($this->projectLayout('custom_filter/edit', array( + $this->response->html($this->helper->layout->project('custom_filter/edit', array( 'values' => empty($values) ? $filter : $values, 'errors' => $errors, 'project' => $project, diff --git a/app/Controller/Doc.php b/app/Controller/Doc.php index a233b120..6f309d48 100644 --- a/app/Controller/Doc.php +++ b/app/Controller/Doc.php @@ -52,8 +52,6 @@ class Doc extends Base } } - $this->response->html($this->template->layout('doc/show', $this->readFile($filename) + array( - 'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()), - ))); + $this->response->html($this->helper->layout->app('doc/show', $this->readFile($filename))); } } diff --git a/app/Controller/Export.php b/app/Controller/Export.php index c39f58a1..977a4107 100644 --- a/app/Controller/Export.php +++ b/app/Controller/Export.php @@ -27,7 +27,7 @@ class Export extends Base $this->response->csv($data); } - $this->response->html($this->projectLayout('export/'.$action, array( + $this->response->html($this->helper->layout->project('export/'.$action, array( 'values' => array( 'controller' => 'export', 'action' => $action, diff --git a/app/Controller/File.php b/app/Controller/File.php index b46f7d19..50db3865 100644 --- a/app/Controller/File.php +++ b/app/Controller/File.php @@ -23,17 +23,11 @@ class File extends Base if ($this->request->isPost() && $this->file->uploadScreenshot($task['project_id'], $task['id'], $this->request->getValue('screenshot')) !== false) { $this->flash->success(t('Screenshot uploaded successfully.')); - - if ($this->request->getStringParam('redirect') === 'board') { - $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); - } - - $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))); + return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true); } - $this->response->html($this->taskLayout('file/screenshot', array( + $this->response->html($this->helper->layout->task('file/screenshot', array( 'task' => $task, - 'redirect' => 'task', ))); } @@ -46,7 +40,7 @@ class File extends Base { $task = $this->getTask(); - $this->response->html($this->taskLayout('file/new', array( + $this->response->html($this->helper->layout->task('file/new', array( 'task' => $task, 'max_size' => ini_get('upload_max_filesize'), ))); @@ -65,7 +59,7 @@ class File extends Base $this->flash->failure(t('Unable to upload the file.')); } - $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))); + $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true); } /** @@ -184,7 +178,7 @@ class File extends Base $task = $this->getTask(); $file = $this->file->getById($this->request->getIntegerParam('file_id')); - $this->response->html($this->taskLayout('file/remove', array( + $this->response->html($this->helper->layout->task('file/remove', array( 'task' => $task, 'file' => $file, ))); diff --git a/app/Controller/Gantt.php b/app/Controller/Gantt.php index ac0e6fad..2d1edc08 100644 --- a/app/Controller/Gantt.php +++ b/app/Controller/Gantt.php @@ -23,10 +23,9 @@ class Gantt extends Base $project_ids = $this->projectPermission->getActiveProjectIds($this->userSession->getId()); } - $this->response->html($this->template->layout('gantt/projects', array( + $this->response->html($this->helper->layout->app('gantt/projects', array( 'projects' => $this->projectGanttFormatter->filter($project_ids)->format(), 'title' => t('Gantt chart for all projects'), - 'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()), ))); } @@ -65,7 +64,7 @@ class Gantt extends Base $filter->getQuery()->asc('column_position')->asc(TaskModel::TABLE.'.position'); } - $this->response->html($this->template->layout('gantt/project', $params + array( + $this->response->html($this->helper->layout->app('gantt/project', $params + array( 'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false), 'sorting' => $sorting, 'tasks' => $filter->format(), @@ -102,19 +101,22 @@ class Gantt extends Base { $project = $this->getProject(); + $values = $values + array( + 'project_id' => $project['id'], + 'column_id' => $this->board->getFirstColumn($project['id']), + 'position' => 1 + ); + + $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values)); + $this->response->html($this->template->render('gantt/task_creation', array( + 'project' => $project, 'errors' => $errors, - 'values' => $values + array( - 'project_id' => $project['id'], - 'column_id' => $this->board->getFirstColumn($project['id']), - 'position' => 1 - ), + 'values' => $values, 'users_list' => $this->projectUserRole->getAssignableUsersList($project['id'], true, false, true), 'colors_list' => $this->color->getList(), 'categories_list' => $this->category->getList($project['id']), 'swimlanes_list' => $this->swimlane->getList($project['id'], false, true), - 'date_format' => $this->config->get('application_date_format'), - 'date_formats' => $this->dateParser->getAvailableFormats(), 'title' => $project['name'].' > '.t('New task') ))); } diff --git a/app/Controller/Group.php b/app/Controller/Group.php index e952c0e5..fa47f428 100644 --- a/app/Controller/Group.php +++ b/app/Controller/Group.php @@ -24,8 +24,7 @@ class Group extends Base ->setQuery($this->group->getQuery()) ->calculate(); - $this->response->html($this->template->layout('group/index', array( - 'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()), + $this->response->html($this->helper->layout->app('group/index', array( 'title' => t('Groups').' ('.$paginator->getTotal().')', 'paginator' => $paginator, ))); @@ -48,8 +47,7 @@ class Group extends Base ->setQuery($this->groupMember->getQuery($group_id)) ->calculate(); - $this->response->html($this->template->layout('group/users', array( - 'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()), + $this->response->html($this->helper->layout->app('group/users', array( 'title' => t('Members of %s', $group['name']).' ('.$paginator->getTotal().')', 'paginator' => $paginator, 'group' => $group, @@ -63,8 +61,7 @@ class Group extends Base */ public function create(array $values = array(), array $errors = array()) { - $this->response->html($this->template->layout('group/create', array( - 'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()), + $this->response->html($this->helper->layout->app('group/create', array( 'errors' => $errors, 'values' => $values, 'title' => t('New group') @@ -104,8 +101,7 @@ class Group extends Base $values = $this->group->getById($this->request->getIntegerParam('group_id')); } - $this->response->html($this->template->layout('group/edit', array( - 'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()), + $this->response->html($this->helper->layout->app('group/edit', array( 'errors' => $errors, 'values' => $values, 'title' => t('Edit group') @@ -148,8 +144,7 @@ class Group extends Base $values['group_id'] = $group_id; } - $this->response->html($this->template->layout('group/associate', array( - 'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()), + $this->response->html($this->helper->layout->app('group/associate', array( 'users' => $this->user->prepareList($this->groupMember->getNotMembers($group_id)), 'group' => $group, 'errors' => $errors, @@ -191,7 +186,7 @@ class Group extends Base $group = $this->group->getById($group_id); $user = $this->user->getById($user_id); - $this->response->html($this->template->layout('group/dissociate', array( + $this->response->html($this->helper->layout->app('group/dissociate', array( 'group' => $group, 'user' => $user, 'title' => t('Remove user from group "%s"', $group['name']), @@ -228,7 +223,7 @@ class Group extends Base $group_id = $this->request->getIntegerParam('group_id'); $group = $this->group->getById($group_id); - $this->response->html($this->template->layout('group/remove', array( + $this->response->html($this->helper->layout->app('group/remove', array( 'group' => $group, 'title' => t('Remove group'), ))); diff --git a/app/Controller/Link.php b/app/Controller/Link.php index d52d1f91..ec7ab1af 100644 --- a/app/Controller/Link.php +++ b/app/Controller/Link.php @@ -12,22 +12,6 @@ namespace Kanboard\Controller; class Link extends Base { /** - * Common layout for config views - * - * @access private - * @param string $template Template name - * @param array $params Template parameters - * @return string - */ - private function layout($template, array $params) - { - $params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()); - $params['config_content_for_layout'] = $this->template->render($template, $params); - - return $this->template->layout('config/layout', $params); - } - - /** * Get the current link * * @access private @@ -51,7 +35,7 @@ class Link extends Base */ public function index(array $values = array(), array $errors = array()) { - $this->response->html($this->layout('link/index', array( + $this->response->html($this->helper->layout->config('link/index', array( 'links' => $this->link->getMergedList(), 'values' => $values, 'errors' => $errors, @@ -91,7 +75,7 @@ class Link extends Base $link = $this->getLink(); $link['label'] = t($link['label']); - $this->response->html($this->layout('link/edit', array( + $this->response->html($this->helper->layout->config('link/edit', array( 'values' => $values ?: $link, 'errors' => $errors, 'labels' => $this->link->getList($link['id']), @@ -131,7 +115,7 @@ class Link extends Base { $link = $this->getLink(); - $this->response->html($this->layout('link/remove', array( + $this->response->html($this->helper->layout->config('link/remove', array( 'link' => $link, 'title' => t('Remove a link') ))); diff --git a/app/Controller/Listing.php b/app/Controller/Listing.php index b9c851f5..c7d3d9a8 100644 --- a/app/Controller/Listing.php +++ b/app/Controller/Listing.php @@ -30,7 +30,7 @@ class Listing extends Base ->setQuery($query) ->calculate(); - $this->response->html($this->template->layout('listing/show', $params + array( + $this->response->html($this->helper->layout->app('listing/show', $params + array( 'paginator' => $paginator, ))); } diff --git a/app/Controller/Oauth.php b/app/Controller/Oauth.php index ed901def..452faecd 100644 --- a/app/Controller/Oauth.php +++ b/app/Controller/Oauth.php @@ -11,36 +11,6 @@ namespace Kanboard\Controller; class Oauth extends Base { /** - * Link or authenticate a Google account - * - * @access public - */ - public function google() - { - $this->step1('Google'); - } - - /** - * Link or authenticate a Github account - * - * @access public - */ - public function github() - { - $this->step1('Github'); - } - - /** - * Link or authenticate a Gitlab account - * - * @access public - */ - public function gitlab() - { - $this->step1('Gitlab'); - } - - /** * Unlink external account * * @access public @@ -65,7 +35,7 @@ class Oauth extends Base * @access private * @param string $provider */ - private function step1($provider) + protected function step1($provider) { $code = $this->request->getStringParam('code'); @@ -79,11 +49,11 @@ class Oauth extends Base /** * Link or authenticate the user * - * @access private + * @access protected * @param string $provider * @param string $code */ - private function step2($provider, $code) + protected function step2($provider, $code) { $this->authenticationManager->getProvider($provider)->setCode($code); @@ -97,10 +67,10 @@ class Oauth extends Base /** * Link the account * - * @access private + * @access protected * @param string $provider */ - private function link($provider) + protected function link($provider) { $authProvider = $this->authenticationManager->getProvider($provider); @@ -117,15 +87,15 @@ class Oauth extends Base /** * Authenticate the account * - * @access private + * @access protected * @param string $provider */ - private function authenticate($provider) + protected function authenticate($provider) { if ($this->authenticationManager->oauthAuthentication($provider)) { $this->response->redirect($this->helper->url->to('app', 'index')); } else { - $this->response->html($this->template->layout('auth/index', array( + $this->response->html($this->helper->layout->app('auth/index', array( 'errors' => array('login' => t('External authentication failed')), 'values' => array(), 'no_layout' => true, diff --git a/app/Controller/PasswordReset.php b/app/Controller/PasswordReset.php index 23567c9c..f6a0eb8e 100644 --- a/app/Controller/PasswordReset.php +++ b/app/Controller/PasswordReset.php @@ -17,7 +17,7 @@ class PasswordReset extends Base { $this->checkActivation(); - $this->response->html($this->template->layout('password_reset/create', array( + $this->response->html($this->helper->layout->app('password_reset/create', array( 'errors' => $errors, 'values' => $values, 'no_layout' => true, @@ -53,7 +53,7 @@ class PasswordReset extends Base $user_id = $this->passwordReset->getUserIdByToken($token); if ($user_id !== false) { - $this->response->html($this->template->layout('password_reset/change', array( + $this->response->html($this->helper->layout->app('password_reset/change', array( 'token' => $token, 'errors' => $errors, 'values' => $values, diff --git a/app/Controller/Project.php b/app/Controller/Project.php index ffd62b09..cdfbd94a 100644 --- a/app/Controller/Project.php +++ b/app/Controller/Project.php @@ -32,8 +32,7 @@ class Project extends Base ->setQuery($this->project->getQueryColumnStats($project_ids)) ->calculate(); - $this->response->html($this->template->layout('project/index', array( - 'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()), + $this->response->html($this->helper->layout->app('project/index', array( 'paginator' => $paginator, 'nb_projects' => $nb_projects, 'title' => t('Projects').' ('.$nb_projects.')' @@ -49,7 +48,7 @@ class Project extends Base { $project = $this->getProject(); - $this->response->html($this->projectLayout('project/show', array( + $this->response->html($this->helper->layout->project('project/show', array( 'project' => $project, 'stats' => $this->project->getTaskStats($project['id']), 'title' => $project['name'], @@ -78,7 +77,7 @@ class Project extends Base $this->response->redirect($this->helper->url->to('project', 'share', array('project_id' => $project['id']))); } - $this->response->html($this->projectLayout('project/share', array( + $this->response->html($this->helper->layout->project('project/share', array( 'project' => $project, 'title' => t('Public access'), ))); @@ -99,7 +98,7 @@ class Project extends Base $this->response->redirect($this->helper->url->to('project', 'integrations', array('project_id' => $project['id']))); } - $this->response->html($this->projectLayout('project/integrations', array( + $this->response->html($this->helper->layout->project('project/integrations', array( 'project' => $project, 'title' => t('Integrations'), 'webhook_token' => $this->config->get('webhook_token'), @@ -124,7 +123,7 @@ class Project extends Base $this->response->redirect($this->helper->url->to('project', 'notifications', array('project_id' => $project['id']))); } - $this->response->html($this->projectLayout('project/notifications', array( + $this->response->html($this->helper->layout->project('project/notifications', array( 'notifications' => $this->projectNotification->readSettings($project['id']), 'types' => $this->projectNotificationType->getTypes(), 'project' => $project, @@ -153,7 +152,7 @@ class Project extends Base $this->response->redirect($this->helper->url->to('project', 'index')); } - $this->response->html($this->projectLayout('project/remove', array( + $this->response->html($this->helper->layout->project('project/remove', array( 'project' => $project, 'title' => t('Remove project') ))); @@ -171,17 +170,18 @@ class Project extends Base $project = $this->getProject(); if ($this->request->getStringParam('duplicate') === 'yes') { - $values = array_keys($this->request->getValues()); - if ($this->projectDuplication->duplicate($project['id'], $values) !== false) { + $project_id = $this->projectDuplication->duplicate($project['id'], array_keys($this->request->getValues()), $this->userSession->getId()); + + if ($project_id !== false) { $this->flash->success(t('Project cloned successfully.')); } else { $this->flash->failure(t('Unable to clone this project.')); } - $this->response->redirect($this->helper->url->to('project', 'index')); + $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id))); } - $this->response->html($this->projectLayout('project/duplicate', array( + $this->response->html($this->helper->layout->project('project/duplicate', array( 'project' => $project, 'title' => t('Clone this project') ))); @@ -208,7 +208,7 @@ class Project extends Base $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id']))); } - $this->response->html($this->projectLayout('project/disable', array( + $this->response->html($this->helper->layout->project('project/disable', array( 'project' => $project, 'title' => t('Project activation') ))); @@ -235,62 +235,9 @@ class Project extends Base $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id']))); } - $this->response->html($this->projectLayout('project/enable', array( + $this->response->html($this->helper->layout->project('project/enable', array( 'project' => $project, 'title' => t('Project activation') ))); } - - /** - * Display a form to create a new project - * - * @access public - */ - public function create(array $values = array(), array $errors = array()) - { - $is_private = isset($values['is_private']) && $values['is_private'] == 1; - - $this->response->html($this->template->layout('project/new', array( - 'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()), - 'values' => $values, - 'errors' => $errors, - 'is_private' => $is_private, - 'title' => $is_private ? t('New private project') : t('New project'), - ))); - } - - /** - * Display a form to create a private project - * - * @access public - */ - public function createPrivate(array $values = array(), array $errors = array()) - { - $values['is_private'] = 1; - $this->create($values, $errors); - } - - /** - * Validate and save a new project - * - * @access public - */ - public function save() - { - $values = $this->request->getValues(); - list($valid, $errors) = $this->projectValidator->validateCreation($values); - - if ($valid) { - $project_id = $this->project->create($values, $this->userSession->getId(), true); - - if ($project_id > 0) { - $this->flash->success(t('Your project have been created successfully.')); - $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id))); - } - - $this->flash->failure(t('Unable to create your project.')); - } - - $this->create($values, $errors); - } } diff --git a/app/Controller/ProjectCreation.php b/app/Controller/ProjectCreation.php new file mode 100644 index 00000000..61ea2586 --- /dev/null +++ b/app/Controller/ProjectCreation.php @@ -0,0 +1,125 @@ +<?php + +namespace Kanboard\Controller; + +/** + * Project Creation Controller + * + * @package controller + * @author Frederic Guillot + */ +class ProjectCreation extends Base +{ + /** + * Display a form to create a new project + * + * @access public + */ + public function create(array $values = array(), array $errors = array()) + { + $is_private = isset($values['is_private']) && $values['is_private'] == 1; + $projects_list = array(0 => t('Do not duplicate anything')) + $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()); + + $this->response->html($this->helper->layout->app('project_creation/create', array( + 'values' => $values, + 'errors' => $errors, + 'is_private' => $is_private, + 'projects_list' => $projects_list, + 'title' => $is_private ? t('New private project') : t('New project'), + ))); + } + + /** + * Display a form to create a private project + * + * @access public + */ + public function createPrivate(array $values = array(), array $errors = array()) + { + $values['is_private'] = 1; + $this->create($values, $errors); + } + + /** + * Validate and save a new project + * + * @access public + */ + public function save() + { + $values = $this->request->getValues(); + list($valid, $errors) = $this->projectValidator->validateCreation($values); + + if ($valid) { + $project_id = $this->createOrDuplicate($values); + + if ($project_id > 0) { + $this->flash->success(t('Your project have been created successfully.')); + return $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id))); + } + + $this->flash->failure(t('Unable to create your project.')); + } + + $this->create($values, $errors); + } + + /** + * Create or duplicate a project + * + * @access private + * @param array $values + * @return boolean|integer + */ + private function createOrDuplicate(array $values) + { + if ($values['src_project_id'] == 0) { + return $this->createNewProject($values); + } + + return $this->duplicateNewProject($values); + } + + /** + * Save a new project + * + * @access private + * @param array $values + * @return boolean|integer + */ + private function createNewProject(array $values) + { + $project = array( + 'name' => $values['name'], + 'is_private' => $values['is_private'], + ); + + return $this->project->create($project, $this->userSession->getId(), true); + } + + /** + * Creatte from another project + * + * @access private + * @param array $values + * @return boolean|integer + */ + private function duplicateNewProject(array $values) + { + $selection = array(); + + foreach ($this->projectDuplication->getOptionalSelection() as $item) { + if (isset($values[$item]) && $values[$item] == 1) { + $selection[] = $item; + } + } + + return $this->projectDuplication->duplicate( + $values['src_project_id'], + $selection, + $this->userSession->getId(), + $values['name'], + $values['is_private'] == 1 + ); + } +} diff --git a/app/Controller/ProjectEdit.php b/app/Controller/ProjectEdit.php index 0dfc7de3..f4a3a7cb 100644 --- a/app/Controller/ProjectEdit.php +++ b/app/Controller/ProjectEdit.php @@ -89,11 +89,11 @@ class ProjectEdit extends Base { if ($redirect === 'edit') { if (isset($values['is_private'])) { - if (! $this->helper->user->hasProjectAccess('project', 'create', $project['id'])) { + if (! $this->helper->user->hasProjectAccess('ProjectCreation', 'create', $project['id'])) { unset($values['is_private']); } } elseif ($project['is_private'] == 1 && ! isset($values['is_private'])) { - if ($this->helper->user->hasProjectAccess('project', 'create', $project['id'])) { + if ($this->helper->user->hasProjectAccess('ProjectCreation', 'create', $project['id'])) { $values += array('is_private' => 0); } } @@ -114,7 +114,7 @@ class ProjectEdit extends Base { $project = $this->getProject(); - $this->response->html($this->projectLayout($template, array( + $this->response->html($this->helper->layout->project($template, array( 'owners' => $this->projectUserRole->getAssignableUsersList($project['id'], true), 'values' => empty($values) ? $project : $values, 'errors' => $errors, diff --git a/app/Controller/ProjectPermission.php b/app/Controller/ProjectPermission.php index 4434d017..800da02f 100644 --- a/app/Controller/ProjectPermission.php +++ b/app/Controller/ProjectPermission.php @@ -13,6 +13,24 @@ use Kanboard\Core\Security\Role; class ProjectPermission extends Base { /** + * Permissions are only available for team projects + * + * @access protected + * @param integer $project_id Default project id + * @return array + */ + protected function getProject($project_id = 0) + { + $project = parent::getProject($project_id); + + if ($project['is_private'] == 1) { + $this->forbidden(); + } + + return $project; + } + + /** * Show all permissions * * @access public @@ -25,7 +43,7 @@ class ProjectPermission extends Base $values['role'] = Role::PROJECT_MEMBER; } - $this->response->html($this->projectLayout('project_permission/index', array( + $this->response->html($this->helper->layout->project('project_permission/index', array( 'project' => $project, 'users' => $this->projectUserRole->getUsers($project['id']), 'groups' => $this->projectGroupRole->getGroups($project['id']), @@ -62,6 +80,7 @@ class ProjectPermission extends Base */ public function addUser() { + $project = $this->getProject(); $values = $this->request->getValues(); if ($this->projectUserRole->addUser($values['project_id'], $values['user_id'], $values['role'])) { @@ -70,7 +89,7 @@ class ProjectPermission extends Base $this->flash->failure(t('Unable to update this project.')); } - $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id']))); + $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id']))); } /** @@ -81,19 +100,16 @@ class ProjectPermission extends Base public function removeUser() { $this->checkCSRFParam(); + $project = $this->getProject(); + $user_id = $this->request->getIntegerParam('user_id'); - $values = array( - 'project_id' => $this->request->getIntegerParam('project_id'), - 'user_id' => $this->request->getIntegerParam('user_id'), - ); - - if ($this->projectUserRole->removeUser($values['project_id'], $values['user_id'])) { + if ($this->projectUserRole->removeUser($project['id'], $user_id)) { $this->flash->success(t('Project updated successfully.')); } else { $this->flash->failure(t('Unable to update this project.')); } - $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id']))); + $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id']))); } /** @@ -103,10 +119,10 @@ class ProjectPermission extends Base */ public function changeUserRole() { - $project_id = $this->request->getIntegerParam('project_id'); + $project = $this->getProject(); $values = $this->request->getJson(); - if (! empty($project_id) && ! empty($values) && $this->projectUserRole->changeUserRole($project_id, $values['id'], $values['role'])) { + if (! empty($project) && ! empty($values) && $this->projectUserRole->changeUserRole($project['id'], $values['id'], $values['role'])) { $this->response->json(array('status' => 'ok')); } else { $this->response->json(array('status' => 'error')); @@ -120,19 +136,20 @@ class ProjectPermission extends Base */ public function addGroup() { + $project = $this->getProject(); $values = $this->request->getValues(); if (empty($values['group_id']) && ! empty($values['external_id'])) { $values['group_id'] = $this->group->create($values['name'], $values['external_id']); } - if ($this->projectGroupRole->addGroup($values['project_id'], $values['group_id'], $values['role'])) { + if ($this->projectGroupRole->addGroup($project['id'], $values['group_id'], $values['role'])) { $this->flash->success(t('Project updated successfully.')); } else { $this->flash->failure(t('Unable to update this project.')); } - $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id']))); + $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id']))); } /** @@ -143,19 +160,16 @@ class ProjectPermission extends Base public function removeGroup() { $this->checkCSRFParam(); + $project = $this->getProject(); + $group_id = $this->request->getIntegerParam('group_id'); - $values = array( - 'project_id' => $this->request->getIntegerParam('project_id'), - 'group_id' => $this->request->getIntegerParam('group_id'), - ); - - if ($this->projectGroupRole->removeGroup($values['project_id'], $values['group_id'])) { + if ($this->projectGroupRole->removeGroup($project['id'], $group_id)) { $this->flash->success(t('Project updated successfully.')); } else { $this->flash->failure(t('Unable to update this project.')); } - $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id']))); + $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id']))); } /** @@ -165,10 +179,10 @@ class ProjectPermission extends Base */ public function changeGroupRole() { - $project_id = $this->request->getIntegerParam('project_id'); + $project = $this->getProject(); $values = $this->request->getJson(); - if (! empty($project_id) && ! empty($values) && $this->projectGroupRole->changeGroupRole($project_id, $values['id'], $values['role'])) { + if (! empty($project) && ! empty($values) && $this->projectGroupRole->changeGroupRole($project['id'], $values['id'], $values['role'])) { $this->response->json(array('status' => 'ok')); } else { $this->response->json(array('status' => 'error')); diff --git a/app/Controller/Projectuser.php b/app/Controller/Projectuser.php index 78b93ab6..9cd21021 100644 --- a/app/Controller/Projectuser.php +++ b/app/Controller/Projectuser.php @@ -14,23 +14,6 @@ use Kanboard\Core\Security\Role; */ class Projectuser extends Base { - /** - * Common layout for users overview views - * - * @access private - * @param string $template Template name - * @param array $params Template parameters - * @return string - */ - private function layout($template, array $params) - { - $params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()); - $params['content_for_sublayout'] = $this->template->render($template, $params); - $params['filter'] = array('user_id' => $params['user_id']); - - return $this->template->layout('project_user/layout', $params); - } - private function common() { $user_id = $this->request->getIntegerParam('user_id', UserModel::EVERYBODY_ID); @@ -62,7 +45,7 @@ class Projectuser extends Base ->setQuery($query) ->calculate(); - $this->response->html($this->layout('project_user/roles', array( + $this->response->html($this->helper->layout->projectUser('project_user/roles', array( 'paginator' => $paginator, 'title' => $title, 'user_id' => $user_id, @@ -88,7 +71,7 @@ class Projectuser extends Base ->setQuery($query) ->calculate(); - $this->response->html($this->layout('project_user/tasks', array( + $this->response->html($this->helper->layout->projectUser('project_user/tasks', array( 'paginator' => $paginator, 'title' => $title, 'user_id' => $user_id, diff --git a/app/Controller/Search.php b/app/Controller/Search.php index 390210c0..9b9b9e65 100644 --- a/app/Controller/Search.php +++ b/app/Controller/Search.php @@ -36,8 +36,7 @@ class Search extends Base $nb_tasks = $paginator->getTotal(); } - $this->response->html($this->template->layout('search/index', array( - 'board_selector' => $projects, + $this->response->html($this->helper->layout->app('search/index', array( 'values' => array( 'search' => $search, 'controller' => 'search', diff --git a/app/Controller/Subtask.php b/app/Controller/Subtask.php index caaaa85e..f8798906 100644 --- a/app/Controller/Subtask.php +++ b/app/Controller/Subtask.php @@ -2,8 +2,6 @@ namespace Kanboard\Controller; -use Kanboard\Model\Subtask as SubtaskModel; - /** * Subtask controller * @@ -13,20 +11,20 @@ use Kanboard\Model\Subtask as SubtaskModel; class Subtask extends Base { /** - * Get the current subtask - * - * @access private - * @return array + * Show list of subtasks */ - private function getSubtask() + public function show() { - $subtask = $this->subtask->getById($this->request->getIntegerParam('subtask_id')); - - if (empty($subtask)) { - $this->notfound(); - } + $task = $this->getTask(); - return $subtask; + $this->response->html($this->helper->layout->task('subtask/show', array( + 'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']), + 'task' => $task, + 'project' => $this->getProject(), + 'subtasks' => $this->subtask->getAll($task['id']), + 'editable' => true, + 'redirect' => 'subtask', + ))); } /** @@ -45,7 +43,7 @@ class Subtask extends Base ); } - $this->response->html($this->taskLayout('subtask/create', array( + $this->response->html($this->helper->layout->task('subtask/create', array( 'values' => $values, 'errors' => $errors, 'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']), @@ -73,10 +71,10 @@ class Subtask extends Base } if (isset($values['another_subtask']) && $values['another_subtask'] == 1) { - $this->response->redirect($this->helper->url->to('subtask', 'create', array('project_id' => $task['project_id'], 'task_id' => $task['id'], 'another_subtask' => 1))); + return $this->create(array('project_id' => $task['project_id'], 'task_id' => $task['id'], 'another_subtask' => 1)); } - $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks')); + return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'), true); } $this->create($values, $errors); @@ -92,7 +90,7 @@ class Subtask extends Base $task = $this->getTask(); $subtask = $this->getSubTask(); - $this->response->html($this->taskLayout('subtask/edit', array( + $this->response->html($this->helper->layout->task('subtask/edit', array( 'values' => empty($values) ? $subtask : $values, 'errors' => $errors, 'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']), @@ -122,7 +120,7 @@ class Subtask extends Base $this->flash->failure(t('Unable to update your sub-task.')); } - $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks')); + return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true); } $this->edit($values, $errors); @@ -138,7 +136,7 @@ class Subtask extends Base $task = $this->getTask(); $subtask = $this->getSubtask(); - $this->response->html($this->taskLayout('subtask/remove', array( + $this->response->html($this->helper->layout->task('subtask/remove', array( 'subtask' => $subtask, 'task' => $task, ))); @@ -161,97 +159,7 @@ class Subtask extends Base $this->flash->failure(t('Unable to remove this sub-task.')); } - $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks')); - } - - /** - * Change status to the next status: Toto -> In Progress -> Done - * - * @access public - */ - public function toggleStatus() - { - $task = $this->getTask(); - $subtask = $this->getSubtask(); - $redirect = $this->request->getStringParam('redirect', 'task'); - - $this->subtask->toggleStatus($subtask['id']); - - if ($redirect === 'board') { - $this->sessionStorage->hasSubtaskInProgress = $this->subtask->hasSubtaskInProgress($this->userSession->getId()); - - $this->response->html($this->template->render('board/tooltip_subtasks', array( - 'subtasks' => $this->subtask->getAll($task['id']), - 'task' => $task, - ))); - } - - $this->toggleRedirect($task, $redirect); - } - - /** - * Handle subtask restriction (popover) - * - * @access public - */ - public function subtaskRestriction() - { - $task = $this->getTask(); - $subtask = $this->getSubtask(); - - $this->response->html($this->template->render('subtask/restriction_change_status', array( - 'status_list' => array( - SubtaskModel::STATUS_TODO => t('Todo'), - SubtaskModel::STATUS_DONE => t('Done'), - ), - 'subtask_inprogress' => $this->subtask->getSubtaskInProgress($this->userSession->getId()), - 'subtask' => $subtask, - 'task' => $task, - 'redirect' => $this->request->getStringParam('redirect'), - ))); - } - - /** - * Change status of the in progress subtask and the other subtask - * - * @access public - */ - public function changeRestrictionStatus() - { - $task = $this->getTask(); - $subtask = $this->getSubtask(); - $values = $this->request->getValues(); - - // Change status of the previous in progress subtask - $this->subtask->update(array( - 'id' => $values['id'], - 'status' => $values['status'], - )); - - // Set the current subtask to in pogress - $this->subtask->update(array( - 'id' => $subtask['id'], - 'status' => SubtaskModel::STATUS_INPROGRESS, - )); - - $this->toggleRedirect($task, $values['redirect']); - } - - /** - * Redirect to the right page - * - * @access private - */ - private function toggleRedirect(array $task, $redirect) - { - switch ($redirect) { - case 'board': - $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); - case 'dashboard': - $this->response->redirect($this->helper->url->to('app', 'index')); - default: - $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'subtasks')); - } + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true); } /** @@ -267,8 +175,9 @@ class Subtask extends Base $subtask_id = $this->request->getIntegerParam('subtask_id'); $direction = $this->request->getStringParam('direction'); $method = $direction === 'up' ? 'moveUp' : 'moveDown'; + $redirect = $this->request->getStringParam('redirect', 'task'); $this->subtask->$method($task_id, $subtask_id); - $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $project_id, 'task_id' => $task_id), 'subtasks')); + $this->response->redirect($this->helper->url->to($redirect, 'show', array('project_id' => $project_id, 'task_id' => $task_id), 'subtasks')); } } diff --git a/app/Controller/SubtaskRestriction.php b/app/Controller/SubtaskRestriction.php new file mode 100644 index 00000000..56024867 --- /dev/null +++ b/app/Controller/SubtaskRestriction.php @@ -0,0 +1,61 @@ +<?php + +namespace Kanboard\Controller; + +use Kanboard\Model\Subtask as SubtaskModel; + +/** + * Subtask Restriction + * + * @package controller + * @author Frederic Guillot + */ +class SubtaskRestriction extends Base +{ + /** + * Show popup + * + * @access public + */ + public function popover() + { + $task = $this->getTask(); + $subtask = $this->getSubtask(); + + $this->response->html($this->template->render('subtask_restriction/popover', array( + 'status_list' => array( + SubtaskModel::STATUS_TODO => t('Todo'), + SubtaskModel::STATUS_DONE => t('Done'), + ), + 'subtask_inprogress' => $this->subtask->getSubtaskInProgress($this->userSession->getId()), + 'subtask' => $subtask, + 'task' => $task, + ))); + } + + /** + * Change status of the in progress subtask and the other subtask + * + * @access public + */ + public function update() + { + $task = $this->getTask(); + $subtask = $this->getSubtask(); + $values = $this->request->getValues(); + + // Change status of the previous "in progress" subtask + $this->subtask->update(array( + 'id' => $values['id'], + 'status' => $values['status'], + )); + + // Set the current subtask to "in progress" + $this->subtask->update(array( + 'id' => $subtask['id'], + 'status' => SubtaskModel::STATUS_INPROGRESS, + )); + + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true); + } +} diff --git a/app/Controller/SubtaskStatus.php b/app/Controller/SubtaskStatus.php new file mode 100644 index 00000000..efe8a974 --- /dev/null +++ b/app/Controller/SubtaskStatus.php @@ -0,0 +1,28 @@ +<?php + +namespace Kanboard\Controller; + +/** + * Subtask Status + * + * @package controller + * @author Frederic Guillot + */ +class SubtaskStatus extends Base +{ + /** + * Change status to the next status: Toto -> In Progress -> Done + * + * @access public + */ + public function change() + { + $task = $this->getTask(); + $subtask = $this->getSubtask(); + + $status = $this->subtask->toggleStatus($subtask['id']); + $subtask['status'] = $status; + + $this->response->html($this->helper->subtask->toggleStatus($subtask, $task['project_id'])); + } +} diff --git a/app/Controller/Swimlane.php b/app/Controller/Swimlane.php index 66410888..4e8c2863 100644 --- a/app/Controller/Swimlane.php +++ b/app/Controller/Swimlane.php @@ -40,7 +40,7 @@ class Swimlane extends Base { $project = $this->getProject(); - $this->response->html($this->projectLayout('swimlane/index', array( + $this->response->html($this->helper->layout->project('swimlane/index', array( 'default_swimlane' => $this->swimlane->getDefault($project['id']), 'active_swimlanes' => $this->swimlane->getAllByStatus($project['id'], SwimlaneModel::ACTIVE), 'inactive_swimlanes' => $this->swimlane->getAllByStatus($project['id'], SwimlaneModel::INACTIVE), @@ -108,7 +108,7 @@ class Swimlane extends Base $project = $this->getProject(); $swimlane = $this->getSwimlane($project['id']); - $this->response->html($this->projectLayout('swimlane/edit', array( + $this->response->html($this->helper->layout->project('swimlane/edit', array( 'values' => empty($values) ? $swimlane : $values, 'errors' => $errors, 'project' => $project, @@ -150,7 +150,7 @@ class Swimlane extends Base $project = $this->getProject(); $swimlane = $this->getSwimlane($project['id']); - $this->response->html($this->projectLayout('swimlane/remove', array( + $this->response->html($this->helper->layout->project('swimlane/remove', array( 'project' => $project, 'swimlane' => $swimlane, 'title' => t('Remove a swimlane') diff --git a/app/Controller/Task.php b/app/Controller/Task.php index 1811dcb7..0d463725 100644 --- a/app/Controller/Task.php +++ b/app/Controller/Task.php @@ -30,7 +30,7 @@ class Task extends Base $this->notfound(true); } - $this->response->html($this->template->layout('task/public', array( + $this->response->html($this->helper->layout->app('task/public', array( 'project' => $project, 'comments' => $this->comment->getAll($task['id']), 'subtasks' => $this->subtask->getAll($task['id']), @@ -64,7 +64,7 @@ class Task extends Base $this->dateParser->format($values, array('date_started'), 'Y-m-d H:i'); - $this->response->html($this->taskLayout('task/show', array( + $this->response->html($this->helper->layout->task('task/show', array( 'project' => $this->project->getById($task['project_id']), 'files' => $this->file->getAllDocuments($task['id']), 'images' => $this->file->getAllImages($task['id']), @@ -95,7 +95,7 @@ class Task extends Base { $task = $this->getTask(); - $this->response->html($this->taskLayout('task/analytics', array( + $this->response->html($this->helper->layout->task('task/analytics', array( 'title' => $task['title'], 'task' => $task, 'lead_time' => $this->taskAnalytic->getLeadTime($task), @@ -121,7 +121,7 @@ class Task extends Base ->setQuery($this->subtaskTimeTracking->getTaskQuery($task['id'])) ->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks'); - $this->response->html($this->taskLayout('task/time_tracking_details', array( + $this->response->html($this->helper->layout->task('task/time_tracking_details', array( 'task' => $task, 'subtask_paginator' => $subtask_paginator, ))); @@ -136,7 +136,7 @@ class Task extends Base { $task = $this->getTask(); - $this->response->html($this->taskLayout('task/transitions', array( + $this->response->html($this->helper->layout->task('task/transitions', array( 'task' => $task, 'transitions' => $this->transition->getAllByTask($task['id']), ))); @@ -167,7 +167,7 @@ class Task extends Base $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); } - $this->response->html($this->taskLayout('task/remove', array( + $this->response->html($this->helper->layout->task('task/remove', array( 'task' => $task, ))); } diff --git a/app/Controller/TaskExternalLink.php b/app/Controller/TaskExternalLink.php new file mode 100644 index 00000000..f26922dd --- /dev/null +++ b/app/Controller/TaskExternalLink.php @@ -0,0 +1,185 @@ +<?php + +namespace Kanboard\Controller; + +use Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound; + +/** + * Task External Link Controller + * + * @package controller + * @author Frederic Guillot + */ +class TaskExternalLink extends Base +{ + /** + * Creation form + * + * @access public + */ + public function show() + { + $task = $this->getTask(); + + $this->response->html($this->helper->layout->task('task_external_link/show', array( + 'links' => $this->taskExternalLink->getAll($task['id']), + 'task' => $task, + 'title' => t('List of external links'), + ))); + } + + /** + * First creation form + * + * @access public + */ + public function find(array $values = array(), array $errors = array()) + { + $task = $this->getTask(); + + $this->response->html($this->helper->layout->task('task_external_link/find', array( + 'values' => $values, + 'errors' => $errors, + 'task' => $task, + 'types' => $this->externalLinkManager->getTypes(), + ))); + } + + /** + * Second creation form + * + * @access public + */ + public function create() + { + try { + + $task = $this->getTask(); + $values = $this->request->getValues(); + + $provider = $this->externalLinkManager->setUserInput($values)->find(); + $link = $provider->getLink(); + + $this->response->html($this->helper->layout->task('task_external_link/create', array( + 'values' => array( + 'title' => $link->getTitle(), + 'url' => $link->getUrl(), + 'link_type' => $provider->getType(), + ), + 'dependencies' => $provider->getDependencies(), + 'errors' => array(), + 'task' => $task, + ))); + + } catch (ExternalLinkProviderNotFound $e) { + $errors = array('text' => array(t('Unable to fetch link information.'))); + $this->find($values, $errors); + } + } + + /** + * Save link + * + * @access public + */ + public function save() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + list($valid, $errors) = $this->externalLinkValidator->validateCreation($values); + + if ($valid && $this->taskExternalLink->create($values)) { + $this->flash->success(t('Link added successfully.')); + return $this->response->redirect($this->helper->url->to('TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true); + } + + $this->edit($values, $errors); + } + + /** + * Edit form + * + * @access public + */ + public function edit(array $values = array(), array $errors = array()) + { + $task = $this->getTask(); + $link_id = $this->request->getIntegerParam('link_id'); + + if ($link_id > 0) { + $values = $this->taskExternalLink->getById($link_id); + } + + if (empty($values)) { + return $this->notfound(); + } + + $provider = $this->externalLinkManager->getProvider($values['link_type']); + + $this->response->html($this->helper->layout->task('task_external_link/edit', array( + 'values' => $values, + 'errors' => $errors, + 'task' => $task, + 'dependencies' => $provider->getDependencies(), + ))); + } + + /** + * Update link + * + * @access public + */ + public function update() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + list($valid, $errors) = $this->externalLinkValidator->validateModification($values); + + if ($valid && $this->taskExternalLink->update($values)) { + $this->flash->success(t('Link updated successfully.')); + return $this->response->redirect($this->helper->url->to('TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true); + } + + $this->edit($values, $errors); + } + + /** + * Confirmation dialog before removing a link + * + * @access public + */ + public function confirm() + { + $task = $this->getTask(); + $link_id = $this->request->getIntegerParam('link_id'); + $link = $this->taskExternalLink->getById($link_id); + + if (empty($link)) { + return $this->notfound(); + } + + $this->response->html($this->helper->layout->task('task_external_link/remove', array( + 'link' => $link, + 'task' => $task, + ))); + } + + /** + * Remove a link + * + * @access public + */ + public function remove() + { + $this->checkCSRFParam(); + $task = $this->getTask(); + + if ($this->taskExternalLink->remove($this->request->getIntegerParam('link_id'))) { + $this->flash->success(t('Link removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this link.')); + } + + $this->response->redirect($this->helper->url->to('TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))); + } +} diff --git a/app/Controller/TaskImport.php b/app/Controller/TaskImport.php index f09c14ce..460c608c 100644 --- a/app/Controller/TaskImport.php +++ b/app/Controller/TaskImport.php @@ -20,7 +20,7 @@ class TaskImport extends Base { $project = $this->getProject(); - $this->response->html($this->projectLayout('task_import/step1', array( + $this->response->html($this->helper->layout->project('task_import/step1', array( 'project' => $project, 'values' => $values, 'errors' => $errors, diff --git a/app/Controller/TaskRecurrence.php b/app/Controller/TaskRecurrence.php new file mode 100644 index 00000000..f02f3cdc --- /dev/null +++ b/app/Controller/TaskRecurrence.php @@ -0,0 +1,61 @@ +<?php + +namespace Kanboard\Controller; + +/** + * Task Recurrence controller + * + * @package controller + * @author Frederic Guillot + */ +class TaskRecurrence extends Base +{ + /** + * Edit recurrence form + * + * @access public + */ + public function edit(array $values = array(), array $errors = array()) + { + $task = $this->getTask(); + + if (empty($values)) { + $values = $task; + } + + $this->response->html($this->helper->layout->task('task_recurrence/edit', array( + 'values' => $values, + 'errors' => $errors, + 'task' => $task, + 'recurrence_status_list' => $this->task->getRecurrenceStatusList(), + 'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(), + 'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(), + 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(), + ))); + } + + /** + * Update recurrence form + * + * @access public + */ + public function update() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->taskValidator->validateEditRecurrence($values); + + if ($valid) { + if ($this->taskModification->update($values)) { + $this->flash->success(t('Task updated successfully.')); + } else { + $this->flash->failure(t('Unable to update your task.')); + } + + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true); + } + + $this->edit($values, $errors); + } +} diff --git a/app/Controller/Taskcreation.php b/app/Controller/Taskcreation.php index 49ccea7f..e661587c 100644 --- a/app/Controller/Taskcreation.php +++ b/app/Controller/Taskcreation.php @@ -18,22 +18,21 @@ class Taskcreation extends Base public function create(array $values = array(), array $errors = array()) { $project = $this->getProject(); - $method = $this->request->isAjax() ? 'render' : 'layout'; $swimlanes_list = $this->swimlane->getList($project['id'], false, true); if (empty($values)) { $values = array( 'swimlane_id' => $this->request->getIntegerParam('swimlane_id', key($swimlanes_list)), 'column_id' => $this->request->getIntegerParam('column_id'), - 'color_id' => $this->request->getStringParam('color_id', $this->color->getDefaultColor()), - 'owner_id' => $this->request->getIntegerParam('owner_id'), - 'another_task' => $this->request->getIntegerParam('another_task'), + 'color_id' => $this->color->getDefaultColor(), + 'owner_id' => $this->userSession->getId(), ); + + $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values)); } - $this->response->html($this->template->$method('task_creation/form', array( + $this->response->html($this->template->render('task_creation/form', array( 'project' => $project, - 'ajax' => $this->request->isAjax(), 'errors' => $errors, 'values' => $values + array('project_id' => $project['id']), 'columns_list' => $this->board->getColumnsList($project['id']), @@ -41,8 +40,6 @@ class Taskcreation extends Base 'colors_list' => $this->color->getList(), 'categories_list' => $this->category->getList($project['id']), 'swimlanes_list' => $swimlanes_list, - 'date_format' => $this->config->get('application_date_format'), - 'date_formats' => $this->dateParser->getAvailableFormats(), 'title' => $project['name'].' > '.t('New task') ))); } @@ -61,25 +58,26 @@ class Taskcreation extends Base if ($valid && $this->taskCreation->create($values)) { $this->flash->success(t('Task created successfully.')); - $this->afterSave($project, $values); - } else { - $this->flash->failure(t('Unable to create your task.')); + return $this->afterSave($project, $values); } + $this->flash->failure(t('Unable to create your task.')); $this->create($values, $errors); } private function afterSave(array $project, array &$values) { if (isset($values['another_task']) && $values['another_task'] == 1) { - unset($values['title']); - unset($values['description']); - - if (! $this->request->isAjax()) { - $this->response->redirect($this->helper->url->to('taskcreation', 'create', $values)); - } - } else { - $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $project['id']))); + return $this->create(array( + 'owner_id' => $values['owner_id'], + 'color_id' => $values['color_id'], + 'category_id' => isset($values['category_id']) ? $values['category_id'] : 0, + 'column_id' => $values['column_id'], + 'swimlane_id' => isset($values['swimlane_id']) ? $values['swimlane_id'] : 0, + 'another_task' => 1, + )); } + + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $project['id']))); } } diff --git a/app/Controller/Taskduplication.php b/app/Controller/Taskduplication.php index ae8bfcbc..7e7fccd6 100644 --- a/app/Controller/Taskduplication.php +++ b/app/Controller/Taskduplication.php @@ -2,8 +2,6 @@ namespace Kanboard\Controller; -use Kanboard\Model\Project as ProjectModel; - /** * Task Duplication controller * @@ -30,11 +28,11 @@ class Taskduplication extends Base $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task_id))); } else { $this->flash->failure(t('Unable to create this task.')); - $this->response->redirect($this->helper->url->to('taskduplication', 'duplicate', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); + $this->response->redirect($this->helper->url->to('taskduplication', 'duplicate', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true); } } - $this->response->html($this->taskLayout('task_duplication/duplicate', array( + $this->response->html($this->helper->layout->task('task_duplication/duplicate', array( 'task' => $task, ))); } @@ -109,7 +107,7 @@ class Taskduplication extends Base private function chooseDestination(array $task, $template) { $values = array(); - $projects_list = $this->projectUserRole->getProjectsByUser($this->userSession->getId(), array(ProjectModel::ACTIVE)); + $projects_list = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()); unset($projects_list[$task['project_id']]); @@ -130,7 +128,7 @@ class Taskduplication extends Base $users_list = array(); } - $this->response->html($this->taskLayout($template, array( + $this->response->html($this->helper->layout->task($template, array( 'values' => $values, 'task' => $task, 'projects_list' => $projects_list, diff --git a/app/Controller/Tasklink.php b/app/Controller/Tasklink.php index a81d3ee5..fdb4fada 100644 --- a/app/Controller/Tasklink.php +++ b/app/Controller/Tasklink.php @@ -22,13 +22,32 @@ class Tasklink extends Base $link = $this->taskLink->getById($this->request->getIntegerParam('link_id')); if (empty($link)) { - $this->notfound(); + return $this->notfound(); } return $link; } /** + * Show links + * + * @access public + */ + public function show() + { + $task = $this->getTask(); + $project = $this->project->getById($task['project_id']); + + $this->response->html($this->helper->layout->task('tasklink/show', array( + 'links' => $this->taskLink->getAllGroupedByLabel($task['id']), + 'task' => $task, + 'project' => $project, + 'editable' => true, + 'is_public' => false, + ))); + } + + /** * Creation form * * @access public @@ -36,20 +55,8 @@ class Tasklink extends Base public function create(array $values = array(), array $errors = array()) { $task = $this->getTask(); - $ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax'); - - if ($ajax && empty($errors)) { - $this->response->html($this->template->render('tasklink/create', array( - 'values' => $values, - 'errors' => $errors, - 'task' => $task, - 'labels' => $this->link->getList(0, false), - 'title' => t('Add a new link'), - 'ajax' => $ajax, - ))); - } - $this->response->html($this->taskLayout('tasklink/create', array( + $this->response->html($this->helper->layout->task('tasklink/create', array( 'values' => $values, 'errors' => $errors, 'task' => $task, @@ -67,19 +74,13 @@ class Tasklink extends Base { $task = $this->getTask(); $values = $this->request->getValues(); - $ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax'); list($valid, $errors) = $this->taskLinkValidator->validateCreation($values); if ($valid) { if ($this->taskLink->create($values['task_id'], $values['opposite_task_id'], $values['link_id'])) { $this->flash->success(t('Link added successfully.')); - - if ($ajax) { - $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); - } - - $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links'); + return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links', true); } $errors = array('title' => array(t('The exact same link already exists'))); @@ -105,7 +106,7 @@ class Tasklink extends Base $values['title'] = '#'.$opposite_task['id'].' - '.$opposite_task['title']; } - $this->response->html($this->taskLayout('tasklink/edit', array( + $this->response->html($this->helper->layout->task('tasklink/edit', array( 'values' => $values, 'errors' => $errors, 'task_link' => $task_link, @@ -130,7 +131,7 @@ class Tasklink extends Base if ($valid) { if ($this->taskLink->update($values['id'], $values['task_id'], $values['opposite_task_id'], $values['link_id'])) { $this->flash->success(t('Link updated successfully.')); - $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links'); + return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links'); } $this->flash->failure(t('Unable to update your link.')); @@ -149,7 +150,7 @@ class Tasklink extends Base $task = $this->getTask(); $link = $this->getTaskLink(); - $this->response->html($this->taskLayout('tasklink/remove', array( + $this->response->html($this->helper->layout->task('tasklink/remove', array( 'link' => $link, 'task' => $task, ))); diff --git a/app/Controller/Taskmodification.php b/app/Controller/Taskmodification.php index 2c97970b..0e9316b2 100644 --- a/app/Controller/Taskmodification.php +++ b/app/Controller/Taskmodification.php @@ -48,46 +48,44 @@ class Taskmodification extends Base * * @access public */ - public function description() + public function description(array $values = array(), array $errors = array()) { $task = $this->getTask(); - $ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax'); - if ($this->request->isPost()) { - $values = $this->request->getValues(); - - list($valid, $errors) = $this->taskValidator->validateDescriptionCreation($values); - - if ($valid) { - if ($this->taskModification->update($values)) { - $this->flash->success(t('Task updated successfully.')); - } else { - $this->flash->failure(t('Unable to update your task.')); - } - - if ($ajax) { - $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); - } else { - $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); - } - } - } else { - $values = $task; - $errors = array(); + if (empty($values)) { + $values = array('id' => $task['id'], 'description' => $task['description']); } - $params = array( + $this->response->html($this->helper->layout->task('task_modification/edit_description', array( 'values' => $values, 'errors' => $errors, 'task' => $task, - 'ajax' => $ajax, - ); + ))); + } - if ($ajax) { - $this->response->html($this->template->render('task_modification/edit_description', $params)); - } else { - $this->response->html($this->taskLayout('task_modification/edit_description', $params)); + /** + * Update description + * + * @access public + */ + public function updateDescription() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->taskValidator->validateDescriptionCreation($values); + + if ($valid) { + if ($this->taskModification->update($values)) { + $this->flash->success(t('Task updated successfully.')); + } else { + $this->flash->failure(t('Unable to update your task.')); + } + + return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true); } + + $this->description($values, $errors); } /** @@ -99,15 +97,15 @@ class Taskmodification extends Base { $task = $this->getTask(); $project = $this->project->getById($task['project_id']); - $ajax = $this->request->isAjax(); if (empty($values)) { $values = $task; + $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values)); } $this->dateParser->format($values, array('date_due')); - $params = array( + $this->response->html($this->helper->layout->task('task_modification/edit_task', array( 'project' => $project, 'values' => $values, 'errors' => $errors, @@ -117,16 +115,7 @@ class Taskmodification extends Base 'categories_list' => $this->category->getList($task['project_id']), 'date_format' => $this->config->get('application_date_format'), 'date_formats' => $this->dateParser->getAvailableFormats(), - 'ajax' => $ajax, - ); - - if ($ajax) { - $html = $this->template->render('task_modification/edit_task', $params); - } else { - $html = $this->taskLayout('task_modification/edit_task', $params); - } - - $this->response->html($html); + ))); } /** @@ -143,56 +132,10 @@ class Taskmodification extends Base if ($valid && $this->taskModification->update($values)) { $this->flash->success(t('Task updated successfully.')); - - if ($this->request->isAjax()) { - $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); - } else { - $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); - } + return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true); } else { $this->flash->failure(t('Unable to update your task.')); $this->edit($values, $errors); } } - - /** - * Edit recurrence form - * - * @access public - */ - public function recurrence() - { - $task = $this->getTask(); - - if ($this->request->isPost()) { - $values = $this->request->getValues(); - - list($valid, $errors) = $this->taskValidator->validateEditRecurrence($values); - - if ($valid) { - if ($this->taskModification->update($values)) { - $this->flash->success(t('Task updated successfully.')); - } else { - $this->flash->failure(t('Unable to update your task.')); - } - - $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); - } - } else { - $values = $task; - $errors = array(); - } - - $params = array( - 'values' => $values, - 'errors' => $errors, - 'task' => $task, - 'recurrence_status_list' => $this->task->getRecurrenceStatusList(), - 'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(), - 'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(), - 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(), - ); - - $this->response->html($this->taskLayout('task_modification/edit_recurrence', $params)); - } } diff --git a/app/Controller/Taskstatus.php b/app/Controller/Taskstatus.php index b03baebf..c07f2cc5 100644 --- a/app/Controller/Taskstatus.php +++ b/app/Controller/Taskstatus.php @@ -17,9 +17,7 @@ class Taskstatus extends Base */ public function close() { - $task = $this->getTask(); - $this->changeStatus($task, 'close', t('Task closed successfully.'), t('Unable to close this task.')); - $this->renderTemplate($task, 'task_status/close'); + $this->changeStatus('close', 'task_status/close', t('Task closed successfully.'), t('Unable to close this task.')); } /** @@ -29,13 +27,22 @@ class Taskstatus extends Base */ public function open() { - $task = $this->getTask(); - $this->changeStatus($task, 'open', t('Task opened successfully.'), t('Unable to open this task.')); - $this->renderTemplate($task, 'task_status/open'); + $this->changeStatus('open', 'task_status/open', t('Task opened successfully.'), t('Unable to open this task.')); } - private function changeStatus(array $task, $method, $success_message, $failure_message) + /** + * Common method to change status + * + * @access private + * @param string $method + * @param string $template + * @param string $success_message + * @param string $failure_message + */ + private function changeStatus($method, $template, $success_message, $failure_message) { + $task = $this->getTask(); + if ($this->request->getStringParam('confirmation') === 'yes') { $this->checkCSRFParam(); @@ -45,28 +52,11 @@ class Taskstatus extends Base $this->flash->failure($failure_message); } - if ($this->request->getStringParam('redirect') === 'board') { - $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); - } - - $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))); - } - } - - private function renderTemplate(array $task, $template) - { - $redirect = $this->request->getStringParam('redirect'); - - if ($this->request->isAjax()) { - $this->response->html($this->template->render($template, array( - 'task' => $task, - 'redirect' => $redirect, - ))); + return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true); } - $this->response->html($this->taskLayout($template, array( + $this->response->html($this->helper->layout->task($template, array( 'task' => $task, - 'redirect' => $redirect, ))); } } diff --git a/app/Controller/Twofactor.php b/app/Controller/Twofactor.php index 8dbfcf66..10292261 100644 --- a/app/Controller/Twofactor.php +++ b/app/Controller/Twofactor.php @@ -33,7 +33,7 @@ class Twofactor extends User $this->checkCurrentUser($user); unset($this->sessionStorage->twoFactorSecret); - $this->response->html($this->layout('twofactor/index', array( + $this->response->html($this->helper->layout->user('twofactor/index', array( 'user' => $user, 'provider' => $this->authenticationManager->getPostAuthenticationProvider()->getName(), ))); @@ -60,7 +60,7 @@ class Twofactor extends User $provider->setSecret($this->sessionStorage->twoFactorSecret); } - $this->response->html($this->layout('twofactor/show', array( + $this->response->html($this->helper->layout->user('twofactor/show', array( 'user' => $user, 'secret' => $this->sessionStorage->twoFactorSecret, 'qrcode_url' => $provider->getQrCodeUrl($label), @@ -165,7 +165,7 @@ class Twofactor extends User $this->sessionStorage->twoFactorBeforeCodeCalled = true; } - $this->response->html($this->template->layout('twofactor/check', array( + $this->response->html($this->helper->layout->app('twofactor/check', array( 'title' => t('Check two factor authentication code'), ))); } @@ -191,7 +191,7 @@ class Twofactor extends User $this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id']))); } - $this->response->html($this->layout('twofactor/disable', array( + $this->response->html($this->helper->layout->user('twofactor/disable', array( 'user' => $user, ))); } diff --git a/app/Controller/User.php b/app/Controller/User.php index 97e01553..881266d4 100644 --- a/app/Controller/User.php +++ b/app/Controller/User.php @@ -15,27 +15,6 @@ use Kanboard\Core\Security\Role; class User extends Base { /** - * Common layout for user views - * - * @access protected - * @param string $template Template name - * @param array $params Template parameters - * @return string - */ - protected function layout($template, array $params) - { - $content = $this->template->render($template, $params); - $params['user_content_for_layout'] = $content; - $params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()); - - if (isset($params['user'])) { - $params['title'] = ($params['user']['name'] ?: $params['user']['username']).' (#'.$params['user']['id'].')'; - } - - return $this->template->layout('user/layout', $params); - } - - /** * List all users * * @access public @@ -50,8 +29,7 @@ class User extends Base ->calculate(); $this->response->html( - $this->template->layout('user/index', array( - 'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()), + $this->helper->layout->app('user/index', array( 'title' => t('Users').' ('.$paginator->getTotal().')', 'paginator' => $paginator, ))); @@ -71,8 +49,7 @@ class User extends Base } $this->response->html( - $this->template->layout('user/profile', array( - 'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()), + $this->helper->layout->app('user/profile', array( 'title' => $user['name'] ?: $user['username'], 'user' => $user, ) @@ -88,11 +65,10 @@ class User extends Base { $is_remote = $this->request->getIntegerParam('remote') == 1 || (isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1); - $this->response->html($this->template->layout($is_remote ? 'user/create_remote' : 'user/create_local', array( + $this->response->html($this->helper->layout->app($is_remote ? 'user/create_remote' : 'user/create_local', array( 'timezones' => $this->config->getTimezones(true), 'languages' => $this->config->getLanguages(true), 'roles' => $this->role->getApplicationRoles(), - 'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()), 'projects' => $this->project->getList(), 'errors' => $errors, 'values' => $values + array('role' => Role::APP_USER), @@ -142,7 +118,7 @@ class User extends Base public function show() { $user = $this->getUser(); - $this->response->html($this->layout('user/show', array( + $this->response->html($this->helper->layout->user('user/show', array( 'user' => $user, 'timezones' => $this->config->getTimezones(true), 'languages' => $this->config->getLanguages(true), @@ -166,7 +142,7 @@ class User extends Base ->setQuery($this->subtaskTimeTracking->getUserQuery($user['id'])) ->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks'); - $this->response->html($this->layout('user/timesheet', array( + $this->response->html($this->helper->layout->user('user/timesheet', array( 'subtask_paginator' => $subtask_paginator, 'user' => $user, ))); @@ -180,7 +156,7 @@ class User extends Base public function passwordReset() { $user = $this->getUser(); - $this->response->html($this->layout('user/password_reset', array( + $this->response->html($this->helper->layout->user('user/password_reset', array( 'tokens' => $this->passwordReset->getAll($user['id']), 'user' => $user, ))); @@ -194,7 +170,7 @@ class User extends Base public function last() { $user = $this->getUser(); - $this->response->html($this->layout('user/last', array( + $this->response->html($this->helper->layout->user('user/last', array( 'last_logins' => $this->lastLogin->getAll($user['id']), 'user' => $user, ))); @@ -208,7 +184,7 @@ class User extends Base public function sessions() { $user = $this->getUser(); - $this->response->html($this->layout('user/sessions', array( + $this->response->html($this->helper->layout->user('user/sessions', array( 'sessions' => $this->rememberMeSession->getAll($user['id']), 'user' => $user, ))); @@ -243,7 +219,7 @@ class User extends Base $this->response->redirect($this->helper->url->to('user', 'notifications', array('user_id' => $user['id']))); } - $this->response->html($this->layout('user/notifications', array( + $this->response->html($this->helper->layout->user('user/notifications', array( 'projects' => $this->projectUserRole->getProjectsByUser($user['id'], array(ProjectModel::ACTIVE)), 'notifications' => $this->userNotification->readSettings($user['id']), 'types' => $this->userNotificationType->getTypes(), @@ -268,7 +244,7 @@ class User extends Base $this->response->redirect($this->helper->url->to('user', 'integrations', array('user_id' => $user['id']))); } - $this->response->html($this->layout('user/integrations', array( + $this->response->html($this->helper->layout->user('user/integrations', array( 'user' => $user, 'values' => $this->userMetadata->getall($user['id']), ))); @@ -282,7 +258,7 @@ class User extends Base public function external() { $user = $this->getUser(); - $this->response->html($this->layout('user/external', array( + $this->response->html($this->helper->layout->user('user/external', array( 'last_logins' => $this->lastLogin->getAll($user['id']), 'user' => $user, ))); @@ -310,7 +286,7 @@ class User extends Base $this->response->redirect($this->helper->url->to('user', 'share', array('user_id' => $user['id']))); } - $this->response->html($this->layout('user/share', array( + $this->response->html($this->helper->layout->user('user/share', array( 'user' => $user, 'title' => t('Public access'), ))); @@ -342,7 +318,7 @@ class User extends Base } } - $this->response->html($this->layout('user/password', array( + $this->response->html($this->helper->layout->user('user/password', array( 'values' => $values, 'errors' => $errors, 'user' => $user, @@ -384,7 +360,7 @@ class User extends Base } } - $this->response->html($this->layout('user/edit', array( + $this->response->html($this->helper->layout->user('user/edit', array( 'values' => $values, 'errors' => $errors, 'user' => $user, @@ -422,7 +398,7 @@ class User extends Base } } - $this->response->html($this->layout('user/authentication', array( + $this->response->html($this->helper->layout->user('user/authentication', array( 'values' => $values, 'errors' => $errors, 'user' => $user, @@ -450,7 +426,7 @@ class User extends Base $this->response->redirect($this->helper->url->to('user', 'index')); } - $this->response->html($this->layout('user/remove', array( + $this->response->html($this->helper->layout->user('user/remove', array( 'user' => $user, ))); } diff --git a/app/Controller/UserImport.php b/app/Controller/UserImport.php index cbc5aa14..debd69e5 100644 --- a/app/Controller/UserImport.php +++ b/app/Controller/UserImport.php @@ -18,7 +18,7 @@ class UserImport extends Base */ public function step1(array $values = array(), array $errors = array()) { - $this->response->html($this->template->layout('user_import/step1', array( + $this->response->html($this->helper->layout->app('user_import/step1', array( 'values' => $values, 'errors' => $errors, 'max_size' => ini_get('upload_max_filesize'), diff --git a/app/Core/Base.php b/app/Core/Base.php index 2821e5ae..ab99fcea 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -16,6 +16,7 @@ use Pimple\Container; * @property \Kanboard\Analytic\AverageLeadCycleTimeAnalytic $averageLeadCycleTimeAnalytic * @property \Kanboard\Analytic\AverageTimeSpentColumnAnalytic $averageTimeSpentColumnAnalytic * @property \Kanboard\Core\Action\ActionManager $actionManager + * @property \Kanboard\Core\ExternalLink\ExternalLinkManager $externalLinkManager * @property \Kanboard\Core\Cache\MemoryCache $memoryCache * @property \Kanboard\Core\Event\EventManager $eventManager * @property \Kanboard\Core\Group\GroupManager $groupManager @@ -97,6 +98,7 @@ use Pimple\Container; * @property \Kanboard\Model\TaskCreation $taskCreation * @property \Kanboard\Model\TaskDuplication $taskDuplication * @property \Kanboard\Model\TaskExport $taskExport + * @property \Kanboard\Model\TaskExternalLink $taskExternalLink * @property \Kanboard\Model\TaskImport $taskImport * @property \Kanboard\Model\TaskFinder $taskFinder * @property \Kanboard\Model\TaskFilter $taskFilter @@ -132,6 +134,7 @@ use Pimple\Container; * @property \Kanboard\Validator\SubtaskValidator $subtaskValidator * @property \Kanboard\Validator\SwimlaneValidator $swimlaneValidator * @property \Kanboard\Validator\TaskLinkValidator $taskLinkValidator + * @property \Kanboard\Validator\TaskExternalLinkValidator $taskExternalLinkValidator * @property \Kanboard\Validator\TaskValidator $taskValidator * @property \Kanboard\Validator\UserValidator $userValidator * @property \Psr\Log\LoggerInterface $logger diff --git a/app/Core/Event/EventManager.php b/app/Core/Event/EventManager.php index 8d76bfcb..162d23e8 100644 --- a/app/Core/Event/EventManager.php +++ b/app/Core/Event/EventManager.php @@ -52,6 +52,7 @@ class EventManager Task::EVENT_CLOSE => t('Closing a task'), Task::EVENT_CREATE_UPDATE => t('Task creation or modification'), Task::EVENT_ASSIGNEE_CHANGE => t('Task assignee change'), + Task::EVENT_DAILY_CRONJOB => t('Daily background job for tasks'), ); $events = array_merge($events, $this->events); diff --git a/app/Core/ExternalLink/ExternalLinkInterface.php b/app/Core/ExternalLink/ExternalLinkInterface.php new file mode 100644 index 00000000..2dbc0a19 --- /dev/null +++ b/app/Core/ExternalLink/ExternalLinkInterface.php @@ -0,0 +1,36 @@ +<?php + +namespace Kanboard\Core\ExternalLink; + +/** + * External Link Interface + * + * @package externalLink + * @author Frederic Guillot + */ +interface ExternalLinkInterface +{ + /** + * Get link title + * + * @access public + * @return string + */ + public function getTitle(); + + /** + * Get link URL + * + * @access public + * @return string + */ + public function getUrl(); + + /** + * Set link URL + * + * @access public + * @param string $url + */ + public function setUrl($url); +} diff --git a/app/Core/ExternalLink/ExternalLinkManager.php b/app/Core/ExternalLink/ExternalLinkManager.php new file mode 100644 index 00000000..1fa423c2 --- /dev/null +++ b/app/Core/ExternalLink/ExternalLinkManager.php @@ -0,0 +1,173 @@ +<?php + +namespace Kanboard\Core\ExternalLink; + +use Kanboard\Core\Base; + +/** + * External Link Manager + * + * @package externalLink + * @author Frederic Guillot + */ +class ExternalLinkManager extends Base +{ + /** + * Automatic type value + * + * @var string + */ + const TYPE_AUTO = 'auto'; + + /** + * Registered providers + * + * @access private + * @var array + */ + private $providers = array(); + + /** + * Type chosen by the user + * + * @access private + * @var string + */ + private $userInputType = ''; + + /** + * Text entered by the user + * + * @access private + * @var string + */ + private $userInputText = ''; + + /** + * Register a new provider + * + * Providers are registered in a LIFO queue + * + * @access public + * @param ExternalLinkProviderInterface $provider + * @return ExternalLinkManager + */ + public function register(ExternalLinkProviderInterface $provider) + { + array_unshift($this->providers, $provider); + return $this; + } + + /** + * Get provider + * + * @access public + * @param string $type + * @throws ExternalLinkProviderNotFound + * @return ExternalLinkProviderInterface + */ + public function getProvider($type) + { + foreach ($this->providers as $provider) { + if ($provider->getType() === $type) { + return $provider; + } + } + + throw new ExternalLinkProviderNotFound('Unable to find link provider: '.$type); + } + + /** + * Get link types + * + * @access public + * @return array + */ + public function getTypes() + { + $types = array(); + + foreach ($this->providers as $provider) { + $types[$provider->getType()] = $provider->getName(); + } + + asort($types); + + return array(self::TYPE_AUTO => t('Auto')) + $types; + } + + /** + * Get dependency label from a provider + * + * @access public + * @param string $type + * @param string $dependency + * @return string + */ + public function getDependencyLabel($type, $dependency) + { + $provider = $this->getProvider($type); + $dependencies = $provider->getDependencies(); + return isset($dependencies[$dependency]) ? $dependencies[$dependency] : $dependency; + } + + /** + * Find a provider that match + * + * @access public + * @throws ExternalLinkProviderNotFound + * @return ExternalLinkProviderInterface + */ + public function find() + { + if ($this->userInputType === self::TYPE_AUTO) { + $provider = $this->findProvider(); + } else { + $provider = $this->getProvider($this->userInputType); + $provider->setUserTextInput($this->userInputText); + + if (! $provider->match()) { + throw new ExternalLinkProviderNotFound('Unable to parse URL with selected provider'); + } + } + + if ($provider === null) { + throw new ExternalLinkProviderNotFound('Unable to find link information from provided information'); + } + + return $provider; + } + + /** + * Set form values + * + * @access public + * @param array $values + * @return ExternalLinkManager + */ + public function setUserInput(array $values) + { + $this->userInputType = empty($values['type']) ? self::TYPE_AUTO : $values['type']; + $this->userInputText = empty($values['text']) ? '' : trim($values['text']); + return $this; + } + + /** + * Find a provider that user input + * + * @access private + * @return ExternalLinkProviderInterface + */ + private function findProvider() + { + foreach ($this->providers as $provider) { + $provider->setUserTextInput($this->userInputText); + + if ($provider->match()) { + return $provider; + } + } + + return null; + } +} diff --git a/app/Core/ExternalLink/ExternalLinkProviderInterface.php b/app/Core/ExternalLink/ExternalLinkProviderInterface.php new file mode 100644 index 00000000..c908e1eb --- /dev/null +++ b/app/Core/ExternalLink/ExternalLinkProviderInterface.php @@ -0,0 +1,71 @@ +<?php + +namespace Kanboard\Core\ExternalLink; + +/** + * External Link Provider Interface + * + * @package externalLink + * @author Frederic Guillot + */ +interface ExternalLinkProviderInterface +{ + /** + * Get provider name (label) + * + * @access public + * @return string + */ + public function getName(); + + /** + * Get link type (will be saved in the database) + * + * @access public + * @return string + */ + public function getType(); + + /** + * Get a dictionary of supported dependency types by the provider + * + * Example: + * + * [ + * 'related' => t('Related'), + * 'child' => t('Child'), + * 'parent' => t('Parent'), + * 'self' => t('Self'), + * ] + * + * The dictionary key is saved in the database. + * + * @access public + * @return array + */ + public function getDependencies(); + + /** + * Set text entered by the user + * + * @access public + * @param string $input + */ + public function setUserTextInput($input); + + /** + * Return true if the provider can parse correctly the user input + * + * @access public + * @return boolean + */ + public function match(); + + /** + * Get the link found with the properties + * + * @access public + * @return ExternalLinkInterface + */ + public function getLink(); +} diff --git a/app/Core/ExternalLink/ExternalLinkProviderNotFound.php b/app/Core/ExternalLink/ExternalLinkProviderNotFound.php new file mode 100644 index 00000000..4fd05202 --- /dev/null +++ b/app/Core/ExternalLink/ExternalLinkProviderNotFound.php @@ -0,0 +1,15 @@ +<?php + +namespace Kanboard\Core\ExternalLink; + +use Exception; + +/** + * External Link Provider Not Found Exception + * + * @package externalLink + * @author Frederic Guillot + */ +class ExternalLinkProviderNotFound extends Exception +{ +} diff --git a/app/Core/Helper.php b/app/Core/Helper.php index 5edaa3f0..bf71769f 100644 --- a/app/Core/Helper.php +++ b/app/Core/Helper.php @@ -20,6 +20,7 @@ use Pimple\Container; * @property \Helper\Text $text * @property \Helper\Url $url * @property \Helper\User $user + * @property \Helper\Layout $layout */ class Helper { diff --git a/app/Core/Http/Client.php b/app/Core/Http/Client.php index c6bf36a6..12b0a1cb 100644 --- a/app/Core/Http/Client.php +++ b/app/Core/Http/Client.php @@ -34,6 +34,19 @@ class Client extends Base const HTTP_USER_AGENT = 'Kanboard'; /** + * Send a GET HTTP request + * + * @access public + * @param string $url + * @param string[] $headers + * @return string + */ + public function get($url, array $headers = array()) + { + return $this->doRequest('GET', $url, '', $headers); + } + + /** * Send a GET HTTP request and parse JSON response * * @access public diff --git a/app/Core/Http/Response.php b/app/Core/Http/Response.php index 7fefddeb..d098f519 100644 --- a/app/Core/Http/Response.php +++ b/app/Core/Http/Response.php @@ -68,11 +68,12 @@ class Response extends Base * * @access public * @param string $url Redirection URL + * @param boolean $self If Ajax request and true: refresh the current page */ - public function redirect($url) + public function redirect($url, $self = false) { - if ($this->request->getServerVariable('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest') { - header('X-Ajax-Redirect: '.$url); + if ($this->request->isAjax()) { + header('X-Ajax-Redirect: '.($self ? 'self' : $url)); } else { header('Location: '.$url); } diff --git a/app/Event/GenericEvent.php b/app/Event/GenericEvent.php index 1129fd16..94a51479 100644 --- a/app/Event/GenericEvent.php +++ b/app/Event/GenericEvent.php @@ -7,7 +7,7 @@ use Symfony\Component\EventDispatcher\Event as BaseEvent; class GenericEvent extends BaseEvent implements ArrayAccess { - private $container = array(); + protected $container = array(); public function __construct(array $values = array()) { diff --git a/app/Event/TaskListEvent.php b/app/Event/TaskListEvent.php new file mode 100644 index 00000000..9be1a7d9 --- /dev/null +++ b/app/Event/TaskListEvent.php @@ -0,0 +1,11 @@ +<?php + +namespace Kanboard\Event; + +class TaskListEvent extends GenericEvent +{ + public function setTasks(array &$tasks) + { + $this->container['tasks'] =& $tasks; + } +} diff --git a/app/ExternalLink/AttachmentLink.php b/app/ExternalLink/AttachmentLink.php new file mode 100644 index 00000000..5a0d1344 --- /dev/null +++ b/app/ExternalLink/AttachmentLink.php @@ -0,0 +1,26 @@ +<?php + +namespace Kanboard\ExternalLink; + +use Kanboard\Core\ExternalLink\ExternalLinkInterface; + +/** + * Attachment Link + * + * @package externalLink + * @author Frederic Guillot + */ +class AttachmentLink extends BaseLink implements ExternalLinkInterface +{ + /** + * Get link title + * + * @access public + * @return string + */ + public function getTitle() + { + $path = parse_url($this->url, PHP_URL_PATH); + return basename($path); + } +} diff --git a/app/ExternalLink/AttachmentLinkProvider.php b/app/ExternalLink/AttachmentLinkProvider.php new file mode 100644 index 00000000..df27284f --- /dev/null +++ b/app/ExternalLink/AttachmentLinkProvider.php @@ -0,0 +1,117 @@ +<?php + +namespace Kanboard\ExternalLink; + +use Kanboard\Core\ExternalLink\ExternalLinkProviderInterface; + +/** + * Attachment Link Provider + * + * @package externalLink + * @author Frederic Guillot + */ +class AttachmentLinkProvider extends BaseLinkProvider implements ExternalLinkProviderInterface +{ + /** + * File extensions that are not attachments + * + * @access protected + * @var array + */ + protected $extensions = array( + 'html', + 'htm', + 'xhtml', + 'php', + 'jsp', + 'do', + 'action', + 'asp', + 'aspx', + 'cgi', + ); + + /** + * Get provider name + * + * @access public + * @return string + */ + public function getName() + { + return t('Attachment'); + } + + /** + * Get link type + * + * @access public + * @return string + */ + public function getType() + { + return 'attachment'; + } + + /** + * Get a dictionary of supported dependency types by the provider + * + * @access public + * @return array + */ + public function getDependencies() + { + return array( + 'related' => t('Related'), + ); + } + + /** + * Return true if the provider can parse correctly the user input + * + * @access public + * @return boolean + */ + public function match() + { + if (preg_match('/^https?:\/\/.*\.([^\/]+)$/', $this->userInput, $matches)) { + return $this->isValidExtension($matches[1]); + } + + return false; + } + + /** + * Get the link found with the properties + * + * @access public + * @return ExternalLinkInterface + */ + public function getLink() + { + $link = new AttachmentLink($this->container); + $link->setUrl($this->userInput); + + return $link; + } + + /** + * Check file extension + * + * @access protected + * @param string $extension + * @return boolean + */ + protected function isValidExtension($extension) + { + $extension = strtolower($extension); + + foreach ($this->extensions as $ext) { + if ($extension === $ext) { + return false; + } + } + + return true; + } +} diff --git a/app/ExternalLink/BaseLink.php b/app/ExternalLink/BaseLink.php new file mode 100644 index 00000000..08693ae7 --- /dev/null +++ b/app/ExternalLink/BaseLink.php @@ -0,0 +1,44 @@ +<?php + +namespace Kanboard\ExternalLink; + +use Kanboard\Core\Base; + +/** + * Base Link + * + * @package externalLink + * @author Frederic Guillot + */ +abstract class BaseLink extends Base +{ + /** + * URL + * + * @access protected + * @var string + */ + protected $url = ''; + + /** + * Get link URL + * + * @access public + * @return string + */ + public function getUrl() + { + return $this->url; + } + + /** + * Set link URL + * + * @access public + * @param string $url + */ + public function setUrl($url) + { + $this->url = $url; + } +} diff --git a/app/ExternalLink/BaseLinkProvider.php b/app/ExternalLink/BaseLinkProvider.php new file mode 100644 index 00000000..749cda94 --- /dev/null +++ b/app/ExternalLink/BaseLinkProvider.php @@ -0,0 +1,33 @@ +<?php + +namespace Kanboard\ExternalLink; + +use Kanboard\Core\Base; + +/** + * Base Link Provider + * + * @package externalLink + * @author Frederic Guillot + */ +abstract class BaseLinkProvider extends Base +{ + /** + * User input + * + * @access protected + * @var string + */ + protected $userInput = ''; + + /** + * Set text entered by the user + * + * @access public + * @param string $input + */ + public function setUserTextInput($input) + { + $this->userInput = trim($input); + } +} diff --git a/app/ExternalLink/WebLink.php b/app/ExternalLink/WebLink.php new file mode 100644 index 00000000..9338ca42 --- /dev/null +++ b/app/ExternalLink/WebLink.php @@ -0,0 +1,37 @@ +<?php + +namespace Kanboard\ExternalLink; + +use Kanboard\Core\ExternalLink\ExternalLinkInterface; + +/** + * Web Link + * + * @package externalLink + * @author Frederic Guillot + */ +class WebLink extends BaseLink implements ExternalLinkInterface +{ + /** + * Get link title + * + * @access public + * @return string + */ + public function getTitle() + { + $html = $this->httpClient->get($this->url); + + if (preg_match('/<title>(.*)<\/title>/siU', $html, $matches)) { + return trim($matches[1]); + } + + $components = parse_url($this->url); + + if (! empty($components['host']) && ! empty($components['path'])) { + return $components['host'].$components['path']; + } + + return t('Title not found'); + } +} diff --git a/app/ExternalLink/WebLinkProvider.php b/app/ExternalLink/WebLinkProvider.php new file mode 100644 index 00000000..ea6dc132 --- /dev/null +++ b/app/ExternalLink/WebLinkProvider.php @@ -0,0 +1,77 @@ +<?php + +namespace Kanboard\ExternalLink; + +use Kanboard\Core\ExternalLink\ExternalLinkProviderInterface; + +/** + * Web Link Provider + * + * @package externalLink + * @author Frederic Guillot + */ +class WebLinkProvider extends BaseLinkProvider implements ExternalLinkProviderInterface +{ + /** + * Get provider name + * + * @access public + * @return string + */ + public function getName() + { + return t('Web Link'); + } + + /** + * Get link type + * + * @access public + * @return string + */ + public function getType() + { + return 'weblink'; + } + + /** + * Get a dictionary of supported dependency types by the provider + * + * @access public + * @return array + */ + public function getDependencies() + { + return array( + 'related' => t('Related'), + ); + } + + /** + * Return true if the provider can parse correctly the user input + * + * @access public + * @return boolean + */ + public function match() + { + $startWithHttp = strpos($this->userInput, 'http://') === 0 || strpos($this->userInput, 'https://') === 0; + $validUrl = filter_var($this->userInput, FILTER_VALIDATE_URL); + + return $startWithHttp && $validUrl; + } + + /** + * Get the link found with the properties + * + * @access public + * @return ExternalLinkInterface + */ + public function getLink() + { + $link = new WebLink($this->container); + $link->setUrl($this->userInput); + + return $link; + } +} diff --git a/app/Helper/Layout.php b/app/Helper/Layout.php new file mode 100644 index 00000000..3db23920 --- /dev/null +++ b/app/Helper/Layout.php @@ -0,0 +1,171 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; + +/** + * Layout helpers + * + * @package helper + * @author Frederic Guillot + */ +class Layout extends Base +{ + /** + * Render a template without the layout if Ajax request + * + * @access public + * @param string $template Template name + * @param array $params Template parameters + * @return string + */ + public function app($template, array $params = array()) + { + if ($this->request->isAjax()) { + return $this->template->render($template, $params); + } + + if (! isset($params['no_layout']) && ! isset($params['board_selector'])) { + $params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()); + } + + return $this->template->layout($template, $params); + } + + /** + * Common layout for user views + * + * @access public + * @param string $template Template name + * @param array $params Template parameters + * @return string + */ + public function user($template, array $params) + { + if (isset($params['user'])) { + $params['title'] = '#'.$params['user']['id'].' '.($params['user']['name'] ?: $params['user']['username']); + } + + return $this->subLayout('user/layout', 'user/sidebar', $template, $params); + } + + /** + * Common layout for task views + * + * @access public + * @param string $template Template name + * @param array $params Template parameters + * @return string + */ + public function task($template, array $params) + { + $params['title'] = $params['task']['title']; + return $this->subLayout('task/layout', 'task/sidebar', $template, $params); + } + + /** + * Common layout for project views + * + * @access public + * @param string $template + * @param array $params + * @param string $sidebar + * @return string + */ + public function project($template, array $params, $sidebar = 'project/sidebar') + { + if (empty($params['title'])) { + $params['title'] = $params['project']['name']; + } elseif ($params['project']['name'] !== $params['title']) { + $params['title'] = $params['project']['name'].' > '.$params['title']; + } + + return $this->subLayout('project/layout', $sidebar, $template, $params); + } + + /** + * Common layout for project user views + * + * @access public + * @param string $template + * @param array $params + * @return string + */ + public function projectUser($template, array $params) + { + $params['filter'] = array('user_id' => $params['user_id']); + return $this->subLayout('project_user/layout', 'project_user/sidebar', $template, $params); + } + + /** + * Common layout for config views + * + * @access public + * @param string $template + * @param array $params + * @return string + */ + public function config($template, array $params) + { + if (! isset($params['values'])) { + $params['values'] = $this->config->getAll(); + } + + if (! isset($params['errors'])) { + $params['errors'] = array(); + } + + return $this->subLayout('config/layout', 'config/sidebar', $template, $params); + } + + /** + * Common layout for dashboard views + * + * @access public + * @param string $template + * @param array $params + * @return string + */ + public function dashboard($template, array $params) + { + return $this->subLayout('app/layout', 'app/sidebar', $template, $params); + } + + /** + * Common layout for analytic views + * + * @access public + * @param string $template + * @param array $params + * @return string + */ + public function analytic($template, array $params) + { + return $this->subLayout('analytic/layout', 'analytic/sidebar', $template, $params); + } + + /** + * Common method to generate a sublayout + * + * @access public + * @param string $sublayout + * @param string $sidebar + * @param string $template + * @param array $params + * @return string + */ + public function subLayout($sublayout, $sidebar, $template, array $params = array()) + { + $content = $this->template->render($template, $params); + + if ($this->request->isAjax()) { + return $content; + } + + $params['content_for_sublayout'] = $content; + $params['sidebar_template'] = $sidebar; + + return $this->app($sublayout, $params); + } +} diff --git a/app/Helper/Subtask.php b/app/Helper/Subtask.php index 90bd733e..38074b78 100644 --- a/app/Helper/Subtask.php +++ b/app/Helper/Subtask.php @@ -10,37 +10,82 @@ namespace Kanboard\Helper; */ class Subtask extends \Kanboard\Core\Base { + public function getTitle(array $subtask) + { + if ($subtask['status'] == 0) { + $html = '<i class="fa fa-square-o fa-fw"></i>'; + } elseif ($subtask['status'] == 1) { + $html = '<i class="fa fa-gears fa-fw"></i>'; + } else { + $html = '<i class="fa fa-check-square-o fa-fw"></i>'; + } + + return $html.$this->helper->e($subtask['title']); + } + /** * Get the link to toggle subtask status * * @access public * @param array $subtask - * @param string $redirect * @param integer $project_id * @return string */ - public function toggleStatus(array $subtask, $redirect, $project_id = 0) + public function toggleStatus(array $subtask, $project_id) { - if ($project_id > 0 && ! $this->helper->user->hasProjectAccess('subtask', 'edit', $project_id)) { - return trim($this->template->render('subtask/icons', array('subtask' => $subtask))) . $this->helper->e($subtask['title']); + if (! $this->helper->user->hasProjectAccess('subtask', 'edit', $project_id)) { + return $this->getTitle($subtask); } + $params = array('task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id']); + if ($subtask['status'] == 0 && isset($this->sessionStorage->hasSubtaskInProgress) && $this->sessionStorage->hasSubtaskInProgress) { - return $this->helper->url->link( - trim($this->template->render('subtask/icons', array('subtask' => $subtask))) . $this->helper->e($subtask['title']), - 'subtask', - 'subtaskRestriction', - array('task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'], 'redirect' => $redirect), - false, - 'popover task-board-popover' - ); + return $this->helper->url->link($this->getTitle($subtask), 'SubtaskRestriction', 'popover', $params, false, 'popover'); } - return $this->helper->url->link( - trim($this->template->render('subtask/icons', array('subtask' => $subtask))) . $this->helper->e($subtask['title']), - 'subtask', - 'toggleStatus', - array('task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'], 'redirect' => $redirect) - ); + return $this->helper->url->link($this->getTitle($subtask), 'SubtaskStatus', 'change', $params, false, 'ajax-replace'); + } + + public function selectTitle(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="1"', 'required', 'maxlength="255"'), $attributes); + + $html = $this->helper->form->label(t('Title'), 'title'); + $html .= $this->helper->form->text('title', $values, $errors, $attributes); + + return $html; + } + + public function selectAssignee(array $users, array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="2"'), $attributes); + + $html = $this->helper->form->label(t('Assignee'), 'user_id'); + $html .= $this->helper->form->select('user_id', $users, $values, $errors, $attributes); + $html .= ' <a href="#" class="assign-me" data-target-id="form-user_id" data-current-id="'.$this->userSession->getId().'" title="'.t('Assign to me').'">'.t('Me').'</a>'; + + return $html; + } + + public function selectTimeEstimated(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="3"'), $attributes); + + $html = $this->helper->form->label(t('Original estimate'), 'time_estimated'); + $html .= $this->helper->form->numeric('time_estimated', $values, $errors, $attributes); + $html .= ' '.t('hours'); + + return $html; + } + + public function selectTimeSpent(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="4"'), $attributes); + + $html = $this->helper->form->label(t('Time spent'), 'time_spent'); + $html .= $this->helper->form->numeric('time_spent', $values, $errors, $attributes); + $html .= ' '.t('hours'); + + return $html; } } diff --git a/app/Helper/Task.php b/app/Helper/Task.php index 500b8a89..1cb36b86 100644 --- a/app/Helper/Task.php +++ b/app/Helper/Task.php @@ -12,6 +12,14 @@ use Kanboard\Core\Base; */ class Task extends Base { + /** + * Local cache for project columns + * + * @access private + * @var array + */ + private $columns = array(); + public function getColors() { return $this->color->getList(); @@ -37,6 +45,53 @@ class Task extends Base return $this->taskPermission->canRemoveTask($task); } + public function selectAssignee(array $users, array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="3"'), $attributes); + + $html = $this->helper->form->label(t('Assignee'), 'owner_id'); + $html .= $this->helper->form->select('owner_id', $users, $values, $errors, $attributes); + $html .= ' <a href="#" class="assign-me" data-target-id="form-owner_id" data-current-id="'.$this->userSession->getId().'" title="'.t('Assign to me').'">'.t('Me').'</a>'; + + return $html; + } + + public function selectCategory(array $categories, array $values, array $errors = array(), array $attributes = array(), $allow_one_item = false) + { + $attributes = array_merge(array('tabindex="4"'), $attributes); + $html = ''; + + if (! (! $allow_one_item && count($categories) === 1 && key($categories) == 0)) { + $html .= $this->helper->form->label(t('Category'), 'category_id'); + $html .= $this->helper->form->select('category_id', $categories, $values, $errors, $attributes); + } + + return $html; + } + + public function selectSwimlane(array $swimlanes, array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="5"'), $attributes); + $html = ''; + + if (! (count($swimlanes) === 1 && key($swimlanes) == 0)) { + $html .= $this->helper->form->label(t('Swimlane'), 'swimlane_id'); + $html .= $this->helper->form->select('swimlane_id', $swimlanes, $values, $errors, $attributes); + } + + return $html; + } + + public function selectColumn(array $columns, array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="6"'), $attributes); + + $html = $this->helper->form->label(t('Column'), 'column_id'); + $html .= $this->helper->form->select('column_id', $columns, $values, $errors, $attributes); + + return $html; + } + public function selectPriority(array $project, array $values) { $html = ''; @@ -53,6 +108,61 @@ class Task extends Base return $html; } + public function selectScore(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="8"'), $attributes); + + $html = $this->helper->form->label(t('Complexity'), 'score'); + $html .= $this->helper->form->number('score', $values, $errors, $attributes); + + return $html; + } + + public function selectTimeEstimated(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="9"'), $attributes); + + $html = $this->helper->form->label(t('Original estimate'), 'time_estimated'); + $html .= $this->helper->form->numeric('time_estimated', $values, $errors, $attributes); + $html .= ' '.t('hours'); + + return $html; + } + + public function selectTimeSpent(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="10"'), $attributes); + + $html = $this->helper->form->label(t('Time spent'), 'time_spent'); + $html .= $this->helper->form->numeric('time_spent', $values, $errors, $attributes); + $html .= ' '.t('hours'); + + return $html; + } + + public function selectStartDate(array $values, array $errors = array(), array $attributes = array()) + { + $placeholder = $this->helper->text->in($this->config->get('application_date_format'), $this->dateParser->getAvailableFormats()); + $attributes = array_merge(array('tabindex="11"', 'placeholder="'.$placeholder.'"'), $attributes); + + $html = $this->helper->form->label(t('Start Date'), 'date_started'); + $html .= $this->helper->form->text('date_started', $values, $errors, $attributes, 'form-date'); + + return $html; + } + + public function selectDueDate(array $values, array $errors = array(), array $attributes = array()) + { + $placeholder = $this->helper->text->in($this->config->get('application_date_format'), $this->dateParser->getAvailableFormats()); + $attributes = array_merge(array('tabindex="12"', 'placeholder="'.$placeholder.'"'), $attributes); + + $html = $this->helper->form->label(t('Due Date'), 'date_due'); + $html .= $this->helper->form->text('date_due', $values, $errors, $attributes, 'form-date'); + $html .= '<div class="form-help">'.t('Others formats accepted: %s and %s', date('Y-m-d'), date('Y_m_d')).'</div>'; + + return $html; + } + public function formatPriority(array $project, array $task) { $html = ''; @@ -65,4 +175,13 @@ class Task extends Base return $html; } + + public function getProgress($task) + { + if (! isset($this->columns[$task['project_id']])) { + $this->columns[$task['project_id']] = $this->board->getColumnsList($task['project_id']); + } + + return $this->task->getProgress($task, $this->columns[$task['project_id']]); + } } diff --git a/app/Locale/bs_BA/translations.php b/app/Locale/bs_BA/translations.php index 90ab1296..eafdfd0b 100644 --- a/app/Locale/bs_BA/translations.php +++ b/app/Locale/bs_BA/translations.php @@ -260,9 +260,6 @@ return array( 'External authentication failed' => 'Vanjska autentikacija nije uspostavljena', 'Your external account is linked to your profile successfully.' => 'Uspješno uspostavljena vanjska autentikacija', 'Email' => 'E-mail', - 'Link my Google Account' => 'Poveži sa Google nalogom', - 'Unlink my Google Account' => 'Ukini vezu sa Google nalogom', - 'Login with my Google Account' => 'Prijavi se preko Google naloga', 'Project not found.' => 'Projekat nije pronađen.', 'Task removed successfully.' => 'Zadatak uspješno uklonjen.', 'Unable to remove this task.' => 'Nemoguće uklanjanje zadatka.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Maksimalna veličina: ', 'Unable to upload the file.' => 'Nije moguće snimiti fajl.', 'Display another project' => 'Prikaži drugi projekat', - 'Login with my Github Account' => 'Prijavi me s mojim Github korisničkim računom', - 'Link my Github Account' => 'Poveži s mojim Github korisničkim računom', - 'Unlink my Github Account' => 'Odbavi vez s mojim Github korisničkim računom', 'Created by %s' => 'Kreirao %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Posljednja izmjena %e %B %Y o %k:%M', 'Tasks Export' => 'Izvoz zadataka', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Promijeni šifru', 'Password modification' => 'Izmjena šifre', 'External authentications' => 'Vanjske autentikacije', - 'Google Account' => 'Google korisnički račun', - 'Github Account' => 'Github korisnički račun', 'Never connected.' => 'Bez konekcija.', 'No account linked.' => 'Bez povezanih korisničkih računa.', 'Account linked.' => 'Korisnički račun povezan.', @@ -846,10 +838,6 @@ return array( 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Ovaj grafik pokazuje prosjek vremena vođenja i vremenskog ciklusa za posljednjih %d zadataka tokom vremena.', 'Average time into each column' => 'Prosječno vrijeme u svakoj koloni', 'Lead and cycle time' => 'Vrijeme vođenja i vremenski ciklus', - 'Google Authentication' => 'Google autentifikacija', - 'Help on Google authentication' => 'Pomoć na Google autentifikacija', - 'Github Authentication' => 'Github autentifikacija', - 'Help on Github authentication' => 'Pomoć na Github autentifikacija', 'Lead time: ' => 'Vrijeme vođenja: ', 'Cycle time: ' => 'Vremenski ciklus: ', 'Time spent into each column' => 'Utrošeno vrijeme u svakoj koloni', @@ -858,8 +846,6 @@ return array( 'If the task is not closed the current time is used instead of the completion date.' => 'Ako zadatak nije zatvoren trenutno vrijeme je iskorišteno umjesto datuma završetka.', 'Set automatically the start date' => 'Automatski postavi početno vrijeme', 'Edit Authentication' => 'Uredi autentifikaciju', - 'Google Id' => 'Google Id', - 'Github Id' => 'Github Id', 'Remote user' => 'Vanjski korisnik', 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Vanjski korisnik ne čuva šifru u Kanboard bazi, npr: LDAP, Google i Github korisnički računi.', 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Ako ste označili kvadratić "Zabrani prijavnu formu", unos pristupnih podataka u prijavnoj formi će biti ignorisan.', @@ -917,14 +903,7 @@ return array( 'Link type' => 'Tip veze', 'Change task color when using a specific task link' => 'Promijeni boju zadatka kada se koristi određena veza na zadatku', 'Task link creation or modification' => 'Veza na zadatku je napravljena ili izmijenjena', - 'Login with my Gitlab Account' => 'Prijava s mojim Gitlab korisničkim računom', 'Milestone' => 'Prekretnica', - 'Gitlab Authentication' => 'Gitlab autentifikacija', - 'Help on Gitlab authentication' => 'Pomoć na Gitlab autentifikacija', - 'Gitlab Id' => 'Gitlab Id', - 'Gitlab Account' => 'Gitlab korisnički račun', - 'Link my Gitlab Account' => 'Veza s mojim Gitlab korisničkim računom', - 'Unlink my Gitlab Account' => 'Prekini vezu s mojim Gitlab korisničkim računom', 'Documentation: %s' => 'Dokumentacija: %s', 'Switch to the Gantt chart view' => 'Promijeni u gantogram pregled', 'Reset the search/filter box' => 'Vrati na početno pretragu/filtere', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/cs_CZ/translations.php b/app/Locale/cs_CZ/translations.php index 83d88f35..ef615066 100644 --- a/app/Locale/cs_CZ/translations.php +++ b/app/Locale/cs_CZ/translations.php @@ -260,9 +260,6 @@ return array( // 'External authentication failed' => '', // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'E-Mail', - 'Link my Google Account' => 'Propojit s Google účtem', - 'Unlink my Google Account' => 'Odpojit Google účet', - 'Login with my Google Account' => 'Přihlášení pomocí Google účtu', 'Project not found.' => 'Projekt nebyl nalezen.', 'Task removed successfully.' => 'Úkol byl úspěšně odebrán.', 'Unable to remove this task.' => 'Tento úkol nelze odebrat.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Maximální velikost: ', 'Unable to upload the file.' => 'Soubor nelze nahrát.', 'Display another project' => 'Zobrazit jiný projekt', - // 'Login with my Github Account' => '', - // 'Link my Github Account' => '', - // 'Unlink my Github Account' => '', 'Created by %s' => 'Vytvořeno uživatelem %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Poslední úprava dne %d.%m.%Y v čase %H:%M', 'Tasks Export' => 'Export úkolů', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Změnit heslo', 'Password modification' => 'Změna hesla', 'External authentications' => 'Vzdálená autorizace', - 'Google Account' => 'Google účet', - 'Github Account' => 'github účet', 'Never connected.' => 'Zatím nikdy nespojen.', 'No account linked.' => 'Žádné propojení účtu.', 'Account linked.' => 'Propojení účtu', @@ -846,10 +838,6 @@ return array( 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Graf ukazuje průměrnou dodací lhůtu a dobu cyklu pro posledních %d úkolů v průběhu času', 'Average time into each column' => 'Průměrná doba v každé fázi', 'Lead and cycle time' => 'Dodací lhůta a doba cyklu', - 'Google Authentication' => 'Ověřování pomocí služby Google', - 'Help on Google authentication' => 'Nápověda k ověřování pomocí služby Google', - 'Github Authentication' => 'Ověřování pomocí služby Github', - 'Help on Github authentication' => 'Nápověda k ověřování pomocí služby Github', 'Lead time: ' => 'Dodací lhůta: ', 'Cycle time: ' => 'Doba cyklu: ', 'Time spent into each column' => 'Čas strávený v každé fázi', @@ -858,8 +846,6 @@ return array( 'If the task is not closed the current time is used instead of the completion date.' => 'Jestliže není úkol uzavřen, místo termínu dokončení je použit aktuální čas.', 'Set automatically the start date' => 'Nastavit automaticky počáteční datum', 'Edit Authentication' => 'Upravit ověřování', - 'Google Id' => 'Google ID', - 'Github Id' => 'Github ID', 'Remote user' => 'Vzdálený uživatel', 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Hesla vzdáleným uživatelům se neukládají do databáze Kanboard. Naříklad: LDAP, Google a Github účty.', 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Pokud zaškrtnete políčko "Zakázat přihlašovací formulář", budou pověření zadané do přihlašovacího formuláře ignorovány.', @@ -917,14 +903,7 @@ return array( // 'Link type' => '', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', - // 'Login with my Gitlab Account' => '', // 'Milestone' => '', - // 'Gitlab Authentication' => '', - // 'Help on Gitlab authentication' => '', - // 'Gitlab Id' => '', - // 'Gitlab Account' => '', - // 'Link my Gitlab Account' => '', - // 'Unlink my Gitlab Account' => '', // 'Documentation: %s' => '', // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php index 7a82bc1e..6ac60df5 100644 --- a/app/Locale/da_DK/translations.php +++ b/app/Locale/da_DK/translations.php @@ -260,9 +260,6 @@ return array( // 'External authentication failed' => '', // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'E-Mail', - 'Link my Google Account' => 'Forbind min Google-konto', - 'Unlink my Google Account' => 'Fjern forbindelsen til min Google-konto', - 'Login with my Google Account' => 'Login med min Google-konto', 'Project not found.' => 'Projekt ikke fundet.', 'Task removed successfully.' => 'Opgaven er fjernet.', 'Unable to remove this task.' => 'Opgaven kunne ikke fjernes.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Maksimum størrelse: ', 'Unable to upload the file.' => 'Filen kunne ikke uploades.', 'Display another project' => 'Vis et andet projekt...', - 'Login with my Github Account' => 'Login med min Github-konto', - 'Link my Github Account' => 'Forbind min Github-konto', - 'Unlink my Github Account' => 'Fjern forbindelsen til min Github-konto', 'Created by %s' => 'Oprettet af %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Sidst redigeret %d.%m.%Y - %H:%M', 'Tasks Export' => 'Opgave eksport', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Skift adgangskode', 'Password modification' => 'Adgangskode ændring', 'External authentications' => 'Ekstern autentificering', - 'Google Account' => 'Google-konto', - 'Github Account' => 'Github-konto', 'Never connected.' => 'Aldrig forbundet.', 'No account linked.' => 'Ingen kontoer forfundet.', 'Account linked.' => 'Konto forbundet.', @@ -846,10 +838,6 @@ return array( // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', // 'Average time into each column' => '', // 'Lead and cycle time' => '', - // 'Google Authentication' => '', - // 'Help on Google authentication' => '', - // 'Github Authentication' => '', - // 'Help on Github authentication' => '', // 'Lead time: ' => '', // 'Cycle time: ' => '', // 'Time spent into each column' => '', @@ -858,8 +846,6 @@ return array( // 'If the task is not closed the current time is used instead of the completion date.' => '', // 'Set automatically the start date' => '', // 'Edit Authentication' => '', - // 'Google Id' => '', - // 'Github Id' => '', // 'Remote user' => '', // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', @@ -917,14 +903,7 @@ return array( // 'Link type' => '', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', - // 'Login with my Gitlab Account' => '', // 'Milestone' => '', - // 'Gitlab Authentication' => '', - // 'Help on Gitlab authentication' => '', - // 'Gitlab Id' => '', - // 'Gitlab Account' => '', - // 'Link my Gitlab Account' => '', - // 'Unlink my Gitlab Account' => '', // 'Documentation: %s' => '', // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php index 92346a15..a6e5f969 100644 --- a/app/Locale/de_DE/translations.php +++ b/app/Locale/de_DE/translations.php @@ -20,7 +20,7 @@ return array( 'Red' => 'Rot', 'Orange' => 'Orange', 'Grey' => 'Grau', - 'Brown' => 'Bran', + 'Brown' => 'Braun', 'Deep Orange' => 'Dunkelorange', 'Dark Grey' => 'Dunkelgrau', 'Pink' => 'Pink', @@ -28,7 +28,7 @@ return array( 'Cyan' => 'Cyan', 'Lime' => 'Limette', 'Light Green' => 'Hellgrün', - 'Amber' => 'Braun (Amber)', + 'Amber' => 'Bernstein', 'Save' => 'Speichern', 'Login' => 'Anmelden', 'Official website:' => 'Offizielle Webseite:', @@ -96,7 +96,7 @@ return array( 'Edit a task' => 'Aufgabe bearbeiten', 'Column' => 'Spalte', 'Color' => 'Farbe', - 'Assignee' => 'Zuständig', + 'Assignee' => 'Zuständiger', 'Create another task' => 'Weitere Aufgabe erstellen', 'New task' => 'Neue Aufgabe', 'Open a task' => 'Öffne eine Aufgabe', @@ -260,9 +260,6 @@ return array( 'External authentication failed' => 'Externe Authentifizierung fehlgeschlagen', 'Your external account is linked to your profile successfully.' => 'Dein externer Account wurde erfolgreich mit deinem Profil verbunden', 'Email' => 'E-Mail', - 'Link my Google Account' => 'Verbinde meinen Google-Account', - 'Unlink my Google Account' => 'Verbindung mit meinem Google-Account trennen', - 'Login with my Google Account' => 'Anmelden mit meinem Google-Account', 'Project not found.' => 'Das Projekt wurde nicht gefunden.', 'Task removed successfully.' => 'Aufgabe erfolgreich gelöscht.', 'Unable to remove this task.' => 'Löschen der Aufgabe nicht möglich.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Maximalgröße: ', 'Unable to upload the file.' => 'Hochladen der Datei nicht möglich.', 'Display another project' => 'Zu Projekt wechseln', - 'Login with my Github Account' => 'Anmelden mit meinem Github-Account', - 'Link my Github Account' => 'Mit meinem Github-Account verbinden', - 'Unlink my Github Account' => 'Verbindung mit meinem Github-Account trennen', 'Created by %s' => 'Erstellt durch %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Letzte Änderung am %d.%m.%Y um %H:%M', 'Tasks Export' => 'Aufgaben exportieren', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Passwort ändern', 'Password modification' => 'Passwortänderung', 'External authentications' => 'Externe Authentisierungsmethoden', - 'Google Account' => 'Google-Account', - 'Github Account' => 'Github-Account', 'Never connected.' => 'Noch nie verbunden.', 'No account linked.' => 'Kein Account verbunden.', 'Account linked.' => 'Account verbunden', @@ -846,10 +838,6 @@ return array( 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Das Diagramm zeigt die durchschnittliche Durchlauf- und Zykluszeit der letzten %d Aufgaben über die Zeit an.', 'Average time into each column' => 'Durchschnittzeit in jeder Spalte', 'Lead and cycle time' => 'Durchlauf- und Zykluszeit', - 'Google Authentication' => 'Google-Authentifizierung', - 'Help on Google authentication' => 'Hilfe bei Google-Authentifizierung', - 'Github Authentication' => 'Github-Authentifizierung', - 'Help on Github authentication' => 'Hilfe bei Github-Authentifizierung', 'Lead time: ' => 'Durchlaufzeit:', 'Cycle time: ' => 'Zykluszeit:', 'Time spent into each column' => 'zeit verbracht in jeder Spalte', @@ -858,8 +846,6 @@ return array( 'If the task is not closed the current time is used instead of the completion date.' => 'Wenn die Aufgabe nicht geschlossen ist, wird die aktuelle Zeit statt der Fertigstellung verwendet.', 'Set automatically the start date' => 'Setze Startdatum automatisch', 'Edit Authentication' => 'Authentifizierung bearbeiten', - 'Google Id' => 'Google Id', - 'Github Id' => 'Github Id', 'Remote user' => 'Remote-Benutzer', 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Remote-Benutzer haben kein Passwort in der Kanboard Datenbank, Beispiel LDAP, Goole und Github Accounts', 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Wenn die Box "Verbiete Login-Formular" angeschaltet ist, werden Eingaben in das Login Formular ignoriert.', @@ -917,14 +903,7 @@ return array( 'Link type' => 'Verbindungstyp', 'Change task color when using a specific task link' => 'Aufgabefarbe ändern bei bestimmter Aufgabenverbindung', 'Task link creation or modification' => 'Aufgabenverbindung erstellen oder bearbeiten', - 'Login with my Gitlab Account' => 'Mit Gitlab Account einloggen', 'Milestone' => 'Meilenstein', - 'Gitlab Authentication' => 'Gitlab-Authentifizierung', - 'Help on Gitlab authentication' => 'Hilfe bei Gitlab-Authentifizierung', - 'Gitlab Id' => 'Gitlab Id', - 'Gitlab Account' => 'Gitlab Account', - 'Link my Gitlab Account' => 'Verknüpfe mein Gitlab Account', - 'Unlink my Gitlab Account' => 'Trenne meinen Gitlab Account', 'Documentation: %s' => 'Dokumentation: %s', 'Switch to the Gantt chart view' => 'Zur Gantt-Diagramm Ansicht wechseln', 'Reset the search/filter box' => 'Suche/Filter-Box zurücksetzen', @@ -1096,26 +1075,59 @@ return array( 'Creation' => 'Erstellung', 'Expiration' => 'Ablauf', 'Password reset history' => 'Verlauf Passwortrücksetzung', - // 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '', - // 'Do you really want to close all tasks of this column?' => '', - // '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '', - // 'Close all tasks of this column' => '', - // 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '', - // 'My dashboard' => '', - // 'My profile' => '', - // 'Project owner: ' => '', - // 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '', - // 'Project owner' => '', - // 'Those dates are useful for the project Gantt chart.' => '', - // 'Private projects do not have users and groups management.' => '', - // 'There is no project member.' => '', - // 'Priority' => '', - // 'Task priority' => '', - // 'General' => '', - // 'Dates' => '', - // 'Default priority' => '', - // 'Lowest priority' => '', - // 'Highest priority' => '', - // 'If you put zero to the low and high priority, this feature will be disabled.' => '', - // 'Priority: %d' => '', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Alle Aufgaben der Spalte "%s" und der Swimlane "%s" wurden erfolgreich geschlossen', + 'Do you really want to close all tasks of this column?' => 'Wollen Sie wirklich alle Aufgaben in dieser Spalte schließen?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d Aufgabe(n) in der Spalte "%s" und in der Swimlane "%s" werden geschlossen.', + 'Close all tasks of this column' => 'Alle Aufgaben in dieser Spalte schließen', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Kein Plugin hat eine Projekt-Benachrichtigungsmethode registriert. Sie können individuelle Meldungen in Ihrem Benutzerprofil konfigurieren', + 'My dashboard' => 'Mein Dashboard', + 'My profile' => 'Mein Profil', + 'Project owner: ' => 'Projekt-Besitzer:', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Die Projekt-Kennung ist optional und muss alphanumerisch sein, beispielsweise: MYPROJECT.', + 'Project owner' => 'Projekt-Besitzer', + 'Those dates are useful for the project Gantt chart.' => 'Diese Daten sind nützlich für das Gantt-Diagramm.', + 'Private projects do not have users and groups management.' => 'Private Projekte haben kein Benutzer- und Gruppen-Management.', + 'There is no project member.' => 'Es gibt kein Projekt-Mitglied.', + 'Priority' => 'Priorität', + 'Task priority' => 'Aufgaben-Priorität', + 'General' => 'Allgemein', + 'Dates' => 'Daten', + 'Default priority' => 'Standard-Priorität', + 'Lowest priority' => 'Niedrigste Priorität', + 'Highest priority' => 'Höchste Priorität', + 'If you put zero to the low and high priority, this feature will be disabled.' => 'Wenn Sie Null bei höchster und niedrigster Priorität eintragen, wird diese Funktion deaktiviert.', + 'Priority: %d' => 'Priorität: %d', + 'Close a task when there is no activity' => 'Schliesse eine Aufgabe, wenn keine Aktivitäten vorhanden sind', + 'Duration in days' => 'Dauer in Tagen', + 'Send email when there is no activity on a task' => 'Versende eine Email, wenn keine Aktivitäten an einer Aufgabe vorhanden sind', + 'List of external links' => 'Liste der externen Verbindungen', + 'Unable to fetch link information.' => 'Kann keine Informationen über Verbindungen holen', + 'Daily background job for tasks' => 'Tägliche Hintergrundarbeit für Aufgaben', + 'Auto' => 'Auto', + 'Related' => 'Verbunden', + 'Attachment' => 'Anhang', + 'Title not found' => 'Titel nicht gefunden', + 'Web Link' => 'Weblink', + 'External links' => 'Externe Verbindungen', + 'Add external link' => 'Externe Verbindung hinzufügen', + 'Type' => 'Typ', + 'Dependency' => 'Abhängigkeit', + 'View internal links' => 'Zeige interne Verbindungen', + 'View external links' => 'Zeige externe Verbindungen', + 'Add internal link' => 'Füge interne Verbindung hinzu', + 'Add a new external link' => 'Füge eine neue externe Verbindung hinzu', + 'Edit external link' => 'Externe Verbindung bearbeiten', + 'External link' => 'Externe Verbindung', + 'Copy and paste your link here...' => 'Kopieren Sie Ihren Link hier...', + 'URL' => 'URL', + 'There is no external link for the moment.' => 'Es gibt im Moment keine externe Verbindung.', + 'Internal links' => 'Interne Verbindungen', + 'There is no internal link for the moment.' => 'Es gibt im Moment keine interne Verbindung.', + 'Assign to me' => 'Mir zuweisen', + 'Me' => 'Mich', + 'Do not duplicate anything' => 'Nichts duplizieren', + 'Projects management' => 'Projektmanagement', + 'Users management' => 'Benutzermanagement', + 'Groups management' => 'Gruppenmanagement', + 'Create from another project' => 'Von einem anderen Projekt erstellen', ); diff --git a/app/Locale/el_GR/translations.php b/app/Locale/el_GR/translations.php new file mode 100644 index 00000000..ffed12b8 --- /dev/null +++ b/app/Locale/el_GR/translations.php @@ -0,0 +1,1133 @@ +<?php + +return array( + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'Τίποτα', + 'edit' => 'Διορθωτής', + 'Edit' => 'Διόρθωση', + 'remove' => 'αφαιρετής', + 'Remove' => 'Αφαίρεση', + 'Update' => 'Ενημέρωση', + 'Yes' => 'Ναι', + 'No' => 'Όχι', + 'cancel' => 'ακύρωση', + 'or' => 'ή', + 'Yellow' => 'Κίτρινο', + 'Blue' => 'Μπλέ', + 'Green' => 'Πράσινο', + 'Purple' => 'Βιολετί', + 'Red' => 'Κόκκινο', + 'Orange' => 'Ποστοκαλί', + 'Grey' => 'Γκρίζο', + 'Brown' => 'Καφέ', + 'Deep Orange' => 'Βαθύ πορτοκαλί', + 'Dark Grey' => 'Βαθύ γκρί', + 'Pink' => 'Ρόζ', + 'Teal' => 'Τουρκουάζ', + 'Cyan' => 'Γαλάζιο', + 'Lime' => 'Λεμονί', + 'Light Green' => 'Ανοιχτό πράσινο', + 'Amber' => 'Κεχριμπαρί', + 'Save' => 'Αποθήκευση', + 'Login' => 'Είσοδος', + 'Official website:' => 'Επίσημο web site :', + 'Unassigned' => 'Μη εκχωρημένο', + 'View this task' => 'Προβολή της εργασίας', + 'Remove user' => 'Αφαίρεση χρήστη', + 'Do you really want to remove this user: "%s"?' => 'Θέλετε σίγουρα να αφαιρέσετε αυτό τον χρήστη : « %s » ?', + 'New user' => 'Νέος Χρήστης', + 'All users' => 'Όλοι οι χρήστες', + 'Username' => 'Όνομα χρήστη', + 'Password' => 'Κωδικός', + 'Administrator' => 'Διαχειριστής', + 'Sign in' => 'Είσοδος', + 'Users' => 'Χρήστες', + 'No user' => 'Δεν υπάρχει χρήστης', + 'Forbidden' => 'Δεν επιτρέπεται η πρόσβαση', + 'Access Forbidden' => 'Δεν επιτρέπεται η πρόσβαση', + 'Edit user' => 'Διόρθωση χρήστη', + 'Logout' => 'Αποσύνδεση', + 'Bad username or password' => 'Λάθος όνομα χρήστη ή κωδικού πρόσβασης', + 'Edit project' => 'Διόρθωση έργου', + 'Name' => 'Όνομα', + 'Projects' => 'Έργα', + 'No project' => 'Δεν υπάρχουν μέλη για το έργο', + 'Project' => 'Έργο', + 'Status' => 'Κατάσταση', + 'Tasks' => 'Εργασίες', + 'Board' => 'Κεντρικός πίνακας έργου', + 'Actions' => 'Ενέργειες', + 'Inactive' => 'Ανενεργός', + 'Active' => 'Ενεργός', + 'Add this column' => 'Προσθήκη αυτής της στήλης', + '%d tasks on the board' => '%d εργασίες στον κεντρικό πίνακα έργου', + '%d tasks in total' => '%d εργασιών στο σύνολο', + 'Unable to update this board.' => 'Αδύνατη η ενημέρωση αυτού του πίνακα', + 'Edit board' => 'Ενημέρωση πίνακα', + 'Disable' => 'Απενεργοποίηση', + 'Enable' => 'Ενεργοποίηση', + 'New project' => 'Νέο έργο', + 'Do you really want to remove this project: "%s"?' => 'Αφαίρεση του έργου : « %s » ?', + 'Remove project' => 'Αφαίρεση του έργου', + 'Edit the board for "%s"' => 'Διόρθωση πίνακα από « %s »', + 'All projects' => 'Όλα τα έργα', + 'Change columns' => 'Αλλαγή στηλών', + 'Add a new column' => 'Πρόσθήκη στήλης', + 'Title' => 'Τίτλος', + 'Nobody assigned' => 'Δεν έχει ανατεθεί', + 'Assigned to %s' => 'Ανατιθεμένο στον %s', + 'Remove a column' => 'Αφαίρεση στήλης', + 'Remove a column from a board' => 'Αφαίρεση στήλης από τον πίνακα', + 'Unable to remove this column.' => 'Αδύνατη η αφαίρεση της στήλης', + 'Do you really want to remove this column: "%s"?' => 'Θέλετε να αφαιρέσετε τη στήλη : « %s » ?', + 'This action will REMOVE ALL TASKS associated to this column!' => 'Αυτή η ενέργεια θα ΑΦΑΙΡΕΣΕΙ ΟΛΕΣ ΤΙΣ ΕΡΓΑΣΙΕΣ που είναι σχετικές με τη στήλη!!', + 'Settings' => 'Προτιμήσεις', + 'Application settings' => 'Παραμετροποίηση εφαρμογής', + 'Language' => 'Γλώσσα', + 'Webhook token:' => 'Διακριτικό ασφαλείας (token) webhooks :', + 'API token:' => 'Διακριτικό ασφαλείας (token) API :', + 'Database size:' => 'Μέγεθος βάσης δεδομένων :', + 'Download the database' => 'Κατεβάστε τη βάση δεδομένων', + 'Optimize the database' => 'Βελτιστοποίηση της βάσης δεδομένων', + '(VACUUM command)' => '(VACUUM command)', + '(Gzip compressed Sqlite file)' => '(Gzip compressed Sqlite file)', + 'Close a task' => 'Κλείσιμο εργασίας', + 'Edit a task' => 'Διόρθωση εργασίας', + 'Column' => 'Στήλη', + 'Color' => 'Χρώμα', + 'Assignee' => 'Ανατεθιμένα', + 'Create another task' => 'Δημιουργία και άλλης εργασίας', + 'New task' => 'Νέα εργασία', + 'Open a task' => 'Άνοιγμα εργασίας', + 'Do you really want to open this task: "%s"?' => 'Άνοιγμα της εργασίας : « %s » ?', + 'Back to the board' => 'Επιστροφή στον κεντρικό πίνακα έργου', + 'Created on %B %e, %Y at %k:%M %p' => 'Δημιουργήθηκε στις %d/%m/%Y και ώρα %H:%M', + 'There is nobody assigned' => 'Δεν έχει ανατεθεί σε κανένα', + 'Column on the board:' => 'Στήλη στον κεντρικό πίνακα : ', + 'Status is open' => 'Η κατάσταση είναι "ανοικτά"', + 'Status is closed' => 'Η κατάσταση είναι "κλειστά"', + 'Close this task' => 'Κλείσιμο εργασίας', + 'Open this task' => 'Άνοιγμα εργασίας', + 'There is no description.' => 'Δεν υπάρχει περιγραφή.', + 'Add a new task' => 'Προσθήκη νέας εργασίας', + 'The username is required' => 'Το όνομα χρήστη απαιτείται', + 'The maximum length is %d characters' => 'Ο μέγιστος αριθμός χαρακτήρων είναι %d χαρακτήρες', + 'The minimum length is %d characters' => 'Ο ελάχιστος αριθμός χαρακτήρων είναι %d χαρακτήρες', + 'The password is required' => 'Ο κωδικός απαιτείται', + 'This value must be an integer' => 'Η τιμή πρέπει να είναι ακέραιος', + 'The username must be unique' => 'Το όνομα χρήστη πρέπει να είναι μοναδικό', + 'The user id is required' => 'το αναγνωριστικό χρήστη απαιτείται', + 'Passwords don\'t match' => 'Οι κωδικοί πρόσβασης δεν ταιριάζουν', + 'The confirmation is required' => 'Η επειβεβαίωση απαιτείται', + 'The project is required' => 'Το έργο απαιτείται', + 'The id is required' => 'το αναγνωριστικό απαιτείται', + 'The project id is required' => 'το αναγνωριστικό έργου απαιτείται', + 'The project name is required' => 'Η ονομασία έργου απαιτείται', + 'The title is required' => 'Ο τίτλος απαιτείται', + 'Settings saved successfully.' => 'Οι προτιμήσεις αποθηκεύθηκαν με επιτυχία.', + 'Unable to save your settings.' => 'Αδυναμία αποθήκευσης προτιμήσεων.', + 'Database optimization done.' => 'Η βελτιστοποίηση της βάσης δεδομένων έγινε με επιτυχία.', + 'Your project have been created successfully.' => 'Το έργο δημιουργήθηκε.', + 'Unable to create your project.' => 'Δεν είναι δυνατή η δημιουργία του έργου', + 'Project updated successfully.' => 'Το έργο ενημερώθηκε με επιτυχία.', + 'Unable to update this project.' => 'Δεν είναι δυνατή η ενημέρωση του έργου.', + 'Unable to remove this project.' => 'Δεν είναι δυνατή η διαγραφή του έργου', + 'Project removed successfully.' => 'Το έργο αφαιρέθηκε επιτυχώς.', + 'Project activated successfully.' => 'Το έργο ενεργοποιήθηκε με επιτυχία', + 'Unable to activate this project.' => 'Δεν είναι δυνατή η ενεργοποίηση του έργου', + 'Project disabled successfully.' => 'Το έργο ενεργοποιήθηκε με επιτυχία', + 'Unable to disable this project.' => 'Δεν είναι δυνατή η επενεργοποίηση του έργου.', + 'Unable to open this task.' => 'Δεν είναι δυνατό το άνοιγμα της εργασίας', + 'Task opened successfully.' => 'Η εργασία άνοιξε με επιτυχία', + 'Unable to close this task.' => 'Δεν είναι δυνατό το κλείσιμο της εργασίας', + 'Task closed successfully.' => 'Η εργασία έκλεισε επιτυχώς', + 'Unable to update your task.' => 'Δεν είναι δυνατή η ενημέρωση της εργασίας', + 'Task updated successfully.' => 'Η εργασία ενημερώθηκε επιτυχώς', + 'Unable to create your task.' => 'Δεν είναι δυνατή η δημιουργία της εργασίας', + 'Task created successfully.' => 'Η εργασία δημιουργήθηκε επιτυχώς', + 'User created successfully.' => 'Ο χρήστης δημιουργήθηκε με επιτυχία', + 'Unable to create your user.' => 'Δεν είναι δυνατή η δημιουργία χρήστη', + 'User updated successfully.' => 'Ο χρήστης ενημερωθηκε με επιτυχία', + 'Unable to update your user.' => 'Δεν είναι δυνατή η ενημέρωση του χρήστη', + 'User removed successfully.' => 'Ο χρήστης αφαιρέθηκε με επιτυχία.', + 'Unable to remove this user.' => 'Δεν είναι δυνατή η αφαίρεση χρήστη.', + 'Board updated successfully.' => 'Ο πίνακας ενημερώθηκε με επιτυχία.', + 'Ready' => 'Έτοιμο', + 'Backlog' => 'Πρέπει να', + 'Work in progress' => 'Σε πρόοδο', + 'Done' => 'Ολοκληρωμένα', + 'Application version:' => 'Version εφαρμογής :', + 'Completed on %B %e, %Y at %k:%M %p' => 'Ολοκληρώθηκε στις %d/%m/%Y και ώρα %H:%M', + '%B %e, %Y at %k:%M %p' => '%d/%m/%Y και ώρα %H:%M', + 'Date created' => 'Ημερομηνία δημιουργίας', + 'Date completed' => 'Ημερομηνία ολοκλήρωσης', + 'Id' => 'Αναγνωριστικό.', + '%d closed tasks' => '%d κλειστές εργασίες', + 'No task for this project' => 'Αριθμός εργασιών για το έργο', + 'Public link' => 'Δημόσιος σύνδεσμος', + 'Change assignee' => 'Αλλαγή ανάθεσης', + 'Change assignee for the task "%s"' => 'Αλλαγή ανάθεσης για την εργασία « %s »', + 'Timezone' => 'Timezone', + 'Sorry, I didn\'t find this information in my database!' => 'Δυστυχώς δεν βρέθηκε αυτή η πληροφορία στη βάση δεδομένων', + 'Page not found' => 'Η σελίδα δεν βρέθηκε', + 'Complexity' => 'Πολυπλοκότητα', + 'Task limit' => 'Όριο εργασιών.', + 'Task count' => 'Αρίθμηση εργασιών', + 'User' => 'Χρήστης', + 'Comments' => 'Σχόλια', + 'Write your text in Markdown' => 'Δυνατότητα γραφής και σε Markdown', + 'Leave a comment' => 'Αφήστε ένα σχόλιο', + 'Comment is required' => 'Το σχόλιο απαιτείται', + 'Leave a description' => 'Αφήστε μια περιγραφή', + 'Comment added successfully.' => 'Το σχόλιο σας προστέθηκε με επιτυχία.', + 'Unable to create your comment.' => 'Δεν είναι δυνατή η προσθήκη του σχολίου σας.', + 'Edit this task' => 'Διόρθωση εργασίας', + 'Due Date' => 'Μέχρι την ημερομηνία', + 'Invalid date' => 'Μη ορθή ημερομηνία', + 'Must be done before %B %e, %Y' => 'Πρέπει να ολοκληρωθεί πριν τις %d/%m/%Y', + '%B %e, %Y' => '%d %B %Y', + '%b %e, %Y' => '%d/%m/%Y', + 'Automatic actions' => 'Αυτόματες ενέργειες', + 'Your automatic action have been created successfully.' => 'Η αυτόματη ενέργεια δημιουργήθηκε με επιτυχία.', + 'Unable to create your automatic action.' => 'Impossible de créer votre action automatisée.', + 'Remove an action' => 'Αφαίρεση ενέργειας', + 'Unable to remove this action.' => 'Δεν είναι δυνατή η αφαίρεση αυτής της ενέργειας', + 'Action removed successfully.' => 'Η ενέργεια αφαιρέθηκε με επιτυχία.', + 'Automatic actions for the project "%s"' => 'Αυτόματες ενέργειες για το έργο « %s »', + 'Defined actions' => 'Ορισμένες ενέργειες', + 'Add an action' => 'Προσθήκη ενέργειας', + 'Event name' => 'Ονομασία συμβάντος', + 'Action name' => 'Ονομασία ενέργειας', + 'Action parameters' => 'Παράμετροι ενέργειας', + 'Action' => 'Ενέργεια', + 'Event' => 'Συμβάν', + 'When the selected event occurs execute the corresponding action.' => 'Όταν εμφανίζεται το επιλεγμένο συμβάν εκτελέστε την αντίστοιχη ενέργεια.', + 'Next step' => 'Επόμενο βήμα', + 'Define action parameters' => 'Ορισμός παραμέτρων ενέργειας', + 'Save this action' => 'Αποθήκευση ενέργειας', + 'Do you really want to remove this action: "%s"?' => 'Αφαίρεση της ενέργειας: « %s » ?', + 'Remove an automatic action' => 'Αφαίρεση της αυτόματης ενέργειας', + 'Assign the task to a specific user' => 'Ανάθεση της εργασίας σε συγκεκριμένο χρήστη', + 'Assign the task to the person who does the action' => 'Ανάθεση της εργασίας στο άτομο που κάνει την ενέργεια', + 'Duplicate the task to another project' => 'Αντιγραφή εργασίας σε άλλο έργο', + 'Move a task to another column' => 'Μεταφορά εργασίας σε άλλη στήλη', + 'Task modification' => 'Διόρθωση εργασίας', + 'Task creation' => 'Δημιουργία εργασίας', + 'Closing a task' => 'Κλείσιμο εργασίας', + 'Assign a color to a specific user' => 'Ανάθεση χρώματος σε συγκεκριμένο χρήστη', + 'Column title' => 'Τίτλος στήλης', + 'Position' => 'Θέση', + 'Move Up' => 'Μετακίνηση πάνω', + 'Move Down' => 'Μετακίνηση κάτω', + 'Duplicate to another project' => 'Αντιγραφή σε άλλο έργο', + 'Duplicate' => 'Αντιγραφή', + 'link' => 'σύνδεσμος', + 'Comment updated successfully.' => 'Το σχόλιο ενημερώθηκε με επιτυχία.', + 'Unable to update your comment.' => 'Αδυναμία ενημέρωσης σχολίου.', + 'Remove a comment' => 'Διαγραφή σχολίου', + 'Comment removed successfully.' => 'Το σχόλιο διαγράφτηκε με επιτυχία.', + 'Unable to remove this comment.' => 'Αδυναμία διαγραφής σχολίου.', + 'Do you really want to remove this comment?' => 'Θέλετε να αφαιρέσετε το σχόλιο ?', + 'Only administrators or the creator of the comment can access to this page.' => 'Μόνο οι διαχειριστές και ο δημιουργός του σχολίου έχουν πρόσβαση σε αυτήν τη σελίδα.', + 'Current password for the user "%s"' => 'Ο τρέχοντας κωδικός για τον χρήστη « %s »', + 'The current password is required' => 'Ο τρέχοντας κωδικός πρόσβασης απαιτείται', + 'Wrong password' => 'Λάθος κωδικός πρόσβασης', + 'Unknown' => 'Άγνωστο', + 'Last logins' => 'Τελευταίες συνδέσεις', + 'Login date' => 'Ημερομηνία σύνδεσης', + 'Authentication method' => 'Μέθοδος ελέγχου ταυτότητας', + 'IP address' => 'Διεύθυνση IP', + 'User agent' => 'User agent', + 'Persistent connections' => 'Μόνιμες συνδέσεις', + 'No session.' => 'Καμμία συνεδρία', + 'Expiration date' => 'Ημερομηνία λήξης', + 'Remember Me' => 'Remember Me', + 'Creation date' => 'Ημερομηνία δημιουργίας', + 'Everybody' => 'Όλα', + 'Open' => 'Ανοικτά', + 'Closed' => 'Κλειστά', + 'Search' => 'Αναζήτηση', + 'Nothing found.' => 'Δεν βρέθηκε.', + 'Due date' => 'Μέχρι την ημερομηνία', + 'Others formats accepted: %s and %s' => 'Άλλες δεκτές μορφοποιήσεις : %s και %s', + 'Description' => 'Περιγραφή', + '%d comments' => '%d σχόλια', + '%d comment' => '%d σχόλιο', + 'Email address invalid' => 'Μη αποδεκτή διεύθυνση email', + 'Your external account is not linked anymore to your profile.' => 'Ο λογαριασμός σας δεν συνδέεται πλέον με το προφίλ σας.', + 'Unable to unlink your external account.' => 'Αδυναμία αποσύνδεσης του εξωτερικού σας λογαριασμού.', + 'External authentication failed' => 'Αποτυχία εξωτερικής σύνδεσης', + 'Your external account is linked to your profile successfully.' => 'Ο λογαριασμός σας συνδέθηκε με το προφίλ σας με επιτυχία.', + 'Email' => 'Email', + 'Project not found.' => 'Το έργο δεν βρέθηκε.', + 'Task removed successfully.' => 'Η εργασία αφαιρέθηκε με επιτυχία.', + 'Unable to remove this task.' => 'Δεν είναι δυνατή η αφαίρεση της εργασίας.', + 'Remove a task' => 'Αφαίρεση εργασίας', + 'Do you really want to remove this task: "%s"?' => 'Αφαίρεση της εργασίας « %s » ?', + 'Assign automatically a color based on a category' => 'Αυτόματη εκχώρηση ενός χρώματος με βάση την κατηγορία', + 'Assign automatically a category based on a color' => 'Αυτόματη εκχώρηση μιας κατηγορίας με βάση το χρώμα', + 'Task creation or modification' => 'Δημιουργία ή τροποποίηση εργασιών', + 'Category' => 'Κατηγορία', + 'Category:' => 'Κατηγορία :', + 'Categories' => 'Κατηγορίες', + 'Category not found.' => 'Η κατηγορία δεν βρέθηκε', + 'Your category have been created successfully.' => 'Η κατηγορία δημιουργήθηκε.', + 'Unable to create your category.' => 'Δεν είναι δυνατή η δημιουργία της κατηγορίας.', + 'Your category have been updated successfully.' => 'Η ενημέρωση της κατηγορίας καταχωρήθηκε με επιτυχία.', + 'Unable to update your category.' => 'Δεν είναι δυνατή η ενημέρωση της κατηγορίας.', + 'Remove a category' => 'Διαγραφή κατηγορίας', + 'Category removed successfully.' => 'Η κατηγορία διεγράφει επιτυχώς.', + 'Unable to remove this category.' => 'Δεν είναι δυνατή η διαγραφή της κατηγορίας.', + 'Category modification for the project "%s"' => 'Τροποποίηση κατηγορίας για το έργο « %s »', + 'Category Name' => 'Ονομασία κατηγορίας', + 'Add a new category' => 'Προσθήκη νέας κατηγορίας', + 'Do you really want to remove this category: "%s"?' => 'Διαγραφή της κατηγορίας « %s » ?', + 'All categories' => 'Όλες οι κατηγορίες', + 'No category' => 'Χωρίς κατηγορία', + 'The name is required' => 'Η ονομασία απαιτείται', + 'Remove a file' => 'Αφαίρεση αρχείου', + 'Unable to remove this file.' => 'Αδυναμία αφαίρεσης αρχείου.', + 'File removed successfully.' => 'Το αρχείο αφαιρέθηκε επιτυχώς.', + 'Attach a document' => 'Προσθήκη Συνημμένου', + 'Do you really want to remove this file: "%s"?' => 'Αφαίρεση του αρχείου: « %s » ?', + 'Attachments' => 'Συνημμένα', + 'Edit the task' => 'Διόρθωση εργασίας', + 'Edit the description' => 'Διόρθωση περιγραφής', + 'Add a comment' => 'Προσθήκη σχολίου', + 'Edit a comment' => 'Διόρθωση σχολίου', + 'Summary' => 'Περίληψη', + 'Time tracking' => 'Παρακολούθηση χρόνου', + 'Estimate:' => 'Κατ\' εκτίμηση :', + 'Spent:' => 'Ξοδεύτηκε :', + 'Do you really want to remove this sub-task?' => 'Διαγραφή της υπο-εργασίας ?', + 'Remaining:' => 'Απομένει :', + 'hours' => 'ώρες', + 'spent' => 'ξοδεύτηκε', + 'estimated' => 'κατ\' εκτίμηση', + 'Sub-Tasks' => 'Υπο-Εργασίες', + 'Add a sub-task' => 'Προσθήκη υπο-εργασίας', + 'Original estimate' => 'Original estimate', + 'Create another sub-task' => 'Δημιουργία κι άλλης υπο-εργασίας', + 'Time spent' => 'Χρόνος που ξοδεύτηκε', + 'Edit a sub-task' => 'Διόρθωση υπο-εργασίας', + 'Remove a sub-task' => 'Διαγραφή υπο-εργασίας', + 'The time must be a numeric value' => 'Ο χρόνος πρέπει να είναι αριθμός', + 'Todo' => 'Πρέπει να γίνουν', + 'In progress' => 'Σε πρόοδο', + 'Sub-task removed successfully.' => 'Η υπο-εργασία αφαιρέθηκε με επιτυχία.', + 'Unable to remove this sub-task.' => 'Αδυναμία αφαίρεσης υπο-εργασίας.', + 'Sub-task updated successfully.' => 'Η υπο-εργασία ενημερώθηκε με επιτυχία.', + 'Unable to update your sub-task.' => 'Αδύνατο να ενημερωθεί η υπο-εργασία.', + 'Unable to create your sub-task.' => 'Αδύνατο να δημιουργηθεί η υπο-εργασία.', + 'Sub-task added successfully.' => 'Η υπο-εργασία προστέθηκε με επιτυχία.', + 'Maximum size: ' => 'Μέγιστο μέγεθος : ', + 'Unable to upload the file.' => 'Δεν είναι δυνατή η μεταφόρτωση του αρχείου.', + 'Display another project' => 'Εμφάνιση άλλου έργου', + 'Created by %s' => 'Δημιουργήθηκε από %s', + 'Last modified on %B %e, %Y at %k:%M %p' => 'Τελευταία ενημέρωση: %d/%m/%Y à %H:%M', + 'Tasks Export' => 'Εξαγωγή εργασιών', + 'Tasks exportation for "%s"' => 'Εξαγωγή εργασιών για το έργο « %s »', + 'Start Date' => 'Ημερομηνία έναρξης', + 'End Date' => 'ημερομηνία λήξης', + 'Execute' => 'Εκτέλεση', + 'Task Id' => 'Task Id', + 'Creator' => 'Δημιουργός', + 'Modification date' => 'Ημερομηνία τροποποίησης', + 'Completion date' => 'Ημερομηνία ολοκλήρωσης', + 'Clone' => 'Clone', + 'Project cloned successfully.' => 'Το έργο κλωνοποιήθηκε με επιτυχία.', + 'Unable to clone this project.' => 'Αδύνατο να κλωνοποιηθεί το έργο.', + 'Enable email notifications' => 'Ενεργοποίηση ειδοποιήσεων ηλεκτρονικού ταχυδρομείου', + 'Task position:' => 'Θέση έργου :', + 'The task #%d have been opened.' => 'Η εργασία #%d έχει ανοίξει.', + 'The task #%d have been closed.' => 'Η εργασία #%d έχει κλείσει.', + 'Sub-task updated' => 'Η υπο-εργασία ενημερώθηκε', + 'Title:' => 'Τίτλος :', + 'Status:' => 'Κατάσταση :', + 'Assignee:' => 'Εντολοδόχος :', + 'Time tracking:' => 'Παρακολούθηση του χρόνου :', + 'New sub-task' => 'Νέα υπο-εργασία', + 'New attachment added "%s"' => 'Νέα επικόλληση προστέθηκε « %s »', + 'Comment updated' => 'Το σχόλιο ενημερώθηκε', + 'New comment posted by %s' => 'Νέο σχόλιο από τον χρήστη « %s »', + 'New attachment' => 'New attachment', + 'New comment' => 'Νέο σχόλιο', + 'New subtask' => 'Νέα υπο-εργασία', + 'Subtask updated' => 'Υπο-Εργασία ενημερώθηκε', + 'Task updated' => 'Η εργασία ενημερώθηκε', + 'Task closed' => 'Η εργασία έκλεισε', + 'Task opened' => 'Η εργασία άνοιξε', + 'I want to receive notifications only for those projects:' => 'Θέλω να ενημερώνομαι αποκλειστικά για:', + 'view the task on Kanboard' => 'Προβολή της εργασίας στο Kanboard', + 'Public access' => 'Ανοιχτή πρόσβαση', + 'User management' => 'Διαχείριση χρηστών', + 'Active tasks' => 'Ενεργοποιημένες εργασίες', + 'Disable public access' => 'Απενεργοποίηση δημόσιας πρόσβασης', + 'Enable public access' => 'Ενεργοποίηση δημόσιας πρόσβασης', + 'Public access disabled' => 'Δημόσια πρόσβαση απενεργοποιήθηκε', + 'Do you really want to disable this project: "%s"?' => 'Θέλετε πραγματικά να απενεργοποιήσετε το έργο : « %s » ?', + 'Do you really want to enable this project: "%s"?' => 'Θέλετε πραγματικά να ενεργοποιήσετε το έργο : « %s » ?', + 'Project activation' => 'Ενεργοποίηση έργου', + 'Move the task to another project' => 'Μεταφορά της εργασίας σε άλλο έργο', + 'Move to another project' => 'Μεταφορά σε άλλο έργο', + 'Do you really want to duplicate this task?' => 'Θέλετε πραγματικά να επαναλάβετε αυτή την εργασία;?', + 'Duplicate a task' => 'Επανάληψη εργασίας', + 'External accounts' => 'Εξωτερικοί λογαριασμοί', + 'Account type' => 'Τύπος λογαριασμού', + 'Local' => 'Τοπική', + 'Remote' => 'Απομακρυσμένη', + 'Enabled' => 'Ενεργή', + 'Disabled' => 'Απενεργοποιημένη', + 'Username:' => 'Username :', + 'Name:' => 'Όνομα :', + 'Email:' => 'Email :', + 'Notifications:' => 'Ειδοποιήσεις :', + 'Notifications' => 'Ειδοποιήσεις', + 'Account type:' => 'Τύπος λογαριασμού :', + 'Edit profile' => 'Επεξεργασία προφίλ', + 'Change password' => 'Αλλαγή password', + 'Password modification' => 'Τροποποίηση password ', + 'External authentications' => 'Εξωτερικές πιστοποιήσεις', + 'Never connected.' => 'Ποτέ δεν συνδέθηκε.', + 'No account linked.' => 'Δεν υπάρχουν ενεργοί λογαριασμοί', + 'Account linked.' => 'Λογαριασμός συνδέθηκε', + 'No external authentication enabled.' => 'Καμία εξωτερική πιστοποιήση ενεργοποιημένη.', + 'Password modified successfully.' => 'Το password τροποποιήθηκε με επιτυχία.', + 'Unable to change the password.' => 'Αδύνατο να αλλάξει το password.', + 'Change category for the task "%s"' => 'Αλλαγή κατηγορίας της εργασίας « %s »', + 'Change category' => 'Αλλαγή κατηγορίας', + '%s updated the task %s' => '%s ενημέρωσε την εργασία %s', + '%s opened the task %s' => '%s άνοιξε την εργασία %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s άλλαξε την εργασία %s στη θέση n°%d στη στήλη « %s »', + '%s moved the task %s to the column "%s"' => '%s άλλαξε την εργασία %s στη στήλη « %s »', + '%s created the task %s' => '%s δημιούργησε την εργασία %s', + '%s closed the task %s' => '%s έκλεισε την εργασία %s', + '%s created a subtask for the task %s' => '%s δημιούργησε την υπο-εργασία στην εργασία %s', + '%s updated a subtask for the task %s' => '%s ενημέρωση την υπο-εργασία στην εργασία %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Ανατέθηκε στον %s με μια εκτίμηση του %s/%sh', + 'Not assigned, estimate of %sh' => 'Δεν έχει εκχωρηθεί, εκτίμηση %sh', + '%s updated a comment on the task %s' => '%s ενημερώθηκε ένα σχόλιο στην εργασία %s', + '%s commented the task %s' => '%s σχολίασε την εργασία %s', + '%s\'s activity' => 'δραστηριότητα του έργου %s', + 'RSS feed' => 'RSS feed', + '%s updated a comment on the task #%d' => '%s ενημέρωσε ένα σχόλιο στην εργασία n°%d', + '%s commented on the task #%d' => '%s σχολίασε την εργασία n°%d', + '%s updated a subtask for the task #%d' => '%s ενημέρωσε μια υπο-εργασία στην εργασία n °%d', + '%s created a subtask for the task #%d' => '%s δημιούργησε μια υπο-εργασία στην εργασία n°%d', + '%s updated the task #%d' => '%s ενημέρωσε την εργασία n°%d', + '%s created the task #%d' => '%s δημιούργησε την εργασία n°%d', + '%s closed the task #%d' => '%s έκλεισε την εργασία n°%d', + '%s open the task #%d' => '%s άνοιξε την εργασία n°%d', + '%s moved the task #%d to the column "%s"' => '%s μετακίνησε την εργασία n°%d στη στήλη « %s »', + '%s moved the task #%d to the position %d in the column "%s"' => '%s μετακίνησε την εργασία n°%d στη θέση n°%d της στήλης « %s »', + 'Activity' => 'Δραστηριότητα', + 'Default values are "%s"' => 'Οι προεπιλεγμένες τιμές είναι « %s »', + 'Default columns for new projects (Comma-separated)' => 'Προεπιλεγμένες στήλες για νέα έργα (Comma-separated)', + 'Task assignee change' => 'Αλλαγή εκδοχέα εργασίας', + '%s change the assignee of the task #%d to %s' => '%s άλλαξε τον εκδοχέα της εργασίας n˚%d σε %s', + '%s changed the assignee of the task %s to %s' => '%s ενημέρωσε τον εκδοχέα της εργασίας %s σε %s', + 'New password for the user "%s"' => 'Νέο password του χρήστη « %s »', + 'Choose an event' => 'Επιλογή event', + 'Create a task from an external provider' => 'Δημιουργήστε μια εργασία από ένα εξωτερικό πάροχο', + 'Change the assignee based on an external username' => 'Αλλάξτε τον εκδοχέα βάση ενός εξωτερικού username', + 'Change the category based on an external label' => 'Αλλάξτε τον εκδοχέα βάση ενός εξωτερικού label', + 'Reference' => 'Reference', + 'Reference: %s' => 'Reference : %s', + 'Label' => 'Label', + 'Database' => 'Database', + 'About' => 'About', + 'Database driver:' => 'Database driver :', + 'Board settings' => 'Board settings', + 'URL and token' => 'URL / token', + 'Webhook settings' => 'Webhook settings', + 'URL for task creation:' => 'URL για δημιουργία εργασίας: :', + 'Reset token' => 'Reset token', + 'API endpoint:' => 'URL API :', + 'Refresh interval for private board' => 'Ανανέωση interval στο private board', + 'Refresh interval for public board' => 'Ανανέωση interval στο public board', + 'Task highlight period' => 'Περίοδος εργασίας', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Περίοδος (σε δευτερόλεπτα) για να εξετάσει ότι ένα έργο τροποποιήθηκε πρόσφατα (0 για να απενεργοποιήσετε, 2 ημέρες από προεπιλογή)', + 'Frequency in second (60 seconds by default)' => 'Συχνότητα σε δευτερόλεπτα (60 δευτερόλεπτα από προεπιλογή)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Συχνότητα σε δευτερόλεπτα (0 για να απενεργοποιήσετε αυτή τη λειτουργία, 10 δευτερόλεπτα από προεπιλογή)', + 'Application URL' => 'Application URL', + 'Example: http://example.kanboard.net/ (used by email notifications)' => 'Παράδειγμα : http://exemple.kanboard.net/ (utilisé pour les notifications)', + 'Token regenerated.' => 'Token regenerated.', + 'Date format' => 'Μορφή ημερομηνίας', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO format είναι πάντα αποδεκτό, π.χ. : « %s » και « %s »', + 'New private project' => 'Νέο ιδιωτικό έργο', + 'This project is private' => 'Αυτό το έργο είναι ιδιωτικό', + 'Type here to create a new sub-task' => 'Πληκτρολογήστε εδώ για να δημιουργήσετε μια νέα υπο-εργασία', + 'Add' => 'Προσθήκη', + 'Estimated time: %s hours' => 'Εκτιμώμενος χρόνος: %s ώρες', + 'Time spent: %s hours' => 'Χρόνος που δαπανήθηκε : %s ώρες', + 'Started on %B %e, %Y' => 'Άρχισε την %d/%m/%Y', + 'Start date' => 'Ημερομηνία έναρξης', + 'Time estimated' => 'Εκτιμώμενος χρόνος', + 'There is nothing assigned to you.' => 'Δεν σας έχει ανατεθεί τίποτα.', + 'My tasks' => 'Οι εργασίες μου', + 'Activity stream' => 'Ροή δραστηριότητας', + 'Dashboard' => 'Κεντρικό ταμπλό', + 'Confirmation' => 'Επιβεβαίωση', + 'Allow everybody to access to this project' => 'Να επιτρέπετε σε όλους να έχουν πρόσβαση σε αυτό το έργο', + 'Everybody have access to this project.' => 'Όλοι έχουν πρόσβαση σε αυτό το έργο.', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Δημιουργήστε ένα σχόλιο από έναν εξωτερικό πάροχο', + 'Project management' => 'Διαχείριση έργων', + 'My projects' => 'Τα έργα μου', + 'Columns' => 'Στήλες', + 'Task' => 'Εργασία', + 'Your are not member of any project.' => 'Δεν είστε μέλος κάποιου έργου.', + 'Percentage' => 'Ποσοστό', + 'Number of tasks' => 'Αριθμός εργασιών', + 'Task distribution' => 'Κατανομή εργασιών', + 'Reportings' => 'Αναφορές', + 'Task repartition for "%s"' => 'Επανάληψη εργασιών για « %s »', + 'Analytics' => 'Αναλύσεις', + 'Subtask' => 'Υπο-Εργασία', + 'My subtasks' => 'Οι υπο-εργασίες μου', + 'User repartition' => 'Επαναλήψεις χρηστών', + 'User repartition for "%s"' => 'Επαναλήψεις χρηστών για « %s »', + 'Clone this project' => 'Κλωνοποίηση έργου', + 'Column removed successfully.' => 'Η στήλη αφαιρέθηκε με επιτυχία.', + 'Not enough data to show the graph.' => 'Ελλειπή δεδομένα για να εμφανιστεί το γράφημα.', + 'Previous' => 'Προηγούμενο', + 'The id must be an integer' => 'Το id δεν πρέπει να είναι ακέραιος', + 'The project id must be an integer' => 'Το αναγνωριστικό έργου πρέπει να είναι ακέραιος', + 'The status must be an integer' => 'Το status πρέπει να είναι ακέραιος', + 'The subtask id is required' => 'Το id της υπο-εργασίας είναι υποχρεωτικό', + 'The subtask id must be an integer' => 'Το id της υπο-εργασίας πρέπει να είναι ακέραιος', + 'The task id is required' => 'Το id της εργασίας είναι υποχρεωτικό', + 'The task id must be an integer' => 'Το id της εργασίας πρέπει να είναι ακέραιος', + 'The user id must be an integer' => 'Το user id πρέπει να είναι ακέραιος', + 'This value is required' => 'Η τιμή είναι υποχρεωτική', + 'This value must be numeric' => 'Η τιμή πρέπει να είναι αριθμός', + 'Unable to create this task.' => 'Αδύνατο να δημιουργηθεί αυτή η εργασία.', + 'Cumulative flow diagram' => 'Συγκεντρωτικό διάγραμμα ροής', + 'Cumulative flow diagram for "%s"' => 'Συγκεντρωτικό διάγραμμα ροής για « %s »', + 'Daily project summary' => 'Καθημερινή περίληψη του έργου', + 'Daily project summary export' => 'Εξαγωγή της καθημερινής περίληψης του έργου', + 'Daily project summary export for "%s"' => 'Εξαγωγή της καθημερινής περίληψης του έργου « %s »', + 'Exports' => 'Εξαγωγές', + 'This export contains the number of tasks per column grouped per day.' => 'Αυτή η κατάσταση περιέχει τον αριθμό των εργασιών ανά στήλη ομαδοποιημένα ανά ημέρα.', + 'Nothing to preview...' => 'Τίποτα για προεπισκόπηση...', + 'Preview' => 'Προεπισκόπηση', + 'Write' => 'Write', + 'Active swimlanes' => 'Ενεργά swimlanes', + 'Add a new swimlane' => 'Πρόσθεσε ένα νέο λωρίδα', + 'Change default swimlane' => 'Αλλαγή του default λωρίδα', + 'Default swimlane' => 'Default λωρίδα', + 'Do you really want to remove this swimlane: "%s"?' => 'Σίγουρα θέλετε να αφαιρέσετε τη λωρίδα : « %s » ?', + 'Inactive swimlanes' => 'Λωρίδες ανενεργές', + 'Remove a swimlane' => 'Αφαιρέστε μια λωρίδα', + 'Rename' => 'Μετονομασία', + 'Show default swimlane' => 'Εμφάνιση προεπιλεγμένων λωρίδων', + 'Swimlane modification for the project "%s"' => 'Τροποποίηση λωρίδας για το έργο « %s »', + 'Swimlane not found.' => 'Η λωρίδα δεν βρέθηκε.', + 'Swimlane removed successfully.' => 'Η λωρίδα αφαιρέθηκε με επιτυχία.', + 'Swimlanes' => 'Swimlanes', + 'Swimlane updated successfully.' => 'Η λωρίδα ενημερώθηκε με επιτυχία.', + 'The default swimlane have been updated successfully.' => 'Η προεπιλεγμένη λωρίδα ενημερώθηκε με επιτυχία.', + 'Unable to create your swimlane.' => 'Αδύνατο να δημιουργηθεί η λωρίδα.', + 'Unable to remove this swimlane.' => 'Αδύνατο να αφαιρεθεί η λωρίδα.', + 'Unable to update this swimlane.' => 'Αδύνατο να ενημερωθεί η λωρίδα.', + 'Your swimlane have been created successfully.' => 'Η λωρίδα δημιουργήθηκε με επιτυχία.', + 'Example: "Bug, Feature Request, Improvement"' => 'Παράδειγμα: "Bug, Feature Request, Improvement »', + 'Default categories for new projects (Comma-separated)' => 'Προεπιλεγμένες κατηγορίες για νέα έργα (Διαχωρισμένων με κόμμα)', + 'Integrations' => 'Ενσωματώσεις', + 'Integration with third-party services' => 'Ενοποίηση με υπηρεσίες τρίτων', + 'Subtask Id' => 'Id υπο-εργασίας', + 'Subtasks' => 'Υπο-Εργασίες', + 'Subtasks Export' => 'Εξαγωγή υπο-εργασίων', + 'Subtasks exportation for "%s"' => 'Εξαγωγή υπο-εργασίων για το έργο « %s »', + 'Task Title' => 'Τίτλος εργασίας', + 'Untitled' => 'Χωρίς τίτλο', + 'Application default' => 'Προεπιλογή από την εφαρμογή', + 'Language:' => 'Γλώσσα :', + 'Timezone:' => 'Timezone :', + 'All columns' => 'Όλες οι στήλες', + 'Calendar' => 'Ημερολόγιο', + 'Next' => 'Επόμενο', + '#%d' => 'n˚%d', + 'All swimlanes' => 'Όλες οι λωρίδες', + 'All colors' => 'Όλα τα χρώματα', + 'Moved to column %s' => 'Μεταφορά στη στήλη %s', + 'Change description' => 'Επεξεργασία περιγραφής', + 'User dashboard' => 'Κεντρικό ταμπλό χρήστη', + 'Allow only one subtask in progress at the same time for a user' => 'Αφήστε μόνο μία υπο-εργασία σε εξέλιξη ταυτόχρονα για έναν χρήστη', + 'Edit column "%s"' => 'Επεξεργασία στήλης « %s »', + 'Select the new status of the subtask: "%s"' => 'Επιλογή νέας κατάστασης της υπο-εργασίας : « %s »', + 'Subtask timesheet' => 'Πρόγραμμα υπο-εργασίας', + 'There is nothing to show.' => 'Δεν υπάρχει κάτι.', + 'Time Tracking' => 'Παρακολούθηση χρονοδιαγράμματος', + 'You already have one subtask in progress' => 'Έχτε ήδη μια υπο-εργασία σε εξέλιξη', + 'Which parts of the project do you want to duplicate?' => 'Ποιά κομμάτια του έργου θέλετε να αντιγράψετε ?', + 'Disallow login form' => 'Απαγόρευση φόρμας σύνδεσης', + 'Start' => 'Εκκίνηση', + 'End' => 'Τέλος', + 'Task age in days' => 'Χρόνος εργασίας σε μέρες', + 'Days in this column' => 'Μέρες σε αυτή την στήλη', + '%dd' => '%dημ', + 'Add a link' => 'Προσθήκη ενός link', + 'Add a new link' => 'Προσθήκη ενός νέου link', + 'Do you really want to remove this link: "%s"?' => 'Θέλετε σίγουρα να αφαιρέσετε αυτό το link : « %s » ?', + 'Do you really want to remove this link with task #%d?' => 'Θέλετε σίγουρα να αφαιρέσετε αυτό το link του έργου n°%d ?', + 'Field required' => 'Υποχρεωτικό πεδίο', + 'Link added successfully.' => 'Το link προστέθηκε με επιτυχία.', + 'Link updated successfully.' => 'Το link ενημερώθηκε με επιτυχία.', + 'Link removed successfully.' => 'Το link αφαιρέθηκε με επιτυχία.', + 'Link labels' => 'Link labels', + 'Link modification' => 'Τροποποίηση Link ', + 'Links' => 'Links', + 'Link settings' => 'Ρυθμίσεις συνδέσμων', + 'Opposite label' => 'Αντίθετο label', + 'Remove a link' => 'Αφαίρεση ενός link', + 'Task\'s links' => 'Σύνδεσμοι εργασιών', + 'The labels must be different' => 'Τα label πρέπει να είναι διαφορετικά', + 'There is no link.' => 'Δεν υπάρχει σύνδεσμος.', + 'This label must be unique' => 'Το label πρέπει να είναι μοναδικό', + 'Unable to create your link.' => 'Αδύνατο να δημιουργήσετε σύνδεσμο.', + 'Unable to update your link.' => 'Αδύνατο να ενημερώσετε τον σύνδεσμο.', + 'Unable to remove this link.' => 'Αδύνατο να αφαιρέσετε τον σύνδεσμο.', + 'relates to' => 'συνδέεται με', + 'blocks' => 'blocks', + 'is blocked by' => 'μπλοκάρεται από', + 'duplicates' => 'διπλότυπα', + 'is duplicated by' => 'αντιγράφεται από', + 'is a child of' => 'είναι ένα παιδί του', + 'is a parent of' => 'είναι ο πατέρας του', + 'targets milestone' => 'στόχοι οροσήμου', + 'is a milestone of' => 'είναι ένα ορόσημο της', + 'fixes' => 'διορθώσεις', + 'is fixed by' => 'διορθώθηκε από', + 'This task' => 'Αυτή η εργασία', + '<1h' => '<1ωρ', + '%dh' => '%dωρ', + '%b %e' => '%e %b', + 'Expand tasks' => 'Ανάπτυξη εργασιών', + 'Collapse tasks' => 'Σύμπτυξη εργασιών', + 'Expand/collapse tasks' => 'Ανάπτυξη/σύμπτυξη εργασιών', + 'Close dialog box' => 'Κλείστε το παράθυρο διαλόγου', + 'Submit a form' => 'Αποστολή φόρμας', + 'Board view' => 'Προβολή κεντρικού πίνακα', + 'Keyboard shortcuts' => 'Συντομεύσεις πληκτρολογίου', + 'Open board switcher' => 'Άνοιγμα μεταγωγέα κεντρικού πίνακα', + 'Application' => 'Εφαρμογή', + 'since %B %e, %Y at %k:%M %p' => 'από το %d/%m/%Y à %H:%M', + 'Compact view' => 'Συμπηκνωμένη προβολή', + 'Horizontal scrolling' => 'Οριζόντια ολίσθηση', + 'Compact/wide view' => 'Συμπηκνωμένη/Ευρεία Προβολή', + 'No results match:' => 'Δεν ταιριάζει κανένα αποτέλεσμα :', + 'Currency' => 'Νόμισμα', + 'Files' => 'Αρχεία', + 'Images' => 'Εικόνες', + 'Private project' => 'Ιδιωτικό έργο', + 'AUD - Australian Dollar' => 'AUD - Australian Dollar', + 'CAD - Canadian Dollar' => 'CAD - Canadian Dollar', + 'CHF - Swiss Francs' => 'CHF - Swiss Francs', + 'Custom Stylesheet' => 'Custom Stylesheet', + 'download' => 'download', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - British Pound', + 'INR - Indian Rupee' => 'INR - Indian Rupee', + 'JPY - Japanese Yen' => 'JPY - Japanese Yen', + 'NZD - New Zealand Dollar' => 'NZD - New Zealand Dollar', + 'RSD - Serbian dinar' => 'RSD - Serbian dinar', + 'USD - US Dollar' => 'USD - US Dollar', + 'Destination column' => 'Στήλη προορισμού', + 'Move the task to another column when assigned to a user' => 'Μετακινήστε την εργασία σε άλλη στήλη όταν ανατεθεί σε ένα χρήστη', + 'Move the task to another column when assignee is cleared' => 'Μετακινήστε την εργασία σε άλλη στήλη όταν ο εκδοχέας είναι ελεύθερος', + 'Source column' => 'Πηγή στήλης', + 'Transitions' => 'Μεταβάσεις', + 'Executer' => 'Εκτέλεση', + 'Time spent in the column' => 'Χρόνος που αφιερώθηκε στη στήλη', + 'Task transitions' => 'Μεταβίβαση εργασίας', + 'Task transitions export' => 'Εξαγωγή μεταβιβάσεων εργασιών', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Η έκθεση αυτή περιέχει όλες τις κινήσεις της στήλης για κάθε εργασία με την ημερομηνία, το χρήστη και το χρόνο που δαπανάται για κάθε μετάβαση.', + 'Currency rates' => 'Ισοτιμίες', + 'Rate' => 'Τιμή', + 'Change reference currency' => 'Αλλαγή ισοτιμίας', + 'Add a new currency rate' => 'Προσθήκη ισοτιμίας', + 'Reference currency' => 'Αναφορά ισοτιμίας', + 'The currency rate have been added successfully.' => 'Η ισοτιμία προστέθηκε με επιτυχία.', + 'Unable to add this currency rate.' => 'Αδύνατο να προστεθεί αυτή η ισοτιμία.', + 'Webhook URL' => 'Webhook URL', + '%s remove the assignee of the task %s' => '%s αφαίρεσε τον εκδοχέα της εργασίας %s', + 'Enable Gravatar images' => 'Ενεργοποίηση εικόνων Gravatar', + 'Information' => 'Πληροφορίες', + 'Check two factor authentication code' => 'Ελέγξτε δύο παράγοντες ελέγχου ταυτότητας κωδικού', + 'The two factor authentication code is not valid.' => 'Ο κωδικός ελέγχου ταυτότητας δύο παραγόντων δεν είναι σωστός.', + 'The two factor authentication code is valid.' => 'Ο κωδικός ελέγχου ταυτότητας δύο παραγόντων είναι σωστός.', + 'Code' => 'Κωδικός', + 'Two factor authentication' => 'Κωδικός ελέγχου ταυτότητας δύο παραγόντων', + 'This QR code contains the key URI: ' => 'Αυτό το QR code περιέχει το url : ', + 'Check my code' => 'Έλεγχος του κωδικού μου', + 'Secret key: ' => 'Μυστικό κλειδί : ', + 'Test your device' => 'Ελέγξτε τη συσκευή σας', + 'Assign a color when the task is moved to a specific column' => 'Αντιστοίχιση χρώματος όταν η εργασία κινείται σε μια συγκεκριμένη στήλη', + '%s via Kanboard' => '%s via Kanboard', + 'uploaded by: %s' => 'ανέβασμα από %s', + 'uploaded on: %s' => 'ανέβασμα την %s', + 'size: %s' => 'μέγεθος : %s', + 'Burndown chart for "%s"' => 'Δημιουργία διαγράμματος για « %s »', + 'Burndown chart' => 'Δημιουργία διαγράμματος', + 'This chart show the task complexity over the time (Work Remaining).' => 'Αυτό το γράφημα δείχνει την πολυπλοκότητα του έργου κατά την πάροδο του χρόνου (Εργασία που παραμένει).', + 'Screenshot taken %s' => 'Το screenshot αποθηκεύτηκε από %s', + 'Add a screenshot' => 'Προσθήκη ενός screenshot', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Πάρτε ένα screenshot και πατήστε CTRL+V or ⌘+V για να το επικολλήσετε εδώ.', + 'Screenshot uploaded successfully.' => 'Το screenshot ανέβηκε με επιτυχία.', + 'SEK - Swedish Krona' => 'SEK - Swedish Krona', + 'Identifier' => 'Αναγνωριστικό', + 'Disable two factor authentication' => 'Απενεργοποίηση κωδικού ελέγχου ταυτότητας δύο παραγόντων', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Είστε σίγουροι ότι θέλετε να απενεργοποίησετε τον κωδικό ελέγχου ταυτότητας δύο παραγόντων : « %s » ?', + 'Edit link' => 'Επεξεργασία συνδέσμου', + 'Start to type task title...' => 'Ξεκινήστε να πληκτρολογείτε τον τίτλο της εργασίας...', + 'A task cannot be linked to itself' => 'Μια εργασία δεν μπορεί να συνδεθεί με τον εαυτό της', + 'The exact same link already exists' => 'Ο σύνδεσμος υπάρχει ήδη', + 'Recurrent task is scheduled to be generated' => 'Επαναλαμβανόμενο έργο έχει προγραμματιστεί να δημιουργηθεί', + 'Recurring information' => 'Επαναλαμβανόμενη πληροφορία', + 'Score' => 'Score', + 'The identifier must be unique' => 'Το αναγνωριστικό πρέπει να είναι μοναδικό', + 'This linked task id doesn\'t exists' => 'Αυτό το συνδεδεμένο id της εργασίας δεν υπάρχει', + 'This value must be alphanumeric' => 'Η τιμή πρέπει να είναι αλφαριθμητική', + 'Edit recurrence' => 'Επεξεργασία επανάληψης', + 'Generate recurrent task' => 'Δημιουργία επαναλαμβανόμενου έργου', + 'Trigger to generate recurrent task' => 'Έναυσμα για τη δημιουργία επαναλαμβανόμενης εργασίας', + 'Factor to calculate new due date' => 'Συντελεστής για τον υπολογισμό νέας ημερομηνίας καθηκόντων', + 'Timeframe to calculate new due date' => 'Χρονικό πλαίσιο για τον υπολογισμό νέας ημερομηνίας καθηκόντων', + 'Base date to calculate new due date' => 'Ημερομηνία βάσης για τον υπολογισμό νέας ημερομηνίας καθηκόντων', + 'Action date' => 'Ημέρα ενέργειας', + 'Base date to calculate new due date: ' => 'Ημερομηνία βάσης για τον υπολογισμό νέας ημερομηνίας καθηκόντων : ', + 'This task has created this child task: ' => 'Το έργο αυτό έχει δημιουργήθει από το έργο παιδί του: ', + 'Day(s)' => 'Μέρα(ες)', + 'Existing due date' => 'Υπάρχουσα ημερομηνία καθηκόντων', + 'Factor to calculate new due date: ' => 'Συντελεστής για τον υπολογισμό νέας ημερομηνίας καθηκόντων: ', + 'Month(s)' => 'Μήνες', + 'Recurrence' => 'Επανάληψη', + 'This task has been created by: ' => 'Αυτό το έργο δημιουργήθηκε από τον χρήστη :', + 'Recurrent task has been generated:' => 'Το επαναλαμβανόμενο έργο έχει παραχθεί :', + 'Timeframe to calculate new due date: ' => 'Χρονοδιάγραμμα υπολογισμού νέας ημερομηνίας καθηκόντων : ', + 'Trigger to generate recurrent task: ' => 'Έναυσμα για τη δημιουργία επαναλαμβανόμενη εργασίας : ', + 'When task is closed' => 'Όταν το έργο έχει τελειώσει', + 'When task is moved from first column' => 'Όταν το έργο έχει μετακινηθεί στην 1η στήλη', + 'When task is moved to last column' => 'Όταν το έργο έχει μετακινηθεί στην τελευταία στήλη', + 'Year(s)' => 'Χρόνος(οι)', + 'Calendar settings' => 'Ρυθμίσεις ημερολογίου', + 'Project calendar view' => 'Προβολή ημερολογίου έργων', + 'Project settings' => 'Ρυθμίσεις έργου', + 'Show subtasks based on the time tracking' => 'Εμφάνιση υπο-εργασίων με βάση την παρακολούθηση του χρόνου', + 'Show tasks based on the creation date' => 'Εμφάνιση έργων με βάση την ημερομηνία δημιουργίας', + 'Show tasks based on the start date' => 'Εμφάνιση έργων με βάση την ημερομηνία δημιουργίας ', + 'Subtasks time tracking' => 'Παρακολούθηση χρόνου υπο-εργασίων', + 'User calendar view' => 'Προβολή του ημερολογίου του χρήστη', + 'Automatically update the start date' => 'Αυτόματη ενημέρωση της ημερομηνίας έναρξης', + 'iCal feed' => 'iCal feed', + 'Preferences' => 'Προτιμήσεις', + 'Security' => 'Ασφάλεια', + 'Two factor authentication disabled' => 'Η πιστοποίηση δύο παραγόντων απενεργοποιήθηκε', + 'Two factor authentication enabled' => 'Η πιστοποίηση δύο παραγόντων ενεργοποιήθηκε', + 'Unable to update this user.' => 'Αδύνατο να ενημερωθεί αυτός ο χρήστης.', + 'There is no user management for private projects.' => 'Δεν υπάρχει διαχείριση χρηστών για ιδιωτικά έργα.', + 'User that will receive the email' => 'Ο χρήστης που θα λάβει το μήνυμα ηλεκτρονικού ταχυδρομείου', + 'Email subject' => 'Θέμα ηλεκτρονικού ταχυδρομείου', + 'Date' => 'Ημέρα', + 'Add a comment log when moving the task between columns' => 'Προσθέστε ένα σχόλιο καταγραφής κατά τη μετακίνηση του έργου μεταξύ των στηλών', + 'Move the task to another column when the category is changed' => 'Μετακινήστε την εργασία σε άλλη στήλη, όταν η κατηγορία έχει αλλάξει', + 'Send a task by email to someone' => 'Στείλτε μια εργασία μέσω ηλεκτρονικού ταχυδρομείου σε κάποιον', + 'Reopen a task' => 'Ξανα-ανοίξτε μια εργασία', + 'Column change' => 'Αλλαγή στήλης', + 'Position change' => 'Αλλαγή θέσης', + 'Swimlane change' => 'Αλλαγή λωρίδας', + 'Assignee change' => 'Αλλαγή εκδοχέα', + '[%s] Overdue tasks' => '[%s] Εκπρόθεσμες εργασίες', + 'Notification' => 'Κοινοποίηση', + '%s moved the task #%d to the first swimlane' => '%s μετέφερε την εργασία n°%d στην 1η λωρίδα', + '%s moved the task #%d to the swimlane "%s"' => '%s μετέφερε την εργασία n°%d στη λωρίδα « %s »', + 'Swimlane' => 'Λωρίδα', + 'Gravatar' => 'Gravatar', + '%s moved the task %s to the first swimlane' => '%s μετέφερε την εργασία %s στην 1η λωρίδα', + '%s moved the task %s to the swimlane "%s"' => '%s μετέφερε την εργασία %s στη λωρίδα « %s »', + 'This report contains all subtasks information for the given date range.' => 'Η έκθεση αυτή περιέχει όλες τις υπο-εργασίες για το συγκεκριμένο εύρος ημερομηνιών.', + 'This report contains all tasks information for the given date range.' => 'Η έκθεση αυτή περιέχει όλες τις πληροφορίες για το συγκεκριμένο εύρος ημερομηνιών.', + 'Project activities for %s' => 'Ενέργειες για το έργο « %s »', + 'view the board on Kanboard' => 'δείτε τον πίνακα στο Kanboard', + 'The task have been moved to the first swimlane' => 'Η εργασία αυτή έχει μετακινηθεί στην πρώτη λωρίδα', + 'The task have been moved to another swimlane:' => 'Η εργασία αυτή έχει μετακινηθεί σε άλλη λωρίδα :', + 'Overdue tasks for the project "%s"' => 'Εκπρόθεσμες εργασίες για το έργο « %s »', + 'New title: %s' => 'Νέος τίτλος : %s', + 'The task is not assigned anymore' => 'Η εργασία δεν έχει ανατεθεί πλέον', + 'New assignee: %s' => 'Καινούργια ανάθεση : %s', + 'There is no category now' => 'Δεν υπάρχει κατηγορία τώρα', + 'New category: %s' => 'Νέα κατηγορία : %s', + 'New color: %s' => 'Νέο χρώμα : %s', + 'New complexity: %d' => 'Νέα πολυπλοκότητα : %d', + 'The due date have been removed' => 'Η ημερομηνία καθηκόντων έχει αφαιρεθεί', + 'There is no description anymore' => 'Δεν υπάρχει περιγραφή πλέον', + 'Recurrence settings have been modified' => 'Οι ρυθμίσεις επανάληψης έχουν τροποποιηθεί', + 'Time spent changed: %sh' => 'Ο χρόνος που πέρασε έχει αλλάξει : %sh', + 'Time estimated changed: %sh' => 'Ο εκτιμώμενος χρόνος άλλαξε : %sh', + 'The field "%s" have been updated' => 'Το πεδίο « %s » έχει ενημερωθεί', + 'The description have been modified' => 'Η περιγραφή έχει ενημερωθεί', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Σίγουρα θέλετε να κλείσετε την εργασία « %s » και την υπο-εργασία ?', + 'Swimlane: %s' => 'Λωρίδα : %s', + 'I want to receive notifications for:' => 'Επιθυμώ να λαμβάνω ενημερώσεις για :', + 'All tasks' => 'Όλες οι εργασίες', + 'Only for tasks assigned to me' => 'Μόνο σε εργασίες που μου έχουν ανατεθεί', + 'Only for tasks created by me' => 'Μόνο σε εργασίες που έχουν δημιουργηθεί από εμένα', + 'Only for tasks created by me and assigned to me' => 'Μόνο σε εργασίες που έχουν δημιουργηθεί από εμένα και μου έχουν ανατεθεί', + '%A' => '%A', + '%b %e, %Y, %k:%M %p' => '%d/%m/%Y %H:%M', + 'New due date: %B %e, %Y' => 'Νέα ημέρα καθηκόντων : %d/%m/%Y', + 'Start date changed: %B %e, %Y' => 'Ημέρα εκκίνησης άλλαξε : %d/%m/%Y', + '%k:%M %p' => '%H:%M', + '%%Y-%%m-%%d' => '%%d/%%m/%%Y', + 'Total for all columns' => 'Σύνολο για όλες τις στήλες', + 'You need at least 2 days of data to show the chart.' => 'Έχετε τουλάχιστον 2 ημέρες δεδομένων για να εμφανιστούν στο διάγραμμα.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Διακοπή ρολογιού', + 'Start timer' => 'Έναρξη ρολογιού', + 'Add project member' => 'Προσθήκη νέου μέλους έργου', + 'Enable notifications' => 'Ενεργοποίηση ειδοποιήσεων', + 'My activity stream' => 'Η ροή δραστηριοτήτων μου', + 'My calendar' => 'Το ημερολόγιο μου', + 'Search tasks' => 'Αναζήτηση εργασιών', + 'Back to the calendar' => 'Πίσω στο ημερολόγιο', + 'Filters' => 'Φίλτρα', + 'Reset filters' => 'Επαναφορά φίλτρων', + 'My tasks due tomorrow' => 'Οι εργασίες καθηκόντων μου αύριο', + 'Tasks due today' => 'Οι εργασίες καθηκόντων μου αύριο', + 'Tasks due tomorrow' => 'Εργασίες καθηκόντων αύριο', + 'Tasks due yesterday' => 'Εργασίες καθηκόντων χτές', + 'Closed tasks' => 'Κλειστές εργασίες', + 'Open tasks' => 'Ανοιχτές εργασίες', + 'Not assigned' => 'Δεν έχουν εκχωρηθεί', + 'View advanced search syntax' => 'Δείτε τη σύνταξη "αναζήτησης για προχωρημένους"', + 'Overview' => 'Επισκόπηση', + '%b %e %Y' => '%b %e %Y', + 'Board/Calendar/List view' => 'Πίνακας / Ημερολόγιο / Προβολή λίστας', + 'Switch to the board view' => 'Εναλλαγή στην προβολή του πίνακα', + 'Switch to the calendar view' => 'Εναλλαγή στην προβολή ημερολογίου', + 'Switch to the list view' => 'Εναλλαγή στην προβολή λίστας', + 'Go to the search/filter box' => 'Μετάβαση στο πλαίσιο αναζήτησης / φίλτρο', + 'There is no activity yet.' => 'Δεν υπάρχει καμία δραστηριότητα ακόμα.', + 'No tasks found.' => 'Δεν βρέθηκαν εργασίες.', + 'Keyboard shortcut: "%s"' => 'Συντόμευση πληκτρολογίου : « %s »', + 'List' => 'Λίστα', + 'Filter' => 'Φίλτρο', + 'Advanced search' => 'Προχωρημένη Αναζήτηση', + 'Example of query: ' => 'Παράδειγμα ερωτήματος : ', + 'Search by project: ' => 'Αναζήτηση με βάση το έργο : ', + 'Search by column: ' => 'Αναζήτηση με βάση την στήλη : ', + 'Search by assignee: ' => 'Αναζήτηση με βάση τον δικαιοδόχο : ', + 'Search by color: ' => 'Αναζήτηση βάση χρώματος : ', + 'Search by category: ' => 'Αναζήτηση βάση κατηγορίας : ', + 'Search by description: ' => 'Αναζήτηση βάση περιγραφής : ', + 'Search by due date: ' => 'Αναζήτηση βάση ημέρας λήξης : ', + 'Lead and Cycle time for "%s"' => 'Lead & cycle time για « %s »', + 'Average time spent into each column for "%s"' => 'Μέσος χρόνος παραμονής σε κάθε στήλη για « %s »', + 'Average time spent into each column' => 'Μέσος χρόνος παραμονής σε κάθε στήλη', + 'Average time spent' => 'Μέσος χρόνος που δαπανήθηκε', + 'This chart show the average time spent into each column for the last %d tasks.' => 'Αυτό το γράφημα δείχνει ότι ο μέσος χρόνος που δαπανάται σε κάθε στήλη για τις τελευταίες %d εργασίες', + 'Average Lead and Cycle time' => 'Average Lead & Cycle time', + 'Average lead time: ' => 'Average lead time : ', + 'Average cycle time: ' => 'Average cycle time : ', + 'Cycle Time' => 'Cycle time', + 'Lead Time' => 'Lead time', + 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Αυτό το γράφημα δείχνει το average lead and cycle time για τις τελευταίες %d εργασίες κατά τη διάρκεια του χρόνου.', + 'Average time into each column' => 'Μέσος χρόνος σε κάθε στήλη', + 'Lead and cycle time' => 'Lead et cycle time', + 'Lead time: ' => 'Lead time : ', + 'Cycle time: ' => 'Cycle time : ', + 'Time spent into each column' => 'Ο χρόνος που δαπανήθηκε σε κάθε στήλη', + 'The lead time is the duration between the task creation and the completion.' => 'Το <lead time> είναι η διάρκεια μεταξύ της δημιουργίας του έργου και της ολοκλήρωσης του.', + 'The cycle time is the duration between the start date and the completion.' => 'Το <cycle time> είναι η διάρκεια μεταξύ της ημερομηνίας εκκίνησης και της ολοκλήρωσης του.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Εάν η εργασία δεν έχει κλείσει η τρέχουσα ώρα χρησιμοποιείται αντί της ημερομηνίας ολοκλήρωσης.', + 'Set automatically the start date' => 'Ρυθμίστε αυτόματα την ημερομηνία έναρξης', + 'Edit Authentication' => 'Επεξεργασία ταυτοποίησης', + 'Remote user' => 'Απομακρυσμένος χρήστης', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Στους απομακρυσμένους χρήστες δεν αποθηκεύονται οι κωδικοί πρόσβασης εντός της βάσης δεδομένων της τρέχουσας εφαρμογής, Παραδείγματα: LDAP, Google and Github accounts.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Αν ενεργοποιήσετε την επιλογή "Απαγόρευση φόρμας σύνδεσης", τα στοιχεία που εισάγονται στη φόρμα σύνδεσης αγνοούνται.', + 'New remote user' => 'Νέος απομακρυσμένος χρήστης', + 'New local user' => 'Νέος τοπικός χρήστης', + 'Default task color' => 'Προεπιλογή χρώματος εργασίας', + 'Hide sidebar' => 'Απόκρυψη sidebar', + 'Expand sidebar' => 'Διαστολή sidebar', + 'This feature does not work with all browsers.' => 'Αυτή η δυνατότητα δεν δουλεύει σε όλους τους browsers', + 'There is no destination project available.' => 'Δεν υπάρχει διαθέσιμο κανένα έργο προορισμού.', + 'Trigger automatically subtask time tracking' => 'Αυτόματη ενεργοποίηση της παρακολούθησης χρόνου σε υπο-εργασίες', + 'Include closed tasks in the cumulative flow diagram' => 'Να συμπεριλαμβάνονται οι κλειστές εργασίες στο συσσωρευτικό διάγραμμα ροής', + 'Current swimlane: %s' => 'Τρέχουσα λωρίδα : %s', + 'Current column: %s' => 'Τρέχουσα στήλη : %s', + 'Current category: %s' => 'Τρέχουσα κατηγορία : %s', + 'no category' => 'Καμμία κατηγορία', + 'Current assignee: %s' => 'Τρέχον εκδοχέας : %s', + 'not assigned' => 'δεν έχει εκχωρηθεί', + 'Author:' => 'Συγγραφέας :', + 'contributors' => 'συνεισφέροντες', + 'License:' => 'Άδεια :', + 'License' => 'Άδεια', + 'Enter the text below' => 'Πληκτρολογήστε το παρακάτω κείμενο', + 'Gantt chart for %s' => 'Gantt διάγραμμα για %s', + 'Sort by position' => 'Ταξινόμηση κατά Θέση', + 'Sort by date' => 'Ταξινόμηση κατά ημέρα', + 'Add task' => 'Προσθήκη εργασίας', + 'Start date:' => 'Ημέρα εκκίνησης :', + 'Due date:' => 'Ημέρα καθηκόντων :', + 'There is no start date or due date for this task.' => 'Δεν υπάρχει ημερομηνία έναρξης ή ημερομηνία λήξης καθηκόντων για το έργο αυτό.', + 'Moving or resizing a task will change the start and due date of the task.' => 'Μετακίνηση ή αλλαγή μεγέθους μιας εργασίας θα αλλάξει την ώρα έναρξης και ημερομηνία λήξης της εργασίας.', + 'There is no task in your project.' => 'Δεν υπάρχει καμία εργασία στο έργο σας.', + 'Gantt chart' => 'Διαγράμματα Gantt', + 'People who are project managers' => 'Οι άνθρωποι που είναι οι διευθυντές έργων', + 'People who are project members' => 'Οι άνθρωποι που είναι μέλη των έργων', + 'NOK - Norwegian Krone' => 'NOK - Norwegian Krone', + 'Show this column' => 'Εμφάνιση αυτής της στήλης', + 'Hide this column' => 'Απόκρυψη αυτής τη στήλη', + 'open file' => 'Άνοιγμα αρχείου', + 'End date' => 'Ημερομηνία λήξης', + 'Users overview' => 'Επισκόπηση χρηστών', + 'Managers' => 'Διευθυντές', + 'Members' => 'Μέλη', + 'Shared project' => 'Κοινόχρηστο έργο', + 'Project managers' => 'Διευθυντές έργου', + 'Gantt chart for all projects' => 'Διάγραμμα Gantt για όλα τα έργα', + 'Projects list' => 'Λίστα έργων', + 'Gantt chart for this project' => 'Διάγραμμα Gantt για το έργο', + 'Project board' => 'Κεντρικός πίνακας έργου', + 'End date:' => 'Ημερομηνία λήξης :', + 'There is no start date or end date for this project.' => 'Δεν υπάρχει ημερομηνία έναρξης ή λήξης για το έργο αυτό.', + 'Projects Gantt chart' => 'Διάγραμμα Gantt έργων', + 'Start date: %s' => 'Ημερομηνία αρχής : %s', + 'End date: %s' => 'Ημερομηνία τέλους : %s', + 'Link type' => 'Τύπος συνδέσμου', + 'Change task color when using a specific task link' => 'Αλλαγή χρώματος εργασίας χρησιμοποιώντας συγκεκριμένο σύνδεσμο εργασίας', + 'Task link creation or modification' => 'Σύνδεσμος δημιουργίας ή τροποποίησης εργασίας', + 'Milestone' => 'Ορόσημο', + 'Documentation: %s' => 'Τεκμηρίωση : %s', + 'Switch to the Gantt chart view' => 'Μεταφορά σε προβολή διαγράμματος Gantt', + 'Reset the search/filter box' => 'Αρχικοποίηση του πεδίου αναζήτησης/φιλτραρίσματος', + 'Documentation' => 'Τεκμηρίωση', + 'Table of contents' => 'Πίνακας περιεχομένων', + 'Gantt' => 'Gantt', + 'Author' => 'Δημιουργός', + 'Version' => 'Έκδοση', + 'Plugins' => 'Πρόσθετα', + 'There is no plugin loaded.' => 'Δεν έχει φορτωθεί plugin', + 'Set maximum column height' => 'Ορισμός μέγιστου ύψους στήλης', + 'Remove maximum column height' => 'Αφαίρεση μέγιστου ύψους στήλης', + 'My notifications' => 'Οι ειδοποιήσεις μου', + 'Custom filters' => 'Φίλτρα ορισμένα από τον χρήστη', + 'Your custom filter have been created successfully.' => 'Το παρεμετροποιημένο από τον χρήστη φίλτρο δημιουργήθηκε με επιτυχία', + 'Unable to create your custom filter.' => 'Δεν είναι δυνατή η δημιουργία του φίλτρου ορισμένου από τον χρήστη.', + 'Custom filter removed successfully.' => 'Το παρεμετροποιημένο από τον χρήστη φίλτρο αφαιρέθηκε με επιτυχία.', + 'Unable to remove this custom filter.' => 'Δεν είναι δυνατή η αφαίρεση του παρεμετροποιημένου από τον χρήστη φίλτρου.', + 'Edit custom filter' => 'Διόρθωση παρεμετροποιημένου από τον χρήστη φίλτρου', + 'Your custom filter have been updated successfully.' => 'Το παρεμετροποιημένο από τον χρήστη φίλτρο, διορθώθηκε με επιτυχία.', + 'Unable to update custom filter.' => 'Δεν είναι δυνατή η ενημέρωση του παρεμετροποιημένου από τον χρήστη φίλτρου.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Νέο συνημμένο για την εργασία n°%d : %s', + 'New comment on task #%d' => 'Νέο σχόλιο για την εργασία n°%d', + 'Comment updated on task #%d' => 'Ενημέρωση σχολίου για την εργασία n°%d', + 'New subtask on task #%d' => 'Νέα υπο-εργασία για την εργασία n°%d', + 'Subtask updated on task #%d' => 'Ενημέρωση υπό-εργασίας για την εργασία n°%d', + 'New task #%d: %s' => 'Νέα εργασία n°%d : %s', + 'Task updated #%d' => 'Η εργασία n°%d ενημερώθηκε με επιτυχία', + 'Task #%d closed' => 'Η εργασία n°%d έκλεισε', + 'Task #%d opened' => 'Η εργασία n°%d άνοιξε', + 'Column changed for task #%d' => 'Η στήλη άλλαξε για την εργασία n°%d', + 'New position for task #%d' => 'Νέα θέση για την εργασία n°%d', + 'Swimlane changed for task #%d' => 'Η λωρίδα άλλαξε για την εργασία n°%d', + 'Assignee changed on task #%d' => 'Η ανάθεση άλλαξε για την εργασία n°%d', + '%d overdue tasks' => '%d εκπρόθεσμες εργασίες', + 'Task #%d is overdue' => 'Η εργασία n°%d είναι εκπρόθεσμη', + 'No new notifications.' => 'Χωρίς νέες ειδοποιήσεις.', + 'Mark all as read' => 'Μαρκάρισμα όλων ως διαβασμένα', + 'Mark as read' => 'Μαρκάρισμα ως διαβασμένο', + 'Total number of tasks in this column across all swimlanes' => 'Συνολικός αριθμός εργασιών σε αυτήν τη στήλη σε όλες τις λωρίδες', + 'Collapse swimlane' => 'Συρίκνωση λωρίδας', + 'Expand swimlane' => 'Ανάπτυξη λωρίδας', + 'Add a new filter' => 'Προσθήκη νέου φίλτρου', + 'Share with all project members' => 'Διαμοίραση με όλους τους χρήστες του έργου', + 'Shared' => 'Διαμοιρασμένα', + 'Owner' => 'Ιδιοκτήτης', + 'Unread notifications' => 'Αδιάβαστες ειδοποιήσεις', + 'My filters' => 'Τα φίλτρα μου', + 'Notification methods:' => 'Μέθοδοι ειδοποίησης :', + 'Import tasks from CSV file' => 'Εισαγωγή εργασιών μέσω αρχείου CSV', + 'Unable to read your file' => 'Δεν είναι δυνατή η ανάγνωση του αρχείου', + '%d task(s) have been imported successfully.' => '%d η(οι) εργασία(ες) εισήχθησαν με επιτυχία.', + 'Nothing have been imported!' => 'Τίποτα δεν εισήχθη', + 'Import users from CSV file' => 'Εισαγωγή χρηστών μέσω αρχείου CSV', + '%d user(s) have been imported successfully.' => '%d ο(οι) χρήστης(ες) εισήχθησαν με επιτυχία.', + 'Comma' => 'Κόμμα', + 'Semi-colon' => 'Ερωτηματικό', + 'Tab' => 'Tab', + 'Vertical bar' => 'Κατακόρυφη μπάρα', + 'Double Quote' => 'Διπλά εισαγωγικά', + 'Single Quote' => 'Μονά εισαγωγικά', + '%s attached a file to the task #%d' => '%s συνημμένο αρχείο στην εργασία n°%d', + 'There is no column or swimlane activated in your project!' => 'Δεν υπάρχει στήλη ή λωρίδα ενεργοποιημένη στο έργο !', + 'Append filter (instead of replacement)' => 'Προσθήκη φίλτρου (αντί αντικατάσταση)', + 'Append/Replace' => 'Προσθήκη/Αντικατάσταση', + 'Append' => 'Προσθήκη', + 'Replace' => 'Αντικατάσταση', + 'Import' => 'Εισαγωγή', + 'change sorting' => 'Αλλαγή ταξινόμησης', + 'Tasks Importation' => 'Εισαγωγή εργασιών', + 'Delimiter' => 'Delimiter', + 'Enclosure' => 'Enclosure', + 'CSV File' => 'Αρχείο CSV', + 'Instructions' => 'Οδηγίες', + 'Your file must use the predefined CSV format' => 'Το αρχείο σας πρέπει να χρησιμοποιεί προκαθορισμένη μορφοποίηση CSV', + 'Your file must be encoded in UTF-8' => 'Το αρχείο σας πρέπει να έχει κωδικοποίηση χαρακτήρων UTF-8', + 'The first row must be the header' => 'Η πρώτη γραμμή πρέπει να είναι η κεφαλίδα', + 'Duplicates are not verified for you' => 'Οι Διπλοεγγραφές δεν ελέγχονται', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Η ημερομηνία λήξης πρέπει να χρησιμοποιεί τη μορφοποίηση ISO : EEEE-MM-HH ή στα αγγλικά YYYY-MM-DD', + 'Download CSV template' => 'Κατέβασμα πρότυπου αρχείου CSV', + 'No external integration registered.' => 'No external integration registered.', + 'Duplicates are not imported' => 'Διπλοεγγραφές δεν εισήχθησαν', + 'Usernames must be lowercase and unique' => 'Οι ονομασίες χρηστών πρέπει να είναι σε μικρά γράμματα (lowercase) και μοναδικά', + 'Passwords will be encrypted if present' => 'Οι κωδικοί πρόσβασης κρυπτογραφούνται, αν υπάρχουν', + '%s attached a new file to the task %s' => '%s νέο συνημμένο αρχείο της εργασίας %s', + 'Assign automatically a category based on a link' => 'Ανατίθεται αυτόματα κατηγορία, βασισμένη στον σύνδεσμο', + 'BAM - Konvertible Mark' => 'BAM - Konvertible Mark', + 'Assignee Username' => 'Δικαιοδόχο όνομα χρήστη', + 'Assignee Name' => 'Δικαιοδόχο όνομα', + 'Groups' => 'Ομάδες', + 'Members of %s' => 'Μέλη του %s', + 'New group' => 'Νέα ομάδα', + 'Group created successfully.' => 'Η ομάδα δημιουργήθηκε με επιτυχία.', + 'Unable to create your group.' => 'Δεν είναι δυνατή η δημιουργία της ομάδας.', + 'Edit group' => 'Διόρθωση ομάδας', + 'Group updated successfully.' => 'Η ομάδα ενημερώθηκε με επιτυχία.', + 'Unable to update your group.' => 'Δεν είναι δυνατή η ενημέρωση της ομάδας.', + 'Add group member to "%s"' => 'Προσθήκη του μέλους της ομάδας στην « %s »', + 'Group member added successfully.' => 'Το μέλος της ομ΄δας προστέθηκε με επιτυχία.', + 'Unable to add group member.' => 'Δεν είναι δυνατή η προσθήκη μέλους ομάδας.', + 'Remove user from group "%s"' => 'Αφαίρεση μέλους από την ομάδα « %s »', + 'User removed successfully from this group.' => 'Ο χρήστης αφαιρέθηκε με επιτυχία από την ομάδα.', + 'Unable to remove this user from the group.' => 'Δεν είναι δυνατή η αφαίρεση του χρήστη από την ομάδα.', + 'Remove group' => 'Αφαίρεση ομάδας', + 'Group removed successfully.' => 'Η ομάδα αφαιρέθηκε με επιτυχία.', + 'Unable to remove this group.' => 'Δεν είναι δυνατή η αφαίρεση της ομάδας.', + 'Project Permissions' => 'Επιτρέψεις έργου', + 'Manager' => 'Διευθυντής', + 'Project Manager' => 'Διευθυντής έργου', + 'Project Member' => 'Μέλος σε έργο', + 'Project Viewer' => 'Μέλος μόνο για προβολή έργου', + 'Your account is locked for %d minutes' => 'Ο λογαριασμός σας κλειδώθηκε για %d λεπτά', + 'Invalid captcha' => 'Μη αποδεκτό Captcha', + 'The name must be unique' => 'Το όνομα πρέπει να είναι μοναδικό', + 'View all groups' => 'Προβολή όλων των ομάδων', + 'View group members' => 'Προβολή των μελών της ομάδας', + 'There is no user available.' => 'Δεν υπάρχει διαθέσιμος χρήστης', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Αφαίρεση του χρήστη « %s » από την ομάδα « %s » ?', + 'There is no group.' => 'Δεν υπάρχει ομάδα.', + 'External Id' => 'Εξωτερικό αναγνωριστικό', + 'Add group member' => 'Προσθήκη μέλους ομάδας', + 'Do you really want to remove this group: "%s"?' => 'Αφαίρεση της ομάδας : « %s » ?', + 'There is no user in this group.' => 'Δεν υπάρχει χρήστης σε αυτήν την ομάδα', + 'Remove this user' => 'Αφαίρεση χρήστη', + 'Permissions' => 'Επιτρέψεις', + 'Allowed Users' => 'Χρήστες που επιτρέπονται', + 'No user have been allowed specifically.' => 'Δεν υπάρχει χρήστης που να επιτρέπεται συγκεκριμένα.', + 'Role' => 'Ρόλος', + 'Enter user name...' => 'Εισαγωγή ονόματος χρήστη...', + 'Allowed Groups' => 'Ομάδες που επιτρέπονται', + 'No group have been allowed specifically.' => 'Δεν υπάρχει ομάδα που να επιτρέπεται συγκεκριμένα.', + 'Group' => 'Ομάδα', + 'Group Name' => 'Ονομασία ομάδας', + 'Enter group name...' => 'Εισαγωγή ονομασίας ομάδας...', + 'Role:' => 'Ρόλος :', + 'Project members' => 'Μέλη έργου', + 'Compare hours for "%s"' => 'Σύγκριση ωρών για « %s »', + '%s mentioned you in the task #%d' => '%s αναφέρονται σε εσάς, στη εργασία n°%d', + '%s mentioned you in a comment on the task #%d' => '%s αναφέρονται σε εσάς σε σχόλιο, στη εργασίας n°%d', + 'You were mentioned in the task #%d' => 'Αναφέρεστε στην εργασία n°%d', + 'You were mentioned in a comment on the task #%d' => 'Αναφέρεστε σε σχόλιο, στην εργασία n°%d', + 'Mentioned' => 'Αναφέρεται', + 'Compare Estimated Time vs Actual Time' => 'Σύγκριση προβλεπόμενου χρόνου vs πραγματικού χρόνου', + 'Estimated hours: ' => 'Προβλεπόμενες ώρες : ', + 'Actual hours: ' => 'Πραγματικές ώρες : ', + 'Hours Spent' => 'Δαπανόμενες ώρες', + 'Hours Estimated' => 'Προβλεπόμενες ώρες', + 'Estimated Time' => 'Προβλεπόμενος χρόνος', + 'Actual Time' => 'Πραγματικός χρόνος', + 'Estimated vs actual time' => 'Προβλεπόμενος vs πραγματικός χρόνος', + 'RUB - Russian Ruble' => 'RUB - Russian Ruble', + 'Assign the task to the person who does the action when the column is changed' => 'Ανάθεση της εργασίας στο άτομο κάνει την ενέργεια όταν η στήλη αλλάζει', + 'Close a task in a specific column' => 'Κλείσιμο εργασίας σε συγκεκριμένη στήλη', + 'Time-based One-time Password Algorithm' => 'Time-based One-time Password Algorithm', + 'Two-Factor Provider: ' => 'Two-Factor Provider : ', + 'Disable two-factor authentication' => 'Disable two-factor authentication', + 'Enable two-factor authentication' => 'Enable two-factor authentication', + 'There is no integration registered at the moment.' => 'There is no integration registered at the moment.', + 'Password Reset for Kanboard' => 'Αρχικοποίηση κωδικών πρόσβασης για την εφαρμογή Kanboard', + 'Forgot password?' => 'Ξεχάσατε τον κωδικό πρόσβασης ?', + 'Enable "Forget Password"' => 'Ενεργοποίηση « Ξέχασα τον κωδικό πρόσβασης »', + 'Password Reset' => 'Αρχικοποίηση κωδικού πρόσβασης', + 'New password' => 'Νέος κωδικός πρόσβασης', + 'Change Password' => 'Αλλαγή κωδικού πρόσβασης', + 'To reset your password click on this link:' => 'Για να αρχικοποιηθεί ο κωδικός πρόσβασης σας πατήστε σε αυτόν τον σύνδεσμο :', + 'Last Password Reset' => 'Αρχικοποίηση τελευταίου κωδικού πρόσβασης', + 'The password has never been reinitialized.' => 'Ο κωδικός πρόσβασης δεν μπορεί να αρχικοποιηθεί για δεύτερη φορά.', + 'Creation' => 'Δημιουργία', + 'Expiration' => 'Λήξη', + 'Password reset history' => 'Ιστορικό αρχικοποίησης κωδικών πρόσβασης', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Όλες οι εργασίας της στήλης « %s » και η λωρίδα « %s » έκλεισαν με επιτυχία.', + 'Do you really want to close all tasks of this column?' => 'Να κλείσουν όλες οι εργασίες αυτής της στήλης ?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d εργασία(ες) στη στήλη « %s » και στη λωρίδα « %s » θα κλείσουν.', + 'Close all tasks of this column' => 'Κλείσιμο όλων των εργασιών αυτής της στήλης', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Κανένα plugin δεν έχει καταχωρηθεί με τη μέθοδο της κοινοποίησης του έργου. Μπορείτε ακόμα να διαμορφώσετε τις μεμονωμένες κοινοποιήσεις στο προφίλ χρήστη σας.', + 'My dashboard' => 'Το κεντρικό ταμπλό μου', + 'My profile' => 'Το προφίλ μου', + // 'Project owner: ' => '', + // 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '', + // 'Project owner' => '', + // 'Those dates are useful for the project Gantt chart.' => '', + // 'Private projects do not have users and groups management.' => '', + // 'There is no project member.' => '', + // 'Priority' => '', + // 'Task priority' => '', + // 'General' => '', + // 'Dates' => '', + // 'Default priority' => '', + // 'Lowest priority' => '', + // 'Highest priority' => '', + // 'If you put zero to the low and high priority, this feature will be disabled.' => '', + // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', +); diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php index 16c96ec5..9f922391 100644 --- a/app/Locale/es_ES/translations.php +++ b/app/Locale/es_ES/translations.php @@ -260,9 +260,6 @@ return array( 'External authentication failed' => 'Falló la autenticación externa', 'Your external account is linked to your profile successfully.' => 'Su cuenta externa se ha vinculado exitosamente con su perfil.', 'Email' => 'Correo', - 'Link my Google Account' => 'Vincular con mi Cuenta en Google', - 'Unlink my Google Account' => 'Desvincular de mi Cuenta en Google', - 'Login with my Google Account' => 'Ingresar con mi Cuenta de Google', 'Project not found.' => 'Proyecto no hallado.', 'Task removed successfully.' => 'Tarea suprimida correctamente.', 'Unable to remove this task.' => 'No pude suprimir esta tarea.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Tamaño máximo', 'Unable to upload the file.' => 'No pude cargar el fichero.', 'Display another project' => 'Mostrar otro proyecto', - 'Login with my Github Account' => 'Ingresar con mi cuenta de Github', - 'Link my Github Account' => 'Vincular mi cuenta de Github', - 'Unlink my Github Account' => 'Desvincular mi cuenta de Github', 'Created by %s' => 'Creado por %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Última modificación %e de %B de %Y a las %k:%M %p', 'Tasks Export' => 'Exportar tareas', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Cambiar contraseña', 'Password modification' => 'Modificacion de contraseña', 'External authentications' => 'Autenticación externa', - 'Google Account' => 'Cuenta de Google', - 'Github Account' => 'Cuenta de Github', 'Never connected.' => 'Nunca se ha conectado.', 'No account linked.' => 'Sin vínculo con cuenta.', 'Account linked.' => 'Vinculada con Cuenta.', @@ -846,10 +838,6 @@ return array( 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Esta gráfica muestra el plazo medio de entrega y de ciclo para las %d últimas tareas transcurridas.', 'Average time into each column' => 'Tiempo medio en cada columna', 'Lead and cycle time' => 'Plazo de entrega y de ciclo', - 'Google Authentication' => 'Autenticación de Google', - 'Help on Google authentication' => 'Ayuda con la aAutenticación de Google', - 'Github Authentication' => 'Autenticación de Github', - 'Help on Github authentication' => 'Ayuda con la autenticación de Github', 'Lead time: ' => 'Plazo de entrega: ', 'Cycle time: ' => 'Tiempo de Ciclo: ', 'Time spent into each column' => 'Tiempo empleado en cada columna', @@ -858,8 +846,6 @@ return array( 'If the task is not closed the current time is used instead of the completion date.' => 'Si la tarea no se cierra, se usa la fecha actual en lugar de la de terminación.', 'Set automatically the start date' => 'Poner la fecha de inicio de forma automática', 'Edit Authentication' => 'Editar autenticación', - 'Google Id' => 'Id de Google', - 'Github Id' => 'Id de Github', 'Remote user' => 'Usuario remoto', 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Los usuarios remotos no almacenan sus contraseñas en la base de datos Kanboard, por ejemplo: cuentas de LDAP, Google y Github', 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Si marcas la caja de edición "Desactivar formulario de ingreso", se ignoran las credenciales entradas en el formulario de ingreso.', @@ -917,14 +903,7 @@ return array( 'Link type' => 'Tipo de enlace', 'Change task color when using a specific task link' => 'Cambiar colo de la tarea al usar un enlace específico a tarea', 'Task link creation or modification' => 'Creación o modificación de enlace a tarea', - 'Login with my Gitlab Account' => 'Ingresar usando mi Cuenta en Gitlab', 'Milestone' => 'Hito', - 'Gitlab Authentication' => 'Autenticación Gitlab', - 'Help on Gitlab authentication' => 'Ayuda con autenticación Gitlab', - 'Gitlab Id' => 'Id de Gitlab', - 'Gitlab Account' => 'Cuenta de Gitlab', - 'Link my Gitlab Account' => 'Enlazar con mi Cuenta en Gitlab', - 'Unlink my Gitlab Account' => 'Desenlazar con mi Cuenta en Gitlab', 'Documentation: %s' => 'Documentación: %s', 'Switch to the Gantt chart view' => 'Conmutar a vista de diagrama de Gantt', 'Reset the search/filter box' => 'Limpiar la caja del filtro de búsqueda', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php index cde825e2..84da392f 100644 --- a/app/Locale/fi_FI/translations.php +++ b/app/Locale/fi_FI/translations.php @@ -260,9 +260,6 @@ return array( // 'External authentication failed' => '', // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'Sähköposti', - 'Link my Google Account' => 'Linkitä Google-tili', - 'Unlink my Google Account' => 'Poista Google-tilin linkitys', - 'Login with my Google Account' => 'Kirjaudu Google tunnuksella', 'Project not found.' => 'Projektia ei löytynyt.', 'Task removed successfully.' => 'Tehtävä poistettiin onnistuneesti.', 'Unable to remove this task.' => 'Tehtävän poistaminen epäonnistui.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Maksimikoko: ', 'Unable to upload the file.' => 'Tiedoston lataus epäonnistui.', 'Display another project' => 'Näytä toinen projekti', - 'Login with my Github Account' => 'Kirjaudu sisään Github-tililläni', - 'Link my Github Account' => 'Liitä Github-tilini', - 'Unlink my Github Account' => 'Poista liitos Github-tiliini', 'Created by %s' => 'Luonut: %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Viimeksi muokattu %B %e, %Y kello %H:%M', 'Tasks Export' => 'Tehtävien vienti', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Vaihda salasana', 'Password modification' => 'Salasanan vaihto', 'External authentications' => 'Muut tunnistautumistavat', - 'Google Account' => 'Google-tili', - 'Github Account' => 'Github-tili', 'Never connected.' => 'Ei koskaan liitetty.', 'No account linked.' => 'Tiliä ei ole liitetty.', 'Account linked.' => 'Tili on liitetty.', @@ -846,10 +838,6 @@ return array( // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', // 'Average time into each column' => '', // 'Lead and cycle time' => '', - // 'Google Authentication' => '', - // 'Help on Google authentication' => '', - // 'Github Authentication' => '', - // 'Help on Github authentication' => '', // 'Lead time: ' => '', // 'Cycle time: ' => '', // 'Time spent into each column' => '', @@ -858,8 +846,6 @@ return array( // 'If the task is not closed the current time is used instead of the completion date.' => '', // 'Set automatically the start date' => '', // 'Edit Authentication' => '', - // 'Google Id' => '', - // 'Github Id' => '', // 'Remote user' => '', // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', @@ -917,14 +903,7 @@ return array( // 'Link type' => '', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', - // 'Login with my Gitlab Account' => '', // 'Milestone' => '', - // 'Gitlab Authentication' => '', - // 'Help on Gitlab authentication' => '', - // 'Gitlab Id' => '', - // 'Gitlab Account' => '', - // 'Link my Gitlab Account' => '', - // 'Unlink my Gitlab Account' => '', // 'Documentation: %s' => '', // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php index 32110e1c..ea5e901a 100644 --- a/app/Locale/fr_FR/translations.php +++ b/app/Locale/fr_FR/translations.php @@ -260,9 +260,6 @@ return array( 'External authentication failed' => 'L’authentification externe a échoué', 'Your external account is linked to your profile successfully.' => 'Votre compte externe est désormais lié à votre profil.', 'Email' => 'Email', - 'Link my Google Account' => 'Lier mon compte Google', - 'Unlink my Google Account' => 'Ne plus utiliser mon compte Google', - 'Login with my Google Account' => 'Se connecter avec mon compte Google', 'Project not found.' => 'Projet introuvable.', 'Task removed successfully.' => 'Tâche supprimée avec succès.', 'Unable to remove this task.' => 'Impossible de supprimer cette tâche.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Taille maximum : ', 'Unable to upload the file.' => 'Impossible de transférer le fichier.', 'Display another project' => 'Afficher un autre projet', - 'Login with my Github Account' => 'Se connecter avec mon compte Github', - 'Link my Github Account' => 'Lier mon compte Github', - 'Unlink my Github Account' => 'Ne plus utiliser mon compte Github', 'Created by %s' => 'Créé par %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Modifié le %d/%m/%Y à %H:%M', 'Tasks Export' => 'Exportation des tâches', @@ -397,8 +391,6 @@ return array( 'Change password' => 'Changer le mot de passe', 'Password modification' => 'Changement de mot de passe', 'External authentications' => 'Authentifications externes', - 'Google Account' => 'Compte Google', - 'Github Account' => 'Compte Github', 'Never connected.' => 'Jamais connecté.', 'No account linked.' => 'Aucun compte attaché.', 'Account linked.' => 'Compte attaché.', @@ -848,10 +840,6 @@ return array( 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Ce graphique montre la durée moyenne du lead et cycle time pour les %d dernières tâches.', 'Average time into each column' => 'Temps moyen dans chaque colonne', 'Lead and cycle time' => 'Lead et cycle time', - 'Google Authentication' => 'Authentification Google', - 'Help on Google authentication' => 'Aide sur l\'authentification Google', - 'Github Authentication' => 'Authentification Github', - 'Help on Github authentication' => 'Aide sur l\'authentification Github', 'Lead time: ' => 'Lead time : ', 'Cycle time: ' => 'Temps de cycle : ', 'Time spent into each column' => 'Temps passé dans chaque colonne', @@ -860,8 +848,6 @@ return array( 'If the task is not closed the current time is used instead of the completion date.' => 'Si la tâche n\'est pas fermée, l\'heure courante est utilisée à la place de la date de complétion.', 'Set automatically the start date' => 'Définir automatiquement la date de début', 'Edit Authentication' => 'Modifier l\'authentification', - 'Google Id' => 'Identifiant Google', - 'Github Id' => 'Identifiant Github', 'Remote user' => 'Utilisateur distant', 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Les utilisateurs distants ne stockent pas leur mot de passe dans la base de données de Kanboard, exemples : comptes LDAP, Github ou Google.', 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Si vous cochez la case « Interdire le formulaire d\'authentification », les identifiants entrés dans le formulaire d\'authentification seront ignorés.', @@ -919,14 +905,7 @@ return array( 'Link type' => 'Type de lien', 'Change task color when using a specific task link' => 'Changer la couleur de la tâche lorsqu\'un lien spécifique est utilisé', 'Task link creation or modification' => 'Création ou modification d\'un lien sur une tâche', - 'Login with my Gitlab Account' => 'Se connecter avec mon compte Gitlab', 'Milestone' => 'Étape importante', - 'Gitlab Authentication' => 'Authentification Gitlab', - 'Help on Gitlab authentication' => 'Aide sur l\'authentification Gitlab', - 'Gitlab Id' => 'Identifiant Gitlab', - 'Gitlab Account' => 'Compte Gitlab', - 'Link my Gitlab Account' => 'Lier mon compte Gitlab', - 'Unlink my Gitlab Account' => 'Ne plus utiliser mon compte Gitlab', 'Documentation: %s' => 'Documentation : %s', 'Switch to the Gantt chart view' => 'Passer à la vue en diagramme de Gantt', 'Reset the search/filter box' => 'Réinitialiser le champ de recherche', @@ -1121,4 +1100,37 @@ return array( 'Highest priority' => 'Priorité haute', 'If you put zero to the low and high priority, this feature will be disabled.' => 'Si vous mettez zéro pour la priorité basse et haute, cette fonctionnalité sera désactivée.', 'Priority: %d' => 'Priorité : %d', + 'Close a task when there is no activity' => 'Fermer une tâche sans activité', + 'Duration in days' => 'Durée en jours', + 'Send email when there is no activity on a task' => 'Envoyer un email lorsqu\'il n\'y a pas d\'activité sur une tâche', + 'List of external links' => 'Liste des liens externes', + 'Unable to fetch link information.' => 'Impossible de récupérer les informations sur le lien.', + 'Daily background job for tasks' => 'Tâche planifée quotidienne pour les tâches', + 'Auto' => 'Auto', + 'Related' => 'Relié', + 'Attachment' => 'Pièce-jointe', + 'Title not found' => 'Titre non trouvé', + 'Web Link' => 'Lien web', + 'External links' => 'Liens externes', + 'Add external link' => 'Ajouter un lien externe', + 'Type' => 'Type', + 'Dependency' => 'Dépendance', + 'View internal links' => 'Voir les liens internes', + 'View external links' => 'Voir les liens externes', + 'Add internal link' => 'Ajouter un lien interne', + 'Add a new external link' => 'Ajouter un nouveau lien externe', + 'Edit external link' => 'Modifier un lien externe', + 'External link' => 'Lien externe', + 'Copy and paste your link here...' => 'Copier-coller vôtre lien ici...', + 'URL' => 'URL', + 'There is no external link for the moment.' => 'Il n\'y a pas de lien externe pour le moment.', + 'Internal links' => 'Liens internes', + 'There is no internal link for the moment.' => 'Il n\'y a pas de lien interne pour le moment.', + 'Assign to me' => 'Assigner à moi', + 'Me' => 'Moi', + 'Do not duplicate anything' => 'Ne rien dupliquer', + 'Projects management' => 'Gestion des projets', + 'Users management' => 'Gestion des utilisateurs', + 'Groups management' => 'Gestion des groupes', + 'Create from another project' => 'Créer depuis un autre projet', ); diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php index 25d55bb2..e3df67b4 100644 --- a/app/Locale/hu_HU/translations.php +++ b/app/Locale/hu_HU/translations.php @@ -260,9 +260,6 @@ return array( // 'External authentication failed' => '', // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'E-mail', - 'Link my Google Account' => 'Kapcsold össze a Google fiókkal', - 'Unlink my Google Account' => 'Válaszd le a Google fiókomat', - 'Login with my Google Account' => 'Jelentkezzen be Google fiókkal', 'Project not found.' => 'A projekt nem található.', 'Task removed successfully.' => 'Feladat sikeresen törölve.', 'Unable to remove this task.' => 'A feladatot nem lehet törölni.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Maximális méret: ', 'Unable to upload the file.' => 'Fájl feltöltése nem lehetséges.', 'Display another project' => 'Másik projekt megjelenítése', - 'Login with my Github Account' => 'Jelentkezzen be Github fiókkal', - 'Link my Github Account' => 'Github fiók csatolása', - 'Unlink my Github Account' => 'Github fiók leválasztása', 'Created by %s' => 'Készítette: %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Utolsó módosítás: %Y. %m. %d. %H:%M', 'Tasks Export' => 'Feladatok exportálása', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Jelszó módosítása', 'Password modification' => 'Jelszó módosítása', 'External authentications' => 'Külső azonosítás', - 'Google Account' => 'Google fiók', - 'Github Account' => 'Github fiók', 'Never connected.' => 'Sosem csatlakozva.', 'No account linked.' => 'Nincs csatlakoztatott fiók.', 'Account linked.' => 'Fiók csatlakoztatva.', @@ -846,10 +838,6 @@ return array( // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', // 'Average time into each column' => '', // 'Lead and cycle time' => '', - // 'Google Authentication' => '', - // 'Help on Google authentication' => '', - // 'Github Authentication' => '', - // 'Help on Github authentication' => '', // 'Lead time: ' => '', // 'Cycle time: ' => '', // 'Time spent into each column' => '', @@ -858,8 +846,6 @@ return array( // 'If the task is not closed the current time is used instead of the completion date.' => '', // 'Set automatically the start date' => '', // 'Edit Authentication' => '', - // 'Google Id' => '', - // 'Github Id' => '', // 'Remote user' => '', // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', @@ -917,14 +903,7 @@ return array( // 'Link type' => '', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', - // 'Login with my Gitlab Account' => '', // 'Milestone' => '', - // 'Gitlab Authentication' => '', - // 'Help on Gitlab authentication' => '', - // 'Gitlab Id' => '', - // 'Gitlab Account' => '', - // 'Link my Gitlab Account' => '', - // 'Unlink my Gitlab Account' => '', // 'Documentation: %s' => '', // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/id_ID/translations.php b/app/Locale/id_ID/translations.php index e3316405..5eeb1492 100644 --- a/app/Locale/id_ID/translations.php +++ b/app/Locale/id_ID/translations.php @@ -260,9 +260,6 @@ return array( 'External authentication failed' => 'Otentifikasi eksternal gagal', 'Your external account is linked to your profile successfully.' => 'Akun eksternal anda berhasil dihubungkan ke profil anda.', 'Email' => 'Email', - 'Link my Google Account' => 'Hubungkan akun Google saya', - 'Unlink my Google Account' => 'Putuskan akun Google saya', - 'Login with my Google Account' => 'Masuk menggunakan akun Google saya', 'Project not found.' => 'Proyek tidak ditemukan.', 'Task removed successfully.' => 'Tugas berhasil dihapus.', 'Unable to remove this task.' => 'Tidak dapat menghapus tugas ini.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Ukuran maksimum: ', 'Unable to upload the file.' => 'Tidak dapat mengunggah berkas.', 'Display another project' => 'Lihat proyek lain', - 'Login with my Github Account' => 'Masuk menggunakan akun Github saya', - 'Link my Github Account' => 'Hubungkan akun Github saya ', - 'Unlink my Github Account' => 'Putuskan akun Github saya', 'Created by %s' => 'Dibuat oleh %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Modifikasi terakhir pada tanggal %d/%m/%Y à %H:%M', 'Tasks Export' => 'Ekspor Tugas', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Rubah kata sandri', 'Password modification' => 'Modifikasi kata sandi', 'External authentications' => 'Otentifikasi eksternal', - 'Google Account' => 'Akun Google', - 'Github Account' => 'Akun Github', 'Never connected.' => 'Tidak pernah terhubung.', 'No account linked.' => 'Tidak ada akun terhubung.', 'Account linked.' => 'Akun terhubung.', @@ -846,10 +838,6 @@ return array( 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Grafik ini menunjukkan memimpin rata-rata dan waktu siklus untuk %d tugas terakhir dari waktu ke waktu.', 'Average time into each column' => 'Rata-rata waktu ke setiap kolom', 'Lead and cycle time' => 'Lead dan siklus waktu', - 'Google Authentication' => 'Google Otentifikasi', - 'Help on Google authentication' => 'Bantuan pada otentifikasi Google', - 'Github Authentication' => 'Otentifikasi Github', - 'Help on Github authentication' => 'Bantuan pada otentifikasi Github', 'Lead time: ' => 'Lead time : ', 'Cycle time: ' => 'Siklus waktu : ', 'Time spent into each column' => 'Waktu yang dihabiskan di setiap kolom', @@ -858,8 +846,6 @@ return array( 'If the task is not closed the current time is used instead of the completion date.' => 'Jika tugas tidak ditutup waktu saat ini yang digunakan sebagai pengganti tanggal penyelesaian.', 'Set automatically the start date' => 'Secara otomatis mengatur tanggal mulai', 'Edit Authentication' => 'Modifikasi Otentifikasi', - 'Google Id' => 'Id Google', - 'Github Id' => 'Id Github', 'Remote user' => 'Pengguna jauh', 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Pengguna jauh tidak menyimpan kata sandi mereka dalam basis data Kanboard, contoh: akun LDAP, Google dan Github.', 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Jika anda mencentang kotak "Larang formulir login", kredensial masuk ke formulis login akan diabaikan.', @@ -917,14 +903,7 @@ return array( 'Link type' => 'Tipe tautan', 'Change task color when using a specific task link' => 'Rubah warna tugas ketika menggunakan tautan tugas yang spesifik', 'Task link creation or modification' => 'Tautan pembuatan atau modifikasi tugas ', - 'Login with my Gitlab Account' => 'Masuk menggunakan akun Gitlab saya', 'Milestone' => 'Milestone', - 'Gitlab Authentication' => 'Authentification Gitlab', - 'Help on Gitlab authentication' => 'Bantuan pada otentifikasi Gitlab', - 'Gitlab Id' => 'Id Gitlab', - 'Gitlab Account' => 'Akun Gitlab', - 'Link my Gitlab Account' => 'Hubungkan akun Gitlab saya', - 'Unlink my Gitlab Account' => 'Putuskan akun Gitlab saya', 'Documentation: %s' => 'Dokumentasi : %s', 'Switch to the Gantt chart view' => 'Beralih ke tampilan grafik Gantt', 'Reset the search/filter box' => 'Atur ulang pencarian/kotak filter', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php index 1e32213f..9e96e470 100644 --- a/app/Locale/it_IT/translations.php +++ b/app/Locale/it_IT/translations.php @@ -4,14 +4,14 @@ return array( 'number.decimals_separator' => ',', 'number.thousands_separator' => '.', 'None' => 'Nessuno', - 'edit' => 'modificare', - 'Edit' => 'Modificare', - 'remove' => 'cancellare', - 'Remove' => 'Cancellare', - 'Update' => 'Aggiornare', + 'edit' => 'modifica', + 'Edit' => 'Modifica', + 'remove' => 'cancella', + 'Remove' => 'Cancella', + 'Update' => 'Aggiorna', 'Yes' => 'Si', 'No' => 'No', - 'cancel' => 'annullare', + 'cancel' => 'annulla', 'or' => 'o', 'Yellow' => 'Giallo', 'Blue' => 'Blu', @@ -20,68 +20,68 @@ return array( 'Red' => 'Rosso', 'Orange' => 'Arancione', 'Grey' => 'Grigio', - // 'Brown' => '', - // 'Deep Orange' => '', - // 'Dark Grey' => '', - // 'Pink' => '', - // 'Teal' => '', - // 'Cyan' => '', + 'Brown' => 'Marrone', + 'Deep Orange' => 'Arancio scuro', + 'Dark Grey' => 'Grigio scuro', + 'Pink' => 'Rosa', + 'Teal' => 'Verde foglia di tè', + 'Cyan' => 'Ciano', // 'Lime' => '', - // 'Light Green' => '', - // 'Amber' => '', - 'Save' => 'Salvare', - 'Login' => 'Entra', + 'Light Green' => 'Verde chiaro', + 'Amber' => 'Ambra', + 'Save' => 'Salva', + 'Login' => 'Accedi', 'Official website:' => 'Sito web ufficiale:', 'Unassigned' => 'Non assegnato', - 'View this task' => 'Vedere questo compito', - 'Remove user' => 'Cancellare un utente', - 'Do you really want to remove this user: "%s"?' => 'Veramente vuoi cancellare questo utente: « %s » ?', - 'New user' => 'Aggiungere un utente', + 'View this task' => 'Visualizza questo task', + 'Remove user' => 'Cancella un utente', + 'Do you really want to remove this user: "%s"?' => 'Veramente vuoi cancellare questo utente: "%s" ?', + 'New user' => 'Aggiungi un utente', 'All users' => 'Tutti gli utenti', 'Username' => 'Nome utente', 'Password' => 'Password', 'Administrator' => 'Amministratore', - 'Sign in' => 'Iscriversi', + 'Sign in' => 'Accedi', 'Users' => 'Utenti', 'No user' => 'Nessun utente', 'Forbidden' => 'Vietato', 'Access Forbidden' => 'Accesso vietato', - 'Edit user' => 'Modificare un utente', - 'Logout' => 'Uscire', + 'Edit user' => 'Modifica un utente', + 'Logout' => 'Esci', 'Bad username or password' => 'Utente o password errati', - 'Edit project' => 'Modificare progetto', + 'Edit project' => 'Modifica progetto', 'Name' => 'Nome', 'Projects' => 'Progetti', 'No project' => 'Nessun progetto', 'Project' => 'Progetto', 'Status' => 'Stato', - 'Tasks' => 'Compiti', + 'Tasks' => 'Tasks', 'Board' => 'Bacheca', 'Actions' => 'Azioni', 'Inactive' => 'Inattivo', 'Active' => 'Attivo', - 'Add this column' => 'Aggiungere questa colonna', - '%d tasks on the board' => '%d compiti sulla bacheca', - '%d tasks in total' => '%d compiti in totale', - 'Unable to update this board.' => 'Non si può aggiornare questa bacheca.', - 'Edit board' => 'Modificare questa bacheca', - 'Disable' => 'Disattivare', - 'Enable' => 'Attivare', + 'Add this column' => 'Aggiungi questa colonna', + '%d tasks on the board' => '%d task sulla bacheca', + '%d tasks in total' => '%d task in totale', + 'Unable to update this board.' => 'Impossibile aggiornare questa bacheca.', + 'Edit board' => 'Modifica questa bacheca', + 'Disable' => 'Disattiva', + 'Enable' => 'Attiva', 'New project' => 'Nuovo progetto', - 'Do you really want to remove this project: "%s"?' => 'Veramente vuoi eliminare questo progetto: « %s » ?', - 'Remove project' => 'Cancellare il progetto', - 'Edit the board for "%s"' => 'Modificare la bacheca per « %s »', + 'Do you really want to remove this project: "%s"?' => 'Veramente vuoi eliminare il seguente progetto: "%s" ?', + 'Remove project' => 'Cancella il progetto', + 'Edit the board for "%s"' => 'Modifica la bacheca per "%s"', 'All projects' => 'Tutti i progetti', - 'Change columns' => 'Cambiare le colonne', - 'Add a new column' => 'Aggiungere una nuova colonna', + 'Change columns' => 'Cambia le colonne', + 'Add a new column' => 'Aggiungi una nuova colonna', 'Title' => 'Titolo', 'Nobody assigned' => 'Nessuno assegnato', 'Assigned to %s' => 'Assegnato a %s', - 'Remove a column' => 'Cancellare questa colonna', - 'Remove a column from a board' => 'Cancellare una colonna da una bacheca', - 'Unable to remove this column.' => 'Non si può cancellare questa colonna.', - 'Do you really want to remove this column: "%s"?' => 'Veramente desideri cancellare questa colonna : « %s » ?', - 'This action will REMOVE ALL TASKS associated to this column!' => 'Questa azione cancellerà TUTTI I COMPITI legati a questa colonna!', + 'Remove a column' => 'Cancella questa colonna', + 'Remove a column from a board' => 'Cancella una colonna da una bacheca', + 'Unable to remove this column.' => 'Impossibile cancellare questa colonna.', + 'Do you really want to remove this column: "%s"?' => 'Veramente desideri cancellare questa colonna: "%s" ?', + 'This action will REMOVE ALL TASKS associated to this column!' => 'Questa azione cancellerà TUTTI I TASK legati a questa colonna!', 'Settings' => 'Impostazioni', 'Application settings' => 'Impostazioni dell\'applicazione', 'Language' => 'Lingua', @@ -92,25 +92,25 @@ return array( 'Optimize the database' => 'Ottimizare la base dati', '(VACUUM command)' => '(Comando VACUUM)', '(Gzip compressed Sqlite file)' => '(File Sqlite compresso in Gzip)', - 'Close a task' => 'Chiudere un compito', - 'Edit a task' => 'Modificare un compito', - 'Column' => 'colonna', + 'Close a task' => 'Chiudi un task', + 'Edit a task' => 'Modifica un task', + 'Column' => 'Colonna', 'Color' => 'Colore', - 'Assignee' => 'Persona assegnata', - 'Create another task' => 'Creare un nuovo compito', - 'New task' => 'Nuovo compito', - 'Open a task' => 'Aprire un compito', - 'Do you really want to open this task: "%s"?' => 'Veramente desideri aprire questo compito: « %s » ?', - 'Back to the board' => 'Tornare alla bacheca', + 'Assignee' => 'Assegnatario', + 'Create another task' => 'Crea un nuovo task', + 'New task' => 'Nuovo task', + 'Open a task' => 'Apri un task', + 'Do you really want to open this task: "%s"?' => 'Veramente desideri aprire questo task: "%s" ?', + 'Back to the board' => 'Torna alla bacheca', 'Created on %B %e, %Y at %k:%M %p' => 'Creato il %B %e, %Y alle %k:%M %p', - 'There is nobody assigned' => 'Non c\'è nessuno assegnato a questo compito', + 'There is nobody assigned' => 'Nessuno è assegnato a questo task', 'Column on the board:' => 'Colonna sulla bacheca: ', 'Status is open' => 'Stato aperto', - 'Status is closed' => 'stato chiuso', - 'Close this task' => 'Chiudere questo compito', - 'Open this task' => 'Aprire questo compito', - 'There is no description.' => 'Non c\'è descrizione.', - 'Add a new task' => 'Aggiungere un nuovo compito', + 'Status is closed' => 'Stato chiuso', + 'Close this task' => 'Chiudi questo task', + 'Open this task' => 'Apri questo task', + 'There is no description.' => 'Nessuna descrizione presente.', + 'Add a new task' => 'Aggiungere un nuovo task', 'The username is required' => 'Si richiede un nome di utente', 'The maximum length is %d characters' => 'La lunghezza massima è di %d caratteri', 'The minimum length is %d characters' => 'La lunghezza minima è di %d caratteri', @@ -126,32 +126,32 @@ return array( 'The project name is required' => 'Si richiede il nome del progetto', 'The title is required' => 'Si richiede un titolo', 'Settings saved successfully.' => 'Impostazioni salvate correttamente.', - 'Unable to save your settings.' => 'Non si possono salvare le impostazioni.', + 'Unable to save your settings.' => 'Impossibile salvare le impostazioni.', 'Database optimization done.' => 'Ottimizzazione della base dati conclusa.', 'Your project have been created successfully.' => 'Il tuo progetto è stato creato correttamente.', - 'Unable to create your project.' => 'Non si può creare il progetto.', + 'Unable to create your project.' => 'Impossibile creare il progetto.', 'Project updated successfully.' => 'Progetto aggiornato correttamente.', - 'Unable to update this project.' => 'Non si può aggiornare il progetto.', - 'Unable to remove this project.' => 'Non si può cancellare questo progetto.', + 'Unable to update this project.' => 'Impossibile aggiornare il progetto.', + 'Unable to remove this project.' => 'Impossibile cancellare questo progetto.', 'Project removed successfully.' => 'Progetto cancellato correttamente.', 'Project activated successfully.' => 'Progetto attivato correttamente.', - 'Unable to activate this project.' => 'Non si può attivare il progetto.', + 'Unable to activate this project.' => 'Impossibile attivare il progetto.', 'Project disabled successfully.' => 'Progetto disattivato correttamente.', - 'Unable to disable this project.' => 'Non si può disattivare il progetto.', - 'Unable to open this task.' => 'Non si può aprire questo compito.', - 'Task opened successfully.' => 'Il compito è stato aperto correttamente.', - 'Unable to close this task.' => 'Non si può chiudere questo compito.', - 'Task closed successfully.' => 'Compito chiuso correttamente.', - 'Unable to update your task.' => 'Non si può modificare questo compito.', - 'Task updated successfully.' => 'Compito modificato correttamente.', - 'Unable to create your task.' => 'Non si può creare questo compito.', - 'Task created successfully.' => 'Compito creato correttamente.', + 'Unable to disable this project.' => 'Impossibile disattivare il progetto.', + 'Unable to open this task.' => 'Impossibile aprire questo task.', + 'Task opened successfully.' => 'Il task è stato aperto correttamente.', + 'Unable to close this task.' => 'Impossibile chiudere questo task.', + 'Task closed successfully.' => 'Task chiuso correttamente.', + 'Unable to update your task.' => 'Impossibile modificare questo task.', + 'Task updated successfully.' => 'Task modificato correttamente.', + 'Unable to create your task.' => 'Impossibile creare questo task.', + 'Task created successfully.' => 'Task creato correttamente.', 'User created successfully.' => 'Utente creato correttamente.', - 'Unable to create your user.' => 'Non si può creare l\'utente.', + 'Unable to create your user.' => 'Impossibile creare l\'utente.', 'User updated successfully.' => 'Utente aggiornato correttamente.', - 'Unable to update your user.' => 'Non si può aggiornare questo utente.', + 'Unable to update your user.' => 'Impossibile aggiornare questo utente.', 'User removed successfully.' => 'Utente cancellato correttamente.', - 'Unable to remove this user.' => 'Non si può cancellare questo utente.', + 'Unable to remove this user.' => 'Impossibile cancellare questo utente.', 'Board updated successfully.' => 'Bacheca aggiornata correttamente.', 'Ready' => 'Pronto', 'Backlog' => 'In attesa', @@ -162,39 +162,39 @@ return array( // '%B %e, %Y at %k:%M %p' => '', 'Date created' => 'Data di creazione', 'Date completed' => 'Data di termine', - 'Id' => 'Identificatore', - '%d closed tasks' => '%d compiti chiusi', - 'No task for this project' => 'Nessun compito per questo progetto', + // 'Id' => '', + '%d closed tasks' => '%d task chiusi', + 'No task for this project' => 'Nessun task per questo progetto', 'Public link' => 'Link pubblico', - 'Change assignee' => 'Cambiare la persona assegnata', - 'Change assignee for the task "%s"' => 'Cambiare la persona assegnata per il compito « %s »', + 'Change assignee' => 'Cambia l\'assegnatario', + 'Change assignee for the task "%s"' => 'Cambia l\'assegnatario per il task "%s"', 'Timezone' => 'Fuso orario', - 'Sorry, I didn\'t find this information in my database!' => 'Mi dispiace, non ho trovato questa informazione sulla base dati!', + 'Sorry, I didn\'t find this information in my database!' => 'Spiacente, non ho trovato questa informazione sul database!', 'Page not found' => 'Pagina non trovata', 'Complexity' => 'Complessità', - 'Task limit' => 'Numero massimo di compiti', - 'Task count' => 'Numero di compiti', + 'Task limit' => 'Limite di task', + 'Task count' => 'Numero di task', 'User' => 'Utente', 'Comments' => 'Commenti', 'Write your text in Markdown' => 'Scrivi il testo in Markdown', - 'Leave a comment' => 'Lasciare un commento', + 'Leave a comment' => 'Lascia un commento', 'Comment is required' => 'Si richiede un commento', - 'Leave a description' => 'Lasciare una descrizione', + 'Leave a description' => 'Lascia una descrizione', 'Comment added successfully.' => 'Commenti aggiunti correttamente.', - 'Unable to create your comment.' => 'Non si può creare questo commento.', - 'Edit this task' => 'Modificare questo compito', + 'Unable to create your comment.' => 'Impossibile creare questo commento.', + 'Edit this task' => 'Modifica questo task', 'Due Date' => 'Data di scadenza', - 'Invalid date' => 'Data sbagliata', + 'Invalid date' => 'Data non valida', 'Must be done before %B %e, %Y' => 'Deve essere completato prima del %B %e, %Y', // '%B %e, %Y' => '', // '%b %e, %Y' => '', 'Automatic actions' => 'Azioni automatiche', 'Your automatic action have been created successfully.' => 'l\'azione automatica è stata creata correttamente.', - 'Unable to create your automatic action.' => 'Non si può creare quest\'azione automatica.', + 'Unable to create your automatic action.' => 'Impossibile creare quest\'azione automatica.', 'Remove an action' => 'Cancellare un\'azione', - 'Unable to remove this action.' => 'Non si può cancellare questa azione.', + 'Unable to remove this action.' => 'Impossibile cancellare questa azione.', 'Action removed successfully.' => 'Azione cancellata correttamente.', - 'Automatic actions for the project "%s"' => 'Azioni automatiche per questo progetto « %s »', + 'Automatic actions for the project "%s"' => 'Azioni automatiche per il progetto "%s"', 'Defined actions' => 'Azioni definite', 'Add an action' => 'Aggiungi un\'azione', 'Event name' => 'Nome dell\'evento', @@ -202,43 +202,43 @@ return array( 'Action parameters' => 'Parametri d\'azione', 'Action' => 'Azione', 'Event' => 'Evento', - 'When the selected event occurs execute the corresponding action.' => 'Quando accade l\'evento selezionato, eseguire l\'azione corrispondente.', - 'Next step' => 'Passo seguente', + 'When the selected event occurs execute the corresponding action.' => 'Quando si verifica l\'evento selezionato, eseguire l\'azione corrispondente.', + 'Next step' => 'Passo successivo', 'Define action parameters' => 'Definire i parametri dell\'azione', - 'Save this action' => 'Salvare questa azione', - 'Do you really want to remove this action: "%s"?' => 'Veramente vuole cancellare questa azione « %s » ?', - 'Remove an automatic action' => 'Cancellare un\'azione automatica', - 'Assign the task to a specific user' => 'Assegnare questo compito a un utente specifico', - 'Assign the task to the person who does the action' => 'Assegnare il compito all\'utente che svolge l\'azione', - 'Duplicate the task to another project' => 'Duplicare il compito in altro progetto', - 'Move a task to another column' => 'Muovere un compito in un\'altra colonna', - 'Task modification' => 'Modifica di un compito', - 'Task creation' => 'Creazione di un compito', - 'Closing a task' => 'Chiudere un compito', + 'Save this action' => 'Salva questa azione', + 'Do you really want to remove this action: "%s"?' => 'Vuoi veramente cancellare la seguente azione: "%s"?', + 'Remove an automatic action' => 'Cancella un\'azione automatica', + 'Assign the task to a specific user' => 'Assegna il task ad un utente specifico', + 'Assign the task to the person who does the action' => 'Assegna il task all\'utente che compie l\'azione', + 'Duplicate the task to another project' => 'Duplica il task in altro progetto', + 'Move a task to another column' => 'Sposta un task in un\'altra colonna', + 'Task modification' => 'Modifica di un task', + 'Task creation' => 'Creazione di un task', + 'Closing a task' => 'Chiusura di un task', 'Assign a color to a specific user' => 'Assegna un colore ad un utente specifico', 'Column title' => 'Titolo della colonna', 'Position' => 'Posizione', - 'Move Up' => 'Alzare', - 'Move Down' => 'Abassare', - 'Duplicate to another project' => 'Duplicare in un altro progetto', - 'Duplicate' => 'Duplicare', - 'link' => 'link', + 'Move Up' => 'Sposta in alto', + 'Move Down' => 'Sposta in basso', + 'Duplicate to another project' => 'Duplica in un altro progetto', + 'Duplicate' => 'Duplica', + 'link' => 'relazione', 'Comment updated successfully.' => 'Commento aggiornato correttamente.', - 'Unable to update your comment.' => 'Non si può aggiornare questo commento.', - 'Remove a comment' => 'Cancellare un commento', + 'Unable to update your comment.' => 'Impossibile aggiornare questo commento.', + 'Remove a comment' => 'Cancella un commento', 'Comment removed successfully.' => 'Commento cancellato correttamente.', - 'Unable to remove this comment.' => 'Non si può cancellare questo commento.', - 'Do you really want to remove this comment?' => 'Desidera cancellare questo commento?', + 'Unable to remove this comment.' => 'Impossibile cancellare questo commento.', + 'Do you really want to remove this comment?' => 'Vuoi davvero cancellare questo commento?', 'Only administrators or the creator of the comment can access to this page.' => 'Solo gli amministratori o l\'autore del commento hanno accesso a questa pagina.', - 'Current password for the user "%s"' => 'Password attuale per l\'utente: « %s »', + 'Current password for the user "%s"' => 'Password attuale per l\'utente "%s"', 'The current password is required' => 'Si richiede la password attuale', - 'Wrong password' => 'password sbagliata', + 'Wrong password' => 'Password errata', 'Unknown' => 'Sconociuto', - 'Last logins' => 'Ultimi ingressi', - 'Login date' => 'Data di ingresso', - 'Authentication method' => 'Metodo di autenticazzione', + 'Last logins' => 'Ultimi accessi', + 'Login date' => 'Data di accesso', + 'Authentication method' => 'Metodo di autenticazione', 'IP address' => 'Indirizzo IP', - 'User agent' => 'Navigatore', + 'User agent' => 'User agent', 'Persistent connections' => 'Connessioni persistenti', 'No session.' => 'Non esiste sessione.', 'Expiration date' => 'Data di scadenza', @@ -247,97 +247,91 @@ return array( 'Everybody' => 'Tutti', 'Open' => 'Aperto', 'Closed' => 'Chiuso', - 'Search' => 'Cercare', + 'Search' => 'Cerca', 'Nothing found.' => 'Non si è trovato nulla.', 'Due date' => 'Data di scadenza', - 'Others formats accepted: %s and %s' => 'Altri formati accettati: %s y %s', + 'Others formats accepted: %s and %s' => 'Altri formati accettati: %s e %s', 'Description' => 'Descrizione', '%d comments' => '%d commenti', '%d comment' => '%d commento', - 'Email address invalid' => 'Indirizzo e-mail sbagliato', - // 'Your external account is not linked anymore to your profile.' => '', - // 'Unable to unlink your external account.' => '', - // 'External authentication failed' => '', - // 'Your external account is linked to your profile successfully.' => '', + 'Email address invalid' => 'Indirizzo Email non valido', + 'Your external account is not linked anymore to your profile.' => 'Il tuo account esterno non è più collegato al tuo profilo.', + 'Unable to unlink your external account.' => 'Impossibile scollegare il tuo account esterno.', + 'External authentication failed' => 'Autenticazione esterna fallita', + 'Your external account is linked to your profile successfully.' => 'Il tuo account esterno è stato collegato al tuo profilo con successo.', 'Email' => 'E-mail', - 'Link my Google Account' => 'Collegare il mio Account di Google', - 'Unlink my Google Account' => 'Scollegare il mio account di Google', - 'Login with my Google Account' => 'Entra con il mio Account di Google', 'Project not found.' => 'progetto non trovato.', - 'Task removed successfully.' => 'Compito cancellato correttamente.', - 'Unable to remove this task.' => 'Non si può cancellare questo compito.', - 'Remove a task' => 'Cancellare un compito', - 'Do you really want to remove this task: "%s"?' => 'Veramente vuoi cancellare questo compito: "%s"?', - 'Assign automatically a color based on a category' => 'Assegnare un colore in modo automatico basandosi sulla categoria', - 'Assign automatically a category based on a color' => 'Assegnare una categoria in modo automatico basandosi sul colore', - 'Task creation or modification' => 'Creazione o modifica di compito', + 'Task removed successfully.' => 'Task cancellato correttamente.', + 'Unable to remove this task.' => 'Impossibile cancellare questo task.', + 'Remove a task' => 'Cancella un task', + 'Do you really want to remove this task: "%s"?' => 'Vuoi davvero cancellare questo task: "%s"?', + 'Assign automatically a color based on a category' => 'Assegna un colore in modo automatico basandosi sulla categoria', + 'Assign automatically a category based on a color' => 'Assegna una categoria in modo automatico basandosi sul colore', + 'Task creation or modification' => 'Creazione o modifica di task', 'Category' => 'Categoria', 'Category:' => 'Categoria:', 'Categories' => 'Categorie', 'Category not found.' => 'Categoria non trovata.', 'Your category have been created successfully.' => 'La tua categoria è stata creata correttamente.', - 'Unable to create your category.' => 'Non si può creare la tua categoria.', + 'Unable to create your category.' => 'Impossibile creare la tua categoria.', 'Your category have been updated successfully.' => 'La tua categoria è stata aggiornata correttamente.', - 'Unable to update your category.' => 'Non si può aggiornare la tua categoria.', - 'Remove a category' => 'Cancellare una categoria', + 'Unable to update your category.' => 'Impossibile aggiornare la tua categoria.', + 'Remove a category' => 'Cancella una categoria', 'Category removed successfully.' => 'Categoria cancellata correttamente.', - 'Unable to remove this category.' => 'Non si può cancellare questa categoria.', - 'Category modification for the project "%s"' => 'Modifica di categoria per il progetto "%s"', - 'Category Name' => 'Nome di categoria', + 'Unable to remove this category.' => 'Impossibile cancellare questa categoria.', + 'Category modification for the project "%s"' => 'Modifica della categoria per il progetto "%s"', + 'Category Name' => 'Nome della categoria', 'Add a new category' => 'Aggiungere una nuova categoria', - 'Do you really want to remove this category: "%s"?' => 'Vuoi veramente cancellare questa categoria: "%s"?', + 'Do you really want to remove this category: "%s"?' => 'Vuoi veramente cancellare la seguente categoria: "%s"?', 'All categories' => 'Tutte le categorie', 'No category' => 'Senza categoria', 'The name is required' => 'Si richiede un nome', - 'Remove a file' => 'Cancellare un file', - 'Unable to remove this file.' => 'Non si può cancellare questo file.', + 'Remove a file' => 'Cancella un file', + 'Unable to remove this file.' => 'Impossibile cancellare questo file.', 'File removed successfully.' => 'File cancellato correttamente.', - 'Attach a document' => 'Allegare un documento', + 'Attach a document' => 'Allega un documento', 'Do you really want to remove this file: "%s"?' => 'Vuoi veramente cancellare questo file: "%s"?', 'Attachments' => 'Allegati', - 'Edit the task' => 'Modificare il compito', - 'Edit the description' => 'Modificare la descrizione', - 'Add a comment' => 'Aggiungere un commento', - 'Edit a comment' => 'Modificare un commento', + 'Edit the task' => 'Modifica il task', + 'Edit the description' => 'Modifica la descrizione', + 'Add a comment' => 'Aggiungi un commento', + 'Edit a comment' => 'Modifica un commento', 'Summary' => 'Sommario', - 'Time tracking' => 'Time tracking', + // 'Time tracking' => '', 'Estimate:' => 'Stimato:', 'Spent:' => 'Trascorso:', - 'Do you really want to remove this sub-task?' => 'Vuoi veramente cancellare questo sotto-compito?', + 'Do you really want to remove this sub-task?' => 'Vuoi veramente cancellare questo sotto-task?', 'Remaining:' => 'Rimangono', 'hours' => 'ore', 'spent' => 'trascorse', 'estimated' => 'stimate', - 'Sub-Tasks' => 'Sotto-compiti', - 'Add a sub-task' => 'Aggiungere un sotto-compito', + 'Sub-Tasks' => 'Sotto-tasks', + 'Add a sub-task' => 'Aggiungi un sotto-task', 'Original estimate' => 'Stima originale', - 'Create another sub-task' => 'Creare un altro sotto-compito', - 'Time spent' => 'Tempo Trascorso', - 'Edit a sub-task' => 'Modificare un sotto-compito', - 'Remove a sub-task' => 'Cancellare un sotto-compito', + 'Create another sub-task' => 'Crea un altro sotto-task', + 'Time spent' => 'Tempo trascorso', + 'Edit a sub-task' => 'Modifica un sotto-task', + 'Remove a sub-task' => 'Cancella un sotto-task', 'The time must be a numeric value' => 'Il tempo deve essere un valore numerico', 'Todo' => 'Da fare', 'In progress' => 'In corso', - 'Sub-task removed successfully.' => 'Sotto-compito cancellato correttamente.', - 'Unable to remove this sub-task.' => 'Non si può cancellare questo sotto-compito.', - 'Sub-task updated successfully.' => 'Sotto-compito aggiornato correttamente.', - 'Unable to update your sub-task.' => 'Non si può aggiornare il tuo sotto-compito.', - 'Unable to create your sub-task.' => 'Non si può creare il tuo sotto-compito.', - 'Sub-task added successfully.' => 'Sotto-compito aggiunto correttamente.', - 'Maximum size: ' => 'Dimensioni massime', - 'Unable to upload the file.' => 'Non si può caricare il file.', - 'Display another project' => 'Mostrare un altro progetto', - 'Login with my Github Account' => 'Entrare col tuo account di Github', - 'Link my Github Account' => 'Collegare il mio account Github', - 'Unlink my Github Account' => 'Scollegare il mio account di Github', + 'Sub-task removed successfully.' => 'Sotto-task cancellato correttamente.', + 'Unable to remove this sub-task.' => 'Impossibile cancellare questo sotto-task.', + 'Sub-task updated successfully.' => 'Sotto-task aggiornato correttamente.', + 'Unable to update your sub-task.' => 'Impossibile aggiornare il tuo sotto-task.', + 'Unable to create your sub-task.' => 'Impossibile creare il tuo sotto-task.', + 'Sub-task added successfully.' => 'Sotto-task aggiunto correttamente.', + 'Maximum size: ' => 'Dimensioni massime: ', + 'Unable to upload the file.' => 'Impossibile caricare il file.', + 'Display another project' => 'Mostra un altro progetto', 'Created by %s' => 'Creato da %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Ultima modifica il %d/%m/%Y alle %H:%M', - 'Tasks Export' => 'Esportazione di compiti', - 'Tasks exportation for "%s"' => 'Esportazione di compiti per « %s »', + 'Tasks Export' => 'Export dei task', + 'Tasks exportation for "%s"' => 'Export dei task per "%s"', 'Start Date' => 'Data d\'inizio', 'End Date' => 'Data di fine', - 'Execute' => 'Eseguire', - 'Task Id' => 'Identificatore del compito', + 'Execute' => 'Esegui', + 'Task Id' => 'Id del task', 'Creator' => 'Creatore', 'Modification date' => 'Data di modifica', 'Completion date' => 'Data di termine', @@ -345,127 +339,125 @@ return array( 'Project cloned successfully.' => 'Progetto clonato con successo.', 'Unable to clone this project.' => 'Impossibile clonare questo progetto', 'Enable email notifications' => 'Abilita le notifiche via email', - 'Task position:' => 'Posizione del compito:', - 'The task #%d have been opened.' => 'Il compito #%d è stato aperto.', - 'The task #%d have been closed.' => 'Il compito #%d è stato chiuso.', - 'Sub-task updated' => 'Sotto-compito aggiornato', + 'Task position:' => 'Posizione del task:', + 'The task #%d have been opened.' => 'Il task #%d è stato aperto.', + 'The task #%d have been closed.' => 'Il task #%d è stato chiuso.', + 'Sub-task updated' => 'Sotto-task aggiornato', 'Title:' => 'Titolo', 'Status:' => 'Stato', - 'Assignee:' => 'Assegnatario', - 'Time tracking:' => 'Gestione del tempo:', - 'New sub-task' => 'Nuovo sotto-compito', - 'New attachment added "%s"' => 'Nuovo allegato aggiunto « %s »', + 'Assignee:' => 'Assegnatario:', + // 'Time tracking:' => '', + 'New sub-task' => 'Nuovo sotto-task', + 'New attachment added "%s"' => 'Nuovo allegato aggiunto "%s"', 'Comment updated' => 'Commento aggiornato', - 'New comment posted by %s' => 'Nuovo commento aggiunto da « %s »', + 'New comment posted by %s' => 'Nuovo commento aggiunto da "%s"', 'New attachment' => 'Nuovo allegato', 'New comment' => 'Nuovo commento', - 'New subtask' => 'Nuovo sotto-compito', - 'Subtask updated' => 'Sotto-compito aggiornato', - 'Task updated' => 'Compito aggiornato', - 'Task closed' => 'Compito chiuso', - 'Task opened' => 'Compito aperto', + 'New subtask' => 'Nuovo sotto-task', + 'Subtask updated' => 'Sotto-task aggiornato', + 'Task updated' => 'Task aggiornato', + 'Task closed' => 'Task chiuso', + 'Task opened' => 'Task aperto', 'I want to receive notifications only for those projects:' => 'Vorrei ricevere le notifiche solo da questi progetti:', - 'view the task on Kanboard' => 'vedi il compito su Kanboard', + 'view the task on Kanboard' => 'vedi il task su Kanboard', 'Public access' => 'Accesso pubblico', 'User management' => 'Gestione utenti', - 'Active tasks' => 'Compiti attivi', + 'Active tasks' => 'Task attivi', 'Disable public access' => 'Disabilita l\'accesso pubblico', 'Enable public access' => 'Abilita l\'accesso pubblico', 'Public access disabled' => 'Accesso pubblico disattivato', - 'Do you really want to disable this project: "%s"?' => 'Vuoi davvero disabilitare questo progetto: "%s"?', - 'Do you really want to enable this project: "%s"?' => 'Vuoi davvero abilitare questo progetto: "%s"?', + 'Do you really want to disable this project: "%s"?' => 'Vuoi davvero disabilitare il seguente progetto: "%s"?', + 'Do you really want to enable this project: "%s"?' => 'Vuoi davvero abilitare il seguente progetto: "%s"?', 'Project activation' => 'Attivazione progetto', - 'Move the task to another project' => 'Muovi il compito in un altro progetto', - 'Move to another project' => 'Muovi in un altro progetto', - 'Do you really want to duplicate this task?' => 'Vuoi davvero duplicare questo compito?', - 'Duplicate a task' => 'Duplica il compito', + 'Move the task to another project' => 'Sposta il task in un altro progetto', + 'Move to another project' => 'Sposta in un altro progetto', + 'Do you really want to duplicate this task?' => 'Vuoi davvero duplicare questo task?', + 'Duplicate a task' => 'Duplica il task', 'External accounts' => 'Account esterni', 'Account type' => 'Tipo di account', 'Local' => 'Locale', 'Remote' => 'Remoto', 'Enabled' => 'Abilitato', 'Disabled' => 'Disabilitato', - // 'Username:' => '', + 'Username:' => 'Nome utente:', 'Name:' => 'Nome:', // 'Email:' => '', 'Notifications:' => 'Notifiche:', 'Notifications' => 'Notifiche', 'Account type:' => 'Tipo di account', - 'Edit profile' => 'Modifica il profilo', + 'Edit profile' => 'Modifica profilo', 'Change password' => 'Cambia password', 'Password modification' => 'Modifica della password', 'External authentications' => 'Autenticazione esterna', - 'Google Account' => 'Account Google', - 'Github Account' => 'Account Github', 'Never connected.' => 'Mai connesso.', 'No account linked.' => 'Nessun account collegato.', 'Account linked.' => 'Account collegato.', 'No external authentication enabled.' => 'Nessuna autenticazione esterna abilitata.', 'Password modified successfully.' => 'Password modificata con successo.', 'Unable to change the password.' => 'Impossibile cambiare la password.', - 'Change category for the task "%s"' => 'Cambia categoria per il compito "%s"', + 'Change category for the task "%s"' => 'Cambia categoria per il task "%s"', 'Change category' => 'Cambia categoria', - '%s updated the task %s' => '%s ha aggiornato il compito %s', - '%s opened the task %s' => '%s ha aperto il compito %s', - '%s moved the task %s to the position #%d in the column "%s"' => '%s ha spostato il compito %s nella posizione #%d della colonna "%s"', - '%s moved the task %s to the column "%s"' => '%s ha spostato il compito %s nella colonna "%s"', - '%s created the task %s' => '%s ha creato il compito %s', - '%s closed the task %s' => '%s ha chiuso il compito %s', - '%s created a subtask for the task %s' => '%s ha creato un sotto-compito per il compito %s', - '%s updated a subtask for the task %s' => '%s ha aggiornato un sotto-compito per il compito %s', + '%s updated the task %s' => '%s ha aggiornato il task %s', + '%s opened the task %s' => '%s ha aperto il task %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s ha spostato il task %s nella posizione #%d della colonna "%s"', + '%s moved the task %s to the column "%s"' => '%s ha spostato il task %s nella colonna "%s"', + '%s created the task %s' => '%s ha creato il task %s', + '%s closed the task %s' => '%s ha chiuso il task %s', + '%s created a subtask for the task %s' => '%s ha creato un sotto-task per il task %s', + '%s updated a subtask for the task %s' => '%s ha aggiornato un sotto-task per il task %s', 'Assigned to %s with an estimate of %s/%sh' => 'Assegnato a %s con una stima di %s/%sh', 'Not assigned, estimate of %sh' => 'Non assegnato, stima %sh', - '%s updated a comment on the task %s' => '%s ha aggiornato un commento del compito %s', - '%s commented the task %s' => '%s ha commentato il compito %s', + '%s updated a comment on the task %s' => '%s ha aggiornato un commento nel task %s', + '%s commented the task %s' => '%s ha commentato il task %s', '%s\'s activity' => 'Attività di %s', 'RSS feed' => 'Feed RSS', - '%s updated a comment on the task #%d' => '%s ha aggiornato un commento del compito #%d', - '%s commented on the task #%d' => '%s ha commentato il compito #%d', - '%s updated a subtask for the task #%d' => '%s ha aggiornato un sotto-compito del compito #%d', - '%s created a subtask for the task #%d' => '%s ha creato un sotto-compito del compito #%d', - '%s updated the task #%d' => '%s ha aggiornato il compito #%d', - '%s created the task #%d' => '%s ha creato il compito #%d', - '%s closed the task #%d' => '%s ha chiuso il compito #%d', - '%s open the task #%d' => '%s ha aperto il compito #%d', - '%s moved the task #%d to the column "%s"' => '%s ha spostato il compito #%d nella colonna "%s"', - '%s moved the task #%d to the position %d in the column "%s"' => '%s ha spostato il compito #%d nella posizione %d della colonna "%s"', + '%s updated a comment on the task #%d' => '%s ha aggiornato un commento del task #%d', + '%s commented on the task #%d' => '%s ha commentato il task #%d', + '%s updated a subtask for the task #%d' => '%s ha aggiornato un sotto-task del task #%d', + '%s created a subtask for the task #%d' => '%s ha creato un sotto-task del task #%d', + '%s updated the task #%d' => '%s ha aggiornato il task #%d', + '%s created the task #%d' => '%s ha creato il task #%d', + '%s closed the task #%d' => '%s ha chiuso il task #%d', + '%s open the task #%d' => '%s ha aperto il task #%d', + '%s moved the task #%d to the column "%s"' => '%s ha spostato il task #%d nella colonna "%s"', + '%s moved the task #%d to the position %d in the column "%s"' => '%s ha spostato il task #%d nella posizione %d della colonna "%s"', 'Activity' => 'Attività', 'Default values are "%s"' => 'Valori di default "%s"', 'Default columns for new projects (Comma-separated)' => 'Colonne di default per i nuovi progetti (Separati da virgola)', - 'Task assignee change' => 'Cambiare l\'assegnatario del compito', - '%s change the assignee of the task #%d to %s' => '%s dai l\'assegnazione del compito #%d a %s', - '%s changed the assignee of the task %s to %s' => '%s ha cambiato l\'assegnatario del compito %s a %s', + 'Task assignee change' => 'Cambia l\'assegnatario del task', + '%s change the assignee of the task #%d to %s' => '%s dai l\'assegnazione del task #%d a %s', + '%s changed the assignee of the task %s to %s' => '%s ha cambiato l\'assegnatario del task %s a %s', 'New password for the user "%s"' => 'Nuova password per l\'utente "%s"', 'Choose an event' => 'Scegli un evento', - 'Create a task from an external provider' => 'Crea un compito da un provider esterno', + 'Create a task from an external provider' => 'Crea un task da un provider esterno', 'Change the assignee based on an external username' => 'Cambia l\'assegnatario basandosi su un username esterno', 'Change the category based on an external label' => 'Cambia la categoria basandosi su un\'etichetta esterna', - 'Reference' => 'Referenza', - 'Reference: %s' => 'Referenza :%s', + 'Reference' => 'Riferimento', + 'Reference: %s' => 'Riferimento :%s', 'Label' => 'Etichetta', // 'Database' => '', - 'About' => 'Info', + 'About' => 'Informazioni', 'Database driver:' => 'Driver per Database', 'Board settings' => 'Impostazioni bacheca', 'URL and token' => 'URL e token', 'Webhook settings' => 'Impostazione Webhook', - 'URL for task creation:' => 'URL per la creazione dei compiti:', + 'URL for task creation:' => 'URL per la creazione dei tasks:', 'Reset token' => 'Rigenera il token', 'API endpoint:' => 'Endpoint dell\'API:', 'Refresh interval for private board' => 'Intervallo di refresh per le bacheche private', 'Refresh interval for public board' => 'Intervallo di refresh per le bacheche pubbliche', - 'Task highlight period' => 'Periodo di evidenza per il compito', - 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periodo (in secondi) per considerare un compito come modificato recentemente (0 per disabilitare, 2 giorni di default)', + 'Task highlight period' => 'Periodo di evidenza per il task', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periodo (in secondi) per considerare un task come modificato recentemente (0 per disabilitare, 2 giorni di default)', 'Frequency in second (60 seconds by default)' => 'Frequenza in secondi (60 secondi di default)', 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frequenza in secondi (0 secondi di default)', 'Application URL' => 'URL dell\'applicazione', 'Example: http://example.kanboard.net/ (used by email notifications)' => 'Esempio: http://example.kanboard.net/ (usato dalle notifiche email)', - 'Token regenerated.' => 'Token rigenerato', + 'Token regenerated.' => 'Token rigenerato.', 'Date format' => 'Formato data', 'ISO format is always accepted, example: "%s" and "%s"' => 'Il formato ISO è sempre accettato, esempio: "%s" e "%s"', 'New private project' => 'Nuovo progetto privato', 'This project is private' => 'Questo progetto è privato', - 'Type here to create a new sub-task' => 'Scrivi qui per creare un sotto-compito', + 'Type here to create a new sub-task' => 'Scrivi qui per creare un sotto-task', 'Add' => 'Aggiungi', 'Estimated time: %s hours' => 'Tempo stimato: %s ore', 'Time spent: %s hours' => 'Tempo trascorso: %s ore', @@ -473,28 +465,28 @@ return array( 'Start date' => 'Data di inizio', 'Time estimated' => 'Tempo stimato', 'There is nothing assigned to you.' => 'Non c\'è nulla assegnato a te.', - 'My tasks' => 'I miei compiti', + 'My tasks' => 'I miei task', 'Activity stream' => 'Flusso di attività', 'Dashboard' => 'Bacheca', 'Confirmation' => 'Conferma', 'Allow everybody to access to this project' => 'Abilita tutti ad accedere a questo progetto', - 'Everybody have access to this project.' => 'Tutti hanno accesso a questo progetto', + 'Everybody have access to this project.' => 'Tutti hanno accesso a questo progetto.', // 'Webhooks' => '', // 'API' => '', 'Create a comment from an external provider' => 'Crea un commit da un provider esterno', - 'Project management' => 'Gestione del progetto', + 'Project management' => 'Gestione progetti', 'My projects' => 'I miei progetti', 'Columns' => 'Colonne', - 'Task' => 'Compito', - 'Your are not member of any project.' => 'Non sei membro di alcun progetto', + 'Task' => 'Task', + 'Your are not member of any project.' => 'Non sei membro di alcun progetto.', 'Percentage' => 'Percentuale', - 'Number of tasks' => 'Numero di compiti', - 'Task distribution' => 'Distribuzione dei compiti', + 'Number of tasks' => 'Numero di task', + 'Task distribution' => 'Distribuzione dei task', 'Reportings' => 'Rapporti', - 'Task repartition for "%s"' => 'Ripartizione compiti per "%s"', + 'Task repartition for "%s"' => 'Ripartizione task per "%s"', // 'Analytics' => '', - 'Subtask' => 'Sotto-compiti', - 'My subtasks' => 'I miei sotto-compiti', + 'Subtask' => 'Sotto-task', + 'My subtasks' => 'I miei sotto-task', 'User repartition' => 'Ripartizione per utente', 'User repartition for "%s"' => 'Ripartizione utente per "%s"', 'Clone this project' => 'Clona questo progetto', @@ -504,52 +496,52 @@ return array( 'The id must be an integer' => 'L\'id deve essere un intero', 'The project id must be an integer' => 'L\'id del progetto deve essere un intero', 'The status must be an integer' => 'Lo status deve essere un intero', - 'The subtask id is required' => 'L\'id del sotto-compito è necessario', - 'The subtask id must be an integer' => 'L\'id del sotto-compito deve essere un intero', - 'The task id is required' => 'Richiesto l\'id del compito', - 'The task id must be an integer' => 'L\'id del compito deve essere un intero', + 'The subtask id is required' => 'L\'id del sotto-task è necessario', + 'The subtask id must be an integer' => 'L\'id del sotto-task deve essere un intero', + 'The task id is required' => 'Richiesto l\'id del task', + 'The task id must be an integer' => 'L\'id del task deve essere un intero', 'The user id must be an integer' => 'L\'id dell\'utente deve essere un intero', 'This value is required' => 'Questo valore è necessario', 'This value must be numeric' => 'Questo valore deve essere numerico', - 'Unable to create this task.' => 'Impossibile creare questo compito', + 'Unable to create this task.' => 'Impossibile creare questo task', 'Cumulative flow diagram' => 'Diagramma di flusso cumulativo', 'Cumulative flow diagram for "%s"' => 'Diagramma di flusso comulativo per "%s"', 'Daily project summary' => 'Sommario giornaliero del progetto', - 'Daily project summary export' => 'Esportazione del sommario giornaliero del progetto', - 'Daily project summary export for "%s"' => 'Esportazione del sommario giornaliero del progetto per "%s"', + 'Daily project summary export' => 'Export del sommario giornaliero del progetto', + 'Daily project summary export for "%s"' => 'Export del sommario giornaliero del progetto per "%s"', 'Exports' => 'Esporta', - 'This export contains the number of tasks per column grouped per day.' => 'Questo export contiene il numero di compiti per colonna raggruppati per giorno', + 'This export contains the number of tasks per column grouped per day.' => 'Questo export contiene il numero di task per colonna raggruppati per giorno', 'Nothing to preview...' => 'Nessuna anteprima...', 'Preview' => 'Anteprima', 'Write' => 'Scrivi', 'Active swimlanes' => 'Corsie attive', 'Add a new swimlane' => 'Aggiungi una corsia', - 'Change default swimlane' => 'Cambia la corsia di default', - 'Default swimlane' => 'Corsia di default', - 'Do you really want to remove this swimlane: "%s"?' => 'Vuoi davvero rimuovere questa corsia: "%s"?', + 'Change default swimlane' => 'Cambia la corsia predefinita', + 'Default swimlane' => 'Corsia predefinita', + 'Do you really want to remove this swimlane: "%s"?' => 'Vuoi davvero rimuovere la seguente corsia: "%s"?', 'Inactive swimlanes' => 'Corsie inattive', 'Remove a swimlane' => 'Rimuovi una corsia', 'Rename' => 'Rinomina', - 'Show default swimlane' => 'Mostra le corsie di default', + 'Show default swimlane' => 'Mostra la corsia predefinita', 'Swimlane modification for the project "%s"' => 'Modifica corsia per il progetto "%s"', - 'Swimlane not found.' => 'Corsia non trovata', - 'Swimlane removed successfully.' => 'Corsia rimossa con successo', + 'Swimlane not found.' => 'Corsia non trovata.', + 'Swimlane removed successfully.' => 'Corsia rimossa con successo.', 'Swimlanes' => 'Corsie', - 'Swimlane updated successfully.' => 'Corsia aggiornata con successo', - 'The default swimlane have been updated successfully.' => 'La corsia di default è stata aggiornata con successo.', - 'Unable to create your swimlane.' => 'Impossibile creare la sua corsia.', + 'Swimlane updated successfully.' => 'Corsia aggiornata con successo.', + 'The default swimlane have been updated successfully.' => 'La corsia predefinita è stata aggiornata con successo.', + 'Unable to create your swimlane.' => 'Impossibile creare la corsia.', 'Unable to remove this swimlane.' => 'Impossibile rimuovere questa corsia.', 'Unable to update this swimlane.' => 'Impossibile aggiornare questa corsia.', - 'Your swimlane have been created successfully.' => 'La sua corsia è stata creata con successo', - 'Example: "Bug, Feature Request, Improvement"' => 'Esempio: "Bug, Richiesta di Funzioni, Migliorie"', + 'Your swimlane have been created successfully.' => 'La tua corsia è stata creata con successo', + 'Example: "Bug, Feature Request, Improvement"' => 'Esempi: "Bug, Richiesta di Funzioni, Migliorie"', 'Default categories for new projects (Comma-separated)' => 'Categorie di default per i progetti (Separati da virgola)', 'Integrations' => 'Integrazioni', 'Integration with third-party services' => 'Integrazione con servizi di terze parti', - 'Subtask Id' => 'Id del sotto-compito', - 'Subtasks' => 'Sotto-compiti', - 'Subtasks Export' => 'Esporta sotto-compiti', - 'Subtasks exportation for "%s"' => 'Esportazione dei sotto-compiti per "%s"', - 'Task Title' => 'Titolo del compito', + 'Subtask Id' => 'Id del sotto-task', + 'Subtasks' => 'Sotto-task', + 'Subtasks Export' => 'Esporta i sotto-task', + 'Subtasks exportation for "%s"' => 'Export dei sotto-task per "%s"', + 'Task Title' => 'Titolo del task', 'Untitled' => 'Senza titolo', 'Application default' => 'Default dell\'applicazione', 'Language:' => 'Lingua', @@ -563,41 +555,41 @@ return array( 'Moved to column %s' => 'Spostato sulla colonna "%s"', 'Change description' => 'Cambia descrizione', 'User dashboard' => 'Bacheca utente', - 'Allow only one subtask in progress at the same time for a user' => 'Permetti un solo sotto-compito in progresso per utente nello stesso tempo', + 'Allow only one subtask in progress at the same time for a user' => 'Permetti un solo sotto-task in corso per utente alla volta', 'Edit column "%s"' => 'Modifica la colonna "%s"', - 'Select the new status of the subtask: "%s"' => 'Selziona il nuovo status per il sotto-compito: "%s"', - 'Subtask timesheet' => 'Timesheet del sotto-compito', + 'Select the new status of the subtask: "%s"' => 'Seleziona il nuovo status per il sotto-task: "%s"', + 'Subtask timesheet' => 'Timesheet del sotto-task', 'There is nothing to show.' => 'Nulla da mostrare.', - 'Time Tracking' => 'Gestione del tempo', - 'You already have one subtask in progress' => 'Hai già un sotto-compito in progresso', + // 'Time Tracking' => '', + 'You already have one subtask in progress' => 'Hai già un sotto-task in corso', 'Which parts of the project do you want to duplicate?' => 'Quali parti del progetto vuoi duplicare?', - // 'Disallow login form' => '', + 'Disallow login form' => 'Disabilita il form di login', 'Start' => 'Inizio', 'End' => 'Fine', - 'Task age in days' => 'Anzianità del compito in giorni', + 'Task age in days' => 'Anzianità del task in giorni', 'Days in this column' => 'Giorni in questa colonna', // '%dd' => '', - 'Add a link' => 'Aggiungi un link', - 'Add a new link' => 'Aggiungi un nuovo link', - 'Do you really want to remove this link: "%s"?' => 'Vuoi davvero rimuovere questo link: "%s"?', - 'Do you really want to remove this link with task #%d?' => 'Vuoi davvero rimuovere questo link dal compito #%d?', + 'Add a link' => 'Aggiungi una relazione', + 'Add a new link' => 'Aggiungi una nuova relazione', + 'Do you really want to remove this link: "%s"?' => 'Vuoi davvero rimuovere la seguente relazione: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Vuoi davvero rimuovere questa relazione dal task #%d?', 'Field required' => 'Campo necessario', - 'Link added successfully.' => 'Link aggiunto con successo.', - 'Link updated successfully.' => 'Linka aggiornato con successo.', - 'Link removed successfully.' => 'Link rimosso con successo.', - 'Link labels' => 'Etichette dei link', - 'Link modification' => 'Modifica link', - 'Links' => 'Link', - 'Link settings' => 'Impostazioni link', - // 'Opposite label' => '', - 'Remove a link' => 'Rimuovi un link', - 'Task\'s links' => 'Link del compito', + 'Link added successfully.' => 'Relazione aggiunta con successo.', + 'Link updated successfully.' => 'Relazione aggiornata con successo.', + 'Link removed successfully.' => 'Relazione rimosso con successo.', + 'Link labels' => 'Etichette delle relazioni', + 'Link modification' => 'Modifica relazione', + 'Links' => 'Relazioni', + 'Link settings' => 'Impostazioni relazioni', + 'Opposite label' => 'Etichetta contraria', + 'Remove a link' => 'Rimuovi una relazione', + 'Task\'s links' => 'Relazioni del task', 'The labels must be different' => 'Le etichette devono essere diverse', - 'There is no link.' => 'Non c\'è alcun link', - 'This label must be unique' => 'Questa etichetta deve essere unica', - 'Unable to create your link.' => 'Impossibile creare il suo link.', - 'Unable to update your link.' => 'Impossibile aggiornare il suo link.', - 'Unable to remove this link.' => 'Impossibile rimuovere il suo link.', + 'There is no link.' => 'Nessuna relazione presente.', + 'This label must be unique' => 'Questa etichetta deve essere univoca', + 'Unable to create your link.' => 'Impossibile creare la relazione.', + 'Unable to update your link.' => 'Impossibile aggiornare la relazione.', + 'Unable to remove this link.' => 'Impossibile rimuovere la relazione.', 'relates to' => 'si riferisce a', 'blocks' => 'blocca', 'is blocked by' => 'è bloccato da', @@ -609,18 +601,18 @@ return array( 'is a milestone of' => 'è una milestone di', 'fixes' => 'sistema', 'is fixed by' => 'è sistemato da', - 'This task' => 'Questo compito', + 'This task' => 'Questo task', // '<1h' => '', // '%dh' => '', // '%b %e' => '', - 'Expand tasks' => 'Espandi i compiti', - 'Collapse tasks' => 'Minimizza i compiti', - 'Expand/collapse tasks' => 'Espandi/Minimizza compiti', - 'Close dialog box' => 'Chiudi dialog box', + 'Expand tasks' => 'Espandi i task', + 'Collapse tasks' => 'Minimizza i task', + 'Expand/collapse tasks' => 'Espandi/minimizza i task', + 'Close dialog box' => 'Chiudi la finestra di dialogo', 'Submit a form' => 'Invia i dati', 'Board view' => 'Vista bacheca', 'Keyboard shortcuts' => 'Scorciatoie da tastiera', - 'Open board switcher' => 'Apri il selezionatore di bacheche', + 'Open board switcher' => 'Apri il selettore di bacheche', 'Application' => 'Applicazione', 'since %B %e, %Y at %k:%M %p' => 'dal %B %e, %Y alle %k:%M %p', 'Compact view' => 'Vista compatta', @@ -628,13 +620,13 @@ return array( 'Compact/wide view' => 'Vista compatta/estesa', 'No results match:' => 'Nessun risultato trovato:', 'Currency' => 'Valuta', - // 'Files' => '', + 'Files' => 'File', 'Images' => 'Immagini', 'Private project' => 'Progetto privato', 'AUD - Australian Dollar' => 'AUD - Dollari Australiani', 'CAD - Canadian Dollar' => 'CAD - Dollari Canadesi', 'CHF - Swiss Francs' => 'CHF - Franchi Svizzeri', - 'Custom Stylesheet' => 'CSS personalizzato', + 'Custom Stylesheet' => 'Foglio di stile personalizzato', // 'download' => '', // 'EUR - Euro' => '', 'GBP - British Pound' => 'GBP - Pound Inglesi', @@ -644,15 +636,15 @@ return array( 'RSD - Serbian dinar' => 'RSD - Dinar Serbi', 'USD - US Dollar' => 'USD - Dollari Americani', 'Destination column' => 'Colonna destinazione', - 'Move the task to another column when assigned to a user' => 'Sposta il compito in un\'altra colonna quando viene assegnato ad un utente', - 'Move the task to another column when assignee is cleared' => 'Sposta il compito in un\'altra colonna quando l\'assegnatario cancellato', + 'Move the task to another column when assigned to a user' => 'Sposta il task in un\'altra colonna quando viene assegnato ad un utente', + 'Move the task to another column when assignee is cleared' => 'Sposta il task in un\'altra colonna quando l\'assegnatario viene cancellato', 'Source column' => 'Colonna sorgente', 'Transitions' => 'Transizioni', 'Executer' => 'Esecutore', 'Time spent in the column' => 'Tempo trascorso nella colonna', - 'Task transitions' => 'Transizioni del compito', - 'Task transitions export' => 'Esporta le transizioni del compito', - 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Questo report contiene tutti i movimenti di colonna per ogni compito con le date, l\'utente ed il tempo trascorso per ogni transizione', + 'Task transitions' => 'Transizioni dei task', + 'Task transitions export' => 'Export delle transizioni dei task', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Questo report contiene tutti gli spotamenti di colonna per ogni task con le date, l\'utente ed il tempo trascorso per ogni transizione', 'Currency rates' => 'Tassi di cambio', 'Rate' => 'Cambio', 'Change reference currency' => 'Cambia la valuta di riferimento', @@ -661,461 +653,481 @@ return array( 'The currency rate have been added successfully.' => 'Il tasso di cambio è stato aggiunto con successo.', 'Unable to add this currency rate.' => 'Impossibile aggiungere questo tasso di cambio.', 'Webhook URL' => 'URL Webhook', - '%s remove the assignee of the task %s' => '%s rimuove l\'assegnatario del compito %s', + '%s remove the assignee of the task %s' => '%s rimuove l\'assegnatario del task %s', 'Enable Gravatar images' => 'Abilita immagini Gravatar', 'Information' => 'Informazioni', - 'Check two factor authentication code' => 'Controlla il codice di autenticazione a due fattori', - 'The two factor authentication code is not valid.' => 'Il codice di autenticazione a due fattori non è valido', - 'The two factor authentication code is valid.' => 'Il codice di autenticazione a due fattori è valido', + 'Check two factor authentication code' => 'Controlla il codice di autenticazione "two-factor"', + 'The two factor authentication code is not valid.' => 'Il codice di autenticazione "two-factor" non è valido', + 'The two factor authentication code is valid.' => 'Il codice di autenticazione "two-factor" è valido', 'Code' => 'Codice', - 'Two factor authentication' => 'Autenticazione a due fattori', + 'Two factor authentication' => 'Autenticazione "two-factor"', 'This QR code contains the key URI: ' => 'Questo QR code contiene l\'URI: ', 'Check my code' => 'Controlla il mio codice', 'Secret key: ' => 'Chiave privata:', 'Test your device' => 'Testa il tuo dispositivo', - 'Assign a color when the task is moved to a specific column' => 'Assegna un colore quando il compito viene spostato in una colonna specifica', - // '%s via Kanboard' => '', - // 'uploaded by: %s' => '', - // 'uploaded on: %s' => '', - // 'size: %s' => '', - // 'Burndown chart for "%s"' => '', - // 'Burndown chart' => '', - // 'This chart show the task complexity over the time (Work Remaining).' => '', - // 'Screenshot taken %s' => '', - // 'Add a screenshot' => '', - // 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => '', - // 'Screenshot uploaded successfully.' => '', - // 'SEK - Swedish Krona' => '', - // 'Identifier' => '', - // 'Disable two factor authentication' => '', - // 'Do you really want to disable the two factor authentication for this user: "%s"?' => '', - // 'Edit link' => '', - // 'Start to type task title...' => '', - // 'A task cannot be linked to itself' => '', - // 'The exact same link already exists' => '', - // 'Recurrent task is scheduled to be generated' => '', - // 'Recurring information' => '', - // 'Score' => '', - // 'The identifier must be unique' => '', - // 'This linked task id doesn\'t exists' => '', - // 'This value must be alphanumeric' => '', - // 'Edit recurrence' => '', - // 'Generate recurrent task' => '', - // 'Trigger to generate recurrent task' => '', - // 'Factor to calculate new due date' => '', - // 'Timeframe to calculate new due date' => '', - // 'Base date to calculate new due date' => '', - // 'Action date' => '', - // 'Base date to calculate new due date: ' => '', - // 'This task has created this child task: ' => '', - // 'Day(s)' => '', - // 'Existing due date' => '', - // 'Factor to calculate new due date: ' => '', - // 'Month(s)' => '', - // 'Recurrence' => '', - // 'This task has been created by: ' => '', - // 'Recurrent task has been generated:' => '', - // 'Timeframe to calculate new due date: ' => '', - // 'Trigger to generate recurrent task: ' => '', - // 'When task is closed' => '', - // 'When task is moved from first column' => '', - // 'When task is moved to last column' => '', - // 'Year(s)' => '', - // 'Calendar settings' => '', - // 'Project calendar view' => '', - // 'Project settings' => '', - // 'Show subtasks based on the time tracking' => '', - // 'Show tasks based on the creation date' => '', - // 'Show tasks based on the start date' => '', - // 'Subtasks time tracking' => '', - // 'User calendar view' => '', - // 'Automatically update the start date' => '', - // 'iCal feed' => '', - // 'Preferences' => '', - // 'Security' => '', - // 'Two factor authentication disabled' => '', - // 'Two factor authentication enabled' => '', - // 'Unable to update this user.' => '', - // 'There is no user management for private projects.' => '', - // 'User that will receive the email' => '', - // 'Email subject' => '', - // 'Date' => '', - // 'Add a comment log when moving the task between columns' => '', - // 'Move the task to another column when the category is changed' => '', - // 'Send a task by email to someone' => '', - // 'Reopen a task' => '', - // 'Column change' => '', - // 'Position change' => '', - // 'Swimlane change' => '', - // 'Assignee change' => '', - // '[%s] Overdue tasks' => '', - // 'Notification' => '', - // '%s moved the task #%d to the first swimlane' => '', - // '%s moved the task #%d to the swimlane "%s"' => '', - // 'Swimlane' => '', + 'Assign a color when the task is moved to a specific column' => 'Assegna un colore quando il task viene spostato in una colonna specifica', + '%s via Kanboard' => '%s tramite Kanboard', + 'uploaded by: %s' => 'caricato da: %s', + 'uploaded on: %s' => 'caricato su: %s', + 'size: %s' => 'Dimensione: %s', + 'Burndown chart for "%s"' => 'Grafico Burndown per "%s"', + 'Burndown chart' => 'Grafico Burndown', + 'This chart show the task complexity over the time (Work Remaining).' => 'Questo grafico mostra la complessità dei task nel tempo (Lavoro residuo).', + 'Screenshot taken %s' => 'Schermata catturata %s', + 'Add a screenshot' => 'Aggiungi una schermata', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Cattura una schermata e premi CTRL+V o ⌘+V per incollarla qui.', + 'Screenshot uploaded successfully.' => 'Schermata caricata correttamente.', + 'SEK - Swedish Krona' => 'SEK - Corona svedese', + 'Identifier' => 'Identificatore', + 'Disable two factor authentication' => 'Disabilita l\'autenticazione "two-factor"', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Vuoi davvero disabilitare l\'autenticazione "two-factor" per questo utente: "%s"?', + 'Edit link' => 'Modifica relazione', + 'Start to type task title...' => 'Inzia a digitare il titolo di un task...', + 'A task cannot be linked to itself' => 'Un task non può essere correlato a se stesso', + 'The exact same link already exists' => 'La stessa relazione risulta già esistente', + 'Recurrent task is scheduled to be generated' => 'Il task ricorrente è pianificato per essere generato', + 'Recurring information' => 'Informazione di ricorrenza', + 'Score' => 'Punteggio', + 'The identifier must be unique' => 'L\'identificatore deve essere univoco', + 'This linked task id doesn\'t exists' => 'L\'id del task correlato non esiste', + 'This value must be alphanumeric' => 'Questo valore deve essere alfanumerico', + 'Edit recurrence' => 'Modifica ricorrenza', + 'Generate recurrent task' => 'Genera task ricorrente', + 'Trigger to generate recurrent task' => 'Trigger per generare il task ricorrente', + 'Factor to calculate new due date' => 'Fattore numerico per calcolare la nuova data di scadenza', + 'Timeframe to calculate new due date' => 'Lasso temporale per calcolare la nuova data di scadenza', + 'Base date to calculate new due date' => 'Data di partenza per calcolare la nuova data di scadenza', + 'Action date' => 'Data di azione (action date)', + 'Base date to calculate new due date: ' => 'Data di base per calcolare la nuova data di scadenza: ', + 'This task has created this child task: ' => 'Questo task ha creato il seguente task figlio: ', + 'Day(s)' => 'Giorno/i', + 'Existing due date' => 'Data di scadenza esistente', + 'Factor to calculate new due date: ' => 'Fattore numerico per calcolare la nuova data di scadenza: ', + 'Month(s)' => 'Mese/i', + 'Recurrence' => 'Ricorrenza', + 'This task has been created by: ' => 'Questo task è stato creato da: ', + 'Recurrent task has been generated:' => 'Il task ricorrente è stato generato:', + 'Timeframe to calculate new due date: ' => 'Lasso temporale per calcolare la nuova data di scadenza: ', + 'Trigger to generate recurrent task: ' => 'Trigger per generare il task ricorrente: ', + 'When task is closed' => 'Quando un task è chiuso', + 'When task is moved from first column' => 'Quando un task è spostato dalla prima colonna', + 'When task is moved to last column' => 'Quando un task è spostato nell\'ultima colonna', + 'Year(s)' => 'Anno/i', + 'Calendar settings' => 'Impostazioni del calendario', + 'Project calendar view' => 'Vista di progetto a calendario', + 'Project settings' => 'Impostazioni di progetto', + 'Show subtasks based on the time tracking' => 'Mostra i sotto-task in base al time tracking', + 'Show tasks based on the creation date' => 'Mostra i task in base alla data di creazione', + 'Show tasks based on the start date' => 'Mostra i task in base alla data di inzio', + 'Subtasks time tracking' => 'Time tracking per i sotto-task', + 'User calendar view' => 'Vista utente a calendario', + 'Automatically update the start date' => 'Aggiorna automaticamente la data di inzio', + 'iCal feed' => 'feed iCal', + 'Preferences' => 'Preferenze', + 'Security' => 'Sicurezza', + 'Two factor authentication disabled' => 'Two factor authentication disabilitata', + 'Two factor authentication enabled' => 'Two factor authentication abilitata', + 'Unable to update this user.' => 'Impossibile aggiornare questo utente.', + 'There is no user management for private projects.' => 'Non è prevista la gestione di utenti per i progetti privati.', + 'User that will receive the email' => 'Utente che riceverà l\'email', + 'Email subject' => 'Soggetto dell\'email', + 'Date' => 'Data', + 'Add a comment log when moving the task between columns' => 'Aggiungi un log quando si sposta un task tra colonne', + 'Move the task to another column when the category is changed' => 'Sposta il task in un\'altra colonna quando la categoria viene modificata', + 'Send a task by email to someone' => 'Invia un task via email a qualcuno', + 'Reopen a task' => 'Riapri un task', + 'Column change' => 'Cambio di colonna', + 'Position change' => 'Cambio di posizione', + 'Swimlane change' => 'Cambio di corsia', + 'Assignee change' => 'Cambio assegnatario', + '[%s] Overdue tasks' => '[%s] Task scaduti', + 'Notification' => 'Notifica', + '%s moved the task #%d to the first swimlane' => '%s ha spostato il task #%d nella prima corsia', + '%s moved the task #%d to the swimlane "%s"' => '%s ha spostato il task #%d nella corsia "%s"', + 'Swimlane' => 'Corsia', // 'Gravatar' => '', - // '%s moved the task %s to the first swimlane' => '', - // '%s moved the task %s to the swimlane "%s"' => '', - // 'This report contains all subtasks information for the given date range.' => '', - // 'This report contains all tasks information for the given date range.' => '', - // 'Project activities for %s' => '', - // 'view the board on Kanboard' => '', - // 'The task have been moved to the first swimlane' => '', - // 'The task have been moved to another swimlane:' => '', - // 'Overdue tasks for the project "%s"' => '', - // 'New title: %s' => '', - // 'The task is not assigned anymore' => '', - // 'New assignee: %s' => '', - // 'There is no category now' => '', - // 'New category: %s' => '', - // 'New color: %s' => '', - // 'New complexity: %d' => '', - // 'The due date have been removed' => '', - // 'There is no description anymore' => '', - // 'Recurrence settings have been modified' => '', - // 'Time spent changed: %sh' => '', - // 'Time estimated changed: %sh' => '', - // 'The field "%s" have been updated' => '', - // 'The description have been modified' => '', - // 'Do you really want to close the task "%s" as well as all subtasks?' => '', - // 'Swimlane: %s' => '', - // 'I want to receive notifications for:' => '', - // 'All tasks' => '', - // 'Only for tasks assigned to me' => '', - // 'Only for tasks created by me' => '', - // 'Only for tasks created by me and assigned to me' => '', + '%s moved the task %s to the first swimlane' => '%s ha spostato il task %s nella prima corsia', + '%s moved the task %s to the swimlane "%s"' => '%s ha spostato il task %s nella corsia %s', + 'This report contains all subtasks information for the given date range.' => 'Questo report contiente tutte le informazioni sui sotto-task nell\'arco temporale indicato.', + 'This report contains all tasks information for the given date range.' => 'Questo report contiente tutte le informazioni sui task nell\'arco temporale indicato.', + 'Project activities for %s' => 'Attività di progetto per %s', + 'view the board on Kanboard' => 'guarda la bacheca su Kanboard', + 'The task have been moved to the first swimlane' => 'Il task è stato spostato nella prima corsia', + 'The task have been moved to another swimlane:' => 'Il task è stato spostato in un\'altra corsia:', + 'Overdue tasks for the project "%s"' => 'Task scaduti per il progetto "%s"', + 'New title: %s' => 'Nuovo titolo: %s', + 'The task is not assigned anymore' => 'Il task non è più assegnato a nessuno', + 'New assignee: %s' => 'Nuovo assegnatario: %s', + 'There is no category now' => 'Non è presente più nessuna categoria', + 'New category: %s' => 'Nuova categoria: %s', + 'New color: %s' => 'Nuovo colorei: %s', + 'New complexity: %d' => 'Nuova complessità: %d', + 'The due date have been removed' => 'La data di scadenza è stata rimossa', + 'There is no description anymore' => 'Non è presente più alcuna descrizione.', + 'Recurrence settings have been modified' => 'Le impostazioni di ricorrenza sono state modificate', + 'Time spent changed: %sh' => 'Tempo trascorso modificato: %sh', + 'Time estimated changed: %sh' => 'Tempo stimato modificato: %sh', + 'The field "%s" have been updated' => 'Il campo %s è stato aggiornato', + 'The description have been modified' => 'La descrizione è stata modificata', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Vuoi veramente chiudere il task "%s" e i relativi sotto-task?', + 'Swimlane: %s' => 'Corsia: %s', + 'I want to receive notifications for:' => 'Voglio ricevere le notifiche per:', + 'All tasks' => 'Tutti i task', + 'Only for tasks assigned to me' => 'Solo per i task assegnati a me', + 'Only for tasks created by me' => 'Solo per i task creati da me', + 'Only for tasks created by me and assigned to me' => 'Solo per i task creati da me e assegnati a me', // '%A' => '', // '%b %e, %Y, %k:%M %p' => '', - // 'New due date: %B %e, %Y' => '', - // 'Start date changed: %B %e, %Y' => '', + 'New due date: %B %e, %Y' => 'Nuova data di scadenza: %B %e, %Y', + 'Start date changed: %B %e, %Y' => 'Data di inizio cambiata: %B %e, %Y', // '%k:%M %p' => '', // '%%Y-%%m-%%d' => '', - // 'Total for all columns' => '', - // 'You need at least 2 days of data to show the chart.' => '', + 'Total for all columns' => 'Totale per tutte le colonne', + 'You need at least 2 days of data to show the chart.' => 'Hai bisogno di almeno 2 giorni di dati per mostrare il grafico.', // '<15m' => '', // '<30m' => '', - // 'Stop timer' => '', - // 'Start timer' => '', - // 'Add project member' => '', - // 'Enable notifications' => '', - // 'My activity stream' => '', - // 'My calendar' => '', - // 'Search tasks' => '', - // 'Back to the calendar' => '', - // 'Filters' => '', - // 'Reset filters' => '', - // 'My tasks due tomorrow' => '', - // 'Tasks due today' => '', - // 'Tasks due tomorrow' => '', - // 'Tasks due yesterday' => '', - // 'Closed tasks' => '', - // 'Open tasks' => '', - // 'Not assigned' => '', - // 'View advanced search syntax' => '', - // 'Overview' => '', + 'Stop timer' => 'Ferma il timer', + 'Start timer' => 'Avvia il timer', + 'Add project member' => 'Aggiungi un membro di progetto', + 'Enable notifications' => 'Abilita notifiche', + 'My activity stream' => 'Il mio flusso di attività', + 'My calendar' => 'Il mio calendario', + 'Search tasks' => 'Ricerca task', + 'Back to the calendar' => 'Torna al calendario', + 'Filters' => 'Filtri', + 'Reset filters' => 'Annulla filtri', + 'My tasks due tomorrow' => 'I miei task da completare per domani', + 'Tasks due today' => 'Task da completare oggi', + 'Tasks due tomorrow' => 'Task da completare per domani', + 'Tasks due yesterday' => 'Task da completare ieri', + 'Closed tasks' => 'Task chiusi', + 'Open tasks' => 'Task aperti', + 'Not assigned' => 'Non assegnato', + 'View advanced search syntax' => 'Visualizza la sintassi di ricerca avanzata', + 'Overview' => 'Panoramica', // '%b %e %Y' => '', // 'Board/Calendar/List view' => '', - // 'Switch to the board view' => '', - // 'Switch to the calendar view' => '', - // 'Switch to the list view' => '', - // 'Go to the search/filter box' => '', - // 'There is no activity yet.' => '', - // 'No tasks found.' => '', - // 'Keyboard shortcut: "%s"' => '', - // 'List' => '', - // 'Filter' => '', - // 'Advanced search' => '', - // 'Example of query: ' => '', - // 'Search by project: ' => '', - // 'Search by column: ' => '', - // 'Search by assignee: ' => '', - // 'Search by color: ' => '', - // 'Search by category: ' => '', - // 'Search by description: ' => '', - // 'Search by due date: ' => '', - // 'Lead and Cycle time for "%s"' => '', - // 'Average time spent into each column for "%s"' => '', - // 'Average time spent into each column' => '', - // 'Average time spent' => '', - // 'This chart show the average time spent into each column for the last %d tasks.' => '', - // 'Average Lead and Cycle time' => '', - // 'Average lead time: ' => '', - // 'Average cycle time: ' => '', - // 'Cycle Time' => '', - // 'Lead Time' => '', - // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', - // 'Average time into each column' => '', - // 'Lead and cycle time' => '', - // 'Google Authentication' => '', - // 'Help on Google authentication' => '', - // 'Github Authentication' => '', - // 'Help on Github authentication' => '', - // 'Lead time: ' => '', - // 'Cycle time: ' => '', - // 'Time spent into each column' => '', - // 'The lead time is the duration between the task creation and the completion.' => '', - // 'The cycle time is the duration between the start date and the completion.' => '', - // 'If the task is not closed the current time is used instead of the completion date.' => '', - // 'Set automatically the start date' => '', - // 'Edit Authentication' => '', - // 'Google Id' => '', - // 'Github Id' => '', - // 'Remote user' => '', - // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', - // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', - // 'New remote user' => '', - // 'New local user' => '', - // 'Default task color' => '', - // 'Hide sidebar' => '', - // 'Expand sidebar' => '', - // 'This feature does not work with all browsers.' => '', - // 'There is no destination project available.' => '', - // 'Trigger automatically subtask time tracking' => '', - // 'Include closed tasks in the cumulative flow diagram' => '', - // 'Current swimlane: %s' => '', - // 'Current column: %s' => '', - // 'Current category: %s' => '', - // 'no category' => '', - // 'Current assignee: %s' => '', - // 'not assigned' => '', - // 'Author:' => '', - // 'contributors' => '', - // 'License:' => '', - // 'License' => '', - // 'Enter the text below' => '', - // 'Gantt chart for %s' => '', - // 'Sort by position' => '', - // 'Sort by date' => '', - // 'Add task' => '', - // 'Start date:' => '', - // 'Due date:' => '', - // 'There is no start date or due date for this task.' => '', - // 'Moving or resizing a task will change the start and due date of the task.' => '', - // 'There is no task in your project.' => '', - // 'Gantt chart' => '', - // 'People who are project managers' => '', - // 'People who are project members' => '', - // 'NOK - Norwegian Krone' => '', - // 'Show this column' => '', - // 'Hide this column' => '', - // 'open file' => '', - // 'End date' => '', - // 'Users overview' => '', + 'Switch to the board view' => 'Passa alla vista "bacheca"', + 'Switch to the calendar view' => 'Passa alla vista "calendario"', + 'Switch to the list view' => 'Passa alla vista "elenco"', + 'Go to the search/filter box' => 'Vai alla casella di ricerca/filtro', + 'There is no activity yet.' => 'Non è presente ancora nessuna attività.', + 'No tasks found.' => 'Nessun task trovato.', + 'Keyboard shortcut: "%s"' => 'Scorciatoia da tastiera: "%s"', + 'List' => 'Lista', + 'Filter' => 'Filtro', + 'Advanced search' => 'Riceca avanzata', + 'Example of query: ' => 'Esempio di query: ', + 'Search by project: ' => 'Ricerca per progetto: ', + 'Search by column: ' => 'Ricerca per colonna: ', + 'Search by assignee: ' => 'Ricerca per assegnatario: ', + 'Search by color: ' => 'Ricerca per colore: ', + 'Search by category: ' => 'Ricerca per categoria: ', + 'Search by description: ' => 'Ricerca per descrizione: ', + 'Search by due date: ' => 'Ricerca per data di scadenza: ', + 'Lead and Cycle time for "%s"' => 'Tempo di consegna (Lead Time) e lavorazione (Cycle Time) per "%s"', + 'Average time spent into each column for "%s"' => 'Tempo medio trascorso in ogni colonna per "%s"', + 'Average time spent into each column' => 'Tempo medio trascorso in ogni colonna', + 'Average time spent' => 'Tempo medio trascorso', + 'This chart show the average time spent into each column for the last %d tasks.' => 'Questo grafico mostra il tempo medio trascorso in ogni colonna per gli ultimi %d task.', + 'Average Lead and Cycle time' => 'Tempo medio di consegna (Lead Time) e lavorazione (Cycle Time)', + 'Average lead time: ' => 'Tempo medio di consegna (Lead Time): ', + 'Average cycle time: ' => 'Tempo medio di lavorazione (Cycle Time): ', + 'Cycle Time' => 'Tempo di lavorazione (Cycle Time)', + 'Lead Time' => 'Tempo di consegna (Lead Time)', + 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Questo grafico mostra i tempi medi di consegna (Lead Time) e lavorazione (Cycle Time) per gli ultimi %d task.', + 'Average time into each column' => 'Tempo medio in ogni colonna', + 'Lead and cycle time' => 'Tempo di consegna e lavorazione', + 'Lead time: ' => 'Tempo di consegna (Lead Time): ', + 'Cycle time: ' => 'Tempo di lavorazione (Cycle Time): ', + 'Time spent into each column' => 'Tempo trascorso in ogni colonna', + 'The lead time is the duration between the task creation and the completion.' => 'Il tempo di consegna (Lead Time) è la durata tra la creazione di un task ed il suo completamento.', + 'The cycle time is the duration between the start date and the completion.' => 'Il tempo di lavorazione (Cycle Time) è la durata tra la data di inzio della lavorazione di un task ed il suo completamento.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Se il task non è chiuso sarà usata la data attuale invece della data di completamento.', + 'Set automatically the start date' => 'Imposta automaticamente la data di inzio', + 'Edit Authentication' => 'Modifica Autenticazione', + 'Remote user' => 'Utente remoto', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'La password degli utenti remoti (ad esempio: LDAP, account Google e Github) non è salvata nel database di Kanboard', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Se imposti l\'opzione "Disabilita il form di login", le credenzali inserite nella saranno ignorate.', + 'New remote user' => 'Nuovo utente remoto', + 'New local user' => 'Nuovo utente locale', + 'Default task color' => 'Colore predefinito dei task', + 'Hide sidebar' => 'Nascondi barra laterale', + 'Expand sidebar' => 'Espandi barra laterale', + 'This feature does not work with all browsers.' => 'Questa feature non funziona con tutti i browser.', + 'There is no destination project available.' => 'Non ci sono progetti disponbili come destinazione.', + 'Trigger automatically subtask time tracking' => 'Attiva automaticamente il time-tracking per i sotto-task', + 'Include closed tasks in the cumulative flow diagram' => 'Includi i task chiusi nel diagramma di flusso cumulativo', + 'Current swimlane: %s' => 'Corsia attuale: %s', + 'Current column: %s' => 'Colonna attuale: %s', + 'Current category: %s' => 'Categoria attuale: %s', + 'no category' => 'nessuna categoria', + 'Current assignee: %s' => 'Assegnatario attuale: %s', + 'not assigned' => 'non assegnato', + 'Author:' => 'Autore', + 'contributors' => 'contributori', + 'License:' => 'Licenza:', + 'License' => 'Licenza', + 'Enter the text below' => 'Inserisci il testo qui sotto', + 'Gantt chart for %s' => 'Grafico Gantt per %s', + 'Sort by position' => 'Ordina per posizione', + 'Sort by date' => 'Ordina per data', + 'Add task' => 'Aggiungi task', + 'Start date:' => 'Data di inizio:', + 'Due date:' => 'Data di completamento:', + 'There is no start date or due date for this task.' => 'Nessuna data di inzio o di scadenza per questo task.', + 'Moving or resizing a task will change the start and due date of the task.' => 'Spostando o ridimensionado un task ne modifca la data di inzio e di scadenza.', + 'There is no task in your project.' => 'Non ci sono task nel tuo progetto.', + 'Gantt chart' => 'Grafici Gantt', + 'People who are project managers' => 'Persone che sono manager di progetto', + 'People who are project members' => 'Persone che sono membri di progetto', + 'NOK - Norwegian Krone' => 'NOK - Corone norvegesi', + 'Show this column' => 'Mostra questa colonna', + 'Hide this column' => 'Nascondi questa colonna', + 'open file' => 'apri file', + 'End date' => 'Data di fine', + 'Users overview' => 'Panoramica utenti', // 'Managers' => '', - // 'Members' => '', - // 'Shared project' => '', - // 'Project managers' => '', - // 'Gantt chart for all projects' => '', - // 'Projects list' => '', - // 'Gantt chart for this project' => '', - // 'Project board' => '', - // 'End date:' => '', - // 'There is no start date or end date for this project.' => '', - // 'Projects Gantt chart' => '', - // 'Start date: %s' => '', - // 'End date: %s' => '', - // 'Link type' => '', - // 'Change task color when using a specific task link' => '', - // 'Task link creation or modification' => '', - // 'Login with my Gitlab Account' => '', + 'Members' => 'Membri', + 'Shared project' => 'Progetto condiviso', + 'Project managers' => 'Manager del progetto', + 'Gantt chart for all projects' => 'Grafico Gantt per tutti i progetti', + 'Projects list' => 'Elenco progetti', + 'Gantt chart for this project' => 'Grafico Gantt per questo progetto', + 'Project board' => 'Bacheca del progetto', + 'End date:' => 'Data di fine:', + 'There is no start date or end date for this project.' => 'Non è prevista una data di inzio o fine per questo progetto.', + 'Projects Gantt chart' => 'Grafico Gantt dei progetti', + 'Start date: %s' => 'Data di inizio: %s', + 'End date: %s' => 'Data di fine: %s', + 'Link type' => 'Tipo di link', + 'Change task color when using a specific task link' => 'Cambia colore del task quando si un utilizza una determinata relazione di task', + 'Task link creation or modification' => 'Creazione o modifica di relazione di task', // 'Milestone' => '', - // 'Gitlab Authentication' => '', - // 'Help on Gitlab authentication' => '', - // 'Gitlab Id' => '', - // 'Gitlab Account' => '', - // 'Link my Gitlab Account' => '', - // 'Unlink my Gitlab Account' => '', - // 'Documentation: %s' => '', - // 'Switch to the Gantt chart view' => '', - // 'Reset the search/filter box' => '', - // 'Documentation' => '', - // 'Table of contents' => '', - // 'Gantt' => '', - // 'Author' => '', - // 'Version' => '', - // 'Plugins' => '', - // 'There is no plugin loaded.' => '', - // 'Set maximum column height' => '', - // 'Remove maximum column height' => '', - // 'My notifications' => '', - // 'Custom filters' => '', - // 'Your custom filter have been created successfully.' => '', - // 'Unable to create your custom filter.' => '', - // 'Custom filter removed successfully.' => '', - // 'Unable to remove this custom filter.' => '', - // 'Edit custom filter' => '', - // 'Your custom filter have been updated successfully.' => '', - // 'Unable to update custom filter.' => '', + 'Documentation: %s' => 'Documentazione: %s', + 'Switch to the Gantt chart view' => 'Passa alla vista Grafico Gantt', + 'Reset the search/filter box' => 'Resetta la riceca/filtro', + 'Documentation' => 'Documentazione', + 'Table of contents' => 'Indice dei contenuti', + 'Gantt' => 'Gantt', + 'Author' => 'Autore', + 'Version' => 'Versione', + 'Plugins' => 'Plugin', + 'There is no plugin loaded.' => 'Nessun plugin è stato caricato.', + 'Set maximum column height' => 'Imposta l\'altezza massima della colonna', + 'Remove maximum column height' => 'Rimuovi l\'altezza massima della colonna', + 'My notifications' => 'Le mie notifiche', + 'Custom filters' => 'Filtri personalizzati', + 'Your custom filter have been created successfully.' => 'Il filtro personalizzato è stato creato con successo.', + 'Unable to create your custom filter.' => 'Impossibile creare il filtro personalizzato.', + 'Custom filter removed successfully.' => 'Filtro personalizzato rimosso con successo.', + 'Unable to remove this custom filter.' => 'Impossibile rimuovere questo filtro personalizzato', + 'Edit custom filter' => 'Modifica il filtro personalizzato', + 'Your custom filter have been updated successfully.' => 'Il filtro personalizzato è stato aggiornato con successo.', + 'Unable to update custom filter.' => 'Impossibile aggiornare il filtro personalizzato.', // 'Web' => '', - // 'New attachment on task #%d: %s' => '', - // 'New comment on task #%d' => '', - // 'Comment updated on task #%d' => '', - // 'New subtask on task #%d' => '', - // 'Subtask updated on task #%d' => '', - // 'New task #%d: %s' => '', - // 'Task updated #%d' => '', - // 'Task #%d closed' => '', - // 'Task #%d opened' => '', - // 'Column changed for task #%d' => '', - // 'New position for task #%d' => '', - // 'Swimlane changed for task #%d' => '', - // 'Assignee changed on task #%d' => '', - // '%d overdue tasks' => '', - // 'Task #%d is overdue' => '', - // 'No new notifications.' => '', - // 'Mark all as read' => '', - // 'Mark as read' => '', - // 'Total number of tasks in this column across all swimlanes' => '', - // 'Collapse swimlane' => '', - // 'Expand swimlane' => '', - // 'Add a new filter' => '', - // 'Share with all project members' => '', - // 'Shared' => '', - // 'Owner' => '', - // 'Unread notifications' => '', - // 'My filters' => '', - // 'Notification methods:' => '', - // 'Import tasks from CSV file' => '', - // 'Unable to read your file' => '', - // '%d task(s) have been imported successfully.' => '', - // 'Nothing have been imported!' => '', - // 'Import users from CSV file' => '', - // '%d user(s) have been imported successfully.' => '', - // 'Comma' => '', - // 'Semi-colon' => '', - // 'Tab' => '', - // 'Vertical bar' => '', - // 'Double Quote' => '', - // 'Single Quote' => '', - // '%s attached a file to the task #%d' => '', - // 'There is no column or swimlane activated in your project!' => '', - // 'Append filter (instead of replacement)' => '', - // 'Append/Replace' => '', - // 'Append' => '', - // 'Replace' => '', - // 'Import' => '', - // 'change sorting' => '', - // 'Tasks Importation' => '', - // 'Delimiter' => '', + 'New attachment on task #%d: %s' => 'Nuovo allegato nel task #%d: %s', + 'New comment on task #%d' => 'Nuovo commento nel task #%d', + 'Comment updated on task #%d' => 'Commento aggiornato nel task #%d', + 'New subtask on task #%d' => 'Nuovo sotto-task nel task #%d', + 'Subtask updated on task #%d' => 'Sotto-task aggiornato nel task #%d', + 'New task #%d: %s' => 'Nuovo task #%d: %s', + 'Task updated #%d' => 'Task #%d aggiornato', + 'Task #%d closed' => 'Task #%d chiuso', + 'Task #%d opened' => 'Task #%d aperto', + 'Column changed for task #%d' => 'Colonna modificata per il task #%d', + 'New position for task #%d' => 'Nuova posizione per il task #%d', + 'Swimlane changed for task #%d' => 'Corsia modificata per il task #%d', + 'Assignee changed on task #%d' => 'Assegnatario modificato per il task #%d', + '%d overdue tasks' => '%d task scaduti', + 'Task #%d is overdue' => 'Il task #%d è scaduto', + 'No new notifications.' => 'Nessuna nuova notifica.', + 'Mark all as read' => 'Segna tutti come letti', + 'Mark as read' => 'Segna come letto', + 'Total number of tasks in this column across all swimlanes' => 'Numero totale di task in questa colonna per tutte le corsie', + 'Collapse swimlane' => 'Minimizza corsia', + 'Expand swimlane' => 'Espandi corsia', + 'Add a new filter' => 'Aggiungi un nuovo filtro', + 'Share with all project members' => 'Condividi con tutti i membri del progetto', + 'Shared' => 'Condiviso', + 'Owner' => 'Proprietario', + 'Unread notifications' => 'Notifiche non lette', + 'My filters' => 'I miei filtri', + 'Notification methods:' => 'Metodi di notifica', + 'Import tasks from CSV file' => 'Importa task da file CSV', + 'Unable to read your file' => 'Impossibile leggere il file', + '%d task(s) have been imported successfully.' => '%d task sono stati importati con sucesso.', + 'Nothing have been imported!' => 'Non è stato importato nulla!', + 'Import users from CSV file' => 'Importa utenti da file CSV', + '%d user(s) have been imported successfully.' => '%d utenti importati con successo.', + 'Comma' => 'Virgola', + 'Semi-colon' => 'Punto e virgola', + 'Tab' => 'Tabulazione', + 'Vertical bar' => 'Barra verticale', + 'Double Quote' => 'Apice singolo', + 'Single Quote' => 'Doppio apice', + '%s attached a file to the task #%d' => '%s ha allegato un file al task #%d', + 'There is no column or swimlane activated in your project!' => 'Non ci sono colonne o corsie attive all\'interno del tuo progetto!', + 'Append filter (instead of replacement)' => 'Aggiungi filtro (anzichè sostituirlo)', + 'Append/Replace' => 'Aggiungi/Sostituisci', + 'Append' => 'Aggiungi', + 'Replace' => 'Sostituisci', + 'Import' => 'Importa', + 'change sorting' => 'cambia ordinamento', + 'Tasks Importation' => 'Importazione task', + 'Delimiter' => 'Delimitatore', // 'Enclosure' => '', - // 'CSV File' => '', - // 'Instructions' => '', - // 'Your file must use the predefined CSV format' => '', - // 'Your file must be encoded in UTF-8' => '', - // 'The first row must be the header' => '', - // 'Duplicates are not verified for you' => '', - // 'The due date must use the ISO format: YYYY-MM-DD' => '', - // 'Download CSV template' => '', - // 'No external integration registered.' => '', - // 'Duplicates are not imported' => '', - // 'Usernames must be lowercase and unique' => '', - // 'Passwords will be encrypted if present' => '', - // '%s attached a new file to the task %s' => '', - // 'Assign automatically a category based on a link' => '', + 'CSV File' => 'File CSV', + 'Instructions' => 'Istruzioni', + 'Your file must use the predefined CSV format' => 'Il file deve rispettare il formato CSV predefinito', + 'Your file must be encoded in UTF-8' => 'Il file deve essere codificato in formato UTF-8', + 'The first row must be the header' => 'La prima riga deve contenere l\'intestazione', + 'Duplicates are not verified for you' => 'Le righe duplicate non verranno controllate', + 'The due date must use the ISO format: YYYY-MM-DD' => 'La data di scadenza deve usare il formato ISO: YYYY-MM-DD', + 'Download CSV template' => 'Scarica il template CSV', + 'No external integration registered.' => 'Nessuna integrazione esterna presente.', + 'Duplicates are not imported' => 'I duplicati non veranno importati', + 'Usernames must be lowercase and unique' => 'I nomi utente devono essere in minuscolo e univoci', + 'Passwords will be encrypted if present' => 'Se presenti, le password verranno criptate', + '%s attached a new file to the task %s' => '%s ha allegato un nuovo file al task %s', + 'Assign automatically a category based on a link' => 'Assegna automaticamente una categoria sulla base di una relazione', // 'BAM - Konvertible Mark' => '', - // 'Assignee Username' => '', - // 'Assignee Name' => '', - // 'Groups' => '', - // 'Members of %s' => '', - // 'New group' => '', - // 'Group created successfully.' => '', - // 'Unable to create your group.' => '', - // 'Edit group' => '', - // 'Group updated successfully.' => '', - // 'Unable to update your group.' => '', - // 'Add group member to "%s"' => '', - // 'Group member added successfully.' => '', - // 'Unable to add group member.' => '', - // 'Remove user from group "%s"' => '', - // 'User removed successfully from this group.' => '', - // 'Unable to remove this user from the group.' => '', - // 'Remove group' => '', - // 'Group removed successfully.' => '', - // 'Unable to remove this group.' => '', - // 'Project Permissions' => '', + 'Assignee Username' => 'Nome utente dell\'assegnatario', + 'Assignee Name' => 'Nome dell\'assegnatario', + 'Groups' => 'Gruppi', + 'Members of %s' => 'Membri di %s', + 'New group' => 'Nuovo gruppo', + 'Group created successfully.' => 'Gruppo creato con successo', + 'Unable to create your group.' => 'Impossibile creare il gruppo', + 'Edit group' => 'Modifica gruppo', + 'Group updated successfully.' => 'Gruppo aggiornato con sucesso.', + 'Unable to update your group.' => 'Impossibile aggiornare il gruppo', + 'Add group member to "%s"' => 'Aggiungi un membro al gruppo "%s"', + 'Group member added successfully.' => 'Membro del gruppo aggiunto con successo.', + 'Unable to add group member.' => 'Impossibile aggiungere un membro del gruppo', + 'Remove user from group "%s"' => 'Rimuovi utente dal gruppo "%s"', + 'User removed successfully from this group.' => 'Utente rimosso dal gruppo con successo.', + 'Unable to remove this user from the group.' => 'Impossibile rimuovere l\'utentei dal gruppo.', + 'Remove group' => 'Rimuovi gruppo', + 'Group removed successfully.' => 'Gruppo rimosso con sucesso.', + 'Unable to remove this group.' => 'Impossibile rimuovere questo gruppo.', + 'Project Permissions' => 'Permessi del progetto', // 'Manager' => '', - // 'Project Manager' => '', - // 'Project Member' => '', - // 'Project Viewer' => '', - // 'Your account is locked for %d minutes' => '', - // 'Invalid captcha' => '', - // 'The name must be unique' => '', - // 'View all groups' => '', - // 'View group members' => '', - // 'There is no user available.' => '', - // 'Do you really want to remove the user "%s" from the group "%s"?' => '', - // 'There is no group.' => '', - // 'External Id' => '', - // 'Add group member' => '', - // 'Do you really want to remove this group: "%s"?' => '', - // 'There is no user in this group.' => '', - // 'Remove this user' => '', - // 'Permissions' => '', - // 'Allowed Users' => '', - // 'No user have been allowed specifically.' => '', - // 'Role' => '', - // 'Enter user name...' => '', - // 'Allowed Groups' => '', - // 'No group have been allowed specifically.' => '', - // 'Group' => '', - // 'Group Name' => '', - // 'Enter group name...' => '', - // 'Role:' => '', - // 'Project members' => '', - // 'Compare hours for "%s"' => '', - // '%s mentioned you in the task #%d' => '', - // '%s mentioned you in a comment on the task #%d' => '', - // 'You were mentioned in the task #%d' => '', - // 'You were mentioned in a comment on the task #%d' => '', - // 'Mentioned' => '', - // 'Compare Estimated Time vs Actual Time' => '', - // 'Estimated hours: ' => '', - // 'Actual hours: ' => '', - // 'Hours Spent' => '', - // 'Hours Estimated' => '', - // 'Estimated Time' => '', - // 'Actual Time' => '', - // 'Estimated vs actual time' => '', - // 'RUB - Russian Ruble' => '', - // 'Assign the task to the person who does the action when the column is changed' => '', - // 'Close a task in a specific column' => '', - // 'Time-based One-time Password Algorithm' => '', + 'Project Manager' => 'Manager del progetto', + 'Project Member' => 'Membro del progetto', + 'Project Viewer' => 'Osservatore del progetto', + 'Your account is locked for %d minutes' => 'Il tuo account è bloccato per %d minuti', + 'Invalid captcha' => 'Captcha non valido', + 'The name must be unique' => 'Il nome deve essere univoco', + 'View all groups' => 'Visualiza tutti i gruppi', + 'View group members' => 'Visualizza i membri del gruppo', + 'There is no user available.' => 'Nessun utente disponibile.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Vuoi davvero rimuovere l\'utente "%s" dal gruppo "%s"?', + 'There is no group.' => 'Nessun gruppo presente', + 'External Id' => 'Id esterno', + 'Add group member' => 'Aggiungi un membro del gruppo', + 'Do you really want to remove this group: "%s"?' => 'Vuoi davvero rimuovere questo gruppo: "%s"?', + 'There is no user in this group.' => 'Nessun utente in questo gruppo.', + 'Remove this user' => 'Rimuovi questo utente', + 'Permissions' => 'Permessi', + 'Allowed Users' => 'Utenti autorizzati', + 'No user have been allowed specifically.' => 'Nessun utente è stato esplicitamente autorizzato.', + 'Role' => 'Ruolo', + 'Enter user name...' => 'Inserisci il nome utente...', + 'Allowed Groups' => 'Gruppi autorizzati', + 'No group have been allowed specifically.' => 'Nessun gruppo è stato esplicitamente autorizzato.', + 'Group' => 'Gruppo', + 'Group Name' => 'Nome del gruppo', + 'Enter group name...' => 'Inserisci il nome del gruppo...', + 'Role:' => 'Ruolo:', + 'Project members' => 'Membri del progetto', + 'Compare hours for "%s"' => 'Confronta le ore per "%s"', + '%s mentioned you in the task #%d' => '%s ti ha menzionato nel task #%d', + '%s mentioned you in a comment on the task #%d' => '%s ti ha menzionato in un commento del task #%d', + 'You were mentioned in the task #%d' => 'Sei stato menzionato nel task #%d', + 'You were mentioned in a comment on the task #%d' => 'Sei stato menzionato in un commento del task #%d', + 'Mentioned' => 'Menzionato', + 'Compare Estimated Time vs Actual Time' => 'Confronta il Tempo Stimato vs Tempo Effettivo', + 'Estimated hours: ' => 'Ore stimate: ', + 'Actual hours: ' => 'Ore effettive: ', + 'Hours Spent' => 'Ore impiegate', + 'Hours Estimated' => 'Ore stimate', + 'Estimated Time' => 'Tempo stimato', + 'Actual Time' => 'Tempo effettivo', + 'Estimated vs actual time' => 'Tempo stimato vs Tempo effettivo', + 'RUB - Russian Ruble' => 'RUB - Rublo russo', + 'Assign the task to the person who does the action when the column is changed' => 'Assegna il task alla persona che esegue l\'azione quando la colonna viene modificata', + 'Close a task in a specific column' => 'Chiudi un task in una specifica colonna', + 'Time-based One-time Password Algorithm' => 'Algoritmo per la Time-based One-time Password', // 'Two-Factor Provider: ' => '', - // 'Disable two-factor authentication' => '', - // 'Enable two-factor authentication' => '', - // 'There is no integration registered at the moment.' => '', - // 'Password Reset for Kanboard' => '', - // 'Forgot password?' => '', - // 'Enable "Forget Password"' => '', - // 'Password Reset' => '', - // 'New password' => '', - // 'Change Password' => '', - // 'To reset your password click on this link:' => '', - // 'Last Password Reset' => '', - // 'The password has never been reinitialized.' => '', - // 'Creation' => '', - // 'Expiration' => '', - // 'Password reset history' => '', - // 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '', - // 'Do you really want to close all tasks of this column?' => '', - // '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '', - // 'Close all tasks of this column' => '', - // 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '', - // 'My dashboard' => '', - // 'My profile' => '', - // 'Project owner: ' => '', - // 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '', - // 'Project owner' => '', - // 'Those dates are useful for the project Gantt chart.' => '', - // 'Private projects do not have users and groups management.' => '', - // 'There is no project member.' => '', - // 'Priority' => '', - // 'Task priority' => '', - // 'General' => '', - // 'Dates' => '', - // 'Default priority' => '', - // 'Lowest priority' => '', - // 'Highest priority' => '', - // 'If you put zero to the low and high priority, this feature will be disabled.' => '', - // 'Priority: %d' => '', + 'Disable two-factor authentication' => 'Disabilita l\'autenticazione "two-factor"', + 'Enable two-factor authentication' => 'Abilita l\'autenticazione "two-factor"', + 'There is no integration registered at the moment.' => 'Nessuna integrazione disponibile al momento.', + 'Password Reset for Kanboard' => 'Reimposta password per Kanboard', + 'Forgot password?' => 'Password dimenticata?', + 'Enable "Forget Password"' => 'Abilita funzione "Password dimenticata"', + 'Password Reset' => 'Reimposta password', + 'New password' => 'Nuova password', + 'Change Password' => 'Cambia password', + 'To reset your password click on this link:' => 'Per reimpostare la tua password clicca su questo link:', + 'Last Password Reset' => 'Ultimo cambio password', + 'The password has never been reinitialized.' => 'La password non è mai stata reimpostata.', + 'Creation' => 'Creazione', + 'Expiration' => 'Scadenza', + 'Password reset history' => 'Storico cambio password', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Tutti i task della colonna "%s" e della corsia "%s" sono stati chiusi con successo.', + 'Do you really want to close all tasks of this column?' => 'Vuoi veramente chiudere tutti i task di questa colonna?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d task della colonna "%s" e della corsia "%s" saranno chiusi.', + 'Close all tasks of this column' => 'Chiudi tutti i task di questa colonna', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Nessun plugin ha caricato un metodo di notifica di progetto. Puoi tuttavia configurare le notifiche personali dal tuo profilo utente.', + 'My dashboard' => 'La mia bacheca', + 'My profile' => 'Il mio profilo', + 'Project owner: ' => 'Proprietario del progetto: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'L\'identificativo del progetto è opzionale e deve essere alfanumerico, ad esempio: MIOPROGETTO.', + 'Project owner' => 'Proprietario del progetto', + 'Those dates are useful for the project Gantt chart.' => 'Le seguenti date sono utilizzate per i grafici Gantt di progetto.', + 'Private projects do not have users and groups management.' => 'Per i progetti privati non è prevista la gestione di utenti e gruppi.', + 'There is no project member.' => 'Non è impostato un membro del progetto.', + 'Priority' => 'Priorità', + 'Task priority' => 'Priorità del task', + 'General' => 'Generale', + 'Dates' => 'Date', + 'Default priority' => 'Priorità predefinita', + 'Lowest priority' => 'Priorità minima', + 'Highest priority' => 'Priorità massima', + 'If you put zero to the low and high priority, this feature will be disabled.' => 'Se imposti a zero la priorità massima e minima, questa funzionalità sarà disabilitata.', + 'Priority: %d' => 'Priorità: %d', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php index b9cde718..b95c960d 100644 --- a/app/Locale/ja_JP/translations.php +++ b/app/Locale/ja_JP/translations.php @@ -260,9 +260,6 @@ return array( // 'External authentication failed' => '', // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'Email', - 'Link my Google Account' => 'Google アカウントをリンクする', - 'Unlink my Google Account' => 'Google アカウントのリンクを解除する', - 'Login with my Google Account' => 'Google アカウントでログインする', 'Project not found.' => 'プロジェクトが見つかりません。', 'Task removed successfully.' => 'タスクを削除しました。', 'Unable to remove this task.' => 'タスクの削除に失敗しました。', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => '最大: ', 'Unable to upload the file.' => 'ファイルのアップロードに失敗しました。', 'Display another project' => '別のプロジェクトを表示', - 'Login with my Github Account' => 'Github アカウントでログインする', - 'Link my Github Account' => 'Github アカウントをリンクする', - 'Unlink my Github Account' => 'Github アカウントとのリンクを解除する', 'Created by %s' => '%s が作成', 'Last modified on %B %e, %Y at %k:%M %p' => ' %Y/%m/%d %H:%M に変更', 'Tasks Export' => 'タスクの出力', @@ -395,8 +389,6 @@ return array( 'Change password' => 'パスワードの変更', 'Password modification' => 'パスワードの変更', 'External authentications' => '外部認証', - 'Google Account' => 'Google アカウント', - 'Github Account' => 'Github アカウント', 'Never connected.' => '未接続。', 'No account linked.' => 'アカウントがリンクしていません。', 'Account linked.' => 'アカウントがリンクしました。', @@ -846,10 +838,6 @@ return array( // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', // 'Average time into each column' => '', // 'Lead and cycle time' => '', - // 'Google Authentication' => '', - // 'Help on Google authentication' => '', - // 'Github Authentication' => '', - // 'Help on Github authentication' => '', // 'Lead time: ' => '', // 'Cycle time: ' => '', // 'Time spent into each column' => '', @@ -858,8 +846,6 @@ return array( // 'If the task is not closed the current time is used instead of the completion date.' => '', // 'Set automatically the start date' => '', // 'Edit Authentication' => '', - // 'Google Id' => '', - // 'Github Id' => '', // 'Remote user' => '', // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', @@ -917,14 +903,7 @@ return array( // 'Link type' => '', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', - // 'Login with my Gitlab Account' => '', // 'Milestone' => '', - // 'Gitlab Authentication' => '', - // 'Help on Gitlab authentication' => '', - // 'Gitlab Id' => '', - // 'Gitlab Account' => '', - // 'Link my Gitlab Account' => '', - // 'Unlink my Gitlab Account' => '', // 'Documentation: %s' => '', // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/my_MY/translations.php b/app/Locale/my_MY/translations.php index 43c288c7..cbea7c16 100644 --- a/app/Locale/my_MY/translations.php +++ b/app/Locale/my_MY/translations.php @@ -260,9 +260,6 @@ return array( 'External authentication failed' => 'Otentifikasi eksternal gagal', 'Your external account is linked to your profile successfully.' => 'Akaun eksternal anda berhasil dihubungkan ke profil anda.', 'Email' => 'Email', - 'Link my Google Account' => 'Hubungkan Akaun Google saya', - 'Unlink my Google Account' => 'Putuskan Akaun Google saya', - 'Login with my Google Account' => 'Masuk menggunakan Akaun Google saya', 'Project not found.' => 'projek tidak ditemukan.', 'Task removed successfully.' => 'Tugas berhasil dihapus.', 'Unable to remove this task.' => 'Tidak dapat menghapus tugas ini.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Ukuran maksimum: ', 'Unable to upload the file.' => 'Tidak dapat mengunggah berkas.', 'Display another project' => 'Lihat projek lain', - 'Login with my Github Account' => 'Masuk menggunakan Akaun Github saya', - 'Link my Github Account' => 'Hubungkan Akaun Github saya ', - 'Unlink my Github Account' => 'Putuskan Akaun Github saya', 'Created by %s' => 'Dibuat oleh %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Modifikasi terakhir pada tanggal %d/%m/%Y à %H:%M', 'Tasks Export' => 'Ekspor Tugas', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Rubah kata sandri', 'Password modification' => 'Modifikasi kata laluan', 'External authentications' => 'Otentifikasi eksternal', - 'Google Account' => 'Akaun Google', - 'Github Account' => 'Akaun Github', 'Never connected.' => 'Tidak pernah terhubung.', 'No account linked.' => 'Tidak ada Akaun terhubung.', 'Account linked.' => 'Akaun terhubung.', @@ -846,10 +838,6 @@ return array( 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Grafik ini menunjukkan memimpin rata-rata dan waktu siklus untuk %d tugas terakhir dari waktu ke waktu.', 'Average time into each column' => 'Rata-rata waktu ke setiap kolom', 'Lead and cycle time' => 'Lead dan siklus waktu', - 'Google Authentication' => 'Google Otentifikasi', - 'Help on Google authentication' => 'Bantuan pada otentifikasi Google', - 'Github Authentication' => 'Otentifikasi Github', - 'Help on Github authentication' => 'Bantuan pada otentifikasi Github', 'Lead time: ' => 'Lead time : ', 'Cycle time: ' => 'Siklus waktu : ', 'Time spent into each column' => 'Waktu yang dihabiskan di setiap kolom', @@ -858,8 +846,6 @@ return array( 'If the task is not closed the current time is used instead of the completion date.' => 'Jika tugas tidak ditutup waktu saat ini yang digunakan sebagai pengganti tanggal penyelesaian.', 'Set automatically the start date' => 'Secara otomatis mengatur tanggal mulai', 'Edit Authentication' => 'Modifikasi Otentifikasi', - 'Google Id' => 'Id Google', - 'Github Id' => 'Id Github', 'Remote user' => 'Pengguna jauh', 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Pengguna jauh tidak menyimpan kata laluan mereka dalam basis data Kanboard, contoh: Akaun LDAP, Google dan Github.', 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Jika anda mencentang kotak "Larang formulir login", kredensial masuk ke formulis login akan diabaikan.', @@ -917,14 +903,7 @@ return array( 'Link type' => 'Jenis pautan', 'Change task color when using a specific task link' => 'Rubah warna tugas ketika menggunakan Pautan tugas yang spesifik', 'Task link creation or modification' => 'Pautan tugas pada penciptaan atau penyuntingan', - 'Login with my Gitlab Account' => 'Masuk menggunakan Akaun Gitlab saya', 'Milestone' => 'Batu Tanda', - 'Gitlab Authentication' => 'Otentifikasi Gitlab', - 'Help on Gitlab authentication' => 'Bantuan pada otentifikasi Gitlab', - 'Gitlab Id' => 'Id Gitlab', - 'Gitlab Account' => 'Akaun Gitlab', - 'Link my Gitlab Account' => 'Hubungkan akaun Gitlab saya', - 'Unlink my Gitlab Account' => 'Putuskan akaun Gitlab saya', 'Documentation: %s' => 'Dokumentasi : %s', 'Switch to the Gantt chart view' => 'Beralih ke tampilan Carta Gantt', 'Reset the search/filter box' => 'Tetap semula pencarian/saringan', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/nb_NO/translations.php b/app/Locale/nb_NO/translations.php index 682f44a8..28980448 100644 --- a/app/Locale/nb_NO/translations.php +++ b/app/Locale/nb_NO/translations.php @@ -260,9 +260,6 @@ return array( // 'External authentication failed' => '', // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'Epost', - 'Link my Google Account' => 'Knytt til min Google-konto', - 'Unlink my Google Account' => 'Fjern knytningen til min Google-konto', - 'Login with my Google Account' => 'Login med min Google-konto', 'Project not found.' => 'Prosjekt ikke funnet.', 'Task removed successfully.' => 'Oppgaven er fjernet.', 'Unable to remove this task.' => 'Oppgaven kunne ikke fjernes.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Maksimum størrelse: ', 'Unable to upload the file.' => 'Filen kunne ikke lastes opp.', 'Display another project' => 'Vis annet prosjekt...', - // 'Login with my Github Account' => '', - // 'Link my Github Account' => '', - // 'Unlink my Github Account' => '', 'Created by %s' => 'Opprettet av %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Sist endret %d.%m.%Y - %H:%M', 'Tasks Export' => 'Oppgave eksport', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Endre passord', 'Password modification' => 'Passordendring', 'External authentications' => 'Ekstern godkjenning', - 'Google Account' => 'Google-konto', - 'Github Account' => 'GitHub-konto', 'Never connected.' => 'Aldri innlogget.', 'No account linked.' => 'Ingen kontoer knyttet.', 'Account linked.' => 'Konto knyttet.', @@ -846,10 +838,6 @@ return array( // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', // 'Average time into each column' => '', // 'Lead and cycle time' => '', - // 'Google Authentication' => '', - // 'Help on Google authentication' => '', - // 'Github Authentication' => '', - // 'Help on Github authentication' => '', // 'Lead time: ' => '', // 'Cycle time: ' => '', // 'Time spent into each column' => '', @@ -858,8 +846,6 @@ return array( // 'If the task is not closed the current time is used instead of the completion date.' => '', // 'Set automatically the start date' => '', // 'Edit Authentication' => '', - // 'Google Id' => '', - // 'Github Id' => '', // 'Remote user' => '', // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', @@ -917,14 +903,7 @@ return array( 'Link type' => 'Relasjonstype', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', - // 'Login with my Gitlab Account' => '', 'Milestone' => 'Milepæl', - // 'Gitlab Authentication' => '', - // 'Help on Gitlab authentication' => '', - // 'Gitlab Id' => '', - // 'Gitlab Account' => '', - // 'Link my Gitlab Account' => '', - // 'Unlink my Gitlab Account' => '', 'Documentation: %s' => 'Dokumentasjon: %s', 'Switch to the Gantt chart view' => 'Gantt skjema visning', 'Reset the search/filter box' => 'Nullstill søk/filter', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/nl_NL/translations.php b/app/Locale/nl_NL/translations.php index 4f38f256..4d2dd0ea 100644 --- a/app/Locale/nl_NL/translations.php +++ b/app/Locale/nl_NL/translations.php @@ -260,9 +260,6 @@ return array( // 'External authentication failed' => '', // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'Email', - 'Link my Google Account' => 'Link mijn Google Account', - 'Unlink my Google Account' => 'Link met Google Account verwijderen', - 'Login with my Google Account' => 'Inloggen met mijn Google Account', 'Project not found.' => 'Project niet gevonden.', 'Task removed successfully.' => 'Taak succesvol verwijderd.', 'Unable to remove this task.' => 'Taak verwijderen niet gelukt.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Maximale grootte : ', 'Unable to upload the file.' => 'Uploaden van bestand niet gelukt.', 'Display another project' => 'Een ander project weergeven', - 'Login with my Github Account' => 'Login met mijn Github Account', - 'Link my Github Account' => 'Link met mijn Github', - 'Unlink my Github Account' => 'Link met mijn Github verwijderen', 'Created by %s' => 'Aangemaakt door %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Laatst gewijzigd op %d/%m/%Y à %H:%M', 'Tasks Export' => 'Taken exporteren', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Wachtwoord aanpassen', 'Password modification' => 'Wachtwoord aanpassen', 'External authentications' => 'Externe authenticatie', - 'Google Account' => 'Google Account', - 'Github Account' => 'Github Account', 'Never connected.' => 'Nooit verbonden.', 'No account linked.' => 'Geen account gelinkt.', 'Account linked.' => 'Account gelinkt.', @@ -846,10 +838,6 @@ return array( // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', // 'Average time into each column' => '', // 'Lead and cycle time' => '', - // 'Google Authentication' => '', - // 'Help on Google authentication' => '', - // 'Github Authentication' => '', - // 'Help on Github authentication' => '', // 'Lead time: ' => '', // 'Cycle time: ' => '', // 'Time spent into each column' => '', @@ -858,8 +846,6 @@ return array( // 'If the task is not closed the current time is used instead of the completion date.' => '', // 'Set automatically the start date' => '', // 'Edit Authentication' => '', - // 'Google Id' => '', - // 'Github Id' => '', // 'Remote user' => '', // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', @@ -917,14 +903,7 @@ return array( // 'Link type' => '', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', - // 'Login with my Gitlab Account' => '', // 'Milestone' => '', - // 'Gitlab Authentication' => '', - // 'Help on Gitlab authentication' => '', - // 'Gitlab Id' => '', - // 'Gitlab Account' => '', - // 'Link my Gitlab Account' => '', - // 'Unlink my Gitlab Account' => '', // 'Documentation: %s' => '', // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php index ee0ceb47..02c7d066 100644 --- a/app/Locale/pl_PL/translations.php +++ b/app/Locale/pl_PL/translations.php @@ -260,9 +260,6 @@ return array( // 'External authentication failed' => '', // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'Email', - 'Link my Google Account' => 'Połącz z kontem Google', - 'Unlink my Google Account' => 'Rozłącz z kontem Google', - 'Login with my Google Account' => 'Zaloguj przy pomocy konta Google', 'Project not found.' => 'Projek nieznaleziony.', 'Task removed successfully.' => 'Zadanie usunięto pomyślnie.', 'Unable to remove this task.' => 'Nie można usunąć tego zadania.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Maksymalny rozmiar: ', 'Unable to upload the file.' => 'Nie można wczytać pliku.', 'Display another project' => 'Wyświetl inny projekt', - 'Login with my Github Account' => 'Zaloguj przy użyciu konta Github', - 'Link my Github Account' => 'Podłącz konto Github', - 'Unlink my Github Account' => 'Odłącz konto Github', 'Created by %s' => 'Utworzone przez %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Ostatnio zmienione %e %B %Y o %k:%M', 'Tasks Export' => 'Eksport zadań', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Zmień hasło', 'Password modification' => 'Zmiana hasła', 'External authentications' => 'Autentykacja zewnętrzna', - 'Google Account' => 'Konto Google', - 'Github Account' => 'Konto Github', 'Never connected.' => 'Nigdy nie połączone.', 'No account linked.' => 'Brak połączonych kont.', 'Account linked.' => 'Konto połączone.', @@ -846,10 +838,6 @@ return array( // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', // 'Average time into each column' => '', // 'Lead and cycle time' => '', - // 'Google Authentication' => '', - // 'Help on Google authentication' => '', - // 'Github Authentication' => '', - // 'Help on Github authentication' => '', // 'Lead time: ' => '', // 'Cycle time: ' => '', // 'Time spent into each column' => '', @@ -858,8 +846,6 @@ return array( // 'If the task is not closed the current time is used instead of the completion date.' => '', // 'Set automatically the start date' => '', 'Edit Authentication' => 'Edycja autoryzacji', - // 'Google Id' => '', - // 'Github Id' => '', // 'Remote user' => '', // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', @@ -917,14 +903,7 @@ return array( 'Link type' => 'Typ adresu URL', 'Change task color when using a specific task link' => 'Zmień kolor zadania używając specjalnego adresu URL', 'Task link creation or modification' => 'Adres URL do utworzenia zadania lub modyfikacji', - // 'Login with my Gitlab Account' => '', 'Milestone' => 'Kamień milowy', - // 'Gitlab Authentication' => '', - // 'Help on Gitlab authentication' => '', - // 'Gitlab Id' => '', - // 'Gitlab Account' => '', - // 'Link my Gitlab Account' => '', - // 'Unlink my Gitlab Account' => '', // 'Documentation: %s' => '', // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php index 09e87048..91dde38d 100644 --- a/app/Locale/pt_BR/translations.php +++ b/app/Locale/pt_BR/translations.php @@ -260,9 +260,6 @@ return array( 'External authentication failed' => 'Autenticação externa falhou', 'Your external account is linked to your profile successfully.' => 'Sua conta externa está agora ligada ao seu perfil.', 'Email' => 'E-mail', - 'Link my Google Account' => 'Vincular minha Conta do Google', - 'Unlink my Google Account' => 'Desvincular minha Conta do Google', - 'Login with my Google Account' => 'Entrar com minha Conta do Google', 'Project not found.' => 'Projeto não encontrado.', 'Task removed successfully.' => 'Tarefa removida com sucesso.', 'Unable to remove this task.' => 'Não foi possível remover esta tarefa.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Tamanho máximo: ', 'Unable to upload the file.' => 'Não foi possível carregar o arquivo.', 'Display another project' => 'Exibir outro projeto', - 'Login with my Github Account' => 'Entrar com minha Conta do Github', - 'Link my Github Account' => 'Associar à minha Conta do Github', - 'Unlink my Github Account' => 'Desassociar a minha Conta do Github', 'Created by %s' => 'Criado por %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Última modificação em %B %e, %Y às %k: %M %p', 'Tasks Export' => 'Exportar Tarefas', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Alterar senha', 'Password modification' => 'Alteração de senha', 'External authentications' => 'Autenticação externa', - 'Google Account' => 'Conta do Google', - 'Github Account' => 'Conta do Github', 'Never connected.' => 'Nunca conectado.', 'No account linked.' => 'Nenhuma conta associada.', 'Account linked.' => 'Conta associada.', @@ -846,10 +838,6 @@ return array( 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Este gráfico mostra o tempo médio do Lead and cycle time das últimas %d tarefas.', 'Average time into each column' => 'Tempo médio de cada coluna', 'Lead and cycle time' => 'Lead and cycle time', - 'Google Authentication' => 'Autenticação Google', - 'Help on Google authentication' => 'Ajuda com a autenticação Google', - 'Github Authentication' => 'Autenticação Github', - 'Help on Github authentication' => 'Ajuda com a autenticação Github', 'Lead time: ' => 'Lead time: ', 'Cycle time: ' => 'Cycle time: ', 'Time spent into each column' => 'Tempo gasto em cada coluna', @@ -858,8 +846,6 @@ return array( 'If the task is not closed the current time is used instead of the completion date.' => 'Se a tarefa não está fechada, a hora atual é usada no lugar da data de conclusão.', 'Set automatically the start date' => 'Definir automaticamente a data de início', 'Edit Authentication' => 'Modificar a autenticação', - 'Google Id' => 'Google ID', - 'Github Id' => 'Github Id', 'Remote user' => 'Usuário remoto', 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Os usuários remotos não conservam as suas senhas no banco de dados Kanboard, exemplos: contas LDAP, Github ou Google.', 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Se você marcar "Interdir o formulário de autenticação", os identificadores entrados no formulário de login serão ignorado.', @@ -917,14 +903,7 @@ return array( 'Link type' => 'Tipo de link', 'Change task color when using a specific task link' => 'Mudar a cor da tarefa quando um link específico é utilizado', 'Task link creation or modification' => 'Criação ou modificação de um link em uma tarefa', - 'Login with my Gitlab Account' => 'Login com a minha conta Gitlab', 'Milestone' => 'Milestone', - 'Gitlab Authentication' => 'Autenticação Gitlab', - 'Help on Gitlab authentication' => 'Ajuda com a autenticação Gitlab', - 'Gitlab Id' => 'Gitlab Id', - 'Gitlab Account' => 'Conta Gitlab', - 'Link my Gitlab Account' => 'Vincular minha conta Gitlab', - 'Unlink my Gitlab Account' => 'Desvincular minha conta Gitlab', 'Documentation: %s' => 'Documentação: %s', 'Switch to the Gantt chart view' => 'Mudar para a vista gráfico de Gantt', 'Reset the search/filter box' => 'Reiniciar o campo de pesquisa', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/pt_PT/translations.php b/app/Locale/pt_PT/translations.php index 19c2ebf7..573ea05f 100644 --- a/app/Locale/pt_PT/translations.php +++ b/app/Locale/pt_PT/translations.php @@ -260,9 +260,6 @@ return array( 'External authentication failed' => 'Autenticação externa falhou', 'Your external account is linked to your profile successfully.' => 'A sua conta externa foi vinculada com sucesso ao seu perfil', 'Email' => 'E-mail', - 'Link my Google Account' => 'Vincular a minha Conta do Google', - 'Unlink my Google Account' => 'Desvincular a minha Conta do Google', - 'Login with my Google Account' => 'Entrar com a minha Conta do Google', 'Project not found.' => 'Projecto não encontrado.', 'Task removed successfully.' => 'Tarefa removida com sucesso.', 'Unable to remove this task.' => 'Não foi possível remover esta tarefa.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Tamanho máximo: ', 'Unable to upload the file.' => 'Não foi possível carregar o arquivo.', 'Display another project' => 'Mostrar outro projecto', - 'Login with my Github Account' => 'Entrar com a minha Conta do Github', - 'Link my Github Account' => 'Associar à minha Conta do Github', - 'Unlink my Github Account' => 'Desassociar a minha Conta do Github', 'Created by %s' => 'Criado por %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Última modificação em %B %e, %Y às %k: %M %p', 'Tasks Export' => 'Exportar Tarefas', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Alterar senha', 'Password modification' => 'Alteração de senha', 'External authentications' => 'Autenticação externa', - 'Google Account' => 'Conta do Google', - 'Github Account' => 'Conta do Github', 'Never connected.' => 'Nunca conectado.', 'No account linked.' => 'Nenhuma conta associada.', 'Account linked.' => 'Conta associada.', @@ -846,10 +838,6 @@ return array( 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Este gráfico mostra o tempo médio de espera e ciclo para as últimas %d tarefas realizadas.', 'Average time into each column' => 'Tempo médio em cada coluna', 'Lead and cycle time' => 'Tempo de Espera e Ciclo', - 'Google Authentication' => 'Autenticação Google', - 'Help on Google authentication' => 'Ajuda com autenticação Google', - 'Github Authentication' => 'Autenticação Github', - 'Help on Github authentication' => 'Ajuda com autenticação Github', 'Lead time: ' => 'Tempo de Espera: ', 'Cycle time: ' => 'Tempo de Ciclo: ', 'Time spent into each column' => 'Tempo gasto em cada coluna', @@ -858,8 +846,6 @@ return array( 'If the task is not closed the current time is used instead of the completion date.' => 'Se a tarefa não estiver fechada o hora actual será usada em vez da hora de conclusão', 'Set automatically the start date' => 'Definir data de inicio automáticamente', 'Edit Authentication' => 'Editar Autenticação', - 'Google Id' => 'Id Google', - 'Github Id' => 'Id Github', 'Remote user' => 'Utilizador remoto', 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Utilizadores remotos não guardam a password na base de dados do Kanboard, por exemplo: LDAP, contas do Google e Github.', 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Se activar a opção "Desactivar login", as credenciais digitadas no login serão ignoradas.', @@ -917,14 +903,7 @@ return array( 'Link type' => 'Tipo de ligação', 'Change task color when using a specific task link' => 'Alterar cor da tarefa quando se usar um tipo especifico de ligação de tarefa', 'Task link creation or modification' => 'Criação ou modificação de ligação de tarefa', - 'Login with my Gitlab Account' => 'Login com a minha Conta Gitlab', 'Milestone' => 'Objectivo', - 'Gitlab Authentication' => 'Autenticação Gitlab', - 'Help on Gitlab authentication' => 'Ajuda com autenticação Gitlab', - 'Gitlab Id' => 'Id Gitlab', - 'Gitlab Account' => 'Conta Gitlab', - 'Link my Gitlab Account' => 'Connectar a minha Conta Gitlab', - 'Unlink my Gitlab Account' => 'Desconectar a minha Conta Gitlab', 'Documentation: %s' => 'Documentação: %s', 'Switch to the Gantt chart view' => 'Mudar para vista de gráfico de Gantt', 'Reset the search/filter box' => 'Repor caixa de procura/filtro', @@ -1100,22 +1079,50 @@ return array( 'Do you really want to close all tasks of this column?' => 'Tem a certeza que quer fechar todas as tarefas nesta coluna?', '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tarefa(s) na coluna "%s" e na swimlane "%s" serão fechadas.', 'Close all tasks of this column' => 'Fechar todas as tarefas nesta coluna', - // 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '', - // 'My dashboard' => '', - // 'My profile' => '', - // 'Project owner: ' => '', - // 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '', - // 'Project owner' => '', - // 'Those dates are useful for the project Gantt chart.' => '', - // 'Private projects do not have users and groups management.' => '', - // 'There is no project member.' => '', - // 'Priority' => '', - // 'Task priority' => '', - // 'General' => '', - // 'Dates' => '', - // 'Default priority' => '', - // 'Lowest priority' => '', - // 'Highest priority' => '', - // 'If you put zero to the low and high priority, this feature will be disabled.' => '', - // 'Priority: %d' => '', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Nenhum plugin tem registado um método de notificação do projecto. Pode continuar a configurar notificações individuais no seu perfil de utilizador.', + 'My dashboard' => 'Meu painel', + 'My profile' => 'Meu perfil', + 'Project owner: ' => 'Dono do projecto: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'O identificador do projecto é opcional e tem de ser alfa-numerico, exemplo: MEUPROJECTO.', + 'Project owner' => 'Dono do projecto', + 'Those dates are useful for the project Gantt chart.' => 'Estas datas são uteis para o gráfico de Grantt do projecto.', + 'Private projects do not have users and groups management.' => 'Projectos privados não têm gestão de utilizadores nem de grupos.', + 'There is no project member.' => 'Não existe membro do projecto.', + 'Priority' => 'Prioridade', + 'Task priority' => 'Prioridade da Tarefa', + 'General' => 'Geral', + 'Dates' => 'Datas', + 'Default priority' => 'Prioridade por defeito', + 'Lowest priority' => 'Prioridade mais baixa', + 'Highest priority' => 'Prioridade mais alta', + 'If you put zero to the low and high priority, this feature will be disabled.' => 'Se colocar zero na prioridade baixa ou alta, essa funcionalidade será desactivada.', + 'Priority: %d' => 'Prioridade: %d', + 'Close a task when there is no activity' => 'Fechar tarefa quando não há actividade', + 'Duration in days' => 'Duração em dias', + 'Send email when there is no activity on a task' => 'Enviar email quando não há actividade numa tarefa', + 'List of external links' => 'Lista de ligações externas', + 'Unable to fetch link information.' => 'Impossivel obter informação da ligação.', + 'Daily background job for tasks' => 'Trabalho diário em segundo plano para tarefas', + 'Auto' => 'Auto', + 'Related' => 'Relacionado', + 'Attachment' => 'Anexo', + 'Title not found' => 'Titulo não encontrado', + 'Web Link' => 'Ligação Web', + 'External links' => 'Ligações externas', + 'Add external link' => 'Adicionar ligação externa', + 'Type' => 'Tipo', + 'Dependency' => 'Dependencia', + 'View internal links' => 'Ver ligações internas', + 'View external links' => 'Ver ligações externas', + 'Add internal link' => 'Adicionar ligação interna', + 'Add a new external link' => 'Adicionar nova ligação externa', + 'Edit external link' => 'Editar ligação externa', + 'External link' => 'Ligação externa', + 'Copy and paste your link here...' => 'Copie e cole a sua ligação aqui...', + 'URL' => 'URL', + 'There is no external link for the moment.' => 'De momento não existe nenhuma ligação externa.', + 'Internal links' => 'Ligações internas', + 'There is no internal link for the moment.' => 'De momento não existe nenhuma ligação interna.', + 'Assign to me' => 'Assignar a mim', + 'Me' => 'Eu', ); diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php index d09258ed..36155b5d 100644 --- a/app/Locale/ru_RU/translations.php +++ b/app/Locale/ru_RU/translations.php @@ -120,7 +120,7 @@ return array( 'The user id is required' => 'Необходим ID пользователя', 'Passwords don\'t match' => 'Пароли не совпадают', 'The confirmation is required' => 'Необходимо подтверждение', - 'The project is required' => 'Необъодимо указать проект', + 'The project is required' => 'Необходимо указать проект', 'The id is required' => 'Необходим ID', 'The project id is required' => 'Необходим ID проекта', 'The project name is required' => 'Необходимо имя проекта', @@ -177,7 +177,7 @@ return array( 'User' => 'Пользователь', 'Comments' => 'Комментарии', 'Write your text in Markdown' => 'Справка по синтаксису Markdown', - 'Leave a comment' => 'Оставить комментарий 2', + 'Leave a comment' => 'Оставить комментарий', 'Comment is required' => 'Нужен комментарий', 'Leave a description' => 'Напишите описание', 'Comment added successfully.' => 'Комментарий успешно добавлен.', @@ -189,7 +189,7 @@ return array( '%B %e, %Y' => '%B, %e, %Y', '%b %e, %Y' => '%b %e, %Y', 'Automatic actions' => 'Автоматические действия', - 'Your automatic action have been created successfully.' => 'Автоматика успешно настроена.', + 'Your automatic action have been created successfully.' => 'Автоматизированное действие успешно настроено.', 'Unable to create your automatic action.' => 'Не удалось создать автоматизированное действие.', 'Remove an action' => 'Удалить действие', 'Unable to remove this action.' => 'Не удалось удалить действие', @@ -260,9 +260,6 @@ return array( 'External authentication failed' => 'Внешняя авторизация не удалась', 'Your external account is linked to your profile successfully.' => 'Ваш внешний аккаунт успешно подключен к профилю.', 'Email' => 'E-mail', - 'Link my Google Account' => 'Привязать мой профиль к Google', - 'Unlink my Google Account' => 'Отвязать мой профиль от Google', - 'Login with my Google Account' => 'Аутентификация через Google', 'Project not found.' => 'Проект не найден.', 'Task removed successfully.' => 'Задача удалена.', 'Unable to remove this task.' => 'Не удалось удалить эту задачу.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Максимальный размер: ', 'Unable to upload the file.' => 'Не удалось загрузить файл.', 'Display another project' => 'Показать другой проект', - 'Login with my Github Account' => 'Аутентификация через Github', - 'Link my Github Account' => 'Привязать мой профиль к Github', - 'Unlink my Github Account' => 'Отвязать мой профиль от Github', 'Created by %s' => 'Создано %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Последнее изменение %d/%m/%Y в %H:%M', 'Tasks Export' => 'Экспорт задач', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Сменить пароль', 'Password modification' => 'Изменение пароля', 'External authentications' => 'Внешняя аутентификация', - 'Google Account' => 'Профиль Google', - 'Github Account' => 'Профиль Github', 'Never connected.' => 'Ранее не соединялось.', 'No account linked.' => 'Нет связанных профилей.', 'Account linked.' => 'Профиль связан.', @@ -839,17 +831,13 @@ return array( 'Average time spent' => 'Затрачено времени в среднем', 'This chart show the average time spent into each column for the last %d tasks.' => 'Эта диаграмма показывает среднее время, проведенное задачами в каждой колонке за последний %d.', 'Average Lead and Cycle time' => 'Среднее время выполнения и цикла', - 'Average lead time: ' => 'Среднее время выполнения', - 'Average cycle time: ' => 'Среднее время цикла', + 'Average lead time: ' => 'Среднее время выполнения: ', + 'Average cycle time: ' => 'Среднее время цикла: ', 'Cycle Time' => 'Время цикла', 'Lead Time' => 'Время выполнения', 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Эта диаграма показывает среднее время выполнения и цикла задачь в последние %d.', 'Average time into each column' => 'Среднее время в каждом столбце', 'Lead and cycle time' => 'Время выполнения и цикла', - 'Google Authentication' => 'Авторизация Google', - 'Help on Google authentication' => 'Помощь в авторизации Google', - 'Github Authentication' => 'Авторизация Github', - 'Help on Github authentication' => 'Помощь в авторизации Github', 'Lead time: ' => 'Время выполнения:', 'Cycle time: ' => 'Время цикла:', 'Time spent into each column' => 'Время, проведенное в каждой колонке', @@ -858,16 +846,14 @@ return array( 'If the task is not closed the current time is used instead of the completion date.' => 'Если задача не закрыта, то текущая дата будет указана в дате завершения задачи.', 'Set automatically the start date' => 'Установить автоматическую дату начала', 'Edit Authentication' => 'Редактировать авторизацию', - 'Google Id' => 'Google Id', - 'Github Id' => 'Github Id', 'Remote user' => 'Удаленный пользователь', 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Учетные данные для входа через LDAP, Google и Github не будут сохранены в Kanboard.', 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Если вы установите флажок "Запретить форму входа", учетные данные, введенные в форму входа будет игнорироваться.', 'New remote user' => 'Новый удаленный пользователь', 'New local user' => 'Новый локальный пользователь', 'Default task color' => 'Стандартные цвета задач', - 'Hide sidebar' => 'Свернуть сайдбар', - 'Expand sidebar' => 'Показать сайдбар', + 'Hide sidebar' => 'Свернуть боковое меню', + 'Expand sidebar' => 'Показать боковое меню', 'This feature does not work with all browsers.' => 'Эта функция доступна не во всех браузерах.', 'There is no destination project available.' => 'Нет доступного для назначения проекта.', 'Trigger automatically subtask time tracking' => 'Триггер автоматического отслеживания времени подзадач', @@ -876,14 +862,14 @@ return array( 'Current column: %s' => 'Текущая колонка: %s', 'Current category: %s' => 'Текущая категория: %s', 'no category' => 'без категории', - 'Current assignee: %s' => 'Current assignee: %s', + 'Current assignee: %s' => 'Текущее назначенное лицо: %s', 'not assigned' => 'не назначен', 'Author:' => 'Автор:', 'contributors' => 'соавторы', 'License:' => 'Лицензия:', 'License' => 'Лицензия', 'Enter the text below' => 'Введите текст ниже', - 'Gantt chart for %s' => 'Диаграмма Гантта для %s', + 'Gantt chart for %s' => 'Диаграмма Ганта для %s', 'Sort by position' => 'Сортировать по позиции', 'Sort by date' => 'Сортировать по дате', 'Add task' => 'Добавить задачу', @@ -892,7 +878,7 @@ return array( 'There is no start date or due date for this task.' => 'Для этой задачи нет даты начала или завершения.', 'Moving or resizing a task will change the start and due date of the task.' => 'Изменение или перемещение задачи повлечет изменение даты начала завершения задачи.', 'There is no task in your project.' => 'В Вашем проекте задач нет.', - 'Gantt chart' => 'Диаграмма Гантта', + 'Gantt chart' => 'Диаграмма Ганта', 'People who are project managers' => 'Люди, которые менеджеры проекта', 'People who are project members' => 'Люди, которые участники проекта', 'NOK - Norwegian Krone' => 'НК - Норвежская крона', @@ -905,32 +891,25 @@ return array( 'Members' => 'Участники', 'Shared project' => 'Общие/публичные проекты', 'Project managers' => 'Менеджер проекта', - 'Gantt chart for all projects' => 'Диаграмма Гантта для всех проектов', + 'Gantt chart for all projects' => 'Диаграмма Ганта для всех проектов', 'Projects list' => 'Список проектов', - 'Gantt chart for this project' => 'Диаграмма Гантта для этого проекта', + 'Gantt chart for this project' => 'Диаграмма Ганта для этого проекта', 'Project board' => 'Доска проекта', 'End date:' => 'Дата завершения:', 'There is no start date or end date for this project.' => 'В проекте не указаны дата начала или завершения.', - 'Projects Gantt chart' => 'Диаграмма Гантта проектов', + 'Projects Gantt chart' => 'Диаграмма Ганта проектов', 'Start date: %s' => 'Дата начала: %s', 'End date: %s' => 'Дата завершения: %s', 'Link type' => 'Тип ссылки', 'Change task color when using a specific task link' => 'Изменение цвета задач при использовании ссылки на определенные задачи', 'Task link creation or modification' => 'Ссылка на создание или модификацию задачи', - 'Login with my Gitlab Account' => 'Авторизоваться через аккаунт Gitlab', 'Milestone' => 'Веха', - 'Gitlab Authentication' => 'Авторизация через Gitlab', - 'Help on Gitlab authentication' => 'Помощь а авторизации через Gitlab', - 'Gitlab Id' => 'Gitlab Id', - 'Gitlab Account' => 'Аккаунт Gitlab', - 'Link my Gitlab Account' => 'Привязать аккаунт Gitlab', - 'Unlink my Gitlab Account' => 'Отвязать аккаунт Gitlab', 'Documentation: %s' => 'Документация: %s', - 'Switch to the Gantt chart view' => 'Переключиться в режим диаграммы Гантта', + 'Switch to the Gantt chart view' => 'Переключиться в режим диаграммы Ганта', 'Reset the search/filter box' => 'Сбросить поиск/фильтр', 'Documentation' => 'Документация', 'Table of contents' => 'Содержание', - 'Gantt' => 'Гантт', + 'Gantt' => 'Гант', 'Author' => 'Автор', 'Version' => 'Версия', 'Plugins' => 'Плагины', @@ -1040,8 +1019,8 @@ return array( 'Your account is locked for %d minutes' => 'Ваш аккаунт заблокирован на %d минут', 'Invalid captcha' => 'Неверный код подтверждения', 'The name must be unique' => 'Имя должно быть уникальным', - 'View all groups' => 'Просмотр всех группы', - 'View group members' => 'Просмотр всех группы участников группы', + 'View all groups' => 'Просмотр всех групп', + 'View group members' => 'Просмотр участников группы', 'There is no user available.' => 'Нет доступных пользователей.', 'Do you really want to remove the user "%s" from the group "%s"?' => 'Вы действительно хотите удалить пользователя "%s" из группы "%s"?', 'There is no group.' => 'Нет созданных групп.', @@ -1076,46 +1055,81 @@ return array( 'Estimated Time' => 'Запланировано времени', 'Actual Time' => 'Затрачено времени', 'Estimated vs actual time' => 'Запланировано и реально затрачено времени', - // 'RUB - Russian Ruble' => '', - // 'Assign the task to the person who does the action when the column is changed' => '', - // 'Close a task in a specific column' => '', - // 'Time-based One-time Password Algorithm' => '', - // 'Two-Factor Provider: ' => '', - // 'Disable two-factor authentication' => '', - // 'Enable two-factor authentication' => '', - // 'There is no integration registered at the moment.' => '', - // 'Password Reset for Kanboard' => '', - // 'Forgot password?' => '', - // 'Enable "Forget Password"' => '', - // 'Password Reset' => '', - // 'New password' => '', - // 'Change Password' => '', - // 'To reset your password click on this link:' => '', - // 'Last Password Reset' => '', - // 'The password has never been reinitialized.' => '', - // 'Creation' => '', - // 'Expiration' => '', - // 'Password reset history' => '', - // 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '', - // 'Do you really want to close all tasks of this column?' => '', - // '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '', - // 'Close all tasks of this column' => '', - // 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '', - // 'My dashboard' => '', - // 'My profile' => '', - // 'Project owner: ' => '', - // 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '', - // 'Project owner' => '', - // 'Those dates are useful for the project Gantt chart.' => '', - // 'Private projects do not have users and groups management.' => '', - // 'There is no project member.' => '', - // 'Priority' => '', - // 'Task priority' => '', - // 'General' => '', - // 'Dates' => '', - // 'Default priority' => '', - // 'Lowest priority' => '', - // 'Highest priority' => '', - // 'If you put zero to the low and high priority, this feature will be disabled.' => '', - // 'Priority: %d' => '', + 'RUB - Russian Ruble' => 'Руб - Российский рубль', + 'Assign the task to the person who does the action when the column is changed' => 'Назначить задачу пользователю, который произвел изменение в колонке', + 'Close a task in a specific column' => 'Закрыть задачу в выбранной колонке', + 'Time-based One-time Password Algorithm' => 'Зависимый от времени, одноразовый алгоритм пароля', + 'Two-Factor Provider: ' => 'Провайдер двух-факторной авторизации: ', + 'Disable two-factor authentication' => 'Отключить двух-факторную авторизацию', + 'Enable two-factor authentication' => 'Включить двух-факторную авторизацию', + 'There is no integration registered at the moment.' => 'Интеграции в данный момент не зарегистрированы.', + 'Password Reset for Kanboard' => 'Сброс пароля для Kanboard', + 'Forgot password?' => 'Забыли пароль?', + 'Enable "Forget Password"' => 'Включить возможность восстановления пароля', + 'Password Reset' => 'Сброс пароля', + 'New password' => 'Новый пароль', + 'Change Password' => 'Изменить пароль', + 'To reset your password click on this link:' => 'Чтобы изменить пароль нажмите на эту ссылку:', + 'Last Password Reset' => 'Последний сброс пароля', + 'The password has never been reinitialized.' => 'Пароль никогда не был сброшен.', + 'Creation' => 'Создан', + 'Expiration' => 'Истекает', + 'Password reset history' => 'История сброса пароля', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Все задачи для колонки "%s" и дорожки "%s" были успешно закрыты.', + 'Do you really want to close all tasks of this column?' => 'Вы действительно хотите закрыть все задачи из этой колонки?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d задач в колонке "%s" и дорожке "%s" будут закрыты.', + 'Close all tasks of this column' => 'Закрыть все задачи в этой колонке', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Нет плагинов уведомлений проекта. Вы можете настроить индивидуальные уведомления в вашем профиле пользователя.', + 'My dashboard' => 'Мой кабинет', + 'My profile' => 'Мой профиль', + 'Project owner: ' => 'Владелец проекта:', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Идентификатор проекта не обязателен и должен содержать буквенно-цифровые символы, пример: MYPROJECT', + 'Project owner' => 'Владелец проекта', + 'Those dates are useful for the project Gantt chart.' => 'Эти даты используются для диаграммы Ганта проекта.', + 'Private projects do not have users and groups management.' => 'Приватные проекты не имеют управления пользователями и группами.', + 'There is no project member.' => 'Нет участников проекта.', + 'Priority' => 'Приоритет', + 'Task priority' => 'Приоритет задачи', + 'General' => 'Общее', + 'Dates' => 'Даты', + 'Default priority' => 'Приоритет по-умолчанию', + 'Lowest priority' => 'Наименьший приоритет', + 'Highest priority' => 'Наивысший приоритет', + 'If you put zero to the low and high priority, this feature will be disabled.' => 'Если Вы введете 0 для наименьшего и наивысшего приоритета, этот функционал будет отключен.', + 'Priority: %d' => 'Приоритет: %d', + 'Close a task when there is no activity' => 'Закрывать задачу, когда нет активности', + 'Duration in days' => 'Длительность в днях', + 'Send email when there is no activity on a task' => 'Отправлять email, когда активность по задаче отсутствует', + 'List of external links' => 'Список внешних ссылок', + 'Unable to fetch link information.' => 'Не удалось получить информацию о ссылке', + 'Daily background job for tasks' => 'Ежедневные фоновые работы для задач', + 'Auto' => 'Авто', + 'Related' => 'Связано', + 'Attachment' => 'Вложение', + 'Title not found' => 'Заголовок не найден', + 'Web Link' => 'Web-ссылка', + 'External links' => 'Внешние ссылки', + 'Add external link' => 'Добавить внешнюю ссылку', + 'Type' => 'Тип', + 'Dependency' => 'Зависимость', + 'View internal links' => 'Просмотр внутренних ссылок', + 'View external links' => 'Просмотр внешних ссылок', + 'Add internal link' => 'Добавить внутреннюю ссылку', + 'Add a new external link' => 'Добавить новую внешнюю ссылку', + 'Edit external link' => 'Изменить внешнюю ссылку', + 'External link' => 'Внешняя ссылка', + 'Copy and paste your link here...' => 'Скопируйте и вставьте вашу ссылку здесь', + 'URL' => 'URL', + 'There is no external link for the moment.' => 'На данный момент внешние ссылки отсутствуют', + 'Internal links' => 'Внутренние ссылки', + 'There is no internal link for the moment.' => 'На данные момент внутреннии ссылки отсутствуют', + 'Assign to me' => 'Связать со мной', + 'Me' => 'Мне', + 'Do not duplicate anything' => 'Не дублировать ничего', + 'Projects management' => 'Управление проектами', + 'Users management' => 'Управление пользователями', + 'Groups management' => 'Управление группами', + 'Create from another project' => 'Создать из другого проекта', + 'View all sub-tasks' => 'Просмотр всех подзадач' + ); diff --git a/app/Locale/sr_Latn_RS/translations.php b/app/Locale/sr_Latn_RS/translations.php index 2b3553c2..cebe208e 100644 --- a/app/Locale/sr_Latn_RS/translations.php +++ b/app/Locale/sr_Latn_RS/translations.php @@ -260,9 +260,6 @@ return array( // 'External authentication failed' => '', // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'E-mail', - 'Link my Google Account' => 'Poveži sa Google nalogom', - 'Unlink my Google Account' => 'Ukini vezu sa Google nalogom', - 'Login with my Google Account' => 'Prijavi se preko Google naloga', 'Project not found.' => 'Projekat nije pronađen.', 'Task removed successfully.' => 'Zadatak uspešno uklonjen.', 'Unable to remove this task.' => 'Nemoguće uklanjanje zadatka.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Maksimalna veličina: ', 'Unable to upload the file.' => 'Nije moguće snimiti fajl.', 'Display another project' => 'Prikaži drugi projekat', - 'Login with my Github Account' => 'Zaloguj przy użyciu konta Github', - 'Link my Github Account' => 'Podłącz konto Github', - 'Unlink my Github Account' => 'Odłącz konto Github', 'Created by %s' => 'Kreirao %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Poslednja izmena %e %B %Y o %k:%M', 'Tasks Export' => 'Izvoz zadataka', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Izmeni lozinku', 'Password modification' => 'Izmena lozinke', 'External authentications' => 'Spoljne akcije', - 'Google Account' => 'Google nalog', - 'Github Account' => 'Github nalog', 'Never connected.' => 'Bez konekcija.', 'No account linked.' => 'Bez povezanih naloga.', 'Account linked.' => 'Nalog povezan.', @@ -846,10 +838,6 @@ return array( // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', // 'Average time into each column' => '', // 'Lead and cycle time' => '', - // 'Google Authentication' => '', - // 'Help on Google authentication' => '', - // 'Github Authentication' => '', - // 'Help on Github authentication' => '', // 'Lead time: ' => '', // 'Cycle time: ' => '', // 'Time spent into each column' => '', @@ -858,8 +846,6 @@ return array( // 'If the task is not closed the current time is used instead of the completion date.' => '', // 'Set automatically the start date' => '', // 'Edit Authentication' => '', - // 'Google Id' => '', - // 'Github Id' => '', // 'Remote user' => '', // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', @@ -917,14 +903,7 @@ return array( // 'Link type' => '', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', - // 'Login with my Gitlab Account' => '', // 'Milestone' => '', - // 'Gitlab Authentication' => '', - // 'Help on Gitlab authentication' => '', - // 'Gitlab Id' => '', - // 'Gitlab Account' => '', - // 'Link my Gitlab Account' => '', - // 'Unlink my Gitlab Account' => '', // 'Documentation: %s' => '', // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php index 1c01e94d..4b5b318d 100644 --- a/app/Locale/sv_SE/translations.php +++ b/app/Locale/sv_SE/translations.php @@ -260,9 +260,6 @@ return array( 'External authentication failed' => 'Extern autentisering misslyckades', 'Your external account is linked to your profile successfully.' => 'Ditt externa konto länkades till din profil.', 'Email' => 'Epost', - 'Link my Google Account' => 'Länka till mitt Google-konto', - 'Unlink my Google Account' => 'Ta bort länken till mitt Google-konto', - 'Login with my Google Account' => 'Logga in med mitt Google-konto', 'Project not found.' => 'Projektet kunde inte hittas', 'Task removed successfully.' => 'Uppgiften har tagits bort', 'Unable to remove this task.' => 'Kunde inte ta bort denna uppgift', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Maxstorlek: ', 'Unable to upload the file.' => 'Kunde inte ladda upp filen.', 'Display another project' => 'Visa ett annat projekt', - 'Login with my Github Account' => 'Logga in med mitt Github-konto', - 'Link my Github Account' => 'Anslut mitt Github-konto', - 'Unlink my Github Account' => 'Koppla ifrån mitt Github-konto', 'Created by %s' => 'Skapad av %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Senaste ändring %Y-%m-%d kl %H:%M', 'Tasks Export' => 'Exportera uppgifter', @@ -395,8 +389,6 @@ return array( 'Change password' => 'Byt lösenord', 'Password modification' => 'Ändra lösenord', 'External authentications' => 'Extern autentisering', - 'Google Account' => 'Googlekonto', - 'Github Account' => 'Githubkonto', 'Never connected.' => 'Inte ansluten.', 'No account linked.' => 'Inget konto länkat.', 'Account linked.' => 'Konto länkat.', @@ -846,10 +838,6 @@ return array( 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Diagramet visar medel av led och cykeltid för de senaste %d uppgifterna över tiden.', 'Average time into each column' => 'Medeltidsåtgång i varje kolumn', 'Lead and cycle time' => 'Led och cykeltid', - 'Google Authentication' => 'Google autentisering', - 'Help on Google authentication' => 'Hjälp för Google autentisering', - 'Github Authentication' => 'Github autentisering', - 'Help on Github authentication' => 'Hjälp för Github autentisering', 'Lead time: ' => 'Ledtid', 'Cycle time: ' => 'Cykeltid', 'Time spent into each column' => 'Tidsåtgång per kolumn', @@ -858,8 +846,6 @@ return array( 'If the task is not closed the current time is used instead of the completion date.' => 'Om uppgiften inte är stängd används nuvarande tid istället för slutförandedatum.', 'Set automatically the start date' => 'Sätt startdatum automatiskt', 'Edit Authentication' => 'Ändra autentisering', - 'Google Id' => 'Google Id', - 'Github Id' => 'Github Id', 'Remote user' => 'Extern användare', 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Externa användares lösenord lagras inte i Kanboard-databasen, exempel: LDAP, Google och Github-konton.', 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Om du aktiverar boxen "Tillåt inte loginformulär" kommer inloggningsuppgifter i formuläret att ignoreras.', @@ -917,14 +903,7 @@ return array( // 'Link type' => '', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', - // 'Login with my Gitlab Account' => '', // 'Milestone' => '', - // 'Gitlab Authentication' => '', - // 'Help on Gitlab authentication' => '', - // 'Gitlab Id' => '', - // 'Gitlab Account' => '', - // 'Link my Gitlab Account' => '', - // 'Unlink my Gitlab Account' => '', // 'Documentation: %s' => '', // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php index e0de5844..75808182 100644 --- a/app/Locale/th_TH/translations.php +++ b/app/Locale/th_TH/translations.php @@ -260,9 +260,6 @@ return array( // 'External authentication failed' => '', // 'Your external account is linked to your profile successfully.' => '', 'Email' => 'อีเมล', - 'Link my Google Account' => 'เชื่อมต่อกับกูเกิลแอคเคาท์', - 'Unlink my Google Account' => 'ไม่เชื่อมต่อกับกูเกิลแอคเคาท์', - 'Login with my Google Account' => 'เข้าใช้ด้วยกูเกิลแอคเคาท์', 'Project not found.' => 'หาโปรเจคไม่พบ', 'Task removed successfully.' => 'ลบงานเรียบร้อยแล้ว', 'Unable to remove this task.' => 'ไม่สามารถลบงานนี้', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'ขนาดสูงสุด:', 'Unable to upload the file.' => 'ไม่สามารถอัพโหลดไฟล์ได้', 'Display another project' => 'แสดงโปรเจคอื่น', - 'Login with my Github Account' => 'เข้าใช้ด้วยกิทฮับแอคเคาท์', - 'Link my Github Account' => 'เชื่อมกับกิทฮับแอคเคาท์', - 'Unlink my Github Account' => 'ยกเลิกการเชื่อมกับกิทอับแอคเคาท์', 'Created by %s' => 'สร้างโดย %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'แก้ไขล่าสุดวันที่ %B %e, %Y เวลา %k:%M %p', 'Tasks Export' => 'ส่งออกงาน', @@ -395,8 +389,6 @@ return array( 'Change password' => 'เปลี่ยนรหัสผ่าน', 'Password modification' => 'แก้ไขรหัสผ่าน', 'External authentications' => 'การยืนยันภายนอก', - 'Google Account' => 'กูเกิลแอคเคาท์', - 'Github Account' => 'กิทฮับแอคเคาท์', 'Never connected.' => 'ไม่เชื่อมต่อ', 'No account linked.' => 'แอคเคาท์ไม่มีการเชื่อม', 'Account linked.' => 'แอคเคาท์เชื่อมต่อแล้ว', @@ -846,10 +838,6 @@ return array( // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', // 'Average time into each column' => '', // 'Lead and cycle time' => '', - // 'Google Authentication' => '', - // 'Help on Google authentication' => '', - // 'Github Authentication' => '', - // 'Help on Github authentication' => '', // 'Lead time: ' => '', // 'Cycle time: ' => '', // 'Time spent into each column' => '', @@ -858,8 +846,6 @@ return array( // 'If the task is not closed the current time is used instead of the completion date.' => '', // 'Set automatically the start date' => '', // 'Edit Authentication' => '', - // 'Google Id' => '', - // 'Github Id' => '', // 'Remote user' => '', // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', @@ -917,14 +903,7 @@ return array( // 'Link type' => '', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', - // 'Login with my Gitlab Account' => '', // 'Milestone' => '', - // 'Gitlab Authentication' => '', - // 'Help on Gitlab authentication' => '', - // 'Gitlab Id' => '', - // 'Gitlab Account' => '', - // 'Link my Gitlab Account' => '', - // 'Unlink my Gitlab Account' => '', // 'Documentation: %s' => '', // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/tr_TR/translations.php b/app/Locale/tr_TR/translations.php index c4da560d..ebcc0527 100644 --- a/app/Locale/tr_TR/translations.php +++ b/app/Locale/tr_TR/translations.php @@ -260,9 +260,6 @@ return array( 'External authentication failed' => 'Harici hesap doğrulaması başarısız', 'Your external account is linked to your profile successfully.' => 'Harici hesabınız profilinizle 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 removed successfully.' => 'Görev başarıyla silindi.', 'Unable to remove this task.' => 'Görev silinemiyor.', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => 'Maksimum boyutu', 'Unable to upload the file.' => 'Dosya yüklenemiyor.', 'Display another project' => 'Başka bir proje göster', - 'Login with my Github Account' => 'Github hesabımla giriş yap', - 'Link my Github Account' => 'Github hesabını ilişkilendir', - 'Unlink my Github Account' => 'Github hesabıyla bağlantıyı kopar', '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', @@ -395,8 +389,6 @@ return array( '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ı.', @@ -846,10 +838,6 @@ return array( 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Bu grafik son %d görev için zaman içinde gerçekleşen ortalama teslim ve çevrim sürelerini gösterir.', 'Average time into each column' => 'Her bir sütunda ortalama zaman', 'Lead and cycle time' => 'Teslim ve çevrim süresi', - 'Google Authentication' => 'Google doğrulaması', - 'Help on Google authentication' => 'Google doğrulaması hakkında yardım', - 'Github Authentication' => 'Github doğrulaması', - 'Help on Github authentication' => 'Github doğrulaması hakkında yardım', 'Lead time: ' => 'Teslim süresi: ', 'Cycle time: ' => 'Çevrim süresi: ', 'Time spent into each column' => 'Her sütunda harcanan zaman', @@ -858,8 +846,6 @@ return array( 'If the task is not closed the current time is used instead of the completion date.' => 'Eğer görev henüz kapatılmamışsa, tamamlanma tarihi yerine şu anki tarih kullanılır.', 'Set automatically the start date' => 'Başlangıç tarihini otomatik olarak belirle', 'Edit Authentication' => 'Doğrulamayı düzenle', - 'Google Id' => 'Google kimliği', - 'Github Id' => 'Github Kimliği', 'Remote user' => 'Uzak kullanıcı', 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Uzak kullanıcıların şifreleri Kanboard veritabanında saklanmaz, örnek: LDAP, Google ve Github hesapları', 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Eğer giriş formuna erişimi engelleyi seçerseniz, giriş formuna girilen bilgiler gözardı edilir.', @@ -917,14 +903,7 @@ return array( 'Link type' => 'Bağlantı türü', 'Change task color when using a specific task link' => 'Belirli bir görev bağlantısı kullanıldığında görevin rengini değiştir', 'Task link creation or modification' => 'Görev bağlantısı oluşturulması veya değiştirilmesi', - 'Login with my Gitlab Account' => 'Gitlab hesabımla giriş yap', 'Milestone' => 'Kilometre taşı', - 'Gitlab Authentication' => 'Gitlab doğrulaması', - 'Help on Gitlab authentication' => 'Gitlab doğrulaması hakkında yardım', - 'Gitlab Id' => 'Gitlab kimliği', - 'Gitlab Account' => 'Gitlab hesabı', - 'Link my Gitlab Account' => 'Gitlab hesabını ilişkilendir', - 'Unlink my Gitlab Account' => 'Gitlab hesabımla bağlantıyı kopar', 'Documentation: %s' => 'Dokümantasyon: %s', 'Switch to the Gantt chart view' => 'Gantt diyagramı görünümüne geç', 'Reset the search/filter box' => 'Arama/Filtre kutusunu sıfırla', @@ -1118,4 +1097,37 @@ return array( // 'Highest priority' => '', // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Priority: %d' => '', + // 'Close a task when there is no activity' => '', + // 'Duration in days' => '', + // 'Send email when there is no activity on a task' => '', + // 'List of external links' => '', + // 'Unable to fetch link information.' => '', + // 'Daily background job for tasks' => '', + // 'Auto' => '', + // 'Related' => '', + // 'Attachment' => '', + // 'Title not found' => '', + // 'Web Link' => '', + // 'External links' => '', + // 'Add external link' => '', + // 'Type' => '', + // 'Dependency' => '', + // 'View internal links' => '', + // 'View external links' => '', + // 'Add internal link' => '', + // 'Add a new external link' => '', + // 'Edit external link' => '', + // 'External link' => '', + // 'Copy and paste your link here...' => '', + // 'URL' => '', + // 'There is no external link for the moment.' => '', + // 'Internal links' => '', + // 'There is no internal link for the moment.' => '', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php index e0b90b58..05852fa3 100644 --- a/app/Locale/zh_CN/translations.php +++ b/app/Locale/zh_CN/translations.php @@ -20,15 +20,15 @@ return array( 'Red' => '红色', 'Orange' => '橘色', 'Grey' => '灰色', - // 'Brown' => '', - // 'Deep Orange' => '', - // 'Dark Grey' => '', - // 'Pink' => '', - // 'Teal' => '', - // 'Cyan' => '', - // 'Lime' => '', - // 'Light Green' => '', - // 'Amber' => '', + 'Brown' => '褐色', + 'Deep Orange' => '橘红色', + 'Dark Grey' => '深灰色', + 'Pink' => '粉红色', + 'Teal' => '青色', + 'Cyan' => '蓝绿色', + 'Lime' => '绿黄色', + 'Light Green' => '浅绿色', + 'Amber' => '黄褐色', 'Save' => '保存', 'Login' => '登录', 'Official website:' => '官方网站:', @@ -40,7 +40,7 @@ return array( 'All users' => '所有用户', 'Username' => '用户名', 'Password' => '密码', - 'Administrator' => '管理员', + 'Administrator' => '超级管理员', 'Sign in' => '登录', 'Users' => '用户', 'No user' => '没有用户', @@ -103,13 +103,13 @@ return array( 'Do you really want to open this task: "%s"?' => '你确定要开这个任务吗?"%s"', 'Back to the board' => '回到看板', 'Created on %B %e, %Y at %k:%M %p' => '创建时间:%Y/%m/%d %H:%M', - 'There is nobody assigned' => '无人负责', + 'There is nobody assigned' => '当前无人被指派', 'Column on the board:' => '看板上的栏目:', 'Status is open' => '开启状态', 'Status is closed' => '关闭状态', 'Close this task' => '关闭该任务', 'Open this task' => '开启该任务', - 'There is no description.' => '无描述。', + 'There is no description.' => '当前没有描述。', 'Add a new task' => '添加新任务', 'The username is required' => '需要用户名', 'The maximum length is %d characters' => '最长%d个英文字符', @@ -241,7 +241,7 @@ return array( 'User agent' => '浏览器标识', 'Persistent connections' => '持续连接', 'No session.' => '无会话', - 'Expiration date' => '过期', + 'Expiration date' => '过期日期', 'Remember Me' => '记住我', 'Creation date' => '创建日期', 'Everybody' => '所有人', @@ -255,14 +255,11 @@ return array( '%d comments' => '%d个评论', '%d comment' => '%d个评论', 'Email address invalid' => '电子邮件地址无效', - // 'Your external account is not linked anymore to your profile.' => '', - // 'Unable to unlink your external account.' => '', - // 'External authentication failed' => '', - // 'Your external account is linked to your profile successfully.' => '', + 'Your external account is not linked anymore to your profile.' => '你的外部账户关联已解除', + 'Unable to unlink your external account.' => '无法关联到你的外部账户', + 'External authentication failed' => '外部认证失败', + 'Your external account is linked to your profile successfully.' => '你已成功关联到你的外部账户', 'Email' => '电子邮件', - 'Link my Google Account' => '关联我的google帐号', - 'Unlink my Google Account' => '去除我的google帐号关联', - 'Login with my Google Account' => '用我的google帐号登录', 'Project not found.' => '未发现项目', 'Task removed successfully.' => '任务成功去除', 'Unable to remove this task.' => '无法移除该任务。', @@ -327,9 +324,6 @@ return array( 'Maximum size: ' => '大小上限:', 'Unable to upload the file.' => '无法上传文件', 'Display another project' => '显示其它项目', - 'Login with my Github Account' => '用Github账号登录', - 'Link my Github Account' => '链接Github账号', - 'Unlink my Github Account' => '取消Github账号链接', 'Created by %s' => '创建者:%s', 'Last modified on %B %e, %Y at %k:%M %p' => '最后修改:%Y/%m/%d/ %H:%M', 'Tasks Export' => '任务导出', @@ -395,8 +389,6 @@ return array( 'Change password' => '修改密码', 'Password modification' => '修改密码', 'External authentications' => '外部认证', - 'Google Account' => '谷歌账号', - 'Github Account' => 'Github 账号', 'Never connected.' => '从未连接。', 'No account linked.' => '未链接账号。', 'Account linked.' => '已经链接账号。', @@ -472,7 +464,7 @@ return array( 'Started on %B %e, %Y' => '开始时间: %Y/%m/%d %H:%M ', 'Start date' => '启动日期', 'Time estimated' => '预计时间', - 'There is nothing assigned to you.' => '无任务指派给你。', + 'There is nothing assigned to you.' => '当前无任务指派给你。', 'My tasks' => '我的任务', 'Activity stream' => '动态记录', 'Dashboard' => '面板', @@ -522,25 +514,25 @@ return array( 'Nothing to preview...' => '没有需要预览的内容', 'Preview' => '预览', 'Write' => '书写', - 'Active swimlanes' => '活动泳道', - 'Add a new swimlane' => '添加新泳道', - 'Change default swimlane' => '修改默认泳道', - 'Default swimlane' => '默认泳道', - 'Do you really want to remove this swimlane: "%s"?' => '确定要删除泳道:"%s"?', - 'Inactive swimlanes' => '非活动泳道', - 'Remove a swimlane' => '删除泳道', + 'Active swimlanes' => '活动里程碑', + 'Add a new swimlane' => '添加新里程碑', + 'Change default swimlane' => '修改默认里程碑', + 'Default swimlane' => '默认里程碑', + 'Do you really want to remove this swimlane: "%s"?' => '确定要删除里程碑:"%s"?', + 'Inactive swimlanes' => '非活动里程碑', + 'Remove a swimlane' => '删除里程碑', 'Rename' => '重命名', - 'Show default swimlane' => '显示默认泳道', - 'Swimlane modification for the project "%s"' => '项目"%s"的泳道变更', - 'Swimlane not found.' => '未找到泳道。', - 'Swimlane removed successfully.' => '成功删除泳道', - 'Swimlanes' => '泳道', - 'Swimlane updated successfully.' => '成功更新了泳道。', - 'The default swimlane have been updated successfully.' => '成功更新了默认泳道。', - 'Unable to create your swimlane.' => '无法创建泳道。', - 'Unable to remove this swimlane.' => '无法删除此泳道', - 'Unable to update this swimlane.' => '无法更新此泳道', - 'Your swimlane have been created successfully.' => '已经成功创建泳道。', + 'Show default swimlane' => '显示默认里程碑', + 'Swimlane modification for the project "%s"' => '项目"%s"的里程碑变更', + 'Swimlane not found.' => '未找到里程碑。', + 'Swimlane removed successfully.' => '成功删除里程碑', + 'Swimlanes' => '里程碑', + 'Swimlane updated successfully.' => '成功更新了里程碑。', + 'The default swimlane have been updated successfully.' => '成功更新了默认里程碑。', + 'Unable to create your swimlane.' => '无法创建里程碑。', + 'Unable to remove this swimlane.' => '无法删除此里程碑', + 'Unable to update this swimlane.' => '无法更新此里程碑', + 'Your swimlane have been created successfully.' => '已经成功创建里程碑。', 'Example: "Bug, Feature Request, Improvement"' => '示例:“缺陷,功能需求,提升', 'Default categories for new projects (Comma-separated)' => '新项目的默认分类(用逗号分隔)', 'Integrations' => '整合', @@ -558,7 +550,7 @@ return array( 'Calendar' => '日程表', 'Next' => '前进', '#%d' => '#%d', - 'All swimlanes' => '全部泳道', + 'All swimlanes' => '全部里程碑', 'All colors' => '全部颜色', 'Moved to column %s' => '移动到栏目 %s', 'Change description' => '修改描述', @@ -567,11 +559,11 @@ return array( 'Edit column "%s"' => '编辑栏目"%s"', 'Select the new status of the subtask: "%s"' => '选择子任务的新状态:"%s"', 'Subtask timesheet' => '子任务时间', - 'There is nothing to show.' => '无内容。', + 'There is nothing to show.' => '当前无内容可展示。', 'Time Tracking' => '时间记录', 'You already have one subtask in progress' => '你已经有了一个进行中的子任务', 'Which parts of the project do you want to duplicate?' => '要复制项目的哪些内容?', - // 'Disallow login form' => '', + 'Disallow login form' => '禁止登陆', 'Start' => '开始', 'End' => '结束', 'Task age in days' => '任务存在天数', @@ -593,22 +585,22 @@ return array( 'Remove a link' => '删除关联', 'Task\'s links' => '任务的关联', 'The labels must be different' => '标签不能一样', - 'There is no link.' => '没有关联', + 'There is no link.' => '当前没有关联', 'This label must be unique' => '关联必须唯一', 'Unable to create your link.' => '无法创建关联。', 'Unable to update your link.' => '无法更新关联。', 'Unable to remove this link.' => '无法删除关联。', 'relates to' => '关联到', - // 'blocks' => '', - // 'is blocked by' => '', - // 'duplicates' => '', - // 'is duplicated by' => '', - // 'is a child of' => '', - // 'is a parent of' => '', - // 'targets milestone' => '', - // 'is a milestone of' => '', - // 'fixes' => '', - // 'is fixed by' => '', + 'blocks' => '阻塞', + 'is blocked by' => '阻塞于', + 'duplicates' => '重复', + 'is duplicated by' => '重复于', + 'is a child of' => '子任务自', + 'is a parent of' => '父任务于', + 'targets milestone' => '里程碑目标', + 'is a milestone of' => '属于里程碑', + 'fixes' => '修复', + 'is fixed by' => '修复于', 'This task' => '此任务', '<1h' => '<1h', '%dh' => '%dh', @@ -622,7 +614,7 @@ return array( 'Keyboard shortcuts' => '键盘快捷方式', 'Open board switcher' => '打开面板切换器', 'Application' => '应用程序', - // 'since %B %e, %Y at %k:%M %p' => '', + 'since %B %e, %Y at %k:%M %p' => '自从 %B %e, %Y %k:%M %p', 'Compact view' => '紧凑视图', 'Horizontal scrolling' => '水平滚动', 'Compact/wide view' => '紧凑/宽视图', @@ -631,18 +623,18 @@ return array( 'Files' => '文件', 'Images' => '图片', 'Private project' => '私人项目', - // 'AUD - Australian Dollar' => '', - // 'CAD - Canadian Dollar' => '', - // 'CHF - Swiss Francs' => '', + 'AUD - Australian Dollar' => '澳元', + 'CAD - Canadian Dollar' => '加元', + 'CHF - Swiss Francs' => '瑞士法郎', 'Custom Stylesheet' => '自定义样式表', 'download' => '下载', - // 'EUR - Euro' => '', - // 'GBP - British Pound' => '', - // 'INR - Indian Rupee' => '', - // 'JPY - Japanese Yen' => '', - // 'NZD - New Zealand Dollar' => '', - // 'RSD - Serbian dinar' => '', - // 'USD - US Dollar' => '', + 'EUR - Euro' => '欧元', + 'GBP - British Pound' => '英镑', + 'INR - Indian Rupee' => '印度卢比', + 'JPY - Japanese Yen' => '日元', + 'NZD - New Zealand Dollar' => '新西兰元', + 'RSD - Serbian dinar' => '第纳尔', + 'USD - US Dollar' => '美元', 'Destination column' => '目标栏目', 'Move the task to another column when assigned to a user' => '指定负责人时移动到其它栏目', 'Move the task to another column when assignee is cleared' => '移除负责人时移动到其它栏目', @@ -676,126 +668,126 @@ return array( 'Assign a color when the task is moved to a specific column' => '任务移动到指定栏目时设置颜色', '%s via Kanboard' => '%s 通过KanBoard', 'uploaded by: %s' => '由: %s 上传', - // 'uploaded on: %s' => '', - // 'size: %s' => '', - // 'Burndown chart for "%s"' => '', - // 'Burndown chart' => '', - // 'This chart show the task complexity over the time (Work Remaining).' => '', + 'uploaded on: %s' => '上传于 %s', + 'size: %s' => '大小:%s', + 'Burndown chart for "%s"' => '燃尽图:"%s"', + 'Burndown chart' => '燃尽图', + 'This chart show the task complexity over the time (Work Remaining).' => '图表显示任务随时间(剩余时间)的复杂度', 'Screenshot taken %s' => '已截图 %s', - // 'Add a screenshot' => '', - // 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => '', - // 'Screenshot uploaded successfully.' => '', - // 'SEK - Swedish Krona' => '', - // 'Identifier' => '', - // 'Disable two factor authentication' => '', - // 'Do you really want to disable the two factor authentication for this user: "%s"?' => '', + 'Add a screenshot' => '添加截图', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => '获取截图并按CTRL+V或者⌘+V粘贴到这里', + 'Screenshot uploaded successfully.' => '截图上传成功', + 'SEK - Swedish Krona' => '瑞郎', + 'Identifier' => '标识符', + 'Disable two factor authentication' => '禁用双重认证', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => '你真的要禁用 "%s" 的双重认证吗?', 'Edit link' => '编辑链接', - // 'Start to type task title...' => '', - // 'A task cannot be linked to itself' => '', - // 'The exact same link already exists' => '', - // 'Recurrent task is scheduled to be generated' => '', - // 'Recurring information' => '', - // 'Score' => '', - // 'The identifier must be unique' => '', - // 'This linked task id doesn\'t exists' => '', - // 'This value must be alphanumeric' => '', - // 'Edit recurrence' => '', - // 'Generate recurrent task' => '', - // 'Trigger to generate recurrent task' => '', - // 'Factor to calculate new due date' => '', - // 'Timeframe to calculate new due date' => '', - // 'Base date to calculate new due date' => '', - // 'Action date' => '', - // 'Base date to calculate new due date: ' => '', - // 'This task has created this child task: ' => '', - // 'Day(s)' => '', - // 'Existing due date' => '', - // 'Factor to calculate new due date: ' => '', - // 'Month(s)' => '', - // 'Recurrence' => '', - // 'This task has been created by: ' => '', - // 'Recurrent task has been generated:' => '', - // 'Timeframe to calculate new due date: ' => '', - // 'Trigger to generate recurrent task: ' => '', - // 'When task is closed' => '', - // 'When task is moved from first column' => '', - // 'When task is moved to last column' => '', - // 'Year(s)' => '', + 'Start to type task title...' => '输入任务标题...', + 'A task cannot be linked to itself' => '任务不能关联到本身', + 'The exact same link already exists' => '相同的关联已存在', + 'Recurrent task is scheduled to be generated' => '循环性任务将按计划生成', + 'Recurring information' => '循环信息', + 'Score' => '积分', + 'The identifier must be unique' => '标识符必须唯一', + 'This linked task id doesn\'t exists' => '关联任务不存在', + 'This value must be alphanumeric' => '此值必须是字母或者数字', + 'Edit recurrence' => '编辑循环周期', + 'Generate recurrent task' => '生成循环任务', + 'Trigger to generate recurrent task' => '生成循环任务的触发器', + 'Factor to calculate new due date' => '超期因素', + 'Timeframe to calculate new due date' => '计算超期时间表', + 'Base date to calculate new due date' => '计算超期基准日期', + 'Action date' => '操作日期', + 'Base date to calculate new due date: ' => '基准日期', + 'This task has created this child task: ' => '此任务创建了子任务:', + 'Day(s)' => '天', + 'Existing due date' => '已超期', + 'Factor to calculate new due date: ' => '超期因素', + 'Month(s)' => '月', + 'Recurrence' => '循环', + 'This task has been created by: ' => '此任务被谁创建:', + 'Recurrent task has been generated:' => '循环任务已生成:', + 'Timeframe to calculate new due date: ' => '计算超期时间表', + 'Trigger to generate recurrent task: ' => '生成循环任务的触发器', + 'When task is closed' => '当任务关闭时', + 'When task is moved from first column' => '当任务从第一列任务栏移走时', + 'When task is moved to last column' => '当任务移动到最后一列任务栏时', + 'Year(s)' => '年', 'Calendar settings' => '日程设置', - // 'Project calendar view' => '', + 'Project calendar view' => '项目日历表', 'Project settings' => '项目设置', - // 'Show subtasks based on the time tracking' => '', - // 'Show tasks based on the creation date' => '', - // 'Show tasks based on the start date' => '', - // 'Subtasks time tracking' => '', + 'Show subtasks based on the time tracking' => '在时间跟踪上显示子任务', + 'Show tasks based on the creation date' => '显示任务创建日期于', + 'Show tasks based on the start date' => '显示任务开始日期于', + 'Subtasks time tracking' => '子任务时间跟踪', 'User calendar view' => '用户日程视图', 'Automatically update the start date' => '自动更新开始日期', - // 'iCal feed' => '', + 'iCal feed' => '日历订阅', 'Preferences' => '偏好', 'Security' => '安全', - // 'Two factor authentication disabled' => '', - // 'Two factor authentication enabled' => '', - // 'Unable to update this user.' => '', - // 'There is no user management for private projects.' => '', - // 'User that will receive the email' => '', + 'Two factor authentication disabled' => '双重认证已禁用', + 'Two factor authentication enabled' => '双重认证已启用', + 'Unable to update this user.' => '无法更新此用户', + 'There is no user management for private projects.' => '私有项目下无用户可管理', + 'User that will receive the email' => '用户将收到邮件', 'Email subject' => '邮件主题', 'Date' => '日期', - // 'Add a comment log when moving the task between columns' => '', - // 'Move the task to another column when the category is changed' => '', - // 'Send a task by email to someone' => '', + 'Add a comment log when moving the task between columns' => '当任务移动到任务栏时添加评论日志', + 'Move the task to another column when the category is changed' => '当任务分类改变时移动到任务栏', + 'Send a task by email to someone' => '发送任务邮件到用户', 'Reopen a task' => '重新开始一个任务', - // 'Column change' => '', - // 'Position change' => '', - // 'Swimlane change' => '', - // 'Assignee change' => '', - // '[%s] Overdue tasks' => '', + 'Column change' => '任务栏改变', + 'Position change' => '位置改变', + 'Swimlane change' => '里程碑改变', + 'Assignee change' => '指派人改变', + '[%s] Overdue tasks' => '[%s] 超期任务', 'Notification' => '通知', - // '%s moved the task #%d to the first swimlane' => '', - // '%s moved the task #%d to the swimlane "%s"' => '', - // 'Swimlane' => '', - // 'Gravatar' => '', - // '%s moved the task %s to the first swimlane' => '', - // '%s moved the task %s to the swimlane "%s"' => '', - // 'This report contains all subtasks information for the given date range.' => '', - // 'This report contains all tasks information for the given date range.' => '', - // 'Project activities for %s' => '', - // 'view the board on Kanboard' => '', - // 'The task have been moved to the first swimlane' => '', - // 'The task have been moved to another swimlane:' => '', - // 'Overdue tasks for the project "%s"' => '', - // 'New title: %s' => '', - // 'The task is not assigned anymore' => '', - // 'New assignee: %s' => '', - // 'There is no category now' => '', - // 'New category: %s' => '', - // 'New color: %s' => '', - // 'New complexity: %d' => '', - // 'The due date have been removed' => '', - // 'There is no description anymore' => '', - // 'Recurrence settings have been modified' => '', - // 'Time spent changed: %sh' => '', - // 'Time estimated changed: %sh' => '', - // 'The field "%s" have been updated' => '', - // 'The description have been modified' => '', - // 'Do you really want to close the task "%s" as well as all subtasks?' => '', - // 'Swimlane: %s' => '', - // 'I want to receive notifications for:' => '', - // 'All tasks' => '', - // 'Only for tasks assigned to me' => '', - // 'Only for tasks created by me' => '', - // 'Only for tasks created by me and assigned to me' => '', + '%s moved the task #%d to the first swimlane' => '%s将任务#%d移动到了首个里程碑', + '%s moved the task #%d to the swimlane "%s"' => '%s将任务#%d移动到了里程碑"%s"下', + 'Swimlane' => '里程碑', + 'Gravatar' => 'Gravatar头像', + '%s moved the task %s to the first swimlane' => '%s将任务%s移动到了首个里程碑', + '%s moved the task %s to the swimlane "%s"' => '%s将任务%s移动到了里程碑"%s"下', + 'This report contains all subtasks information for the given date range.' => '该报告包含了指定日期范围内的所有子任务信息。', + 'This report contains all tasks information for the given date range.' => '该报告包含了指定日期范围内的所有任务信息。', + 'Project activities for %s' => '%s的项目活动记录', + 'view the board on Kanboard' => '在看板上查看面板', + 'The task have been moved to the first swimlane' => '该任务已被移动到首个里程碑', + 'The task have been moved to another swimlane:' => '该任务已被移动到别的里程碑:', + 'Overdue tasks for the project "%s"' => '"%s"项目下的超期任务', + 'New title: %s' => '新标题:%s', + 'The task is not assigned anymore' => '该任务没有指派人', + 'New assignee: %s' => '新指派到:%s', + 'There is no category now' => '当前没有分类', + 'New category: %s' => '新分类:%s', + 'New color: %s' => '新颜色:%s', + 'New complexity: %d' => '新的复杂度:%d', + 'The due date have been removed' => '超期时间已被移除', + 'There is no description anymore' => '当前没有描述', + 'Recurrence settings have been modified' => '循环周期已被更改', + 'Time spent changed: %sh' => '时间花费已变更:%sh', + 'Time estimated changed: %sh' => '时间预估已变更:%sh', + 'The field "%s" have been updated' => '"%s"字段已更新', + 'The description have been modified' => '描述已更改', + 'Do you really want to close the task "%s" as well as all subtasks?' => '你是否要移除所有子任务的同时父任务"%s"', + 'Swimlane: %s' => '里程碑:%s', + 'I want to receive notifications for:' => '我想接收以下相关通知:', + 'All tasks' => '所有任务', + 'Only for tasks assigned to me' => '所有指派给我的任务', + 'Only for tasks created by me' => '所有我创建的任务', + 'Only for tasks created by me and assigned to me' => '所有我创建的并且指派给我的任务', // '%A' => '', // '%b %e, %Y, %k:%M %p' => '', - // 'New due date: %B %e, %Y' => '', - // 'Start date changed: %B %e, %Y' => '', + 'New due date: %B %e, %Y' => '新的超期时间:%B %e, %Y', + 'Start date changed: %B %e, %Y' => '开始时间已变为:%B %e, %Y', // '%k:%M %p' => '', // '%%Y-%%m-%%d' => '', - // 'Total for all columns' => '', - // 'You need at least 2 days of data to show the chart.' => '', - // '<15m' => '', - // '<30m' => '', - 'Stop timer' => '停止定时器', - 'Start timer' => '开启定时器', + 'Total for all columns' => '所有栏目下的', + 'You need at least 2 days of data to show the chart.' => '当前柱状图至少需要2天的数据。', + '<15m' => '小于15分钟', + '<30m' => '小于30分钟', + 'Stop timer' => '停止计时器', + 'Start timer' => '开启计时器', 'Add project member' => '添加项目成员', 'Enable notifications' => '打开通知', 'My activity stream' => '我的活动流', @@ -819,25 +811,25 @@ return array( 'Switch to the calendar view' => '切换到日程视图', 'Switch to the list view' => '切换到列表视图', 'Go to the search/filter box' => '前往搜索/过滤箱', - 'There is no activity yet.' => '目前无任何活动.', + 'There is no activity yet.' => '当前无任何活动.', 'No tasks found.' => '没有找人任何任务.', 'Keyboard shortcut: "%s"' => '快捷键: "%s"', 'List' => '列表', 'Filter' => '过滤器', 'Advanced search' => '高级搜索', 'Example of query: ' => '查询示例: ', - 'Search by project: ' => '按项目搜索: ', - 'Search by column: ' => '按列搜索: ', - 'Search by assignee: ' => '按被指派人搜索: ', - 'Search by color: ' => '按颜色搜索: ', - // 'Search by category: ' => '', - // 'Search by description: ' => '', - // 'Search by due date: ' => '', + 'Search by project: ' => '按项目: ', + 'Search by column: ' => '按任务栏: ', + 'Search by assignee: ' => '按被指派人: ', + 'Search by color: ' => '按颜色: ', + 'Search by category: ' => '按分类:', + 'Search by description: ' => '按描述:', + 'Search by due date: ' => '按超期时间:', // 'Lead and Cycle time for "%s"' => '', - // 'Average time spent into each column for "%s"' => '', - // 'Average time spent into each column' => '', - // 'Average time spent' => '', - // 'This chart show the average time spent into each column for the last %d tasks.' => '', + 'Average time spent into each column for "%s"' => '"%s"在每个任务栏下平均花费时间', + 'Average time spent into each column' => '每个任务栏平均花费时间', + 'Average time spent' => '平均花费时间', + 'This chart show the average time spent into each column for the last %d tasks.' => '当前柱状图表示最新%d条任务在每个任务栏下的平均花费时间', // 'Average Lead and Cycle time' => '', // 'Average lead time: ' => '', // 'Average cycle time: ' => '', @@ -846,10 +838,6 @@ return array( // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', // 'Average time into each column' => '', // 'Lead and cycle time' => '', - // 'Google Authentication' => '', - // 'Help on Google authentication' => '', - // 'Github Authentication' => '', - // 'Help on Github authentication' => '', // 'Lead time: ' => '', // 'Cycle time: ' => '', // 'Time spent into each column' => '', @@ -857,24 +845,22 @@ return array( // 'The cycle time is the duration between the start date and the completion.' => '', // 'If the task is not closed the current time is used instead of the completion date.' => '', // 'Set automatically the start date' => '', - // 'Edit Authentication' => '', - // 'Google Id' => '', - // 'Github Id' => '', - // 'Remote user' => '', - // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', - // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', - // 'New remote user' => '', - // 'New local user' => '', + 'Edit Authentication' => '编辑认证信息', + 'Remote user' => '远程用户', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '远程用户不会在看板数据库保存密码,例如:LDAP,GOOGLE,GitHub。', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '如果选中“禁止登陆来自”,登陆表单内的验证信息将被忽略。', + 'New remote user' => '新加远程用户', + 'New local user' => '新加本地用户', 'Default task color' => '默认任务颜色', - // 'Hide sidebar' => '', - // 'Expand sidebar' => '', - // 'This feature does not work with all browsers.' => '', - // 'There is no destination project available.' => '', + 'Hide sidebar' => '隐藏侧边栏', + 'Expand sidebar' => '展开侧边栏', + 'This feature does not work with all browsers.' => '本功能只在部分浏览器下工作正常。', + 'There is no destination project available.' => '当前没有目标项目可用', 'Trigger automatically subtask time tracking' => '自动跟踪子任务时间', 'Include closed tasks in the cumulative flow diagram' => '在累计流程图中包含已关闭任务', - // 'Current swimlane: %s' => '', - // 'Current column: %s' => '', - // 'Current category: %s' => '', + 'Current swimlane: %s' => '当前里程碑:%s', + 'Current column: %s' => '当前任务栏:%s', + 'Current category: %s' => '当前分类:%s', 'no category' => '无分类', 'Current assignee: %s' => '当前被指派人: %s', 'not assigned' => '未指派', @@ -889,233 +875,259 @@ return array( 'Add task' => '添加任务', 'Start date:' => '开始日期', 'Due date:' => '到期日期', - // 'There is no start date or due date for this task.' => '', - // 'Moving or resizing a task will change the start and due date of the task.' => '', - // 'There is no task in your project.' => '', + 'There is no start date or due date for this task.' => '当前任务没有开始或结束时间。', + 'Moving or resizing a task will change the start and due date of the task.' => '移动或者重设任务将改变任务开始和结束时间。', + 'There is no task in your project.' => '当前项目还没有任务', 'Gantt chart' => '甘特图', - // 'People who are project managers' => '', - // 'People who are project members' => '', - // 'NOK - Norwegian Krone' => '', - // 'Show this column' => '', - // 'Hide this column' => '', - // 'open file' => '', + 'People who are project managers' => '项目管理员', + 'People who are project members' => '项目成员', + 'NOK - Norwegian Krone' => '克朗', + 'Show this column' => '显示任务栏', + 'Hide this column' => '隐藏任务栏', + 'open file' => '打开文件', 'End date' => '结束日期', 'Users overview' => '用户概览', - // 'Managers' => '', + 'Managers' => '管理员', 'Members' => '成员', - // 'Shared project' => '', - // 'Project managers' => '', - // 'Gantt chart for all projects' => '', - // 'Projects list' => '', - // 'Gantt chart for this project' => '', - // 'Project board' => '', - // 'End date:' => '', - // 'There is no start date or end date for this project.' => '', - // 'Projects Gantt chart' => '', - // 'Start date: %s' => '', - // 'End date: %s' => '', - // 'Link type' => '', - // 'Change task color when using a specific task link' => '', - // 'Task link creation or modification' => '', - // 'Login with my Gitlab Account' => '', - // 'Milestone' => '', - // 'Gitlab Authentication' => '', - // 'Help on Gitlab authentication' => '', - // 'Gitlab Id' => '', - // 'Gitlab Account' => '', - // 'Link my Gitlab Account' => '', - // 'Unlink my Gitlab Account' => '', - // 'Documentation: %s' => '', - // 'Switch to the Gantt chart view' => '', - // 'Reset the search/filter box' => '', - // 'Documentation' => '', - // 'Table of contents' => '', - // 'Gantt' => '', - // 'Author' => '', - // 'Version' => '', - // 'Plugins' => '', - // 'There is no plugin loaded.' => '', - // 'Set maximum column height' => '', - // 'Remove maximum column height' => '', - // 'My notifications' => '', - // 'Custom filters' => '', - // 'Your custom filter have been created successfully.' => '', - // 'Unable to create your custom filter.' => '', - // 'Custom filter removed successfully.' => '', - // 'Unable to remove this custom filter.' => '', - // 'Edit custom filter' => '', - // 'Your custom filter have been updated successfully.' => '', - // 'Unable to update custom filter.' => '', - // 'Web' => '', - // 'New attachment on task #%d: %s' => '', - // 'New comment on task #%d' => '', - // 'Comment updated on task #%d' => '', - // 'New subtask on task #%d' => '', - // 'Subtask updated on task #%d' => '', - // 'New task #%d: %s' => '', - // 'Task updated #%d' => '', - // 'Task #%d closed' => '', - // 'Task #%d opened' => '', - // 'Column changed for task #%d' => '', - // 'New position for task #%d' => '', - // 'Swimlane changed for task #%d' => '', - // 'Assignee changed on task #%d' => '', - // '%d overdue tasks' => '', - // 'Task #%d is overdue' => '', - // 'No new notifications.' => '', - // 'Mark all as read' => '', - // 'Mark as read' => '', - // 'Total number of tasks in this column across all swimlanes' => '', - // 'Collapse swimlane' => '', - // 'Expand swimlane' => '', - // 'Add a new filter' => '', - // 'Share with all project members' => '', - // 'Shared' => '', - // 'Owner' => '', - // 'Unread notifications' => '', - // 'My filters' => '', - // 'Notification methods:' => '', - // 'Import tasks from CSV file' => '', - // 'Unable to read your file' => '', - // '%d task(s) have been imported successfully.' => '', - // 'Nothing have been imported!' => '', - // 'Import users from CSV file' => '', - // '%d user(s) have been imported successfully.' => '', - // 'Comma' => '', - // 'Semi-colon' => '', - // 'Tab' => '', - // 'Vertical bar' => '', - // 'Double Quote' => '', - // 'Single Quote' => '', - // '%s attached a file to the task #%d' => '', - // 'There is no column or swimlane activated in your project!' => '', - // 'Append filter (instead of replacement)' => '', - // 'Append/Replace' => '', - // 'Append' => '', - // 'Replace' => '', - // 'Import' => '', - // 'change sorting' => '', - // 'Tasks Importation' => '', - // 'Delimiter' => '', - // 'Enclosure' => '', - // 'CSV File' => '', - // 'Instructions' => '', - // 'Your file must use the predefined CSV format' => '', - // 'Your file must be encoded in UTF-8' => '', - // 'The first row must be the header' => '', - // 'Duplicates are not verified for you' => '', - // 'The due date must use the ISO format: YYYY-MM-DD' => '', - // 'Download CSV template' => '', - // 'No external integration registered.' => '', - // 'Duplicates are not imported' => '', - // 'Usernames must be lowercase and unique' => '', - // 'Passwords will be encrypted if present' => '', - // '%s attached a new file to the task %s' => '', - // 'Assign automatically a category based on a link' => '', - // 'BAM - Konvertible Mark' => '', - // 'Assignee Username' => '', - // 'Assignee Name' => '', - // 'Groups' => '', - // 'Members of %s' => '', - // 'New group' => '', - // 'Group created successfully.' => '', - // 'Unable to create your group.' => '', - // 'Edit group' => '', - // 'Group updated successfully.' => '', - // 'Unable to update your group.' => '', - // 'Add group member to "%s"' => '', - // 'Group member added successfully.' => '', - // 'Unable to add group member.' => '', - // 'Remove user from group "%s"' => '', - // 'User removed successfully from this group.' => '', - // 'Unable to remove this user from the group.' => '', - // 'Remove group' => '', - // 'Group removed successfully.' => '', - // 'Unable to remove this group.' => '', - // 'Project Permissions' => '', - // 'Manager' => '', - // 'Project Manager' => '', - // 'Project Member' => '', - // 'Project Viewer' => '', - // 'Your account is locked for %d minutes' => '', - // 'Invalid captcha' => '', - // 'The name must be unique' => '', - // 'View all groups' => '', - // 'View group members' => '', - // 'There is no user available.' => '', - // 'Do you really want to remove the user "%s" from the group "%s"?' => '', - // 'There is no group.' => '', - // 'External Id' => '', - // 'Add group member' => '', - // 'Do you really want to remove this group: "%s"?' => '', - // 'There is no user in this group.' => '', - // 'Remove this user' => '', - // 'Permissions' => '', - // 'Allowed Users' => '', - // 'No user have been allowed specifically.' => '', - // 'Role' => '', - // 'Enter user name...' => '', - // 'Allowed Groups' => '', - // 'No group have been allowed specifically.' => '', - // 'Group' => '', - // 'Group Name' => '', - // 'Enter group name...' => '', - // 'Role:' => '', - // 'Project members' => '', - // 'Compare hours for "%s"' => '', - // '%s mentioned you in the task #%d' => '', - // '%s mentioned you in a comment on the task #%d' => '', - // 'You were mentioned in the task #%d' => '', - // 'You were mentioned in a comment on the task #%d' => '', - // 'Mentioned' => '', - // 'Compare Estimated Time vs Actual Time' => '', - // 'Estimated hours: ' => '', - // 'Actual hours: ' => '', - // 'Hours Spent' => '', - // 'Hours Estimated' => '', - // 'Estimated Time' => '', - // 'Actual Time' => '', - // 'Estimated vs actual time' => '', - // 'RUB - Russian Ruble' => '', - // 'Assign the task to the person who does the action when the column is changed' => '', - // 'Close a task in a specific column' => '', - // 'Time-based One-time Password Algorithm' => '', - // 'Two-Factor Provider: ' => '', - // 'Disable two-factor authentication' => '', - // 'Enable two-factor authentication' => '', - // 'There is no integration registered at the moment.' => '', - // 'Password Reset for Kanboard' => '', - // 'Forgot password?' => '', - // 'Enable "Forget Password"' => '', - // 'Password Reset' => '', - // 'New password' => '', - // 'Change Password' => '', - // 'To reset your password click on this link:' => '', - // 'Last Password Reset' => '', - // 'The password has never been reinitialized.' => '', - // 'Creation' => '', - // 'Expiration' => '', - // 'Password reset history' => '', - // 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '', - // 'Do you really want to close all tasks of this column?' => '', - // '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '', - // 'Close all tasks of this column' => '', - // 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '', - // 'My dashboard' => '', - // 'My profile' => '', - // 'Project owner: ' => '', - // 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '', - // 'Project owner' => '', - // 'Those dates are useful for the project Gantt chart.' => '', - // 'Private projects do not have users and groups management.' => '', - // 'There is no project member.' => '', - // 'Priority' => '', - // 'Task priority' => '', - // 'General' => '', - // 'Dates' => '', - // 'Default priority' => '', - // 'Lowest priority' => '', - // 'Highest priority' => '', - // 'If you put zero to the low and high priority, this feature will be disabled.' => '', - // 'Priority: %d' => '', + 'Shared project' => '公开项目', + 'Project managers' => '项目管理员', + 'Gantt chart for all projects' => '所有项目的甘特图', + 'Projects list' => '项目列表', + 'Gantt chart for this project' => '此项目的甘特图', + 'Project board' => '项目面板', + 'End date:' => '结束日期', + 'There is no start date or end date for this project.' => '当前项目没有开始或结束日期', + 'Projects Gantt chart' => '项目甘特图', + 'Start date: %s' => '开始日期%s', + 'End date: %s' => '结束日期%s', + 'Link type' => '关联类型', + 'Change task color when using a specific task link' => '当任务关联到指定任务时改变颜色', + 'Task link creation or modification' => '任务链接创建或更新时间', + 'Milestone' => '里程碑', + 'Documentation: %s' => '文档:%s', + 'Switch to the Gantt chart view' => '切换到甘特图', + 'Reset the search/filter box' => '重置搜索/过滤框', + 'Documentation' => '文档', + 'Table of contents' => '表内容', + 'Gantt' => '甘特图', + 'Author' => '作者', + 'Version' => '版本', + 'Plugins' => '插件', + 'There is no plugin loaded.' => '当前没有插件载入', + 'Set maximum column height' => '设置任务栏最大高度', + 'Remove maximum column height' => '移除任务栏最大高度', + 'My notifications' => '我的通知', + 'Custom filters' => '自定义过滤器', + 'Your custom filter have been created successfully.' => '成功创建过滤器', + 'Unable to create your custom filter.' => '无法创建过滤器', + 'Custom filter removed successfully.' => '成功删除过滤器', + 'Unable to remove this custom filter.' => '无法删除这个过滤器', + 'Edit custom filter' => '编辑过滤器', + 'Your custom filter have been updated successfully.' => '你的过滤器更新成功', + 'Unable to update custom filter.' => '无法更新过滤器', + 'Web' => 'web', + 'New attachment on task #%d: %s' => '任务#%d下的新附件:%s', + 'New comment on task #%d' => '任务#%d下的新评论', + 'Comment updated on task #%d' => '任务#%d的评论已更新', + 'New subtask on task #%d' => '任务#%d下新的子任务', + 'Subtask updated on task #%d' => '任务#%d下的子任务已更新', + 'New task #%d: %s' => '新任务#%d:%s', + 'Task updated #%d' => '任务#%d已更新', + 'Task #%d closed' => '任务#%d已关闭', + 'Task #%d opened' => '任务#%d已打开', + 'Column changed for task #%d' => '任务#%d的任务栏已改变', + 'New position for task #%d' => '任务#%d的新状态', + 'Swimlane changed for task #%d' => '任务#%d的里程碑已改变', + 'Assignee changed on task #%d' => '任务#%d的指派人已改变', + '%d overdue tasks' => '%d条超期任务', + 'Task #%d is overdue' => '任务#%d已超期', + 'No new notifications.' => '没有新通知', + 'Mark all as read' => '标记所有为已读', + 'Mark as read' => '标记为已读', + 'Total number of tasks in this column across all swimlanes' => '此任务栏下的任务数(跨里程碑)', + 'Collapse swimlane' => '收起里程碑', + 'Expand swimlane' => '展开里程碑', + 'Add a new filter' => '添加新过滤器', + 'Share with all project members' => '对项目所有成员共享', + 'Shared' => '共享', + 'Owner' => '所有人', + 'Unread notifications' => '未读通知', + 'My filters' => '我的过滤器', + 'Notification methods:' => '通知提醒方式:', + 'Import tasks from CSV file' => '从CSV文件导入任务', + 'Unable to read your file' => '无法读取文件', + '%d task(s) have been imported successfully.' => '成功导入%d条任务。', + 'Nothing have been imported!' => '没有信息被导入!', + 'Import users from CSV file' => '从CSV文件导入用户', + '%d user(s) have been imported successfully.' => '成功导入%d个用户。', + 'Comma' => '逗号', + 'Semi-colon' => '分号', + 'Tab' => '制表符', + 'Vertical bar' => '竖线', + 'Double Quote' => '双引号', + 'Single Quote' => '单引号', + '%s attached a file to the task #%d' => '%s添加文件到任务#%d', + 'There is no column or swimlane activated in your project!' => '当前项目没有活动任务栏或里程碑', + 'Append filter (instead of replacement)' => '追加过滤', + 'Append/Replace' => '追加/替换', + 'Append' => '追加', + 'Replace' => '替换', + 'Import' => '导入', + 'change sorting' => '改变排序', + 'Tasks Importation' => '任务重要性', + 'Delimiter' => '分隔符', + 'Enclosure' => '附件', + 'CSV File' => 'CSV文件', + 'Instructions' => '操作指南', + 'Your file must use the predefined CSV format' => '文件必须为预定格式的CSV文件', + 'Your file must be encoded in UTF-8' => '文件编码必须为UTF-8', + 'The first row must be the header' => '第一行必须为表头', + 'Duplicates are not verified for you' => '无法验证重复信息', + 'The due date must use the ISO format: YYYY-MM-DD' => '超期日期必须为ISO格式:YYYY-MM-DD', + 'Download CSV template' => '下载CSV模板', + 'No external integration registered.' => '没有外部注册信息', + 'Duplicates are not imported' => '重复信息未导入', + 'Usernames must be lowercase and unique' => '用户名必须小写且唯一', + 'Passwords will be encrypted if present' => '密码将被加密', + '%s attached a new file to the task %s' => '"%s"添加了附件到任务"%s"', + 'Assign automatically a category based on a link' => '基于链接自动关联分类', + 'BAM - Konvertible Mark' => '波斯尼亚马克', + 'Assignee Username' => '指派用户名', + 'Assignee Name' => '指派名称', + 'Groups' => '用户组', + 'Members of %s' => '“%s”组成员', + 'New group' => '新加用户组', + 'Group created successfully.' => '用户组创建成功', + 'Unable to create your group.' => '无法创建你的用户组', + 'Edit group' => '编辑用户组', + 'Group updated successfully.' => '用户组更新成功', + 'Unable to update your group.' => '无法更新你的用户组', + 'Add group member to "%s"' => '添加到用户组"%s"', + 'Group member added successfully.' => '成功添加用户组成员', + 'Unable to add group member.' => '无法添加用户组成员', + 'Remove user from group "%s"' => '从"%s"组中移除用户', + 'User removed successfully from this group.' => '用户已从该用户组中删除', + 'Unable to remove this user from the group.' => '无法从该用户组中删除用户', + 'Remove group' => '删除用户组', + 'Group removed successfully.' => '用户组已删除', + 'Unable to remove this group.' => '无法删除该用户组', + 'Project Permissions' => '项目权限', + 'Manager' => '管理员', + 'Project Manager' => '项目管理员', + 'Project Member' => '项目成员', + 'Project Viewer' => '项目观察员', + 'Your account is locked for %d minutes' => '你的账户被锁定%d分钟', + 'Invalid captcha' => '验证码无效', + 'The name must be unique' => '请确保用户名唯一', + 'View all groups' => '查看所有用户组', + 'View group members' => '查看该组所有用户', + 'There is no user available.' => '当前没有有效用户', + 'Do you really want to remove the user "%s" from the group "%s"?' => '你确定把用户"%s"从"%s"中移除?', + 'There is no group.' => '当前没有用户组', + 'External Id' => '关联ID', + 'Add group member' => '添加用户组成员', + 'Do you really want to remove this group: "%s"?' => '你真的想要移除用户组:"%s"?', + 'There is no user in this group.' => '当前用户组下没有成员', + 'Remove this user' => '移除用户', + 'Permissions' => '权限', + 'Allowed Users' => '被允许的用户', + 'No user have been allowed specifically.' => '没有被允许的用户。', + 'Role' => '角色', + 'Enter user name...' => '输入用户名...', + 'Allowed Groups' => '被允许的用户组', + 'No group have been allowed specifically.' => '没有被允许的用户组。', + 'Group' => '用户组', + 'Group Name' => '用户组名称', + 'Enter group name...' => '输入用户组名称...', + 'Role:' => '角色:', + 'Project members' => '项目成员', + 'Compare hours for "%s"' => '比较"%s"的时间', + '%s mentioned you in the task #%d' => '%s在任务#%d里提及你', + '%s mentioned you in a comment on the task #%d' => '%s在任务#%d的评论里提及你', + 'You were mentioned in the task #%d' => '你在任务#%d里被提及', + 'You were mentioned in a comment on the task #%d' => '你在任务#%d的评论里被提及', + 'Mentioned' => '被提及', + 'Compare Estimated Time vs Actual Time' => '对比预估时间VS实际时间', + 'Estimated hours: ' => '预估小时数', + 'Actual hours: ' => '实际小时数', + 'Hours Spent' => '花费小时数', + 'Hours Estimated' => '预估小时数', + 'Estimated Time' => '预估时间', + 'Actual Time' => '实际时间', + 'Estimated vs actual time' => '预估时间 VS 实际时间', + 'RUB - Russian Ruble' => '卢布', + 'Assign the task to the person who does the action when the column is changed' => '当任务所属任务栏改变时分配到指定用户', + 'Close a task in a specific column' => '关闭指定任务栏下的任务', + 'Time-based One-time Password Algorithm' => '基于时间的一次性密码算法', + 'Two-Factor Provider: ' => '双重认证提供商', + 'Disable two-factor authentication' => '禁用双重认证', + 'Enable two-factor authentication' => '启用双重认证', + 'There is no integration registered at the moment.' => '当前没有注册关联', + 'Password Reset for Kanboard' => '重置kanboard密码', + 'Forgot password?' => '忘记密码?', + 'Enable "Forget Password"' => '启用找回密码', + 'Password Reset' => '密码重置', + 'New password' => '新密码', + 'Change Password' => '更改密码', + 'To reset your password click on this link:' => '点击此链接重置你的密码', + 'Last Password Reset' => '上次密码重置', + 'The password has never been reinitialized.' => '密码从未被重新初始化', + 'Creation' => '创建时间', + 'Expiration' => '过期时间', + 'Password reset history' => '密码重置历史', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '任务栏 "%s" 和 里程碑 "%s" 下的所有任务已关闭', + 'Do you really want to close all tasks of this column?' => '你确定要关闭此任务栏下的所有任务?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '任务栏 "%s" 和 里程碑 "%s" 下 %d 条任务将被关闭。', + 'Close all tasks of this column' => '关闭此任务栏下的所有任务', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '没有插件注册到项目通知接口,你仍然可以在你个人设置里启用单独的通知', + 'My dashboard' => '我的控制台', + 'My profile' => '我的个人信息', + 'Project owner: ' => '项目负责人', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '项目标识符是可选的,且只能是数字或字母组成,例如:MYPROJECT。', + 'Project owner' => '项目负责人', + 'Those dates are useful for the project Gantt chart.' => '那些时间对于项目甘特图非常有用', + 'Private projects do not have users and groups management.' => '私有项目下没有成员或组可管理', + 'There is no project member.' => '当前没有项目成员', + 'Priority' => '优先级', + 'Task priority' => '任务优先级', + 'General' => '常用', + 'Dates' => '时间', + 'Default priority' => '默认优先级', + 'Lowest priority' => '最低优先级', + 'Highest priority' => '最高优先级', + 'If you put zero to the low and high priority, this feature will be disabled.' => '如果你为优先级填0,将禁用本功能', + 'Priority: %d' => '优先级:%d', + 'Close a task when there is no activity' => '当任务没有活动记录时关闭任务', + 'Duration in days' => '持续天数', + 'Send email when there is no activity on a task' => '当任务没有活动记录时发送邮件', + 'List of external links' => '列出外部关联', + 'Unable to fetch link information.' => '无法获取关联信息', + 'Daily background job for tasks' => '每日后台任务', + 'Auto' => '自动', + 'Related' => '相关的', + 'Attachment' => '附件', + 'Title not found' => '标题未找到', + 'Web Link' => '网页链接', + 'External links' => '外部关联', + 'Add external link' => '添加外部关联', + 'Type' => '类型', + 'Dependency' => '依赖', + 'View internal links' => '查看内部关联', + 'View external links' => '查看外部关联', + 'Add internal link' => '添加内部关联', + 'Add a new external link' => '添加新的外部关联', + 'Edit external link' => '编辑外部关联', + 'External link' => '外部关联', + 'Copy and paste your link here...' => '复制并粘贴链接到当前位置...', + 'URL' => 'URL', + 'There is no external link for the moment.' => '当前没有外部关联。', + 'Internal links' => '内部关联', + 'There is no internal link for the moment.' => '当前没有内部关联。', + // 'Assign to me' => '', + // 'Me' => '', + // 'Do not duplicate anything' => '', + // 'Projects management' => '', + // 'Users management' => '', + // 'Groups management' => '', + // 'Create from another project' => '', ); diff --git a/app/Model/Category.php b/app/Model/Category.php index 58cee738..883fc282 100644 --- a/app/Model/Category.php +++ b/app/Model/Category.php @@ -193,11 +193,12 @@ class Category extends Base */ public function duplicate($src_project_id, $dst_project_id) { - $categories = $this->db->table(self::TABLE) - ->columns('name') - ->eq('project_id', $src_project_id) - ->asc('name') - ->findAll(); + $categories = $this->db + ->table(self::TABLE) + ->columns('name') + ->eq('project_id', $src_project_id) + ->asc('name') + ->findAll(); foreach ($categories as $category) { $category['project_id'] = $dst_project_id; diff --git a/app/Model/Config.php b/app/Model/Config.php index 55999310..6f009175 100644 --- a/app/Model/Config.php +++ b/app/Model/Config.php @@ -76,6 +76,7 @@ class Config extends Setting 'en_US' => 'English', 'es_ES' => 'Español', 'fr_FR' => 'Français', + 'el_GR' => 'Grec', 'it_IT' => 'Italiano', 'hu_HU' => 'Magyar', 'my_MY' => 'Melayu', @@ -131,7 +132,8 @@ class Config extends Setting 'zh_CN' => 'zh-cn', 'ja_JP' => 'ja', 'th_TH' => 'th', - 'id_ID' => 'id' + 'id_ID' => 'id', + 'el_GR' => 'el', ); $lang = $this->getCurrentLanguage(); diff --git a/app/Model/LastLogin.php b/app/Model/LastLogin.php index f5be020e..a1734819 100644 --- a/app/Model/LastLogin.php +++ b/app/Model/LastLogin.php @@ -39,14 +39,14 @@ class LastLogin extends Base $this->cleanup($user_id); return $this->db - ->table(self::TABLE) - ->insert(array( - 'auth_type' => $auth_type, - 'user_id' => $user_id, - 'ip' => $ip, - 'user_agent' => substr($user_agent, 0, 255), - 'date_creation' => time(), - )); + ->table(self::TABLE) + ->insert(array( + 'auth_type' => $auth_type, + 'user_id' => $user_id, + 'ip' => $ip, + 'user_agent' => substr($user_agent, 0, 255), + 'date_creation' => time(), + )); } /** diff --git a/app/Model/ProjectDuplication.php b/app/Model/ProjectDuplication.php index f0c66834..9c5f80ad 100644 --- a/app/Model/ProjectDuplication.php +++ b/app/Model/ProjectDuplication.php @@ -2,6 +2,8 @@ namespace Kanboard\Model; +use Kanboard\Core\Security\Role; + /** * Project Duplication * @@ -12,6 +14,28 @@ namespace Kanboard\Model; class ProjectDuplication extends Base { /** + * Get list of optional models to duplicate + * + * @access public + * @return string[] + */ + public function getOptionalSelection() + { + return array('category', 'projectPermission', 'action', 'swimlane', 'task'); + } + + /** + * Get list of all possible models to duplicate + * + * @access public + * @return string[] + */ + public function getPossibleSelection() + { + return array('board', 'category', 'projectPermission', 'action', 'swimlane', 'task'); + } + + /** * Get a valid project name for the duplication * * @access public @@ -31,78 +55,106 @@ class ProjectDuplication extends Base } /** - * Create a project from another one - * - * @param integer $project_id Project Id - * @return integer Cloned Project Id - */ - public function copy($project_id) - { - $project = $this->project->getById($project_id); - - $values = array( - 'name' => $this->getClonedProjectName($project['name']), - 'is_active' => true, - 'last_modified' => 0, - 'token' => '', - 'is_public' => 0, - 'is_private' => empty($project['is_private']) ? 0 : 1, - ); - - if (! $this->db->table(Project::TABLE)->save($values)) { - return 0; - } - - return $this->db->getLastId(); - } - - /** * Clone a project with all settings * - * @param integer $project_id Project Id - * @param array $part_selection Selection of optional project parts to duplicate. Possible options: 'swimlane', 'action', 'category', 'task' - * @return integer Cloned Project Id + * @param integer $src_project_id Project Id + * @param array $selection Selection of optional project parts to duplicate + * @param integer $owner_id Owner of the project + * @param string $name Name of the project + * @param boolean $private Force the project to be private + * @return integer Cloned Project Id */ - public function duplicate($project_id, $part_selection = array('category', 'action')) + public function duplicate($src_project_id, $selection = array('projectPermission', 'category', 'action'), $owner_id = 0, $name = null, $private = null) { $this->db->startTransaction(); // Get the cloned project Id - $clone_project_id = $this->copy($project_id); + $dst_project_id = $this->copy($src_project_id, $owner_id, $name, $private); - if (! $clone_project_id) { + if ($dst_project_id === false) { $this->db->cancelTransaction(); return false; } // Clone Columns, Categories, Permissions and Actions - $optional_parts = array('swimlane', 'action', 'category'); - foreach (array('board', 'category', 'projectPermission', 'action', 'swimlane') as $model) { + foreach ($this->getPossibleSelection() as $model) { // Skip if optional part has not been selected - if (in_array($model, $optional_parts) && ! in_array($model, $part_selection)) { + if (in_array($model, $this->getOptionalSelection()) && ! in_array($model, $selection)) { continue; } - if (! $this->$model->duplicate($project_id, $clone_project_id)) { + // Skip permissions for private projects + if ($private && $model === 'projectPermission') { + continue; + } + + if (! $this->$model->duplicate($src_project_id, $dst_project_id)) { $this->db->cancelTransaction(); return false; } } + if (! $this->makeOwnerManager($dst_project_id, $owner_id)) { + $this->db->cancelTransaction(); + return false; + } + $this->db->closeTransaction(); - // Clone Tasks if in $part_selection - if (in_array('task', $part_selection)) { - $tasks = $this->taskFinder->getAll($project_id); + return (int) $dst_project_id; + } + + /** + * Create a project from another one + * + * @access private + * @param integer $src_project_id + * @param integer $owner_id + * @param string $name + * @param boolean $private + * @return integer + */ + private function copy($src_project_id, $owner_id = 0, $name = null, $private = null) + { + $project = $this->project->getById($src_project_id); + $is_private = empty($project['is_private']) ? 0 : 1; + + $values = array( + 'name' => $name ?: $this->getClonedProjectName($project['name']), + 'is_active' => 1, + 'last_modified' => time(), + 'token' => '', + 'is_public' => 0, + 'is_private' => $private ? 1 : $is_private, + 'owner_id' => $owner_id, + ); + + if (! $this->db->table(Project::TABLE)->save($values)) { + return false; + } + + return $this->db->getLastId(); + } + + /** + * Make sure that the creator of the duplicated project is alsp owner + * + * @access private + * @param integer $dst_project_id + * @param integer $owner_id + * @return boolean + */ + private function makeOwnerManager($dst_project_id, $owner_id) + { + if ($owner_id > 0) { + $this->projectUserRole->removeUser($dst_project_id, $owner_id); - foreach ($tasks as $task) { - if (! $this->taskDuplication->duplicateToProject($task['id'], $clone_project_id)) { - return false; - } + if (! $this->projectUserRole->addUser($dst_project_id, $owner_id, Role::PROJECT_MANAGER)) { + return false; } } - return (int) $clone_project_id; + return true; } } diff --git a/app/Model/Subtask.php b/app/Model/Subtask.php index 0e039bb3..14853941 100644 --- a/app/Model/Subtask.php +++ b/app/Model/Subtask.php @@ -353,15 +353,16 @@ class Subtask extends Base * * @access public * @param integer $subtask_id - * @return bool + * @return boolean|integer */ public function toggleStatus($subtask_id) { $subtask = $this->getById($subtask_id); + $status = ($subtask['status'] + 1) % 3; $values = array( 'id' => $subtask['id'], - 'status' => ($subtask['status'] + 1) % 3, + 'status' => $status, 'task_id' => $subtask['task_id'], ); @@ -369,7 +370,7 @@ class Subtask extends Base $values['user_id'] = $this->userSession->getId(); } - return $this->update($values); + return $this->update($values) ? $status : false; } /** @@ -435,10 +436,10 @@ class Subtask extends Base return $this->db->transaction(function (Database $db) use ($src_task_id, $dst_task_id) { $subtasks = $db->table(Subtask::TABLE) - ->columns('title', 'time_estimated', 'position') - ->eq('task_id', $src_task_id) - ->asc('position') - ->findAll(); + ->columns('title', 'time_estimated', 'position') + ->eq('task_id', $src_task_id) + ->asc('position') + ->findAll(); foreach ($subtasks as &$subtask) { $subtask['task_id'] = $dst_task_id; diff --git a/app/Model/SubtaskTimeTracking.php b/app/Model/SubtaskTimeTracking.php index a741dbb5..b766b542 100644 --- a/app/Model/SubtaskTimeTracking.php +++ b/app/Model/SubtaskTimeTracking.php @@ -302,11 +302,11 @@ class SubtaskTimeTracking extends Base { $hook = 'model:subtask-time-tracking:calculate:time-spent'; $start_time = $this->db - ->table(self::TABLE) - ->eq('subtask_id', $subtask_id) - ->eq('user_id', $user_id) - ->eq('end', 0) - ->findOneColumn('start'); + ->table(self::TABLE) + ->eq('subtask_id', $subtask_id) + ->eq('user_id', $user_id) + ->eq('end', 0) + ->findOneColumn('start'); if (empty($start_time)) { return 0; diff --git a/app/Model/Swimlane.php b/app/Model/Swimlane.php index e5124e8e..6b0dcdc5 100644 --- a/app/Model/Swimlane.php +++ b/app/Model/Swimlane.php @@ -96,10 +96,11 @@ class Swimlane extends Base */ public function getDefault($project_id) { - $result = $this->db->table(Project::TABLE) - ->eq('id', $project_id) - ->columns('id', 'default_swimlane', 'show_default_swimlane') - ->findOne(); + $result = $this->db + ->table(Project::TABLE) + ->eq('id', $project_id) + ->columns('id', 'default_swimlane', 'show_default_swimlane') + ->findOne(); if ($result['default_swimlane'] === 'Default swimlane') { $result['default_swimlane'] = t($result['default_swimlane']); @@ -117,10 +118,11 @@ class Swimlane extends Base */ public function getAll($project_id) { - return $this->db->table(self::TABLE) - ->eq('project_id', $project_id) - ->orderBy('position', 'asc') - ->findAll(); + return $this->db + ->table(self::TABLE) + ->eq('project_id', $project_id) + ->orderBy('position', 'asc') + ->findAll(); } /** @@ -133,9 +135,10 @@ class Swimlane extends Base */ public function getAllByStatus($project_id, $status = self::ACTIVE) { - $query = $this->db->table(self::TABLE) - ->eq('project_id', $project_id) - ->eq('is_active', $status); + $query = $this->db + ->table(self::TABLE) + ->eq('project_id', $project_id) + ->eq('is_active', $status); if ($status == self::ACTIVE) { $query->asc('position'); @@ -155,17 +158,19 @@ class Swimlane extends Base */ public function getSwimlanes($project_id) { - $swimlanes = $this->db->table(self::TABLE) - ->columns('id', 'name', 'description') - ->eq('project_id', $project_id) - ->eq('is_active', self::ACTIVE) - ->orderBy('position', 'asc') - ->findAll(); - - $default_swimlane = $this->db->table(Project::TABLE) - ->eq('id', $project_id) - ->eq('show_default_swimlane', 1) - ->findOneColumn('default_swimlane'); + $swimlanes = $this->db + ->table(self::TABLE) + ->columns('id', 'name', 'description') + ->eq('project_id', $project_id) + ->eq('is_active', self::ACTIVE) + ->orderBy('position', 'asc') + ->findAll(); + + $default_swimlane = $this->db + ->table(Project::TABLE) + ->eq('id', $project_id) + ->eq('show_default_swimlane', 1) + ->findOneColumn('default_swimlane'); if ($default_swimlane) { if ($default_swimlane === 'Default swimlane') { @@ -200,11 +205,12 @@ class Swimlane extends Base $swimlanes[0] = $default === 'Default swimlane' ? t($default) : $default; } - return $swimlanes + $this->db->hashtable(self::TABLE) - ->eq('project_id', $project_id) - ->in('is_active', $only_active ? array(self::ACTIVE) : array(self::ACTIVE, self::INACTIVE)) - ->orderBy('position', 'asc') - ->getAll('id', 'name'); + return $swimlanes + $this->db + ->hashtable(self::TABLE) + ->eq('project_id', $project_id) + ->in('is_active', $only_active ? array(self::ACTIVE) : array(self::ACTIVE, self::INACTIVE)) + ->orderBy('position', 'asc') + ->getAll('id', 'name'); } /** @@ -232,9 +238,10 @@ class Swimlane extends Base */ public function update(array $values) { - return $this->db->table(self::TABLE) - ->eq('id', $values['id']) - ->update($values); + return $this->db + ->table(self::TABLE) + ->eq('id', $values['id']) + ->update($values); } /** @@ -247,12 +254,12 @@ class Swimlane extends Base public function updateDefault(array $values) { return $this->db - ->table(Project::TABLE) - ->eq('id', $values['id']) - ->update(array( - 'default_swimlane' => $values['default_swimlane'], - 'show_default_swimlane' => $values['show_default_swimlane'], - )); + ->table(Project::TABLE) + ->eq('id', $values['id']) + ->update(array( + 'default_swimlane' => $values['default_swimlane'], + 'show_default_swimlane' => $values['show_default_swimlane'], + )); } /** @@ -264,10 +271,11 @@ class Swimlane extends Base */ public function getLastPosition($project_id) { - return $this->db->table(self::TABLE) - ->eq('project_id', $project_id) - ->eq('is_active', 1) - ->count() + 1; + return $this->db + ->table(self::TABLE) + ->eq('project_id', $project_id) + ->eq('is_active', 1) + ->count() + 1; } /** @@ -281,12 +289,12 @@ class Swimlane extends Base public function disable($project_id, $swimlane_id) { $result = $this->db - ->table(self::TABLE) - ->eq('id', $swimlane_id) - ->update(array( - 'is_active' => self::INACTIVE, - 'position' => 0, - )); + ->table(self::TABLE) + ->eq('id', $swimlane_id) + ->update(array( + 'is_active' => self::INACTIVE, + 'position' => 0, + )); if ($result) { // Re-order positions @@ -307,12 +315,12 @@ class Swimlane extends Base public function enable($project_id, $swimlane_id) { return $this->db - ->table(self::TABLE) - ->eq('id', $swimlane_id) - ->update(array( - 'is_active' => self::ACTIVE, - 'position' => $this->getLastPosition($project_id), - )); + ->table(self::TABLE) + ->eq('id', $swimlane_id) + ->update(array( + 'is_active' => self::ACTIVE, + 'position' => $this->getLastPosition($project_id), + )); } /** @@ -353,11 +361,12 @@ class Swimlane extends Base public function updatePositions($project_id) { $position = 0; - $swimlanes = $this->db->table(self::TABLE) - ->eq('project_id', $project_id) - ->eq('is_active', 1) - ->asc('position') - ->findAllByColumn('id'); + $swimlanes = $this->db + ->table(self::TABLE) + ->eq('project_id', $project_id) + ->eq('is_active', 1) + ->asc('position') + ->findAllByColumn('id'); if (! $swimlanes) { return false; @@ -365,8 +374,8 @@ class Swimlane extends Base foreach ($swimlanes as $swimlane_id) { $this->db->table(self::TABLE) - ->eq('id', $swimlane_id) - ->update(array('position' => ++$position)); + ->eq('id', $swimlane_id) + ->update(array('position' => ++$position)); } return true; @@ -383,10 +392,10 @@ class Swimlane extends Base public function moveDown($project_id, $swimlane_id) { $swimlanes = $this->db->hashtable(self::TABLE) - ->eq('project_id', $project_id) - ->eq('is_active', self::ACTIVE) - ->asc('position') - ->getAll('id', 'position'); + ->eq('project_id', $project_id) + ->eq('is_active', self::ACTIVE) + ->asc('position') + ->getAll('id', 'position'); $positions = array_flip($swimlanes); @@ -416,10 +425,10 @@ class Swimlane extends Base public function moveUp($project_id, $swimlane_id) { $swimlanes = $this->db->hashtable(self::TABLE) - ->eq('project_id', $project_id) - ->eq('is_active', self::ACTIVE) - ->asc('position') - ->getAll('id', 'position'); + ->eq('project_id', $project_id) + ->eq('is_active', self::ACTIVE) + ->asc('position') + ->getAll('id', 'position'); $positions = array_flip($swimlanes); diff --git a/app/Model/Task.php b/app/Model/Task.php index 7aa9e312..38fdd0d5 100644 --- a/app/Model/Task.php +++ b/app/Model/Task.php @@ -42,6 +42,7 @@ class Task extends Base const EVENT_ASSIGNEE_CHANGE = 'task.assignee_change'; const EVENT_OVERDUE = 'task.overdue'; const EVENT_USER_MENTION = 'task.user.mention'; + const EVENT_DAILY_CRONJOB = 'task.cronjob.daily'; /** * Recurrence: status @@ -198,4 +199,25 @@ class Task extends Base return round(($position * 100) / count($columns), 1); } + + /** + * Helper method to duplicate all tasks to another project + * + * @access public + * @param integer $src_project_id + * @param integer $dst_project_id + * @return boolean + */ + public function duplicate($src_project_id, $dst_project_id) + { + $task_ids = $this->taskFinder->getAllIds($src_project_id, array(Task::STATUS_OPEN, Task::STATUS_CLOSED)); + + foreach ($task_ids as $task_id) { + if (! $this->taskDuplication->duplicateToProject($task_id, $dst_project_id)) { + return false; + } + } + + return true; + } } diff --git a/app/Model/TaskExternalLink.php b/app/Model/TaskExternalLink.php new file mode 100644 index 00000000..f2c756b4 --- /dev/null +++ b/app/Model/TaskExternalLink.php @@ -0,0 +1,99 @@ +<?php + +namespace Kanboard\Model; + +/** + * Task External Link Model + * + * @package model + * @author Frederic Guillot + */ +class TaskExternalLink extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'task_has_external_links'; + + /** + * Get all links + * + * @access public + * @param integer $task_id + * @return array + */ + public function getAll($task_id) + { + $types = $this->externalLinkManager->getTypes(); + + $links = $this->db->table(self::TABLE) + ->columns(self::TABLE.'.*', User::TABLE.'.name AS creator_name', User::TABLE.'.username AS creator_username') + ->eq('task_id', $task_id) + ->asc('title') + ->join(User::TABLE, 'id', 'creator_id') + ->findAll(); + + foreach ($links as &$link) { + $link['dependency_label'] = $this->externalLinkManager->getDependencyLabel($link['link_type'], $link['dependency']); + $link['type'] = isset($types[$link['link_type']]) ? $types[$link['link_type']] : t('Unknown'); + } + + return $links; + } + + /** + * Get link + * + * @access public + * @param integer $link_id + * @return array + */ + public function getById($link_id) + { + return $this->db->table(self::TABLE)->eq('id', $link_id)->findOne(); + } + + /** + * Add a new link in the database + * + * @access public + * @param array $values Form values + * @return boolean|integer + */ + public function create(array $values) + { + unset($values['id']); + $values['creator_id'] = $this->userSession->getId(); + $values['date_creation'] = time(); + $values['date_modification'] = $values['date_creation']; + + return $this->persist(self::TABLE, $values); + } + + /** + * Modify external link + * + * @access public + * @param array $values Form values + * @return boolean + */ + public function update(array $values) + { + $values['date_modification'] = time(); + return $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values); + } + + /** + * Remove a link + * + * @access public + * @param integer $link_id + * @return boolean + */ + public function remove($link_id) + { + return $this->db->table(self::TABLE)->eq('id', $link_id)->remove(); + } +} diff --git a/app/Model/TaskFinder.php b/app/Model/TaskFinder.php index 1c83136b..4d673097 100644 --- a/app/Model/TaskFinder.php +++ b/app/Model/TaskFinder.php @@ -88,11 +88,12 @@ class TaskFinder extends Base return $this->db ->table(Task::TABLE) ->columns( - '(SELECT count(*) FROM '.Comment::TABLE.' WHERE task_id=tasks.id) AS nb_comments', - '(SELECT count(*) FROM '.File::TABLE.' WHERE task_id=tasks.id) AS nb_files', - '(SELECT count(*) FROM '.Subtask::TABLE.' WHERE '.Subtask::TABLE.'.task_id=tasks.id) AS nb_subtasks', - '(SELECT count(*) FROM '.Subtask::TABLE.' WHERE '.Subtask::TABLE.'.task_id=tasks.id AND status=2) AS nb_completed_subtasks', - '(SELECT count(*) FROM '.TaskLink::TABLE.' WHERE '.TaskLink::TABLE.'.task_id = tasks.id) AS nb_links', + '(SELECT COUNT(*) FROM '.Comment::TABLE.' WHERE task_id=tasks.id) AS nb_comments', + '(SELECT COUNT(*) FROM '.File::TABLE.' WHERE task_id=tasks.id) AS nb_files', + '(SELECT COUNT(*) FROM '.Subtask::TABLE.' WHERE '.Subtask::TABLE.'.task_id=tasks.id) AS nb_subtasks', + '(SELECT COUNT(*) FROM '.Subtask::TABLE.' WHERE '.Subtask::TABLE.'.task_id=tasks.id AND status=2) AS nb_completed_subtasks', + '(SELECT COUNT(*) FROM '.TaskLink::TABLE.' WHERE '.TaskLink::TABLE.'.task_id = tasks.id) AS nb_links', + '(SELECT COUNT(*) FROM '.TaskExternalLink::TABLE.' WHERE '.TaskExternalLink::TABLE.'.task_id = tasks.id) AS nb_external_links', '(SELECT DISTINCT 1 FROM '.TaskLink::TABLE.' WHERE '.TaskLink::TABLE.'.task_id = tasks.id AND '.TaskLink::TABLE.'.link_id = 9) AS is_milestone', 'tasks.id', 'tasks.reference', @@ -179,6 +180,23 @@ class TaskFinder extends Base } /** + * Get all tasks for a given project and status + * + * @access public + * @param integer $project_id + * @param array $status + * @return array + */ + public function getAllIds($project_id, array $status = array(Task::STATUS_OPEN)) + { + return $this->db + ->table(Task::TABLE) + ->eq(Task::TABLE.'.project_id', $project_id) + ->in(Task::TABLE.'.is_active', $status) + ->findAllByColumn('id'); + } + + /** * Get overdue tasks query * * @access public diff --git a/app/Model/TaskLink.php b/app/Model/TaskLink.php index 87aae55e..034fcf45 100644 --- a/app/Model/TaskLink.php +++ b/app/Model/TaskLink.php @@ -75,6 +75,7 @@ class TaskLink extends Base Task::TABLE.'.title', Task::TABLE.'.is_active', Task::TABLE.'.project_id', + Task::TABLE.'.column_id', Task::TABLE.'.time_spent AS task_time_spent', Task::TABLE.'.time_estimated AS task_time_estimated', Task::TABLE.'.owner_id AS task_assignee_id', diff --git a/app/Model/User.php b/app/Model/User.php index ac0e7491..dd622207 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -48,20 +48,7 @@ class User extends Base */ public function getQuery() { - return $this->db - ->table(self::TABLE) - ->columns( - 'id', - 'username', - 'name', - 'email', - 'role', - 'is_ldap_user', - 'notifications_enabled', - 'google_id', - 'github_id', - 'twofactor_activated' - ); + return $this->db->table(self::TABLE); } /** @@ -278,7 +265,7 @@ class User extends Base * * @access public * @param array $values Form values - * @return array + * @return boolean */ public function update(array $values) { diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index 8f1db510..036958b6 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -6,7 +6,26 @@ use PDO; use Kanboard\Core\Security\Token; use Kanboard\Core\Security\Role; -const VERSION = 103; +const VERSION = 104; + +function version_104(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE task_has_external_links ( + id INT NOT NULL AUTO_INCREMENT, + link_type VARCHAR(100) NOT NULL, + dependency VARCHAR(100) NOT NULL, + title VARCHAR(255) NOT NULL, + url VARCHAR(255) NOT NULL, + date_creation INT NOT NULL, + date_modification INT NOT NULL, + task_id INT NOT NULL, + creator_id INT DEFAULT 0, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + PRIMARY KEY(id) + ) ENGINE=InnoDB CHARSET=utf8 + "); +} function version_103(PDO $pdo) { diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php index a7bf8054..363b633b 100644 --- a/app/Schema/Postgres.php +++ b/app/Schema/Postgres.php @@ -6,7 +6,25 @@ use PDO; use Kanboard\Core\Security\Token; use Kanboard\Core\Security\Role; -const VERSION = 83; +const VERSION = 84; + +function version_84(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE task_has_external_links ( + id SERIAL, + link_type VARCHAR(100) NOT NULL, + dependency VARCHAR(100) NOT NULL, + title VARCHAR(255) NOT NULL, + url VARCHAR(255) NOT NULL, + date_creation INT NOT NULL, + date_modification INT NOT NULL, + task_id INT NOT NULL, + creator_id INT DEFAULT 0, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ) + "); +} function version_83(PDO $pdo) { diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index 08689749..bc701341 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -6,7 +6,25 @@ use Kanboard\Core\Security\Token; use Kanboard\Core\Security\Role; use PDO; -const VERSION = 95; +const VERSION = 96; + +function version_96(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE task_has_external_links ( + id INTEGER PRIMARY KEY, + link_type TEXT NOT NULL, + dependency TEXT NOT NULL, + title TEXT NOT NULL, + url TEXT NOT NULL, + date_creation INTEGER NOT NULL, + date_modification INTEGER NOT NULL, + task_id INTEGER NOT NULL, + creator_id INTEGER DEFAULT 0, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ) + "); +} function version_95(PDO $pdo) { diff --git a/app/ServiceProvider/ActionProvider.php b/app/ServiceProvider/ActionProvider.php index 0aba29f1..3692f190 100644 --- a/app/ServiceProvider/ActionProvider.php +++ b/app/ServiceProvider/ActionProvider.php @@ -23,12 +23,14 @@ use Kanboard\Action\TaskCloseColumn; use Kanboard\Action\TaskCreation; use Kanboard\Action\TaskDuplicateAnotherProject; use Kanboard\Action\TaskEmail; +use Kanboard\Action\TaskEmailNoActivity; use Kanboard\Action\TaskMoveAnotherProject; use Kanboard\Action\TaskMoveColumnAssigned; use Kanboard\Action\TaskMoveColumnCategoryChange; use Kanboard\Action\TaskMoveColumnUnAssigned; use Kanboard\Action\TaskOpen; use Kanboard\Action\TaskUpdateStartDate; +use Kanboard\Action\TaskCloseNoActivity; /** * Action Provider @@ -63,9 +65,11 @@ class ActionProvider implements ServiceProviderInterface $container['actionManager']->register(new TaskAssignUser($container)); $container['actionManager']->register(new TaskClose($container)); $container['actionManager']->register(new TaskCloseColumn($container)); + $container['actionManager']->register(new TaskCloseNoActivity($container)); $container['actionManager']->register(new TaskCreation($container)); $container['actionManager']->register(new TaskDuplicateAnotherProject($container)); $container['actionManager']->register(new TaskEmail($container)); + $container['actionManager']->register(new TaskEmailNoActivity($container)); $container['actionManager']->register(new TaskMoveAnotherProject($container)); $container['actionManager']->register(new TaskMoveColumnAssigned($container)); $container['actionManager']->register(new TaskMoveColumnCategoryChange($container)); diff --git a/app/ServiceProvider/AuthenticationProvider.php b/app/ServiceProvider/AuthenticationProvider.php index a516cffe..4196a470 100644 --- a/app/ServiceProvider/AuthenticationProvider.php +++ b/app/ServiceProvider/AuthenticationProvider.php @@ -11,9 +11,6 @@ use Kanboard\Core\Security\Role; use Kanboard\Auth\RememberMeAuth; use Kanboard\Auth\DatabaseAuth; use Kanboard\Auth\LdapAuth; -use Kanboard\Auth\GitlabAuth; -use Kanboard\Auth\GithubAuth; -use Kanboard\Auth\GoogleAuth; use Kanboard\Auth\TotpAuth; use Kanboard\Auth\ReverseProxyAuth; @@ -47,18 +44,6 @@ class AuthenticationProvider implements ServiceProviderInterface $container['authenticationManager']->register(new LdapAuth($container)); } - if (GITLAB_AUTH) { - $container['authenticationManager']->register(new GitlabAuth($container)); - } - - if (GITHUB_AUTH) { - $container['authenticationManager']->register(new GithubAuth($container)); - } - - if (GOOGLE_AUTH) { - $container['authenticationManager']->register(new GoogleAuth($container)); - } - $container['projectAccessMap'] = $this->getProjectAccessMap(); $container['applicationAccessMap'] = $this->getApplicationAccessMap(); @@ -98,12 +83,18 @@ class AuthenticationProvider implements ServiceProviderInterface $acl->add('ProjectEdit', '*', Role::PROJECT_MANAGER); $acl->add('Projectuser', '*', Role::PROJECT_MANAGER); $acl->add('Subtask', '*', Role::PROJECT_MEMBER); + $acl->add('SubtaskRestriction', '*', Role::PROJECT_MEMBER); + $acl->add('SubtaskStatus', '*', Role::PROJECT_MEMBER); $acl->add('Swimlane', '*', Role::PROJECT_MANAGER); $acl->add('Task', 'remove', Role::PROJECT_MEMBER); $acl->add('Taskcreation', '*', Role::PROJECT_MEMBER); $acl->add('Taskduplication', '*', Role::PROJECT_MEMBER); + $acl->add('TaskRecurrence', '*', Role::PROJECT_MEMBER); $acl->add('TaskImport', '*', Role::PROJECT_MANAGER); $acl->add('Tasklink', '*', Role::PROJECT_MEMBER); + $acl->add('Tasklink', array('show'), Role::PROJECT_VIEWER); + $acl->add('TaskExternalLink', '*', Role::PROJECT_MEMBER); + $acl->add('TaskExternalLink', array('show'), Role::PROJECT_VIEWER); $acl->add('Taskmodification', '*', Role::PROJECT_MEMBER); $acl->add('Taskstatus', '*', Role::PROJECT_MEMBER); $acl->add('Timer', '*', Role::PROJECT_MEMBER); @@ -126,7 +117,6 @@ class AuthenticationProvider implements ServiceProviderInterface $acl->setRoleHierarchy(Role::APP_MANAGER, array(Role::APP_USER, Role::APP_PUBLIC)); $acl->setRoleHierarchy(Role::APP_USER, array(Role::APP_PUBLIC)); - $acl->add('Oauth', array('google', 'github', 'gitlab'), Role::APP_PUBLIC); $acl->add('Auth', array('login', 'check'), Role::APP_PUBLIC); $acl->add('Captcha', '*', Role::APP_PUBLIC); $acl->add('PasswordReset', '*', Role::APP_PUBLIC); @@ -141,8 +131,7 @@ class AuthenticationProvider implements ServiceProviderInterface $acl->add('Gantt', array('projects', 'saveProjectDate'), Role::APP_MANAGER); $acl->add('Group', '*', Role::APP_ADMIN); $acl->add('Link', '*', Role::APP_ADMIN); - $acl->add('Project', array('users', 'allowEverybody', 'allow', 'role', 'revoke', 'create'), Role::APP_MANAGER); - $acl->add('ProjectPermission', '*', Role::APP_USER); + $acl->add('ProjectCreation', 'create', Role::APP_MANAGER); $acl->add('Projectuser', '*', Role::APP_MANAGER); $acl->add('Twofactor', 'disable', Role::APP_ADMIN); $acl->add('UserImport', '*', Role::APP_ADMIN); diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php index df4e183b..61a4c512 100644 --- a/app/ServiceProvider/ClassProvider.php +++ b/app/ServiceProvider/ClassProvider.php @@ -61,6 +61,7 @@ class ClassProvider implements ServiceProviderInterface 'TaskCreation', 'TaskDuplication', 'TaskExport', + 'TaskExternalLink', 'TaskFinder', 'TaskFilter', 'TaskLink', @@ -97,6 +98,7 @@ class ClassProvider implements ServiceProviderInterface 'CommentValidator', 'CurrencyValidator', 'CustomFilterValidator', + 'ExternalLinkValidator', 'GroupValidator', 'LinkValidator', 'PasswordResetValidator', diff --git a/app/ServiceProvider/ExternalLinkProvider.php b/app/ServiceProvider/ExternalLinkProvider.php new file mode 100644 index 00000000..c4bbc4cf --- /dev/null +++ b/app/ServiceProvider/ExternalLinkProvider.php @@ -0,0 +1,34 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Kanboard\Core\ExternalLink\ExternalLinkManager; +use Kanboard\ExternalLink\WebLinkProvider; +use Kanboard\ExternalLink\AttachmentLinkProvider; + +/** + * External Link Provider + * + * @package serviceProvider + * @author Frederic Guillot + */ +class ExternalLinkProvider implements ServiceProviderInterface +{ + /** + * Register providers + * + * @access public + * @param \Pimple\Container $container + * @return \Pimple\Container + */ + public function register(Container $container) + { + $container['externalLinkManager'] = new ExternalLinkManager($container); + $container['externalLinkManager']->register(new WebLinkProvider($container)); + $container['externalLinkManager']->register(new AttachmentLinkProvider($container)); + + return $container; + } +} diff --git a/app/ServiceProvider/RouteProvider.php b/app/ServiceProvider/RouteProvider.php index 057a1b3c..7064884e 100644 --- a/app/ServiceProvider/RouteProvider.php +++ b/app/ServiceProvider/RouteProvider.php @@ -43,10 +43,12 @@ class RouteProvider implements ServiceProviderInterface $container['route']->addRoute('search', 'search', 'index'); $container['route']->addRoute('search/:search', 'search', 'index'); + // ProjectCreation routes + $container['route']->addRoute('project/create', 'ProjectCreation', 'create'); + $container['route']->addRoute('project/create/private', 'ProjectCreation', 'createPrivate'); + // Project routes $container['route']->addRoute('projects', 'project', 'index'); - $container['route']->addRoute('project/create', 'project', 'create'); - $container['route']->addRoute('project/create/private', 'project', 'createPrivate'); $container['route']->addRoute('project/:project_id', 'project', 'show'); $container['route']->addRoute('p/:project_id', 'project', 'show'); $container['route']->addRoute('project/:project_id/customer-filter', 'customfilter', 'index'); @@ -105,24 +107,10 @@ class RouteProvider implements ServiceProviderInterface $container['route']->addRoute('project/:project_id/task/:task_id/activity', 'activity', 'task'); $container['route']->addRoute('project/:project_id/task/:task_id/screenshot', 'file', 'screenshot'); $container['route']->addRoute('project/:project_id/task/:task_id/upload', 'file', 'create'); - $container['route']->addRoute('project/:project_id/task/:task_id/comment', 'comment', 'create'); - $container['route']->addRoute('project/:project_id/task/:task_id/link', 'tasklink', 'create'); $container['route']->addRoute('project/:project_id/task/:task_id/transitions', 'task', 'transitions'); $container['route']->addRoute('project/:project_id/task/:task_id/analytics', 'task', 'analytics'); - $container['route']->addRoute('project/:project_id/task/:task_id/remove', 'task', 'remove'); - - $container['route']->addRoute('project/:project_id/task/:task_id/edit', 'taskmodification', 'edit'); - $container['route']->addRoute('project/:project_id/task/:task_id/description', 'taskmodification', 'description'); - $container['route']->addRoute('project/:project_id/task/:task_id/recurrence', 'taskmodification', 'recurrence'); - - $container['route']->addRoute('project/:project_id/task/:task_id/close', 'taskstatus', 'close'); - $container['route']->addRoute('project/:project_id/task/:task_id/open', 'taskstatus', 'open'); - - $container['route']->addRoute('project/:project_id/task/:task_id/duplicate', 'taskduplication', 'duplicate'); - $container['route']->addRoute('project/:project_id/task/:task_id/copy', 'taskduplication', 'copy'); - $container['route']->addRoute('project/:project_id/task/:task_id/copy/:dst_project_id', 'taskduplication', 'copy'); - $container['route']->addRoute('project/:project_id/task/:task_id/move', 'taskduplication', 'move'); - $container['route']->addRoute('project/:project_id/task/:task_id/move/:dst_project_id', 'taskduplication', 'move'); + $container['route']->addRoute('project/:project_id/task/:task_id/internal/links', 'tasklink', 'show'); + $container['route']->addRoute('project/:project_id/task/:task_id/external/links', 'TaskExternalLink', 'show'); // Exports $container['route']->addRoute('export/tasks/:project_id', 'export', 'tasks'); @@ -205,9 +193,6 @@ class RouteProvider implements ServiceProviderInterface $container['route']->addRoute('documentation', 'doc', 'show'); // Auth routes - $container['route']->addRoute('oauth/google', 'oauth', 'google'); - $container['route']->addRoute('oauth/github', 'oauth', 'github'); - $container['route']->addRoute('oauth/gitlab', 'oauth', 'gitlab'); $container['route']->addRoute('login', 'auth', 'login'); $container['route']->addRoute('logout', 'auth', 'logout'); diff --git a/app/Template/action/event.php b/app/Template/action/event.php index 7f968a97..b4741a98 100644 --- a/app/Template/action/event.php +++ b/app/Template/action/event.php @@ -11,7 +11,7 @@ <?= $this->form->hidden('action_name', $values) ?> <?= $this->form->label(t('Event'), 'event_name') ?> - <?= $this->form->select('event_name', $events, $values) ?><br/> + <?= $this->form->select('event_name', $events, $values) ?> <div class="form-help"> <?= t('When the selected event occurs execute the corresponding action.') ?> diff --git a/app/Template/action/index.php b/app/Template/action/index.php index 8275f080..c28c7a4b 100644 --- a/app/Template/action/index.php +++ b/app/Template/action/index.php @@ -67,9 +67,9 @@ <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Action'), 'action_name') ?> - <?= $this->form->select('action_name', $available_actions, $values) ?><br/> + <?= $this->form->select('action_name', $available_actions, $values) ?> <div class="form-actions"> - <input type="submit" value="<?= t('Next step') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Next step') ?>" class="btn btn-blue"> </div> </form>
\ No newline at end of file diff --git a/app/Template/action/params.php b/app/Template/action/params.php index dcfaa9cc..a2350dea 100644 --- a/app/Template/action/params.php +++ b/app/Template/action/params.php @@ -15,22 +15,25 @@ <?php if ($this->text->contains($param_name, 'column_id')): ?> <?= $this->form->label($param_desc, $param_name) ?> - <?= $this->form->select('params['.$param_name.']', $columns_list, $values) ?><br/> + <?= $this->form->select('params['.$param_name.']', $columns_list, $values) ?> <?php elseif ($this->text->contains($param_name, 'user_id')): ?> <?= $this->form->label($param_desc, $param_name) ?> - <?= $this->form->select('params['.$param_name.']', $users_list, $values) ?><br/> + <?= $this->form->select('params['.$param_name.']', $users_list, $values) ?> <?php elseif ($this->text->contains($param_name, 'project_id')): ?> <?= $this->form->label($param_desc, $param_name) ?> - <?= $this->form->select('params['.$param_name.']', $projects_list, $values) ?><br/> + <?= $this->form->select('params['.$param_name.']', $projects_list, $values) ?> <?php elseif ($this->text->contains($param_name, 'color_id')): ?> <?= $this->form->label($param_desc, $param_name) ?> - <?= $this->form->select('params['.$param_name.']', $colors_list, $values) ?><br/> + <?= $this->form->select('params['.$param_name.']', $colors_list, $values) ?> <?php elseif ($this->text->contains($param_name, 'category_id')): ?> <?= $this->form->label($param_desc, $param_name) ?> - <?= $this->form->select('params['.$param_name.']', $categories_list, $values) ?><br/> + <?= $this->form->select('params['.$param_name.']', $categories_list, $values) ?> <?php elseif ($this->text->contains($param_name, 'link_id')): ?> <?= $this->form->label($param_desc, $param_name) ?> - <?= $this->form->select('params['.$param_name.']', $links_list, $values) ?><br/> + <?= $this->form->select('params['.$param_name.']', $links_list, $values) ?> + <?php elseif ($this->text->contains($param_name, 'duration')): ?> + <?= $this->form->label($param_desc, $param_name) ?> + <?= $this->form->number('params['.$param_name.']', $values) ?> <?php else: ?> <?= $this->form->label($param_desc, $param_name) ?> <?= $this->form->text('params['.$param_name.']', $values) ?> diff --git a/app/Template/analytic/layout.php b/app/Template/analytic/layout.php index ff532fc0..f1dba552 100644 --- a/app/Template/analytic/layout.php +++ b/app/Template/analytic/layout.php @@ -31,9 +31,8 @@ </li> </ul> </div> - <section class="sidebar-container" id="analytic-section"> - - <?= $this->render('analytic/sidebar', array('project' => $project)) ?> + <section class="sidebar-container"> + <?= $this->render($sidebar_template, array('project' => $project)) ?> <div class="sidebar-content"> <?= $content_for_sublayout ?> diff --git a/app/Template/analytic/sidebar.php b/app/Template/analytic/sidebar.php index 19eef8d5..f1810c6a 100644 --- a/app/Template/analytic/sidebar.php +++ b/app/Template/analytic/sidebar.php @@ -23,6 +23,4 @@ <?= $this->url->link(t('Estimated vs actual time'), 'analytic', 'compareHours', array('project_id' => $project['id'])) ?> </li> </ul> - <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> - <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> </div> diff --git a/app/Template/app/layout.php b/app/Template/app/layout.php index ad1d5a9e..0550cef4 100644 --- a/app/Template/app/layout.php +++ b/app/Template/app/layout.php @@ -1,15 +1,15 @@ <section id="main"> <div class="page-header page-header-mobile"> <ul> - <?php if ($this->user->hasAccess('project', 'create')): ?> + <?php if ($this->user->hasAccess('ProjectCreation', 'create')): ?> <li> <i class="fa fa-plus fa-fw"></i> - <?= $this->url->link(t('New project'), 'project', 'create') ?> + <?= $this->url->link(t('New project'), 'ProjectCreation', 'create', array(), false, 'popover') ?> </li> <?php endif ?> <li> <i class="fa fa-lock fa-fw"></i> - <?= $this->url->link(t('New private project'), 'project', 'createPrivate') ?> + <?= $this->url->link(t('New private project'), 'ProjectCreation', 'createPrivate', array(), false, 'popover') ?> </li> <li> <i class="fa fa-search fa-fw"></i> @@ -19,20 +19,10 @@ <i class="fa fa-folder fa-fw"></i> <?= $this->url->link(t('Project management'), 'project', 'index') ?> </li> - <?php if ($this->user->hasAccess('user', 'index')): ?> - <li> - <i class="fa fa-user fa-fw"></i> - <?= $this->url->link(t('User management'), 'user', 'index') ?> - </li> - <li> - <i class="fa fa-cog fa-fw"></i> - <?= $this->url->link(t('Settings'), 'config', 'index') ?> - </li> - <?php endif ?> </ul> </div> <section class="sidebar-container" id="dashboard"> - <?= $this->render('app/sidebar', array('user' => $user)) ?> + <?= $this->render($sidebar_template, array('user' => $user)) ?> <div class="sidebar-content"> <?= $content_for_sublayout ?> </div> diff --git a/app/Template/app/sidebar.php b/app/Template/app/sidebar.php index b5e14aaf..3f0d988b 100644 --- a/app/Template/app/sidebar.php +++ b/app/Template/app/sidebar.php @@ -24,6 +24,4 @@ </li> <?= $this->hook->render('template:dashboard:sidebar') ?> </ul> - <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> - <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> </div>
\ No newline at end of file diff --git a/app/Template/app/subtasks.php b/app/Template/app/subtasks.php index b4c87bab..f72f21fb 100644 --- a/app/Template/app/subtasks.php +++ b/app/Template/app/subtasks.php @@ -24,7 +24,7 @@ <?= $this->url->link($this->e($subtask['task_name']), 'task', 'show', array('task_id' => $subtask['task_id'], 'project_id' => $subtask['project_id'])) ?> </td> <td> - <?= $this->subtask->toggleStatus($subtask, 'dashboard') ?> + <?= $this->subtask->toggleStatus($subtask, $subtask['project_id']) ?> </td> <td> <?php if (! empty($subtask['time_spent'])): ?> diff --git a/app/Template/auth/index.php b/app/Template/auth/index.php index a1059d6f..41441e69 100644 --- a/app/Template/auth/index.php +++ b/app/Template/auth/index.php @@ -39,21 +39,4 @@ <?php endif ?> <?= $this->hook->render('template:auth:login-form:after') ?> - - <?php if (GOOGLE_AUTH || GITHUB_AUTH || GITLAB_AUTH): ?> - <ul class="no-bullet"> - <?php if (GOOGLE_AUTH): ?> - <li><?= $this->url->link(t('Login with my Google Account'), 'oauth', 'google') ?></li> - <?php endif ?> - - <?php if (GITHUB_AUTH): ?> - <li><?= $this->url->link(t('Login with my Github Account'), 'oauth', 'github') ?></li> - <?php endif ?> - - <?php if (GITLAB_AUTH): ?> - <li><?= $this->url->link(t('Login with my Gitlab Account'), 'oauth', 'gitlab') ?></li> - <?php endif ?> - </ul> - <?php endif ?> - </div>
\ No newline at end of file diff --git a/app/Template/board/popover_assignee.php b/app/Template/board/popover_assignee.php index e86ba420..8db95323 100644 --- a/app/Template/board/popover_assignee.php +++ b/app/Template/board/popover_assignee.php @@ -9,8 +9,7 @@ <?= $this->form->hidden('id', $values) ?> <?= $this->form->hidden('project_id', $values) ?> - <?= $this->form->label(t('Assignee'), 'owner_id') ?> - <?= $this->form->select('owner_id', $users_list, $values, array(), array('autofocus')) ?><br/> + <?= $this->task->selectAssignee($users_list, $values, array(), array('autofocus')) ?> <div class="form-actions"> <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> diff --git a/app/Template/board/popover_category.php b/app/Template/board/popover_category.php index 224ce8d1..e65593cd 100644 --- a/app/Template/board/popover_category.php +++ b/app/Template/board/popover_category.php @@ -9,8 +9,7 @@ <?= $this->form->hidden('id', $values) ?> <?= $this->form->hidden('project_id', $values) ?> - <?= $this->form->label(t('Category'), 'category_id') ?> - <?= $this->form->select('category_id', $categories_list, $values, array(), array('autofocus')) ?><br/> + <?= $this->task->selectCategory($categories_list, $values, array(), array('autofocus'), true) ?> <div class="form-actions"> <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> diff --git a/app/Template/board/task_footer.php b/app/Template/board/task_footer.php index 26f3b1d4..1912dd83 100644 --- a/app/Template/board/task_footer.php +++ b/app/Template/board/task_footer.php @@ -35,7 +35,11 @@ <?php endif ?> <?php if (! empty($task['nb_links'])): ?> - <span title="<?= t('Links') ?>" class="tooltip" data-href="<?= $this->url->href('BoardTooltip', 'tasklinks', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-code-fork"></i> <?= $task['nb_links'] ?></span> + <span title="<?= t('Links') ?>" class="tooltip" data-href="<?= $this->url->href('BoardTooltip', 'tasklinks', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-code-fork fa-fw"></i><?= $task['nb_links'] ?></span> + <?php endif ?> + + <?php if (! empty($task['nb_external_links'])): ?> + <span title="<?= t('External links') ?>" class="tooltip" data-href="<?= $this->url->href('BoardTooltip', 'externallinks', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-external-link fa-fw"></i><?= $task['nb_external_links'] ?></span> <?php endif ?> <?php if (! empty($task['nb_subtasks'])): ?> diff --git a/app/Template/board/task_menu.php b/app/Template/board/task_menu.php index b5ed125d..bd582185 100644 --- a/app/Template/board/task_menu.php +++ b/app/Template/board/task_menu.php @@ -7,11 +7,12 @@ <li><i class="fa fa-pencil-square-o fa-fw"></i> <?= $this->url->link(t('Edit this task'), 'taskmodification', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li> <li><i class="fa fa-comment-o fa-fw"></i> <?= $this->url->link(t('Add a comment'), 'comment', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li> <li><i class="fa fa-code-fork fa-fw"></i> <?= $this->url->link(t('Add a link'), 'tasklink', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li> + <li><i class="fa fa-external-link fa-fw"></i> <?= $this->url->link(t('Add external link'), 'TaskExternalLink', 'find', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li> <li><i class="fa fa-camera fa-fw"></i> <?= $this->url->link(t('Add a screenshot'), 'BoardPopover', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li> <?php if ($task['is_active'] == 1): ?> - <li><i class="fa fa-close fa-fw"></i> <?= $this->url->link(t('Close this task'), 'taskstatus', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board'), false, 'popover') ?></li> + <li><i class="fa fa-close fa-fw"></i> <?= $this->url->link(t('Close this task'), 'taskstatus', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li> <?php else: ?> - <li><i class="fa fa-check-square-o fa-fw"></i> <?= $this->url->link(t('Open this task'), 'taskstatus', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board'), false, 'popover') ?></li> + <li><i class="fa fa-check-square-o fa-fw"></i> <?= $this->url->link(t('Open this task'), 'taskstatus', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li> <?php endif ?> </ul> </span>
\ No newline at end of file diff --git a/app/Template/board/tooltip_external_links.php b/app/Template/board/tooltip_external_links.php new file mode 100644 index 00000000..7681c06c --- /dev/null +++ b/app/Template/board/tooltip_external_links.php @@ -0,0 +1,20 @@ +<table class="table-striped table-small"> + <tr> + <th class="column-20"><?= t('Type') ?></th> + <th class="column-80"><?= t('Title') ?></th> + <th class="column-10"><?= t('Dependency') ?></th> + </tr> + <?php foreach ($links as $link): ?> + <tr> + <td> + <?= $link['type'] ?> + </td> + <td> + <a href="<?= $link['url'] ?>" target="_blank"><?= $this->e($link['title']) ?></a> + </td> + <td> + <?= $this->e($link['dependency_label']) ?> + </td> + </tr> + <?php endforeach ?> +</table>
\ No newline at end of file diff --git a/app/Template/board/tooltip_subtasks.php b/app/Template/board/tooltip_subtasks.php index 5c273e08..65b7ce4d 100644 --- a/app/Template/board/tooltip_subtasks.php +++ b/app/Template/board/tooltip_subtasks.php @@ -1,7 +1,12 @@ -<section id="tooltip-subtasks"> +<table class="table-stripped"> <?php foreach ($subtasks as $subtask): ?> - <?= $this->subtask->toggleStatus($subtask, 'board', $task['project_id']) ?> - <?= $this->e(empty($subtask['username']) ? '' : ' ['.$this->user->getFullname($subtask).']') ?> - <br/> + <tr> + <td class="column-80"> + <?= $this->subtask->toggleStatus($subtask, $task['project_id']) ?> + </td> + <td> + <?= $this->e($subtask['username'] ?: $this->user->getFullname($subtask)) ?> + </td> + </tr> <?php endforeach ?> -</section> +</table> diff --git a/app/Template/board/tooltip_tasklinks.php b/app/Template/board/tooltip_tasklinks.php index 62304330..b51f90ed 100644 --- a/app/Template/board/tooltip_tasklinks.php +++ b/app/Template/board/tooltip_tasklinks.php @@ -1,19 +1,24 @@ <div class="tooltip-tasklinks"> - <ul> - <?php foreach ($links as $link): ?> - <li> - <strong><?= t($link['label']) ?></strong> - [<i><?= $link['project_name'] ?></i>] - <?= $this->url->link( - $this->e('#'.$link['task_id'].' - '.$link['title']), - 'task', 'show', array('task_id' => $link['task_id'], 'project_id' => $link['project_id']), - false, - $link['is_active'] ? '' : 'task-link-closed' - ) ?> - <?php if (! empty($link['task_assignee_username'])): ?> - [<?= $this->e($link['task_assignee_name'] ?: $link['task_assignee_username']) ?>] - <?php endif ?> - </li> + <dl> + <?php foreach ($links as $label => $grouped_links): ?> + <dt><strong><?= t($label) ?></strong></dt> + <?php foreach ($grouped_links as $link): ?> + <dd> + <span class="progress"><?= $this->task->getProgress($link).'%' ?></span> + <?= $this->url->link( + $this->e('#'.$link['task_id'].' '.$link['title']), + 'task', 'show', array('task_id' => $link['task_id'], 'project_id' => $link['project_id']), + false, + $link['is_active'] ? '' : 'task-link-closed' + ) ?> + <?php if (! empty($link['task_assignee_username'])): ?> + [<?= $this->e($link['task_assignee_name'] ?: $link['task_assignee_username']) ?>] + <?php endif ?> + <?php if ($task['project_id'] != $link['project_id']): ?> + (<i><?= $link['project_name'] ?></i>) + <?php endif ?> + </dd> + <?php endforeach ?> <?php endforeach ?> - </ul> + </dl> </div>
\ No newline at end of file diff --git a/app/Template/comment/create.php b/app/Template/comment/create.php index e9a6404d..15dd3a8e 100644 --- a/app/Template/comment/create.php +++ b/app/Template/comment/create.php @@ -1,8 +1,7 @@ <div class="page-header"> <h2><?= t('Add a comment') ?></h2> </div> - -<form method="post" action="<?= $this->url->href('comment', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => isset($ajax))) ?>" autocomplete="off" class="form-comment"> +<form class="popover-form" method="post" action="<?= $this->url->href('comment', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off" class="form-comment"> <?= $this->form->csrf() ?> <?= $this->form->hidden('task_id', $values) ?> <?= $this->form->hidden('user_id', $values) ?> @@ -41,11 +40,7 @@ <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> <?php if (! isset($skip_cancel)): ?> <?= t('or') ?> - <?php if (isset($ajax)): ?> - <?= $this->url->link(t('cancel'), 'board', 'show', array('project_id' => $task['project_id'])) ?> - <?php else: ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - <?php endif ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> <?php endif ?> </div> </form> diff --git a/app/Template/comment/edit.php b/app/Template/comment/edit.php index e01f3da4..6db952cc 100644 --- a/app/Template/comment/edit.php +++ b/app/Template/comment/edit.php @@ -2,7 +2,7 @@ <h2><?= t('Edit a comment') ?></h2> </div> -<form method="post" action="<?= $this->url->href('comment', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id'])) ?>" autocomplete="off"> +<form class="popover-form" method="post" action="<?= $this->url->href('comment', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> @@ -29,8 +29,8 @@ <div class="form-help"><?= $this->url->doc(t('Write your text in Markdown'), 'syntax-guide') ?></div> <div class="form-actions"> - <input type="submit" value="<?= t('Update') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Update') ?>" class="btn btn-blue"> <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> </form> diff --git a/app/Template/comment/forbidden.php b/app/Template/comment/forbidden.php deleted file mode 100644 index 1e306d45..00000000 --- a/app/Template/comment/forbidden.php +++ /dev/null @@ -1,7 +0,0 @@ -<div class="page-header"> - <h2><?= t('Forbidden') ?></h2> -</div> - -<p class="alert alert-error"> - <?= t('Only administrators or the creator of the comment can access to this page.') ?> -</p>
\ No newline at end of file diff --git a/app/Template/comment/remove.php b/app/Template/comment/remove.php index afc3346f..1b5004f4 100644 --- a/app/Template/comment/remove.php +++ b/app/Template/comment/remove.php @@ -12,6 +12,6 @@ <div class="form-actions"> <?= $this->url->link(t('Yes'), 'comment', 'remove', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id']), true, 'btn btn-red') ?> <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> </div>
\ No newline at end of file diff --git a/app/Template/comment/show.php b/app/Template/comment/show.php index 44457653..26b300e8 100644 --- a/app/Template/comment/show.php +++ b/app/Template/comment/show.php @@ -18,10 +18,10 @@ <li><a href="#comment-<?= $comment['id'] ?>"><?= t('link') ?></a></li> <?php if ($editable && ($this->user->isAdmin() || $this->user->isCurrentUser($comment['user_id']))): ?> <li> - <?= $this->url->link(t('remove'), 'comment', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id'])) ?> + <?= $this->url->link(t('remove'), 'comment', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id']), false, 'popover') ?> </li> <li> - <?= $this->url->link(t('edit'), 'comment', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id'])) ?> + <?= $this->url->link(t('edit'), 'comment', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id']), false, 'popover') ?> </li> <?php endif ?> </ul> diff --git a/app/Template/config/application.php b/app/Template/config/application.php index ec7d8462..35d85dd8 100644 --- a/app/Template/config/application.php +++ b/app/Template/config/application.php @@ -1,7 +1,6 @@ <div class="page-header"> <h2><?= t('Application settings') ?></h2> </div> -<section> <form method="post" action="<?= $this->url->href('config', 'application') ?>" autocomplete="off"> <?= $this->form->csrf() ?> @@ -23,10 +22,9 @@ <?= $this->form->checkbox('password_reset', t('Enable "Forget Password"'), 1, $values['password_reset'] == 1) ?> <?= $this->form->label(t('Custom Stylesheet'), 'application_stylesheet') ?> - <?= $this->form->textarea('application_stylesheet', $values, $errors) ?><br/> + <?= $this->form->textarea('application_stylesheet', $values, $errors) ?> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> </div> </form> -</section>
\ No newline at end of file diff --git a/app/Template/config/board.php b/app/Template/config/board.php index 19a4bcd7..f787a931 100644 --- a/app/Template/config/board.php +++ b/app/Template/config/board.php @@ -1,25 +1,23 @@ <div class="page-header"> <h2><?= t('Board settings') ?></h2> </div> -<section> <form method="post" action="<?= $this->url->href('config', 'board') ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->label(t('Task highlight period'), 'board_highlight_period') ?> - <?= $this->form->number('board_highlight_period', $values, $errors) ?><br/> + <?= $this->form->number('board_highlight_period', $values, $errors) ?> <p class="form-help"><?= t('Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)') ?></p> <?= $this->form->label(t('Refresh interval for public board'), 'board_public_refresh_interval') ?> - <?= $this->form->number('board_public_refresh_interval', $values, $errors) ?><br/> + <?= $this->form->number('board_public_refresh_interval', $values, $errors) ?> <p class="form-help"><?= t('Frequency in second (60 seconds by default)') ?></p> <?= $this->form->label(t('Refresh interval for private board'), 'board_private_refresh_interval') ?> - <?= $this->form->number('board_private_refresh_interval', $values, $errors) ?><br/> + <?= $this->form->number('board_private_refresh_interval', $values, $errors) ?> <p class="form-help"><?= t('Frequency in second (0 to disable this feature, 10 seconds by default)') ?></p> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> </div> </form> -</section>
\ No newline at end of file diff --git a/app/Template/config/integrations.php b/app/Template/config/integrations.php index bba85672..ced051f7 100644 --- a/app/Template/config/integrations.php +++ b/app/Template/config/integrations.php @@ -3,29 +3,9 @@ </div> <form method="post" action="<?= $this->url->href('config', 'integrations') ?>" autocomplete="off"> - <?= $this->form->csrf() ?> - <?= $this->hook->render('template:config:integrations', array('values' => $values)) ?> - <h3><i class="fa fa-google"></i> <?= t('Google Authentication') ?></h3> - <div class="listing"> - <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('oauth', 'google', array(), false, '', true) ?>"/><br/> - <p class="form-help"><?= $this->url->doc(t('Help on Google authentication'), 'google-authentication') ?></p> - </div> - - <h3><i class="fa fa-github"></i> <?= t('Github Authentication') ?></h3> - <div class="listing"> - <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('oauth', 'github', array(), false, '', true) ?>"/><br/> - <p class="form-help"><?= $this->url->doc(t('Help on Github authentication'), 'github-authentication') ?></p> - </div> - - <h3><img src="<?= $this->url->dir() ?>assets/img/gitlab-icon.png"/> <?= t('Gitlab Authentication') ?></h3> - <div class="listing"> - <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('oauth', 'gitlab', array(), false, '', true) ?>"/><br/> - <p class="form-help"><?= $this->url->doc(t('Help on Gitlab authentication'), 'gitlab-authentication') ?></p> - </div> - <h3><img src="<?= $this->url->dir() ?>assets/img/gravatar-icon.png"/> <?= t('Gravatar') ?></h3> <div class="listing"> <?= $this->form->checkbox('integration_gravatar', t('Enable Gravatar images'), 1, $values['integration_gravatar'] == 1) ?> diff --git a/app/Template/config/layout.php b/app/Template/config/layout.php index 028f138c..f34caaab 100644 --- a/app/Template/config/layout.php +++ b/app/Template/config/layout.php @@ -1,10 +1,10 @@ <section id="main"> <section class="sidebar-container" id="config-section"> - <?= $this->render('config/sidebar') ?> + <?= $this->render($sidebar_template) ?> <div class="sidebar-content"> - <?= $config_content_for_layout ?> + <?= $content_for_sublayout ?> </div> </section> </section>
\ No newline at end of file diff --git a/app/Template/config/project.php b/app/Template/config/project.php index c58a7bac..a212f65f 100644 --- a/app/Template/config/project.php +++ b/app/Template/config/project.php @@ -1,7 +1,6 @@ <div class="page-header"> <h2><?= t('Project settings') ?></h2> </div> -<section> <form method="post" action="<?= $this->url->href('config', 'project') ?>" autocomplete="off"> <?= $this->form->csrf() ?> @@ -10,11 +9,11 @@ <?= $this->form->select('default_color', $colors, $values, $errors) ?> <?= $this->form->label(t('Default columns for new projects (Comma-separated)'), 'board_columns') ?> - <?= $this->form->text('board_columns', $values, $errors) ?><br/> + <?= $this->form->text('board_columns', $values, $errors) ?> <p class="form-help"><?= t('Default values are "%s"', $default_columns) ?></p> <?= $this->form->label(t('Default categories for new projects (Comma-separated)'), 'project_categories') ?> - <?= $this->form->text('project_categories', $values, $errors) ?><br/> + <?= $this->form->text('project_categories', $values, $errors) ?> <p class="form-help"><?= t('Example: "Bug, Feature Request, Improvement"') ?></p> <?= $this->form->checkbox('subtask_restriction', t('Allow only one subtask in progress at the same time for a user'), 1, $values['subtask_restriction'] == 1) ?> @@ -22,7 +21,6 @@ <?= $this->form->checkbox('cfd_include_closed_tasks', t('Include closed tasks in the cumulative flow diagram'), 1, $values['cfd_include_closed_tasks'] == 1) ?> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> </div> </form> -</section>
\ No newline at end of file diff --git a/app/Template/config/sidebar.php b/app/Template/config/sidebar.php index a8174505..dd51bc74 100644 --- a/app/Template/config/sidebar.php +++ b/app/Template/config/sidebar.php @@ -34,11 +34,6 @@ <li <?= $this->app->checkMenuSelection('config', 'api') ?>> <?= $this->url->link(t('API'), 'config', 'api') ?> </li> - <li> - <?= $this->url->link(t('Documentation'), 'doc', 'show') ?> - </li> <?= $this->hook->render('template:config:sidebar') ?> </ul> - <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> - <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> </div>
\ No newline at end of file diff --git a/app/Template/currency/index.php b/app/Template/currency/index.php index 1c78c47a..07b58a8b 100644 --- a/app/Template/currency/index.php +++ b/app/Template/currency/index.php @@ -29,10 +29,10 @@ <?= $this->form->csrf() ?> <?= $this->form->label(t('Reference currency'), 'application_currency') ?> - <?= $this->form->select('application_currency', $currencies, $config_values, $errors) ?><br/> + <?= $this->form->select('application_currency', $currencies, $config_values, $errors) ?> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> </div> </form> @@ -43,12 +43,12 @@ <?= $this->form->csrf() ?> <?= $this->form->label(t('Currency'), 'currency') ?> - <?= $this->form->select('currency', $currencies, $values, $errors) ?><br/> + <?= $this->form->select('currency', $currencies, $values, $errors) ?> <?= $this->form->label(t('Rate'), 'rate') ?> - <?= $this->form->text('rate', $values, $errors, array(), 'form-numeric') ?><br/> + <?= $this->form->text('rate', $values, $errors, array(), 'form-numeric') ?> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> </div> </form> diff --git a/app/Template/export/sidebar.php b/app/Template/export/sidebar.php index 44448520..6a1de7e9 100644 --- a/app/Template/export/sidebar.php +++ b/app/Template/export/sidebar.php @@ -15,6 +15,4 @@ </li> <?= $this->hook->render('template:export:sidebar') ?> </ul> - <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> - <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> </div>
\ No newline at end of file diff --git a/app/Template/export/subtasks.php b/app/Template/export/subtasks.php index 4aad2641..f2a00f84 100644 --- a/app/Template/export/subtasks.php +++ b/app/Template/export/subtasks.php @@ -1,7 +1,5 @@ <div class="page-header"> - <h2> - <?= t('Subtasks exportation for "%s"', $project['name']) ?> - </h2> + <h2><?= t('Subtasks exportation for "%s"', $project['name']) ?></h2> </div> <p class="alert alert-info"><?= t('This report contains all subtasks information for the given date range.') ?></p> @@ -13,7 +11,7 @@ <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Start Date'), 'from') ?> - <?= $this->form->text('from', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?><br/> + <?= $this->form->text('from', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?> <?= $this->form->label(t('End Date'), 'to') ?> <?= $this->form->text('to', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?> @@ -21,6 +19,6 @@ <div class="form-help"><?= t('Others formats accepted: %s and %s', date('Y-m-d'), date('Y_m_d')) ?></div> <div class="form-actions"> - <input type="submit" value="<?= t('Execute') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Execute') ?>" class="btn btn-blue"> </div> </form>
\ No newline at end of file diff --git a/app/Template/export/summary.php b/app/Template/export/summary.php index ffbd6ac2..0c2a96fb 100644 --- a/app/Template/export/summary.php +++ b/app/Template/export/summary.php @@ -1,7 +1,5 @@ <div class="page-header"> - <h2> - <?= t('Daily project summary export for "%s"', $project['name']) ?> - </h2> + <h2><?= t('Daily project summary export for "%s"', $project['name']) ?></h2> </div> <p class="alert alert-info"><?= t('This export contains the number of tasks per column grouped per day.') ?></p> @@ -13,7 +11,7 @@ <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Start Date'), 'from') ?> - <?= $this->form->text('from', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?><br/> + <?= $this->form->text('from', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?> <?= $this->form->label(t('End Date'), 'to') ?> <?= $this->form->text('to', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?> @@ -21,6 +19,6 @@ <div class="form-help"><?= t('Others formats accepted: %s and %s', date('Y-m-d'), date('Y_m_d')) ?></div> <div class="form-actions"> - <input type="submit" value="<?= t('Execute') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Execute') ?>" class="btn btn-blue"> </div> </form>
\ No newline at end of file diff --git a/app/Template/export/tasks.php b/app/Template/export/tasks.php index c74c8f98..c27149d2 100644 --- a/app/Template/export/tasks.php +++ b/app/Template/export/tasks.php @@ -1,7 +1,5 @@ <div class="page-header"> - <h2> - <?= t('Tasks exportation for "%s"', $project['name']) ?> - </h2> + <h2><?= t('Tasks exportation for "%s"', $project['name']) ?></h2> </div> <p class="alert alert-info"><?= t('This report contains all tasks information for the given date range.') ?></p> @@ -13,7 +11,7 @@ <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Start Date'), 'from') ?> - <?= $this->form->text('from', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?><br/> + <?= $this->form->text('from', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?> <?= $this->form->label(t('End Date'), 'to') ?> <?= $this->form->text('to', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?> @@ -21,6 +19,6 @@ <div class="form-help"><?= t('Others formats accepted: %s and %s', date('Y-m-d'), date('Y_m_d')) ?></div> <div class="form-actions"> - <input type="submit" value="<?= t('Execute') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Execute') ?>" class="btn btn-blue"> </div> </form>
\ No newline at end of file diff --git a/app/Template/export/transitions.php b/app/Template/export/transitions.php index bf6ef249..d935bde1 100644 --- a/app/Template/export/transitions.php +++ b/app/Template/export/transitions.php @@ -1,7 +1,5 @@ <div class="page-header"> - <h2> - <?= t('Task transitions export') ?> - </h2> + <h2><?= t('Task transitions export') ?></h2> </div> <p class="alert alert-info"><?= t('This report contains all column moves for each task with the date, the user and the time spent for each transition.') ?></p> @@ -13,7 +11,7 @@ <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Start Date'), 'from') ?> - <?= $this->form->text('from', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?><br/> + <?= $this->form->text('from', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?> <?= $this->form->label(t('End Date'), 'to') ?> <?= $this->form->text('to', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?> @@ -21,6 +19,6 @@ <div class="form-help"><?= t('Others formats accepted: %s and %s', date('Y-m-d'), date('Y_m_d')) ?></div> <div class="form-actions"> - <input type="submit" value="<?= t('Execute') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Execute') ?>" class="btn btn-blue"> </div> </form>
\ No newline at end of file diff --git a/app/Template/file/new.php b/app/Template/file/new.php index a1a59eae..1702638d 100644 --- a/app/Template/file/new.php +++ b/app/Template/file/new.php @@ -7,8 +7,8 @@ <input type="file" name="files[]" multiple /> <div class="form-help"><?= t('Maximum size: ') ?><?= is_integer($max_size) ? $this->text->bytes($max_size) : $max_size ?></div> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> </form>
\ No newline at end of file diff --git a/app/Template/file/screenshot.php b/app/Template/file/screenshot.php index 73b72eae..58b93ac3 100644 --- a/app/Template/file/screenshot.php +++ b/app/Template/file/screenshot.php @@ -6,7 +6,7 @@ <p id="screenshot-inner"><?= t('Take a screenshot and press CTRL+V or ⌘+V to paste here.') ?></p> </div> -<form action="<?= $this->url->href('file', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => $redirect)) ?>" method="post"> +<form class="popover-form" action="<?= $this->url->href('file', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post"> <input type="hidden" name="screenshot"/> <?= $this->form->csrf() ?> <div class="form-actions"> diff --git a/app/Template/gantt/projects.php b/app/Template/gantt/projects.php index 46d2af91..84b260bb 100644 --- a/app/Template/gantt/projects.php +++ b/app/Template/gantt/projects.php @@ -1,12 +1,6 @@ <section id="main"> <div class="page-header"> <ul> - <?php if ($this->user->hasAccess('project', 'create')): ?> - <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New project'), 'project', 'create') ?></li> - <?php endif ?> - <li> - <i class="fa fa-lock fa-fw"></i><?= $this->url->link(t('New private project'), 'project', 'create', array('private' => 1)) ?> - </li> <li> <i class="fa fa-folder fa-fw"></i><?= $this->url->link(t('Projects list'), 'project', 'index') ?> </li> diff --git a/app/Template/gantt/task_creation.php b/app/Template/gantt/task_creation.php index 7997e231..a08f41b4 100644 --- a/app/Template/gantt/task_creation.php +++ b/app/Template/gantt/task_creation.php @@ -33,26 +33,13 @@ </div> <div class="form-column"> - <?= $this->form->label(t('Assignee'), 'owner_id') ?> - <?= $this->form->select('owner_id', $users_list, $values, $errors, array('tabindex="3"')) ?><br/> - - <?= $this->form->label(t('Category'), 'category_id') ?> - <?= $this->form->select('category_id', $categories_list, $values, $errors, array('tabindex="4"')) ?><br/> - - <?php if (! (count($swimlanes_list) === 1 && key($swimlanes_list) === 0)): ?> - <?= $this->form->label(t('Swimlane'), 'swimlane_id') ?> - <?= $this->form->select('swimlane_id', $swimlanes_list, $values, $errors, array('tabindex="5"')) ?><br/> - <?php endif ?> - - <?= $this->form->label(t('Complexity'), 'score') ?> - <?= $this->form->number('score', $values, $errors, array('tabindex="6"')) ?><br/> - - <?= $this->form->label(t('Start Date'), 'date_started') ?> - <?= $this->form->text('date_started', $values, $errors, array('placeholder="'.$this->text->in($date_format, $date_formats).'"', 'tabindex="7"'), 'form-date') ?> - - <?= $this->form->label(t('Due Date'), 'date_due') ?> - <?= $this->form->text('date_due', $values, $errors, array('placeholder="'.$this->text->in($date_format, $date_formats).'"', 'tabindex="8"'), 'form-date') ?><br/> - <div class="form-help"><?= t('Others formats accepted: %s and %s', date('Y-m-d'), date('Y_m_d')) ?></div> + <?= $this->task->selectAssignee($users_list, $values, $errors) ?> + <?= $this->task->selectCategory($categories_list, $values, $errors) ?> + <?= $this->task->selectSwimlane($swimlanes_list, $values, $errors) ?> + <?= $this->task->selectPriority($project, $values) ?> + <?= $this->task->selectScore($values, $errors) ?> + <?= $this->task->selectStartDate($values, $errors) ?> + <?= $this->task->selectDueDate($values, $errors) ?> </div> <div class="form-actions"> diff --git a/app/Template/group/associate.php b/app/Template/group/associate.php index dc665bb3..468281e2 100644 --- a/app/Template/group/associate.php +++ b/app/Template/group/associate.php @@ -13,10 +13,10 @@ <?= $this->form->hidden('group_id', $values) ?> <?= $this->form->label(t('User'), 'user_id') ?> - <?= $this->form->select('user_id', $users, $values, $errors, array('required'), 'chosen-select') ?><br/> + <?= $this->form->select('user_id', $users, $values, $errors, array('required'), 'chosen-select') ?> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> <?= t('or') ?> <?= $this->url->link(t('cancel'), 'group', 'index') ?> </div> diff --git a/app/Template/group/create.php b/app/Template/group/create.php index 696e5013..4a935c08 100644 --- a/app/Template/group/create.php +++ b/app/Template/group/create.php @@ -8,10 +8,10 @@ <?= $this->form->csrf() ?> <?= $this->form->label(t('Name'), 'name') ?> - <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="100"')) ?><br/> + <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="100"')) ?> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> <?= t('or') ?> <?= $this->url->link(t('cancel'), 'group', 'index') ?> </div> diff --git a/app/Template/group/edit.php b/app/Template/group/edit.php index 4d7e5e81..d9646ee8 100644 --- a/app/Template/group/edit.php +++ b/app/Template/group/edit.php @@ -11,10 +11,10 @@ <?= $this->form->hidden('external_id', $values) ?> <?= $this->form->label(t('Name'), 'name') ?> - <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="100"')) ?><br/> + <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="100"')) ?> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> <?= t('or') ?> <?= $this->url->link(t('cancel'), 'group', 'index') ?> </div> diff --git a/app/Template/header.php b/app/Template/header.php index 405d07b3..fd9ff24d 100644 --- a/app/Template/header.php +++ b/app/Template/header.php @@ -31,22 +31,65 @@ </select> </li> <?php endif ?> - <li> + <li class="user-links"> <?php if ($this->user->hasNotifications()): ?> <span class="notification"> <?= $this->url->link('<i class="fa fa-bell web-notification-icon"></i>', 'app', 'notifications', array('user_id' => $this->user->getId()), false, '', t('Unread notifications')) ?> </span> <?php endif ?> - <span class="dropdown"> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-plus fa-fw"></i><i class="fa fa-caret-down"></i></a> + <ul> + <?php if ($this->user->hasAccess('ProjectCreation', 'create')): ?> + <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New project'), 'ProjectCreation', 'create', array(), false, 'popover') ?></li> + <?php endif ?> + <li> + <i class="fa fa-lock fa-fw"></i><?= $this->url->link(t('New private project'), 'ProjectCreation', 'createPrivate', array(), false, 'popover') ?> + </li> + </ul> + </div> + + <div class="dropdown"> <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-user fa-fw"></i><i class="fa fa-caret-down"></i></a> <ul> <li class="no-hover"><strong><?= $this->e($this->user->getFullname()) ?></strong></li> - <li><?= $this->url->link(t('My dashboard'), 'app', 'index', array('user_id' => $this->user->getId())) ?></li> - <li><?= $this->url->link(t('My profile'), 'user', 'show', array('user_id' => $this->user->getId())) ?></li> - <li><?= $this->url->link(t('Logout'), 'auth', 'logout') ?></li> + <li> + <i class="fa fa-tachometer fa-fw"></i> + <?= $this->url->link(t('My dashboard'), 'app', 'index', array('user_id' => $this->user->getId())) ?> + </li> + <li> + <i class="fa fa-home fa-fw"></i> + <?= $this->url->link(t('My profile'), 'user', 'show', array('user_id' => $this->user->getId())) ?> + </li> + <li> + <i class="fa fa-folder fa-fw"></i> + <?= $this->url->link(t('Projects management'), 'project', 'index') ?> + </li> + <?php if ($this->user->hasAccess('user', 'index')): ?> + <li> + <i class="fa fa-user fa-fw"></i> + <?= $this->url->link(t('Users management'), 'user', 'index') ?> + </li> + <li> + <i class="fa fa-group fa-fw"></i> + <?= $this->url->link(t('Groups management'), 'group', 'index') ?> + </li> + <li> + <i class="fa fa-cog fa-fw"></i> + <?= $this->url->link(t('Settings'), 'config', 'index') ?> + </li> + <?php endif ?> + <li> + <i class="fa fa-life-ring fa-fw"></i> + <?= $this->url->link(t('Documentation'), 'doc', 'show') ?> + </li> + <li> + <i class="fa fa-sign-out fa-fw"></i> + <?= $this->url->link(t('Logout'), 'auth', 'logout') ?> + </li> </ul> - </span> + </div> </li> </ul> </nav> diff --git a/app/Template/password_reset/change.php b/app/Template/password_reset/change.php index 310f0f97..6d06f442 100644 --- a/app/Template/password_reset/change.php +++ b/app/Template/password_reset/change.php @@ -4,13 +4,13 @@ <?= $this->form->csrf() ?> <?= $this->form->label(t('New password'), 'password') ?> - <?= $this->form->password('password', $values, $errors) ?><br/> + <?= $this->form->password('password', $values, $errors) ?> <?= $this->form->label(t('Confirmation'), 'confirmation') ?> <?= $this->form->password('confirmation', $values, $errors) ?> <div class="form-actions"> - <input type="submit" value="<?= t('Change Password') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Change Password') ?>" class="btn btn-blue"> </div> </form> </div>
\ No newline at end of file diff --git a/app/Template/project/duplicate.php b/app/Template/project/duplicate.php index 8967c306..ca7d3302 100644 --- a/app/Template/project/duplicate.php +++ b/app/Template/project/duplicate.php @@ -10,13 +10,17 @@ <?= $this->form->csrf() ?> + <?php if ($project['is_private'] == 0): ?> + <?= $this->form->checkbox('projectPermission', t('Permissions'), 1, true) ?> + <?php endif ?> + <?= $this->form->checkbox('category', t('Categories'), 1, true) ?> <?= $this->form->checkbox('action', t('Actions'), 1, true) ?> <?= $this->form->checkbox('swimlane', t('Swimlanes'), 1, false) ?> <?= $this->form->checkbox('task', t('Tasks'), 1, false) ?> <div class="form-actions"> - <input type="submit" value="<?= t('Duplicate') ?>" class="btn btn-red"/> + <input type="submit" value="<?= t('Duplicate') ?>" class="btn btn-red"> <?= t('or') ?> <?= $this->url->link(t('cancel'), 'project', 'show', array('project_id' => $project['id'])) ?> </div> </form> diff --git a/app/Template/project/index.php b/app/Template/project/index.php index 3d2a33ea..c5dd267c 100644 --- a/app/Template/project/index.php +++ b/app/Template/project/index.php @@ -1,10 +1,6 @@ <section id="main"> <div class="page-header"> <ul> - <?php if ($this->user->hasAccess('project', 'create')): ?> - <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New project'), 'project', 'create') ?></li> - <?php endif ?> - <li><i class="fa fa-lock fa-fw"></i><?= $this->url->link(t('New private project'), 'project', 'createPrivate') ?></li> <?php if ($this->user->hasAccess('projectuser', 'managers')): ?> <li><i class="fa fa-user fa-fw"></i><?= $this->url->link(t('Users overview'), 'projectuser', 'managers') ?></li> <?php endif ?> diff --git a/app/Template/project/layout.php b/app/Template/project/layout.php index 8ba92ef9..eb391ae5 100644 --- a/app/Template/project/layout.php +++ b/app/Template/project/layout.php @@ -30,7 +30,7 @@ <?= $this->render($sidebar_template, array('project' => $project)) ?> <div class="sidebar-content"> - <?= $project_content_for_layout ?> + <?= $content_for_sublayout ?> </div> </section> </section>
\ No newline at end of file diff --git a/app/Template/project/new.php b/app/Template/project/new.php deleted file mode 100644 index 8e4ccfec..00000000 --- a/app/Template/project/new.php +++ /dev/null @@ -1,24 +0,0 @@ -<section id="main"> - <div class="page-header"> - <ul> - <li><i class="fa fa-folder fa-fw"></i><?= $this->url->link(t('All projects'), 'project', 'index') ?></li> - </ul> - </div> - <form method="post" action="<?= $this->url->href('project', 'save') ?>" autocomplete="off"> - - <?= $this->form->csrf() ?> - <?= $this->form->hidden('is_private', $values) ?> - <?= $this->form->label(t('Name'), 'name') ?> - <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?> - - <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> - <?= t('or') ?> <?= $this->url->link(t('cancel'), 'project', 'index') ?> - </div> - </form> - <?php if (isset($is_private) && $is_private): ?> - <div class="alert alert-info"> - <p><?= t('There is no user management for private projects.') ?></p> - </div> - <?php endif ?> -</section>
\ No newline at end of file diff --git a/app/Template/project/sidebar.php b/app/Template/project/sidebar.php index 2f2ce3ce..304b4aee 100644 --- a/app/Template/project/sidebar.php +++ b/app/Template/project/sidebar.php @@ -63,6 +63,4 @@ <?= $this->hook->render('template:project:sidebar', array('project' => $project)) ?> </ul> - <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> - <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> </div> diff --git a/app/Template/project_creation/create.php b/app/Template/project_creation/create.php new file mode 100644 index 00000000..46ec5d1e --- /dev/null +++ b/app/Template/project_creation/create.php @@ -0,0 +1,42 @@ +<section id="main"> + <div class="page-header"> + <h2><?= $title ?></h2> + </div> + <form class="popover-form" id="project-creation-form" method="post" action="<?= $this->url->href('ProjectCreation', 'save') ?>" autocomplete="off"> + + <?= $this->form->csrf() ?> + <?= $this->form->hidden('is_private', $values) ?> + + <?= $this->form->label(t('Name'), 'name') ?> + <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?> + + <?php if (count($projects_list) > 1): ?> + <?= $this->form->label(t('Create from another project'), 'src_project_id') ?> + <?= $this->form->select('src_project_id', $projects_list, $values) ?> + <?php endif ?> + + <div class="project-creation-options" <?= isset($values['src_project_id']) && $values['src_project_id'] > 0 ? '' : 'style="display: none"' ?>> + <p class="alert"><?= t('Which parts of the project do you want to duplicate?') ?></p> + + <?php if (! $is_private): ?> + <?= $this->form->checkbox('projectPermission', t('Permissions'), 1, true) ?> + <?php endif ?> + + <?= $this->form->checkbox('category', t('Categories'), 1, true) ?> + <?= $this->form->checkbox('action', t('Actions'), 1, true) ?> + <?= $this->form->checkbox('swimlane', t('Swimlanes'), 1, true) ?> + <?= $this->form->checkbox('task', t('Tasks'), 1, false) ?> + </div> + + <div class="form-actions"> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> + <?= t('or') ?> + <?= $this->url->link(t('cancel'), 'project', 'index', array(), false, 'close-popover') ?> + </div> + </form> + <?php if ($is_private): ?> + <div class="alert alert-info"> + <p><?= t('There is no user management for private projects.') ?></p> + </div> + <?php endif ?> +</section>
\ No newline at end of file diff --git a/app/Template/project_edit/general.php b/app/Template/project_edit/general.php index 5caefa2d..28cbb66a 100644 --- a/app/Template/project_edit/general.php +++ b/app/Template/project_edit/general.php @@ -24,7 +24,7 @@ <?= $this->form->select('owner_id', $owners, $values, $errors) ?> </div> - <?php if ($this->user->hasProjectAccess('project', 'create', $project['id'])): ?> + <?php if ($this->user->hasProjectAccess('ProjectCreation', 'create', $project['id'])): ?> <hr> <?= $this->form->checkbox('is_private', t('Private project'), 1, $project['is_private'] == 1) ?> <p class="form-help"><?= t('Private projects do not have users and groups management.') ?></p> diff --git a/app/Template/project_user/layout.php b/app/Template/project_user/layout.php index 3a569da4..3ced5590 100644 --- a/app/Template/project_user/layout.php +++ b/app/Template/project_user/layout.php @@ -1,13 +1,6 @@ <section id="main"> <div class="page-header"> <ul> - <?php if ($this->user->hasAccess('project', 'create')): ?> - <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New project'), 'project', 'create') ?></li> - <?php endif ?> - <li> - <i class="fa fa-lock fa-fw"></i> - <?= $this->url->link(t('New private project'), 'project', 'create', array('private' => 1)) ?> - </li> <li> <i class="fa fa-folder fa-fw"></i> <?= $this->url->link(t('Projects list'), 'project', 'index') ?> @@ -22,7 +15,7 @@ </div> <section class="sidebar-container"> - <?= $this->render('project_user/sidebar', array('users' => $users, 'filter' => $filter)) ?> + <?= $this->render($sidebar_template, array('users' => $users, 'filter' => $filter)) ?> <div class="sidebar-content"> <div class="page-header"> diff --git a/app/Template/project_user/sidebar.php b/app/Template/project_user/sidebar.php index 27f1094c..ff113ebb 100644 --- a/app/Template/project_user/sidebar.php +++ b/app/Template/project_user/sidebar.php @@ -10,7 +10,7 @@ 'chosen-select select-auto-redirect' ) ?> - <br/><br/> + <br><br> <ul> <li <?= $this->app->checkMenuSelection('projectuser', 'managers') ?>> <?= $this->url->link(t('Project managers'), 'projectuser', 'managers', $filter) ?> diff --git a/app/Template/subtask/create.php b/app/Template/subtask/create.php index 82e378f5..8fffd3a9 100644 --- a/app/Template/subtask/create.php +++ b/app/Template/subtask/create.php @@ -2,26 +2,19 @@ <h2><?= t('Add a sub-task') ?></h2> </div> -<form method="post" action="<?= $this->url->href('subtask', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> +<form class="popover-form" method="post" action="<?= $this->url->href('subtask', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('task_id', $values) ?> - - <?= $this->form->label(t('Title'), 'title') ?> - <?= $this->form->text('title', $values, $errors, array('required', 'autofocus', 'maxlength="255"')) ?><br/> - - <?= $this->form->label(t('Assignee'), 'user_id') ?> - <?= $this->form->select('user_id', $users_list, $values, $errors) ?><br/> - - <?= $this->form->label(t('Original estimate'), 'time_estimated') ?> - <?= $this->form->numeric('time_estimated', $values, $errors) ?> <?= t('hours') ?><br/> + <?= $this->subtask->selectTitle($values, $errors, array('autofocus')) ?> + <?= $this->subtask->selectAssignee($users_list, $values, $errors) ?> + <?= $this->subtask->selectTimeEstimated($values, $errors) ?> <?= $this->form->checkbox('another_subtask', t('Create another sub-task'), 1, isset($values['another_subtask']) && $values['another_subtask'] == 1) ?> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> </form> diff --git a/app/Template/subtask/edit.php b/app/Template/subtask/edit.php index 2e583069..acce625e 100644 --- a/app/Template/subtask/edit.php +++ b/app/Template/subtask/edit.php @@ -2,28 +2,19 @@ <h2><?= t('Edit a sub-task') ?></h2> </div> -<form method="post" action="<?= $this->url->href('subtask', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>" autocomplete="off"> +<form class="popover-form" method="post" action="<?= $this->url->href('subtask', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('id', $values) ?> <?= $this->form->hidden('task_id', $values) ?> - - <?= $this->form->label(t('Title'), 'title') ?> - <?= $this->form->text('title', $values, $errors, array('required', 'autofocus', 'maxlength="255"')) ?><br/> - - <?= $this->form->label(t('Assignee'), 'user_id') ?> - <?= $this->form->select('user_id', $users_list, $values, $errors) ?><br/> - - <?= $this->form->label(t('Original estimate'), 'time_estimated') ?> - <?= $this->form->numeric('time_estimated', $values, $errors) ?> <?= t('hours') ?><br/> - - <?= $this->form->label(t('Time spent'), 'time_spent') ?> - <?= $this->form->numeric('time_spent', $values, $errors) ?> <?= t('hours') ?><br/> + <?= $this->subtask->selectTitle($values, $errors, array('autofocus')) ?> + <?= $this->subtask->selectAssignee($users_list, $values, $errors) ?> + <?= $this->subtask->selectTimeEstimated($values, $errors) ?> + <?= $this->subtask->selectTimeSpent($values, $errors) ?> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> </form> diff --git a/app/Template/subtask/icons.php b/app/Template/subtask/icons.php deleted file mode 100644 index 1f31d51f..00000000 --- a/app/Template/subtask/icons.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php if ($subtask['status'] == 0): ?> - <i class="fa fa-square-o fa-fw"></i> -<?php elseif ($subtask['status'] == 1): ?> - <i class="fa fa-gears fa-fw"></i> -<?php else: ?> - <i class="fa fa-check-square-o fa-fw"></i> -<?php endif ?>
\ No newline at end of file diff --git a/app/Template/subtask/menu.php b/app/Template/subtask/menu.php new file mode 100644 index 00000000..878ad68c --- /dev/null +++ b/app/Template/subtask/menu.php @@ -0,0 +1,21 @@ +<div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> + <ul> + <?php if ($subtask['position'] != $first_position): ?> + <li> + <?= $this->url->link(t('Move Up'), 'subtask', 'movePosition', array('project_id' => $task['id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'], 'direction' => 'up'), true) ?> + </li> + <?php endif ?> + <?php if ($subtask['position'] != $last_position): ?> + <li> + <?= $this->url->link(t('Move Down'), 'subtask', 'movePosition', array('project_id' => $task['id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'], 'direction' => 'down'), true) ?> + </li> + <?php endif ?> + <li> + <?= $this->url->link(t('Edit'), 'subtask', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id']), false, 'popover') ?> + </li> + <li> + <?= $this->url->link(t('Remove'), 'subtask', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id']), false, 'popover') ?> + </li> + </ul> +</div> diff --git a/app/Template/subtask/remove.php b/app/Template/subtask/remove.php index 65ade31d..9aef6842 100644 --- a/app/Template/subtask/remove.php +++ b/app/Template/subtask/remove.php @@ -12,6 +12,6 @@ <div class="form-actions"> <?= $this->url->link(t('Yes'), 'subtask', 'remove', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id']), true, 'btn btn-red') ?> <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> </div>
\ No newline at end of file diff --git a/app/Template/subtask/show.php b/app/Template/subtask/show.php index 6945840f..b0326c48 100644 --- a/app/Template/subtask/show.php +++ b/app/Template/subtask/show.php @@ -1,100 +1,12 @@ -<div id="subtasks" class="task-show-section"> - - <?php if (! empty($subtasks)): ?> - <div class="page-header"> - <h2><?= t('Sub-Tasks') ?></h2> - </div> +<div class="page-header"> + <h2><?= t('Sub-Tasks') ?></h2> +</div> - <?php $first_position = $subtasks[0]['position']; ?> - <?php $last_position = $subtasks[count($subtasks) - 1]['position']; ?> - <table class="subtasks-table"> - <tr> - <th class="column-40"><?= t('Title') ?></th> - <th><?= t('Assignee') ?></th> - <th><?= t('Time tracking') ?></th> - <?php if ($editable): ?> - <th class="column-5"></th> - <?php endif ?> - </tr> - <?php foreach ($subtasks as $subtask): ?> - <tr> - <td> - <?php if ($editable): ?> - <?= $this->subtask->toggleStatus($subtask, 'task') ?> - <?php else: ?> - <?= $this->render('subtask/icons', array('subtask' => $subtask)) . $this->e($subtask['title']) ?> - <?php endif ?> - </td> - <td> - <?php if (! empty($subtask['username'])): ?> - <?php if ($editable): ?> - <?= $this->url->link($this->e($subtask['name'] ?: $subtask['username']), 'user', 'show', array('user_id' => $subtask['user_id'])) ?> - <?php else: ?> - <?= $this->e($subtask['name'] ?: $subtask['username']) ?> - <?php endif ?> - <?php endif ?> - </td> - <td> - <ul class="no-bullet"> - <li> - <?php if (! empty($subtask['time_spent'])): ?> - <strong><?= $this->e($subtask['time_spent']).'h' ?></strong> <?= t('spent') ?> - <?php endif ?> +<div id="subtasks"> - <?php if (! empty($subtask['time_estimated'])): ?> - <strong><?= $this->e($subtask['time_estimated']).'h' ?></strong> <?= t('estimated') ?> - <?php endif ?> - </li> - <?php if ($editable && $subtask['user_id'] == $this->user->getId()): ?> - <li> - <?php if ($subtask['is_timer_started']): ?> - <i class="fa fa-pause"></i> - <?= $this->url->link(t('Stop timer'), 'timer', 'subtask', array('timer' => 'stop', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'])) ?> - (<?= $this->dt->age($subtask['timer_start_date']) ?>) - <?php else: ?> - <i class="fa fa-play-circle-o"></i> - <?= $this->url->link(t('Start timer'), 'timer', 'subtask', array('timer' => 'start', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'])) ?> - <?php endif ?> - </li> - <?php endif ?> - </ul> - </td> - <?php if ($editable): ?> - <td> - <div class="dropdown"> - <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> - <ul> - <?php if ($subtask['position'] != $first_position): ?> - <li> - <?= $this->url->link(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'] != $last_position): ?> - <li> - <?= $this->url->link(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->url->link(t('Edit'), 'subtask', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?> - </li> - <li> - <?= $this->url->link(t('Remove'), 'subtask', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?> - </li> - </ul> - </div> - </td> - <?php endif ?> - </tr> - <?php endforeach ?> - </table> - <?php endif ?> + <?= $this->render('subtask/table', array('subtasks' => $subtasks, 'task' => $task, 'editable' => $editable)) ?> <?php if ($editable && $this->user->hasProjectAccess('subtask', 'save', $task['project_id'])): ?> - <?php if (empty($subtasks)): ?> - <div class="page-header"> - <h2><?= t('Sub-Tasks') ?></h2> - </div> - <?php endif ?> <form method="post" action="<?= $this->url->href('subtask', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?> diff --git a/app/Template/subtask/table.php b/app/Template/subtask/table.php new file mode 100644 index 00000000..13f7ada6 --- /dev/null +++ b/app/Template/subtask/table.php @@ -0,0 +1,69 @@ +<?php if (! empty($subtasks)): ?> + + <?php $first_position = $subtasks[0]['position']; ?> + <?php $last_position = $subtasks[count($subtasks) - 1]['position']; ?> + + <table class="subtasks-table"> + <tr> + <th class="column-40"><?= t('Title') ?></th> + <th><?= t('Assignee') ?></th> + <th><?= t('Time tracking') ?></th> + <?php if ($editable): ?> + <th class="column-5"></th> + <?php endif ?> + </tr> + <?php foreach ($subtasks as $subtask): ?> + <tr> + <td> + <?php if ($editable): ?> + <?= $this->subtask->toggleStatus($subtask, $task['project_id']) ?> + <?php else: ?> + <?= $this->subtask->getTitle($subtask) ?> + <?php endif ?> + </td> + <td> + <?php if (! empty($subtask['username'])): ?> + <?= $this->e($subtask['name'] ?: $subtask['username']) ?> + <?php endif ?> + </td> + <td> + <ul class="no-bullet"> + <li> + <?php if (! empty($subtask['time_spent'])): ?> + <strong><?= $this->e($subtask['time_spent']).'h' ?></strong> <?= t('spent') ?> + <?php endif ?> + + <?php if (! empty($subtask['time_estimated'])): ?> + <strong><?= $this->e($subtask['time_estimated']).'h' ?></strong> <?= t('estimated') ?> + <?php endif ?> + </li> + <?php if ($editable && $subtask['user_id'] == $this->user->getId()): ?> + <li> + <?php if ($subtask['is_timer_started']): ?> + <i class="fa fa-pause"></i> + <?= $this->url->link(t('Stop timer'), 'timer', 'subtask', array('timer' => 'stop', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'])) ?> + (<?= $this->dt->age($subtask['timer_start_date']) ?>) + <?php else: ?> + <i class="fa fa-play-circle-o"></i> + <?= $this->url->link(t('Start timer'), 'timer', 'subtask', array('timer' => 'start', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'])) ?> + <?php endif ?> + </li> + <?php endif ?> + </ul> + </td> + <?php if ($editable): ?> + <td> + <?= $this->render('subtask/menu', array( + 'task' => $task, + 'subtask' => $subtask, + 'first_position' => $first_position, + 'last_position' => $last_position, + )) ?> + </td> + <?php endif ?> + </tr> + <?php endforeach ?> + </table> +<?php else: ?> + <p class="alert"><?= t('There is no subtask at the moment.') ?></p> +<?php endif ?> diff --git a/app/Template/subtask/restriction_change_status.php b/app/Template/subtask_restriction/popover.php index 88e91d82..e80d6b6d 100644 --- a/app/Template/subtask/restriction_change_status.php +++ b/app/Template/subtask_restriction/popover.php @@ -1,18 +1,16 @@ <div class="page-header"> <h2><?= t('You already have one subtask in progress') ?></h2> </div> - - <form action="<?= $this->url->href('subtask', 'changeRestrictionStatus', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>" method="post"> +<form class="popover-form" action="<?= $this->url->href('SubtaskRestriction', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>" method="post"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('redirect', array('redirect' => $redirect)) ?> <p><?= t('Select the new status of the subtask: "%s"', $subtask_inprogress['title']) ?></p> <?= $this->form->radios('status', $status_list) ?> <?= $this->form->hidden('id', $subtask_inprogress) ?> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-red"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-red"> <?= t('or') ?> <a href="#" class="close-popover"><?= t('cancel') ?></a> </div> diff --git a/app/Template/swimlane/index.php b/app/Template/swimlane/index.php index 9502cffd..65751606 100644 --- a/app/Template/swimlane/index.php +++ b/app/Template/swimlane/index.php @@ -7,7 +7,7 @@ <?= $this->form->hidden('id', $default_swimlane) ?> <?= $this->form->label(t('Rename'), 'default_swimlane') ?> - <?= $this->form->text('default_swimlane', $default_swimlane, array(), array('required', 'maxlength="50"')) ?><br/> + <?= $this->form->text('default_swimlane', $default_swimlane, array(), array('required', 'maxlength="50"')) ?> <?php if (! empty($active_swimlanes) || $default_swimlane['show_default_swimlane'] == 0): ?> <?= $this->form->checkbox('show_default_swimlane', t('Show default swimlane'), 1, $default_swimlane['show_default_swimlane'] == 1) ?> diff --git a/app/Template/task/comments.php b/app/Template/task/comments.php index 57fb305f..c22e39ec 100644 --- a/app/Template/task/comments.php +++ b/app/Template/task/comments.php @@ -28,7 +28,7 @@ 'task_id' => $task['id'], ), 'errors' => array(), - 'task' => $task + 'task' => $task, )) ?> <?php endif ?> </div> diff --git a/app/Template/task/details.php b/app/Template/task/details.php index d885ca9c..838eb260 100644 --- a/app/Template/task/details.php +++ b/app/Template/task/details.php @@ -1,104 +1,130 @@ -<div class="color-<?= $task['color_id'] ?> task-show-details"> - <h2><?= $this->e('#'.$task['id'].' '.$task['title']) ?></h2> - <?php if ($task['score']): ?> - <span class="task-score"><?= $this->e($task['score']) ?></span> - <?php endif ?> - <ul> - <li> - <strong><?= t('Priority: %d', $task['priority']) ?></strong> - </li> - <?php if ($task['reference']): ?> - <li> - <strong><?= t('Reference: %s', $task['reference']) ?></strong> - </li> - <?php endif ?> - <?php if (! empty($task['swimlane_name'])): ?> - <li> - <?= t('Swimlane: %s', $task['swimlane_name']) ?> - </li> - <?php endif ?> - <li> - <?= dt('Created on %B %e, %Y at %k:%M %p', $task['date_creation']) ?> - </li> - <?php if ($task['date_modification']): ?> - <li> - <?= dt('Last modified on %B %e, %Y at %k:%M %p', $task['date_modification']) ?> - </li> - <?php endif ?> - <?php if ($task['date_completed']): ?> - <li> - <?= dt('Completed on %B %e, %Y at %k:%M %p', $task['date_completed']) ?> - </li> - <?php endif ?> - <?php if ($task['date_started']): ?> - <li> - <?= dt('Started on %B %e, %Y', $task['date_started']) ?> - </li> - <?php endif ?> - <?php if ($task['date_due']): ?> - <li> - <strong><?= dt('Must be done before %B %e, %Y', $task['date_due']) ?></strong> - </li> - <?php endif ?> - <?php if ($task['time_estimated']): ?> - <li> - <?= t('Estimated time: %s hours', $task['time_estimated']) ?> - </li> - <?php endif ?> - <?php if ($task['time_spent']): ?> - <li> - <?= t('Time spent: %s hours', $task['time_spent']) ?> - </li> - <?php endif ?> - <?php if ($task['creator_username']): ?> - <li> - <?= t('Created by %s', $task['creator_name'] ?: $task['creator_username']) ?> - </li> - <?php endif ?> - <li> - <strong> - <?php if ($task['assignee_username']): ?> - <?= t('Assigned to %s', $task['assignee_name'] ?: $task['assignee_username']) ?> - <?php else: ?> - <?= t('There is nobody assigned') ?> - <?php endif ?> - </strong> - </li> - <li> - <?= t('Column on the board:') ?> - <strong><?= $this->e($task['column_title']) ?></strong> - (<?= $this->e($task['project_name']) ?>) - <?= dt('since %B %e, %Y at %k:%M %p', $task['date_moved']) ?> - </li> - <li><?= t('Task position:').' '.$this->e($task['position']) ?></li> - <?php if ($task['category_name']): ?> - <li> - <?= t('Category:') ?> <strong><?= $this->e($task['category_name']) ?></strong> - </li> - <?php endif ?> - <li> - <?php if ($task['is_active'] == 1): ?> - <?= t('Status is open') ?> - <?php else: ?> - <?= t('Status is closed') ?> - <?php endif ?> - </li> - <?php if ($project['is_public']): ?> - <li> - <?= $this->url->link(t('Public link'), 'task', 'readonly', array('task_id' => $task['id'], 'token' => $project['token']), false, '', '', true) ?> - </li> - <?php endif ?> - - <?php if ($editable && $task['recurrence_status'] != \Kanboard\Model\Task::RECURRING_STATUS_NONE): ?> - <li> - <strong><?= t('Recurring information') ?></strong> - <?= $this->render('task/recurring_info', array( - 'task' => $task, - 'recurrence_trigger_list' => $recurrence_trigger_list, - 'recurrence_timeframe_list' => $recurrence_timeframe_list, - 'recurrence_basedate_list' => $recurrence_basedate_list, - )) ?> - </li> - <?php endif ?> - </ul> -</div> +<section id="task-summary"> + <h2><?= $this->e($task['title']) ?></h2> + <div class="task-summary-container color-<?= $task['color_id'] ?>"> + <div class="task-summary-column"> + <ul class="no-bullet"> + <li> + <strong><?= t('Status:') ?></strong> + <span> + <?php if ($task['is_active'] == 1): ?> + <?= t('open') ?> + <?php else: ?> + <?= t('closed') ?> + <?php endif ?> + </span> + </li> + <li> + <strong><?= t('Priority:') ?></strong> <span><?= $task['priority'] ?></span> + </li> + <?php if (! empty($task['reference'])): ?> + <li> + <strong><?= t('Reference:') ?></strong> <span><?= $this->e($task['reference']) ?></span> + </li> + <?php endif ?> + <?php if (! empty($task['score'])): ?> + <li> + <strong><?= t('Complexity:') ?></strong> <span><?= $this->e($task['score']) ?></span> + </li> + <?php endif ?> + <?php if ($project['is_public']): ?> + <li class="smaller"> + <i class="fa fa-external-link fa-fw"></i> + <?= $this->url->link(t('Public link'), 'task', 'readonly', array('task_id' => $task['id'], 'token' => $project['token']), false, '', '', true) ?> + </li> + <?php endif ?> + </ul> + </div> + <div class="task-summary-column"> + <ul class="no-bullet"> + <?php if (! empty($task['category_name'])): ?> + <li> + <strong><?= t('Category:') ?></strong> + <span><?= $this->e($task['category_name']) ?></span> + </li> + <?php endif ?> + <?php if (! empty($task['swimlane_name'])): ?> + <li> + <strong><?= t('Swimlane:') ?></strong> + <span><?= $this->e($task['swimlane_name']) ?></span> + </li> + <?php endif ?> + <li> + <strong><?= t('Column:') ?></strong> + <span><?= $this->e($task['column_title']) ?></span> + </li> + <li> + <strong><?= t('Position:') ?></strong> + <span><?= $task['position'] ?></span> + </li> + </ul> + </div> + <div class="task-summary-column"> + <ul class="no-bullet"> + <li> + <strong><?= t('Assignee:') ?></strong> + <span> + <?php if ($task['assignee_username']): ?> + <?= $this->e($task['assignee_name'] ?: $task['assignee_username']) ?> + <?php else: ?> + <?= t('not assigned') ?> + <?php endif ?> + </span> + </li> + <?php if ($task['creator_username']): ?> + <li> + <strong><?= t('Creator:') ?></strong> + <span><?= $this->e($task['creator_name'] ?: $task['creator_username']) ?></span> + </li> + <?php endif ?> + <?php if ($task['date_due']): ?> + <li> + <strong><?= t('Due date:') ?></strong> + <span><?= dt('%B %e, %Y', $task['date_due']) ?></span> + </li> + <?php endif ?> + <?php if ($task['time_estimated']): ?> + <li> + <strong><?= t('Time estimated:') ?></strong> + <span><?= t('%s hours', $task['time_estimated']) ?></span> + </li> + <?php endif ?> + <?php if ($task['time_spent']): ?> + <li> + <strong><?= t('Time spent:') ?></strong> + <span><?= t('%s hours', $task['time_spent']) ?></span> + </li> + <?php endif ?> + </ul> + </div> + <div class="task-summary-column"> + <ul class="no-bullet"> + <li> + <strong><?= t('Created:') ?></strong> + <span><?= dt('%B %e, %Y at %k:%M %p', $task['date_creation']) ?></span> + </li> + <li> + <strong><?= t('Modified:') ?></strong> + <span><?= dt('%B %e, %Y at %k:%M %p', $task['date_modification']) ?></span> + </li> + <?php if ($task['date_completed']): ?> + <li> + <strong><?= t('Completed:') ?></strong> + <span><?= dt('%B %e, %Y at %k:%M %p', $task['date_completed']) ?></span> + </li> + <?php endif ?> + <?php if ($task['date_started']): ?> + <li> + <strong><?= t('Started:') ?></strong> + <span><?= dt('%B %e, %Y at %k:%M %p', $task['date_started']) ?></span> + </li> + <?php endif ?> + <?php if ($task['date_moved']): ?> + <li> + <strong><?= t('Moved:') ?></strong> + <span><?= dt('%B %e, %Y at %k:%M %p', $task['date_moved']) ?></span> + </li> + <?php endif ?> + </ul> + </div> + </div> +</section> diff --git a/app/Template/task/layout.php b/app/Template/task/layout.php index 9fe1a716..9cbbfec9 100644 --- a/app/Template/task/layout.php +++ b/app/Template/task/layout.php @@ -2,6 +2,9 @@ <div class="page-header"> <ul> <li> + <?= $this->render('task/menu', array('task' => $task)) ?> + </li> + <li> <i class="fa fa-th fa-fw"></i> <?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $task['project_id']), false, '', '', false, $task['swimlane_id'] != 0 ? 'swimlane-'.$task['swimlane_id'] : '') ?> </li> @@ -17,12 +20,12 @@ <?php endif ?> </ul> </div> - <section class="sidebar-container" id="task-section"> + <section class="sidebar-container"> - <?= $this->render('task/sidebar', array('task' => $task)) ?> + <?= $this->render($sidebar_template, array('task' => $task)) ?> <div class="sidebar-content"> - <?= $task_content_for_layout ?> + <?= $content_for_sublayout ?> </div> </section> </section>
\ No newline at end of file diff --git a/app/Template/task/menu.php b/app/Template/task/menu.php new file mode 100644 index 00000000..426cfb3d --- /dev/null +++ b/app/Template/task/menu.php @@ -0,0 +1,72 @@ +<?php if ($this->user->hasProjectAccess('taskmodification', 'edit', $task['project_id'])): ?> +<div class="dropdown"> + <i class="fa fa-caret-down"></i> <a href="#" class="dropdown-menu"><?= t('Actions') ?></a> + <ul> + <li> + <i class="fa fa-pencil-square-o fa-fw"></i> + <?= $this->url->link(t('Edit the task'), 'taskmodification', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + </li> + <li> + <i class="fa fa-align-left fa-fw"></i> + <?= $this->url->link(t('Edit the description'), 'taskmodification', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + </li> + <li> + <i class="fa fa-clock-o fa-fw"></i> + <?= $this->url->link(t('Edit recurrence'), 'TaskRecurrence', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + </li> + <li> + <i class="fa fa-plus fa-fw"></i> + <?= $this->url->link(t('Add a sub-task'), 'subtask', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + </li> + <li> + <i class="fa fa-code-fork fa-fw"></i> + <?= $this->url->link(t('Add internal link'), 'tasklink', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + </li> + <li> + <i class="fa fa-external-link fa-fw"></i> + <?= $this->url->link(t('Add external link'), 'TaskExternalLink', 'find', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + </li> + <li> + <i class="fa fa-comment-o fa-fw"></i> + <?= $this->url->link(t('Add a comment'), 'comment', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + </li> + <li> + <i class="fa fa-file fa-fw"></i> + <?= $this->url->link(t('Attach a document'), 'file', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + </li> + <li> + <i class="fa fa-camera fa-fw"></i> + <?= $this->url->link(t('Add a screenshot'), 'file', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + </li> + <li> + <i class="fa fa-files-o fa-fw"></i> + <?= $this->url->link(t('Duplicate'), 'taskduplication', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + </li> + <li> + <i class="fa fa-clipboard fa-fw"></i> + <?= $this->url->link(t('Duplicate to another project'), 'taskduplication', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + </li> + <li> + <i class="fa fa-clone fa-fw"></i> + <?= $this->url->link(t('Move to another project'), 'taskduplication', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + </li> + <li> + <?php if ($task['is_active'] == 1): ?> + <i class="fa fa-times fa-fw"></i> + <?= $this->url->link(t('Close this task'), 'taskstatus', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?php else: ?> + <i class="fa fa-check-square-o fa-fw"></i> + <?= $this->url->link(t('Open this task'), 'taskstatus', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?php endif ?> + </li> + <?php if ($this->task->canRemove($task)): ?> + <li> + <i class="fa fa-trash-o fa-fw"></i> + <?= $this->url->link(t('Remove'), 'task', 'remove', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + </li> + <?php endif ?> + + <?= $this->hook->render('template:task:menu:actions') ?> + </ul> +</div> +<?php endif ?> diff --git a/app/Template/task/remove.php b/app/Template/task/remove.php index 2f6edc22..e0d655fe 100644 --- a/app/Template/task/remove.php +++ b/app/Template/task/remove.php @@ -10,6 +10,6 @@ <div class="form-actions"> <?= $this->url->link(t('Yes'), 'task', 'remove', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes'), true, 'btn btn-red') ?> <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> </div>
\ No newline at end of file diff --git a/app/Template/task/show.php b/app/Template/task/show.php index f6d47e53..e18b47fc 100644 --- a/app/Template/task/show.php +++ b/app/Template/task/show.php @@ -13,20 +13,20 @@ <?= $this->render('task/description', array('task' => $task)) ?> -<?= $this->render('tasklink/show', array( - 'task' => $task, - 'links' => $links, - 'link_label_list' => $link_label_list, - 'editable' => $this->user->hasProjectAccess('tasklink', 'edit', $project['id']), - 'is_public' => false, -)) ?> - <?= $this->render('subtask/show', array( 'task' => $task, 'subtasks' => $subtasks, 'project' => $project, 'users_list' => isset($users_list) ? $users_list : array(), - 'editable' => $this->user->hasProjectAccess('subtask', 'edit', $project['id']), + 'editable' => true, +)) ?> + +<?= $this->render('tasklink/show', array( + 'task' => $task, + 'links' => $links, + 'link_label_list' => $link_label_list, + 'editable' => true, + 'is_public' => false, )) ?> <?= $this->render('task/time_tracking_summary', array('task' => $task)) ?> diff --git a/app/Template/task/sidebar.php b/app/Template/task/sidebar.php index f522c1c4..c60a1810 100644 --- a/app/Template/task/sidebar.php +++ b/app/Template/task/sidebar.php @@ -1,5 +1,5 @@ <div class="sidebar"> - <h2><?= t('Information') ?></h2> + <h2><?= t('Task #%d', $task['id']) ?></h2> <ul> <li <?= $this->app->checkMenuSelection('task', 'show') ?>> <?= $this->url->link(t('Summary'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> @@ -18,61 +18,16 @@ <?= $this->url->link(t('Time tracking'), 'task', 'timetracking', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <?php endif ?> - - <?= $this->hook->render('template:task:sidebar:information') ?> - </ul> - <?php if ($this->user->hasProjectAccess('taskmodification', 'edit', $task['project_id'])): ?> - <h2><?= t('Actions') ?></h2> - <ul> - <li <?= $this->app->checkMenuSelection('taskmodification', 'edit') ?>> - <?= $this->url->link(t('Edit the task'), 'taskmodification', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - </li> - <li <?= $this->app->checkMenuSelection('taskmodification', 'description') ?>> - <?= $this->url->link(t('Edit the description'), 'taskmodification', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - </li> - <li <?= $this->app->checkMenuSelection('taskmodification', 'recurrence') ?>> - <?= $this->url->link(t('Edit recurrence'), 'taskmodification', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - </li> - <li <?= $this->app->checkMenuSelection('subtask', 'create') ?>> - <?= $this->url->link(t('Add a sub-task'), 'subtask', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - </li> - <li <?= $this->app->checkMenuSelection('tasklink', 'create') ?>> - <?= $this->url->link(t('Add a link'), 'tasklink', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <li <?= $this->app->checkMenuSelection('subtask', 'show') ?>> + <?= $this->url->link(t('Sub-tasks'), 'subtask', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> - <li <?= $this->app->checkMenuSelection('comment', 'create') ?>> - <?= $this->url->link(t('Add a comment'), 'comment', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <li <?= $this->app->checkMenuSelection('tasklink', 'show') ?>> + <?= $this->url->link(t('Internal links'), 'tasklink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> - <li <?= $this->app->checkMenuSelection('file', 'create') ?>> - <?= $this->url->link(t('Attach a document'), 'file', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <li <?= $this->app->checkMenuSelection('TaskExternalLink', 'show') ?>> + <?= $this->url->link(t('External links'), 'TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> - <li <?= $this->app->checkMenuSelection('file', 'screenshot') ?>> - <?= $this->url->link(t('Add a screenshot'), 'file', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - </li> - <li <?= $this->app->checkMenuSelection('taskduplication', 'duplicate') ?>> - <?= $this->url->link(t('Duplicate'), 'taskduplication', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - </li> - <li <?= $this->app->checkMenuSelection('taskduplication', 'copy') ?>> - <?= $this->url->link(t('Duplicate to another project'), 'taskduplication', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - </li> - <li <?= $this->app->checkMenuSelection('taskduplication', 'move') ?>> - <?= $this->url->link(t('Move to another project'), 'taskduplication', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - </li> - <li <?= $this->app->checkMenuSelection('taskstatus') ?>> - <?php if ($task['is_active'] == 1): ?> - <?= $this->url->link(t('Close this task'), 'taskstatus', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - <?php else: ?> - <?= $this->url->link(t('Open this task'), 'taskstatus', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - <?php endif ?> - </li> - <?php if ($this->task->canRemove($task)): ?> - <li <?= $this->app->checkMenuSelection('task', 'remove') ?>> - <?= $this->url->link(t('Remove'), 'task', 'remove', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - </li> - <?php endif ?> - <?= $this->hook->render('template:task:sidebar:actions') ?> + <?= $this->hook->render('template:task:sidebar:information') ?> </ul> - <?php endif ?> - <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> - <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> </div> diff --git a/app/Template/task_creation/form.php b/app/Template/task_creation/form.php index eaf9024d..5a976ac9 100644 --- a/app/Template/task_creation/form.php +++ b/app/Template/task_creation/form.php @@ -1,16 +1,8 @@ -<?php if (! $ajax): ?> -<div class="page-header"> - <ul> - <li><i class="fa fa-th fa-fw"></i><?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $values['project_id'])) ?></li> - </ul> -</div> -<?php else: ?> <div class="page-header"> <h2><?= t('New task') ?></h2> </div> -<?php endif ?> -<form id="task-form" method="post" action="<?= $this->url->href('taskcreation', 'save', array('project_id' => $values['project_id'])) ?>" autocomplete="off"> +<form class="popover-form" method="post" action="<?= $this->url->href('taskcreation', 'save', array('project_id' => $values['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> @@ -55,32 +47,14 @@ <div class="form-column"> <?= $this->form->hidden('project_id', $values) ?> - - <?= $this->form->label(t('Assignee'), 'owner_id') ?> - <?= $this->form->select('owner_id', $users_list, $values, $errors, array('tabindex="3"')) ?> - - <?= $this->form->label(t('Category'), 'category_id') ?> - <?= $this->form->select('category_id', $categories_list, $values, $errors, array('tabindex="4"')) ?> - - <?php if (! (count($swimlanes_list) === 1 && key($swimlanes_list) === 0)): ?> - <?= $this->form->label(t('Swimlane'), 'swimlane_id') ?> - <?= $this->form->select('swimlane_id', $swimlanes_list, $values, $errors, array('tabindex="5"')) ?> - <?php endif ?> - - <?= $this->form->label(t('Column'), 'column_id') ?> - <?= $this->form->select('column_id', $columns_list, $values, $errors, array('tabindex="6"')) ?> - + <?= $this->task->selectAssignee($users_list, $values, $errors) ?> + <?= $this->task->selectCategory($categories_list, $values, $errors) ?> + <?= $this->task->selectSwimlane($swimlanes_list, $values, $errors) ?> + <?= $this->task->selectColumn($columns_list, $values, $errors) ?> <?= $this->task->selectPriority($project, $values) ?> - - <?= $this->form->label(t('Complexity'), 'score') ?> - <?= $this->form->number('score', $values, $errors, array('tabindex="9"')) ?> - - <?= $this->form->label(t('Original estimate'), 'time_estimated') ?> - <?= $this->form->numeric('time_estimated', $values, $errors, array('tabindex="10"')) ?> <?= t('hours') ?> - - <?= $this->form->label(t('Due Date'), 'date_due') ?> - <?= $this->form->text('date_due', $values, $errors, array('placeholder="'.$this->text->in($date_format, $date_formats).'"', 'tabindex="11"'), 'form-date') ?> - <div class="form-help"><?= t('Others formats accepted: %s and %s', date('Y-m-d'), date('Y_m_d')) ?></div> + <?= $this->task->selectScore($values, $errors) ?> + <?= $this->task->selectTimeEstimated($values, $errors) ?> + <?= $this->task->selectDueDate($values, $errors) ?> </div> <div class="form-actions"> diff --git a/app/Template/task_duplication/copy.php b/app/Template/task_duplication/copy.php index 415b8610..fe2c599a 100644 --- a/app/Template/task_duplication/copy.php +++ b/app/Template/task_duplication/copy.php @@ -6,7 +6,7 @@ <p class="alert"><?= t('There is no destination project available.') ?></p> <?php else: ?> - <form method="post" action="<?= $this->url->href('taskduplication', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> + <form class="popover-form" method="post" action="<?= $this->url->href('taskduplication', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> @@ -39,9 +39,9 @@ <p class="form-help"><?= t('Current assignee: %s', ($task['assignee_name'] ?: $task['assignee_username']) ?: e('not assigned')) ?></p> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> </form> diff --git a/app/Template/task_duplication/duplicate.php b/app/Template/task_duplication/duplicate.php index 4b50d9ca..376f6b3b 100644 --- a/app/Template/task_duplication/duplicate.php +++ b/app/Template/task_duplication/duplicate.php @@ -10,6 +10,6 @@ <div class="form-actions"> <?= $this->url->link(t('Yes'), 'taskduplication', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes'), true, 'btn btn-red') ?> <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> </div>
\ No newline at end of file diff --git a/app/Template/task_duplication/move.php b/app/Template/task_duplication/move.php index d8d1ba05..8ab81f5b 100644 --- a/app/Template/task_duplication/move.php +++ b/app/Template/task_duplication/move.php @@ -6,7 +6,7 @@ <p class="alert"><?= t('There is no destination project available.') ?></p> <?php else: ?> - <form method="post" action="<?= $this->url->href('taskduplication', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> + <form class="popover-form" method="post" action="<?= $this->url->href('taskduplication', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> @@ -39,9 +39,9 @@ <p class="form-help"><?= t('Current assignee: %s', ($task['assignee_name'] ?: $task['assignee_username']) ?: e('not assigned')) ?></p> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> </form> diff --git a/app/Template/task_external_link/create.php b/app/Template/task_external_link/create.php new file mode 100644 index 00000000..b62abdb2 --- /dev/null +++ b/app/Template/task_external_link/create.php @@ -0,0 +1,13 @@ +<div class="page-header"> + <h2><?= t('Add a new external link') ?></h2> +</div> + +<form class="popover-form" action="<?= $this->url->href('TaskExternalLink', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off"> + <?= $this->render('task_external_link/form', array('task' => $task, 'dependencies' => $dependencies, 'values' => $values, 'errors' => $errors)) ?> + + <div class="form-actions"> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> + <?= t('or') ?> + <?= $this->url->link(t('cancel'), 'TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> + </div> +</form>
\ No newline at end of file diff --git a/app/Template/task_external_link/edit.php b/app/Template/task_external_link/edit.php new file mode 100644 index 00000000..8caaaebe --- /dev/null +++ b/app/Template/task_external_link/edit.php @@ -0,0 +1,13 @@ +<div class="page-header"> + <h2><?= t('Edit external link') ?></h2> +</div> + +<form class="popover-form" action="<?= $this->url->href('TaskExternalLink', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off"> + <?= $this->render('task_external_link/form', array('task' => $task, 'dependencies' => $dependencies, 'values' => $values, 'errors' => $errors)) ?> + + <div class="form-actions"> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> + <?= t('or') ?> + <?= $this->url->link(t('cancel'), 'TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> + </div> +</form>
\ No newline at end of file diff --git a/app/Template/task_external_link/find.php b/app/Template/task_external_link/find.php new file mode 100644 index 00000000..36a031d3 --- /dev/null +++ b/app/Template/task_external_link/find.php @@ -0,0 +1,28 @@ +<div class="page-header"> + <h2><?= t('Add a new external link') ?></h2> +</div> + +<form class="popover-form" action="<?= $this->url->href('TaskExternalLink', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off"> + <?= $this->form->csrf() ?> + <?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?> + + <?= $this->form->label(t('External link'), 'text') ?> + <?= $this->form->text( + 'text', + $values, + $errors, + array( + 'required', + 'autofocus', + 'placeholder="'.t('Copy and paste your link here...').'"', + )) ?> + + <?= $this->form->label(t('Link type'), 'type') ?> + <?= $this->form->select('type', $types, $values) ?> + + <div class="form-actions"> + <input type="submit" value="<?= t('Next') ?>" class="btn btn-blue"/> + <?= t('or') ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> + </div> +</form>
\ No newline at end of file diff --git a/app/Template/task_external_link/form.php b/app/Template/task_external_link/form.php new file mode 100644 index 00000000..932ca521 --- /dev/null +++ b/app/Template/task_external_link/form.php @@ -0,0 +1,13 @@ +<?= $this->form->csrf() ?> +<?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?> +<?= $this->form->hidden('id', $values) ?> +<?= $this->form->hidden('link_type', $values) ?> + +<?= $this->form->label(t('URL'), 'url') ?> +<?= $this->form->text('url', $values, $errors, array('required')) ?> + +<?= $this->form->label(t('Title'), 'title') ?> +<?= $this->form->text('title', $values, $errors, array('required')) ?> + +<?= $this->form->label(t('Dependency'), 'dependency') ?> +<?= $this->form->select('dependency', $dependencies, $values, $errors) ?> diff --git a/app/Template/task_external_link/remove.php b/app/Template/task_external_link/remove.php new file mode 100644 index 00000000..01535255 --- /dev/null +++ b/app/Template/task_external_link/remove.php @@ -0,0 +1,15 @@ +<div class="page-header"> + <h2><?= t('Remove a link') ?></h2> +</div> + +<div class="confirm"> + <p class="alert alert-info"> + <?= t('Do you really want to remove this link: "%s"?', $link['title']) ?> + </p> + + <div class="form-actions"> + <?= $this->url->link(t('Yes'), 'TaskExternalLink', 'remove', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), true, 'btn btn-red') ?> + <?= t('or') ?> + <?= $this->url->link(t('cancel'), 'TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> + </div> +</div>
\ No newline at end of file diff --git a/app/Template/task_external_link/show.php b/app/Template/task_external_link/show.php new file mode 100644 index 00000000..2dc3d1dd --- /dev/null +++ b/app/Template/task_external_link/show.php @@ -0,0 +1,50 @@ +<div class="page-header"> + <h2><?= t('External links') ?></h2> +</div> + +<?php if (empty($links)): ?> + <p class="alert"><?= t('There is no external link for the moment.') ?></p> +<?php else: ?> + <table class="table-stripped table-small"> + <tr> + <th class="column-10"><?= t('Type') ?></th> + <th><?= t('Title') ?></th> + <th class="column-10"><?= t('Dependency') ?></th> + <th class="column-15"><?= t('Creator') ?></th> + <th class="column-15"><?= t('Date') ?></th> + <?php if ($this->user->hasProjectAccess('TaskExternalLink', 'edit', $task['project_id'])): ?> + <th class="column-5"><?= t('Action') ?></th> + <?php endif ?> + </tr> + <?php foreach ($links as $link): ?> + <tr> + <td> + <?= $link['type'] ?> + </td> + <td> + <a href="<?= $link['url'] ?>" target="_blank"><?= $this->e($link['title']) ?></a> + </td> + <td> + <?= $this->e($link['dependency_label']) ?> + </td> + <td> + <?= $this->e($link['creator_name'] ?: $link['creator_username']) ?> + </td> + <td> + <?= dt('%B %e, %Y', $link['date_creation']) ?> + </td> + <?php if ($this->user->hasProjectAccess('TaskExternalLink', 'edit', $task['project_id'])): ?> + <td> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> + <ul> + <li><?= $this->url->link(t('Edit'), 'TaskExternalLink', 'edit', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li> + <li><?= $this->url->link(t('Remove'), 'TaskExternalLink', 'confirm', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li> + </ul> + </div> + </td> + <?php endif ?> + </tr> + <?php endforeach ?> + </table> +<?php endif ?> diff --git a/app/Template/task_modification/edit_description.php b/app/Template/task_modification/edit_description.php index c38e885d..f5a9b0e1 100644 --- a/app/Template/task_modification/edit_description.php +++ b/app/Template/task_modification/edit_description.php @@ -2,7 +2,7 @@ <h2><?= t('Edit the description') ?></h2> </div> -<form method="post" action="<?= $this->url->href('taskmodification', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => $ajax)) ?>" autocomplete="off"> +<form class="popover-form" method="post" action="<?= $this->url->href('taskmodification', 'updateDescription', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> @@ -39,10 +39,6 @@ <div class="form-actions"> <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> <?= t('or') ?> - <?php if ($ajax): ?> - <?= $this->url->link(t('cancel'), 'board', 'show', array('project_id' => $task['project_id'])) ?> - <?php else: ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - <?php endif ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> </form> diff --git a/app/Template/task_modification/edit_task.php b/app/Template/task_modification/edit_task.php index 2701dd8f..7365648b 100644 --- a/app/Template/task_modification/edit_task.php +++ b/app/Template/task_modification/edit_task.php @@ -1,72 +1,34 @@ <div class="page-header"> <h2><?= t('Edit a task') ?></h2> </div> -<form id="task-form" method="post" action="<?= $this->url->href('taskmodification', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> +<form class="popover-form" method="post" action="<?= $this->url->href('taskmodification', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> + <?= $this->form->hidden('id', $values) ?> + <?= $this->form->hidden('project_id', $values) ?> <div class="form-column"> - <?= $this->form->label(t('Title'), 'title') ?> <?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'maxlength="200"', 'tabindex="1"')) ?> - - <?= $this->form->label(t('Description'), 'description') ?> - <div class="form-tabs"> - <div class="write-area"> - <?= $this->form->textarea( - 'description', - $values, - $errors, - array( - 'placeholder="'.t('Leave a description').'"', - 'tabindex="2"', - 'data-mention-search-url="'.$this->url->href('UserHelper', 'mention', array('project_id' => $task['project_id'])).'"' - ) - ) ?> - </div> - <div class="preview-area"> - <div class="markdown"></div> - </div> - <ul class="form-tabs-nav"> - <li class="form-tab form-tab-selected"> - <i class="fa fa-pencil-square-o fa-fw"></i><a id="markdown-write" href="#"><?= t('Write') ?></a> - </li> - <li class="form-tab"> - <a id="markdown-preview" href="#"><i class="fa fa-eye fa-fw"></i><?= t('Preview') ?></a> - </li> - </ul> - </div> - - <?= $this->render('task/color_picker', array('colors_list' => $colors_list, 'values' => $values)) ?> + <?= $this->task->selectAssignee($users_list, $values, $errors) ?> + <?= $this->task->selectCategory($categories_list, $values, $errors) ?> + <?= $this->task->selectPriority($project, $values) ?> + <?= $this->task->selectScore($values, $errors) ?> </div> <div class="form-column"> - <?= $this->form->hidden('id', $values) ?> - <?= $this->form->hidden('project_id', $values) ?> - - <?= $this->form->label(t('Assignee'), 'owner_id') ?> - <?= $this->form->select('owner_id', $users_list, $values, $errors, array('tabindex="3"')) ?> - - <?= $this->form->label(t('Category'), 'category_id') ?> - <?= $this->form->select('category_id', $categories_list, $values, $errors, array('tabindex="4"')) ?> - - <?= $this->form->label(t('Complexity'), 'score') ?> - <?= $this->form->number('score', $values, $errors, array('tabindex="6"')) ?> - - <?= $this->task->selectPriority($project, $values) ?> + <?= $this->task->selectTimeEstimated($values, $errors) ?> + <?= $this->task->selectTimeSpent($values, $errors) ?> + <?= $this->task->selectDueDate($values, $errors) ?> + </div> - <?= $this->form->label(t('Due Date'), 'date_due') ?> - <?= $this->form->text('date_due', $values, $errors, array('placeholder="'.$this->text->in($date_format, $date_formats).'"', 'tabindex="8"'), 'form-date') ?> - <div class="form-help"><?= t('Others formats accepted: %s and %s', date('Y-m-d'), date('Y_m_d')) ?></div> + <div class="form-clear"> + <?= $this->render('task/color_picker', array('colors_list' => $colors_list, 'values' => $values)) ?> </div> <div class="form-actions"> <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue" tabindex="10"> <?= t('or') ?> - <?php if ($ajax): ?> - <?= $this->url->link(t('cancel'), 'board', 'show', array('project_id' => $task['project_id']), false, 'close-popover') ?> - <?php else: ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - <?php endif ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> </form>
\ No newline at end of file diff --git a/app/Template/task_modification/edit_recurrence.php b/app/Template/task_recurrence/edit.php index dc4faa7a..3c7f2318 100644 --- a/app/Template/task_modification/edit_recurrence.php +++ b/app/Template/task_recurrence/edit.php @@ -15,7 +15,7 @@ <?php if ($task['recurrence_status'] != \Kanboard\Model\Task::RECURRING_STATUS_PROCESSED): ?> - <form method="post" action="<?= $this->url->href('taskmodification', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> + <form class="popover-form" method="post" action="<?= $this->url->href('TaskRecurrence', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> @@ -38,9 +38,9 @@ <?= $this->form->select('recurrence_basedate', $recurrence_basedate_list, $values, $errors) ?> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> </form> diff --git a/app/Template/task_status/close.php b/app/Template/task_status/close.php index d32863bd..7d200544 100644 --- a/app/Template/task_status/close.php +++ b/app/Template/task_status/close.php @@ -8,7 +8,7 @@ </p> <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'taskstatus', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes', 'redirect' => $redirect), true, 'btn btn-red') ?> + <?= $this->url->link(t('Yes'), 'taskstatus', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes'), true, 'btn btn-red popover-link') ?> <?= t('or') ?> <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> diff --git a/app/Template/task_status/open.php b/app/Template/task_status/open.php index 615b2464..5d19bfbe 100644 --- a/app/Template/task_status/open.php +++ b/app/Template/task_status/open.php @@ -8,7 +8,7 @@ </p> <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'taskstatus', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes', 'redirect' => $redirect), true, 'btn btn-red') ?> + <?= $this->url->link(t('Yes'), 'taskstatus', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes'), true, 'btn btn-red popover-link') ?> <?= t('or') ?> <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> diff --git a/app/Template/tasklink/create.php b/app/Template/tasklink/create.php index 2832bdc7..c0d49191 100644 --- a/app/Template/tasklink/create.php +++ b/app/Template/tasklink/create.php @@ -2,7 +2,7 @@ <h2><?= t('Add a new link') ?></h2> </div> -<form action="<?= $this->url->href('tasklink', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => isset($ajax))) ?>" method="post" autocomplete="off"> +<form class="popover-form" action="<?= $this->url->href('tasklink', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?> @@ -28,10 +28,6 @@ <div class="form-actions"> <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> <?= t('or') ?> - <?php if (isset($ajax)): ?> - <?= $this->url->link(t('cancel'), 'board', 'show', array('project_id' => $task['project_id']), false, 'close-popover') ?> - <?php else: ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - <?php endif ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> </div> </form>
\ No newline at end of file diff --git a/app/Template/tasklink/show.php b/app/Template/tasklink/show.php index 5843da17..fd8b37a6 100644 --- a/app/Template/tasklink/show.php +++ b/app/Template/tasklink/show.php @@ -1,15 +1,17 @@ -<?php if (! empty($links)): ?> <div class="page-header"> - <h2><?= t('Links') ?></h2> + <h2><?= t('Internal links') ?></h2> </div> -<table id="links"> +<?php if (empty($links)): ?> + <p class="alert"><?= t('There is no internal link for the moment.') ?></p> +<?php else: ?> +<table id="links" class="table-small table-stripped"> <tr> <th class="column-20"><?= t('Label') ?></th> <th class="column-30"><?= t('Task') ?></th> <th class="column-20"><?= t('Project') ?></th> <th><?= t('Column') ?></th> <th><?= t('Assignee') ?></th> - <?php if ($editable): ?> + <?php if ($editable && $this->user->hasProjectAccess('Tasklink', 'edit', $task['project_id'])): ?> <th class="column-5"><?= t('Action') ?></th> <?php endif ?> </tr> @@ -43,7 +45,7 @@ ) ?> <?php endif ?> - <br/> + <br> <?php if (! empty($link['task_time_spent'])): ?> <strong><?= $this->e($link['task_time_spent']).'h' ?></strong> <?= t('spent') ?> @@ -64,7 +66,7 @@ <?php endif ?> <?php endif ?> </td> - <?php if ($editable): ?> + <?php if ($editable && $this->user->hasProjectAccess('Tasklink', 'edit', $task['project_id'])): ?> <td> <div class="dropdown"> <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> diff --git a/app/Template/twofactor/show.php b/app/Template/twofactor/show.php index dd72965a..d6d8f405 100644 --- a/app/Template/twofactor/show.php +++ b/app/Template/twofactor/show.php @@ -9,7 +9,7 @@ <?php endif ?> <?php if (! empty($qrcode_url)): ?> - <p><br/><img src="<?= $qrcode_url ?>"/><br/><br/></p> + <p><br><img src="<?= $qrcode_url ?>"/><br><br></p> <?php endif ?> <?php if (! empty($key_url)): ?> @@ -26,6 +26,6 @@ <?= $this->form->text('code', array(), array(), array('placeholder="123456"', 'autofocus'), 'form-numeric') ?> <div class="form-actions"> - <input type="submit" value="<?= t('Check my code') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Check my code') ?>" class="btn btn-blue"> </div> </form>
\ No newline at end of file diff --git a/app/Template/user/authentication.php b/app/Template/user/authentication.php index 20c3d372..0c08f3fb 100644 --- a/app/Template/user/authentication.php +++ b/app/Template/user/authentication.php @@ -8,14 +8,7 @@ <?= $this->form->hidden('id', $values) ?> <?= $this->form->hidden('username', $values) ?> - <?= $this->form->label(t('Google Id'), 'google_id') ?> - <?= $this->form->text('google_id', $values, $errors) ?> - - <?= $this->form->label(t('Github Id'), 'github_id') ?> - <?= $this->form->text('github_id', $values, $errors) ?> - - <?= $this->form->label(t('Gitlab Id'), 'gitlab_id') ?> - <?= $this->form->text('gitlab_id', $values, $errors) ?> + <?= $this->hook->render('template:user:authentication:form', array('values' => $values, 'errors' => $errors, 'user' => $user)) ?> <?= $this->form->checkbox('is_ldap_user', t('Remote user'), 1, isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1) ?> <?= $this->form->checkbox('disable_login_form', t('Disallow login form'), 1, isset($values['disable_login_form']) && $values['disable_login_form'] == 1) ?> diff --git a/app/Template/user/create_remote.php b/app/Template/user/create_remote.php index 1cc560cd..7399a010 100644 --- a/app/Template/user/create_remote.php +++ b/app/Template/user/create_remote.php @@ -20,14 +20,7 @@ <?= $this->form->label(t('Email'), 'email') ?> <?= $this->form->email('email', $values, $errors) ?> - <?= $this->form->label(t('Google Id'), 'google_id') ?> - <?= $this->form->text('google_id', $values, $errors) ?> - - <?= $this->form->label(t('Github Id'), 'github_id') ?> - <?= $this->form->text('github_id', $values, $errors) ?> - - <?= $this->form->label(t('Gitlab Id'), 'gitlab_id') ?> - <?= $this->form->text('gitlab_id', $values, $errors) ?> + <?= $this->hook->render('template:user:create-remote:form', array('values' => $values, 'errors' => $errors)) ?> </div> <div class="form-column"> diff --git a/app/Template/user/external.php b/app/Template/user/external.php index 8b1d3c46..22c25af2 100644 --- a/app/Template/user/external.php +++ b/app/Template/user/external.php @@ -2,54 +2,10 @@ <h2><?= t('External authentications') ?></h2> </div> -<?php if (GOOGLE_AUTH): ?> - <h3><i class="fa fa-google"></i> <?= t('Google Account') ?></h3> +<?php $html = $this->hook->render('template:user:external', array('user' => $user)) ?> - <p class="listing"> - <?php if ($this->user->isCurrentUser($user['id'])): ?> - <?php if (empty($user['google_id'])): ?> - <?= $this->url->link(t('Link my Google Account'), 'oauth', 'google', array(), true) ?> - <?php else: ?> - <?= $this->url->link(t('Unlink my Google Account'), 'oauth', 'unlink', array('backend' => 'Google'), true) ?> - <?php endif ?> - <?php else: ?> - <?= empty($user['google_id']) ? t('No account linked.') : t('Account linked.') ?> - <?php endif ?> - </p> -<?php endif ?> - -<?php if (GITHUB_AUTH): ?> - <h3><i class="fa fa-github"></i> <?= t('Github Account') ?></h3> - - <p class="listing"> - <?php if ($this->user->isCurrentUser($user['id'])): ?> - <?php if (empty($user['github_id'])): ?> - <?= $this->url->link(t('Link my Github Account'), 'oauth', 'github', array(), true) ?> - <?php else: ?> - <?= $this->url->link(t('Unlink my Github Account'), 'oauth', 'unlink', array('backend' => 'Github'), true) ?> - <?php endif ?> - <?php else: ?> - <?= empty($user['github_id']) ? t('No account linked.') : t('Account linked.') ?> - <?php endif ?> - </p> -<?php endif ?> - -<?php if (GITLAB_AUTH): ?> - <h3><img src="<?= $this->url->dir() ?>assets/img/gitlab-icon.png"/> <?= t('Gitlab Account') ?></h3> - - <p class="listing"> - <?php if ($this->user->isCurrentUser($user['id'])): ?> - <?php if (empty($user['gitlab_id'])): ?> - <?= $this->url->link(t('Link my Gitlab Account'), 'oauth', 'gitlab', array(), true) ?> - <?php else: ?> - <?= $this->url->link(t('Unlink my Gitlab Account'), 'oauth', 'unlink', array('backend' => 'Gitlab'), true) ?> - <?php endif ?> - <?php else: ?> - <?= empty($user['gitlab_id']) ? t('No account linked.') : t('Account linked.') ?> - <?php endif ?> - </p> -<?php endif ?> - -<?php if (! GOOGLE_AUTH && ! GITHUB_AUTH && ! GITLAB_AUTH): ?> +<?php if (empty($html)): ?> <p class="alert"><?= t('No external authentication enabled.') ?></p> +<?php else: ?> + <?= $html ?> <?php endif ?> diff --git a/app/Template/user/layout.php b/app/Template/user/layout.php index 1e456348..3a0a5ba6 100644 --- a/app/Template/user/layout.php +++ b/app/Template/user/layout.php @@ -13,7 +13,7 @@ <?= $this->render('user/sidebar', array('user' => $user)) ?> <div class="sidebar-content"> - <?= $user_content_for_layout ?> + <?= $content_for_sublayout ?> </div> </section> </section>
\ No newline at end of file diff --git a/app/Template/user/password.php b/app/Template/user/password.php index 3ef28d33..a24a4ee4 100644 --- a/app/Template/user/password.php +++ b/app/Template/user/password.php @@ -9,17 +9,17 @@ <div class="alert alert-error"> <?= $this->form->label(t('Current password for the user "%s"', $this->user->getFullname()), 'current_password') ?> - <?= $this->form->password('current_password', $values, $errors) ?><br/> + <?= $this->form->password('current_password', $values, $errors) ?> </div> <?= $this->form->label(t('New password for the user "%s"', $this->user->getFullname($user)), 'password') ?> - <?= $this->form->password('password', $values, $errors) ?><br/> + <?= $this->form->password('password', $values, $errors) ?> <?= $this->form->label(t('Confirmation'), 'confirmation') ?> - <?= $this->form->password('confirmation', $values, $errors) ?><br/> + <?= $this->form->password('confirmation', $values, $errors) ?> <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> <?= t('or') ?> <?= $this->url->link(t('cancel'), 'user', 'show', array('user_id' => $user['id'])) ?> </div> diff --git a/app/Template/user/sidebar.php b/app/Template/user/sidebar.php index 9f745568..7e367443 100644 --- a/app/Template/user/sidebar.php +++ b/app/Template/user/sidebar.php @@ -78,6 +78,4 @@ </li> <?php endif ?> </ul> - <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> - <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> </div>
\ No newline at end of file diff --git a/app/User/GithubUserProvider.php b/app/User/GithubUserProvider.php deleted file mode 100644 index ae3d7477..00000000 --- a/app/User/GithubUserProvider.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php - -namespace Kanboard\User; - -/** - * Github OAuth User Provider - * - * @package user - * @author Frederic Guillot - */ -class GithubUserProvider extends OAuthUserProvider -{ - /** - * Get external id column name - * - * @access public - * @return string - */ - public function getExternalIdColumn() - { - return 'github_id'; - } -} diff --git a/app/User/GitlabUserProvider.php b/app/User/GitlabUserProvider.php deleted file mode 100644 index a73472c8..00000000 --- a/app/User/GitlabUserProvider.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php - -namespace Kanboard\User; - -/** - * Gitlab OAuth User Provider - * - * @package user - * @author Frederic Guillot - */ -class GitlabUserProvider extends OAuthUserProvider -{ - /** - * Get external id column name - * - * @access public - * @return string - */ - public function getExternalIdColumn() - { - return 'gitlab_id'; - } -} diff --git a/app/User/GoogleUserProvider.php b/app/User/GoogleUserProvider.php deleted file mode 100644 index baa55e03..00000000 --- a/app/User/GoogleUserProvider.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php - -namespace Kanboard\User; - -/** - * Google OAuth User Provider - * - * @package user - * @author Frederic Guillot - */ -class GoogleUserProvider extends OAuthUserProvider -{ - /** - * Get external id column name - * - * @access public - * @return string - */ - public function getExternalIdColumn() - { - return 'google_id'; - } -} diff --git a/app/Validator/ExternalLinkValidator.php b/app/Validator/ExternalLinkValidator.php new file mode 100644 index 00000000..fff4133b --- /dev/null +++ b/app/Validator/ExternalLinkValidator.php @@ -0,0 +1,76 @@ +<?php + +namespace Kanboard\Validator; + +use SimpleValidator\Validator; +use SimpleValidator\Validators; + +/** + * External Link Validator + * + * @package validator + * @author Frederic Guillot + */ +class ExternalLinkValidator extends Base +{ + /** + * 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, $this->commonValidationRules()); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The id is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Common validation rules + * + * @access private + * @return array + */ + private function commonValidationRules() + { + return array( + new Validators\Required('url', t('Field required')), + new Validators\MaxLength('url', t('The maximum length is %d characters', 255), 255), + new Validators\Required('title', t('Field required')), + new Validators\MaxLength('title', t('The maximum length is %d characters', 255), 255), + new Validators\Required('link_type', t('Field required')), + new Validators\MaxLength('link_type', t('The maximum length is %d characters', 100), 100), + new Validators\Required('dependency', t('Field required')), + new Validators\MaxLength('dependency', t('The maximum length is %d characters', 100), 100), + new Validators\Integer('id', t('This value must be an integer')), + new Validators\Required('task_id', t('Field required')), + new Validators\Integer('task_id', t('This value must be an integer')), + ); + } +} diff --git a/app/common.php b/app/common.php index fe287811..399fcb86 100644 --- a/app/common.php +++ b/app/common.php @@ -34,4 +34,5 @@ $container->register(new Kanboard\ServiceProvider\EventDispatcherProvider); $container->register(new Kanboard\ServiceProvider\GroupProvider); $container->register(new Kanboard\ServiceProvider\RouteProvider); $container->register(new Kanboard\ServiceProvider\ActionProvider); +$container->register(new Kanboard\ServiceProvider\ExternalLinkProvider); $container->register(new Kanboard\ServiceProvider\PluginProvider); diff --git a/app/constants.php b/app/constants.php index da3de840..be9bb6a7 100644 --- a/app/constants.php +++ b/app/constants.php @@ -51,27 +51,6 @@ defined('LDAP_GROUP_BASE_DN') or define('LDAP_GROUP_BASE_DN', ''); defined('LDAP_GROUP_FILTER') or define('LDAP_GROUP_FILTER', ''); defined('LDAP_GROUP_ATTRIBUTE_NAME') or define('LDAP_GROUP_ATTRIBUTE_NAME', 'cn'); -// Google authentication -defined('GOOGLE_AUTH') or define('GOOGLE_AUTH', false); -defined('GOOGLE_CLIENT_ID') or define('GOOGLE_CLIENT_ID', ''); -defined('GOOGLE_CLIENT_SECRET') or define('GOOGLE_CLIENT_SECRET', ''); - -// Github authentication -defined('GITHUB_AUTH') or define('GITHUB_AUTH', false); -defined('GITHUB_CLIENT_ID') or define('GITHUB_CLIENT_ID', ''); -defined('GITHUB_CLIENT_SECRET') or define('GITHUB_CLIENT_SECRET', ''); -defined('GITHUB_OAUTH_AUTHORIZE_URL') or define('GITHUB_OAUTH_AUTHORIZE_URL', 'https://github.com/login/oauth/authorize'); -defined('GITHUB_OAUTH_TOKEN_URL') or define('GITHUB_OAUTH_TOKEN_URL', 'https://github.com/login/oauth/access_token'); -defined('GITHUB_API_URL') or define('GITHUB_API_URL', 'https://api.github.com/'); - -// Gitlab authentication -defined('GITLAB_AUTH') or define('GITLAB_AUTH', false); -defined('GITLAB_CLIENT_ID') or define('GITLAB_CLIENT_ID', ''); -defined('GITLAB_CLIENT_SECRET') or define('GITLAB_CLIENT_SECRET', ''); -defined('GITLAB_OAUTH_AUTHORIZE_URL') or define('GITLAB_OAUTH_AUTHORIZE_URL', 'https://gitlab.com/oauth/authorize'); -defined('GITLAB_OAUTH_TOKEN_URL') or define('GITLAB_OAUTH_TOKEN_URL', 'https://gitlab.com/oauth/token'); -defined('GITLAB_API_URL') or define('GITLAB_API_URL', 'https://gitlab.com/api/v3/'); - // Proxy authentication defined('REVERSE_PROXY_AUTH') or define('REVERSE_PROXY_AUTH', false); defined('REVERSE_PROXY_USER_HEADER') or define('REVERSE_PROXY_USER_HEADER', 'REMOTE_USER'); diff --git a/assets/css/app.css b/assets/css/app.css index c2f41142..e300cfc9 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -18,4 +18,4 @@ * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.5.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"} -.c3 svg{font:10px sans-serif}.c3 line,.c3 path{fill:none;stroke:#000}.c3 text{-webkit-user-select:none;-moz-user-select:none;user-select:none}.c3-bars path,.c3-event-rect,.c3-legend-item-tile,.c3-xgrid-focus,.c3-ygrid{shape-rendering:crispEdges}.c3-chart-arc path{stroke:#fff}.c3-chart-arc text{fill:#fff;font-size:13px}.c3-grid line{stroke:#aaa}.c3-grid text{fill:#aaa}.c3-xgrid,.c3-ygrid{stroke-dasharray:3 3}.c3-text.c3-empty{fill:gray;font-size:2em}.c3-line{stroke-width:1px}.c3-circle._expanded_{stroke-width:1px;stroke:#fff}.c3-selected-circle{fill:#fff;stroke-width:2px}.c3-bar{stroke-width:0}.c3-bar._expanded_{fill-opacity:.75}.c3-target.c3-focused{opacity:1}.c3-target.c3-focused path.c3-line,.c3-target.c3-focused path.c3-step{stroke-width:2px}.c3-target.c3-defocused{opacity:.3!important}.c3-region{fill:#4682b4;fill-opacity:.1}.c3-brush .extent{fill-opacity:.1}.c3-legend-item{font-size:12px}.c3-legend-item-hidden{opacity:.15}.c3-legend-background{opacity:.75;fill:#fff;stroke:#d3d3d3;stroke-width:1}.c3-tooltip-container{z-index:10}.c3-tooltip{border-collapse:collapse;border-spacing:0;background-color:#fff;empty-cells:show;-webkit-box-shadow:7px 7px 12px -9px #777;-moz-box-shadow:7px 7px 12px -9px #777;box-shadow:7px 7px 12px -9px #777;opacity:.9}.c3-tooltip tr{border:1px solid #CCC}.c3-tooltip th{background-color:#aaa;font-size:14px;padding:2px 5px;text-align:left;color:#FFF}.c3-tooltip td{font-size:13px;padding:3px 6px;background-color:#fff;border-left:1px dotted #999}.c3-tooltip td>span{display:inline-block;width:10px;height:10px;margin-right:6px}.c3-tooltip td.value{text-align:right}.c3-area{stroke-width:0;opacity:.2}.c3-chart-arcs-title{dominant-baseline:middle;font-size:1.3em}.c3-chart-arcs .c3-chart-arcs-background{fill:#e0e0e0;stroke:none}.c3-chart-arcs .c3-chart-arcs-gauge-unit{fill:#000;font-size:16px}.c3-chart-arcs .c3-chart-arcs-gauge-max,.c3-chart-arcs .c3-chart-arcs-gauge-min{fill:#777}.c3-chart-arc .c3-gauge-value{fill:#000}li,ul,ol,table,tr,td,th,p,blockquote,body{margin:0;padding:0;font-size:100%}body{margin-left:10px;margin-right:10px;padding-bottom:10px;color:#333;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;text-rendering:optimizeLegibility}.page{clear:both}ul.no-bullet li{list-style-type:none;margin-left:0}.pull-right{text-align:right}hr{border:0;height:0;border-top:1px solid rgba(0,0,0,0.1);border-bottom:1px solid rgba(255,255,255,0.3)}.chosen-select{min-height:27px}.avatar{float:left;margin-right:10px}#ui-datepicker-div{font-size:.8em}#app-loading-icon{position:fixed;right:3px;bottom:3px}.web-notification-icon{color:#36c}.web-notification-icon:focus,.web-notification-icon:hover{color:#000}a{color:#36c;border:0}a:focus{outline:0;color:#df5353;text-decoration:none;border:1px dotted #aaa}a:hover{color:#333;text-decoration:none}h1,h2,h3{font-weight:normal;color:#333}h2{font-size:1.3em;margin-bottom:10px}h3{margin-top:10px;font-size:1.2em}table{width:100%;border-collapse:collapse;border-spacing:0;margin-bottom:20px;font-size:.95em}#calendar table{margin-bottom:0}th,td{border:1px solid #eee;padding-top:.5em;padding-bottom:.5em;padding-left:3px;padding-right:3px}td{vertical-align:top}th{background:#fbfbfb;text-align:left}td li{margin-left:20px}.table-small{font-size:.8em}th a{text-decoration:none;color:#333}th a:focus,th a:hover{text-decoration:underline}.table-fixed{table-layout:fixed;white-space:nowrap}.table-fixed th{overflow:hidden}.table-fixed td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.table-stripped tr:nth-child(odd) td{background:#fefefe}.column-3{width:3%}.column-5{width:5%}.column-8{width:7.5%}.column-10{width:10%}.column-12{width:12%}.column-15{width:15%}.column-18{width:18%}.column-20{width:20%}.column-25{width:25%}.column-30{width:30%}.column-35{width:35%}.column-40{width:40%}.column-50{width:50%}.column-60{width:60%}.column-70{width:70%}form{margin-bottom:20px}label{cursor:pointer;display:block;margin-top:10px}input[type="number"],input[type="date"],input[type="email"],input[type="password"],input[type="text"]{color:#888;border:1px solid #ccc;width:300px;max-width:95%;font-size:100%;height:25px;padding-bottom:0;font-family:sans-serif;margin-top:10px;-webkit-appearance:none;appearance:none}input[type="number"]:focus,input[type="date"]:focus,input[type="email"]:focus,input[type="password"]:focus,input[type="text"]:focus,textarea:focus{color:#000;border-color:rgba(82,168,236,0.8);outline:0;box-shadow:0 0 8px rgba(82,168,236,0.6)}input.form-numeric,input[type="number"]{width:70px}textarea{border:1px solid #ccc;width:400px;max-width:99%;height:200px;font-size:100%;font-family:sans-serif}select{max-width:95%}select:focus{outline:0}::-webkit-input-placeholder{color:#ddd;padding-top:2px}::-ms-input-placeholder{color:#ddd;padding-top:2px}::-moz-placeholder{color:#ddd;padding-top:2px}.form-actions{padding-top:20px;clear:both}input.form-error,textarea.form-error{border:2px solid #b94a48}input.form-error:focus,textarea.form-error:focus{box-shadow:none;border:2px solid #b94a48}.form-required{color:red;padding-left:5px;font-weight:bold}.form-errors{color:#b94a48;list-style-type:none}ul.form-errors li{margin-left:0}.form-help{font-size:.8em;color:brown;margin-bottom:15px}.form-inline{padding:0;margin:0;border:0}.form-inline label{display:inline}.form-inline input,.form-inline select{margin:0;margin-right:15px}.form-inline .form-required{display:none}.form-inline-group{display:inline}input.form-datetime,input.form-date{width:150px}input.form-input-large{width:400px}.form-row{margin-top:10px;margin-bottom:20px}.form-column{float:left;margin-right:3%;max-width:47%}.form-column ul{margin-top:15px}.form-login{width:350px;margin:0 auto;margin-top:8%}.form-column li,.form-login li{margin-left:25px;line-height:25px}.form-login h2{margin-bottom:30px;font-size:1.5em;font-weight:bold}label+.form-tabs{margin-top:10px}.form-tabs{width:100%;max-width:800px}ul.form-tabs-nav{margin-bottom:8px;margin-top:0}.form-tabs-nav li{margin-left:0;display:inline}.form-tab{margin-right:20px}.form-tab a{color:#ccc;font-weight:bold;text-decoration:none}.form-tab a:focus,.form-tab a:hover{color:#000}.form-tab-selected a{color:#333}.preview-area{border:1px dashed #000;padding-top:5px;padding-left:5px;padding-right:5px;margin-bottom:5px;display:none;overflow:auto}.reset-password{margin-top:20px}.reset-password a{font-size:.8em;color:#999}.btn{-webkit-appearance:none;appearance:none;display:inline-block;color:#333;border:1px solid #ccc;background:#efefef;padding:5px;padding-left:15px;padding-right:15px;font-size:.9em;cursor:pointer;border-radius:2px}a.btn{text-decoration:none;font-weight:bold}.btn-small{padding:2px;padding-left:5px;padding-right:5px}.btn-red{border-color:#b0281a;background:#d14836;color:#fff}a.btn-red:hover,.btn-red:hover,.btn-red:focus{color:#fff;background:#c53727}a.btn-blue,.btn-blue{border-color:#3079ed;background:#4d90fe;color:#fff}a.btn-blue:hover,.btn-blue:hover,a.btn-blue:focus,.btn-blue:focus{border-color:#2f5bb7;background:#357ae8}.btn-blue:disabled{color:#ccc;border:1px solid #ccc;background:#f7f7f7}#main .alert,.page .alert{margin-top:10px}.alert{padding:8px 35px 8px 14px;margin-bottom:10px;color:#c09853;background-color:#fcf8e3;border:1px solid #fbeed5;border-radius:4px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-normal{color:#333;background-color:#f0f0f0;border-color:#ddd}.alert ul{margin-top:10px;margin-bottom:10px}.alert li{margin-left:25px}.tooltip-arrow:after{background:#fff;border:1px solid #aaa;box-shadow:0 0 5px #aaa}div.ui-tooltip{min-width:200px;max-width:600px;font-size:.85em}.tooltip-arrow{width:20px;height:10px;overflow:hidden;position:absolute}.tooltip-arrow.top{top:-10px}.tooltip-arrow.bottom{bottom:-10px}.tooltip-arrow.align-left{left:10px}.tooltip-arrow.align-right{right:10px}.tooltip-arrow:after{content:"";position:absolute;width:14px;height:14px;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.tooltip-arrow.bottom:after{top:-10px}.tooltip-arrow.top:after{bottom:-10px}.tooltip-arrow.align-left:after{left:0}.tooltip-arrow.align-right:after{right:0}.tooltip-large{width:550px}.ui-tooltip-content .markdown p{margin-bottom:0}.tooltip .fa-info-circle{color:#999;font-size:.95em}.ui-tooltip ul{margin-left:20px}header{margin-top:10px;padding-bottom:10px;border-bottom:1px solid #dedede}header h1{margin:0;padding:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:70%;float:left}header ul{text-align:right;font-size:.9em}header li{display:inline;padding-left:30px}header a{color:#777;text-decoration:none}nav .active a{color:#333;font-weight:bold}.logo a{opacity:.5;color:#d40000}.logo span{color:#333}.logo a:hover{opacity:.8;color:#333}.logo a:focus span,.logo a:hover span{color:#d40000}header h1 .tooltip{opacity:.3;font-size:.6em}.page-header{margin-bottom:20px}.page-header h2{margin:0;padding:0;font-size:1.4em;font-weight:bold;border-bottom:1px dotted #ccc}.page-header h2 a{color:#333;text-decoration:none}.page-header h2 a:focus,.page-header h2 a:hover{color:#aaa}.page-header ul{text-align:left;margin-top:5px;display:inline-block}.menu-inline li,.page-header li{display:inline;padding-right:15px;font-size:.95em}.page-header li.active a{color:#333;text-decoration:none;font-weight:bold}.page-header li.active a:hover,.page-header li.active a:focus{text-decoration:underline}.menu-inline{margin-bottom:5px}@media only screen and (max-width:640px){.page-header-mobile li{display:block;margin-bottom:5px}}.public-board{margin-top:5px}.public-task{max-width:800px;margin:0 auto;margin-top:5px}#board-container{overflow-x:auto}#board{table-layout:fixed;margin-bottom:0}#board th.board-column-header{width:240px}#board td{vertical-align:top}.board-container-compact{overflow-x:initial}@media all and (-ms-high-contrast:active),(-ms-high-contrast:none){.board-container-compact #board{table-layout:auto}}#board th.board-column-header.board-column-compact{width:initial}.board-column-collapsed{display:none}td.board-column-task-collapsed{font-weight:bold;background-color:#fbfbfb}#board th.board-column-header-collapsed{width:28px;min-width:28px;text-align:center;overflow:hidden}.board-rotation-wrapper{position:relative;padding:8px 4px;min-height:150px;overflow:hidden}.board-rotation{white-space:nowrap;-webkit-backface-visibility:hidden;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg);-webkit-transform-origin:0 100%;-moz-transform-origin:0 100%;-ms-transform-origin:0 100%;transform-origin:0 100%}.board-column-title .dropdown-menu{text-decoration:none}.board-add-icon{float:left;padding:0 5px}.board-add-icon a{text-decoration:none;color:#36c;font-size:150%;line-height:70%}.board-add-icon a:focus,.board-add-icon a:hover{text-decoration:none;color:red}.board-column-header-task-count{color:#999;font-weight:normal}th.board-column-header-collapsed .board-column-header-task-count{font-size:.85em}a.board-swimlane-toggle{font-size:.95em;text-decoration:none}a.board-swimlane-toggle:hover,a.board-swimlane-toggle:focus{color:#000;text-decoration:none;border:0}.board-task-list{overflow:auto;min-height:60px}.board-task-list-limit{background-color:#df5353}.draggable-item{cursor:pointer;user-select:none}.draggable-placeholder{border:2px dashed #000;background:#fafafa;height:70px;margin-bottom:10px}div.draggable-item-selected{border:1px solid #000}.task-board-sort-handle{float:left;padding-right:5px}.task-board-saving-state{opacity:.3}.task-board-saving-icon{position:absolute;margin:auto;width:100%;text-align:center;color:#000}.task-board{position:relative;margin-bottom:4px;border:1px solid #000;padding:2px;font-size:.85em;word-wrap:break-word}div.task-board-recent{box-shadow:2px 2px 3px rgba(0,0,0,0.2)}div.task-board-status-closed{user-select:none;border:1px dotted #555}.task-table a,.task-board a{color:#000;text-decoration:none;font-weight:bold}.task-table a:focus,.task-table a:hover,.task-board a:focus,.task-board a:hover{text-decoration:underline}.task-board-collapsed{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}a.task-board-collapsed-title{font-weight:normal}.task-board .dropdown{font-size:1.1em}.task-board-title{margin-top:5px;margin-bottom:5px;font-size:1.1em}.task-board-title a{font-weight:normal}.task-board-user{font-size:.8em}.task-board-current-user a{text-decoration:underline}.task-board-current-user a:focus,.task-board-current-user a:hover{text-decoration:none}a.task-board-nobody{font-weight:normal;font-style:italic;color:#444}.task-board-category-container{text-align:right}.task-board-category{font-weight:bold;font-size:.9em;color:#000;border:1px solid #555;padding:2px;padding-right:5px;padding-left:5px}.task-board-icons{text-align:right;margin-top:8px}.task-board-icons a{opacity:.5}.task-board-icons span{opacity:.5;margin-left:2px}.task-board-icons a:hover,.task-board-icons span:hover{opacity:1.0}.task-board-date{font-weight:bold;color:#000}span.task-board-date-overdue{color:#d90000;opacity:1.0}.task-score{font-weight:bold}.task-board .task-score{font-size:1.1em}.task-show-details .task-score{position:absolute;bottom:5px;right:5px;font-size:2em}.task-board-closed,.task-board-days{position:absolute;right:5px;top:5px;opacity:.5;font-size:.8em}.task-board-days:hover{opacity:1.0}.task-days-age{border:#666 1px solid;padding:1px 4px 1px 2px;border-top-left-radius:3px;border-bottom-left-radius:3px}.task-days-incolumn{border:#666 1px solid;border-left:0;margin-left:-5px;padding:1px 2px 1px 4px;border-top-right-radius:3px;border-bottom-right-radius:3px}.board-container-compact .task-board-days{display:none}.task-show-details{position:relative;border-radius:5px;padding-bottom:10px}.task-show-details h2{font-size:1.8em;margin:0;margin-bottom:25px;padding:0;padding-left:10px;padding-right:10px}.task-show-details li{margin-left:25px;list-style-type:circle}.task-show-section{margin-top:30px;margin-bottom:20px}.task-show-files a{font-weight:bold;text-decoration:none}.task-show-files li{margin-left:25px;list-style-type:square;line-height:25px}.task-show-file-actions{font-size:.75em}.task-show-file-actions:before{content:" ["}.task-show-file-actions:after{content:"]"}.task-show-file-actions a{color:#333}.task-show-description{border-left:4px solid #333;padding-left:20px}.task-show-description-textarea{width:99%;max-width:99%;height:300px}.task-file-viewer{position:relative}.task-file-viewer img{max-width:95%;max-height:85%;margin-top:10px}.task-time-form{margin-top:10px;margin-bottom:25px;padding:3px}.task-link-closed{text-decoration:line-through}.task-show-images{list-style-type:none}.task-show-images li img{width:100%}.task-show-images li .img_container{width:250px;height:100px;overflow:hidden}.task-show-images li{padding:10px;overflow:auto;width:250px;min-height:120px;display:inline-block;vertical-align:top}.task-show-images li p{padding:5px;font-weight:bold}.task-show-images li:hover{background:#eee}.task-show-image-actions{margin-left:5px}.task-show-file-table{width:auto}.task-show-start-link{color:#000}.task-show-start-link:hover,.task-show-start-link:focus{color:red}.flag-milestone{color:green}.color-picker{min-height:35px}.color-square{display:inline-block;width:30px;height:30px;margin-right:5px;margin-bottom:5px;border:1px solid #000;cursor:pointer}.color-square:hover{border-style:dotted}div.color-square-selected{border-width:2px;width:28px;height:28px;box-shadow:3px 2px 10px 0 rgba(180,180,180,0.9)}.comment{margin-bottom:20px}.comment:hover{background:#f7f8e0}.comment-inner{border-left:4px solid #333;padding-bottom:10px;padding-left:20px;margin-left:20px;margin-right:10px}.comment-preview{border:2px solid #000;border-radius:3px;padding:10px}.comment-preview .comment-inner{border:0;padding:0;margin:0}.comment-title{margin-bottom:8px;padding-bottom:3px;border-bottom:1px dotted #aaa}.ui-tooltip .comment-title{font-size:80%}.ui-tooltip .comment-inner{padding-bottom:0}.comment-actions{font-size:.8em;padding:0;text-align:right}.comment-actions li{display:inline;padding-left:5px;padding-right:5px;border-right:1px dotted #000}.comment-actions li:last-child{padding-right:0;border:0}.comment-username{font-weight:bold}.comment-textarea{height:200px;width:80%;max-width:800px}.comment-sorting{font-size:.5em}span.comment-sorting a{color:#555;font-weight:normal;text-decoration:none}span.comment-sorting a:hover{color:#aaa}#comments .comment-textarea{height:80px;width:500px}.subtasks-table{font-size:.85em}.subtasks-table td{vertical-align:middle}.markdown{line-height:1.4em;font-size:1.0}.markdown h1{margin-top:5px;margin-bottom:10px;font-size:1.5em;font-weight:bold;text-decoration:underline}.markdown h2{font-size:1.2em;font-weight:bold;text-decoration:underline}.markdown h3{font-size:1.1em;text-decoration:underline}.markdown h4{font-size:1.1em;text-decoration:underline}.markdown p{margin-bottom:10px}.markdown ol,.markdown ul{margin-left:25px;margin-top:10px;margin-bottom:10px}.markdown pre{background:#fbfbfb;padding:10px;border-radius:5px;border:1px solid #ddd;overflow:auto;color:#444}.markdown blockquote{font-style:italic;border-left:3px solid #ddd;padding-left:10px;margin-bottom:10px;margin-left:20px}.markdown img{display:block;max-width:80%;margin-top:10px}.documentation{margin:0 auto;padding:20px;max-width:850px;background:#fefefe;border:1px solid #ccc;border-radius:5px;font-size:1.1em;color:#555}.documentation img{border:1px solid #333}.documentation h1{text-decoration:none;font-size:1.8em;margin-bottom:30px}.documentation h2{font-size:1.3em;text-decoration:none;border-bottom:1px solid #ccc;margin-bottom:25px}.documentation li{line-height:30px}.user-mention-link{font-weight:bold;color:#000;text-decoration:none}.user-mention-link:hover{color:#555}.listing{border-radius:4px;padding:8px 35px 8px 14px;margin-bottom:20px;border:1px solid #ddd;color:#333;background-color:#fefefe;overflow:auto}.listing li{list-style-type:square;margin-left:20px;margin-bottom:3px}.listing ul{margin-top:15px;margin-bottom:15px}.activity-event{margin-bottom:20px}.activity-datetime{color:#999;font-size:.85em}.activity-content{margin-top:10px;margin-left:20px;padding-left:20px;border-left:2px solid #666}.activity-title{font-weight:bold;color:#000}.activity-description{font-size:.9em;color:#aaa;padding-top:5px}.activity-description ul{margin-top:10px}.activity-description li{margin-left:40px;list-style-type:circle;color:#555}.activity-description .markdown{margin-top:10px;color:#555}.activity-changes{margin-top:10px;font-size:.85em}.activity-changes ul{margin-left:25px}.dashboard-project-stats span{font-size:.75em;margin-right:10px;color:#999}.dashboard-project-stats strong{font-size:1.2em}.dashboard-table-link{font-weight:bold;color:#444;text-decoration:none}.dashboard-table-link:focus,.dashboard-table-link:hover{color:#999}.pagination{text-align:center}.pagination-next{margin-left:5px}.pagination-previous{margin-right:5px}#popover-container{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.8);overflow:auto;z-index:100}#popover-content{position:absolute;width:70%;margin:0 0 0 -35%;left:50%;top:1%;padding:15px;background:#fff;overflow:auto;max-height:85%}#main .confirm{max-width:700px;font-size:1.1em}.sidebar-container{margin-top:10px;position:relative;clear:both}.sidebar-content{margin-left:23%;width:76%;position:absolute}.sidebar{width:20%;float:left;padding:10px;padding-top:0;border:1px solid #ddd;background:#fdfdfd;border-radius:5px}.sidebar li{list-style-type:square;margin-left:30px;line-height:1.8em}.sidebar li.active a{color:#000;font-weight:bold;text-decoration:none}.sidebar li.active a:focus,.sidebar li.active a:hover{text-decoration:underline}.sidebar-collapsed .sidebar{width:10px;padding-bottom:0;float:none}.sidebar-collapsed .sidebar-content{margin:0;margin-top:15px;width:100%}.sidebar-collapse{text-align:right}.sidebar-collapse a,.sidebar-expand a{color:#333;text-decoration:none}.sidebar-collapse a:hover,.sidebar-expand a:hover{color:#df5353}@media only screen and (max-width:1024px){.sidebar{width:25%}.sidebar-content{margin-left:30%;width:70%}}@media only screen and (max-width:767px){.sidebar{width:95%;float:none}.sidebar-content{margin:0;margin-top:15px;width:100%}}@media only screen and (max-width:1080px){div.filter-dropdowns .filters{margin-left:0}div.filter-dropdowns{display:block;margin-top:5px}}@media only screen and (max-width:1024px){body{font-size:.85em}.form-tab{max-width:404px}.form-inline-group input[type="submit"],.form-inline-group label{display:block}.form-inline-group input[type="submit"]{margin-top:20px}td>input[type="text"]{max-width:150px}.task-time-form label{display:block}.task-time-form input[type="submit"]{margin-top:10px;display:block}.page-header .form-input-large{width:300px}}@media only screen and (max-width:1024px) and (orientation:landscape){header{padding-bottom:4px}div.chosen-container{font-size:.9em}input[type="number"],input[type="date"],input[type="email"],input[type="password"],input[type="text"]{height:18px}.page-header .form-input-large{width:300px}}@media only screen and (max-width:640px){.hide-mobile{display:none}}.dropdown{display:inline;position:relative}.dropdown ul{display:none}ul.dropdown-submenu-open{display:block;position:absolute;z-index:1000;min-width:285px;list-style:none;margin:3px 0 0 1px;padding:6px 0;background-color:#fff;border:1px solid #b2b2b2;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,0.15)}.textarea-dropdown li,.dropdown-submenu-open li{display:block;margin:0;padding:0;padding-left:10px;padding-right:10px;padding-top:8px;padding-bottom:8px;font-size:.85em;border-bottom:1px solid #f8f8f8;cursor:pointer}.dropdown-submenu-open li.no-hover{cursor:default}.textarea-dropdown li:last-child,.dropdown-submenu-open li:last-child{border:0}.textarea-dropdown .active,.textarea-dropdown li:hover,.dropdown-submenu-open li:not(.no-hover):hover{background:#4078c0;color:#fff}.textarea-dropdown .active a,.textarea-dropdown li:hover a,.dropdown-submenu-open li:hover a{color:#fff}.textarea-dropdown a,.dropdown-submenu-open a{text-decoration:none;color:#333}.dropdown-submenu-open a:focus{text-decoration:underline}.page-header .dropdown{padding-right:10px}.dropdown-menu-link-icon{color:#333;text-decoration:none}.textarea-dropdown{list-style:none;margin:3px 0 0 1px;padding:6px 0;background-color:#fff;border:1px solid #b2b2b2;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,0.15)}#screenshot-zone{position:relative;border:2px dashed #ccc;width:90%;height:250px;overflow:auto}#screenshot-inner{position:absolute;left:0;bottom:48%;width:100%;text-align:center}#screenshot-zone.screenshot-pasted{border:2px solid #333}.toolbar{font-size:.9em;padding-top:5px}.views{display:inline-block;margin-right:10px;font-size:.9em}.views li{border:1px solid #eee;padding-left:8px;padding-right:8px;padding-top:5px;padding-bottom:5px;display:inline}.menu-inline li.active a,.views li.active a{font-weight:bold;color:#000;text-decoration:none}.views li:first-child{border-right:0;border-top-left-radius:5px;border-bottom-left-radius:5px}.views li:last-child{border-left:0;border-top-right-radius:5px;border-bottom-right-radius:5px}.filters{display:inline-block;border:1px solid #eee;border-radius:5px;padding:5px;padding-right:10px;margin-left:8px}.filters ul{font-size:.8em}.page-header .filters ul{font-size:.9em}form.search{display:inline}div.search{margin-bottom:20px}.filter-dropdowns{font-size:.9em;display:inline-block}div.ganttview-hzheader-month,div.ganttview-hzheader-day,div.ganttview-vtheader,div.ganttview-vtheader-item-name,div.ganttview-vtheader-series,div.ganttview-grid,div.ganttview-grid-row-cell{float:left}div.ganttview-hzheader-month,div.ganttview-hzheader-day{text-align:center}div.ganttview-grid-row-cell.last,div.ganttview-hzheader-day.last,div.ganttview-hzheader-month.last{border-right:0}div.ganttview{border:1px solid #999}div.ganttview-hzheader-month{width:60px;height:20px;border-right:1px solid #d0d0d0;line-height:20px;overflow:hidden}div.ganttview-hzheader-day{width:20px;height:20px;border-right:1px solid #f0f0f0;border-top:1px solid #d0d0d0;line-height:20px;color:#777}div.ganttview-vtheader{margin-top:41px;width:400px;overflow:hidden;background-color:#fff}div.ganttview-vtheader-item{color:#666}div.ganttview-vtheader-series-name{width:400px;height:31px;line-height:31px;padding-left:3px;border-top:1px solid #d0d0d0;font-size:.9em;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}div.ganttview-vtheader-series-name a{color:#666;text-decoration:none}div.ganttview-vtheader-series-name a:hover{color:#333;text-decoration:underline}div.ganttview-vtheader-series-name a i{color:#000}div.ganttview-vtheader-series-name a:hover i{color:#666}div.ganttview-slide-container{overflow:auto;border-left:1px solid #999}div.ganttview-grid-row-cell{width:20px;height:31px;border-right:1px solid #f0f0f0;border-top:1px solid #f0f0f0}div.ganttview-grid-row-cell.ganttview-weekend{background-color:#fafafa}div.ganttview-blocks{margin-top:40px}div.ganttview-block-container{height:28px;padding-top:4px}div.ganttview-block{position:relative;height:25px;background-color:#e5ecf9;border:1px solid silver;border-radius:3px}.ganttview-block-movable{cursor:move}div.ganttview-block-not-defined{border-color:#000;background-color:#000}div.ganttview-block-text{position:absolute;height:12px;font-size:.7em;color:#999;padding:2px 3px}div.ganttview-block div.ui-resizable-handle.ui-resizable-s{bottom:-0}
\ No newline at end of file +.c3 svg{font:10px sans-serif}.c3 line,.c3 path{fill:none;stroke:#000}.c3 text{-webkit-user-select:none;-moz-user-select:none;user-select:none}.c3-bars path,.c3-event-rect,.c3-legend-item-tile,.c3-xgrid-focus,.c3-ygrid{shape-rendering:crispEdges}.c3-chart-arc path{stroke:#fff}.c3-chart-arc text{fill:#fff;font-size:13px}.c3-grid line{stroke:#aaa}.c3-grid text{fill:#aaa}.c3-xgrid,.c3-ygrid{stroke-dasharray:3 3}.c3-text.c3-empty{fill:gray;font-size:2em}.c3-line{stroke-width:1px}.c3-circle._expanded_{stroke-width:1px;stroke:#fff}.c3-selected-circle{fill:#fff;stroke-width:2px}.c3-bar{stroke-width:0}.c3-bar._expanded_{fill-opacity:.75}.c3-target.c3-focused{opacity:1}.c3-target.c3-focused path.c3-line,.c3-target.c3-focused path.c3-step{stroke-width:2px}.c3-target.c3-defocused{opacity:.3!important}.c3-region{fill:#4682b4;fill-opacity:.1}.c3-brush .extent{fill-opacity:.1}.c3-legend-item{font-size:12px}.c3-legend-item-hidden{opacity:.15}.c3-legend-background{opacity:.75;fill:#fff;stroke:#d3d3d3;stroke-width:1}.c3-tooltip-container{z-index:10}.c3-tooltip{border-collapse:collapse;border-spacing:0;background-color:#fff;empty-cells:show;-webkit-box-shadow:7px 7px 12px -9px #777;-moz-box-shadow:7px 7px 12px -9px #777;box-shadow:7px 7px 12px -9px #777;opacity:.9}.c3-tooltip tr{border:1px solid #CCC}.c3-tooltip th{background-color:#aaa;font-size:14px;padding:2px 5px;text-align:left;color:#FFF}.c3-tooltip td{font-size:13px;padding:3px 6px;background-color:#fff;border-left:1px dotted #999}.c3-tooltip td>span{display:inline-block;width:10px;height:10px;margin-right:6px}.c3-tooltip td.value{text-align:right}.c3-area{stroke-width:0;opacity:.2}.c3-chart-arcs-title{dominant-baseline:middle;font-size:1.3em}.c3-chart-arcs .c3-chart-arcs-background{fill:#e0e0e0;stroke:none}.c3-chart-arcs .c3-chart-arcs-gauge-unit{fill:#000;font-size:16px}.c3-chart-arcs .c3-chart-arcs-gauge-max,.c3-chart-arcs .c3-chart-arcs-gauge-min{fill:#777}.c3-chart-arc .c3-gauge-value{fill:#000}li,ul,ol,table,tr,td,th,p,blockquote,body{margin:0;padding:0;font-size:100%}body{margin-left:10px;margin-right:10px;padding-bottom:10px;color:#333;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;text-rendering:optimizeLegibility}.page{clear:both}ul.no-bullet li{list-style-type:none;margin-left:0}.pull-right{text-align:right}hr{border:0;height:0;border-top:1px solid rgba(0,0,0,0.1);border-bottom:1px solid rgba(255,255,255,0.3)}.chosen-select{min-height:27px}.avatar{float:left;margin-right:10px}#ui-datepicker-div{font-size:.8em}#app-loading-icon{position:fixed;right:3px;bottom:3px}.web-notification-icon{color:#36c}.web-notification-icon:focus,.web-notification-icon:hover{color:#000}.smaller{font-size:.85em}a{color:#36c;border:0}a:focus{outline:0;color:#df5353;text-decoration:none;border:1px dotted #aaa}a:hover{color:#333;text-decoration:none}h1,h2,h3{font-weight:normal;color:#333}h2{font-size:1.3em;margin-bottom:10px}h3{margin-top:10px;font-size:1.2em}table{width:100%;border-collapse:collapse;border-spacing:0;margin-bottom:20px;font-size:.95em}#calendar table{margin-bottom:0}th,td{border:1px solid #eee;padding-top:.5em;padding-bottom:.5em;padding-left:3px;padding-right:3px}td{vertical-align:top}th{background:#fbfbfb;text-align:left}td li{margin-left:20px}.table-small{font-size:.8em}th a{text-decoration:none;color:#333}th a:focus,th a:hover{text-decoration:underline}.table-fixed{table-layout:fixed;white-space:nowrap}.table-fixed th{overflow:hidden}.table-fixed td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.table-stripped tr:nth-child(odd) td{background:#fefefe}.column-3{width:3%}.column-5{width:5%}.column-8{width:7.5%}.column-10{width:10%}.column-12{width:12%}.column-15{width:15%}.column-18{width:18%}.column-20{width:20%}.column-25{width:25%}.column-30{width:30%}.column-35{width:35%}.column-40{width:40%}.column-50{width:50%}.column-60{width:60%}.column-70{width:70%}form{margin-bottom:20px}label{cursor:pointer;display:block;margin-top:10px}input[type="number"],input[type="date"],input[type="email"],input[type="password"],input[type="text"]{color:#888;border:1px solid #ccc;width:300px;max-width:95%;font-size:100%;height:25px;padding-bottom:0;font-family:sans-serif;margin-top:10px;-webkit-appearance:none;appearance:none}input[type="number"]:focus,input[type="date"]:focus,input[type="email"]:focus,input[type="password"]:focus,input[type="text"]:focus,textarea:focus{color:#000;border-color:rgba(82,168,236,0.8);outline:0;box-shadow:0 0 8px rgba(82,168,236,0.6)}input.form-numeric,input[type="number"]{width:70px}textarea{border:1px solid #ccc;width:400px;max-width:99%;height:200px;font-size:100%;font-family:sans-serif}select{max-width:95%}select:focus{outline:0}::-webkit-input-placeholder{color:#ddd;padding-top:2px}::-ms-input-placeholder{color:#ddd;padding-top:2px}::-moz-placeholder{color:#ddd;padding-top:2px}.form-actions{padding-top:20px;clear:both}input.form-error,textarea.form-error{border:2px solid #b94a48}input.form-error:focus,textarea.form-error:focus{box-shadow:none;border:2px solid #b94a48}.form-required{color:red;padding-left:5px;font-weight:bold}.form-errors{color:#b94a48;list-style-type:none}ul.form-errors li{margin-left:0}.form-help{font-size:.8em;color:brown;margin-bottom:15px}.form-inline{padding:0;margin:0;border:0}.form-inline label{display:inline}.form-inline input,.form-inline select{margin:0;margin-right:15px}.form-inline .form-required{display:none}.form-inline-group{display:inline}input.form-datetime,input.form-date{width:150px}input.form-input-large{width:400px}.form-column{float:left;margin-right:3%;max-width:47%}.form-column ul{margin-top:15px}.form-clear{clear:both;padding-top:20px;padding-bottom:10px}.form-login{width:350px;margin:0 auto;margin-top:8%}.form-column li,.form-login li{margin-left:25px;line-height:25px}.form-login h2{margin-bottom:30px;font-size:1.5em;font-weight:bold}label+.form-tabs{margin-top:10px}.form-tabs{width:100%;max-width:800px}ul.form-tabs-nav{margin-bottom:8px;margin-top:0}.form-tabs-nav li{margin-left:0;display:inline}.form-tab{margin-right:20px}.form-tab a{color:#ccc;font-weight:bold;text-decoration:none}.form-tab a:focus,.form-tab a:hover{color:#000}.form-tab-selected a{color:#333}.preview-area{border:1px dashed #000;padding-top:5px;padding-left:5px;padding-right:5px;margin-bottom:5px;display:none;overflow:auto}.reset-password{margin-top:20px}.reset-password a{font-size:.8em;color:#999}.btn{-webkit-appearance:none;appearance:none;display:inline-block;color:#333;border:1px solid #ccc;background:#efefef;padding:5px;padding-left:15px;padding-right:15px;font-size:.9em;cursor:pointer;border-radius:2px}a.btn{text-decoration:none;font-weight:bold}.btn-small{padding:2px;padding-left:5px;padding-right:5px}.btn-red{border-color:#b0281a;background:#d14836;color:#fff}a.btn-red:hover,.btn-red:hover,.btn-red:focus{color:#fff;background:#c53727}a.btn-blue,.btn-blue{border-color:#3079ed;background:#4d90fe;color:#fff}a.btn-blue:hover,.btn-blue:hover,a.btn-blue:focus,.btn-blue:focus{border-color:#2f5bb7;background:#357ae8}.btn-blue:disabled{color:#ccc;border:1px solid #ccc;background:#f7f7f7}#main .alert,.page .alert{margin-top:10px}.alert{padding:8px 35px 8px 14px;margin-bottom:10px;color:#c09853;background-color:#fcf8e3;border:1px solid #fbeed5;border-radius:4px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-normal{color:#333;background-color:#f0f0f0;border-color:#ddd}.alert ul{margin-top:10px;margin-bottom:10px}.alert li{margin-left:25px}.tooltip-arrow:after{background:#fff;border:1px solid #aaa;box-shadow:0 0 5px #aaa}div.ui-tooltip{min-width:200px;max-width:600px;font-size:.85em}.tooltip-arrow{width:20px;height:10px;overflow:hidden;position:absolute}.tooltip-arrow.top{top:-10px}.tooltip-arrow.bottom{bottom:-10px}.tooltip-arrow.align-left{left:10px}.tooltip-arrow.align-right{right:10px}.tooltip-arrow:after{content:"";position:absolute;width:14px;height:14px;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.tooltip-arrow.bottom:after{top:-10px}.tooltip-arrow.top:after{bottom:-10px}.tooltip-arrow.align-left:after{left:0}.tooltip-arrow.align-right:after{right:0}.tooltip-large{width:550px}.ui-tooltip-content .markdown p{margin-bottom:0}.tooltip .fa-info-circle{color:#999;font-size:.95em}.ui-tooltip ul{margin-left:20px}.ui-tooltip dl{margin:-5px 0 0 0;padding:0}.ui-tooltip dt{margin-top:5px}.ui-tooltip dd{margin-left:0}.ui-tooltip .progress{display:inline-block;min-width:3em;text-align:right}header{margin-top:10px;padding-bottom:10px;border-bottom:1px solid #dedede}header h1{margin:0;padding:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:70%;float:left}header ul{text-align:right;font-size:.9em}header li{display:inline;padding-left:30px}header a{color:#777;text-decoration:none}nav .active a{color:#333;font-weight:bold}.logo a{opacity:.5;color:#d40000}.logo span{color:#333}.logo a:hover{opacity:.8;color:#333}.logo a:focus span,.logo a:hover span{color:#d40000}header .user-links .dropdown{margin-left:15px}header h1 .tooltip{opacity:.3;font-size:.6em}.page-header{margin-bottom:20px}.page-header h2{margin:0;padding:0;font-size:1.4em;font-weight:bold;border-bottom:1px dotted #ccc}.page-header h2 a{color:#333;text-decoration:none}.page-header h2 a:focus,.page-header h2 a:hover{color:#aaa}.page-header ul{text-align:left;margin-top:5px;display:inline-block}.menu-inline li,.page-header li{display:inline;padding-right:15px;font-size:.95em}.page-header li.active a{color:#333;text-decoration:none;font-weight:bold}.page-header li.active a:hover,.page-header li.active a:focus{text-decoration:underline}.menu-inline{margin-bottom:5px}@media only screen and (max-width:640px){.page-header-mobile li{display:block;margin-bottom:5px}}.public-board{margin-top:5px}.public-task{max-width:800px;margin:0 auto;margin-top:5px}#board-container{overflow-x:auto}#board{table-layout:fixed;margin-bottom:0}#board th.board-column-header{width:240px}#board td{vertical-align:top}.board-container-compact{overflow-x:initial}@media all and (-ms-high-contrast:active),(-ms-high-contrast:none){.board-container-compact #board{table-layout:auto}}#board th.board-column-header.board-column-compact{width:initial}.board-column-collapsed{display:none}td.board-column-task-collapsed{font-weight:bold;background-color:#fbfbfb}#board th.board-column-header-collapsed{width:28px;min-width:28px;text-align:center;overflow:hidden}.board-rotation-wrapper{position:relative;padding:8px 4px;min-height:150px;overflow:hidden}.board-rotation{white-space:nowrap;-webkit-backface-visibility:hidden;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg);-webkit-transform-origin:0 100%;-moz-transform-origin:0 100%;-ms-transform-origin:0 100%;transform-origin:0 100%}.board-column-title .dropdown-menu{text-decoration:none}.board-add-icon{float:left;padding:0 5px}.board-add-icon a{text-decoration:none;color:#36c;font-size:150%;line-height:70%}.board-add-icon a:focus,.board-add-icon a:hover{text-decoration:none;color:red}.board-column-header-task-count{color:#999;font-weight:normal}th.board-column-header-collapsed .board-column-header-task-count{font-size:.85em}a.board-swimlane-toggle{font-size:.95em;text-decoration:none}a.board-swimlane-toggle:hover,a.board-swimlane-toggle:focus{color:#000;text-decoration:none;border:0}.board-task-list{overflow:auto;min-height:60px}.board-task-list-limit{background-color:#df5353}.draggable-item{cursor:pointer;user-select:none}.draggable-placeholder{border:2px dashed #000;background:#fafafa;height:70px;margin-bottom:10px}div.draggable-item-selected{border:1px solid #000}.task-board-sort-handle{float:left;padding-right:5px}.task-board-saving-state{opacity:.3}.task-board-saving-icon{position:absolute;margin:auto;width:100%;text-align:center;color:#000}.task-board{position:relative;margin-bottom:4px;border:1px solid #000;padding:2px;font-size:.85em;word-wrap:break-word}div.task-board-recent{box-shadow:2px 2px 3px rgba(0,0,0,0.2)}div.task-board-status-closed{user-select:none;border:1px dotted #555}.task-table a,.task-board a{color:#000;text-decoration:none;font-weight:bold}.task-table a:focus,.task-table a:hover,.task-board a:focus,.task-board a:hover{text-decoration:underline}.task-board-collapsed{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}a.task-board-collapsed-title{font-weight:normal}.task-board .dropdown{font-size:1.1em}.task-board-title{margin-top:5px;margin-bottom:5px;font-size:1.1em}.task-board-title a{font-weight:normal}.task-board-user{font-size:.8em}.task-board-current-user a{text-decoration:underline}.task-board-current-user a:focus,.task-board-current-user a:hover{text-decoration:none}a.task-board-nobody{font-weight:normal;font-style:italic;color:#444}.task-board-category-container{text-align:right}.task-board-category{font-weight:bold;font-size:.9em;color:#000;border:1px solid #555;padding:2px;padding-right:5px;padding-left:5px}.task-board-icons{text-align:right;margin-top:8px}.task-board-icons a{opacity:.5}.task-board-icons span{opacity:.5;margin-left:2px}.task-board-icons a:hover,.task-board-icons span:hover{opacity:1.0}.task-board-date{font-weight:bold;color:#000}span.task-board-date-overdue{color:#d90000;opacity:1.0}.task-score{font-weight:bold}.task-board .task-score{font-size:1.1em}.task-show-details .task-score{position:absolute;bottom:5px;right:5px;font-size:2em}.task-board-closed,.task-board-days{position:absolute;right:5px;top:5px;opacity:.5;font-size:.8em}.task-board-days:hover{opacity:1.0}.task-days-age{border:#666 1px solid;padding:1px 4px 1px 2px;border-top-left-radius:3px;border-bottom-left-radius:3px}.task-days-incolumn{border:#666 1px solid;border-left:0;margin-left:-5px;padding:1px 2px 1px 4px;border-top-right-radius:3px;border-bottom-right-radius:3px}.board-container-compact .task-board-days{display:none}#task-summary h2{color:#666;font-size:2.5em;margin-top:0;padding-top:0}.task-summary-container{border:2px solid #000;border-radius:8px;padding:15px;display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-justify-content:space-between;justify-content:space-between}.task-summary-column{font-size:.9em;color:#666}.task-summary-column span{color:#555}.task-summary-column li{line-height:23px}.task-show-section{margin-top:30px;margin-bottom:20px}.task-show-files a{font-weight:bold;text-decoration:none}.task-show-files li{margin-left:25px;list-style-type:square;line-height:25px}.task-show-file-actions{font-size:.75em}.task-show-file-actions:before{content:" ["}.task-show-file-actions:after{content:"]"}.task-show-file-actions a{color:#333}.task-show-description{border-left:4px solid #333;padding-left:20px}.task-show-description-textarea{width:99%;max-width:99%;height:300px}.task-file-viewer{position:relative}.task-file-viewer img{max-width:95%;max-height:85%;margin-top:10px}.task-time-form{margin-top:10px;margin-bottom:25px;padding:3px}.task-link-closed{text-decoration:line-through}.task-show-images{list-style-type:none}.task-show-images li img{width:100%}.task-show-images li .img_container{width:250px;height:100px;overflow:hidden}.task-show-images li{padding:10px;overflow:auto;width:250px;min-height:120px;display:inline-block;vertical-align:top}.task-show-images li p{padding:5px;font-weight:bold}.task-show-images li:hover{background:#eee}.task-show-image-actions{margin-left:5px}.task-show-file-table{width:auto}.task-show-start-link{color:#000}.task-show-start-link:hover,.task-show-start-link:focus{color:red}.flag-milestone{color:green}.color-picker{min-height:35px}.color-square{display:inline-block;width:30px;height:30px;margin-right:5px;margin-bottom:5px;border:1px solid #000;cursor:pointer}.color-square:hover{border-style:dotted}div.color-square-selected{border-width:2px;width:28px;height:28px;box-shadow:3px 2px 10px 0 rgba(180,180,180,0.9)}.assign-me{font-size:.8em;vertical-align:bottom}.comment{margin-bottom:20px}.comment:hover{background:#f7f8e0}.comment-inner{border-left:4px solid #333;padding-bottom:10px;padding-left:20px;margin-left:20px;margin-right:10px}.comment-preview{border:2px solid #000;border-radius:3px;padding:10px}.comment-preview .comment-inner{border:0;padding:0;margin:0}.comment-title{margin-bottom:8px;padding-bottom:3px;border-bottom:1px dotted #aaa}.ui-tooltip .comment-title{font-size:80%}.ui-tooltip .comment-inner{padding-bottom:0}.comment-actions{font-size:.8em;padding:0;text-align:right}.comment-actions li{display:inline;padding-left:5px;padding-right:5px;border-right:1px dotted #000}.comment-actions li:last-child{padding-right:0;border:0}.comment-username{font-weight:bold}.comment-textarea{height:200px;width:80%;max-width:800px}.comment-sorting{font-size:.5em}span.comment-sorting a{color:#555;font-weight:normal;text-decoration:none}span.comment-sorting a:hover{color:#aaa}#comments .comment-textarea{height:80px;width:500px}.subtasks-table{font-size:.85em}.subtasks-table td{vertical-align:middle}.markdown{line-height:1.4em;font-size:1.0}.markdown h1{margin-top:5px;margin-bottom:10px;font-size:1.5em;font-weight:bold;text-decoration:underline}.markdown h2{font-size:1.2em;font-weight:bold;text-decoration:underline}.markdown h3{font-size:1.1em;text-decoration:underline}.markdown h4{font-size:1.1em;text-decoration:underline}.markdown p{margin-bottom:10px}.markdown ol,.markdown ul{margin-left:25px;margin-top:10px;margin-bottom:10px}.markdown pre{background:#fbfbfb;padding:10px;border-radius:5px;border:1px solid #ddd;overflow:auto;color:#444}.markdown blockquote{font-style:italic;border-left:3px solid #ddd;padding-left:10px;margin-bottom:10px;margin-left:20px}.markdown img{display:block;max-width:80%;margin-top:10px}.documentation{margin:0 auto;padding:20px;max-width:850px;background:#fefefe;border:1px solid #ccc;border-radius:5px;font-size:1.1em;color:#555}.documentation img{border:1px solid #333}.documentation h1{text-decoration:none;font-size:1.8em;margin-bottom:30px}.documentation h2{font-size:1.3em;text-decoration:none;border-bottom:1px solid #ccc;margin-bottom:25px}.documentation li{line-height:30px}.user-mention-link{font-weight:bold;color:#000;text-decoration:none}.user-mention-link:hover{color:#555}.listing{border-radius:4px;padding:8px 35px 8px 14px;margin-bottom:20px;border:1px solid #ddd;color:#333;background-color:#fcfcfc;overflow:auto}.listing li{list-style-type:square;margin-left:20px;margin-bottom:3px}.listing ul{margin-top:15px;margin-bottom:15px}.activity-event{margin-bottom:20px}.activity-datetime{color:#999;font-size:.85em}.activity-content{margin-top:10px;margin-left:20px;padding-left:20px;border-left:2px solid #666}.activity-title{font-weight:bold;color:#000}.activity-description{font-size:.9em;color:#aaa;padding-top:5px}.activity-description ul{margin-top:10px}.activity-description li{margin-left:40px;list-style-type:circle;color:#555}.activity-description .markdown{margin-top:10px;color:#555}.activity-changes{margin-top:10px;font-size:.85em}.activity-changes ul{margin-left:25px}.dashboard-project-stats span{font-size:.75em;margin-right:10px;color:#999}.dashboard-project-stats strong{font-size:1.2em}.dashboard-table-link{font-weight:bold;color:#444;text-decoration:none}.dashboard-table-link:focus,.dashboard-table-link:hover{color:#999}.pagination{text-align:center}.pagination-next{margin-left:5px}.pagination-previous{margin-right:5px}#popover-container{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.8);overflow:auto;z-index:100}#popover-content{position:absolute;width:70%;margin:0 0 0 -35%;left:50%;top:1%;padding:15px;background:#fff;overflow:auto;max-height:85%}#main .confirm{max-width:700px;font-size:1.1em}.sidebar-container{margin-top:10px;height:100%;display:-ms-flexbox;display:-webkit-box;display:-moz-box;display:-ms-box;display:box;-ms-flex-direction:row;-webkit-box-orient:horizontal;-moz-box-orient:horizontal;-ms-box-orient:horizontal;box-orient:horizontal}.sidebar-content{padding-left:10px;-ms-flex:1;-webkit-box-flex:1;-moz-box-flex:1;-ms-box-flex:1;box-flex:1}.sidebar{padding-right:10px;border-right:1px dotted #eee;font-size:.95em;width:240px;-ms-flex:0 100px;-webkit-box-flex:0;-moz-box-flex:0;-ms-box-flex:0;box-flex:0}.sidebar a{text-decoration:none}.sidebar li{list-style-type:none;line-height:35px;border-bottom:1px dotted #efefef}.sidebar li:hover{border-left:5px solid #555;padding-left:8px}.sidebar li.active{border-left:5px solid #333;padding-left:8px}.sidebar li.active a{color:#333;font-weight:bold}.sidebar li.active a:focus,.sidebar li.active a:hover{color:#555}@media only screen and (max-width:1080px){div.filter-dropdowns .filters{margin-left:0}div.filter-dropdowns{display:block;margin-top:5px}}@media only screen and (max-width:1024px){body{font-size:.85em}.form-tab{max-width:404px}.form-inline-group input[type="submit"],.form-inline-group label{display:block}.form-inline-group input[type="submit"]{margin-top:20px}td>input[type="text"]{max-width:150px}.task-time-form label{display:block}.task-time-form input[type="submit"]{margin-top:10px;display:block}.page-header .form-input-large{width:300px}}@media only screen and (max-width:1024px) and (orientation:landscape){header{padding-bottom:4px}div.chosen-container{font-size:.9em}input[type="number"],input[type="date"],input[type="email"],input[type="password"],input[type="text"]{height:18px}.page-header .form-input-large{width:300px}}@media only screen and (max-width:640px){.hide-mobile{display:none}}.dropdown{display:inline;position:relative}.dropdown ul{display:none}ul.dropdown-submenu-open{display:block;position:absolute;z-index:1000;min-width:285px;list-style:none;margin:3px 0 0 1px;padding:6px 0;background-color:#fff;border:1px solid #b2b2b2;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,0.15)}.textarea-dropdown li,.dropdown-submenu-open li{display:block;margin:0;padding:0;padding-left:10px;padding-right:10px;padding-top:8px;padding-bottom:8px;font-size:.85em;border-bottom:1px solid #f8f8f8;cursor:pointer}.dropdown-submenu-open li.no-hover{cursor:default}.textarea-dropdown li:last-child,.dropdown-submenu-open li:last-child{border:0}.textarea-dropdown .active,.textarea-dropdown li:hover,.dropdown-submenu-open li:not(.no-hover):hover{background:#4078c0;color:#fff}.textarea-dropdown .active a,.textarea-dropdown li:hover a,.dropdown-submenu-open li:hover a{color:#fff}.textarea-dropdown a,.dropdown-submenu-open a{text-decoration:none;color:#333}.dropdown-submenu-open a:focus{text-decoration:underline}.page-header .dropdown{padding-right:10px}.dropdown-menu-link-icon{color:#333;text-decoration:none}.textarea-dropdown{list-style:none;margin:3px 0 0 1px;padding:6px 0;background-color:#fff;border:1px solid #b2b2b2;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,0.15)}#screenshot-zone{position:relative;border:2px dashed #ccc;width:90%;height:250px;overflow:auto}#screenshot-inner{position:absolute;left:0;bottom:48%;width:100%;text-align:center}#screenshot-zone.screenshot-pasted{border:2px solid #333}.toolbar{font-size:.9em;padding-top:5px}.views{display:inline-block;margin-right:10px;font-size:.9em}.views li{border:1px solid #eee;padding-left:8px;padding-right:8px;padding-top:5px;padding-bottom:5px;display:inline}.menu-inline li.active a,.views li.active a{font-weight:bold;color:#000;text-decoration:none}.views li:first-child{border-right:0;border-top-left-radius:5px;border-bottom-left-radius:5px}.views li:last-child{border-left:0;border-top-right-radius:5px;border-bottom-right-radius:5px}.filters{display:inline-block;border:1px solid #eee;border-radius:5px;padding:5px;padding-right:10px;margin-left:8px}.filters ul{font-size:.8em}.page-header .filters ul{font-size:.9em}form.search{display:inline}div.search{margin-bottom:20px}.filter-dropdowns{font-size:.9em;display:inline-block}div.ganttview-hzheader-month,div.ganttview-hzheader-day,div.ganttview-vtheader,div.ganttview-vtheader-item-name,div.ganttview-vtheader-series,div.ganttview-grid,div.ganttview-grid-row-cell{float:left}div.ganttview-hzheader-month,div.ganttview-hzheader-day{text-align:center}div.ganttview-grid-row-cell.last,div.ganttview-hzheader-day.last,div.ganttview-hzheader-month.last{border-right:0}div.ganttview{border:1px solid #999}div.ganttview-hzheader-month{width:60px;height:20px;border-right:1px solid #d0d0d0;line-height:20px;overflow:hidden}div.ganttview-hzheader-day{width:20px;height:20px;border-right:1px solid #f0f0f0;border-top:1px solid #d0d0d0;line-height:20px;color:#777}div.ganttview-vtheader{margin-top:41px;width:400px;overflow:hidden;background-color:#fff}div.ganttview-vtheader-item{color:#666}div.ganttview-vtheader-series-name{width:400px;height:31px;line-height:31px;padding-left:3px;border-top:1px solid #d0d0d0;font-size:.9em;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}div.ganttview-vtheader-series-name a{color:#666;text-decoration:none}div.ganttview-vtheader-series-name a:hover{color:#333;text-decoration:underline}div.ganttview-vtheader-series-name a i{color:#000}div.ganttview-vtheader-series-name a:hover i{color:#666}div.ganttview-slide-container{overflow:auto;border-left:1px solid #999}div.ganttview-grid-row-cell{width:20px;height:31px;border-right:1px solid #f0f0f0;border-top:1px solid #f0f0f0}div.ganttview-grid-row-cell.ganttview-weekend{background-color:#fafafa}div.ganttview-blocks{margin-top:40px}div.ganttview-block-container{height:28px;padding-top:4px}div.ganttview-block{position:relative;height:25px;background-color:#e5ecf9;border:1px solid silver;border-radius:3px}.ganttview-block-movable{cursor:move}div.ganttview-block-not-defined{border-color:#000;background-color:#000}div.ganttview-block-text{position:absolute;height:12px;font-size:.7em;color:#999;padding:2px 3px}div.ganttview-block div.ui-resizable-handle.ui-resizable-s{bottom:-0}.project-creation-options{max-width:500px;border-left:3px dotted #efefef;margin-top:20px;padding-left:15px;padding-bottom:5px;padding-top:5px}
\ No newline at end of file diff --git a/assets/css/print.css b/assets/css/print.css index 0222b865..1873a60d 100644 --- a/assets/css/print.css +++ b/assets/css/print.css @@ -18,4 +18,4 @@ * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.5.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"} -.c3 svg{font:10px sans-serif}.c3 line,.c3 path{fill:none;stroke:#000}.c3 text{-webkit-user-select:none;-moz-user-select:none;user-select:none}.c3-bars path,.c3-event-rect,.c3-legend-item-tile,.c3-xgrid-focus,.c3-ygrid{shape-rendering:crispEdges}.c3-chart-arc path{stroke:#fff}.c3-chart-arc text{fill:#fff;font-size:13px}.c3-grid line{stroke:#aaa}.c3-grid text{fill:#aaa}.c3-xgrid,.c3-ygrid{stroke-dasharray:3 3}.c3-text.c3-empty{fill:gray;font-size:2em}.c3-line{stroke-width:1px}.c3-circle._expanded_{stroke-width:1px;stroke:#fff}.c3-selected-circle{fill:#fff;stroke-width:2px}.c3-bar{stroke-width:0}.c3-bar._expanded_{fill-opacity:.75}.c3-target.c3-focused{opacity:1}.c3-target.c3-focused path.c3-line,.c3-target.c3-focused path.c3-step{stroke-width:2px}.c3-target.c3-defocused{opacity:.3!important}.c3-region{fill:#4682b4;fill-opacity:.1}.c3-brush .extent{fill-opacity:.1}.c3-legend-item{font-size:12px}.c3-legend-item-hidden{opacity:.15}.c3-legend-background{opacity:.75;fill:#fff;stroke:#d3d3d3;stroke-width:1}.c3-tooltip-container{z-index:10}.c3-tooltip{border-collapse:collapse;border-spacing:0;background-color:#fff;empty-cells:show;-webkit-box-shadow:7px 7px 12px -9px #777;-moz-box-shadow:7px 7px 12px -9px #777;box-shadow:7px 7px 12px -9px #777;opacity:.9}.c3-tooltip tr{border:1px solid #CCC}.c3-tooltip th{background-color:#aaa;font-size:14px;padding:2px 5px;text-align:left;color:#FFF}.c3-tooltip td{font-size:13px;padding:3px 6px;background-color:#fff;border-left:1px dotted #999}.c3-tooltip td>span{display:inline-block;width:10px;height:10px;margin-right:6px}.c3-tooltip td.value{text-align:right}.c3-area{stroke-width:0;opacity:.2}.c3-chart-arcs-title{dominant-baseline:middle;font-size:1.3em}.c3-chart-arcs .c3-chart-arcs-background{fill:#e0e0e0;stroke:none}.c3-chart-arcs .c3-chart-arcs-gauge-unit{fill:#000;font-size:16px}.c3-chart-arcs .c3-chart-arcs-gauge-max,.c3-chart-arcs .c3-chart-arcs-gauge-min{fill:#777}.c3-chart-arc .c3-gauge-value{fill:#000}header,.sidebar,.form-comment,.page-header{display:none}a{color:#36c;border:0}a:focus{outline:0;color:#df5353;text-decoration:none;border:1px dotted #aaa}a:hover{color:#333;text-decoration:none}table{width:100%;border-collapse:collapse;border-spacing:0;margin-bottom:20px;font-size:.95em}#calendar table{margin-bottom:0}th,td{border:1px solid #eee;padding-top:.5em;padding-bottom:.5em;padding-left:3px;padding-right:3px}td{vertical-align:top}th{background:#fbfbfb;text-align:left}td li{margin-left:20px}.table-small{font-size:.8em}th a{text-decoration:none;color:#333}th a:focus,th a:hover{text-decoration:underline}.table-fixed{table-layout:fixed;white-space:nowrap}.table-fixed th{overflow:hidden}.table-fixed td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.table-stripped tr:nth-child(odd) td{background:#fefefe}.column-3{width:3%}.column-5{width:5%}.column-8{width:7.5%}.column-10{width:10%}.column-12{width:12%}.column-15{width:15%}.column-18{width:18%}.column-20{width:20%}.column-25{width:25%}.column-30{width:30%}.column-35{width:35%}.column-40{width:40%}.column-50{width:50%}.column-60{width:60%}.column-70{width:70%}.public-board{margin-top:5px}.public-task{max-width:800px;margin:0 auto;margin-top:5px}#board-container{overflow-x:auto}#board{table-layout:fixed;margin-bottom:0}#board th.board-column-header{width:240px}#board td{vertical-align:top}.board-container-compact{overflow-x:initial}@media all and (-ms-high-contrast:active),(-ms-high-contrast:none){.board-container-compact #board{table-layout:auto}}#board th.board-column-header.board-column-compact{width:initial}.board-column-collapsed{display:none}td.board-column-task-collapsed{font-weight:bold;background-color:#fbfbfb}#board th.board-column-header-collapsed{width:28px;min-width:28px;text-align:center;overflow:hidden}.board-rotation-wrapper{position:relative;padding:8px 4px;min-height:150px;overflow:hidden}.board-rotation{white-space:nowrap;-webkit-backface-visibility:hidden;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg);-webkit-transform-origin:0 100%;-moz-transform-origin:0 100%;-ms-transform-origin:0 100%;transform-origin:0 100%}.board-column-title .dropdown-menu{text-decoration:none}.board-add-icon{float:left;padding:0 5px}.board-add-icon a{text-decoration:none;color:#36c;font-size:150%;line-height:70%}.board-add-icon a:focus,.board-add-icon a:hover{text-decoration:none;color:red}.board-column-header-task-count{color:#999;font-weight:normal}th.board-column-header-collapsed .board-column-header-task-count{font-size:.85em}a.board-swimlane-toggle{font-size:.95em;text-decoration:none}a.board-swimlane-toggle:hover,a.board-swimlane-toggle:focus{color:#000;text-decoration:none;border:0}.board-task-list{overflow:auto;min-height:60px}.board-task-list-limit{background-color:#df5353}.draggable-item{cursor:pointer;user-select:none}.draggable-placeholder{border:2px dashed #000;background:#fafafa;height:70px;margin-bottom:10px}div.draggable-item-selected{border:1px solid #000}.task-board-sort-handle{float:left;padding-right:5px}.task-board-saving-state{opacity:.3}.task-board-saving-icon{position:absolute;margin:auto;width:100%;text-align:center;color:#000}.task-board{position:relative;margin-bottom:4px;border:1px solid #000;padding:2px;font-size:.85em;word-wrap:break-word}div.task-board-recent{box-shadow:2px 2px 3px rgba(0,0,0,0.2)}div.task-board-status-closed{user-select:none;border:1px dotted #555}.task-table a,.task-board a{color:#000;text-decoration:none;font-weight:bold}.task-table a:focus,.task-table a:hover,.task-board a:focus,.task-board a:hover{text-decoration:underline}.task-board-collapsed{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}a.task-board-collapsed-title{font-weight:normal}.task-board .dropdown{font-size:1.1em}.task-board-title{margin-top:5px;margin-bottom:5px;font-size:1.1em}.task-board-title a{font-weight:normal}.task-board-user{font-size:.8em}.task-board-current-user a{text-decoration:underline}.task-board-current-user a:focus,.task-board-current-user a:hover{text-decoration:none}a.task-board-nobody{font-weight:normal;font-style:italic;color:#444}.task-board-category-container{text-align:right}.task-board-category{font-weight:bold;font-size:.9em;color:#000;border:1px solid #555;padding:2px;padding-right:5px;padding-left:5px}.task-board-icons{text-align:right;margin-top:8px}.task-board-icons a{opacity:.5}.task-board-icons span{opacity:.5;margin-left:2px}.task-board-icons a:hover,.task-board-icons span:hover{opacity:1.0}.task-board-date{font-weight:bold;color:#000}span.task-board-date-overdue{color:#d90000;opacity:1.0}.task-score{font-weight:bold}.task-board .task-score{font-size:1.1em}.task-show-details .task-score{position:absolute;bottom:5px;right:5px;font-size:2em}.task-board-closed,.task-board-days{position:absolute;right:5px;top:5px;opacity:.5;font-size:.8em}.task-board-days:hover{opacity:1.0}.task-days-age{border:#666 1px solid;padding:1px 4px 1px 2px;border-top-left-radius:3px;border-bottom-left-radius:3px}.task-days-incolumn{border:#666 1px solid;border-left:0;margin-left:-5px;padding:1px 2px 1px 4px;border-top-right-radius:3px;border-bottom-right-radius:3px}.board-container-compact .task-board-days{display:none}.task-show-details{position:relative;border-radius:5px;padding-bottom:10px}.task-show-details h2{font-size:1.8em;margin:0;margin-bottom:25px;padding:0;padding-left:10px;padding-right:10px}.task-show-details li{margin-left:25px;list-style-type:circle}.task-show-section{margin-top:30px;margin-bottom:20px}.task-show-files a{font-weight:bold;text-decoration:none}.task-show-files li{margin-left:25px;list-style-type:square;line-height:25px}.task-show-file-actions{font-size:.75em}.task-show-file-actions:before{content:" ["}.task-show-file-actions:after{content:"]"}.task-show-file-actions a{color:#333}.task-show-description{border-left:4px solid #333;padding-left:20px}.task-show-description-textarea{width:99%;max-width:99%;height:300px}.task-file-viewer{position:relative}.task-file-viewer img{max-width:95%;max-height:85%;margin-top:10px}.task-time-form{margin-top:10px;margin-bottom:25px;padding:3px}.task-link-closed{text-decoration:line-through}.task-show-images{list-style-type:none}.task-show-images li img{width:100%}.task-show-images li .img_container{width:250px;height:100px;overflow:hidden}.task-show-images li{padding:10px;overflow:auto;width:250px;min-height:120px;display:inline-block;vertical-align:top}.task-show-images li p{padding:5px;font-weight:bold}.task-show-images li:hover{background:#eee}.task-show-image-actions{margin-left:5px}.task-show-file-table{width:auto}.task-show-start-link{color:#000}.task-show-start-link:hover,.task-show-start-link:focus{color:red}.flag-milestone{color:green}.color-picker{min-height:35px}.color-square{display:inline-block;width:30px;height:30px;margin-right:5px;margin-bottom:5px;border:1px solid #000;cursor:pointer}.color-square:hover{border-style:dotted}div.color-square-selected{border-width:2px;width:28px;height:28px;box-shadow:3px 2px 10px 0 rgba(180,180,180,0.9)}.comment{margin-bottom:20px}.comment:hover{background:#f7f8e0}.comment-inner{border-left:4px solid #333;padding-bottom:10px;padding-left:20px;margin-left:20px;margin-right:10px}.comment-preview{border:2px solid #000;border-radius:3px;padding:10px}.comment-preview .comment-inner{border:0;padding:0;margin:0}.comment-title{margin-bottom:8px;padding-bottom:3px;border-bottom:1px dotted #aaa}.ui-tooltip .comment-title{font-size:80%}.ui-tooltip .comment-inner{padding-bottom:0}.comment-actions{font-size:.8em;padding:0;text-align:right}.comment-actions li{display:inline;padding-left:5px;padding-right:5px;border-right:1px dotted #000}.comment-actions li:last-child{padding-right:0;border:0}.comment-username{font-weight:bold}.comment-textarea{height:200px;width:80%;max-width:800px}.comment-sorting{font-size:.5em}span.comment-sorting a{color:#555;font-weight:normal;text-decoration:none}span.comment-sorting a:hover{color:#aaa}#comments .comment-textarea{height:80px;width:500px}.subtasks-table{font-size:.85em}.subtasks-table td{vertical-align:middle}.markdown{line-height:1.4em;font-size:1.0}.markdown h1{margin-top:5px;margin-bottom:10px;font-size:1.5em;font-weight:bold;text-decoration:underline}.markdown h2{font-size:1.2em;font-weight:bold;text-decoration:underline}.markdown h3{font-size:1.1em;text-decoration:underline}.markdown h4{font-size:1.1em;text-decoration:underline}.markdown p{margin-bottom:10px}.markdown ol,.markdown ul{margin-left:25px;margin-top:10px;margin-bottom:10px}.markdown pre{background:#fbfbfb;padding:10px;border-radius:5px;border:1px solid #ddd;overflow:auto;color:#444}.markdown blockquote{font-style:italic;border-left:3px solid #ddd;padding-left:10px;margin-bottom:10px;margin-left:20px}.markdown img{display:block;max-width:80%;margin-top:10px}.documentation{margin:0 auto;padding:20px;max-width:850px;background:#fefefe;border:1px solid #ccc;border-radius:5px;font-size:1.1em;color:#555}.documentation img{border:1px solid #333}.documentation h1{text-decoration:none;font-size:1.8em;margin-bottom:30px}.documentation h2{font-size:1.3em;text-decoration:none;border-bottom:1px solid #ccc;margin-bottom:25px}.documentation li{line-height:30px}.user-mention-link{font-weight:bold;color:#000;text-decoration:none}.user-mention-link:hover{color:#555}
\ No newline at end of file +.c3 svg{font:10px sans-serif}.c3 line,.c3 path{fill:none;stroke:#000}.c3 text{-webkit-user-select:none;-moz-user-select:none;user-select:none}.c3-bars path,.c3-event-rect,.c3-legend-item-tile,.c3-xgrid-focus,.c3-ygrid{shape-rendering:crispEdges}.c3-chart-arc path{stroke:#fff}.c3-chart-arc text{fill:#fff;font-size:13px}.c3-grid line{stroke:#aaa}.c3-grid text{fill:#aaa}.c3-xgrid,.c3-ygrid{stroke-dasharray:3 3}.c3-text.c3-empty{fill:gray;font-size:2em}.c3-line{stroke-width:1px}.c3-circle._expanded_{stroke-width:1px;stroke:#fff}.c3-selected-circle{fill:#fff;stroke-width:2px}.c3-bar{stroke-width:0}.c3-bar._expanded_{fill-opacity:.75}.c3-target.c3-focused{opacity:1}.c3-target.c3-focused path.c3-line,.c3-target.c3-focused path.c3-step{stroke-width:2px}.c3-target.c3-defocused{opacity:.3!important}.c3-region{fill:#4682b4;fill-opacity:.1}.c3-brush .extent{fill-opacity:.1}.c3-legend-item{font-size:12px}.c3-legend-item-hidden{opacity:.15}.c3-legend-background{opacity:.75;fill:#fff;stroke:#d3d3d3;stroke-width:1}.c3-tooltip-container{z-index:10}.c3-tooltip{border-collapse:collapse;border-spacing:0;background-color:#fff;empty-cells:show;-webkit-box-shadow:7px 7px 12px -9px #777;-moz-box-shadow:7px 7px 12px -9px #777;box-shadow:7px 7px 12px -9px #777;opacity:.9}.c3-tooltip tr{border:1px solid #CCC}.c3-tooltip th{background-color:#aaa;font-size:14px;padding:2px 5px;text-align:left;color:#FFF}.c3-tooltip td{font-size:13px;padding:3px 6px;background-color:#fff;border-left:1px dotted #999}.c3-tooltip td>span{display:inline-block;width:10px;height:10px;margin-right:6px}.c3-tooltip td.value{text-align:right}.c3-area{stroke-width:0;opacity:.2}.c3-chart-arcs-title{dominant-baseline:middle;font-size:1.3em}.c3-chart-arcs .c3-chart-arcs-background{fill:#e0e0e0;stroke:none}.c3-chart-arcs .c3-chart-arcs-gauge-unit{fill:#000;font-size:16px}.c3-chart-arcs .c3-chart-arcs-gauge-max,.c3-chart-arcs .c3-chart-arcs-gauge-min{fill:#777}.c3-chart-arc .c3-gauge-value{fill:#000}header,.sidebar,.form-comment,.page-header{display:none}a{color:#36c;border:0}a:focus{outline:0;color:#df5353;text-decoration:none;border:1px dotted #aaa}a:hover{color:#333;text-decoration:none}table{width:100%;border-collapse:collapse;border-spacing:0;margin-bottom:20px;font-size:.95em}#calendar table{margin-bottom:0}th,td{border:1px solid #eee;padding-top:.5em;padding-bottom:.5em;padding-left:3px;padding-right:3px}td{vertical-align:top}th{background:#fbfbfb;text-align:left}td li{margin-left:20px}.table-small{font-size:.8em}th a{text-decoration:none;color:#333}th a:focus,th a:hover{text-decoration:underline}.table-fixed{table-layout:fixed;white-space:nowrap}.table-fixed th{overflow:hidden}.table-fixed td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.table-stripped tr:nth-child(odd) td{background:#fefefe}.column-3{width:3%}.column-5{width:5%}.column-8{width:7.5%}.column-10{width:10%}.column-12{width:12%}.column-15{width:15%}.column-18{width:18%}.column-20{width:20%}.column-25{width:25%}.column-30{width:30%}.column-35{width:35%}.column-40{width:40%}.column-50{width:50%}.column-60{width:60%}.column-70{width:70%}.public-board{margin-top:5px}.public-task{max-width:800px;margin:0 auto;margin-top:5px}#board-container{overflow-x:auto}#board{table-layout:fixed;margin-bottom:0}#board th.board-column-header{width:240px}#board td{vertical-align:top}.board-container-compact{overflow-x:initial}@media all and (-ms-high-contrast:active),(-ms-high-contrast:none){.board-container-compact #board{table-layout:auto}}#board th.board-column-header.board-column-compact{width:initial}.board-column-collapsed{display:none}td.board-column-task-collapsed{font-weight:bold;background-color:#fbfbfb}#board th.board-column-header-collapsed{width:28px;min-width:28px;text-align:center;overflow:hidden}.board-rotation-wrapper{position:relative;padding:8px 4px;min-height:150px;overflow:hidden}.board-rotation{white-space:nowrap;-webkit-backface-visibility:hidden;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg);-webkit-transform-origin:0 100%;-moz-transform-origin:0 100%;-ms-transform-origin:0 100%;transform-origin:0 100%}.board-column-title .dropdown-menu{text-decoration:none}.board-add-icon{float:left;padding:0 5px}.board-add-icon a{text-decoration:none;color:#36c;font-size:150%;line-height:70%}.board-add-icon a:focus,.board-add-icon a:hover{text-decoration:none;color:red}.board-column-header-task-count{color:#999;font-weight:normal}th.board-column-header-collapsed .board-column-header-task-count{font-size:.85em}a.board-swimlane-toggle{font-size:.95em;text-decoration:none}a.board-swimlane-toggle:hover,a.board-swimlane-toggle:focus{color:#000;text-decoration:none;border:0}.board-task-list{overflow:auto;min-height:60px}.board-task-list-limit{background-color:#df5353}.draggable-item{cursor:pointer;user-select:none}.draggable-placeholder{border:2px dashed #000;background:#fafafa;height:70px;margin-bottom:10px}div.draggable-item-selected{border:1px solid #000}.task-board-sort-handle{float:left;padding-right:5px}.task-board-saving-state{opacity:.3}.task-board-saving-icon{position:absolute;margin:auto;width:100%;text-align:center;color:#000}.task-board{position:relative;margin-bottom:4px;border:1px solid #000;padding:2px;font-size:.85em;word-wrap:break-word}div.task-board-recent{box-shadow:2px 2px 3px rgba(0,0,0,0.2)}div.task-board-status-closed{user-select:none;border:1px dotted #555}.task-table a,.task-board a{color:#000;text-decoration:none;font-weight:bold}.task-table a:focus,.task-table a:hover,.task-board a:focus,.task-board a:hover{text-decoration:underline}.task-board-collapsed{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}a.task-board-collapsed-title{font-weight:normal}.task-board .dropdown{font-size:1.1em}.task-board-title{margin-top:5px;margin-bottom:5px;font-size:1.1em}.task-board-title a{font-weight:normal}.task-board-user{font-size:.8em}.task-board-current-user a{text-decoration:underline}.task-board-current-user a:focus,.task-board-current-user a:hover{text-decoration:none}a.task-board-nobody{font-weight:normal;font-style:italic;color:#444}.task-board-category-container{text-align:right}.task-board-category{font-weight:bold;font-size:.9em;color:#000;border:1px solid #555;padding:2px;padding-right:5px;padding-left:5px}.task-board-icons{text-align:right;margin-top:8px}.task-board-icons a{opacity:.5}.task-board-icons span{opacity:.5;margin-left:2px}.task-board-icons a:hover,.task-board-icons span:hover{opacity:1.0}.task-board-date{font-weight:bold;color:#000}span.task-board-date-overdue{color:#d90000;opacity:1.0}.task-score{font-weight:bold}.task-board .task-score{font-size:1.1em}.task-show-details .task-score{position:absolute;bottom:5px;right:5px;font-size:2em}.task-board-closed,.task-board-days{position:absolute;right:5px;top:5px;opacity:.5;font-size:.8em}.task-board-days:hover{opacity:1.0}.task-days-age{border:#666 1px solid;padding:1px 4px 1px 2px;border-top-left-radius:3px;border-bottom-left-radius:3px}.task-days-incolumn{border:#666 1px solid;border-left:0;margin-left:-5px;padding:1px 2px 1px 4px;border-top-right-radius:3px;border-bottom-right-radius:3px}.board-container-compact .task-board-days{display:none}#task-summary h2{color:#666;font-size:2.5em;margin-top:0;padding-top:0}.task-summary-container{border:2px solid #000;border-radius:8px;padding:15px;display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-justify-content:space-between;justify-content:space-between}.task-summary-column{font-size:.9em;color:#666}.task-summary-column span{color:#555}.task-summary-column li{line-height:23px}.task-show-section{margin-top:30px;margin-bottom:20px}.task-show-files a{font-weight:bold;text-decoration:none}.task-show-files li{margin-left:25px;list-style-type:square;line-height:25px}.task-show-file-actions{font-size:.75em}.task-show-file-actions:before{content:" ["}.task-show-file-actions:after{content:"]"}.task-show-file-actions a{color:#333}.task-show-description{border-left:4px solid #333;padding-left:20px}.task-show-description-textarea{width:99%;max-width:99%;height:300px}.task-file-viewer{position:relative}.task-file-viewer img{max-width:95%;max-height:85%;margin-top:10px}.task-time-form{margin-top:10px;margin-bottom:25px;padding:3px}.task-link-closed{text-decoration:line-through}.task-show-images{list-style-type:none}.task-show-images li img{width:100%}.task-show-images li .img_container{width:250px;height:100px;overflow:hidden}.task-show-images li{padding:10px;overflow:auto;width:250px;min-height:120px;display:inline-block;vertical-align:top}.task-show-images li p{padding:5px;font-weight:bold}.task-show-images li:hover{background:#eee}.task-show-image-actions{margin-left:5px}.task-show-file-table{width:auto}.task-show-start-link{color:#000}.task-show-start-link:hover,.task-show-start-link:focus{color:red}.flag-milestone{color:green}.color-picker{min-height:35px}.color-square{display:inline-block;width:30px;height:30px;margin-right:5px;margin-bottom:5px;border:1px solid #000;cursor:pointer}.color-square:hover{border-style:dotted}div.color-square-selected{border-width:2px;width:28px;height:28px;box-shadow:3px 2px 10px 0 rgba(180,180,180,0.9)}.assign-me{font-size:.8em;vertical-align:bottom}.comment{margin-bottom:20px}.comment:hover{background:#f7f8e0}.comment-inner{border-left:4px solid #333;padding-bottom:10px;padding-left:20px;margin-left:20px;margin-right:10px}.comment-preview{border:2px solid #000;border-radius:3px;padding:10px}.comment-preview .comment-inner{border:0;padding:0;margin:0}.comment-title{margin-bottom:8px;padding-bottom:3px;border-bottom:1px dotted #aaa}.ui-tooltip .comment-title{font-size:80%}.ui-tooltip .comment-inner{padding-bottom:0}.comment-actions{font-size:.8em;padding:0;text-align:right}.comment-actions li{display:inline;padding-left:5px;padding-right:5px;border-right:1px dotted #000}.comment-actions li:last-child{padding-right:0;border:0}.comment-username{font-weight:bold}.comment-textarea{height:200px;width:80%;max-width:800px}.comment-sorting{font-size:.5em}span.comment-sorting a{color:#555;font-weight:normal;text-decoration:none}span.comment-sorting a:hover{color:#aaa}#comments .comment-textarea{height:80px;width:500px}.subtasks-table{font-size:.85em}.subtasks-table td{vertical-align:middle}.markdown{line-height:1.4em;font-size:1.0}.markdown h1{margin-top:5px;margin-bottom:10px;font-size:1.5em;font-weight:bold;text-decoration:underline}.markdown h2{font-size:1.2em;font-weight:bold;text-decoration:underline}.markdown h3{font-size:1.1em;text-decoration:underline}.markdown h4{font-size:1.1em;text-decoration:underline}.markdown p{margin-bottom:10px}.markdown ol,.markdown ul{margin-left:25px;margin-top:10px;margin-bottom:10px}.markdown pre{background:#fbfbfb;padding:10px;border-radius:5px;border:1px solid #ddd;overflow:auto;color:#444}.markdown blockquote{font-style:italic;border-left:3px solid #ddd;padding-left:10px;margin-bottom:10px;margin-left:20px}.markdown img{display:block;max-width:80%;margin-top:10px}.documentation{margin:0 auto;padding:20px;max-width:850px;background:#fefefe;border:1px solid #ccc;border-radius:5px;font-size:1.1em;color:#555}.documentation img{border:1px solid #333}.documentation h1{text-decoration:none;font-size:1.8em;margin-bottom:30px}.documentation h2{font-size:1.3em;text-decoration:none;border-bottom:1px solid #ccc;margin-bottom:25px}.documentation li{line-height:30px}.user-mention-link{font-weight:bold;color:#000;text-decoration:none}.user-mention-link:hover{color:#555}
\ No newline at end of file diff --git a/assets/css/src/base.css b/assets/css/src/base.css index dd78ec84..7f5642e7 100644 --- a/assets/css/src/base.css +++ b/assets/css/src/base.css @@ -70,3 +70,7 @@ hr { .web-notification-icon:hover { color: #000; } + +.smaller { + font-size: 0.85em; +} diff --git a/assets/css/src/form.css b/assets/css/src/form.css index 24dcb0fc..e42b345b 100644 --- a/assets/css/src/form.css +++ b/assets/css/src/form.css @@ -146,11 +146,6 @@ input.form-input-large { width: 400px; } -.form-row { - margin-top: 10px; - margin-bottom: 20px; -} - .form-column { float: left; margin-right: 3%; @@ -161,6 +156,12 @@ input.form-input-large { margin-top: 15px; } +.form-clear { + clear: both; + padding-top: 20px; + padding-bottom: 10px; +} + .form-login { width: 350px; margin: 0 auto; diff --git a/assets/css/src/header.css b/assets/css/src/header.css index 946a665b..f4903128 100644 --- a/assets/css/src/header.css +++ b/assets/css/src/header.css @@ -55,6 +55,11 @@ nav .active a { color: #d40000; } +/* user links on the left */ +header .user-links .dropdown { + margin-left: 15px; +} + /* title tooltip */ header h1 .tooltip { opacity: 0.3; diff --git a/assets/css/src/listing.css b/assets/css/src/listing.css index c40c4821..e96197e4 100644 --- a/assets/css/src/listing.css +++ b/assets/css/src/listing.css @@ -5,7 +5,7 @@ margin-bottom: 20px; border: 1px solid #ddd; color: #333; - background-color: #fefefe; + background-color: #fcfcfc; overflow: auto; } diff --git a/assets/css/src/project.css b/assets/css/src/project.css new file mode 100644 index 00000000..a4670ef9 --- /dev/null +++ b/assets/css/src/project.css @@ -0,0 +1,8 @@ +.project-creation-options { + max-width: 500px; + border-left: 3px dotted #efefef; + margin-top: 20px; + padding-left: 15px; + padding-bottom: 5px; + padding-top: 5px; +}
\ No newline at end of file diff --git a/assets/css/src/sidebar.css b/assets/css/src/sidebar.css index a9d56865..25b510b8 100644 --- a/assets/css/src/sidebar.css +++ b/assets/css/src/sidebar.css @@ -1,90 +1,65 @@ -/* sidebar */ .sidebar-container { margin-top: 10px; - position: relative; - clear: both; + height: 100%; + display: -ms-flexbox; + display: -webkit-box; + display: -moz-box; + display: -ms-box; + display: box; + -ms-flex-direction: row; + -webkit-box-orient: horizontal; + -moz-box-orient: horizontal; + -ms-box-orient: horizontal; + box-orient: horizontal; } .sidebar-content { - margin-left: 23%; - width: 76%; - position: absolute; + padding-left: 10px; + -ms-flex: 1; + -webkit-box-flex: 1; + -moz-box-flex: 1; + -ms-box-flex: 1; + box-flex: 1; } .sidebar { - width: 20%; - float: left; - padding: 10px; - padding-top: 0; - border: 1px solid #ddd; - background: #fdfdfd; - border-radius: 5px; + padding-right: 10px; + border-right: 1px dotted #eee; + font-size: 0.95em; + width: 240px; + -ms-flex: 0 100px; + -webkit-box-flex: 0; + -moz-box-flex: 0; + -ms-box-flex: 0; + box-flex: 0; } -.sidebar li { - list-style-type: square; - margin-left: 30px; - line-height: 1.8em; -} - -.sidebar li.active a { - color: #000; - font-weight: bold; +.sidebar a { text-decoration: none; } -.sidebar li.active a:focus, -.sidebar li.active a:hover { - text-decoration: underline; -} - -.sidebar-collapsed .sidebar { - width: 10px; - padding-bottom: 0; - float: none; +.sidebar li { + list-style-type: none; + line-height: 35px; + border-bottom: 1px dotted #efefef; } -.sidebar-collapsed .sidebar-content { - margin: 0; - margin-top: 15px; - width: 100%; +.sidebar li:hover { + border-left: 5px solid #555; + padding-left: 8px; } -.sidebar-collapse { - text-align: right; +.sidebar li.active { + border-left: 5px solid #333; + padding-left: 8px; } -.sidebar-collapse a, -.sidebar-expand a { +.sidebar li.active a { color: #333; - text-decoration: none; -} - -.sidebar-collapse a:hover, -.sidebar-expand a:hover { - color: #DF5353; -} - -@media only screen and (max-width: 1024px) { - .sidebar { - width: 25%; - } - - .sidebar-content { - margin-left: 30%; - width: 70%; - } + font-weight: bold; } -@media only screen and (max-width: 767px) { - .sidebar { - width: 95%; - float: none; - } - - .sidebar-content { - margin: 0; - margin-top: 15px; - width: 100%; - } +.sidebar li.active a:focus, +.sidebar li.active a:hover { + color: #555; } diff --git a/assets/css/src/task.css b/assets/css/src/task.css index 7bfb63e2..534152a3 100644 --- a/assets/css/src/task.css +++ b/assets/css/src/task.css @@ -168,24 +168,36 @@ span.task-board-date-overdue { } /* task view */ -.task-show-details { - position: relative; - border-radius: 5px; - padding-bottom: 10px; +#task-summary h2 { + color: #666; + font-size: 2.5em; + margin-top: 0; + padding-top: 0; +} + +.task-summary-container { + border: 2px solid #000; + border-radius: 8px; + padding: 15px; + display: -webkit-flex; + display: flex; + -webkit-flex-direction: row; + flex-direction: row; + -webkit-justify-content: space-between; + justify-content: space-between; +} + +.task-summary-column { + font-size: 0.9em; + color: #666; } -.task-show-details h2 { - font-size: 1.8em; - margin: 0; - margin-bottom: 25px; - padding: 0; - padding-left: 10px; - padding-right: 10px; +.task-summary-column span { + color: #555; } -.task-show-details li { - margin-left: 25px; - list-style-type: circle; +.task-summary-column li { + line-height: 23px; } .task-show-section { @@ -329,3 +341,9 @@ div.color-square-selected { height: 28px; box-shadow: 3px 2px 10px 0 rgba(180,180,180,0.9); } + +/* Assign to me */ +.assign-me { + font-size: 0.8em; + vertical-align: bottom; +} diff --git a/assets/css/src/tooltip.css b/assets/css/src/tooltip.css index f74ac09a..84d709c9 100644 --- a/assets/css/src/tooltip.css +++ b/assets/css/src/tooltip.css @@ -76,3 +76,22 @@ div.ui-tooltip { .ui-tooltip ul { margin-left: 20px; } + +.ui-tooltip dl { + margin: -5px 0 0 0; + padding: 0; +} + +.ui-tooltip dt { + margin-top: 5px; +} + +.ui-tooltip dd { + margin-left: 0; +} + +.ui-tooltip .progress { + display: inline-block; + min-width: 3em; + text-align: right; +}
\ No newline at end of file diff --git a/assets/img/gitlab-icon.png b/assets/img/gitlab-icon.png Binary files differdeleted file mode 100644 index 88d48b40..00000000 --- a/assets/img/gitlab-icon.png +++ /dev/null diff --git a/assets/js/app.js b/assets/js/app.js index 6ab3ca2e..2c3260ad 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1278,4 +1278,4 @@ if (typeof jQuery === 'undefined') { return jQuery; })); -!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){function c(a){return a>1&&5>a&&1!==~~(a/10)}function d(a,b,d,e){var f=a+" ";switch(d){case"s":return b||e?"pár sekund":"pár sekundami";case"m":return b?"minuta":e?"minutu":"minutou";case"mm":return b||e?f+(c(a)?"minuty":"minut"):f+"minutami";case"h":return b?"hodina":e?"hodinu":"hodinou";case"hh":return b||e?f+(c(a)?"hodiny":"hodin"):f+"hodinami";case"d":return b||e?"den":"dnem";case"dd":return b||e?f+(c(a)?"dny":"dní"):f+"dny";case"M":return b||e?"měsíc":"měsícem";case"MM":return b||e?f+(c(a)?"měsíce":"měsíců"):f+"měsíci";case"y":return b||e?"rok":"rokem";case"yy":return b||e?f+(c(a)?"roky":"let"):f+"lety"}}var e="leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec".split("_"),f="led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro".split("_");(b.defineLocale||b.lang).call(b,"cs",{months:e,monthsShort:f,monthsParse:function(a,b){var c,d=[];for(c=0;12>c;c++)d[c]=new RegExp("^"+a[c]+"$|^"+b[c]+"$","i");return d}(e,f),weekdays:"neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota".split("_"),weekdaysShort:"ne_po_út_st_čt_pá_so".split("_"),weekdaysMin:"ne_po_út_st_čt_pá_so".split("_"),longDateFormat:{LT:"H: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:"[dnes v] LT",nextDay:"[zítra v] LT",nextWeek:function(){switch(this.day()){case 0:return"[v neděli v] LT";case 1:case 2:return"[v] dddd [v] LT";case 3:return"[ve středu v] LT";case 4:return"[ve čtvrtek v] LT";case 5:return"[v pátek v] LT";case 6:return"[v sobotu v] LT"}},lastDay:"[včera v] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulou neděli v] LT";case 1:case 2:return"[minulé] dddd [v] LT";case 3:return"[minulou středu v] LT";case 4:case 5:return"[minulý] dddd [v] LT";case 6:return"[minulou sobotu v] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"před %s",s:d,m:d,mm:d,h:d,hh:d,d:d,dd:d,M:d,MM:d,y:d,yy:d},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("cs","cs",{closeText:"Zavřít",prevText:"<Dříve",nextText:"Později>",currentText:"Nyní",monthNames:["leden","únor","březen","duben","květen","červen","červenec","srpen","září","říjen","listopad","prosinec"],monthNamesShort:["led","úno","bře","dub","kvě","čer","čvc","srp","zář","říj","lis","pro"],dayNames:["neděle","pondělí","úterý","středa","čtvrtek","pátek","sobota"],dayNamesShort:["ne","po","út","st","čt","pá","so"],dayNamesMin:["ne","po","út","st","čt","pá","so"],weekHeader:"Týd",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("cs",{buttonText:{month:"Měsíc",week:"Týden",day:"Den",list:"Agenda"},allDayText:"Celý den",eventLimitText:function(a){return"+další: "+a}})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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}}),a.fullCalendar.datepickerLang("da","da",{closeText:"Luk",prevText:"<Forrige",nextText:"Næste>",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:""}),a.fullCalendar.lang("da",{buttonText:{month:"Måned",week:"Uge",day:"Dag",list:"Agenda"},allDayText:"Hele dagen",eventLimitText:"flere"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){function c(a,b,c,d){var e={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[a+" Tage",a+" Tagen"],M:["ein Monat","einem Monat"],MM:[a+" Monate",a+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[a+" Jahre",a+" Jahren"]};return b?e[c][0]:e[c][1]}(b.defineLocale||b.lang).call(b,"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:c,mm:"%d Minuten",h:c,hh:"%d Stunden",d:c,dd:c,M:c,MM:c,y:c,yy:c},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("de","de",{closeText:"Schließen",prevText:"<Zurück",nextText:"Vor>",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:""}),a.fullCalendar.lang("de",{buttonText:{month:"Monat",week:"Woche",day:"Tag",list:"Terminübersicht"},allDayText:"Ganztägig",eventLimitText:function(a){return"+ weitere "+a}})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){var c="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),d="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_");(b.defineLocale||b.lang).call(b,"es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(a,b){return/-MMM-/.test(b)?d[a.month()]:c[a.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}}),a.fullCalendar.datepickerLang("es","es",{closeText:"Cerrar",prevText:"<Ant",nextText:"Sig>",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:""}),a.fullCalendar.lang("es",{buttonText:{month:"Mes",week:"Semana",day:"Día",list:"Agenda"},allDayHtml:"Todo<br/>el día",eventLimitText:"más"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){function c(a,b,c,e){var f="";switch(c){case"s":return e?"muutaman sekunnin":"muutama sekunti";case"m":return e?"minuutin":"minuutti";case"mm":f=e?"minuutin":"minuuttia";break;case"h":return e?"tunnin":"tunti";case"hh":f=e?"tunnin":"tuntia";break;case"d":return e?"päivän":"päivä";case"dd":f=e?"päivän":"päivää";break;case"M":return e?"kuukauden":"kuukausi";case"MM":f=e?"kuukauden":"kuukautta";break;case"y":return e?"vuoden":"vuosi";case"yy":f=e?"vuoden":"vuotta"}return f=d(a,e)+" "+f}function d(a,b){return 10>a?b?f[a]:e[a]:a}var e="nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän".split(" "),f=["nolla","yhden","kahden","kolmen","neljän","viiden","kuuden",e[7],e[8],e[9]];(b.defineLocale||b.lang).call(b,"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:c,m:c,mm:c,h:c,hh:c,d:c,dd:c,M:c,MM:c,y:c,yy:c},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("fi","fi",{closeText:"Sulje",prevText:"«Edellinen",nextText:"Seuraava»",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:""}),a.fullCalendar.lang("fi",{buttonText:{month:"Kuukausi",week:"Viikko",day:"Päivä",list:"Tapahtumat"},allDayText:"Koko päivä",eventLimitText:"lisää"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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(a){return a+(1===a?"er":"")},week:{dow:1,doy:4}}),a.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:""}),a.fullCalendar.lang("fr",{buttonText:{month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},allDayHtml:"Toute la<br/>journée",eventLimitText:"en plus"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){function c(a,b,c,d){var e=a;switch(c){case"s":return d||b?"néhány másodperc":"néhány másodperce";case"m":return"egy"+(d||b?" perc":" perce");case"mm":return e+(d||b?" perc":" perce");case"h":return"egy"+(d||b?" óra":" órája");case"hh":return e+(d||b?" óra":" órája");case"d":return"egy"+(d||b?" nap":" napja");case"dd":return e+(d||b?" nap":" napja");case"M":return"egy"+(d||b?" hónap":" hónapja");case"MM":return e+(d||b?" hónap":" hónapja");case"y":return"egy"+(d||b?" év":" éve");case"yy":return e+(d||b?" év":" éve")}return""}function d(a){return(a?"":"[múlt] ")+"["+e[this.day()]+"] LT[-kor]"}var e="vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton".split(" ");(b.defineLocale||b.lang).call(b,"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(a){return"u"===a.charAt(1).toLowerCase()},meridiem:function(a,b,c){return 12>a?c===!0?"de":"DE":c===!0?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return d.call(this,!0)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return d.call(this,!1)},sameElse:"L"},relativeTime:{future:"%s múlva",past:"%s",s:c,m:c,mm:c,h:c,hh:c,d:c,dd:c,M:c,MM:c,y:c,yy:c},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),a.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:""}),a.fullCalendar.lang("hu",{buttonText:{month:"Hónap",week:"Hét",day:"Nap",list:"Napló"},allDayText:"Egész nap",eventLimitText:"további"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"id",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nov_Des".split("_"),weekdays:"Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu".split("_"),weekdaysShort:"Min_Sen_Sel_Rab_Kam_Jum_Sab".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"LT.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] LT",LLLL:"dddd, D MMMM YYYY [pukul] LT"},meridiemParse:/pagi|siang|sore|malam/,meridiemHour:function(a,b){return 12===a&&(a=0),"pagi"===b?a:"siang"===b?a>=11?a:a+12:"sore"===b||"malam"===b?a+12:void 0},meridiem:function(a,b,c){return 11>a?"pagi":15>a?"siang":19>a?"sore":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Besok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kemarin pukul] LT",lastWeek:"dddd [lalu pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lalu",s:"beberapa detik",m:"semenit",mm:"%d menit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}}),a.fullCalendar.datepickerLang("id","id",{closeText:"Tutup",prevText:"<mundur",nextText:"maju>",currentText:"hari ini",monthNames:["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","Nopember","Desember"],monthNamesShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Agus","Sep","Okt","Nop","Des"],dayNames:["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"],dayNamesShort:["Min","Sen","Sel","Rab","kam","Jum","Sab"],dayNamesMin:["Mg","Sn","Sl","Rb","Km","jm","Sb"],weekHeader:"Mg",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("id",{buttonText:{month:"Bulan",week:"Minggu",day:"Hari",list:"Agenda"},allDayHtml:"Sehari<br/>penuh",eventLimitText:"lebih"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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(a){return(/^[0-9].+$/.test(a)?"tra":"in")+" "+a},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}}),a.fullCalendar.datepickerLang("it","it",{closeText:"Chiudi",prevText:"<Prec",nextText:"Succ>",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:""}),a.fullCalendar.lang("it",{buttonText:{month:"Mese",week:"Settimana",day:"Giorno",list:"Agenda"},allDayHtml:"Tutto il<br/>giorno",eventLimitText:function(a){return"+altri "+a}})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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(a){return"午後"===a},meridiem:function(a,b,c){return 12>a?"午前":"午後"},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年"}}),a.fullCalendar.datepickerLang("ja","ja",{closeText:"閉じる",prevText:"<前",nextText:"次>",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:"年"}),a.fullCalendar.lang("ja",{buttonText:{month:"月",week:"週",day:"日",list:"予定リスト"},allDayText:"終日",eventLimitText:function(a){return"他 "+a+" 件"}})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){var c="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),d="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_");(b.defineLocale||b.lang).call(b,"nl",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(a,b){return/-MMM-/.test(b)?d[a.month()]:c[a.month()]},weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"Zo_Ma_Di_Wo_Do_Vr_Za".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:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",m:"één minuut",mm:"%d minuten",h:"één uur",hh:"%d uur",d:"één dag",dd:"%d dagen",M:"één maand",MM:"%d maanden",y:"één jaar",yy:"%d jaar"},ordinalParse:/\d{1,2}(ste|de)/,ordinal:function(a){return a+(1===a||8===a||a>=20?"ste":"de")},week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("nl","nl",{closeText:"Sluiten",prevText:"←",nextText:"→",currentText:"Vandaag",monthNames:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthNamesShort:["jan","feb","mrt","apr","mei","jun","jul","aug","sep","okt","nov","dec"],dayNames:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],dayNamesShort:["zon","maa","din","woe","don","vri","zat"],dayNamesMin:["zo","ma","di","wo","do","vr","za"],weekHeader:"Wk",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("nl",{buttonText:{month:"Maand",week:"Week",day:"Dag",list:"Agenda"},allDayText:"Hele dag",eventLimitText:"extra"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"nb",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"søn_man_tirs_ons_tors_fre_lør".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),longDateFormat:{LT:"H.mm",LTS:"LT.ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] LT",LLLL:"dddd D. MMMM YYYY [kl.] LT"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[i går kl.] LT",lastWeek:"[forrige] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"for %s siden",s:"noen sekunder",m:"ett minutt",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dager",M:"en måned",MM:"%d måneder",y:"ett år",yy:"%d år"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("nb","nb",{closeText:"Lukk",prevText:"«Forrige",nextText:"Neste»",currentText:"I dag",monthNames:["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],monthNamesShort:["jan","feb","mar","apr","mai","jun","jul","aug","sep","okt","nov","des"],dayNamesShort:["søn","man","tir","ons","tor","fre","lør"],dayNames:["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],dayNamesMin:["sø","ma","ti","on","to","fr","lø"],weekHeader:"Uke",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("nb",{buttonText:{month:"Måned",week:"Uke",day:"Dag",list:"Agenda"},allDayText:"Hele dagen",eventLimitText:"til"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){function c(a){return 5>a%10&&a%10>1&&~~(a/10)%10!==1}function d(a,b,d){var e=a+" ";switch(d){case"m":return b?"minuta":"minutę";case"mm":return e+(c(a)?"minuty":"minut");case"h":return b?"godzina":"godzinę";case"hh":return e+(c(a)?"godziny":"godzin");case"MM":return e+(c(a)?"miesiące":"miesięcy");case"yy":return e+(c(a)?"lata":"lat")}}var e="styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"),f="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_");(b.defineLocale||b.lang).call(b,"pl",{months:function(a,b){return/D MMMM/.test(b)?f[a.month()]:e[a.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:d,mm:d,h:d,hh:d,d:"1 dzień",dd:"%d dni",M:"miesiąc",MM:d,y:"rok",yy:d},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("pl","pl",{closeText:"Zamknij",prevText:"<Poprzedni",nextText:"Następny>",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:""}),a.fullCalendar.lang("pl",{buttonText:{month:"Miesiąc",week:"Tydzień",day:"Dzień",list:"Plan dnia"},allDayText:"Cały dzień",eventLimitText:"więcej"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"pt",{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 LT",LLLL:"dddd, D [de] MMMM [de] YYYY 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:"há %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º",week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("pt","pt",{closeText:"Fechar",prevText:"Anterior",nextText:"Seguinte",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:"Sem",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("pt",{buttonText:{month:"Mês",week:"Semana",day:"Dia",list:"Agenda"},allDayText:"Todo o dia",eventLimitText:"mais"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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º"}),a.fullCalendar.datepickerLang("pt-br","pt-BR",{closeText:"Fechar",prevText:"<Anterior",nextText:"Próximo>",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:""}),a.fullCalendar.lang("pt-br",{buttonText:{month:"Mês",week:"Semana",day:"Dia",list:"Compromissos"},allDayText:"dia inteiro",eventLimitText:function(a){return"mais +"+a}})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){function c(a,b){var c=a.split("_");return b%10===1&&b%100!==11?c[0]:b%10>=2&&4>=b%10&&(10>b%100||b%100>=20)?c[1]:c[2]}function d(a,b,d){var e={mm:b?"минута_минуты_минут":"минуту_минуты_минут",hh:"час_часа_часов",dd:"день_дня_дней",MM:"месяц_месяца_месяцев",yy:"год_года_лет"};return"m"===d?b?"минута":"минуту":a+" "+c(e[d],+a)}function e(a,b){var c={nominative:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"),accusative:"января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря".split("_")},d=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(b)?"accusative":"nominative";return c[d][a.month()]}function f(a,b){var c={nominative:"янв_фев_март_апр_май_июнь_июль_авг_сен_окт_ноя_дек".split("_"),accusative:"янв_фев_мар_апр_мая_июня_июля_авг_сен_окт_ноя_дек".split("_")},d=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(b)?"accusative":"nominative";return c[d][a.month()]}function g(a,b){var c={nominative:"воскресенье_понедельник_вторник_среда_четверг_пятница_суббота".split("_"),accusative:"воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу".split("_")},d=/\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?\] ?dddd/.test(b)?"accusative":"nominative";return c[d][a.day()]}(b.defineLocale||b.lang).call(b,"ru",{months:e,monthsShort:f,weekdays:g,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(a){if(a.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:d,mm:d,h:"час",hh:d,d:"день",dd:d,M:"месяц",MM:d,y:"год",yy:d},meridiemParse:/ночи|утра|дня|вечера/i,isPM:function(a){return/^(дня|вечера)$/.test(a)},meridiem:function(a,b,c){return 4>a?"ночи":12>a?"утра":17>a?"дня":"вечера"},ordinalParse:/\d{1,2}-(й|го|я)/,ordinal:function(a,b){switch(b){case"M":case"d":case"DDD":return a+"-й";case"D":return a+"-го";case"w":case"W":return a+"-я";default:return a}},week:{dow:1,doy:7}}),a.fullCalendar.datepickerLang("ru","ru",{closeText:"Закрыть",prevText:"<Пред",nextText:"След>",currentText:"Сегодня",monthNames:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthNamesShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],dayNames:["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"],dayNamesShort:["вск","пнд","втр","срд","чтв","птн","сбт"],dayNamesMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],weekHeader:"Нед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("ru",{buttonText:{month:"Месяц",week:"Неделя",day:"День",list:"Повестка дня"},allDayText:"Весь день",eventLimitText:function(a){return"+ ещё "+a}})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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(a){var b=a%10,c=1===~~(a%100/10)?"e":1===b?"a":2===b?"a":"e";return a+c},week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("sv","sv",{closeText:"Stäng",prevText:"«Förra",nextText:"Nästa»",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:""}),a.fullCalendar.lang("sv",{buttonText:{month:"Månad",week:"Vecka",day:"Dag",list:"Program"},allDayText:"Heldag",eventLimitText:"till"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){var c={words:{m:["jedan minut","jedne minute"],mm:["minut","minute","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mesec","meseca","meseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(a,b){return 1===a?b[0]:a>=2&&4>=a?b[1]:b[2]},translate:function(a,b,d){var e=c.words[d];return 1===d.length?b?e[0]:e[1]:a+" "+c.correctGrammaticalCase(a,e)}};(b.defineLocale||b.lang).call(b,"sr",{months:["januar","februar","mart","april","maj","jun","jul","avgust","septembar","oktobar","novembar","decembar"],monthsShort:["jan.","feb.","mar.","apr.","maj","jun","jul","avg.","sep.","okt.","nov.","dec."],weekdays:["nedelja","ponedeljak","utorak","sreda","četvrtak","petak","subota"],weekdaysShort:["ned.","pon.","uto.","sre.","čet.","pet.","sub."],weekdaysMin:["ne","po","ut","sr","če","pe","su"],longDateFormat:{LT:"H: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:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedelju] [u] LT";case 3:return"[u] [sredu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){var a=["[prošle] [nedelje] [u] LT","[prošlog] [ponedeljka] [u] LT","[prošlog] [utorka] [u] LT","[prošle] [srede] [u] LT","[prošlog] [četvrtka] [u] LT","[prošlog] [petka] [u] LT","[prošle] [subote] [u] LT"];return a[this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"pre %s",s:"nekoliko sekundi",m:c.translate,mm:c.translate,h:c.translate,hh:c.translate,d:"dan",dd:c.translate,M:"mesec",MM:c.translate,y:"godinu",yy:c.translate},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),a.fullCalendar.datepickerLang("sr","sr",{closeText:"Затвори",prevText:"<",nextText:">",currentText:"Данас",monthNames:["Јануар","Фебруар","Март","Април","Мај","Јун","Јул","Август","Септембар","Октобар","Новембар","Децембар"],monthNamesShort:["Јан","Феб","Мар","Апр","Мај","Јун","Јул","Авг","Сеп","Окт","Нов","Дец"],dayNames:["Недеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"],dayNamesShort:["Нед","Пон","Уто","Сре","Чет","Пет","Суб"],dayNamesMin:["Не","По","Ут","Ср","Че","Пе","Су"],weekHeader:"Сед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("sr",{buttonText:{month:"Месец",week:"Недеља",day:"Дан",list:"Планер"},allDayText:"Цео дан",eventLimitText:function(a){return"+ још "+a}})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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(a){return"หลังเที่ยง"===a},meridiem:function(a,b,c){return 12>a?"ก่อนเที่ยง":"หลังเที่ยง"},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 ปี"}}),a.fullCalendar.datepickerLang("th","th",{closeText:"ปิด",prevText:"« ย้อน",nextText:"ถัดไป »",currentText:"วันนี้",monthNames:["มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม"],monthNamesShort:["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."],dayNames:["อาทิตย์","จันทร์","อังคาร","พุธ","พฤหัสบดี","ศุกร์","เสาร์"],dayNamesShort:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],dayNamesMin:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("th",{buttonText:{month:"เดือน",week:"สัปดาห์",day:"วัน",list:"แผนงาน"},allDayText:"ตลอดวัน",eventLimitText:"เพิ่มเติม"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){var c={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ı"};(b.defineLocale||b.lang).call(b,"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(a){if(0===a)return a+"'ıncı";var b=a%10,d=a%100-b,e=a>=100?100:null;return a+(c[b]||c[d]||c[e])},week:{dow:1,doy:7}}),a.fullCalendar.datepickerLang("tr","tr",{closeText:"kapat",prevText:"<geri",nextText:"ileri>",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:""}),a.fullCalendar.lang("tr",{buttonText:{next:"ileri",month:"Ay",week:"Hafta",day:"Gün",list:"Ajanda"},allDayText:"Tüm gün",eventLimitText:"daha fazla"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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(a,b){return 12===a&&(a=0),"凌晨"===b||"早上"===b||"上午"===b?a:"下午"===b||"晚上"===b?a+12:a>=11?a:a+12},meridiem:function(a,b,c){var d=100*a+b;return 600>d?"凌晨":900>d?"早上":1130>d?"上午":1230>d?"中午":1800>d?"下午":"晚上"},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 a,c;return a=b().startOf("week"),c=this.unix()-a.unix()>=604800?"[下]":"[本]",0===this.minutes()?c+"dddAh点整":c+"dddAh点mm"},lastWeek:function(){var a,c;return a=b().startOf("week"),c=this.unix()<a.unix()?"[上]":"[本]",0===this.minutes()?c+"dddAh点整":c+"dddAh点mm"},sameElse:"LL"},ordinalParse:/\d{1,2}(日|月|周)/,ordinal:function(a,b){switch(b){case"d":case"D":case"DDD":return a+"日";case"M":return a+"月";case"w":case"W":return a+"周";default:return a}},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}}),a.fullCalendar.datepickerLang("zh-cn","zh-CN",{closeText:"关闭",prevText:"<上月",nextText:"下月>",currentText:"今天",monthNames:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthNamesShort:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周六"],dayNamesMin:["日","一","二","三","四","五","六"],weekHeader:"周",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"年"}),a.fullCalendar.lang("zh-cn",{buttonText:{month:"月",week:"周",day:"日",list:"日程"},allDayText:"全天",eventLimitText:function(a){return"另外 "+a+" 个"}})});(function(){function t(x){this.app=x;this.router=new v();this.router.addRoute("screenshot-zone",e)}t.prototype.isOpen=function(){return $("#popover-container").size()>0};t.prototype.open=function(y){var x=this;x.app.dropdown.close();$.get(y,function(z){$("body").append('<div id="popover-container"><div id="popover-content">'+z+"</div></div>");x.app.refresh();x.router.dispatch(this.app);x.afterOpen()})};t.prototype.close=function(x){if(this.isOpen()){if(x){x.preventDefault()}$("#popover-container").remove()}};t.prototype.onClick=function(y){y.preventDefault();y.stopPropagation();var x=y.target.getAttribute("href");if(!x){x=y.target.getAttribute("data-href")}if(x){this.open(x)}};t.prototype.listen=function(){$(document).on("click",".popover",this.onClick.bind(this));$(document).on("click",".close-popover",this.close.bind(this));$(document).on("click","#popover-container",this.close.bind(this));$(document).on("click","#popover-content",function(x){x.stopPropagation()})};t.prototype.afterOpen=function(){var x=this;var y=$("#task-form");if(y){y.on("submit",function(z){z.preventDefault();$.ajax({type:"POST",url:y.attr("action"),data:y.serialize(),success:function(B,C,A){if(A.getResponseHeader("X-Ajax-Redirect")){window.location=A.getResponseHeader("X-Ajax-Redirect")}else{$("#popover-content").html(B);x.afterOpen()}}})})}};function r(){}r.prototype.listen=function(){var x=this;$(document).on("click",function(){x.close()});$(document).on("click",".dropdown-menu",function(B){B.preventDefault();B.stopImmediatePropagation();x.close();var z=$(this).next("ul");var C=$(this).offset();$("body").append(jQuery("<div>",{id:"dropdown"}));z.clone().appendTo("#dropdown");var D=$("#dropdown ul");D.addClass("dropdown-submenu-open");var A=D.outerHeight();var y=D.outerWidth();if(C.top+A-$(window).scrollTop()>$(window).height()){D.css("top",C.top-A-5)}else{D.css("top",C.top+$(this).height())}if(C.left+y>$(window).width()){D.css("left",C.left-y+$(this).outerWidth())}else{D.css("left",C.left)}});$(document).on("click",".dropdown-submenu-open li",function(y){if($(y.target).is("li")){$(this).find("a:visible")[0].click()}});$("textarea[data-mention-search-url]").textcomplete([{match:/(^|\s)@(\w*)$/,search:function(z,A){var y=$("textarea[data-mention-search-url]").data("mention-search-url");$.getJSON(y,{q:z}).done(function(B){A(B)}).fail(function(){A([])})},replace:function(y){return"$1@"+y+" "},cache:true}],{className:"textarea-dropdown"})};r.prototype.close=function(){$("#dropdown").remove()};function q(x){this.app=x}q.prototype.listen=function(){var x=this;$(".tooltip").tooltip({track:false,show:false,hide:false,position:{my:"left-20 top",at:"center bottom+9",using:function(y,z){$(this).css(y);var A=z.target.left+z.target.width/2-z.element.left-20;$("<div>").addClass("tooltip-arrow").addClass(z.vertical).addClass(A<1?"align-left":"align-right").appendTo(this)}},content:function(){var A=this;var y=$(this).attr("data-href");if(!y){return'<div class="markdown">'+$(this).attr("title")+"</div>"}$.get(y,function z(D){var C=$(".ui-tooltip:visible");$(".ui-tooltip-content:visible").html(D);C.css({top:"",left:""});C.children(".tooltip-arrow").remove();var B=$(A).tooltip("option","position");B.of=$(A);C.position(B);$("#tooltip-subtasks a").not(".popover").click(function(E){E.preventDefault();E.stopPropagation();if($(this).hasClass("popover-subtask-restriction")){x.app.popover.open($(this).attr("href"));$(A).tooltip("close")}else{$.get($(this).attr("href"),z)}})});return'<i class="fa fa-spinner fa-spin"></i>'}}).on("mouseenter",function(){var y=this;$(this).tooltip("open");$(".ui-tooltip").on("mouseleave",function(){$(y).tooltip("close")})}).on("mouseleave focusout",function(y){y.stopImmediatePropagation();var z=this;setTimeout(function(){if(!$(".ui-tooltip:hover").length){$(z).tooltip("close")}},100)})};function l(){}l.prototype.showPreview=function(B){B.preventDefault();var y=$(".write-area");var A=$(".preview-area");var x=$("textarea");$("#markdown-write").parent().removeClass("form-tab-selected");$("#markdown-preview").parent().addClass("form-tab-selected");var z=$.ajax({url:$("body").data("markdown-preview-url"),contentType:"application/json",type:"POST",processData:false,dataType:"html",data:JSON.stringify({text:x.val()})});z.done(function(C){A.find(".markdown").html(C);A.css("height",x.css("height"));A.css("width",x.css("width"));y.hide();A.show()})};l.prototype.showWriter=function(x){x.preventDefault();$("#markdown-write").parent().addClass("form-tab-selected");$("#markdown-preview").parent().removeClass("form-tab-selected");$(".write-area").show();$(".preview-area").hide()};l.prototype.listen=function(){$(document).on("click","#markdown-preview",this.showPreview.bind(this));$(document).on("click","#markdown-write",this.showWriter.bind(this))};function b(){}b.prototype.expand=function(x){x.preventDefault();$(".sidebar-container").removeClass("sidebar-collapsed");$(".sidebar-collapse").show();$(".sidebar h2").show();$(".sidebar ul").show();$(".sidebar-expand").hide()};b.prototype.collapse=function(x){x.preventDefault();$(".sidebar-container").addClass("sidebar-collapsed");$(".sidebar-expand").show();$(".sidebar h2").hide();$(".sidebar ul").hide();$(".sidebar-collapse").hide()};b.prototype.listen=function(){$(document).on("click",".sidebar-collapse",this.collapse);$(document).on("click",".sidebar-expand",this.expand)};function f(x){this.app=x;this.keyboardShortcuts()}f.prototype.focus=function(){$(document).on("focus","#form-search",function(){if($("#form-search")[0].setSelectionRange){$("#form-search")[0].setSelectionRange($("#form-search").val().length,$("#form-search").val().length)}})};f.prototype.listen=function(){var x=this;$(document).on("click",".filter-helper",function(A){A.preventDefault();var z=$(this).data("filter");var y=$(this).data("append-filter");if(y){z=$("#form-search").val()+" "+y}$("#form-search").val(z);if($("#board").length){x.app.board.reloadFilters(z)}else{$("form.search").submit()}})};f.prototype.keyboardShortcuts=function(){var x=this;Mousetrap.bind("v b",function(z){var y=$(".view-board");if(y.length){window.location=y.attr("href")}});Mousetrap.bind("v c",function(z){var y=$(".view-calendar");if(y.length){window.location=y.attr("href")}});Mousetrap.bind("v l",function(z){var y=$(".view-listing");if(y.length){window.location=y.attr("href")}});Mousetrap.bind("v g",function(z){var y=$(".view-gantt");if(y.length){window.location=y.attr("href")}});Mousetrap.bind("f",function(z){z.preventDefault();var y=document.getElementById("form-search");if(y){y.focus()}});Mousetrap.bind("r",function(z){z.preventDefault();var y=$(".filter-reset").data("filter");$("#form-search").val(y);if($("#board").length){x.app.board.reloadFilters(y)}else{$("form.search").submit()}})};function m(){this.board=new k(this);this.markdown=new l();this.sidebar=new b();this.search=new f(this);this.swimlane=new g();this.dropdown=new r();this.tooltip=new q(this);this.popover=new t(this);this.task=new a();this.project=new n();this.keyboardShortcuts();this.chosen();this.poll();$(".alert-fade-out").delay(4000).fadeOut(800,function(){$(this).remove()});var x=false;$("select.task-reload-project-destination").change(function(){if(!x){$(".loading-icon").show();x=true;window.location=$(this).data("redirect").replace(/PROJECT_ID/g,$(this).val())}})}m.prototype.listen=function(){this.project.listen();this.popover.listen();this.markdown.listen();this.sidebar.listen();this.tooltip.listen();this.dropdown.listen();this.search.listen();this.task.listen();this.swimlane.listen();this.search.focus();this.autoComplete();this.datePicker();this.focus()};m.prototype.refresh=function(){$(document).off();this.listen()};m.prototype.focus=function(){$("[autofocus]").each(function(x,y){$(this).focus()});$(document).on("focus",".auto-select",function(){$(this).select()});$(document).on("mouseup",".auto-select",function(x){x.preventDefault()})};m.prototype.poll=function(){window.setInterval(this.checkSession,60000)};m.prototype.keyboardShortcuts=function(){var x=this;Mousetrap.bindGlobal("mod+enter",function(){$("form").submit()});Mousetrap.bind("b",function(y){y.preventDefault();$("#board-selector").trigger("chosen:open")});Mousetrap.bindGlobal("esc",function(){x.popover.close();x.dropdown.close()})};m.prototype.checkSession=function(){if(!$(".form-login").length){$.ajax({cache:false,url:$("body").data("status-url"),statusCode:{401:function(){window.location=$("body").data("login-url")}}})}};m.prototype.datePicker=function(){$.datepicker.setDefaults($.datepicker.regional[$("body").data("js-lang")]);$(".form-date").datepicker({showOtherMonths:true,selectOtherMonths:true,dateFormat:"yy-mm-dd",constrainInput:false});$(".form-datetime").datetimepicker({controlType:"select",oneLine:true,dateFormat:"yy-mm-dd",constrainInput:false})};m.prototype.autoComplete=function(){$(".autocomplete").each(function(){var y=$(this);var z=y.data("dst-field");var x=y.data("dst-extra-field");if($("#form-"+z).val()==""){y.parent().find("input[type=submit]").attr("disabled","disabled")}y.autocomplete({source:y.data("search-url"),minLength:1,select:function(A,B){$("input[name="+z+"]").val(B.item.id);if(x){$("input[name="+x+"]").val(B.item[x])}y.parent().find("input[type=submit]").removeAttr("disabled")}})})};m.prototype.chosen=function(){$(".chosen-select").each(function(){var x=$(this).data("search-threshold");if(x===undefined){x=10}$(this).chosen({width:"180px",no_results_text:$(this).data("notfound"),disable_search_threshold:x})});$(".select-auto-redirect").change(function(){var x=new RegExp($(this).data("redirect-regex"),"g");window.location=$(this).data("redirect-url").replace(x,$(this).val())})};m.prototype.showLoadingIcon=function(){$("body").append('<span id="app-loading-icon"> <i class="fa fa-spinner fa-spin"></i></span>')};m.prototype.hideLoadingIcon=function(){$("#app-loading-icon").remove()};m.prototype.isVisible=function(){var x="";if(typeof document.hidden!=="undefined"){x="visibilityState"}else{if(typeof document.mozHidden!=="undefined"){x="mozVisibilityState"}else{if(typeof document.msHidden!=="undefined"){x="msVisibilityState"}else{if(typeof document.webkitHidden!=="undefined"){x="webkitVisibilityState"}}}}if(x!=""){return document[x]=="visible"}return true};m.prototype.formatDuration=function(x){if(x>=86400){return Math.round(x/86400)+"d"}else{if(x>=3600){return Math.round(x/3600)+"h"}else{if(x>=60){return Math.round(x/60)+"m"}}}return x+"s"};function e(){this.pasteCatcher=null}e.prototype.execute=function(){this.initialize()};e.prototype.initialize=function(){this.destroy();if(!window.Clipboard){this.pasteCatcher=document.createElement("div");this.pasteCatcher.id="screenshot-pastezone";this.pasteCatcher.contentEditable="true";this.pasteCatcher.style.opacity=0;this.pasteCatcher.style.position="fixed";this.pasteCatcher.style.top=0;this.pasteCatcher.style.right=0;this.pasteCatcher.style.width=0;document.body.insertBefore(this.pasteCatcher,document.body.firstChild);this.pasteCatcher.focus();document.addEventListener("click",this.setFocus.bind(this));document.getElementById("screenshot-zone").addEventListener("click",this.setFocus.bind(this))}window.addEventListener("paste",this.pasteHandler.bind(this))};e.prototype.destroy=function(){if(this.pasteCatcher!=null){document.body.removeChild(this.pasteCatcher)}else{if(document.getElementById("screenshot-pastezone")){document.body.removeChild(document.getElementById("screenshot-pastezone"))}}document.removeEventListener("click",this.setFocus.bind(this));this.pasteCatcher=null};e.prototype.setFocus=function(){if(this.pasteCatcher!==null){this.pasteCatcher.focus()}};e.prototype.pasteHandler=function(C){if(C.clipboardData&&C.clipboardData.items){var A=C.clipboardData.items;if(A){for(var B=0;B<A.length;B++){if(A[B].type.indexOf("image")!==-1){var z=A[B].getAsFile();var x=new FileReader();var y=this;x.onload=function(D){y.createImage(D.target.result)};x.readAsDataURL(z)}}}}else{setTimeout(this.checkInput.bind(this),100)}};e.prototype.checkInput=function(){var x=this.pasteCatcher.childNodes[0];if(x){if(x.tagName==="IMG"){this.createImage(x.src)}}this.pasteCatcher.innerHTML=""};e.prototype.createImage=function(z){var y=new Image();y.src=z;y.onload=function(){var A=z.split("base64,");var B=A[1];$("input[name=screenshot]").val(B)};var x=document.getElementById("screenshot-zone");x.innerHTML="";x.className="screenshot-pasted";x.appendChild(y);this.destroy();this.initialize()};function j(){}j.prototype.execute=function(){var x=$("#calendar");x.fullCalendar({lang:$("body").data("js-lang"),editable:true,eventLimit:true,defaultView:"month",header:{left:"prev,next today",center:"title",right:"month,agendaWeek,agendaDay"},eventDrop:function(y){$.ajax({cache:false,url:x.data("save-url"),contentType:"application/json",type:"POST",processData:false,data:JSON.stringify({task_id:y.id,date_due:y.start.format()})})},viewRender:function(){var y=x.data("check-url");var A={start:x.fullCalendar("getView").start.format(),end:x.fullCalendar("getView").end.format()};for(var z in A){y+="&"+z+"="+A[z]}$.getJSON(y,function(B){x.fullCalendar("removeEvents");x.fullCalendar("addEventSource",B);x.fullCalendar("rerenderEvents")})}})};function k(x){this.app=x;this.checkInterval=null;this.savingInProgress=false}k.prototype.execute=function(){this.app.swimlane.refresh();this.restoreColumnViewMode();this.compactView();this.poll();this.keyboardShortcuts();this.listen();this.dragAndDrop();$(window).on("load",this.columnScrolling);$(window).resize(this.columnScrolling)};k.prototype.poll=function(){var x=parseInt($("#board").attr("data-check-interval"));if(x>0){this.checkInterval=window.setInterval(this.check.bind(this),x*1000)}};k.prototype.reloadFilters=function(x){this.app.showLoadingIcon();$.ajax({cache:false,url:$("#board").data("reload-url"),contentType:"application/json",type:"POST",processData:false,data:JSON.stringify({search:x}),success:this.refresh.bind(this),error:this.app.hideLoadingIcon.bind(this)})};k.prototype.check=function(){if(this.app.isVisible()&&!this.savingInProgress){var x=this;this.app.showLoadingIcon();$.ajax({cache:false,url:$("#board").data("check-url"),statusCode:{200:function(y){x.refresh(y)},304:function(){x.app.hideLoadingIcon()}}})}};k.prototype.save=function(A,B,x,z){var y=this;this.app.showLoadingIcon();this.savingInProgress=true;$.ajax({cache:false,url:$("#board").data("save-url"),contentType:"application/json",type:"POST",processData:false,data:JSON.stringify({task_id:A,column_id:B,swimlane_id:z,position:x}),success:function(C){y.refresh(C);this.savingInProgress=false},error:function(){y.app.hideLoadingIcon();this.savingInProgress=false}})};k.prototype.refresh=function(x){$("#board-container").replaceWith(x);this.app.refresh();this.app.swimlane.refresh();this.app.hideLoadingIcon();this.listen();this.dragAndDrop();this.compactView();this.restoreColumnViewMode();this.columnScrolling()};k.prototype.dragAndDrop=function(){var x=this;var y={forcePlaceholderSize:true,tolerance:"pointer",connectWith:".board-task-list",placeholder:"draggable-placeholder",items:".draggable-item",stop:function(A,H){var C=H.item;var G=C.attr("data-task-id");var I=C.attr("data-position");var F=C.attr("data-column-id");var E=C.attr("data-swimlane-id");var B=C.parent().attr("data-column-id");var z=C.parent().attr("data-swimlane-id");var D=C.index()+1;C.removeClass("draggable-item-selected");if(B!=F||z!=E||D!=I){x.changeTaskState(G);x.save(G,B,D,z)}},start:function(z,A){A.item.addClass("draggable-item-selected");A.placeholder.height(A.item.height())}};if($.support.touch){$(".task-board-sort-handle").css("display","inline");y.handle=".task-board-sort-handle"}$(".board-task-list").sortable(y)};k.prototype.changeTaskState=function(y){var x=$("div[data-task-id="+y+"]");x.addClass("task-board-saving-state");x.find(".task-board-saving-icon").show()};k.prototype.listen=function(){var x=this;$(document).on("click",".task-board",function(y){if(y.target.tagName!="A"){window.location=$(this).data("task-url")}});$(document).on("click",".filter-toggle-scrolling",function(y){y.preventDefault();x.toggleCompactView()});$(document).on("click",".filter-toggle-height",function(y){y.preventDefault();x.toggleColumnScrolling()});$(document).on("click",".board-toggle-column-view",function(){x.toggleColumnViewMode($(this).data("column-id"))})};k.prototype.toggleColumnScrolling=function(){var x=localStorage.getItem("column_scroll");if(x==undefined){x=1}localStorage.setItem("column_scroll",x==0?1:0);this.columnScrolling()};k.prototype.columnScrolling=function(){if(localStorage.getItem("column_scroll")==0){var x=80;$(".filter-max-height").show();$(".filter-min-height").hide();$(".board-rotation-wrapper").css("min-height","");$(".board-task-list").each(function(){var y=$(this).height();if(y>x){x=y}});$(".board-task-list").css("min-height",x);$(".board-task-list").css("height","")}else{$(".filter-max-height").hide();$(".filter-min-height").show();if($(".board-swimlane").length>1){$(".board-task-list").each(function(){if($(this).height()>500){$(this).css("height",500)}else{$(this).css("min-height",320);$(".board-rotation-wrapper").css("min-height",320)}})}else{var x=$(window).height()-170;$(".board-task-list").css("height",x);$(".board-rotation-wrapper").css("min-height",x)}}};k.prototype.toggleCompactView=function(){var x=localStorage.getItem("horizontal_scroll")||1;localStorage.setItem("horizontal_scroll",x==0?1:0);this.compactView()};k.prototype.compactView=function(){if(localStorage.getItem("horizontal_scroll")==0){$(".filter-wide").show();$(".filter-compact").hide();$("#board-container").addClass("board-container-compact");$("#board th:not(.board-column-header-collapsed)").addClass("board-column-compact")}else{$(".filter-wide").hide();$(".filter-compact").show();$("#board-container").removeClass("board-container-compact");$("#board th").removeClass("board-column-compact")}};k.prototype.toggleCollapsedMode=function(){var x=this;this.app.showLoadingIcon();$.ajax({cache:false,url:$('.filter-display-mode:not([style="display: none;"]) a').attr("href"),success:function(y){$(".filter-display-mode").toggle();x.refresh(y)}})};k.prototype.restoreColumnViewMode=function(){var x=this;$(".board-column-header").each(function(){var y=$(this).data("column-id");if(localStorage.getItem("hidden_column_"+y)){x.hideColumn(y)}})};k.prototype.toggleColumnViewMode=function(x){if(localStorage.getItem("hidden_column_"+x)){this.showColumn(x)}else{this.hideColumn(x)}};k.prototype.hideColumn=function(x){$(".board-column-"+x+" .board-column-expanded").hide();$(".board-column-"+x+" .board-column-collapsed").show();$(".board-column-header-"+x+" .board-column-expanded").hide();$(".board-column-header-"+x+" .board-column-collapsed").show();$(".board-column-header-"+x).each(function(){$(this).removeClass("board-column-compact");$(this).addClass("board-column-header-collapsed")});$(".board-column-"+x).each(function(){$(this).addClass("board-column-task-collapsed")});$(".board-column-"+x+" .board-rotation").each(function(){$(this).css("width",$(".board-column-"+x+"").height())});localStorage.setItem("hidden_column_"+x,1)};k.prototype.showColumn=function(x){$(".board-column-"+x+" .board-column-expanded").show();$(".board-column-"+x+" .board-column-collapsed").hide();$(".board-column-header-"+x+" .board-column-expanded").show();$(".board-column-header-"+x+" .board-column-collapsed").hide();$(".board-column-header-"+x).removeClass("board-column-header-collapsed");$(".board-column-"+x).removeClass("board-column-task-collapsed");if(localStorage.getItem("horizontal_scroll")==0){$(".board-column-header-"+x).addClass("board-column-compact")}localStorage.removeItem("hidden_column_"+x)};k.prototype.keyboardShortcuts=function(){var x=this;Mousetrap.bind("c",function(){x.toggleCompactView()});Mousetrap.bind("s",function(){x.toggleCollapsedMode()});Mousetrap.bind("n",function(){x.app.popover.open($("#board").data("task-creation-url"))})};function g(){}g.prototype.getStorageKey=function(){return"hidden_swimlanes_"+$("#board").data("project-id")};g.prototype.expand=function(y){var z=this.getAllCollapsed();var x=z.indexOf(y);if(x>-1){z.splice(x,1)}localStorage.setItem(this.getStorageKey(),JSON.stringify(z));$(".board-swimlane-columns-"+y).css("display","table-row");$(".board-swimlane-tasks-"+y).css("display","table-row");$(".hide-icon-swimlane-"+y).css("display","inline");$(".show-icon-swimlane-"+y).css("display","none")};g.prototype.collapse=function(x){var y=this.getAllCollapsed();if(y.indexOf(x)<0){y.push(x);localStorage.setItem(this.getStorageKey(),JSON.stringify(y))}$(".board-swimlane-columns-"+x+":not(:first-child)").css("display","none");$(".board-swimlane-tasks-"+x).css("display","none");$(".hide-icon-swimlane-"+x).css("display","none");$(".show-icon-swimlane-"+x).css("display","inline")};g.prototype.isCollapsed=function(x){return this.getAllCollapsed().indexOf(x)>-1};g.prototype.getAllCollapsed=function(){return JSON.parse(localStorage.getItem(this.getStorageKey()))||[]};g.prototype.refresh=function(){var y=this.getAllCollapsed();for(var x=0;x<y.length;x++){this.collapse(y[x])}};g.prototype.listen=function(){var x=this;$(document).on("click",".board-swimlane-toggle",function(z){z.preventDefault();var y=$(this).data("swimlane-id");if(x.isCollapsed(y)){x.expand(y)}else{x.collapse(y)}})};function c(x){this.app=x;this.data=[];this.options={container:"#gantt-chart",showWeekends:true,allowMoves:true,allowResizes:true,cellWidth:21,cellHeight:31,slideWidth:1000,vHeaderWidth:200}}c.prototype.saveRecord=function(x){this.app.showLoadingIcon();$.ajax({cache:false,url:$(this.options.container).data("save-url"),contentType:"application/json",type:"POST",processData:false,data:JSON.stringify(x),complete:this.app.hideLoadingIcon.bind(this)})};c.prototype.execute=function(){this.data=this.prepareData($(this.options.container).data("records"));var A=Math.floor((this.options.slideWidth/this.options.cellWidth)+5);var z=this.getDateRange(A);var x=z[0];var C=z[1];var y=$(this.options.container);var B=jQuery("<div>",{"class":"ganttview"});B.append(this.renderVerticalHeader());B.append(this.renderSlider(x,C));y.append(B);jQuery("div.ganttview-grid-row div.ganttview-grid-row-cell:last-child",y).addClass("last");jQuery("div.ganttview-hzheader-days div.ganttview-hzheader-day:last-child",y).addClass("last");jQuery("div.ganttview-hzheader-months div.ganttview-hzheader-month:last-child",y).addClass("last");if(!$(this.options.container).data("readonly")){this.listenForBlockResize(x);this.listenForBlockMove(x)}else{this.options.allowResizes=false;this.options.allowMoves=false}};c.prototype.renderVerticalHeader=function(){var B=jQuery("<div>",{"class":"ganttview-vtheader"});var y=jQuery("<div>",{"class":"ganttview-vtheader-item"});var A=jQuery("<div>",{"class":"ganttview-vtheader-series"});for(var x=0;x<this.data.length;x++){var z=jQuery("<span>").append(jQuery("<i>",{"class":"fa fa-info-circle tooltip",title:this.getVerticalHeaderTooltip(this.data[x])})).append(" ");if(this.data[x].type=="task"){z.append(jQuery("<a>",{href:this.data[x].link,target:"_blank",title:this.data[x].title}).append(this.data[x].title))}else{z.append(jQuery("<a>",{href:this.data[x].board_link,target:"_blank",title:$(this.options.container).data("label-board-link")}).append('<i class="fa fa-th"></i>')).append(" ").append(jQuery("<a>",{href:this.data[x].gantt_link,target:"_blank",title:$(this.options.container).data("label-gantt-link")}).append('<i class="fa fa-sliders"></i>')).append(" ").append(jQuery("<a>",{href:this.data[x].link,target:"_blank"}).append(this.data[x].title))}A.append(jQuery("<div>",{"class":"ganttview-vtheader-series-name"}).append(z))}y.append(A);B.append(y);return B};c.prototype.renderSlider=function(y,A){var x=jQuery("<div>",{"class":"ganttview-slide-container"});var z=this.getDates(y,A);x.append(this.renderHorizontalHeader(z));x.append(this.renderGrid(z));x.append(this.addBlockContainers());this.addBlocks(x,y);return x};c.prototype.renderHorizontalHeader=function(x){var E=jQuery("<div>",{"class":"ganttview-hzheader"});var C=jQuery("<div>",{"class":"ganttview-hzheader-months"});var B=jQuery("<div>",{"class":"ganttview-hzheader-days"});var A=0;for(var F in x){for(var z in x[F]){var G=x[F][z].length*this.options.cellWidth;A=A+G;C.append(jQuery("<div>",{"class":"ganttview-hzheader-month",css:{width:(G-1)+"px"}}).append($.datepicker.regional[$("body").data("js-lang")].monthNames[z]+" "+F));for(var D in x[F][z]){B.append(jQuery("<div>",{"class":"ganttview-hzheader-day"}).append(x[F][z][D].getDate()))}}}C.css("width",A+"px");B.css("width",A+"px");E.append(C).append(B);return E};c.prototype.renderGrid=function(x){var G=jQuery("<div>",{"class":"ganttview-grid"});var B=jQuery("<div>",{"class":"ganttview-grid-row"});for(var E in x){for(var z in x[E]){for(var D in x[E][z]){var A=jQuery("<div>",{"class":"ganttview-grid-row-cell"});if(this.options.showWeekends&&this.isWeekend(x[E][z][D])){A.addClass("ganttview-weekend")}B.append(A)}}}var F=jQuery("div.ganttview-grid-row-cell",B).length*this.options.cellWidth;B.css("width",F+"px");G.css("width",F+"px");for(var C=0;C<this.data.length;C++){G.append(B.clone())}return G};c.prototype.addBlockContainers=function(){var y=jQuery("<div>",{"class":"ganttview-blocks"});for(var x=0;x<this.data.length;x++){y.append(jQuery("<div>",{"class":"ganttview-block-container"}))}return y};c.prototype.addBlocks=function(y,x){var F=jQuery("div.ganttview-blocks div.ganttview-block-container",y);var z=0;for(var C=0;C<this.data.length;C++){var D=this.data[C];var G=this.daysBetween(D.start,D.end)+1;var B=this.daysBetween(x,D.start);var E=jQuery("<div>",{"class":"ganttview-block-text"});var A=jQuery("<div>",{"class":"ganttview-block tooltip"+(this.options.allowMoves?" ganttview-block-movable":""),title:this.getBarTooltip(D),css:{width:((G*this.options.cellWidth)-9)+"px","margin-left":(B*this.options.cellWidth)+"px"}}).append(E);if(G>=2){E.append(D.progress)}A.data("record",D);this.setBarColor(A,D);if(D.progress!="0%"){A.append(jQuery("<div>",{css:{"z-index":0,position:"absolute",top:0,bottom:0,"background-color":D.color.border,width:D.progress,opacity:0.4}}))}jQuery(F[z]).append(A);z=z+1}};c.prototype.getVerticalHeaderTooltip=function(y){var D="";if(y.type=="task"){D="<strong>"+y.column_title+"</strong> ("+y.progress+")<br/>"+y.title}else{var A=["managers","members"];for(var z in A){var B=A[z];if(!jQuery.isEmptyObject(y.users[B])){var C=jQuery("<ul>");for(var x in y.users[B]){C.append(jQuery("<li>").append(y.users[B][x]))}D+="<p><strong>"+$(this.options.container).data("label-"+B)+"</strong></p>"+C[0].outerHTML}}}return D};c.prototype.getBarTooltip=function(x){var y="";if(x.not_defined){y=$(this.options.container).data("label-not-defined")}else{if(x.type=="task"){y="<strong>"+x.progress+"</strong><br/>"+$(this.options.container).data("label-assignee")+" "+(x.assignee?x.assignee:"")+"<br/>"}y+=$(this.options.container).data("label-start-date")+" "+$.datepicker.formatDate("yy-mm-dd",x.start)+"<br/>";y+=$(this.options.container).data("label-end-date")+" "+$.datepicker.formatDate("yy-mm-dd",x.end)}return y};c.prototype.setBarColor=function(y,x){if(x.not_defined){y.addClass("ganttview-block-not-defined")}else{y.css("background-color",x.color.background);y.css("border-color",x.color.border)}};c.prototype.listenForBlockResize=function(x){var y=this;jQuery("div.ganttview-block",this.options.container).resizable({grid:this.options.cellWidth,handles:"e,w",delay:300,stop:function(){var z=jQuery(this);y.updateDataAndPosition(z,x);y.saveRecord(z.data("record"))}})};c.prototype.listenForBlockMove=function(x){var y=this;jQuery("div.ganttview-block",this.options.container).draggable({axis:"x",delay:300,grid:[this.options.cellWidth,this.options.cellWidth],stop:function(){var z=jQuery(this);y.updateDataAndPosition(z,x);y.saveRecord(z.data("record"))}})};c.prototype.updateDataAndPosition=function(C,A){var x=jQuery("div.ganttview-slide-container",this.options.container);var G=x.scrollLeft();var D=C.offset().left-x.offset().left-1+G;var F=C.data("record");F.not_defined=false;this.setBarColor(C,F);var z=Math.round(D/this.options.cellWidth);var E=this.addDays(this.cloneDate(A),z);F.start=E;var y=C.outerWidth();var B=Math.round(y/this.options.cellWidth)-1;F.end=this.addDays(this.cloneDate(E),B);if(F.type==="task"&&B>0){jQuery("div.ganttview-block-text",C).text(F.progress)}C.attr("title",this.getBarTooltip(F));C.data("record",F);C.css("top","").css("left","").css("position","relative").css("margin-left",D+"px")};c.prototype.getDates=function(B,x){var A=[];A[B.getFullYear()]=[];A[B.getFullYear()][B.getMonth()]=[B];var z=B;while(this.compareDate(z,x)==-1){var y=this.addDays(this.cloneDate(z),1);if(!A[y.getFullYear()]){A[y.getFullYear()]=[]}if(!A[y.getFullYear()][y.getMonth()]){A[y.getFullYear()][y.getMonth()]=[]}A[y.getFullYear()][y.getMonth()].push(y);z=y}return A};c.prototype.prepareData=function(z){for(var y=0;y<z.length;y++){var A=new Date(z[y].start[0],z[y].start[1]-1,z[y].start[2],0,0,0,0);z[y].start=A;var x=new Date(z[y].end[0],z[y].end[1]-1,z[y].end[2],0,0,0,0);z[y].end=x}return z};c.prototype.getDateRange=function(z){var C=new Date();var y=new Date();for(var A=0;A<this.data.length;A++){var B=new Date();B.setTime(Date.parse(this.data[A].start));var x=new Date();x.setTime(Date.parse(this.data[A].end));if(A==0){C=B;y=x}if(this.compareDate(C,B)==1){C=B}if(this.compareDate(y,x)==-1){y=x}}if(this.daysBetween(C,y)<z){y=this.addDays(this.cloneDate(C),z)}C.setDate(C.getDate()-1);return[C,y]};c.prototype.daysBetween=function(A,x){if(!A||!x){return 0}var z=0,y=this.cloneDate(A);while(this.compareDate(y,x)==-1){z=z+1;this.addDays(y,1)}return z};c.prototype.isWeekend=function(x){return x.getDay()%6==0};c.prototype.cloneDate=function(x){return new Date(x.getTime())};c.prototype.addDays=function(x,y){x.setDate(x.getDate()+y*1);return x};c.prototype.compareDate=function(y,x){if(isNaN(y)||isNaN(x)){throw new Error(y+" - "+x)}else{if(y instanceof Date&&x instanceof Date){return(y<x)?-1:(y>x)?1:0}else{throw new TypeError(y+" - "+x)}}};function a(){}a.prototype.listen=function(){$(document).on("click",".color-square",function(){$(".color-square-selected").removeClass("color-square-selected");$(this).addClass("color-square-selected");$("#form-color_id").val($(this).data("color-id"))})};function n(){}n.prototype.listen=function(){$(".project-change-role").on("change",function(){$.ajax({cache:false,url:$(this).data("url"),contentType:"application/json",type:"POST",processData:false,data:JSON.stringify({id:$(this).data("id"),role:$(this).val()})})})};function s(){}s.prototype.execute=function(){var z=$("#chart").data("metrics");var y=[];for(var x=0;x<z.length;x++){y.push([z[x].column_title,z[x].nb_tasks])}c3.generate({data:{columns:y,type:"donut"}})};function p(){}p.prototype.execute=function(){var z=$("#chart").data("metrics");var y=[];for(var x=0;x<z.length;x++){y.push([z[x].user,z[x].nb_tasks])}c3.generate({data:{columns:y,type:"donut"}})};function d(){}d.prototype.execute=function(){var D=$("#chart").data("metrics");var C=[];var x=[];var y=[];var A=d3.time.format("%Y-%m-%d");var E=d3.time.format($("#chart").data("date-format"));for(var B=0;B<D.length;B++){for(var z=0;z<D[B].length;z++){if(B==0){C.push([D[B][z]]);if(z>0){x.push(D[B][z])}}else{C[z].push(D[B][z]);if(z==0){y.push(E(A.parse(D[B][z])))}}}}c3.generate({data:{columns:C,type:"area-spline",groups:[x]},axis:{x:{type:"category",categories:y}}})};function o(){}o.prototype.execute=function(){var C=$("#chart").data("metrics");var B=[[$("#chart").data("label-total")]];var x=[];var z=d3.time.format("%Y-%m-%d");var D=d3.time.format($("#chart").data("date-format"));for(var A=0;A<C.length;A++){for(var y=0;y<C[A].length;y++){if(A==0){B.push([C[A][y]])}else{B[y+1].push(C[A][y]);if(y>0){if(B[0][A]==undefined){B[0].push(0)}B[0][A]+=C[A][y]}if(y==0){x.push(D(z.parse(C[A][y])))}}}}c3.generate({data:{columns:B},axis:{x:{type:"category",categories:x}}})};function h(x){this.app=x}h.prototype.execute=function(){var z=$("#chart").data("metrics");var A=[$("#chart").data("label")];var x=[];for(var y in z){A.push(z[y].average);x.push(z[y].title)}c3.generate({data:{columns:[A],type:"bar"},bar:{width:{ratio:0.5}},axis:{x:{type:"category",categories:x},y:{tick:{format:this.app.formatDuration}}},legend:{show:false}})};function w(x){this.app=x}w.prototype.execute=function(){var z=$("#chart").data("metrics");var A=[$("#chart").data("label")];var x=[];for(var y=0;y<z.length;y++){A.push(z[y].time_spent);x.push(z[y].title)}c3.generate({data:{columns:[A],type:"bar"},bar:{width:{ratio:0.5}},axis:{x:{type:"category",categories:x},y:{tick:{format:this.app.formatDuration}}},legend:{show:false}})};function u(x){this.app=x}u.prototype.execute=function(){var D=$("#chart").data("metrics");var C=[$("#chart").data("label-cycle")];var z=[$("#chart").data("label-lead")];var y=[];var B={};B[$("#chart").data("label-cycle")]="area";B[$("#chart").data("label-lead")]="area-spline";var x={};x[$("#chart").data("label-lead")]="#afb42b";x[$("#chart").data("label-cycle")]="#4e342e";for(var A=0;A<D.length;A++){C.push(parseInt(D[A].avg_cycle_time));z.push(parseInt(D[A].avg_lead_time));y.push(D[A].day)}c3.generate({data:{columns:[z,C],types:B,colors:x},axis:{x:{type:"category",categories:y},y:{tick:{format:this.app.formatDuration}}}})};function i(x){this.app=x}i.prototype.execute=function(){var C=$("#chart").data("metrics");var y=$("#chart").data("label-open");var x=$("#chart").data("label-closed");var D=[$("#chart").data("label-spent")];var B=[$("#chart").data("label-estimated")];var A=[];for(var z in C){D.push(parseFloat(C[z].time_spent));B.push(parseFloat(C[z].time_estimated));A.push(z=="open"?y:x)}c3.generate({data:{columns:[D,B],type:"bar"},bar:{width:{ratio:0.2}},axis:{x:{type:"category",categories:A}},legend:{show:true}})};function v(){this.routes={}}v.prototype.addRoute=function(y,x){this.routes[y]=x};v.prototype.dispatch=function(y){for(var z in this.routes){if(document.getElementById(z)){var x=Object.create(this.routes[z].prototype);this.routes[z].apply(x,[y]);x.execute();break}}};jQuery(document).ready(function(){var y=new m();var x=new v();x.addRoute("board",k);x.addRoute("calendar",j);x.addRoute("screenshot-zone",e);x.addRoute("analytic-task-repartition",s);x.addRoute("analytic-user-repartition",p);x.addRoute("analytic-cfd",d);x.addRoute("analytic-burndown",o);x.addRoute("analytic-avg-time-column",h);x.addRoute("analytic-task-time-column",w);x.addRoute("analytic-lead-cycle-time",u);x.addRoute("analytic-compare-hours",i);x.addRoute("gantt-chart",c);x.dispatch(y);y.listen()})})();
\ No newline at end of file +!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){function c(a){return a>1&&5>a&&1!==~~(a/10)}function d(a,b,d,e){var f=a+" ";switch(d){case"s":return b||e?"pár sekund":"pár sekundami";case"m":return b?"minuta":e?"minutu":"minutou";case"mm":return b||e?f+(c(a)?"minuty":"minut"):f+"minutami";case"h":return b?"hodina":e?"hodinu":"hodinou";case"hh":return b||e?f+(c(a)?"hodiny":"hodin"):f+"hodinami";case"d":return b||e?"den":"dnem";case"dd":return b||e?f+(c(a)?"dny":"dní"):f+"dny";case"M":return b||e?"měsíc":"měsícem";case"MM":return b||e?f+(c(a)?"měsíce":"měsíců"):f+"měsíci";case"y":return b||e?"rok":"rokem";case"yy":return b||e?f+(c(a)?"roky":"let"):f+"lety"}}var e="leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec".split("_"),f="led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro".split("_");(b.defineLocale||b.lang).call(b,"cs",{months:e,monthsShort:f,monthsParse:function(a,b){var c,d=[];for(c=0;12>c;c++)d[c]=new RegExp("^"+a[c]+"$|^"+b[c]+"$","i");return d}(e,f),weekdays:"neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota".split("_"),weekdaysShort:"ne_po_út_st_čt_pá_so".split("_"),weekdaysMin:"ne_po_út_st_čt_pá_so".split("_"),longDateFormat:{LT:"H: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:"[dnes v] LT",nextDay:"[zítra v] LT",nextWeek:function(){switch(this.day()){case 0:return"[v neděli v] LT";case 1:case 2:return"[v] dddd [v] LT";case 3:return"[ve středu v] LT";case 4:return"[ve čtvrtek v] LT";case 5:return"[v pátek v] LT";case 6:return"[v sobotu v] LT"}},lastDay:"[včera v] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulou neděli v] LT";case 1:case 2:return"[minulé] dddd [v] LT";case 3:return"[minulou středu v] LT";case 4:case 5:return"[minulý] dddd [v] LT";case 6:return"[minulou sobotu v] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"před %s",s:d,m:d,mm:d,h:d,hh:d,d:d,dd:d,M:d,MM:d,y:d,yy:d},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("cs","cs",{closeText:"Zavřít",prevText:"<Dříve",nextText:"Později>",currentText:"Nyní",monthNames:["leden","únor","březen","duben","květen","červen","červenec","srpen","září","říjen","listopad","prosinec"],monthNamesShort:["led","úno","bře","dub","kvě","čer","čvc","srp","zář","říj","lis","pro"],dayNames:["neděle","pondělí","úterý","středa","čtvrtek","pátek","sobota"],dayNamesShort:["ne","po","út","st","čt","pá","so"],dayNamesMin:["ne","po","út","st","čt","pá","so"],weekHeader:"Týd",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("cs",{buttonText:{month:"Měsíc",week:"Týden",day:"Den",list:"Agenda"},allDayText:"Celý den",eventLimitText:function(a){return"+další: "+a}})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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}}),a.fullCalendar.datepickerLang("da","da",{closeText:"Luk",prevText:"<Forrige",nextText:"Næste>",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:""}),a.fullCalendar.lang("da",{buttonText:{month:"Måned",week:"Uge",day:"Dag",list:"Agenda"},allDayText:"Hele dagen",eventLimitText:"flere"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){function c(a,b,c,d){var e={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[a+" Tage",a+" Tagen"],M:["ein Monat","einem Monat"],MM:[a+" Monate",a+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[a+" Jahre",a+" Jahren"]};return b?e[c][0]:e[c][1]}(b.defineLocale||b.lang).call(b,"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:c,mm:"%d Minuten",h:c,hh:"%d Stunden",d:c,dd:c,M:c,MM:c,y:c,yy:c},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("de","de",{closeText:"Schließen",prevText:"<Zurück",nextText:"Vor>",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:""}),a.fullCalendar.lang("de",{buttonText:{month:"Monat",week:"Woche",day:"Tag",list:"Terminübersicht"},allDayText:"Ganztägig",eventLimitText:function(a){return"+ weitere "+a}})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){var c="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),d="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_");(b.defineLocale||b.lang).call(b,"es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(a,b){return/-MMM-/.test(b)?d[a.month()]:c[a.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}}),a.fullCalendar.datepickerLang("es","es",{closeText:"Cerrar",prevText:"<Ant",nextText:"Sig>",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:""}),a.fullCalendar.lang("es",{buttonText:{month:"Mes",week:"Semana",day:"Día",list:"Agenda"},allDayHtml:"Todo<br/>el día",eventLimitText:"más"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"el",{monthsNominativeEl:"Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος".split("_"),monthsGenitiveEl:"Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου".split("_"),months:function(a,b){return/D/.test(b.substring(0,b.indexOf("MMMM")))?this._monthsGenitiveEl[a.month()]:this._monthsNominativeEl[a.month()]},monthsShort:"Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ".split("_"),weekdays:"Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο".split("_"),weekdaysShort:"Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ".split("_"),weekdaysMin:"Κυ_Δε_Τρ_Τε_Πε_Πα_Σα".split("_"),meridiem:function(a,b,c){return a>11?c?"μμ":"ΜΜ":c?"πμ":"ΠΜ"},isPM:function(a){return"μ"===(a+"").toLowerCase()[0]},meridiemParse:/[ΠΜ]\.?Μ?\.?/i,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendarEl:{sameDay:"[Σήμερα {}] LT",nextDay:"[Αύριο {}] LT",nextWeek:"dddd [{}] LT",lastDay:"[Χθες {}] LT",lastWeek:function(){switch(this.day()){case 6:return"[το προηγούμενο] dddd [{}] LT";default:return"[την προηγούμενη] dddd [{}] LT"}},sameElse:"L"},calendar:function(a,b){var c=this._calendarEl[a],d=b&&b.hours();return"function"==typeof c&&(c=c.apply(b)),c.replace("{}",d%12===1?"στη":"στις")},relativeTime:{future:"σε %s",past:"%s πριν",s:"λίγα δευτερόλεπτα",m:"ένα λεπτό",mm:"%d λεπτά",h:"μία ώρα",hh:"%d ώρες",d:"μία μέρα",dd:"%d μέρες",M:"ένας μήνας",MM:"%d μήνες",y:"ένας χρόνος",yy:"%d χρόνια"},ordinalParse:/\d{1,2}η/,ordinal:"%dη",week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("el","el",{closeText:"Κλείσιμο",prevText:"Προηγούμενος",nextText:"Επόμενος",currentText:"Σήμερα",monthNames:["Ιανουάριος","Φεβρουάριος","Μάρτιος","Απρίλιος","Μάιος","Ιούνιος","Ιούλιος","Αύγουστος","Σεπτέμβριος","Οκτώβριος","Νοέμβριος","Δεκέμβριος"],monthNamesShort:["Ιαν","Φεβ","Μαρ","Απρ","Μαι","Ιουν","Ιουλ","Αυγ","Σεπ","Οκτ","Νοε","Δεκ"],dayNames:["Κυριακή","Δευτέρα","Τρίτη","Τετάρτη","Πέμπτη","Παρασκευή","Σάββατο"],dayNamesShort:["Κυρ","Δευ","Τρι","Τετ","Πεμ","Παρ","Σαβ"],dayNamesMin:["Κυ","Δε","Τρ","Τε","Πε","Πα","Σα"],weekHeader:"Εβδ",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("el",{buttonText:{month:"Μήνας",week:"Εβδομάδα",day:"Ημέρα",list:"Ατζέντα"},allDayText:"Ολοήμερο",eventLimitText:"περισσότερα"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){function c(a,b,c,e){var f="";switch(c){case"s":return e?"muutaman sekunnin":"muutama sekunti";case"m":return e?"minuutin":"minuutti";case"mm":f=e?"minuutin":"minuuttia";break;case"h":return e?"tunnin":"tunti";case"hh":f=e?"tunnin":"tuntia";break;case"d":return e?"päivän":"päivä";case"dd":f=e?"päivän":"päivää";break;case"M":return e?"kuukauden":"kuukausi";case"MM":f=e?"kuukauden":"kuukautta";break;case"y":return e?"vuoden":"vuosi";case"yy":f=e?"vuoden":"vuotta"}return f=d(a,e)+" "+f}function d(a,b){return 10>a?b?f[a]:e[a]:a}var e="nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän".split(" "),f=["nolla","yhden","kahden","kolmen","neljän","viiden","kuuden",e[7],e[8],e[9]];(b.defineLocale||b.lang).call(b,"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:c,m:c,mm:c,h:c,hh:c,d:c,dd:c,M:c,MM:c,y:c,yy:c},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("fi","fi",{closeText:"Sulje",prevText:"«Edellinen",nextText:"Seuraava»",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:""}),a.fullCalendar.lang("fi",{buttonText:{month:"Kuukausi",week:"Viikko",day:"Päivä",list:"Tapahtumat"},allDayText:"Koko päivä",eventLimitText:"lisää"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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(a){return a+(1===a?"er":"")},week:{dow:1,doy:4}}),a.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:""}),a.fullCalendar.lang("fr",{buttonText:{month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},allDayHtml:"Toute la<br/>journée",eventLimitText:"en plus"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){function c(a,b,c,d){var e=a;switch(c){case"s":return d||b?"néhány másodperc":"néhány másodperce";case"m":return"egy"+(d||b?" perc":" perce");case"mm":return e+(d||b?" perc":" perce");case"h":return"egy"+(d||b?" óra":" órája");case"hh":return e+(d||b?" óra":" órája");case"d":return"egy"+(d||b?" nap":" napja");case"dd":return e+(d||b?" nap":" napja");case"M":return"egy"+(d||b?" hónap":" hónapja");case"MM":return e+(d||b?" hónap":" hónapja");case"y":return"egy"+(d||b?" év":" éve");case"yy":return e+(d||b?" év":" éve")}return""}function d(a){return(a?"":"[múlt] ")+"["+e[this.day()]+"] LT[-kor]"}var e="vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton".split(" ");(b.defineLocale||b.lang).call(b,"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(a){return"u"===a.charAt(1).toLowerCase()},meridiem:function(a,b,c){return 12>a?c===!0?"de":"DE":c===!0?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return d.call(this,!0)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return d.call(this,!1)},sameElse:"L"},relativeTime:{future:"%s múlva",past:"%s",s:c,m:c,mm:c,h:c,hh:c,d:c,dd:c,M:c,MM:c,y:c,yy:c},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),a.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:""}),a.fullCalendar.lang("hu",{buttonText:{month:"Hónap",week:"Hét",day:"Nap",list:"Napló"},allDayText:"Egész nap",eventLimitText:"további"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"id",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nov_Des".split("_"),weekdays:"Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu".split("_"),weekdaysShort:"Min_Sen_Sel_Rab_Kam_Jum_Sab".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"LT.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] LT",LLLL:"dddd, D MMMM YYYY [pukul] LT"},meridiemParse:/pagi|siang|sore|malam/,meridiemHour:function(a,b){return 12===a&&(a=0),"pagi"===b?a:"siang"===b?a>=11?a:a+12:"sore"===b||"malam"===b?a+12:void 0},meridiem:function(a,b,c){return 11>a?"pagi":15>a?"siang":19>a?"sore":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Besok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kemarin pukul] LT",lastWeek:"dddd [lalu pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lalu",s:"beberapa detik",m:"semenit",mm:"%d menit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}}),a.fullCalendar.datepickerLang("id","id",{closeText:"Tutup",prevText:"<mundur",nextText:"maju>",currentText:"hari ini",monthNames:["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","Nopember","Desember"],monthNamesShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Agus","Sep","Okt","Nop","Des"],dayNames:["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"],dayNamesShort:["Min","Sen","Sel","Rab","kam","Jum","Sab"],dayNamesMin:["Mg","Sn","Sl","Rb","Km","jm","Sb"],weekHeader:"Mg",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("id",{buttonText:{month:"Bulan",week:"Minggu",day:"Hari",list:"Agenda"},allDayHtml:"Sehari<br/>penuh",eventLimitText:"lebih"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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(a){return(/^[0-9].+$/.test(a)?"tra":"in")+" "+a},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}}),a.fullCalendar.datepickerLang("it","it",{closeText:"Chiudi",prevText:"<Prec",nextText:"Succ>",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:""}),a.fullCalendar.lang("it",{buttonText:{month:"Mese",week:"Settimana",day:"Giorno",list:"Agenda"},allDayHtml:"Tutto il<br/>giorno",eventLimitText:function(a){return"+altri "+a}})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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(a){return"午後"===a},meridiem:function(a,b,c){return 12>a?"午前":"午後"},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年"}}),a.fullCalendar.datepickerLang("ja","ja",{closeText:"閉じる",prevText:"<前",nextText:"次>",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:"年"}),a.fullCalendar.lang("ja",{buttonText:{month:"月",week:"週",day:"日",list:"予定リスト"},allDayText:"終日",eventLimitText:function(a){return"他 "+a+" 件"}})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){var c="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),d="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_");(b.defineLocale||b.lang).call(b,"nl",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(a,b){return/-MMM-/.test(b)?d[a.month()]:c[a.month()]},weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"Zo_Ma_Di_Wo_Do_Vr_Za".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:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",m:"één minuut",mm:"%d minuten",h:"één uur",hh:"%d uur",d:"één dag",dd:"%d dagen",M:"één maand",MM:"%d maanden",y:"één jaar",yy:"%d jaar"},ordinalParse:/\d{1,2}(ste|de)/,ordinal:function(a){return a+(1===a||8===a||a>=20?"ste":"de")},week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("nl","nl",{closeText:"Sluiten",prevText:"←",nextText:"→",currentText:"Vandaag",monthNames:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthNamesShort:["jan","feb","mrt","apr","mei","jun","jul","aug","sep","okt","nov","dec"],dayNames:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],dayNamesShort:["zon","maa","din","woe","don","vri","zat"],dayNamesMin:["zo","ma","di","wo","do","vr","za"],weekHeader:"Wk",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("nl",{buttonText:{month:"Maand",week:"Week",day:"Dag",list:"Agenda"},allDayText:"Hele dag",eventLimitText:"extra"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"nb",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"søn_man_tirs_ons_tors_fre_lør".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),longDateFormat:{LT:"H.mm",LTS:"LT.ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] LT",LLLL:"dddd D. MMMM YYYY [kl.] LT"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[i går kl.] LT",lastWeek:"[forrige] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"for %s siden",s:"noen sekunder",m:"ett minutt",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dager",M:"en måned",MM:"%d måneder",y:"ett år",yy:"%d år"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("nb","nb",{closeText:"Lukk",prevText:"«Forrige",nextText:"Neste»",currentText:"I dag",monthNames:["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],monthNamesShort:["jan","feb","mar","apr","mai","jun","jul","aug","sep","okt","nov","des"],dayNamesShort:["søn","man","tir","ons","tor","fre","lør"],dayNames:["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],dayNamesMin:["sø","ma","ti","on","to","fr","lø"],weekHeader:"Uke",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("nb",{buttonText:{month:"Måned",week:"Uke",day:"Dag",list:"Agenda"},allDayText:"Hele dagen",eventLimitText:"til"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){function c(a){return 5>a%10&&a%10>1&&~~(a/10)%10!==1}function d(a,b,d){var e=a+" ";switch(d){case"m":return b?"minuta":"minutę";case"mm":return e+(c(a)?"minuty":"minut");case"h":return b?"godzina":"godzinę";case"hh":return e+(c(a)?"godziny":"godzin");case"MM":return e+(c(a)?"miesiące":"miesięcy");case"yy":return e+(c(a)?"lata":"lat")}}var e="styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"),f="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_");(b.defineLocale||b.lang).call(b,"pl",{months:function(a,b){return/D MMMM/.test(b)?f[a.month()]:e[a.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:d,mm:d,h:d,hh:d,d:"1 dzień",dd:"%d dni",M:"miesiąc",MM:d,y:"rok",yy:d},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("pl","pl",{closeText:"Zamknij",prevText:"<Poprzedni",nextText:"Następny>",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:""}),a.fullCalendar.lang("pl",{buttonText:{month:"Miesiąc",week:"Tydzień",day:"Dzień",list:"Plan dnia"},allDayText:"Cały dzień",eventLimitText:"więcej"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"pt",{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 LT",LLLL:"dddd, D [de] MMMM [de] YYYY 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:"há %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º",week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("pt","pt",{closeText:"Fechar",prevText:"Anterior",nextText:"Seguinte",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:"Sem",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("pt",{buttonText:{month:"Mês",week:"Semana",day:"Dia",list:"Agenda"},allDayText:"Todo o dia",eventLimitText:"mais"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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º"}),a.fullCalendar.datepickerLang("pt-br","pt-BR",{closeText:"Fechar",prevText:"<Anterior",nextText:"Próximo>",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:""}),a.fullCalendar.lang("pt-br",{buttonText:{month:"Mês",week:"Semana",day:"Dia",list:"Compromissos"},allDayText:"dia inteiro",eventLimitText:function(a){return"mais +"+a}})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){function c(a,b){var c=a.split("_");return b%10===1&&b%100!==11?c[0]:b%10>=2&&4>=b%10&&(10>b%100||b%100>=20)?c[1]:c[2]}function d(a,b,d){var e={mm:b?"минута_минуты_минут":"минуту_минуты_минут",hh:"час_часа_часов",dd:"день_дня_дней",MM:"месяц_месяца_месяцев",yy:"год_года_лет"};return"m"===d?b?"минута":"минуту":a+" "+c(e[d],+a)}function e(a,b){var c={nominative:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"),accusative:"января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря".split("_")},d=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(b)?"accusative":"nominative";return c[d][a.month()]}function f(a,b){var c={nominative:"янв_фев_март_апр_май_июнь_июль_авг_сен_окт_ноя_дек".split("_"),accusative:"янв_фев_мар_апр_мая_июня_июля_авг_сен_окт_ноя_дек".split("_")},d=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(b)?"accusative":"nominative";return c[d][a.month()]}function g(a,b){var c={nominative:"воскресенье_понедельник_вторник_среда_четверг_пятница_суббота".split("_"),accusative:"воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу".split("_")},d=/\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?\] ?dddd/.test(b)?"accusative":"nominative";return c[d][a.day()]}(b.defineLocale||b.lang).call(b,"ru",{months:e,monthsShort:f,weekdays:g,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(a){if(a.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:d,mm:d,h:"час",hh:d,d:"день",dd:d,M:"месяц",MM:d,y:"год",yy:d},meridiemParse:/ночи|утра|дня|вечера/i,isPM:function(a){return/^(дня|вечера)$/.test(a)},meridiem:function(a,b,c){return 4>a?"ночи":12>a?"утра":17>a?"дня":"вечера"},ordinalParse:/\d{1,2}-(й|го|я)/,ordinal:function(a,b){switch(b){case"M":case"d":case"DDD":return a+"-й";case"D":return a+"-го";case"w":case"W":return a+"-я";default:return a}},week:{dow:1,doy:7}}),a.fullCalendar.datepickerLang("ru","ru",{closeText:"Закрыть",prevText:"<Пред",nextText:"След>",currentText:"Сегодня",monthNames:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthNamesShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],dayNames:["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"],dayNamesShort:["вск","пнд","втр","срд","чтв","птн","сбт"],dayNamesMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],weekHeader:"Нед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("ru",{buttonText:{month:"Месяц",week:"Неделя",day:"День",list:"Повестка дня"},allDayText:"Весь день",eventLimitText:function(a){return"+ ещё "+a}})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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(a){var b=a%10,c=1===~~(a%100/10)?"e":1===b?"a":2===b?"a":"e";return a+c},week:{dow:1,doy:4}}),a.fullCalendar.datepickerLang("sv","sv",{closeText:"Stäng",prevText:"«Förra",nextText:"Nästa»",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:""}),a.fullCalendar.lang("sv",{buttonText:{month:"Månad",week:"Vecka",day:"Dag",list:"Program"},allDayText:"Heldag",eventLimitText:"till"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){var c={words:{m:["jedan minut","jedne minute"],mm:["minut","minute","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mesec","meseca","meseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(a,b){return 1===a?b[0]:a>=2&&4>=a?b[1]:b[2]},translate:function(a,b,d){var e=c.words[d];return 1===d.length?b?e[0]:e[1]:a+" "+c.correctGrammaticalCase(a,e)}};(b.defineLocale||b.lang).call(b,"sr",{months:["januar","februar","mart","april","maj","jun","jul","avgust","septembar","oktobar","novembar","decembar"],monthsShort:["jan.","feb.","mar.","apr.","maj","jun","jul","avg.","sep.","okt.","nov.","dec."],weekdays:["nedelja","ponedeljak","utorak","sreda","četvrtak","petak","subota"],weekdaysShort:["ned.","pon.","uto.","sre.","čet.","pet.","sub."],weekdaysMin:["ne","po","ut","sr","če","pe","su"],longDateFormat:{LT:"H: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:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedelju] [u] LT";case 3:return"[u] [sredu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){var a=["[prošle] [nedelje] [u] LT","[prošlog] [ponedeljka] [u] LT","[prošlog] [utorka] [u] LT","[prošle] [srede] [u] LT","[prošlog] [četvrtka] [u] LT","[prošlog] [petka] [u] LT","[prošle] [subote] [u] LT"];return a[this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"pre %s",s:"nekoliko sekundi",m:c.translate,mm:c.translate,h:c.translate,hh:c.translate,d:"dan",dd:c.translate,M:"mesec",MM:c.translate,y:"godinu",yy:c.translate},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),a.fullCalendar.datepickerLang("sr","sr",{closeText:"Затвори",prevText:"<",nextText:">",currentText:"Данас",monthNames:["Јануар","Фебруар","Март","Април","Мај","Јун","Јул","Август","Септембар","Октобар","Новембар","Децембар"],monthNamesShort:["Јан","Феб","Мар","Апр","Мај","Јун","Јул","Авг","Сеп","Окт","Нов","Дец"],dayNames:["Недеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"],dayNamesShort:["Нед","Пон","Уто","Сре","Чет","Пет","Суб"],dayNamesMin:["Не","По","Ут","Ср","Че","Пе","Су"],weekHeader:"Сед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("sr",{buttonText:{month:"Месец",week:"Недеља",day:"Дан",list:"Планер"},allDayText:"Цео дан",eventLimitText:function(a){return"+ још "+a}})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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(a){return"หลังเที่ยง"===a},meridiem:function(a,b,c){return 12>a?"ก่อนเที่ยง":"หลังเที่ยง"},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 ปี"}}),a.fullCalendar.datepickerLang("th","th",{closeText:"ปิด",prevText:"« ย้อน",nextText:"ถัดไป »",currentText:"วันนี้",monthNames:["มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม"],monthNamesShort:["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."],dayNames:["อาทิตย์","จันทร์","อังคาร","พุธ","พฤหัสบดี","ศุกร์","เสาร์"],dayNamesShort:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],dayNamesMin:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),a.fullCalendar.lang("th",{buttonText:{month:"เดือน",week:"สัปดาห์",day:"วัน",list:"แผนงาน"},allDayText:"ตลอดวัน",eventLimitText:"เพิ่มเติม"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){var c={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ı"};(b.defineLocale||b.lang).call(b,"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(a){if(0===a)return a+"'ıncı";var b=a%10,d=a%100-b,e=a>=100?100:null;return a+(c[b]||c[d]||c[e])},week:{dow:1,doy:7}}),a.fullCalendar.datepickerLang("tr","tr",{closeText:"kapat",prevText:"<geri",nextText:"ileri>",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:""}),a.fullCalendar.lang("tr",{buttonText:{next:"ileri",month:"Ay",week:"Hafta",day:"Gün",list:"Ajanda"},allDayText:"Tüm gün",eventLimitText:"daha fazla"})});!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):a(jQuery,moment)}(function(a,b){(b.defineLocale||b.lang).call(b,"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(a,b){return 12===a&&(a=0),"凌晨"===b||"早上"===b||"上午"===b?a:"下午"===b||"晚上"===b?a+12:a>=11?a:a+12},meridiem:function(a,b,c){var d=100*a+b;return 600>d?"凌晨":900>d?"早上":1130>d?"上午":1230>d?"中午":1800>d?"下午":"晚上"},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 a,c;return a=b().startOf("week"),c=this.unix()-a.unix()>=604800?"[下]":"[本]",0===this.minutes()?c+"dddAh点整":c+"dddAh点mm"},lastWeek:function(){var a,c;return a=b().startOf("week"),c=this.unix()<a.unix()?"[上]":"[本]",0===this.minutes()?c+"dddAh点整":c+"dddAh点mm"},sameElse:"LL"},ordinalParse:/\d{1,2}(日|月|周)/,ordinal:function(a,b){switch(b){case"d":case"D":case"DDD":return a+"日";case"M":return a+"月";case"w":case"W":return a+"周";default:return a}},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}}),a.fullCalendar.datepickerLang("zh-cn","zh-CN",{closeText:"关闭",prevText:"<上月",nextText:"下月>",currentText:"今天",monthNames:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthNamesShort:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周六"],dayNamesMin:["日","一","二","三","四","五","六"],weekHeader:"周",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"年"}),a.fullCalendar.lang("zh-cn",{buttonText:{month:"月",week:"周",day:"日",list:"日程"},allDayText:"全天",eventLimitText:function(a){return"另外 "+a+" 个"}})});(function(){function s(w){this.app=w;this.router=new u();this.router.addRoute("screenshot-zone",d)}s.prototype.isOpen=function(){return $("#popover-container").size()>0};s.prototype.open=function(x){var w=this;w.app.dropdown.close();$.get(x,function(y){$("body").prepend('<div id="popover-container"><div id="popover-content">'+y+"</div></div>");w.app.refresh();w.router.dispatch(this.app);w.afterOpen()})};s.prototype.close=function(w){if(this.isOpen()){if(w){w.preventDefault()}$("#popover-container").remove()}};s.prototype.onClick=function(x){x.preventDefault();x.stopPropagation();var w=x.target.getAttribute("href");if(!w){w=x.target.getAttribute("data-href")}if(w){this.open(w)}};s.prototype.listen=function(){$(document).on("click",".popover",this.onClick.bind(this));$(document).on("click",".close-popover",this.close.bind(this));$(document).on("click","#popover-container",this.close.bind(this));$(document).on("click","#popover-content",function(w){w.stopPropagation()})};s.prototype.afterOpen=function(){var x=this;var w=$("#popover-content .popover-form");if(w){w.on("submit",function(y){y.preventDefault();$.ajax({type:"POST",url:w.attr("action"),data:w.serialize(),success:function(A,B,z){x.afterSubmit(A,z,x)}})})}$(document).on("click",".popover-link",function(y){y.preventDefault();$.ajax({type:"GET",url:$(this).attr("href"),success:function(A,B,z){x.afterSubmit(A,z,x)}})})};s.prototype.afterSubmit=function(y,x,w){var z=x.getResponseHeader("X-Ajax-Redirect");if(z){window.location=z==="self"?window.location.href.split("#")[0]:z}else{$("#popover-content").html(y);$("#popover-content input[autofocus]").focus();w.afterOpen()}};function q(){}q.prototype.listen=function(){var w=this;$(document).on("click",function(){w.close()});$(document).on("click",".dropdown-menu",function(A){A.preventDefault();A.stopImmediatePropagation();w.close();var y=$(this).next("ul");var B=$(this).offset();$("body").append(jQuery("<div>",{id:"dropdown"}));y.clone().appendTo("#dropdown");var C=$("#dropdown ul");C.addClass("dropdown-submenu-open");var z=C.outerHeight();var x=C.outerWidth();if(B.top+z-$(window).scrollTop()>$(window).height()){C.css("top",B.top-z-5)}else{C.css("top",B.top+$(this).height())}if(B.left+x>$(window).width()){C.css("left",B.left-x+$(this).outerWidth())}else{C.css("left",B.left)}});$(document).on("click",".dropdown-submenu-open li",function(x){if($(x.target).is("li")){$(this).find("a:visible")[0].click()}});$("textarea[data-mention-search-url]").textcomplete([{match:/(^|\s)@(\w*)$/,search:function(y,z){var x=$("textarea[data-mention-search-url]").data("mention-search-url");$.getJSON(x,{q:y}).done(function(A){z(A)}).fail(function(){z([])})},replace:function(x){return"$1@"+x+" "},cache:true}],{className:"textarea-dropdown"})};q.prototype.close=function(){$("#dropdown").remove()};function p(w){this.app=w}p.prototype.listen=function(){var w=this;$(".tooltip").tooltip({track:false,show:false,hide:false,position:{my:"left-20 top",at:"center bottom+9",using:function(x,y){$(this).css(x);var z=y.target.left+y.target.width/2-y.element.left-20;$("<div>").addClass("tooltip-arrow").addClass(y.vertical).addClass(z<1?"align-left":"align-right").appendTo(this)}},content:function(){var z=this;var x=$(this).attr("data-href");if(!x){return'<div class="markdown">'+$(this).attr("title")+"</div>"}$.get(x,function y(C){var B=$(".ui-tooltip:visible");$(".ui-tooltip-content:visible").html(C);B.css({top:"",left:""});B.children(".tooltip-arrow").remove();var A=$(z).tooltip("option","position");A.of=$(z);B.position(A)});return'<i class="fa fa-spinner fa-spin"></i>'}}).on("mouseenter",function(){var x=this;$(this).tooltip("open");$(".ui-tooltip").on("mouseleave",function(){$(x).tooltip("close")})}).on("mouseleave focusout",function(x){x.stopImmediatePropagation();var y=this;setTimeout(function(){if(!$(".ui-tooltip:hover").length){$(y).tooltip("close")}},100)})};function k(){}k.prototype.showPreview=function(A){A.preventDefault();var x=$(".write-area");var z=$(".preview-area");var w=$("textarea");$("#markdown-write").parent().removeClass("form-tab-selected");$("#markdown-preview").parent().addClass("form-tab-selected");var y=$.ajax({url:$("body").data("markdown-preview-url"),contentType:"application/json",type:"POST",processData:false,dataType:"html",data:JSON.stringify({text:w.val()})});y.done(function(B){z.find(".markdown").html(B);z.css("height",w.css("height"));z.css("width",w.css("width"));x.hide();z.show()})};k.prototype.showWriter=function(w){w.preventDefault();$("#markdown-write").parent().addClass("form-tab-selected");$("#markdown-preview").parent().removeClass("form-tab-selected");$(".write-area").show();$(".preview-area").hide()};k.prototype.listen=function(){$(document).on("click","#markdown-preview",this.showPreview.bind(this));$(document).on("click","#markdown-write",this.showWriter.bind(this))};function e(w){this.app=w;this.keyboardShortcuts()}e.prototype.focus=function(){$(document).on("focus","#form-search",function(){if($("#form-search")[0].setSelectionRange){$("#form-search")[0].setSelectionRange($("#form-search").val().length,$("#form-search").val().length)}})};e.prototype.listen=function(){var w=this;$(document).on("click",".filter-helper",function(z){z.preventDefault();var y=$(this).data("filter");var x=$(this).data("append-filter");if(x){y=$("#form-search").val()+" "+x}$("#form-search").val(y);if($("#board").length){w.app.board.reloadFilters(y)}else{$("form.search").submit()}})};e.prototype.keyboardShortcuts=function(){var w=this;Mousetrap.bind("v b",function(y){var x=$(".view-board");if(x.length){window.location=x.attr("href")}});Mousetrap.bind("v c",function(y){var x=$(".view-calendar");if(x.length){window.location=x.attr("href")}});Mousetrap.bind("v l",function(y){var x=$(".view-listing");if(x.length){window.location=x.attr("href")}});Mousetrap.bind("v g",function(y){var x=$(".view-gantt");if(x.length){window.location=x.attr("href")}});Mousetrap.bind("f",function(y){y.preventDefault();var x=document.getElementById("form-search");if(x){x.focus()}});Mousetrap.bind("r",function(y){y.preventDefault();var x=$(".filter-reset").data("filter");$("#form-search").val(x);if($("#board").length){w.app.board.reloadFilters(x)}else{$("form.search").submit()}})};function l(){this.board=new j(this);this.markdown=new k();this.search=new e(this);this.swimlane=new f();this.dropdown=new q();this.tooltip=new p(this);this.popover=new s(this);this.task=new a();this.project=new m();this.keyboardShortcuts();this.chosen();this.poll();$(".alert-fade-out").delay(4000).fadeOut(800,function(){$(this).remove()});var w=false;$("select.task-reload-project-destination").change(function(){if(!w){$(".loading-icon").show();w=true;window.location=$(this).data("redirect").replace(/PROJECT_ID/g,$(this).val())}})}l.prototype.listen=function(){this.project.listen();this.popover.listen();this.markdown.listen();this.tooltip.listen();this.dropdown.listen();this.search.listen();this.task.listen();this.swimlane.listen();this.search.focus();this.autoComplete();this.datePicker();this.focus();$(document).on("click",".ajax-replace",function(x){x.preventDefault();var w=$(this);$.ajax({cache:false,url:w.attr("href"),success:function(y){w.replaceWith(y)}})})};l.prototype.refresh=function(){$(document).off();this.listen()};l.prototype.focus=function(){$("[autofocus]").each(function(w,x){$(this).focus()});$(document).on("focus",".auto-select",function(){$(this).select()});$(document).on("mouseup",".auto-select",function(w){w.preventDefault()})};l.prototype.poll=function(){window.setInterval(this.checkSession,60000)};l.prototype.keyboardShortcuts=function(){var w=this;Mousetrap.bindGlobal("mod+enter",function(){$("form").submit()});Mousetrap.bind("b",function(x){x.preventDefault();$("#board-selector").trigger("chosen:open")});Mousetrap.bindGlobal("esc",function(){w.popover.close();w.dropdown.close()})};l.prototype.checkSession=function(){if(!$(".form-login").length){$.ajax({cache:false,url:$("body").data("status-url"),statusCode:{401:function(){window.location=$("body").data("login-url")}}})}};l.prototype.datePicker=function(){$.datepicker.setDefaults($.datepicker.regional[$("body").data("js-lang")]);$(".form-date").datepicker({showOtherMonths:true,selectOtherMonths:true,dateFormat:"yy-mm-dd",constrainInput:false});$(".form-datetime").datetimepicker({controlType:"select",oneLine:true,dateFormat:"yy-mm-dd",constrainInput:false})};l.prototype.autoComplete=function(){$(".autocomplete").each(function(){var x=$(this);var y=x.data("dst-field");var w=x.data("dst-extra-field");if($("#form-"+y).val()==""){x.parent().find("input[type=submit]").attr("disabled","disabled")}x.autocomplete({source:x.data("search-url"),minLength:1,select:function(z,A){$("input[name="+y+"]").val(A.item.id);if(w){$("input[name="+w+"]").val(A.item[w])}x.parent().find("input[type=submit]").removeAttr("disabled")}})})};l.prototype.chosen=function(){$(".chosen-select").each(function(){var w=$(this).data("search-threshold");if(w===undefined){w=10}$(this).chosen({width:"180px",no_results_text:$(this).data("notfound"),disable_search_threshold:w})});$(".select-auto-redirect").change(function(){var w=new RegExp($(this).data("redirect-regex"),"g");window.location=$(this).data("redirect-url").replace(w,$(this).val())})};l.prototype.showLoadingIcon=function(){$("body").append('<span id="app-loading-icon"> <i class="fa fa-spinner fa-spin"></i></span>')};l.prototype.hideLoadingIcon=function(){$("#app-loading-icon").remove()};l.prototype.isVisible=function(){var w="";if(typeof document.hidden!=="undefined"){w="visibilityState"}else{if(typeof document.mozHidden!=="undefined"){w="mozVisibilityState"}else{if(typeof document.msHidden!=="undefined"){w="msVisibilityState"}else{if(typeof document.webkitHidden!=="undefined"){w="webkitVisibilityState"}}}}if(w!=""){return document[w]=="visible"}return true};l.prototype.formatDuration=function(w){if(w>=86400){return Math.round(w/86400)+"d"}else{if(w>=3600){return Math.round(w/3600)+"h"}else{if(w>=60){return Math.round(w/60)+"m"}}}return w+"s"};function d(){this.pasteCatcher=null}d.prototype.execute=function(){this.initialize()};d.prototype.initialize=function(){this.destroy();if(!window.Clipboard){this.pasteCatcher=document.createElement("div");this.pasteCatcher.id="screenshot-pastezone";this.pasteCatcher.contentEditable="true";this.pasteCatcher.style.opacity=0;this.pasteCatcher.style.position="fixed";this.pasteCatcher.style.top=0;this.pasteCatcher.style.right=0;this.pasteCatcher.style.width=0;document.body.insertBefore(this.pasteCatcher,document.body.firstChild);this.pasteCatcher.focus();document.addEventListener("click",this.setFocus.bind(this));document.getElementById("screenshot-zone").addEventListener("click",this.setFocus.bind(this))}window.addEventListener("paste",this.pasteHandler.bind(this))};d.prototype.destroy=function(){if(this.pasteCatcher!=null){document.body.removeChild(this.pasteCatcher)}else{if(document.getElementById("screenshot-pastezone")){document.body.removeChild(document.getElementById("screenshot-pastezone"))}}document.removeEventListener("click",this.setFocus.bind(this));this.pasteCatcher=null};d.prototype.setFocus=function(){if(this.pasteCatcher!==null){this.pasteCatcher.focus()}};d.prototype.pasteHandler=function(B){if(B.clipboardData&&B.clipboardData.items){var z=B.clipboardData.items;if(z){for(var A=0;A<z.length;A++){if(z[A].type.indexOf("image")!==-1){var y=z[A].getAsFile();var w=new FileReader();var x=this;w.onload=function(C){x.createImage(C.target.result)};w.readAsDataURL(y)}}}}else{setTimeout(this.checkInput.bind(this),100)}};d.prototype.checkInput=function(){var w=this.pasteCatcher.childNodes[0];if(w){if(w.tagName==="IMG"){this.createImage(w.src)}}this.pasteCatcher.innerHTML=""};d.prototype.createImage=function(y){var x=new Image();x.src=y;x.onload=function(){var z=y.split("base64,");var A=z[1];$("input[name=screenshot]").val(A)};var w=document.getElementById("screenshot-zone");w.innerHTML="";w.className="screenshot-pasted";w.appendChild(x);this.destroy();this.initialize()};function i(){}i.prototype.execute=function(){var w=$("#calendar");w.fullCalendar({lang:$("body").data("js-lang"),editable:true,eventLimit:true,defaultView:"month",header:{left:"prev,next today",center:"title",right:"month,agendaWeek,agendaDay"},eventDrop:function(x){$.ajax({cache:false,url:w.data("save-url"),contentType:"application/json",type:"POST",processData:false,data:JSON.stringify({task_id:x.id,date_due:x.start.format()})})},viewRender:function(){var x=w.data("check-url");var z={start:w.fullCalendar("getView").start.format(),end:w.fullCalendar("getView").end.format()};for(var y in z){x+="&"+y+"="+z[y]}$.getJSON(x,function(A){w.fullCalendar("removeEvents");w.fullCalendar("addEventSource",A);w.fullCalendar("rerenderEvents")})}})};function j(w){this.app=w;this.checkInterval=null;this.savingInProgress=false}j.prototype.execute=function(){this.app.swimlane.refresh();this.restoreColumnViewMode();this.compactView();this.poll();this.keyboardShortcuts();this.listen();this.dragAndDrop();$(window).on("load",this.columnScrolling);$(window).resize(this.columnScrolling)};j.prototype.poll=function(){var w=parseInt($("#board").attr("data-check-interval"));if(w>0){this.checkInterval=window.setInterval(this.check.bind(this),w*1000)}};j.prototype.reloadFilters=function(w){this.app.showLoadingIcon();$.ajax({cache:false,url:$("#board").data("reload-url"),contentType:"application/json",type:"POST",processData:false,data:JSON.stringify({search:w}),success:this.refresh.bind(this),error:this.app.hideLoadingIcon.bind(this)})};j.prototype.check=function(){if(this.app.isVisible()&&!this.savingInProgress){var w=this;this.app.showLoadingIcon();$.ajax({cache:false,url:$("#board").data("check-url"),statusCode:{200:function(x){w.refresh(x)},304:function(){w.app.hideLoadingIcon()}}})}};j.prototype.save=function(z,A,w,y){var x=this;this.app.showLoadingIcon();this.savingInProgress=true;$.ajax({cache:false,url:$("#board").data("save-url"),contentType:"application/json",type:"POST",processData:false,data:JSON.stringify({task_id:z,column_id:A,swimlane_id:y,position:w}),success:function(B){x.refresh(B);this.savingInProgress=false},error:function(){x.app.hideLoadingIcon();this.savingInProgress=false}})};j.prototype.refresh=function(w){$("#board-container").replaceWith(w);this.app.refresh();this.app.swimlane.refresh();this.app.hideLoadingIcon();this.listen();this.dragAndDrop();this.compactView();this.restoreColumnViewMode();this.columnScrolling()};j.prototype.dragAndDrop=function(){var w=this;var x={forcePlaceholderSize:true,tolerance:"pointer",connectWith:".board-task-list",placeholder:"draggable-placeholder",items:".draggable-item",stop:function(z,G){var B=G.item;var F=B.attr("data-task-id");var H=B.attr("data-position");var E=B.attr("data-column-id");var D=B.attr("data-swimlane-id");var A=B.parent().attr("data-column-id");var y=B.parent().attr("data-swimlane-id");var C=B.index()+1;B.removeClass("draggable-item-selected");if(A!=E||y!=D||C!=H){w.changeTaskState(F);w.save(F,A,C,y)}},start:function(y,z){z.item.addClass("draggable-item-selected");z.placeholder.height(z.item.height())}};if($.support.touch){$(".task-board-sort-handle").css("display","inline");x.handle=".task-board-sort-handle"}$(".board-task-list").sortable(x)};j.prototype.changeTaskState=function(x){var w=$("div[data-task-id="+x+"]");w.addClass("task-board-saving-state");w.find(".task-board-saving-icon").show()};j.prototype.listen=function(){var w=this;$(document).on("click",".task-board",function(x){if(x.target.tagName!="A"){window.location=$(this).data("task-url")}});$(document).on("click",".filter-toggle-scrolling",function(x){x.preventDefault();w.toggleCompactView()});$(document).on("click",".filter-toggle-height",function(x){x.preventDefault();w.toggleColumnScrolling()});$(document).on("click",".board-toggle-column-view",function(){w.toggleColumnViewMode($(this).data("column-id"))})};j.prototype.toggleColumnScrolling=function(){var w=localStorage.getItem("column_scroll");if(w==undefined){w=1}localStorage.setItem("column_scroll",w==0?1:0);this.columnScrolling()};j.prototype.columnScrolling=function(){if(localStorage.getItem("column_scroll")==0){var w=80;$(".filter-max-height").show();$(".filter-min-height").hide();$(".board-rotation-wrapper").css("min-height","");$(".board-task-list").each(function(){var x=$(this).height();if(x>w){w=x}});$(".board-task-list").css("min-height",w);$(".board-task-list").css("height","")}else{$(".filter-max-height").hide();$(".filter-min-height").show();if($(".board-swimlane").length>1){$(".board-task-list").each(function(){if($(this).height()>500){$(this).css("height",500)}else{$(this).css("min-height",320);$(".board-rotation-wrapper").css("min-height",320)}})}else{var w=$(window).height()-170;$(".board-task-list").css("height",w);$(".board-rotation-wrapper").css("min-height",w)}}};j.prototype.toggleCompactView=function(){var w=localStorage.getItem("horizontal_scroll")||1;localStorage.setItem("horizontal_scroll",w==0?1:0);this.compactView()};j.prototype.compactView=function(){if(localStorage.getItem("horizontal_scroll")==0){$(".filter-wide").show();$(".filter-compact").hide();$("#board-container").addClass("board-container-compact");$("#board th:not(.board-column-header-collapsed)").addClass("board-column-compact")}else{$(".filter-wide").hide();$(".filter-compact").show();$("#board-container").removeClass("board-container-compact");$("#board th").removeClass("board-column-compact")}};j.prototype.toggleCollapsedMode=function(){var w=this;this.app.showLoadingIcon();$.ajax({cache:false,url:$('.filter-display-mode:not([style="display: none;"]) a').attr("href"),success:function(x){$(".filter-display-mode").toggle();w.refresh(x)}})};j.prototype.restoreColumnViewMode=function(){var w=this;$(".board-column-header").each(function(){var x=$(this).data("column-id");if(localStorage.getItem("hidden_column_"+x)){w.hideColumn(x)}})};j.prototype.toggleColumnViewMode=function(w){if(localStorage.getItem("hidden_column_"+w)){this.showColumn(w)}else{this.hideColumn(w)}};j.prototype.hideColumn=function(w){$(".board-column-"+w+" .board-column-expanded").hide();$(".board-column-"+w+" .board-column-collapsed").show();$(".board-column-header-"+w+" .board-column-expanded").hide();$(".board-column-header-"+w+" .board-column-collapsed").show();$(".board-column-header-"+w).each(function(){$(this).removeClass("board-column-compact");$(this).addClass("board-column-header-collapsed")});$(".board-column-"+w).each(function(){$(this).addClass("board-column-task-collapsed")});$(".board-column-"+w+" .board-rotation").each(function(){$(this).css("width",$(".board-column-"+w+"").height())});localStorage.setItem("hidden_column_"+w,1)};j.prototype.showColumn=function(w){$(".board-column-"+w+" .board-column-expanded").show();$(".board-column-"+w+" .board-column-collapsed").hide();$(".board-column-header-"+w+" .board-column-expanded").show();$(".board-column-header-"+w+" .board-column-collapsed").hide();$(".board-column-header-"+w).removeClass("board-column-header-collapsed");$(".board-column-"+w).removeClass("board-column-task-collapsed");if(localStorage.getItem("horizontal_scroll")==0){$(".board-column-header-"+w).addClass("board-column-compact")}localStorage.removeItem("hidden_column_"+w)};j.prototype.keyboardShortcuts=function(){var w=this;Mousetrap.bind("c",function(){w.toggleCompactView()});Mousetrap.bind("s",function(){w.toggleCollapsedMode()});Mousetrap.bind("n",function(){w.app.popover.open($("#board").data("task-creation-url"))})};function f(){}f.prototype.getStorageKey=function(){return"hidden_swimlanes_"+$("#board").data("project-id")};f.prototype.expand=function(x){var y=this.getAllCollapsed();var w=y.indexOf(x);if(w>-1){y.splice(w,1)}localStorage.setItem(this.getStorageKey(),JSON.stringify(y));$(".board-swimlane-columns-"+x).css("display","table-row");$(".board-swimlane-tasks-"+x).css("display","table-row");$(".hide-icon-swimlane-"+x).css("display","inline");$(".show-icon-swimlane-"+x).css("display","none")};f.prototype.collapse=function(w){var x=this.getAllCollapsed();if(x.indexOf(w)<0){x.push(w);localStorage.setItem(this.getStorageKey(),JSON.stringify(x))}$(".board-swimlane-columns-"+w+":not(:first-child)").css("display","none");$(".board-swimlane-tasks-"+w).css("display","none");$(".hide-icon-swimlane-"+w).css("display","none");$(".show-icon-swimlane-"+w).css("display","inline")};f.prototype.isCollapsed=function(w){return this.getAllCollapsed().indexOf(w)>-1};f.prototype.getAllCollapsed=function(){return JSON.parse(localStorage.getItem(this.getStorageKey()))||[]};f.prototype.refresh=function(){var x=this.getAllCollapsed();for(var w=0;w<x.length;w++){this.collapse(x[w])}};f.prototype.listen=function(){var w=this;$(document).on("click",".board-swimlane-toggle",function(y){y.preventDefault();var x=$(this).data("swimlane-id");if(w.isCollapsed(x)){w.expand(x)}else{w.collapse(x)}})};function b(w){this.app=w;this.data=[];this.options={container:"#gantt-chart",showWeekends:true,allowMoves:true,allowResizes:true,cellWidth:21,cellHeight:31,slideWidth:1000,vHeaderWidth:200}}b.prototype.saveRecord=function(w){this.app.showLoadingIcon();$.ajax({cache:false,url:$(this.options.container).data("save-url"),contentType:"application/json",type:"POST",processData:false,data:JSON.stringify(w),complete:this.app.hideLoadingIcon.bind(this)})};b.prototype.execute=function(){this.data=this.prepareData($(this.options.container).data("records"));var z=Math.floor((this.options.slideWidth/this.options.cellWidth)+5);var y=this.getDateRange(z);var w=y[0];var B=y[1];var x=$(this.options.container);var A=jQuery("<div>",{"class":"ganttview"});A.append(this.renderVerticalHeader());A.append(this.renderSlider(w,B));x.append(A);jQuery("div.ganttview-grid-row div.ganttview-grid-row-cell:last-child",x).addClass("last");jQuery("div.ganttview-hzheader-days div.ganttview-hzheader-day:last-child",x).addClass("last");jQuery("div.ganttview-hzheader-months div.ganttview-hzheader-month:last-child",x).addClass("last");if(!$(this.options.container).data("readonly")){this.listenForBlockResize(w);this.listenForBlockMove(w)}else{this.options.allowResizes=false;this.options.allowMoves=false}};b.prototype.renderVerticalHeader=function(){var A=jQuery("<div>",{"class":"ganttview-vtheader"});var x=jQuery("<div>",{"class":"ganttview-vtheader-item"});var z=jQuery("<div>",{"class":"ganttview-vtheader-series"});for(var w=0;w<this.data.length;w++){var y=jQuery("<span>").append(jQuery("<i>",{"class":"fa fa-info-circle tooltip",title:this.getVerticalHeaderTooltip(this.data[w])})).append(" ");if(this.data[w].type=="task"){y.append(jQuery("<a>",{href:this.data[w].link,target:"_blank",title:this.data[w].title}).append(this.data[w].title))}else{y.append(jQuery("<a>",{href:this.data[w].board_link,target:"_blank",title:$(this.options.container).data("label-board-link")}).append('<i class="fa fa-th"></i>')).append(" ").append(jQuery("<a>",{href:this.data[w].gantt_link,target:"_blank",title:$(this.options.container).data("label-gantt-link")}).append('<i class="fa fa-sliders"></i>')).append(" ").append(jQuery("<a>",{href:this.data[w].link,target:"_blank"}).append(this.data[w].title))}z.append(jQuery("<div>",{"class":"ganttview-vtheader-series-name"}).append(y))}x.append(z);A.append(x);return A};b.prototype.renderSlider=function(x,z){var w=jQuery("<div>",{"class":"ganttview-slide-container"});var y=this.getDates(x,z);w.append(this.renderHorizontalHeader(y));w.append(this.renderGrid(y));w.append(this.addBlockContainers());this.addBlocks(w,x);return w};b.prototype.renderHorizontalHeader=function(x){var E=jQuery("<div>",{"class":"ganttview-hzheader"});var C=jQuery("<div>",{"class":"ganttview-hzheader-months"});var B=jQuery("<div>",{"class":"ganttview-hzheader-days"});var A=0;for(var F in x){for(var z in x[F]){var G=x[F][z].length*this.options.cellWidth;A=A+G;C.append(jQuery("<div>",{"class":"ganttview-hzheader-month",css:{width:(G-1)+"px"}}).append($.datepicker.regional[$("body").data("js-lang")].monthNames[z]+" "+F));for(var D in x[F][z]){B.append(jQuery("<div>",{"class":"ganttview-hzheader-day"}).append(x[F][z][D].getDate()))}}}C.css("width",A+"px");B.css("width",A+"px");E.append(C).append(B);return E};b.prototype.renderGrid=function(x){var G=jQuery("<div>",{"class":"ganttview-grid"});var B=jQuery("<div>",{"class":"ganttview-grid-row"});for(var E in x){for(var z in x[E]){for(var D in x[E][z]){var A=jQuery("<div>",{"class":"ganttview-grid-row-cell"});if(this.options.showWeekends&&this.isWeekend(x[E][z][D])){A.addClass("ganttview-weekend")}B.append(A)}}}var F=jQuery("div.ganttview-grid-row-cell",B).length*this.options.cellWidth;B.css("width",F+"px");G.css("width",F+"px");for(var C=0;C<this.data.length;C++){G.append(B.clone())}return G};b.prototype.addBlockContainers=function(){var x=jQuery("<div>",{"class":"ganttview-blocks"});for(var w=0;w<this.data.length;w++){x.append(jQuery("<div>",{"class":"ganttview-block-container"}))}return x};b.prototype.addBlocks=function(x,w){var E=jQuery("div.ganttview-blocks div.ganttview-block-container",x);var y=0;for(var B=0;B<this.data.length;B++){var C=this.data[B];var F=this.daysBetween(C.start,C.end)+1;var A=this.daysBetween(w,C.start);var D=jQuery("<div>",{"class":"ganttview-block-text"});var z=jQuery("<div>",{"class":"ganttview-block tooltip"+(this.options.allowMoves?" ganttview-block-movable":""),title:this.getBarTooltip(C),css:{width:((F*this.options.cellWidth)-9)+"px","margin-left":(A*this.options.cellWidth)+"px"}}).append(D);if(F>=2){D.append(C.progress)}z.data("record",C);this.setBarColor(z,C);if(C.progress!="0%"){z.append(jQuery("<div>",{css:{"z-index":0,position:"absolute",top:0,bottom:0,"background-color":C.color.border,width:C.progress,opacity:0.4}}))}jQuery(E[y]).append(z);y=y+1}};b.prototype.getVerticalHeaderTooltip=function(x){var C="";if(x.type=="task"){C="<strong>"+x.column_title+"</strong> ("+x.progress+")<br/>"+x.title}else{var z=["managers","members"];for(var y in z){var A=z[y];if(!jQuery.isEmptyObject(x.users[A])){var B=jQuery("<ul>");for(var w in x.users[A]){B.append(jQuery("<li>").append(x.users[A][w]))}C+="<p><strong>"+$(this.options.container).data("label-"+A)+"</strong></p>"+B[0].outerHTML}}}return C};b.prototype.getBarTooltip=function(w){var x="";if(w.not_defined){x=$(this.options.container).data("label-not-defined")}else{if(w.type=="task"){x="<strong>"+w.progress+"</strong><br/>"+$(this.options.container).data("label-assignee")+" "+(w.assignee?w.assignee:"")+"<br/>"}x+=$(this.options.container).data("label-start-date")+" "+$.datepicker.formatDate("yy-mm-dd",w.start)+"<br/>";x+=$(this.options.container).data("label-end-date")+" "+$.datepicker.formatDate("yy-mm-dd",w.end)}return x};b.prototype.setBarColor=function(x,w){if(w.not_defined){x.addClass("ganttview-block-not-defined")}else{x.css("background-color",w.color.background);x.css("border-color",w.color.border)}};b.prototype.listenForBlockResize=function(w){var x=this;jQuery("div.ganttview-block",this.options.container).resizable({grid:this.options.cellWidth,handles:"e,w",delay:300,stop:function(){var y=jQuery(this);x.updateDataAndPosition(y,w);x.saveRecord(y.data("record"))}})};b.prototype.listenForBlockMove=function(w){var x=this;jQuery("div.ganttview-block",this.options.container).draggable({axis:"x",delay:300,grid:[this.options.cellWidth,this.options.cellWidth],stop:function(){var y=jQuery(this);x.updateDataAndPosition(y,w);x.saveRecord(y.data("record"))}})};b.prototype.updateDataAndPosition=function(B,z){var w=jQuery("div.ganttview-slide-container",this.options.container);var F=w.scrollLeft();var C=B.offset().left-w.offset().left-1+F;var E=B.data("record");E.not_defined=false;this.setBarColor(B,E);var y=Math.round(C/this.options.cellWidth);var D=this.addDays(this.cloneDate(z),y);E.start=D;var x=B.outerWidth();var A=Math.round(x/this.options.cellWidth)-1;E.end=this.addDays(this.cloneDate(D),A);if(E.type==="task"&&A>0){jQuery("div.ganttview-block-text",B).text(E.progress)}B.attr("title",this.getBarTooltip(E));B.data("record",E);B.css("top","").css("left","").css("position","relative").css("margin-left",C+"px")};b.prototype.getDates=function(A,w){var z=[];z[A.getFullYear()]=[];z[A.getFullYear()][A.getMonth()]=[A];var y=A;while(this.compareDate(y,w)==-1){var x=this.addDays(this.cloneDate(y),1);if(!z[x.getFullYear()]){z[x.getFullYear()]=[]}if(!z[x.getFullYear()][x.getMonth()]){z[x.getFullYear()][x.getMonth()]=[]}z[x.getFullYear()][x.getMonth()].push(x);y=x}return z};b.prototype.prepareData=function(y){for(var x=0;x<y.length;x++){var z=new Date(y[x].start[0],y[x].start[1]-1,y[x].start[2],0,0,0,0);y[x].start=z;var w=new Date(y[x].end[0],y[x].end[1]-1,y[x].end[2],0,0,0,0);y[x].end=w}return y};b.prototype.getDateRange=function(y){var B=new Date();var x=new Date();for(var z=0;z<this.data.length;z++){var A=new Date();A.setTime(Date.parse(this.data[z].start));var w=new Date();w.setTime(Date.parse(this.data[z].end));if(z==0){B=A;x=w}if(this.compareDate(B,A)==1){B=A}if(this.compareDate(x,w)==-1){x=w}}if(this.daysBetween(B,x)<y){x=this.addDays(this.cloneDate(B),y)}B.setDate(B.getDate()-1);return[B,x]};b.prototype.daysBetween=function(z,w){if(!z||!w){return 0}var y=0,x=this.cloneDate(z);while(this.compareDate(x,w)==-1){y=y+1;this.addDays(x,1)}return y};b.prototype.isWeekend=function(w){return w.getDay()%6==0};b.prototype.cloneDate=function(w){return new Date(w.getTime())};b.prototype.addDays=function(w,x){w.setDate(w.getDate()+x*1);return w};b.prototype.compareDate=function(x,w){if(isNaN(x)||isNaN(w)){throw new Error(x+" - "+w)}else{if(x instanceof Date&&w instanceof Date){return(x<w)?-1:(x>w)?1:0}else{throw new TypeError(x+" - "+w)}}};function a(){}a.prototype.listen=function(){$(document).on("click",".color-square",function(){$(".color-square-selected").removeClass("color-square-selected");$(this).addClass("color-square-selected");$("#form-color_id").val($(this).data("color-id"))});$(document).on("click",".assign-me",function(y){y.preventDefault();var w=$(this).data("current-id");var x="#"+$(this).data("target-id");if($(x+" option[value="+w+"]").length){$(x).val(w)}})};function m(){}m.prototype.listen=function(){$(".project-change-role").on("change",function(){$.ajax({cache:false,url:$(this).data("url"),contentType:"application/json",type:"POST",processData:false,data:JSON.stringify({id:$(this).data("id"),role:$(this).val()})})});$("#project-creation-form #form-src_project_id").on("change",function(){var w=$(this).val();if(w==0){$(".project-creation-options").hide()}else{$(".project-creation-options").show()}})};function r(){}r.prototype.execute=function(){var y=$("#chart").data("metrics");var x=[];for(var w=0;w<y.length;w++){x.push([y[w].column_title,y[w].nb_tasks])}c3.generate({data:{columns:x,type:"donut"}})};function o(){}o.prototype.execute=function(){var y=$("#chart").data("metrics");var x=[];for(var w=0;w<y.length;w++){x.push([y[w].user,y[w].nb_tasks])}c3.generate({data:{columns:x,type:"donut"}})};function c(){}c.prototype.execute=function(){var C=$("#chart").data("metrics");var B=[];var w=[];var x=[];var z=d3.time.format("%Y-%m-%d");var D=d3.time.format($("#chart").data("date-format"));for(var A=0;A<C.length;A++){for(var y=0;y<C[A].length;y++){if(A==0){B.push([C[A][y]]);if(y>0){w.push(C[A][y])}}else{B[y].push(C[A][y]);if(y==0){x.push(D(z.parse(C[A][y])))}}}}c3.generate({data:{columns:B,type:"area-spline",groups:[w]},axis:{x:{type:"category",categories:x}}})};function n(){}n.prototype.execute=function(){var B=$("#chart").data("metrics");var A=[[$("#chart").data("label-total")]];var w=[];var y=d3.time.format("%Y-%m-%d");var C=d3.time.format($("#chart").data("date-format"));for(var z=0;z<B.length;z++){for(var x=0;x<B[z].length;x++){if(z==0){A.push([B[z][x]])}else{A[x+1].push(B[z][x]);if(x>0){if(A[0][z]==undefined){A[0].push(0)}A[0][z]+=B[z][x]}if(x==0){w.push(C(y.parse(B[z][x])))}}}}c3.generate({data:{columns:A},axis:{x:{type:"category",categories:w}}})};function g(w){this.app=w}g.prototype.execute=function(){var y=$("#chart").data("metrics");var z=[$("#chart").data("label")];var w=[];for(var x in y){z.push(y[x].average);w.push(y[x].title)}c3.generate({data:{columns:[z],type:"bar"},bar:{width:{ratio:0.5}},axis:{x:{type:"category",categories:w},y:{tick:{format:this.app.formatDuration}}},legend:{show:false}})};function v(w){this.app=w}v.prototype.execute=function(){var y=$("#chart").data("metrics");var z=[$("#chart").data("label")];var w=[];for(var x=0;x<y.length;x++){z.push(y[x].time_spent);w.push(y[x].title)}c3.generate({data:{columns:[z],type:"bar"},bar:{width:{ratio:0.5}},axis:{x:{type:"category",categories:w},y:{tick:{format:this.app.formatDuration}}},legend:{show:false}})};function t(w){this.app=w}t.prototype.execute=function(){var C=$("#chart").data("metrics");var B=[$("#chart").data("label-cycle")];var y=[$("#chart").data("label-lead")];var x=[];var A={};A[$("#chart").data("label-cycle")]="area";A[$("#chart").data("label-lead")]="area-spline";var w={};w[$("#chart").data("label-lead")]="#afb42b";w[$("#chart").data("label-cycle")]="#4e342e";for(var z=0;z<C.length;z++){B.push(parseInt(C[z].avg_cycle_time));y.push(parseInt(C[z].avg_lead_time));x.push(C[z].day)}c3.generate({data:{columns:[y,B],types:A,colors:w},axis:{x:{type:"category",categories:x},y:{tick:{format:this.app.formatDuration}}}})};function h(w){this.app=w}h.prototype.execute=function(){var B=$("#chart").data("metrics");var x=$("#chart").data("label-open");var w=$("#chart").data("label-closed");var C=[$("#chart").data("label-spent")];var A=[$("#chart").data("label-estimated")];var z=[];for(var y in B){C.push(parseFloat(B[y].time_spent));A.push(parseFloat(B[y].time_estimated));z.push(y=="open"?x:w)}c3.generate({data:{columns:[C,A],type:"bar"},bar:{width:{ratio:0.2}},axis:{x:{type:"category",categories:z}},legend:{show:true}})};function u(){this.routes={}}u.prototype.addRoute=function(x,w){this.routes[x]=w};u.prototype.dispatch=function(x){for(var y in this.routes){if(document.getElementById(y)){var w=Object.create(this.routes[y].prototype);this.routes[y].apply(w,[x]);w.execute();break}}};jQuery(document).ready(function(){var x=new l();var w=new u();w.addRoute("board",j);w.addRoute("calendar",i);w.addRoute("screenshot-zone",d);w.addRoute("analytic-task-repartition",r);w.addRoute("analytic-user-repartition",o);w.addRoute("analytic-cfd",c);w.addRoute("analytic-burndown",n);w.addRoute("analytic-avg-time-column",g);w.addRoute("analytic-task-time-column",v);w.addRoute("analytic-lead-cycle-time",t);w.addRoute("analytic-compare-hours",h);w.addRoute("gantt-chart",b);w.dispatch(x);x.listen()})})();
\ No newline at end of file diff --git a/assets/js/src/App.js b/assets/js/src/App.js index 976b4554..cd5df66e 100644 --- a/assets/js/src/App.js +++ b/assets/js/src/App.js @@ -1,7 +1,6 @@ function App() { this.board = new Board(this); this.markdown = new Markdown(); - this.sidebar = new Sidebar(); this.search = new Search(this); this.swimlane = new Swimlane(); this.dropdown = new Dropdown(); @@ -33,7 +32,6 @@ App.prototype.listen = function() { this.project.listen(); this.popover.listen(); this.markdown.listen(); - this.sidebar.listen(); this.tooltip.listen(); this.dropdown.listen(); this.search.listen(); @@ -43,6 +41,19 @@ App.prototype.listen = function() { this.autoComplete(); this.datePicker(); this.focus(); + + $(document).on("click", ".ajax-replace", function(e) { + e.preventDefault(); + var el = $(this); + + $.ajax({ + cache: false, + url: el.attr("href"), + success: function(data) { + el.replaceWith(data); + } + }); + }); }; App.prototype.refresh = function() { diff --git a/assets/js/src/Popover.js b/assets/js/src/Popover.js index 8d72dec8..a5ec647c 100644 --- a/assets/js/src/Popover.js +++ b/assets/js/src/Popover.js @@ -13,7 +13,7 @@ Popover.prototype.open = function(link) { self.app.dropdown.close(); $.get(link, function(content) { - $("body").append('<div id="popover-container"><div id="popover-content">' + content + '</div></div>'); + $("body").prepend('<div id="popover-container"><div id="popover-content">' + content + '</div></div>'); self.app.refresh(); self.router.dispatch(this.app); self.afterOpen(); @@ -55,26 +55,47 @@ Popover.prototype.listen = function() { Popover.prototype.afterOpen = function() { var self = this; - var taskForm = $("#task-form"); + var popoverForm = $("#popover-content .popover-form"); - if (taskForm) { - taskForm.on("submit", function(e) { + // Submit forms with Ajax request + if (popoverForm) { + popoverForm.on("submit", function(e) { e.preventDefault(); $.ajax({ type: "POST", - url: taskForm.attr("action"), - data: taskForm.serialize(), + url: popoverForm.attr("action"), + data: popoverForm.serialize(), success: function(data, textStatus, request) { - if (request.getResponseHeader("X-Ajax-Redirect")) { - window.location = request.getResponseHeader("X-Ajax-Redirect"); - } - else { - $("#popover-content").html(data); - self.afterOpen(); - } + self.afterSubmit(data, request, self); } }); }); } + + // Submit link with Ajax request + $(document).on("click", ".popover-link", function(e) { + e.preventDefault(); + + $.ajax({ + type: "GET", + url: $(this).attr("href"), + success: function(data, textStatus, request) { + self.afterSubmit(data, request, self); + } + }); + }); +}; + +Popover.prototype.afterSubmit = function(data, request, self) { + var redirect = request.getResponseHeader("X-Ajax-Redirect"); + + if (redirect) { + window.location = redirect === 'self' ? window.location.href.split("#")[0] : redirect; + } + else { + $("#popover-content").html(data); + $("#popover-content input[autofocus]").focus(); + self.afterOpen(); + } }; diff --git a/assets/js/src/Project.js b/assets/js/src/Project.js index e2412412..19941f03 100644 --- a/assets/js/src/Project.js +++ b/assets/js/src/Project.js @@ -15,4 +15,14 @@ Project.prototype.listen = function() { }) }); }); + + $('#project-creation-form #form-src_project_id').on('change', function() { + var srcProjectId = $(this).val(); + + if (srcProjectId == 0) { + $(".project-creation-options").hide(); + } else { + $(".project-creation-options").show(); + } + }); }; diff --git a/assets/js/src/Sidebar.js b/assets/js/src/Sidebar.js deleted file mode 100644 index 0794d6b3..00000000 --- a/assets/js/src/Sidebar.js +++ /dev/null @@ -1,25 +0,0 @@ -function Sidebar() { -} - -Sidebar.prototype.expand = function(e) { - e.preventDefault(); - $(".sidebar-container").removeClass("sidebar-collapsed"); - $(".sidebar-collapse").show(); - $(".sidebar h2").show(); - $(".sidebar ul").show(); - $(".sidebar-expand").hide(); -}; - -Sidebar.prototype.collapse = function(e) { - e.preventDefault(); - $(".sidebar-container").addClass("sidebar-collapsed"); - $(".sidebar-expand").show(); - $(".sidebar h2").hide(); - $(".sidebar ul").hide(); - $(".sidebar-collapse").hide(); -}; - -Sidebar.prototype.listen = function() { - $(document).on("click", ".sidebar-collapse", this.collapse); - $(document).on("click", ".sidebar-expand", this.expand); -}; diff --git a/assets/js/src/Task.js b/assets/js/src/Task.js index b3dc1b63..75087a40 100644 --- a/assets/js/src/Task.js +++ b/assets/js/src/Task.js @@ -7,4 +7,15 @@ Task.prototype.listen = function() { $(this).addClass("color-square-selected"); $("#form-color_id").val($(this).data("color-id")); }); + + $(document).on("click", ".assign-me", function(e) { + e.preventDefault(); + + var currentId = $(this).data("current-id"); + var dropdownId = "#" + $(this).data("target-id"); + + if ($(dropdownId + ' option[value=' + currentId + ']').length) { + $(dropdownId).val(currentId); + } + }); }; diff --git a/assets/js/src/Tooltip.js b/assets/js/src/Tooltip.js index 0ec8b268..f3ef55f9 100644 --- a/assets/js/src/Tooltip.js +++ b/assets/js/src/Tooltip.js @@ -48,21 +48,6 @@ Tooltip.prototype.listen = function() { var position = $(_this).tooltip("option", "position"); position.of = $(_this); tooltip.position(position); - - // Toggle subtasks status - $('#tooltip-subtasks a').not(".popover").click(function(e) { - - e.preventDefault(); - e.stopPropagation(); - - if ($(this).hasClass("popover-subtask-restriction")) { - self.app.popover.open($(this).attr('href')); - $(_this).tooltip('close'); - } - else { - $.get($(this).attr('href'), setTooltipContent); - } - }); }); return '<i class="fa fa-spinner fa-spin"></i>'; diff --git a/composer.json b/composer.json index a557b199..77cfe45e 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,6 @@ "ext-hash" : "*", "ext-openssl" : "*", "ext-json" : "*", - "ext-hash" : "*", "ext-ctype" : "*", "ext-filter" : "*", "ext-session" : "*", diff --git a/config.default.php b/config.default.php index d8d0ba3b..cc92daa3 100644 --- a/config.default.php +++ b/config.default.php @@ -130,51 +130,6 @@ define('LDAP_GROUP_FILTER', ''); // LDAP attribute for the group name define('LDAP_GROUP_ATTRIBUTE_NAME', 'cn'); -// Enable/disable Google authentication -define('GOOGLE_AUTH', false); - -// Google client id (Get this value from the Google developer console) -define('GOOGLE_CLIENT_ID', ''); - -// Google client secret key (Get this value from the Google developer console) -define('GOOGLE_CLIENT_SECRET', ''); - -// Enable/disable GitHub authentication -define('GITHUB_AUTH', false); - -// GitHub client id (Copy it from your settings -> Applications -> Developer applications) -define('GITHUB_CLIENT_ID', ''); - -// GitHub client secret key (Copy it from your settings -> Applications -> Developer applications) -define('GITHUB_CLIENT_SECRET', ''); - -// Github oauth2 authorize url -define('GITHUB_OAUTH_AUTHORIZE_URL', 'https://github.com/login/oauth/authorize'); - -// Github oauth2 token url -define('GITHUB_OAUTH_TOKEN_URL', 'https://github.com/login/oauth/access_token'); - -// Github API url (don't forget the trailing slash) -define('GITHUB_API_URL', 'https://api.github.com/'); - -// Enable/disable Gitlab authentication -define('GITLAB_AUTH', false); - -// Gitlab application id -define('GITLAB_CLIENT_ID', ''); - -// Gitlab application secret -define('GITLAB_CLIENT_SECRET', ''); - -// Gitlab oauth2 authorize url -define('GITLAB_OAUTH_AUTHORIZE_URL', 'https://gitlab.com/oauth/authorize'); - -// Gitlab oauth2 token url -define('GITLAB_OAUTH_TOKEN_URL', 'https://gitlab.com/oauth/token'); - -// Gitlab API url endpoint (don't forget the trailing slash) -define('GITLAB_API_URL', 'https://gitlab.com/api/v3/'); - // Enable/disable the reverse proxy authentication define('REVERSE_PROXY_AUTH', false); diff --git a/doc/analytics.markdown b/doc/analytics.markdown index 13657b56..d72fc383 100644 --- a/doc/analytics.markdown +++ b/doc/analytics.markdown @@ -62,9 +62,4 @@ This chart show the average lead and cycle time for the last 1000 tasks over tim Those metrics are calculated and recorded every day for the whole project. -Don't forget to run the daily job for statistics calculation -------------------------------------------------------- - -To generate accurate analytic data, you should run the daily cronjob **project daily statistics**. - -[Read the documentation of Kanboard CLI](cli.markdown) +Note: Don't forget to run the [daily cronjob](cronjob.markdown) to have accurate statistics. diff --git a/doc/api-user-procedures.markdown b/doc/api-user-procedures.markdown index 6ecf12c6..9b43e1e1 100644 --- a/doc/api-user-procedures.markdown +++ b/doc/api-user-procedures.markdown @@ -113,6 +113,48 @@ Response example: } ``` +## getUserByName + +- Purpose: **Get user information** +- Parameters: + - **username** (string, required) +- Result on success: **user properties** +- Result on failure: **null** + +Request example: + +```json +{ + "jsonrpc": "2.0", + "method": "getUserByName", + "id": 1769674782, + "params": { + "username": "biloute" + } +} +``` + +Response example: + +```json +{ + "jsonrpc": "2.0", + "id": 1769674782, + "result": { + "id": "1", + "username": "biloute", + "password": "$2y$10$dRs6pPoBu935RpmsrhmbjevJH5MgZ7Kr9QrnVINwwyZ3.MOwqg.0m", + "role": "app-user", + "is_ldap_user": "0", + "name": "", + "email": "", + "google_id": null, + "github_id": null, + "notifications_enabled": "0" + } +} +``` + ## getAllUsers - Purpose: **Get all available users** diff --git a/doc/centos-installation.markdown b/doc/centos-installation.markdown index d0fd6a00..576119b4 100644 --- a/doc/centos-installation.markdown +++ b/doc/centos-installation.markdown @@ -1,6 +1,8 @@ Centos Installation =================== +Note: Some features of Kanboard require that you run [a daily background job](cronjob.markdown). + Centos 7 -------- diff --git a/doc/cli.markdown b/doc/cli.markdown index bcb478dd..9334d84b 100644 --- a/doc/cli.markdown +++ b/doc/cli.markdown @@ -4,7 +4,7 @@ Command Line Interface Kanboard provides a simple command line interface that can be used from any Unix terminal. This tool can be used only on the local machine. -This feature is useful to run commands outside the web server process by example running a huge report. +This feature is useful to run commands outside of the web server processes. Usage ----- @@ -28,6 +28,7 @@ Options: -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug Available commands: + cronjob Execute daily cronjob help Displays help for a command list Lists commands export @@ -42,6 +43,8 @@ Available commands: notification:overdue-tasks Send notifications for overdue tasks projects projects:daily-stats Calculate daily statistics for all projects + trigger + trigger:tasks Trigger scheduler event for all tasks ``` Available commands @@ -116,7 +119,7 @@ Emails will be sent to all users with notifications enabled. You can also display the overdue tasks with the flag `--show`: ```bash -$ ./kanboard notification:overdue-tasks --show +./kanboard notification:overdue-tasks --show +-----+---------+------------+------------+--------------+----------+ | Id | Title | Due date | Project Id | Project name | Assignee | +-----+---------+------------+------------+--------------+----------+ @@ -125,20 +128,22 @@ $ ./kanboard notification:overdue-tasks --show +-----+---------+------------+------------+--------------+----------+ ``` -Cronjob example: - -```bash -# Everyday at 8am we check for due tasks -0 8 * * * cd /path/to/kanboard && ./kanboard notification:overdue-tasks >/dev/null 2>&1 -``` - ### Run daily project stats calculation -You can add a background task to calculate the project statistics every day: +This command calculate the statistics of each project: ```bash -$ ./kanboard projects:daily-stats +./kanboard projects:daily-stats Run calculation for Project #0 Run calculation for Project #1 Run calculation for Project #10 ``` + +### Trigger for tasks + +This command send a "daily cronjob event" to all open tasks of each project. + +```bash +./kanboard trigger:tasks +Trigger task event: project_id=2, nb_tasks=1 +``` diff --git a/doc/config.markdown b/doc/config.markdown index 393efbae..92ff2217 100644 --- a/doc/config.markdown +++ b/doc/config.markdown @@ -174,34 +174,6 @@ define('LDAP_GROUP_FILTER', ''); define('LDAP_GROUP_ATTRIBUTE_NAME', 'cn'); ``` -Google Authentication settings ------------------------------- - -```php -// Enable/disable Google authentication -define('GOOGLE_AUTH', false); - -// Google client id (Get this value from the Google developer console) -define('GOOGLE_CLIENT_ID', ''); - -// Google client secret key (Get this value from the Google developer console) -define('GOOGLE_CLIENT_SECRET', ''); -``` - -Github Authentication settings ------------------------------- - -```php -// Enable/disable GitHub authentication -define('GITHUB_AUTH', false); - -// GitHub client id (Copy it from your settings -> Applications -> Developer applications) -define('GITHUB_CLIENT_ID', ''); - -// GitHub client secret key (Copy it from your settings -> Applications -> Developer applications) -define('GITHUB_CLIENT_SECRET', ''); -``` - Reverse-Proxy Authentication settings ------------------------------------- diff --git a/doc/cronjob.markdown b/doc/cronjob.markdown new file mode 100644 index 00000000..32f12888 --- /dev/null +++ b/doc/cronjob.markdown @@ -0,0 +1,32 @@ +Background Job Scheduling +========================= + +To work properly, Kanboard requires that a background job run on a daily basis. +Usually on Unix platforms, this process is done by `cron`. + +This background job is necessary for these features: + +- Reports and analytics (calculate daily stats of each projects) +- Send overdue task notifications +- Execute automatic actions connected to the event "Daily background job for tasks" + +Configuration on Unix and Linux platforms +----------------------------------------- + +There are multiple ways to define a cronjob on Unix/Linux operating systems, this example is for Ubuntu 14.04. +The procedure is similar to other systems. + +Edit the crontab of your web server user: + +```bash +sudo crontab -u www-data -e +``` + +Example to execute the daily cronjob at 8am: + +```bash +0 8 * * * cd /path/to/kanboard && ./kanboard cronjob >/dev/null 2>&1 +``` + +Note: the cronjob process must have write access to the database in case you are using Sqlite. +Usually, running the cronjob under the web server user is enough. diff --git a/doc/debian-installation.markdown b/doc/debian-installation.markdown index 147fe452..ec956049 100644 --- a/doc/debian-installation.markdown +++ b/doc/debian-installation.markdown @@ -1,6 +1,8 @@ How to install Kanboard on Debian? ================================== +Note: Some features of Kanboard require that you run [a daily background job](cronjob.markdown). + Debian 8 (Jessie) ----------------- diff --git a/doc/freebsd-installation.markdown b/doc/freebsd-installation.markdown index 84b35ad8..7b36dff1 100644 --- a/doc/freebsd-installation.markdown +++ b/doc/freebsd-installation.markdown @@ -55,7 +55,7 @@ Generally 3 elements have to be installed: Fetch and extract ports... ```bash -$ portsnap fetch +$ portsnap fetch $ portsnap extract ``` @@ -122,6 +122,7 @@ there is no need to install it manually. Please note ----------- -Port is being hosted on [bitbucket](https://bitbucket.org/if0/freebsd-kanboard/). Feel free to comment, +- Port is being hosted on [bitbucket](https://bitbucket.org/if0/freebsd-kanboard/). Feel free to comment, fork and suggest updates! -
\ No newline at end of file +- Some features of Kanboard require that you run [a daily background job](cronjob.markdown). + diff --git a/doc/github-authentication.markdown b/doc/github-authentication.markdown deleted file mode 100644 index ba0f371f..00000000 --- a/doc/github-authentication.markdown +++ /dev/null @@ -1,80 +0,0 @@ -Github Authentication -===================== - -Requirements ------------- - -OAuth Github API credentials (available in your [Settings > Applications > Developer applications](https://github.com/settings/applications)) - -How does this work? -------------------- - -The Github authentication in Kanboard uses the [OAuth 2.0](http://oauth.net/2/) protocol, so any user of Kanboard can be linked to a Github account. - -That means you can use your Github account to login on Kanboard. - -How to link a Github account ----------------------------- - -1. Go to your user profile -2. Click on **External accounts** -3. Click on the link **Link my Github Account** -4. You are redirected to the **Github Authorize application form** -5. Authorize Kanboard by clicking on the button **Accept** -6. Your account is now linked - -Now, on the login page you can be authenticated in one click with the link **Login with my Github Account**. - -Your name and email are automatically updated from your Github Account if defined. - -Installation instructions -------------------------- - -### Setting up OAuth 2.0 - -- On Github, go to the page [Register a new OAuth application](https://github.com/settings/applications/new) -- Just follow the [official Github documentation](https://developer.github.com/guides/basics-of-authentication/#registering-your-app) -- In Kanboard, you can get the **callback url** in **Settings > Integrations > Github Authentication** - -### Setting up Kanboard - -Either create a new `config.php` file or rename the `config.default.php` file and set the following values: - -```php -// Enable/disable Github authentication -define('GITHUB_AUTH', true); - -// Github client id (Copy it from your settings -> Applications -> Developer applications) -define('GITHUB_CLIENT_ID', 'YOUR_GITHUB_CLIENT_ID'); - -// Github client secret key (Copy it from your settings -> Applications -> Developer applications) -define('GITHUB_CLIENT_SECRET', 'YOUR_GITHUB_CLIENT_SECRET'); -``` - -### Github Entreprise - -To use this authentication method with Github Enterprise you have to change the default urls. - -Replace these values by your self-hosted instance of Github: - -```php -// Github oauth2 authorize url -define('GITHUB_OAUTH_AUTHORIZE_URL', 'https://github.com/login/oauth/authorize'); - -// Github oauth2 token url -define('GITHUB_OAUTH_TOKEN_URL', 'https://github.com/login/oauth/access_token'); - -// Github API url (don't forget the slash at the end) -define('GITHUB_API_URL', 'https://api.github.com/'); -``` - -Notes ------ - -Kanboard uses these information from your public Github profile: - -- Full name -- Public email address -- Github unique id - -The Github unique id is used to link the local user account and the Github account. diff --git a/doc/gitlab-authentication.markdown b/doc/gitlab-authentication.markdown deleted file mode 100644 index 8d2f0000..00000000 --- a/doc/gitlab-authentication.markdown +++ /dev/null @@ -1,83 +0,0 @@ -Gitlab Authentication -===================== - -Requirements ------------- - -- Account on [Gitlab.com](https://gitlab.com) or you own self-hosted Gitlab instance -- Have Kanboard registered as application in Gitlab - -How does this work? -------------------- - -The Gitlab authentication in Kanboard uses the [OAuth 2.0](http://oauth.net/2/) protocol, so any user of Kanboard can be linked to a Gitlab account. - -That means you can use your Gitlab account to login on Kanboard. - -How to link a Gitlab account ----------------------------- - -1. Go to your user profile -2. Click on **External accounts** -3. Click on the link **Link my Gitlab Account** -4. You are redirected to the **Gitlab authorization form** -5. Authorize Kanboard by clicking on the button **Accept** -6. Your account is now linked - -Now, on the login page you can be authenticated in one click with the link **Login with my Gitlab Account**. - -Your name and email are automatically updated from your Gitlab Account if defined. - -Installation instructions -------------------------- - -### Setting up OAuth 2.0 - -- On Gitlab, register a new application by following the [official documentation](http://doc.gitlab.com/ce/integration/oauth_provider.html) -- In Kanboard, you can get the **callback url** in **Settings > Integrations > Gitlab Authentication**, just copy and paste the url - -### Setting up Kanboard - -Either create a new `config.php` file or rename the `config.default.php` file and set the following values: - -```php -// Enable/disable Gitlab authentication -define('GITLAB_AUTH', true); - -// Gitlab application id -define('GITLAB_CLIENT_ID', 'YOUR_APPLICATION_ID'); - -// Gitlab application secret -define('GITLAB_CLIENT_SECRET', 'YOUR_APPLICATION_SECRET'); -``` - -### Custom endpoints for self-hosted Gitlab - -Change these default values if you use a self-hosted instance of Gitlab: - -```php -// Gitlab oauth2 authorize url -define('GITLAB_OAUTH_AUTHORIZE_URL', 'https://gitlab.com/oauth/authorize'); - -// Gitlab oauth2 token url -define('GITLAB_OAUTH_TOKEN_URL', 'https://gitlab.com/oauth/token'); - -// Gitlab API url endpoint (don't forget the slash at the end) -define('GITLAB_API_URL', 'https://gitlab.com/api/v3/'); -``` - -Notes ------ - -Kanboard uses these information from your Gitlab profile: - -- Full name -- Email address -- Gitlab unique id - -The Gitlab unique id is used to link the local user account and the Gitlab account. - -Known issues ------------- - -Gitlab OAuth will work only with url rewrite enabled. At the moment, Gitlab doesn't support callback url with query string parameters. See [Gitlab issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/2443) diff --git a/doc/google-authentication.markdown b/doc/google-authentication.markdown deleted file mode 100644 index 0f4f3ec1..00000000 --- a/doc/google-authentication.markdown +++ /dev/null @@ -1,64 +0,0 @@ -Google Authentication -===================== - -Requirements ------------- - -OAuth Google API credentials (available in the Google Developer Console) - -How does this work? -------------------- - -- The Google authentication in Kanboard use the OAuth 2.0 protocol -- Any user account in Kanboard can be linked to a Google Account -- When a Kanboard user account is linked to Google, you can login with one click - -Procedure to link a Google Account ----------------------------------- - -1. Go to your user profile -2. Click on **External accounts** -3. Click on the link **Link my Google Account** -4. You are redirected to the **Google Consent screen** -5. Authorize Kanboard by clicking on the button **Accept** -6. Your account is now linked - -Now, on the login page you can be authenticated in one click with the link **Login with my Google Account**. - -Your name and email are automatically updated from your Google Account. - -Installation instructions -------------------------- - -### Setting up OAuth 2.0 in Google Developer Console - -- Follow the [official Google documentation](https://developers.google.com/accounts/docs/OAuth2Login#appsetup) to create a new application -- In Kanboard, you can get the **redirect url** in **Settings > Integrations > Google Authentication** - -### Setting up Kanboad - -Create a custom `config.php` file or copy the `config.default.php` file: - -```php -<?php - -// Enable/disable Google authentication -define('GOOGLE_AUTH', true); // Set this value to true - -// Google client id (Get this value from the Google developer console) -define('GOOGLE_CLIENT_ID', 'YOUR_CLIENT_ID'); - -// Google client secret key (Get this value from the Google developer console) -define('GOOGLE_CLIENT_SECRET', 'YOUR_CLIENT_SECRET'); -``` - -Notes ------ - -Kanboard use these information from your Google profile: - -- Full name -- Email address -- Google unique id - -The Google unique id is used to link together the local user account and the Google account. diff --git a/doc/heroku.markdown b/doc/heroku.markdown index 56d79bc9..f145f70e 100644 --- a/doc/heroku.markdown +++ b/doc/heroku.markdown @@ -35,5 +35,5 @@ heroku open Limitations ----------- -The storage of Heroku is ephemeral, that means uploaded files through Kanboard are not persistent after a reboot. -We may want to install a plugin to store your files in a cloud storage provider like [Amazon S3](https://github.com/kanboard/plugin-s3). +- The storage of Heroku is ephemeral, that means uploaded files through Kanboard are not persistent after a reboot. You may want to install a plugin to store your files in a cloud storage provider like [Amazon S3](https://github.com/kanboard/plugin-s3). +- Some features of Kanboard require that you run [a daily background job](cronjob.markdown). diff --git a/doc/index.markdown b/doc/index.markdown index 1e95fe06..be1baa16 100644 --- a/doc/index.markdown +++ b/doc/index.markdown @@ -103,6 +103,7 @@ Technical details ### Configuration - [Config file](config.markdown) +- [Background tasks](cronjob.markdown) - [Email configuration](email-configuration.markdown) - [URL rewriting](nice-urls.markdown) @@ -117,9 +118,6 @@ Technical details - [LDAP authentication](ldap-authentication.markdown) - [LDAP group synchronization](ldap-group-sync.markdown) - [LDAP parameters](ldap-parameters.markdown) -- [Google authentication](google-authentication.markdown) -- [Github authentication](github-authentication.markdown) -- [Gitlab authentication](gitlab-authentication.markdown) - [Reverse proxy authentication](reverse-proxy-authentication.markdown) ### Contributors diff --git a/doc/installation.markdown b/doc/installation.markdown index b208be8a..dd4283f8 100644 --- a/doc/installation.markdown +++ b/doc/installation.markdown @@ -39,3 +39,8 @@ Security - Don't forget to change the default user/password - Don't allow everybody to access to the directory `data` from the URL. There is already a `.htaccess` for Apache but nothing for Nginx. + +Notes +----- + +- Some features of Kanboard require that you run [a daily background job](cronjob.markdown) diff --git a/doc/nice-urls.markdown b/doc/nice-urls.markdown index 7cb8dbac..9fbb3510 100644 --- a/doc/nice-urls.markdown +++ b/doc/nice-urls.markdown @@ -86,3 +86,37 @@ define('ENABLE_URL_REWRITE', true); ``` Adapt the example above according to your own configuration. + +IIS configuration example +--------------------------- + +Create a web.config in you installation folder: + +```xml +<?xml version="1.0" encoding="UTF-8"?> +<configuration> + <system.webServer> + <rewrite> + <rules> + <rule name="Imported Rule 1" stopProcessing="true"> + <match url="^" ignoreCase="false" /> + <conditions logicalGrouping="MatchAll"> + <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" /> + </conditions> + <action type="Rewrite" url="index.php" appendQueryString="true" /> + </rule> + </rules> + </rewrite> + </system.webServer> +</configuration> + +``` + +In your Kanboard `config.php`: + +```php +define('ENABLE_URL_REWRITE', true); +``` + +Adapt the example above according to your own configuration. + diff --git a/doc/plugin-external-link.markdown b/doc/plugin-external-link.markdown new file mode 100644 index 00000000..36252aff --- /dev/null +++ b/doc/plugin-external-link.markdown @@ -0,0 +1,78 @@ +External Link Providers +======================= + +This functionality allows you to link a task to additional items stored on another system. + +For example, you can link a task to: + +- Traditional web page +- Attachment (PDF documents stored on the web, archive...) +- Any ticketing system (bug tracker, customer support ticket...) + +Each item has a type, a URL, a dependency type and a title. + +By default, Kanboard includes two kinds of providers: + +- Web Link: You copy and paste a link and Kanboard will fetch the page title automatically +- Attachment: Link to anything that is not a web page + +Workflow +-------- + +1. The end-user copy and paste the URL to the form and submit +2. If the link type is "auto", Kanboard will loop through all providers registered until there is a match +3. Then, the link provider returns a object that implements the interface `ExternalLinkInterface` +4. A form is shown to the user with all pre-filled data before to save the link + +Interfaces +---------- + +To implement a new link provider from a plugin, you need to create 2 classes that implement those interfaces: + +- `Kanboard\Core\ExternalLink\ExternalLinkProviderInterface` +- `Kanboard\Core\ExternalLink\ExternalLinkInterface` + +### ExternalLinkProviderInterface + +| Method | Usage | +|----------------------------|-----------------------------------------------------------------| +| `getName()` | Get provider name (label) | +| `getType()` | Get link type (will be saved in the database) | +| `getDependencies()` | Get a dictionary of supported dependency types by the provider | +| `setUserTextInput($input)` | Set text entered by the user | +| `match()` | Return true if the provider can parse correctly the user input | +| `getLink()` | Get the link found with the properties | + +### ExternalLinkInterface + +| Method | Usage | +|-------------------|------------------| +| `getTitle()` | Get link title | +| `getUrl()` | Get link URL | +| `setUrl($url)` | Set link URL | + +Register a new link provider +---------------------------- + +In your `Plugin.php`, just call the method `register()` from the object `ExternalLinkManager`: + +```php +<?php + +namespace Kanboard\Plugin\MyExternalLink; + +use Kanboard\Core\Plugin\Base; + +class Plugin extends Base +{ + public function initialize() + { + $this->externalLinkManager->register(new MyLinkProvider()); + } +} +``` + +Examples +-------- + +- Kanboard includes the default providers "WebLink" and "Attachment" diff --git a/doc/plugin-hooks.markdown b/doc/plugin-hooks.markdown index 6e9718d9..eac027c2 100644 --- a/doc/plugin-hooks.markdown +++ b/doc/plugin-hooks.markdown @@ -58,8 +58,28 @@ class Plugin extends Base } ``` +Example to override default values for task forms: + +```php +class Plugin extends Base +{ + public function initialize() + { + $this->hook->on('controller:task:form:default', function (array $default_values) { + return empty($default_values['score']) ? array('score' => 4) : array(); + }); + } +} +``` + List of merging hooks: +#### controller:task:form:default + +- Override default values for task forms +- Arguments: + - `$default_values`: actual default values (array) + #### controller:calendar:project:events - Add more events to the project calendar diff --git a/doc/plugins.markdown b/doc/plugins.markdown index f3f922f3..55575612 100644 --- a/doc/plugins.markdown +++ b/doc/plugins.markdown @@ -21,6 +21,7 @@ Plugin creators should specify explicitly the compatible versions of Kanboard. I - [Authentication plugin registration](plugin-authentication.markdown) - [Authorization Architecture](plugin-authorization-architecture.markdown) - [Custom Group Providers](plugin-group-provider.markdown) +- [External Link Providers](plugin-external-link.markdown) - [LDAP client](plugin-ldap-client.markdown) Examples of plugins diff --git a/doc/ubuntu-installation.markdown b/doc/ubuntu-installation.markdown index cec3ebba..ab4dfe7c 100644 --- a/doc/ubuntu-installation.markdown +++ b/doc/ubuntu-installation.markdown @@ -26,3 +26,5 @@ sudo unzip kanboard-latest.zip sudo chown -R www-data:www-data kanboard/data sudo rm kanboard-latest.zip ``` + +Some features of Kanboard require that you run [a daily background job](cronjob.markdown). diff --git a/doc/windows-apache-installation.markdown b/doc/windows-apache-installation.markdown index 2c8f74e1..27b6812e 100644 --- a/doc/windows-apache-installation.markdown +++ b/doc/windows-apache-installation.markdown @@ -123,3 +123,8 @@ Tested configuration -------------------- - Windows 2008 R2 / Apache 2.4.12 / PHP 5.6.8 + +Notes +----- + +- Some features of Kanboard require that you run [a daily background job](cronjob.markdown). diff --git a/doc/windows-iis-installation.markdown b/doc/windows-iis-installation.markdown index 6206db21..bd4607de 100644 --- a/doc/windows-iis-installation.markdown +++ b/doc/windows-iis-installation.markdown @@ -65,3 +65,9 @@ Tested configurations - Windows 2008 R2 Standard Edition / IIS 7.5 / PHP 5.5.16 - Windows 2012 Standard Edition / IIS 8.5 / PHP 5.3.29 + +Notes +----- + +- Some features of Kanboard require that you run [a daily background job](cronjob.markdown). + @@ -13,6 +13,8 @@ use Kanboard\Console\ProjectDailyColumnStatsExport; use Kanboard\Console\TransitionExport; use Kanboard\Console\LocaleSync; use Kanboard\Console\LocaleComparator; +use Kanboard\Console\TaskTrigger; +use Kanboard\Console\Cronjob; $container['dispatcher']->dispatch('app.bootstrap', new Event); @@ -25,4 +27,6 @@ $application->add(new ProjectDailyColumnStatsExport($container)); $application->add(new TransitionExport($container)); $application->add(new LocaleSync($container)); $application->add(new LocaleComparator($container)); +$application->add(new TaskTrigger($container)); +$application->add(new Cronjob($container)); $application->run(); diff --git a/tests/integration/ApiTest.php b/tests/integration/ApiTest.php index 798bde42..8b970a6c 100644 --- a/tests/integration/ApiTest.php +++ b/tests/integration/ApiTest.php @@ -563,6 +563,20 @@ class Api extends PHPUnit_Framework_TestCase $this->assertNull($this->client->getUser(2222)); } + public function testGetUserByName() + { + $user = $this->client->getUserByName('toto'); + $this->assertNotFalse($user); + $this->assertTrue(is_array($user)); + $this->assertEquals(2, $user['id']); + + $user = $this->client->getUserByName('manager'); + $this->assertNotEmpty($user); + $this->assertEquals('app-manager', $user['role']); + + $this->assertNull($this->client->getUserByName('nonexistantusername')); + } + public function testUpdateUser() { $user = array(); diff --git a/tests/units/Action/TaskCloseNoActivityTest.php b/tests/units/Action/TaskCloseNoActivityTest.php new file mode 100644 index 00000000..b6e04c47 --- /dev/null +++ b/tests/units/Action/TaskCloseNoActivityTest.php @@ -0,0 +1,43 @@ +<?php + +require_once __DIR__.'/../Base.php'; + +use Kanboard\Event\TaskListEvent; +use Kanboard\Model\TaskCreation; +use Kanboard\Model\TaskFinder; +use Kanboard\Model\Project; +use Kanboard\Model\Task; +use Kanboard\Action\TaskCloseNoActivity; + +class TaskCloseNoActivityTest extends Base +{ + public function testClose() + { + $projectModel = new Project($this->container); + $taskCreationModel = new TaskCreation($this->container); + $taskFinderModel = new TaskFinder($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test'))); + + $this->container['db']->table(Task::TABLE)->eq('id', 1)->update(array('date_modification' => strtotime('-10days'))); + + $tasks = $taskFinderModel->getAll(1); + $event = new TaskListEvent(array('tasks' => $tasks, 'project_id' => 1)); + + $action = new TaskCloseNoActivity($this->container); + $action->setProjectId(1); + $action->setParam('duration', 2); + + $this->assertTrue($action->execute($event, Task::EVENT_DAILY_CRONJOB)); + + $task = $taskFinderModel->getById(1); + $this->assertNotEmpty($task); + $this->assertEquals(0, $task['is_active']); + + $task = $taskFinderModel->getById(2); + $this->assertNotEmpty($task); + $this->assertEquals(1, $task['is_active']); + } +} diff --git a/tests/units/Action/TaskEmailNoActivityTest.php b/tests/units/Action/TaskEmailNoActivityTest.php new file mode 100644 index 00000000..af4baed5 --- /dev/null +++ b/tests/units/Action/TaskEmailNoActivityTest.php @@ -0,0 +1,103 @@ +<?php + +require_once __DIR__.'/../Base.php'; + +use Kanboard\Event\TaskListEvent; +use Kanboard\Model\TaskCreation; +use Kanboard\Model\TaskFinder; +use Kanboard\Model\Project; +use Kanboard\Model\Task; +use Kanboard\Model\User; +use Kanboard\Action\TaskEmailNoActivity; + +class TaskEmailNoActivityTest extends Base +{ + public function testSendEmail() + { + $userModel = new User($this->container); + $projectModel = new Project($this->container); + $taskCreationModel = new TaskCreation($this->container); + $taskFinderModel = new TaskFinder($this->container); + + $this->assertEquals(2, $userModel->create(array('username' => 'test', 'email' => 'chuck@norris', 'name' => 'Chuck Norris'))); + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test'))); + + $this->container['db']->table(Task::TABLE)->eq('id', 1)->update(array('date_modification' => strtotime('-10days'))); + + $tasks = $taskFinderModel->getAll(1); + $event = new TaskListEvent(array('tasks' => $tasks, 'project_id' => 1)); + + $action = new TaskEmailNoActivity($this->container); + $action->setProjectId(1); + $action->setParam('user_id', 2); + $action->setParam('subject', 'Old tasks'); + $action->setParam('duration', 2); + + $this->container['emailClient'] + ->expects($this->once()) + ->method('send') + ->with('chuck@norris', 'Chuck Norris', 'Old tasks', $this->anything()); + + $this->assertTrue($action->execute($event, Task::EVENT_DAILY_CRONJOB)); + } + + public function testUserWithNoEmail() + { + $userModel = new User($this->container); + $projectModel = new Project($this->container); + $taskCreationModel = new TaskCreation($this->container); + $taskFinderModel = new TaskFinder($this->container); + + $this->assertEquals(2, $userModel->create(array('username' => 'test', 'name' => 'Chuck Norris'))); + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test'))); + + $this->container['db']->table(Task::TABLE)->eq('id', 1)->update(array('date_modification' => strtotime('-10days'))); + + $tasks = $taskFinderModel->getAll(1); + $event = new TaskListEvent(array('tasks' => $tasks, 'project_id' => 1)); + + $action = new TaskEmailNoActivity($this->container); + $action->setProjectId(1); + $action->setParam('user_id', 2); + $action->setParam('subject', 'Old tasks'); + $action->setParam('duration', 2); + + $this->container['emailClient'] + ->expects($this->never()) + ->method('send'); + + $this->assertFalse($action->execute($event, Task::EVENT_DAILY_CRONJOB)); + } + + public function testTooRecent() + { + $userModel = new User($this->container); + $projectModel = new Project($this->container); + $taskCreationModel = new TaskCreation($this->container); + $taskFinderModel = new TaskFinder($this->container); + + $this->assertEquals(2, $userModel->create(array('username' => 'test', 'email' => 'chuck@norris', 'name' => 'Chuck Norris'))); + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test'))); + + $tasks = $taskFinderModel->getAll(1); + $event = new TaskListEvent(array('tasks' => $tasks, 'project_id' => 1)); + + $action = new TaskEmailNoActivity($this->container); + $action->setProjectId(1); + $action->setParam('user_id', 2); + $action->setParam('subject', 'Old tasks'); + $action->setParam('duration', 2); + + $this->container['emailClient'] + ->expects($this->never()) + ->method('send'); + + $this->assertFalse($action->execute($event, Task::EVENT_DAILY_CRONJOB)); + } +} diff --git a/tests/units/Analytic/AverageTimeSpentColumnAnalyticTest.php b/tests/units/Analytic/AverageTimeSpentColumnAnalyticTest.php index 75cb181d..8ad6d1e7 100644 --- a/tests/units/Analytic/AverageTimeSpentColumnAnalyticTest.php +++ b/tests/units/Analytic/AverageTimeSpentColumnAnalyticTest.php @@ -16,13 +16,14 @@ class AverageTimeSpentColumnAnalyticTest extends Base $taskCreationModel = new TaskCreation($this->container); $projectModel = new Project($this->container); $averageLeadCycleTimeAnalytic = new AverageTimeSpentColumnAnalytic($this->container); - $now = time(); $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test'))); $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test'))); + $now = time(); + $this->container['db']->table(Task::TABLE)->eq('id', 1)->update(array('date_completed' => $now + 3600)); $this->container['db']->table(Task::TABLE)->eq('id', 2)->update(array('date_completed' => $now + 1800)); @@ -64,13 +65,13 @@ class AverageTimeSpentColumnAnalyticTest extends Base $taskCreationModel = new TaskCreation($this->container); $projectModel = new Project($this->container); $averageLeadCycleTimeAnalytic = new AverageTimeSpentColumnAnalytic($this->container); - $now = time(); $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test'))); $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test'))); + $now = time(); $this->container['db']->table(Task::TABLE)->eq('id', 1)->update(array('date_completed' => $now + 3600)); $this->container['db']->table(Task::TABLE)->eq('id', 2)->update(array('date_completed' => $now + 1800)); diff --git a/tests/units/Auth/GithubAuthTest.php b/tests/units/Auth/GithubAuthTest.php deleted file mode 100644 index e9ab066f..00000000 --- a/tests/units/Auth/GithubAuthTest.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php - -require_once __DIR__.'/../Base.php'; - -use Kanboard\Auth\GithubAuth; -use Kanboard\Model\User; - -class GithubAuthTest extends Base -{ - public function testGetName() - { - $provider = new GithubAuth($this->container); - $this->assertEquals('Github', $provider->getName()); - } - - public function testAuthenticationSuccessful() - { - $profile = array( - 'id' => 1234, - 'email' => 'test@localhost', - 'name' => 'Test', - ); - - $provider = $this - ->getMockBuilder('\Kanboard\Auth\GithubAuth') - ->setConstructorArgs(array($this->container)) - ->setMethods(array( - 'getProfile', - )) - ->getMock(); - - $provider->expects($this->once()) - ->method('getProfile') - ->will($this->returnValue($profile)); - - $this->assertInstanceOf('Kanboard\Auth\GithubAuth', $provider->setCode('1234')); - - $this->assertTrue($provider->authenticate()); - - $user = $provider->getUser(); - $this->assertInstanceOf('Kanboard\User\GithubUserProvider', $user); - $this->assertEquals('Test', $user->getName()); - $this->assertEquals('', $user->getInternalId()); - $this->assertEquals(1234, $user->getExternalId()); - $this->assertEquals('', $user->getRole()); - $this->assertEquals('', $user->getUsername()); - $this->assertEquals('test@localhost', $user->getEmail()); - $this->assertEquals('github_id', $user->getExternalIdColumn()); - $this->assertEquals(array(), $user->getExternalGroupIds()); - $this->assertEquals(array(), $user->getExtraAttributes()); - $this->assertFalse($user->isUserCreationAllowed()); - } - - public function testAuthenticationFailed() - { - $provider = $this - ->getMockBuilder('\Kanboard\Auth\GithubAuth') - ->setConstructorArgs(array($this->container)) - ->setMethods(array( - 'getProfile', - )) - ->getMock(); - - $provider->expects($this->once()) - ->method('getProfile') - ->will($this->returnValue(array())); - - $this->assertFalse($provider->authenticate()); - $this->assertEquals(null, $provider->getUser()); - } - - public function testGetService() - { - $provider = new GithubAuth($this->container); - $this->assertInstanceOf('Kanboard\Core\Http\OAuth2', $provider->getService()); - } - - public function testUnlink() - { - $userModel = new User($this->container); - $provider = new GithubAuth($this->container); - - $this->assertEquals(2, $userModel->create(array('username' => 'test', 'github_id' => '1234'))); - $this->assertNotEmpty($userModel->getByExternalId('github_id', 1234)); - - $this->assertTrue($provider->unlink(2)); - $this->assertEmpty($userModel->getByExternalId('github_id', 1234)); - } -} diff --git a/tests/units/Auth/GitlabAuthTest.php b/tests/units/Auth/GitlabAuthTest.php deleted file mode 100644 index e3ae0bdd..00000000 --- a/tests/units/Auth/GitlabAuthTest.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php - -require_once __DIR__.'/../Base.php'; - -use Kanboard\Auth\GitlabAuth; -use Kanboard\Model\User; - -class GitlabAuthTest extends Base -{ - public function testGetName() - { - $provider = new GitlabAuth($this->container); - $this->assertEquals('Gitlab', $provider->getName()); - } - - public function testAuthenticationSuccessful() - { - $profile = array( - 'id' => 1234, - 'email' => 'test@localhost', - 'name' => 'Test', - ); - - $provider = $this - ->getMockBuilder('\Kanboard\Auth\GitlabAuth') - ->setConstructorArgs(array($this->container)) - ->setMethods(array( - 'getProfile', - )) - ->getMock(); - - $provider->expects($this->once()) - ->method('getProfile') - ->will($this->returnValue($profile)); - - $this->assertInstanceOf('Kanboard\Auth\GitlabAuth', $provider->setCode('1234')); - - $this->assertTrue($provider->authenticate()); - - $user = $provider->getUser(); - $this->assertInstanceOf('Kanboard\User\GitlabUserProvider', $user); - $this->assertEquals('Test', $user->getName()); - $this->assertEquals('', $user->getInternalId()); - $this->assertEquals(1234, $user->getExternalId()); - $this->assertEquals('', $user->getRole()); - $this->assertEquals('', $user->getUsername()); - $this->assertEquals('test@localhost', $user->getEmail()); - $this->assertEquals('gitlab_id', $user->getExternalIdColumn()); - $this->assertEquals(array(), $user->getExternalGroupIds()); - $this->assertEquals(array(), $user->getExtraAttributes()); - $this->assertFalse($user->isUserCreationAllowed()); - } - - public function testAuthenticationFailed() - { - $provider = $this - ->getMockBuilder('\Kanboard\Auth\GitlabAuth') - ->setConstructorArgs(array($this->container)) - ->setMethods(array( - 'getProfile', - )) - ->getMock(); - - $provider->expects($this->once()) - ->method('getProfile') - ->will($this->returnValue(array())); - - $this->assertFalse($provider->authenticate()); - $this->assertEquals(null, $provider->getUser()); - } - - public function testGetService() - { - $provider = new GitlabAuth($this->container); - $this->assertInstanceOf('Kanboard\Core\Http\OAuth2', $provider->getService()); - } - - public function testUnlink() - { - $userModel = new User($this->container); - $provider = new GitlabAuth($this->container); - - $this->assertEquals(2, $userModel->create(array('username' => 'test', 'gitlab_id' => '1234'))); - $this->assertNotEmpty($userModel->getByExternalId('gitlab_id', 1234)); - - $this->assertTrue($provider->unlink(2)); - $this->assertEmpty($userModel->getByExternalId('gitlab_id', 1234)); - } -} diff --git a/tests/units/Auth/GoogleAuthTest.php b/tests/units/Auth/GoogleAuthTest.php deleted file mode 100644 index b9a7d811..00000000 --- a/tests/units/Auth/GoogleAuthTest.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php - -require_once __DIR__.'/../Base.php'; - -use Kanboard\Auth\GoogleAuth; -use Kanboard\Model\User; - -class GoogleAuthTest extends Base -{ - public function testGetName() - { - $provider = new GoogleAuth($this->container); - $this->assertEquals('Google', $provider->getName()); - } - - public function testAuthenticationSuccessful() - { - $profile = array( - 'id' => 1234, - 'email' => 'test@localhost', - 'name' => 'Test', - ); - - $provider = $this - ->getMockBuilder('\Kanboard\Auth\GoogleAuth') - ->setConstructorArgs(array($this->container)) - ->setMethods(array( - 'getProfile', - )) - ->getMock(); - - $provider->expects($this->once()) - ->method('getProfile') - ->will($this->returnValue($profile)); - - $this->assertInstanceOf('Kanboard\Auth\GoogleAuth', $provider->setCode('1234')); - - $this->assertTrue($provider->authenticate()); - - $user = $provider->getUser(); - $this->assertInstanceOf('Kanboard\User\GoogleUserProvider', $user); - $this->assertEquals('Test', $user->getName()); - $this->assertEquals('', $user->getInternalId()); - $this->assertEquals(1234, $user->getExternalId()); - $this->assertEquals('', $user->getRole()); - $this->assertEquals('', $user->getUsername()); - $this->assertEquals('test@localhost', $user->getEmail()); - $this->assertEquals('google_id', $user->getExternalIdColumn()); - $this->assertEquals(array(), $user->getExternalGroupIds()); - $this->assertEquals(array(), $user->getExtraAttributes()); - $this->assertFalse($user->isUserCreationAllowed()); - } - - public function testAuthenticationFailed() - { - $provider = $this - ->getMockBuilder('\Kanboard\Auth\GoogleAuth') - ->setConstructorArgs(array($this->container)) - ->setMethods(array( - 'getProfile', - )) - ->getMock(); - - $provider->expects($this->once()) - ->method('getProfile') - ->will($this->returnValue(array())); - - $this->assertFalse($provider->authenticate()); - $this->assertEquals(null, $provider->getUser()); - } - - public function testGetService() - { - $provider = new GoogleAuth($this->container); - $this->assertInstanceOf('Kanboard\Core\Http\OAuth2', $provider->getService()); - } - - public function testUnlink() - { - $userModel = new User($this->container); - $provider = new GoogleAuth($this->container); - - $this->assertEquals(2, $userModel->create(array('username' => 'test', 'google_id' => '1234'))); - $this->assertNotEmpty($userModel->getByExternalId('google_id', 1234)); - - $this->assertTrue($provider->unlink(2)); - $this->assertEmpty($userModel->getByExternalId('google_id', 1234)); - } -} diff --git a/tests/units/Base.php b/tests/units/Base.php index 4b54cdb0..bfcef418 100644 --- a/tests/units/Base.php +++ b/tests/units/Base.php @@ -10,6 +10,7 @@ use SimpleLogger\Logger; use SimpleLogger\File; use Kanboard\Core\Session\FlashMessage; use Kanboard\Core\Session\SessionStorage; +use Kanboard\ServiceProvider\ActionProvider; class FakeHttpClient { @@ -91,7 +92,11 @@ abstract class Base extends PHPUnit_Framework_TestCase $this->container['logger'] = new Logger; $this->container['logger']->setLogger(new File($this->isWindows() ? 'NUL' : '/dev/null')); $this->container['httpClient'] = new FakeHttpClient; - $this->container['emailClient'] = $this->getMockBuilder('EmailClient')->setMethods(array('send'))->getMock(); + $this->container['emailClient'] = $this + ->getMockBuilder('\Kanboard\Core\Mail\Client') + ->setConstructorArgs(array($this->container)) + ->setMethods(array('send')) + ->getMock(); $this->container['userNotificationType'] = $this ->getMockBuilder('\Kanboard\Model\UserNotificationType') @@ -100,8 +105,9 @@ abstract class Base extends PHPUnit_Framework_TestCase ->getMock(); $this->container['sessionStorage'] = new SessionStorage; + $this->container->register(new ActionProvider); - $this->container['flash'] = function($c) { + $this->container['flash'] = function ($c) { return new FlashMessage($c); }; } diff --git a/tests/units/Core/ExternalLink/ExternalLinkManagerTest.php b/tests/units/Core/ExternalLink/ExternalLinkManagerTest.php new file mode 100644 index 00000000..d284a80b --- /dev/null +++ b/tests/units/Core/ExternalLink/ExternalLinkManagerTest.php @@ -0,0 +1,120 @@ +<?php + +require_once __DIR__.'/../../Base.php'; + +use Kanboard\Core\ExternalLink\ExternalLinkManager; +use Kanboard\ExternalLink\WebLinkProvider; +use Kanboard\ExternalLink\AttachmentLinkProvider; + +class ExternalLinkManagerTest extends Base +{ + public function testRegister() + { + $externalLinkManager = new ExternalLinkManager($this->container); + $webLinkProvider = new WebLinkProvider($this->container); + $attachmentLinkProvider = new AttachmentLinkProvider($this->container); + + $externalLinkManager->register($webLinkProvider); + $externalLinkManager->register($attachmentLinkProvider); + + $this->assertInstanceOf(get_class($webLinkProvider), $externalLinkManager->getProvider($webLinkProvider->getType())); + $this->assertInstanceOf(get_class($attachmentLinkProvider), $externalLinkManager->getProvider($attachmentLinkProvider->getType())); + } + + public function testGetProviderNotFound() + { + $externalLinkManager = new ExternalLinkManager($this->container); + + $this->setExpectedException('\Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound'); + $externalLinkManager->getProvider('not found'); + } + + public function testGetTypes() + { + $externalLinkManager = new ExternalLinkManager($this->container); + $webLinkProvider = new WebLinkProvider($this->container); + $attachmentLinkProvider = new AttachmentLinkProvider($this->container); + + $this->assertEquals(array(ExternalLinkManager::TYPE_AUTO => 'Auto'), $externalLinkManager->getTypes()); + + $externalLinkManager->register($webLinkProvider); + $externalLinkManager->register($attachmentLinkProvider); + + $this->assertEquals( + array(ExternalLinkManager::TYPE_AUTO => 'Auto', 'attachment' => 'Attachment', 'weblink' => 'Web Link'), + $externalLinkManager->getTypes() + ); + } + + public function testGetDependencyLabel() + { + $externalLinkManager = new ExternalLinkManager($this->container); + $webLinkProvider = new WebLinkProvider($this->container); + $attachmentLinkProvider = new AttachmentLinkProvider($this->container); + + $externalLinkManager->register($webLinkProvider); + $externalLinkManager->register($attachmentLinkProvider); + + $this->assertSame('Related', $externalLinkManager->getDependencyLabel($webLinkProvider->getType(), 'related')); + $this->assertSame('custom', $externalLinkManager->getDependencyLabel($webLinkProvider->getType(), 'custom')); + } + + public function testFindProviderNotFound() + { + $externalLinkManager = new ExternalLinkManager($this->container); + $webLinkProvider = new WebLinkProvider($this->container); + $attachmentLinkProvider = new AttachmentLinkProvider($this->container); + + $externalLinkManager->register($webLinkProvider); + $externalLinkManager->register($attachmentLinkProvider); + + $this->setExpectedException('\Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound'); + $externalLinkManager->find(); + } + + public function testFindProvider() + { + $externalLinkManager = new ExternalLinkManager($this->container); + $webLinkProvider = new WebLinkProvider($this->container); + $attachmentLinkProvider = new AttachmentLinkProvider($this->container); + + $externalLinkManager->register($webLinkProvider); + $externalLinkManager->register($attachmentLinkProvider); + + $externalLinkManager->setUserInput(array('text' => 'https://google.com/', 'type' => ExternalLinkManager::TYPE_AUTO)); + $this->assertSame($webLinkProvider, $externalLinkManager->find()); + + $externalLinkManager->setUserInput(array('text' => 'https://google.com/file.pdf', 'type' => ExternalLinkManager::TYPE_AUTO)); + $this->assertSame($attachmentLinkProvider, $externalLinkManager->find()); + } + + public function testFindProviderWithSelectedType() + { + $externalLinkManager = new ExternalLinkManager($this->container); + $webLinkProvider = new WebLinkProvider($this->container); + $attachmentLinkProvider = new AttachmentLinkProvider($this->container); + + $externalLinkManager->register($webLinkProvider); + $externalLinkManager->register($attachmentLinkProvider); + + $externalLinkManager->setUserInput(array('text' => 'https://google.com/', 'type' => $webLinkProvider->getType())); + $this->assertSame($webLinkProvider, $externalLinkManager->find()); + + $externalLinkManager->setUserInput(array('text' => 'https://google.com/file.pdf', 'type' => $attachmentLinkProvider->getType())); + $this->assertSame($attachmentLinkProvider, $externalLinkManager->find()); + } + + public function testFindProviderWithSelectedTypeNotFound() + { + $externalLinkManager = new ExternalLinkManager($this->container); + $webLinkProvider = new WebLinkProvider($this->container); + $attachmentLinkProvider = new AttachmentLinkProvider($this->container); + + $externalLinkManager->register($webLinkProvider); + $externalLinkManager->register($attachmentLinkProvider); + + $this->setExpectedException('\Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound'); + $externalLinkManager->setUserInput(array('text' => 'https://google.com/', 'type' => 'not found')); + $externalLinkManager->find(); + } +} diff --git a/tests/units/ExternalLink/AttachmentLinkProviderTest.php b/tests/units/ExternalLink/AttachmentLinkProviderTest.php new file mode 100644 index 00000000..fe374664 --- /dev/null +++ b/tests/units/ExternalLink/AttachmentLinkProviderTest.php @@ -0,0 +1,64 @@ +<?php + +require_once __DIR__.'/../Base.php'; + +use Kanboard\ExternalLink\AttachmentLinkProvider; + +class AttachmentLinkProviderTest extends Base +{ + public function testGetName() + { + $attachmentLinkProvider = new AttachmentLinkProvider($this->container); + $this->assertEquals('Attachment', $attachmentLinkProvider->getName()); + } + + public function testGetType() + { + $attachmentLinkProvider = new AttachmentLinkProvider($this->container); + $this->assertEquals('attachment', $attachmentLinkProvider->getType()); + } + + public function testGetDependencies() + { + $attachmentLinkProvider = new AttachmentLinkProvider($this->container); + $this->assertEquals(array('related' => 'Related'), $attachmentLinkProvider->getDependencies()); + } + + public function testMatch() + { + $attachmentLinkProvider = new AttachmentLinkProvider($this->container); + + $attachmentLinkProvider->setUserTextInput('http://kanboard.net/FILE.DOC'); + $this->assertTrue($attachmentLinkProvider->match()); + + $attachmentLinkProvider->setUserTextInput('http://kanboard.net/folder/document.PDF'); + $this->assertTrue($attachmentLinkProvider->match()); + + $attachmentLinkProvider->setUserTextInput('http://kanboard.net/archive.zip'); + $this->assertTrue($attachmentLinkProvider->match()); + + $attachmentLinkProvider->setUserTextInput(' https://kanboard.net/folder/archive.tar '); + $this->assertTrue($attachmentLinkProvider->match()); + + $attachmentLinkProvider->setUserTextInput('http:// invalid url'); + $this->assertFalse($attachmentLinkProvider->match()); + + $attachmentLinkProvider->setUserTextInput(''); + $this->assertFalse($attachmentLinkProvider->match()); + + $attachmentLinkProvider->setUserTextInput('http://kanboard.net/folder/document.html'); + $this->assertFalse($attachmentLinkProvider->match()); + + $attachmentLinkProvider->setUserTextInput('http://kanboard.net/folder/DOC.HTML'); + $this->assertFalse($attachmentLinkProvider->match()); + + $attachmentLinkProvider->setUserTextInput('http://kanboard.net/folder/document.do'); + $this->assertFalse($attachmentLinkProvider->match()); + } + + public function testGetLink() + { + $attachmentLinkProvider = new AttachmentLinkProvider($this->container); + $this->assertInstanceOf('\Kanboard\ExternalLink\AttachmentLink', $attachmentLinkProvider->getLink()); + } +} diff --git a/tests/units/ExternalLink/AttachmentLinkTest.php b/tests/units/ExternalLink/AttachmentLinkTest.php new file mode 100644 index 00000000..0211869c --- /dev/null +++ b/tests/units/ExternalLink/AttachmentLinkTest.php @@ -0,0 +1,18 @@ +<?php + +require_once __DIR__.'/../Base.php'; + +use Kanboard\ExternalLink\AttachmentLink; + +class AttachmentLinkTest extends Base +{ + public function testGetTitleFromUrl() + { + $url = 'https://kanboard.net/folder/document.pdf'; + + $link = new AttachmentLink($this->container); + $link->setUrl($url); + $this->assertEquals($url, $link->getUrl()); + $this->assertEquals('document.pdf', $link->getTitle()); + } +} diff --git a/tests/units/ExternalLink/WebLinkProviderTest.php b/tests/units/ExternalLink/WebLinkProviderTest.php new file mode 100644 index 00000000..95110ed8 --- /dev/null +++ b/tests/units/ExternalLink/WebLinkProviderTest.php @@ -0,0 +1,52 @@ +<?php + +require_once __DIR__.'/../Base.php'; + +use Kanboard\ExternalLink\WebLinkProvider; + +class WebLinkProviderTest extends Base +{ + public function testGetName() + { + $webLinkProvider = new WebLinkProvider($this->container); + $this->assertEquals('Web Link', $webLinkProvider->getName()); + } + + public function testGetType() + { + $webLinkProvider = new WebLinkProvider($this->container); + $this->assertEquals('weblink', $webLinkProvider->getType()); + } + + public function testGetDependencies() + { + $webLinkProvider = new WebLinkProvider($this->container); + $this->assertEquals(array('related' => 'Related'), $webLinkProvider->getDependencies()); + } + + public function testMatch() + { + $webLinkProvider = new WebLinkProvider($this->container); + + $webLinkProvider->setUserTextInput('http://kanboard.net/'); + $this->assertTrue($webLinkProvider->match()); + + $webLinkProvider->setUserTextInput('http://kanboard.net/mypage'); + $this->assertTrue($webLinkProvider->match()); + + $webLinkProvider->setUserTextInput(' https://kanboard.net/ '); + $this->assertTrue($webLinkProvider->match()); + + $webLinkProvider->setUserTextInput('http:// invalid url'); + $this->assertFalse($webLinkProvider->match()); + + $webLinkProvider->setUserTextInput(''); + $this->assertFalse($webLinkProvider->match()); + } + + public function testGetLink() + { + $webLinkProvider = new WebLinkProvider($this->container); + $this->assertInstanceOf('\Kanboard\ExternalLink\WebLink', $webLinkProvider->getLink()); + } +} diff --git a/tests/units/ExternalLink/WebLinkTest.php b/tests/units/ExternalLink/WebLinkTest.php new file mode 100644 index 00000000..0644620f --- /dev/null +++ b/tests/units/ExternalLink/WebLinkTest.php @@ -0,0 +1,57 @@ +<?php + +require_once __DIR__.'/../Base.php'; + +use Kanboard\ExternalLink\WebLink; + +class WebLinkTest extends Base +{ + public function testGetTitleFromHtml() + { + $url = 'http://kanboard.net/something'; + $title = 'My title'; + $html = '<!DOCTYPE html><html><head><title> '.$title.' </title></head><body>Test</body></html>'; + + $this->container['httpClient'] = $this + ->getMockBuilder('\Kanboard\Core\Http\Client') + ->setConstructorArgs(array($this->container)) + ->setMethods(array('get')) + ->getMock(); + + $webLink = new WebLink($this->container); + $webLink->setUrl($url); + $this->assertEquals($url, $webLink->getUrl()); + + $this->container['httpClient'] + ->expects($this->once()) + ->method('get') + ->with($url) + ->will($this->returnValue($html)); + + $this->assertEquals($title, $webLink->getTitle()); + } + + public function testGetTitleFromUrl() + { + $url = 'http://kanboard.net/something'; + $html = '<!DOCTYPE html><html><head></head><body>Test</body></html>'; + + $this->container['httpClient'] = $this + ->getMockBuilder('\Kanboard\Core\Http\Client') + ->setConstructorArgs(array($this->container)) + ->setMethods(array('get')) + ->getMock(); + + $webLink = new WebLink($this->container); + $webLink->setUrl($url); + $this->assertEquals($url, $webLink->getUrl()); + + $this->container['httpClient'] + ->expects($this->once()) + ->method('get') + ->with($url) + ->will($this->returnValue($html)); + + $this->assertEquals('kanboard.net/something', $webLink->getTitle()); + } +} diff --git a/tests/units/Helper/UserHelperTest.php b/tests/units/Helper/UserHelperTest.php index 67ccee73..f1099faa 100644 --- a/tests/units/Helper/UserHelperTest.php +++ b/tests/units/Helper/UserHelperTest.php @@ -36,8 +36,8 @@ class UserHelperTest extends Base ); $this->assertTrue($helper->hasAccess('user', 'create')); - $this->assertTrue($helper->hasAccess('project', 'create')); - $this->assertTrue($helper->hasAccess('project', 'createPrivate')); + $this->assertTrue($helper->hasAccess('ProjectCreation', 'create')); + $this->assertTrue($helper->hasAccess('ProjectCreation', 'createPrivate')); } public function testHasAccessForManagers() @@ -50,8 +50,8 @@ class UserHelperTest extends Base ); $this->assertFalse($helper->hasAccess('user', 'create')); - $this->assertTrue($helper->hasAccess('project', 'create')); - $this->assertTrue($helper->hasAccess('project', 'createPrivate')); + $this->assertTrue($helper->hasAccess('ProjectCreation', 'create')); + $this->assertTrue($helper->hasAccess('ProjectCreation', 'createPrivate')); } public function testHasAccessForUsers() @@ -64,8 +64,8 @@ class UserHelperTest extends Base ); $this->assertFalse($helper->hasAccess('user', 'create')); - $this->assertFalse($helper->hasAccess('project', 'create')); - $this->assertTrue($helper->hasAccess('project', 'createPrivate')); + $this->assertFalse($helper->hasAccess('ProjectCreation', 'create')); + $this->assertTrue($helper->hasAccess('ProjectCreation', 'createPrivate')); } public function testHasProjectAccessForAdmins() diff --git a/tests/units/Model/ProjectDuplicationTest.php b/tests/units/Model/ProjectDuplicationTest.php index db5da525..ee5b4ce4 100644 --- a/tests/units/Model/ProjectDuplicationTest.php +++ b/tests/units/Model/ProjectDuplicationTest.php @@ -6,8 +6,11 @@ use Kanboard\Model\Action; use Kanboard\Model\Project; use Kanboard\Model\Category; use Kanboard\Model\ProjectUserRole; +use Kanboard\Model\ProjectGroupRole; use Kanboard\Model\ProjectDuplication; use Kanboard\Model\User; +use Kanboard\Model\Group; +use Kanboard\Model\GroupMember; use Kanboard\Model\Swimlane; use Kanboard\Model\Task; use Kanboard\Model\TaskCreation; @@ -16,7 +19,14 @@ use Kanboard\Core\Security\Role; class ProjectDuplicationTest extends Base { - public function testProjectName() + public function testGetSelections() + { + $projectDuplicationModel = new ProjectDuplication($this->container); + $this->assertCount(5, $projectDuplicationModel->getOptionalSelection()); + $this->assertCount(6, $projectDuplicationModel->getPossibleSelection()); + } + + public function testGetClonedProjectName() { $pd = new ProjectDuplication($this->container); @@ -29,54 +39,142 @@ class ProjectDuplicationTest extends Base $this->assertEquals(str_repeat('a', 42).' (Clone)', $pd->getClonedProjectName(str_repeat('a', 60))); } - public function testCopyProjectWithLongName() + public function testClonePublicProject() { $p = new Project($this->container); $pd = new ProjectDuplication($this->container); - $this->assertEquals(1, $p->create(array('name' => str_repeat('a', 50)))); + $this->assertEquals(1, $p->create(array('name' => 'Public'))); $this->assertEquals(2, $pd->duplicate(1)); $project = $p->getById(2); $this->assertNotEmpty($project); - $this->assertEquals(str_repeat('a', 42).' (Clone)', $project['name']); + $this->assertEquals('Public (Clone)', $project['name']); + $this->assertEquals(1, $project['is_active']); + $this->assertEquals(0, $project['is_private']); + $this->assertEquals(0, $project['is_public']); + $this->assertEquals(0, $project['owner_id']); + $this->assertEmpty($project['token']); } - public function testClonePublicProject() + public function testClonePrivateProject() { $p = new Project($this->container); $pd = new ProjectDuplication($this->container); + $pp = new ProjectUserRole($this->container); - $this->assertEquals(1, $p->create(array('name' => 'Public'))); + $this->assertEquals(1, $p->create(array('name' => 'Private', 'is_private' => 1), 1, true)); $this->assertEquals(2, $pd->duplicate(1)); $project = $p->getById(2); $this->assertNotEmpty($project); - $this->assertEquals('Public (Clone)', $project['name']); - $this->assertEquals(0, $project['is_private']); + $this->assertEquals('Private (Clone)', $project['name']); + $this->assertEquals(1, $project['is_active']); + $this->assertEquals(1, $project['is_private']); $this->assertEquals(0, $project['is_public']); + $this->assertEquals(0, $project['owner_id']); $this->assertEmpty($project['token']); + + $this->assertEquals(Role::PROJECT_MANAGER, $pp->getUserRole(2, 1)); } - public function testClonePrivateProject() + public function testCloneSharedProject() { $p = new Project($this->container); $pd = new ProjectDuplication($this->container); - $this->assertEquals(1, $p->create(array('name' => 'Private', 'is_private' => 1), 1, true)); + $this->assertEquals(1, $p->create(array('name' => 'Shared'))); + $this->assertTrue($p->update(array('id' => 1, 'is_public' => 1, 'token' => 'test'))); + + $project = $p->getById(1); + $this->assertEquals('test', $project['token']); + $this->assertEquals(1, $project['is_public']); + $this->assertEquals(2, $pd->duplicate(1)); $project = $p->getById(2); $this->assertNotEmpty($project); - $this->assertEquals('Private (Clone)', $project['name']); - $this->assertEquals(1, $project['is_private']); + $this->assertEquals('Shared (Clone)', $project['name']); + $this->assertEquals('', $project['token']); $this->assertEquals(0, $project['is_public']); - $this->assertEmpty($project['token']); + } - $pp = new ProjectUserRole($this->container); + public function testCloneInactiveProject() + { + $p = new Project($this->container); + $pd = new ProjectDuplication($this->container); + + $this->assertEquals(1, $p->create(array('name' => 'Inactive'))); + $this->assertTrue($p->update(array('id' => 1, 'is_active' => 0))); + + $project = $p->getById(1); + $this->assertEquals(0, $project['is_active']); + + $this->assertEquals(2, $pd->duplicate(1)); + + $project = $p->getById(2); + $this->assertNotEmpty($project); + $this->assertEquals('Inactive (Clone)', $project['name']); + $this->assertEquals(1, $project['is_active']); + } + + public function testCloneProjectWithOwner() + { + $p = new Project($this->container); + $pd = new ProjectDuplication($this->container); + $projectUserRoleModel = new ProjectUserRole($this->container); + + $this->assertEquals(1, $p->create(array('name' => 'Owner'))); + + $project = $p->getById(1); + $this->assertEquals(0, $project['owner_id']); + + $this->assertEquals(2, $pd->duplicate(1, array('projectPermission'), 1)); - $this->assertEquals(array(1 => 'admin'), $pp->getAssignableUsers(1)); - $this->assertEquals(array(1 => 'admin'), $pp->getAssignableUsers(2)); + $project = $p->getById(2); + $this->assertNotEmpty($project); + $this->assertEquals('Owner (Clone)', $project['name']); + $this->assertEquals(1, $project['owner_id']); + + $this->assertEquals(Role::PROJECT_MANAGER, $projectUserRoleModel->getUserRole(2, 1)); + } + + public function testCloneProjectWithDifferentName() + { + $p = new Project($this->container); + $pd = new ProjectDuplication($this->container); + + $this->assertEquals(1, $p->create(array('name' => 'Owner'))); + + $project = $p->getById(1); + $this->assertEquals(0, $project['owner_id']); + + $this->assertEquals(2, $pd->duplicate(1, array('projectPermission'), 1, 'Foobar')); + + $project = $p->getById(2); + $this->assertNotEmpty($project); + $this->assertEquals('Foobar', $project['name']); + $this->assertEquals(1, $project['owner_id']); + } + + public function testCloneProjectAndForceItToBePrivate() + { + $p = new Project($this->container); + $pd = new ProjectDuplication($this->container); + + $this->assertEquals(1, $p->create(array('name' => 'Owner'))); + + $project = $p->getById(1); + $this->assertEquals(0, $project['owner_id']); + $this->assertEquals(0, $project['is_private']); + + $this->assertEquals(2, $pd->duplicate(1, array('projectPermission'), 1, 'Foobar', true)); + + $project = $p->getById(2); + $this->assertNotEmpty($project); + $this->assertEquals('Foobar', $project['name']); + $this->assertEquals(1, $project['owner_id']); + $this->assertEquals(1, $project['is_private']); } public function testCloneProjectWithCategories() @@ -98,16 +196,9 @@ class ProjectDuplicationTest extends Base $this->assertEquals('P1 (Clone)', $project['name']); $categories = $c->getAll(2); - $this->assertNotempty($categories); - $this->assertEquals(3, count($categories)); - - $this->assertEquals(4, $categories[0]['id']); + $this->assertCount(3, $categories); $this->assertEquals('C1', $categories[0]['name']); - - $this->assertEquals(5, $categories[1]['id']); $this->assertEquals('C2', $categories[1]['name']); - - $this->assertEquals(6, $categories[2]['id']); $this->assertEquals('C3', $categories[2]['name']); } @@ -119,28 +210,115 @@ class ProjectDuplicationTest extends Base $u = new User($this->container); $pd = new ProjectDuplication($this->container); - $this->assertEquals(2, $u->create(array('username' => 'unittest1', 'password' => 'unittest'))); - $this->assertEquals(3, $u->create(array('username' => 'unittest2', 'password' => 'unittest'))); - $this->assertEquals(4, $u->create(array('username' => 'unittest3', 'password' => 'unittest'))); + $this->assertEquals(2, $u->create(array('username' => 'user1'))); + $this->assertEquals(3, $u->create(array('username' => 'user2'))); + $this->assertEquals(4, $u->create(array('username' => 'user3'))); $this->assertEquals(1, $p->create(array('name' => 'P1'))); - $this->assertTrue($pp->addUser(1, 2, Role::PROJECT_MEMBER)); + + $this->assertTrue($pp->addUser(1, 2, Role::PROJECT_MANAGER)); $this->assertTrue($pp->addUser(1, 3, Role::PROJECT_MEMBER)); - $this->assertTrue($pp->addUser(1, 4, Role::PROJECT_MANAGER)); - $this->assertEquals(Role::PROJECT_MEMBER, $pp->getUserRole(1, 2)); - $this->assertEquals(Role::PROJECT_MEMBER, $pp->getUserRole(1, 3)); - $this->assertEquals(Role::PROJECT_MANAGER, $pp->getUserRole(1, 4)); + $this->assertTrue($pp->addUser(1, 4, Role::PROJECT_VIEWER)); $this->assertEquals(2, $pd->duplicate(1)); + $this->assertCount(3, $pp->getUsers(2)); + $this->assertEquals(Role::PROJECT_MANAGER, $pp->getUserRole(2, 2)); + $this->assertEquals(Role::PROJECT_MEMBER, $pp->getUserRole(2, 3)); + $this->assertEquals(Role::PROJECT_VIEWER, $pp->getUserRole(2, 4)); + } + + public function testCloneProjectWithUsersAndOverrideOwner() + { + $p = new Project($this->container); + $c = new Category($this->container); + $pp = new ProjectUserRole($this->container); + $u = new User($this->container); + $pd = new ProjectDuplication($this->container); + + $this->assertEquals(2, $u->create(array('username' => 'user1'))); + $this->assertEquals(1, $p->create(array('name' => 'P1'), 2)); + + $project = $p->getById(1); + $this->assertEquals(2, $project['owner_id']); + + $this->assertTrue($pp->addUser(1, 2, Role::PROJECT_MANAGER)); + $this->assertTrue($pp->addUser(1, 1, Role::PROJECT_MEMBER)); + + $this->assertEquals(2, $pd->duplicate(1, array('projectPermission'), 1)); + + $this->assertCount(2, $pp->getUsers(2)); + $this->assertEquals(Role::PROJECT_MANAGER, $pp->getUserRole(2, 2)); + $this->assertEquals(Role::PROJECT_MANAGER, $pp->getUserRole(2, 1)); + $project = $p->getById(2); - $this->assertNotEmpty($project); - $this->assertEquals('P1 (Clone)', $project['name']); + $this->assertEquals(1, $project['owner_id']); + } - $this->assertEquals(3, count($pp->getUsers(2))); - $this->assertEquals(Role::PROJECT_MEMBER, $pp->getUserRole(2, 2)); - $this->assertEquals(Role::PROJECT_MEMBER, $pp->getUserRole(2, 3)); - $this->assertEquals(Role::PROJECT_MANAGER, $pp->getUserRole(2, 4)); + public function testCloneTeamProjectToPrivatProject() + { + $p = new Project($this->container); + $c = new Category($this->container); + $pp = new ProjectUserRole($this->container); + $u = new User($this->container); + $pd = new ProjectDuplication($this->container); + + $this->assertEquals(2, $u->create(array('username' => 'user1'))); + $this->assertEquals(3, $u->create(array('username' => 'user2'))); + $this->assertEquals(1, $p->create(array('name' => 'P1'), 2)); + + $project = $p->getById(1); + $this->assertEquals(2, $project['owner_id']); + $this->assertEquals(0, $project['is_private']); + + $this->assertTrue($pp->addUser(1, 2, Role::PROJECT_MANAGER)); + $this->assertTrue($pp->addUser(1, 1, Role::PROJECT_MEMBER)); + + $this->assertEquals(2, $pd->duplicate(1, array('projectPermission'), 3, 'My private project', true)); + + $this->assertCount(1, $pp->getUsers(2)); + $this->assertEquals(Role::PROJECT_MANAGER, $pp->getUserRole(2, 3)); + + $project = $p->getById(2); + $this->assertEquals(3, $project['owner_id']); + $this->assertEquals(1, $project['is_private']); + } + + public function testCloneProjectWithGroups() + { + $p = new Project($this->container); + $c = new Category($this->container); + $pd = new ProjectDuplication($this->container); + $userModel = new User($this->container); + $groupModel = new Group($this->container); + $groupMemberModel = new GroupMember($this->container); + $projectGroupRoleModel = new ProjectGroupRole($this->container); + $projectUserRoleModel = new ProjectUserRole($this->container); + + $this->assertEquals(1, $p->create(array('name' => 'P1'))); + + $this->assertEquals(1, $groupModel->create('G1')); + $this->assertEquals(2, $groupModel->create('G2')); + $this->assertEquals(3, $groupModel->create('G3')); + + $this->assertEquals(2, $userModel->create(array('username' => 'user1'))); + $this->assertEquals(3, $userModel->create(array('username' => 'user2'))); + $this->assertEquals(4, $userModel->create(array('username' => 'user3'))); + + $this->assertTrue($groupMemberModel->addUser(1, 2)); + $this->assertTrue($groupMemberModel->addUser(2, 3)); + $this->assertTrue($groupMemberModel->addUser(3, 4)); + + $this->assertTrue($projectGroupRoleModel->addGroup(1, 1, Role::PROJECT_MANAGER)); + $this->assertTrue($projectGroupRoleModel->addGroup(1, 2, Role::PROJECT_MEMBER)); + $this->assertTrue($projectGroupRoleModel->addGroup(1, 3, Role::PROJECT_VIEWER)); + + $this->assertEquals(2, $pd->duplicate(1)); + + $this->assertCount(3, $projectGroupRoleModel->getGroups(2)); + $this->assertEquals(Role::PROJECT_MANAGER, $projectUserRoleModel->getUserRole(2, 2)); + $this->assertEquals(Role::PROJECT_MEMBER, $projectUserRoleModel->getUserRole(2, 3)); + $this->assertEquals(Role::PROJECT_VIEWER, $projectUserRoleModel->getUserRole(2, 4)); } public function testCloneProjectWithActionTaskAssignCurrentUser() @@ -199,7 +377,7 @@ class ProjectDuplicationTest extends Base $this->assertEquals(5, $actions[0]['params']['category_id']); } - public function testCloneProjectWithSwimlanesAndTasks() + public function testCloneProjectWithSwimlanes() { $p = new Project($this->container); $pd = new ProjectDuplication($this->container); @@ -207,31 +385,22 @@ class ProjectDuplicationTest extends Base $tc = new TaskCreation($this->container); $tf = new TaskFinder($this->container); - $this->assertEquals(1, $p->create(array('name' => 'P1'))); + $this->assertEquals(1, $p->create(array('name' => 'P1', 'default_swimlane' => 'New Default'))); // create initial swimlanes $this->assertEquals(1, $s->create(array('project_id' => 1, 'name' => 'S1'))); $this->assertEquals(2, $s->create(array('project_id' => 1, 'name' => 'S2'))); $this->assertEquals(3, $s->create(array('project_id' => 1, 'name' => 'S3'))); - $default_swimlane1 = $s->getDefault(1); - $default_swimlane1['default_swimlane'] = 'New Default'; - - $this->assertTrue($s->updateDefault($default_swimlane1)); + // create initial tasks + $this->assertEquals(1, $tc->create(array('title' => 'T0', 'project_id' => 1, 'swimlane_id' => 0))); + $this->assertEquals(2, $tc->create(array('title' => 'T1', 'project_id' => 1, 'swimlane_id' => 1))); + $this->assertEquals(3, $tc->create(array('title' => 'T2', 'project_id' => 1, 'swimlane_id' => 2))); + $this->assertEquals(4, $tc->create(array('title' => 'T3', 'project_id' => 1, 'swimlane_id' => 3))); - //create initial tasks - $this->assertEquals(1, $tc->create(array('title' => 'T1', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 1))); - $this->assertEquals(2, $tc->create(array('title' => 'T2', 'project_id' => 1, 'column_id' => 2, 'owner_id' => 1))); - $this->assertEquals(3, $tc->create(array('title' => 'T3', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); - - $this->assertNotFalse($pd->duplicate(1, array('category', 'action', 'swimlane', 'task'))); - $project = $p->getByName('P1 (Clone)'); - $this->assertNotFalse($project); - $project_id = $project['id']; - - // Check if Swimlanes have been duplicated - $swimlanes = $s->getAll($project_id); + $this->assertEquals(2, $pd->duplicate(1, array('category', 'swimlane'))); + $swimlanes = $s->getAll(2); $this->assertCount(3, $swimlanes); $this->assertEquals(4, $swimlanes[0]['id']); $this->assertEquals('S1', $swimlanes[0]['name']); @@ -239,29 +408,15 @@ class ProjectDuplicationTest extends Base $this->assertEquals('S2', $swimlanes[1]['name']); $this->assertEquals(6, $swimlanes[2]['id']); $this->assertEquals('S3', $swimlanes[2]['name']); - $new_default = $s->getDefault($project_id); - $this->assertEquals('New Default', $new_default['default_swimlane']); - - // Check if Tasks have been duplicated - $tasks = $tf->getAll($project_id); + $swimlane = $s->getDefault(2); + $this->assertEquals('New Default', $swimlane['default_swimlane']); - $this->assertCount(3, $tasks); - // $this->assertEquals(4, $tasks[0]['id']); - $this->assertEquals('T1', $tasks[0]['title']); - // $this->assertEquals(5, $tasks[1]['id']); - $this->assertEquals('T2', $tasks[1]['title']); - // $this->assertEquals(6, $tasks[2]['id']); - $this->assertEquals('T3', $tasks[2]['title']); - - $p->remove($project_id); - - $this->assertFalse($p->exists($project_id)); - $this->assertCount(0, $s->getAll($project_id)); - $this->assertCount(0, $tf->getAll($project_id)); + // Check if tasks are NOT been duplicated + $this->assertCount(0, $tf->getAll(2)); } - public function testCloneProjectWithSwimlanes() + public function testCloneProjectWithTasks() { $p = new Project($this->container); $pd = new ProjectDuplication($this->container); @@ -271,43 +426,22 @@ class ProjectDuplicationTest extends Base $this->assertEquals(1, $p->create(array('name' => 'P1'))); - // create initial swimlanes - $this->assertEquals(1, $s->create(array('project_id' => 1, 'name' => 'S1'))); - $this->assertEquals(2, $s->create(array('project_id' => 1, 'name' => 'S2'))); - $this->assertEquals(3, $s->create(array('project_id' => 1, 'name' => 'S3'))); + // create initial tasks + $this->assertEquals(1, $tc->create(array('title' => 'T1', 'project_id' => 1, 'column_id' => 1))); + $this->assertEquals(2, $tc->create(array('title' => 'T2', 'project_id' => 1, 'column_id' => 2))); + $this->assertEquals(3, $tc->create(array('title' => 'T3', 'project_id' => 1, 'column_id' => 3))); - $default_swimlane1 = $s->getDefault(1); - $default_swimlane1['default_swimlane'] = 'New Default'; - - $this->assertTrue($s->updateDefault($default_swimlane1)); - - //create initial tasks - $this->assertEquals(1, $tc->create(array('title' => 'T1', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 1))); - $this->assertEquals(2, $tc->create(array('title' => 'T2', 'project_id' => 1, 'column_id' => 2, 'owner_id' => 1))); - $this->assertEquals(3, $tc->create(array('title' => 'T3', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); - - $this->assertNotFalse($pd->duplicate(1, array('category', 'action', 'swimlane'))); - $project = $p->getByName('P1 (Clone)'); - $this->assertNotFalse($project); - $project_id = $project['id']; - - $swimlanes = $s->getAll($project_id); - - $this->assertCount(3, $swimlanes); - $this->assertEquals(4, $swimlanes[0]['id']); - $this->assertEquals('S1', $swimlanes[0]['name']); - $this->assertEquals(5, $swimlanes[1]['id']); - $this->assertEquals('S2', $swimlanes[1]['name']); - $this->assertEquals(6, $swimlanes[2]['id']); - $this->assertEquals('S3', $swimlanes[2]['name']); - $new_default = $s->getDefault($project_id); - $this->assertEquals('New Default', $new_default['default_swimlane']); + $this->assertEquals(2, $pd->duplicate(1, array('category', 'action', 'task'))); - // Check if Tasks have NOT been duplicated - $this->assertCount(0, $tf->getAll($project_id)); + // Check if Tasks have been duplicated + $tasks = $tf->getAll(2); + $this->assertCount(3, $tasks); + $this->assertEquals('T1', $tasks[0]['title']); + $this->assertEquals('T2', $tasks[1]['title']); + $this->assertEquals('T3', $tasks[2]['title']); } - public function testCloneProjectWithTasks() + public function testCloneProjectWithSwimlanesAndTasks() { $p = new Project($this->container); $pd = new ProjectDuplication($this->container); @@ -315,40 +449,39 @@ class ProjectDuplicationTest extends Base $tc = new TaskCreation($this->container); $tf = new TaskFinder($this->container); - $this->assertEquals(1, $p->create(array('name' => 'P1'))); + $this->assertEquals(1, $p->create(array('name' => 'P1', 'default_swimlane' => 'New Default'))); // create initial swimlanes $this->assertEquals(1, $s->create(array('project_id' => 1, 'name' => 'S1'))); $this->assertEquals(2, $s->create(array('project_id' => 1, 'name' => 'S2'))); $this->assertEquals(3, $s->create(array('project_id' => 1, 'name' => 'S3'))); - $default_swimlane1 = $s->getDefault(1); - $default_swimlane1['default_swimlane'] = 'New Default'; - - $this->assertTrue($s->updateDefault($default_swimlane1)); - - //create initial tasks + // create initial tasks $this->assertEquals(1, $tc->create(array('title' => 'T1', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 1))); $this->assertEquals(2, $tc->create(array('title' => 'T2', 'project_id' => 1, 'column_id' => 2, 'owner_id' => 1))); $this->assertEquals(3, $tc->create(array('title' => 'T3', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); - $this->assertNotFalse($pd->duplicate(1, array('category', 'action', 'task'))); - $project = $p->getByName('P1 (Clone)'); - $this->assertNotFalse($project); - $project_id = $project['id']; + $this->assertEquals(2, $pd->duplicate(1, array('projectPermission', 'swimlane', 'task'))); + + // Check if Swimlanes have been duplicated + $swimlanes = $s->getAll(2); + $this->assertCount(3, $swimlanes); + $this->assertEquals(4, $swimlanes[0]['id']); + $this->assertEquals('S1', $swimlanes[0]['name']); + $this->assertEquals(5, $swimlanes[1]['id']); + $this->assertEquals('S2', $swimlanes[1]['name']); + $this->assertEquals(6, $swimlanes[2]['id']); + $this->assertEquals('S3', $swimlanes[2]['name']); - // Check if Swimlanes have NOT been duplicated - $this->assertCount(0, $s->getAll($project_id)); + $swimlane = $s->getDefault(2); + $this->assertEquals('New Default', $swimlane['default_swimlane']); // Check if Tasks have been duplicated - $tasks = $tf->getAll($project_id); + $tasks = $tf->getAll(2); $this->assertCount(3, $tasks); - //$this->assertEquals(4, $tasks[0]['id']); $this->assertEquals('T1', $tasks[0]['title']); - //$this->assertEquals(5, $tasks[1]['id']); $this->assertEquals('T2', $tasks[1]['title']); - //$this->assertEquals(6, $tasks[2]['id']); $this->assertEquals('T3', $tasks[2]['title']); } } diff --git a/tests/units/Model/SubtaskTest.php b/tests/units/Model/SubtaskTest.php index 9be2dff4..34599004 100644 --- a/tests/units/Model/SubtaskTest.php +++ b/tests/units/Model/SubtaskTest.php @@ -159,7 +159,7 @@ class SubtaskTest extends Base $this->assertEquals(0, $subtask['user_id']); $this->assertEquals(1, $subtask['task_id']); - $this->assertTrue($s->toggleStatus(1)); + $this->assertEquals(Subtask::STATUS_INPROGRESS, $s->toggleStatus(1)); $subtask = $s->getById(1); $this->assertNotEmpty($subtask); @@ -167,7 +167,7 @@ class SubtaskTest extends Base $this->assertEquals(0, $subtask['user_id']); $this->assertEquals(1, $subtask['task_id']); - $this->assertTrue($s->toggleStatus(1)); + $this->assertEquals(Subtask::STATUS_DONE, $s->toggleStatus(1)); $subtask = $s->getById(1); $this->assertNotEmpty($subtask); @@ -175,7 +175,7 @@ class SubtaskTest extends Base $this->assertEquals(0, $subtask['user_id']); $this->assertEquals(1, $subtask['task_id']); - $this->assertTrue($s->toggleStatus(1)); + $this->assertEquals(Subtask::STATUS_TODO, $s->toggleStatus(1)); $subtask = $s->getById(1); $this->assertNotEmpty($subtask); @@ -205,7 +205,7 @@ class SubtaskTest extends Base // Set the current logged user $this->container['sessionStorage']->user = array('id' => 1); - $this->assertTrue($s->toggleStatus(1)); + $this->assertEquals(Subtask::STATUS_INPROGRESS, $s->toggleStatus(1)); $subtask = $s->getById(1); $this->assertNotEmpty($subtask); @@ -213,7 +213,7 @@ class SubtaskTest extends Base $this->assertEquals(1, $subtask['user_id']); $this->assertEquals(1, $subtask['task_id']); - $this->assertTrue($s->toggleStatus(1)); + $this->assertEquals(Subtask::STATUS_DONE, $s->toggleStatus(1)); $subtask = $s->getById(1); $this->assertNotEmpty($subtask); @@ -221,7 +221,7 @@ class SubtaskTest extends Base $this->assertEquals(1, $subtask['user_id']); $this->assertEquals(1, $subtask['task_id']); - $this->assertTrue($s->toggleStatus(1)); + $this->assertEquals(Subtask::STATUS_TODO, $s->toggleStatus(1)); $subtask = $s->getById(1); $this->assertNotEmpty($subtask); diff --git a/tests/units/Model/TaskExternalLinkTest.php b/tests/units/Model/TaskExternalLinkTest.php new file mode 100644 index 00000000..28ccab83 --- /dev/null +++ b/tests/units/Model/TaskExternalLinkTest.php @@ -0,0 +1,123 @@ +<?php + +require_once __DIR__.'/../Base.php'; + +use Kanboard\Model\TaskCreation; +use Kanboard\Model\Project; +use Kanboard\Model\TaskExternalLink; +use Kanboard\Core\ExternalLink\ExternalLinkManager; +use Kanboard\ExternalLink\WebLinkProvider; + +class TaskExternalLinkTest extends Base +{ + public function testCreate() + { + $projectModel = new Project($this->container); + $taskCreationModel = new TaskCreation($this->container); + $taskExternalLinkModel = new TaskExternalLink($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'Test', 'project_id' => 1))); + $this->assertEquals(1, $taskExternalLinkModel->create(array('task_id' => 1, 'id' => '', 'url' => 'http://kanboard.net/', 'title' => 'My website', 'link_type' => 'weblink', 'dependency' => 'related'))); + + $link = $taskExternalLinkModel->getById(1); + $this->assertNotEmpty($link); + $this->assertEquals('My website', $link['title']); + $this->assertEquals('http://kanboard.net/', $link['url']); + $this->assertEquals('related', $link['dependency']); + $this->assertEquals('weblink', $link['link_type']); + $this->assertEquals(0, $link['creator_id']); + $this->assertEquals(time(), $link['date_modification'], '', 2); + $this->assertEquals(time(), $link['date_creation'], '', 2); + } + + public function testCreateWithUserSession() + { + $this->container['sessionStorage']->user = array('id' => 1); + + $projectModel = new Project($this->container); + $taskCreationModel = new TaskCreation($this->container); + $taskExternalLinkModel = new TaskExternalLink($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'Test', 'project_id' => 1))); + $this->assertEquals(1, $taskExternalLinkModel->create(array('task_id' => 1, 'id' => '', 'url' => 'http://kanboard.net/', 'title' => 'My website', 'link_type' => 'weblink', 'dependency' => 'related'))); + + $link = $taskExternalLinkModel->getById(1); + $this->assertNotEmpty($link); + $this->assertEquals('My website', $link['title']); + $this->assertEquals('http://kanboard.net/', $link['url']); + $this->assertEquals('related', $link['dependency']); + $this->assertEquals('weblink', $link['link_type']); + $this->assertEquals(1, $link['creator_id']); + $this->assertEquals(time(), $link['date_modification'], '', 2); + $this->assertEquals(time(), $link['date_creation'], '', 2); + } + + public function testModification() + { + $projectModel = new Project($this->container); + $taskCreationModel = new TaskCreation($this->container); + $taskExternalLinkModel = new TaskExternalLink($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'Test', 'project_id' => 1))); + $this->assertEquals(1, $taskExternalLinkModel->create(array('task_id' => 1, 'id' => '', 'url' => 'http://kanboard.net/', 'title' => 'My website', 'link_type' => 'weblink', 'dependency' => 'related'))); + + sleep(1); + + $this->assertTrue($taskExternalLinkModel->update(array('id' => 1, 'url' => 'https://kanboard.net/'))); + + $link = $taskExternalLinkModel->getById(1); + $this->assertNotEmpty($link); + $this->assertEquals('https://kanboard.net/', $link['url']); + $this->assertEquals(time(), $link['date_modification'], '', 2); + } + + public function testRemove() + { + $projectModel = new Project($this->container); + $taskCreationModel = new TaskCreation($this->container); + $taskExternalLinkModel = new TaskExternalLink($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'Test', 'project_id' => 1))); + $this->assertEquals(1, $taskExternalLinkModel->create(array('task_id' => 1, 'id' => '', 'url' => 'http://kanboard.net/', 'title' => 'My website', 'link_type' => 'weblink', 'dependency' => 'related'))); + + $this->assertTrue($taskExternalLinkModel->remove(1)); + $this->assertFalse($taskExternalLinkModel->remove(1)); + + $this->assertEmpty($taskExternalLinkModel->getById(1)); + } + + public function testGetAll() + { + $this->container['sessionStorage']->user = array('id' => 1); + $this->container['externalLinkManager'] = new ExternalLinkManager($this->container); + + $projectModel = new Project($this->container); + $taskCreationModel = new TaskCreation($this->container); + $taskExternalLinkModel = new TaskExternalLink($this->container); + $webLinkProvider = new WebLinkProvider($this->container); + + $this->container['externalLinkManager']->register($webLinkProvider); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'Test', 'project_id' => 1))); + $this->assertEquals(1, $taskExternalLinkModel->create(array('task_id' => 1, 'url' => 'https://miniflux.net/', 'title' => 'MX', 'link_type' => 'weblink', 'dependency' => 'related'))); + $this->assertEquals(2, $taskExternalLinkModel->create(array('task_id' => 1, 'url' => 'http://kanboard.net/', 'title' => 'KB', 'link_type' => 'weblink', 'dependency' => 'related'))); + + $links = $taskExternalLinkModel->getAll(1); + $this->assertCount(2, $links); + $this->assertEquals('KB', $links[0]['title']); + $this->assertEquals('MX', $links[1]['title']); + $this->assertEquals('Web Link', $links[0]['type']); + $this->assertEquals('Web Link', $links[1]['type']); + $this->assertEquals('Related', $links[0]['dependency_label']); + $this->assertEquals('Related', $links[1]['dependency_label']); + $this->assertEquals('admin', $links[0]['creator_username']); + $this->assertEquals('admin', $links[1]['creator_username']); + $this->assertEquals('', $links[0]['creator_name']); + $this->assertEquals('', $links[1]['creator_name']); + } +} diff --git a/tests/units/Validator/ExternalLinkValidatorTest.php b/tests/units/Validator/ExternalLinkValidatorTest.php new file mode 100644 index 00000000..b41b779a --- /dev/null +++ b/tests/units/Validator/ExternalLinkValidatorTest.php @@ -0,0 +1,63 @@ +<?php + +require_once __DIR__.'/../Base.php'; + +use Kanboard\Validator\ExternalLinkValidator; + +class ExternalLinkValidatorTest extends Base +{ + public function testValidateCreation() + { + $validator = new ExternalLinkValidator($this->container); + + $result = $validator->validateCreation(array('url' => 'http://somewhere', 'task_id' => 1, 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related')); + $this->assertTrue($result[0]); + + $result = $validator->validateCreation(array('url' => 'http://somewhere', 'task_id' => 'abc', 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related')); + $this->assertFalse($result[0]); + + $result = $validator->validateCreation(array('url' => 'http://somewhere', 'task_id' => 1, 'title' => 'Title', 'link_type' => 'weblink')); + $this->assertFalse($result[0]); + + $result = $validator->validateCreation(array('url' => 'http://somewhere', 'task_id' => 1, 'title' => 'Title', 'dependency' => 'related')); + $this->assertFalse($result[0]); + + $result = $validator->validateCreation(array('url' => 'http://somewhere', 'task_id' => 1, 'link_type' => 'weblink', 'dependency' => 'related')); + $this->assertFalse($result[0]); + + $result = $validator->validateCreation(array('url' => 'http://somewhere', 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related')); + $this->assertFalse($result[0]); + + $result = $validator->validateCreation(array('task_id' => 1, 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related')); + $this->assertFalse($result[0]); + } + + public function testValidateModification() + { + $validator = new ExternalLinkValidator($this->container); + + $result = $validator->validateModification(array('id' => 1, 'url' => 'http://somewhere', 'task_id' => 1, 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related')); + $this->assertTrue($result[0]); + + $result = $validator->validateModification(array('id' => 1, 'url' => 'http://somewhere', 'task_id' => 'abc', 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related')); + $this->assertFalse($result[0]); + + $result = $validator->validateModification(array('id' => 1, 'url' => 'http://somewhere', 'task_id' => 1, 'title' => 'Title', 'link_type' => 'weblink')); + $this->assertFalse($result[0]); + + $result = $validator->validateModification(array('id' => 1, 'url' => 'http://somewhere', 'task_id' => 1, 'title' => 'Title', 'dependency' => 'related')); + $this->assertFalse($result[0]); + + $result = $validator->validateModification(array('id' => 1, 'url' => 'http://somewhere', 'task_id' => 1, 'link_type' => 'weblink', 'dependency' => 'related')); + $this->assertFalse($result[0]); + + $result = $validator->validateModification(array('id' => 1, 'url' => 'http://somewhere', 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related')); + $this->assertFalse($result[0]); + + $result = $validator->validateModification(array('id' => 1, 'task_id' => 1, 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related')); + $this->assertFalse($result[0]); + + $result = $validator->validateModification(array('url' => 'http://somewhere', 'task_id' => 1, 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related')); + $this->assertFalse($result[0]); + } +} |