1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/kanboard_ynh.git synced 2024-09-03 19:36:17 +02:00

Update to Kanboard v1.0.19

This commit is contained in:
mbugeia 2015-10-14 15:56:23 +02:00
parent b4e4669ca3
commit 641c152ed9
367 changed files with 12063 additions and 7777 deletions

View file

@ -6,6 +6,9 @@ define('DEBUG', false);
// Debug file path
define('DEBUG_FILE', __DIR__.'/data/debug.log');
// Plugins directory
define('PLUGINS_DIR', 'data/plugins');
// Folder for uploaded files, don't forget the trailing slash
define('FILES_DIR', 'data/files/');
@ -65,20 +68,20 @@ define('LDAP_SERVER', '');
// LDAP server port (389 by default)
define('LDAP_PORT', 389);
// By default, require certificate to be verified for ldaps:// style URL. Set to false to skip the verification.
// By default, require certificate to be verified for ldaps:// style URL. Set to false to skip the verification
define('LDAP_SSL_VERIFY', true);
// Enable LDAP START_TLS
define('LDAP_START_TLS', false);
// LDAP bind type: "anonymous", "user" (use the given user/password from the form) and "proxy" (a specific user to browse the LDAP directory)
// LDAP bind type: "anonymous", "user" or "proxy"
define('LDAP_BIND_TYPE', 'anonymous');
// LDAP username to connect with. null for anonymous bind (by default).
// Or for user bind type, you can use a pattern: %s@kanboard.local
// LDAP username to use with proxy mode
// LDAP username pattern to use with user mode
define('LDAP_USERNAME', null);
// LDAP password to connect with. null for anonymous bind (by default).
// LDAP password to use for proxy mode
define('LDAP_PASSWORD', null);
// LDAP account base, i.e. root of all user account
@ -90,16 +93,27 @@ define('LDAP_ACCOUNT_BASE', '');
// Example for OpenLDAP: 'uid=%s'
define('LDAP_USER_PATTERN', '');
// Name of an attribute of the user account object which should be used as the full name of the user.
// Name of an attribute of the user account object which should be used as the full name of the user
define('LDAP_ACCOUNT_FULLNAME', 'displayname');
// Name of an attribute of the user account object which should be used as the email of the user.
// Name of an attribute of the user account object which should be used as the email of the user
define('LDAP_ACCOUNT_EMAIL', 'mail');
// Name of an attribute of the user account object which should be used as the id of the user.
// Name of an attribute of the user account object which should be used as the id of the user. (optional)
// Example for ActiveDirectory: 'samaccountname'
// Example for OpenLDAP: 'uid'
define('LDAP_ACCOUNT_ID', 'samaccountname');
define('LDAP_ACCOUNT_ID', '');
// LDAP Attribute for group membership
define('LDAP_ACCOUNT_MEMBEROF', 'memberof');
// DN for administrators
// Example: CN=Kanboard Admins,CN=Users,DC=kanboard,DC=local
define('LDAP_GROUP_ADMIN_DN', '');
// DN for project administrators
// Example: CN=Kanboard Project Admins,CN=Users,DC=kanboard,DC=local
define('LDAP_GROUP_PROJECT_ADMIN_DN', '');
// By default Kanboard lowercase the ldap username to avoid duplicate users (the database is case sensitive)
// Set to true if you want to preserve the case
@ -174,6 +188,9 @@ define('ENABLE_HSTS', true);
// Enable or disable "X-Frame-Options: DENY" HTTP header
define('ENABLE_XFRAME', true);
// Enable syslog logging
define('ENABLE_SYSLOG', true);
// Escape html inside markdown text
define('MARKDOWN_ESCAPE_HTML', true);
@ -198,3 +215,10 @@ define('BRUTEFORCE_LOCKDOWN_DURATION', 15);
// Session duration in second (0 = until the browser is closed)
// See http://php.net/manual/en/session.configuration.php#ini.session.cookie-lifetime
define('SESSION_DURATION', 1);
// HTTP client proxy
define('HTTP_PROXY_HOSTNAME', '');
define('HTTP_PROXY_PORT', '3128');
define('HTTP_PROXY_USERNAME', '');
define('HTTP_PROXY_PASSWORD', '');

View file

@ -7,3 +7,20 @@
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
</IfModule>
<FilesMatch "(kanboard|config.php|config.default.php)">
<IfModule mod_version.c>
<IfVersion >= 2.3>
Require all denied
</IfVersion>
<IfVersion < 2.3>
Order allow,deny
Deny from all
</IfVersion>
</IfModule>
<IfModule !mod_version.c>
Order allow,deny
Deny from all
</IfModule>
</FilesMatch>

View file

@ -1,3 +1,67 @@
Version 1.0.19
--------------
New features:
* Added web notifications
* Added LDAP group sync
* Added swimlane description
* New plugin system (alpha)
* Added Bahasa Indonesia translation
* Added API procedures: getMyOverdueTasks, getOverdueTasksByProject and GetMyProjects
* Added user API access for procedure getProjectActivity()
* Added config parameter to enable/disable Syslog
* Added custom filters
* Added http client proxy support
Core functionalities moved to plugins:
* Budget planning: https://github.com/kanboard/plugin-budget
* SubtaskForecast: https://github.com/kanboard/plugin-subtask-forecast
* Timetable: https://github.com/kanboard/plugin-timetable
Improvements:
* When duplicating a task redirect to the new task
* Include more shortcut links into the view "My projects"
* Duplicate a project with tasks will copy the new tasks in the same columns
* Offer alternative method to create Mysql and Postgres databases (import sql dump)
* Make sure there is always a trailing slash for application_url
* Do not show the checkbox "Show default swimlane" when there is no active swimlanes
* Append filters instead of replacing value for users and categories dropdowns
* Do not show empty swimlanes in public view
* Change swimlane layout to save space on the screen
* Add the possibility to set/unset max column height (column scrolling)
* Show "Open this task" in dropdown menu for closed tasks
* Show assignee on card only when someone is assigned (hide nobody text)
* Highlight selected item in dropdown menus
* Gantt chart: change bar color according to task progress
* Replace color dropdown by color picker in task forms
* Creating another task stay in the popover (no full page refresh anymore)
* Avoid scrollbar in Gantt chart for row title on Windows platform
* Remove unnecessary margin for calendar header
* Show localized documentation if available
* Add event subtask.delete
* Add abstract storage layer
* Add abstract cache layer
* Add Docker tag for stable version
Others:
* Data directory permissions are not checked anymore
* Data directory is not mandatory anymore for people that use a remote database and remote object storage
Bug fixes:
* Fix typo in template that prevent the Gitlab OAuth link to be displayed
* Fix Markdown preview links focus
* Avoid dropdown menu to be truncated inside a column with scrolling
* Deleting subtask doesn't update task time tracking
* Fix Mysql error about gitlab_id when creating remote user
* Fix subtask timer bug (event called recursively)
* Fix Postgres issue "Cardinality violation" when there is multiple "is_milestone_of" links
* Fix issue with due date greater than year 2038
Version 1.0.18
--------------

View file

@ -1 +1,7 @@
Deny from all
<IfVersion >= 2.3>
Require all denied
</IfVersion>
<IfVersion < 2.3>
Order allow,deny
Deny from all
</IfVersion>

View file

@ -126,6 +126,17 @@ abstract class Base extends \Core\Base
return get_called_class();
}
/**
* Get project id
*
* @access public
* @return integer
*/
public function getProjectId()
{
return $this->project_id;
}
/**
* Set an user defined parameter
*

View file

@ -64,7 +64,9 @@ class TaskDuplicateAnotherProject extends Base
*/
public function doAction(array $data)
{
return (bool) $this->taskDuplication->duplicateToProject($data['task_id'], $this->getParam('project_id'));
$destination_column_id = $this->board->getFirstColumn($this->getParam('project_id'));
return (bool) $this->taskDuplication->duplicateToProject($data['task_id'], $this->getParam('project_id'), null, $destination_column_id);
}
/**

View file

@ -19,6 +19,8 @@ abstract class Base extends \Core\Base
'getMyActivityStream',
'createMyPrivateProject',
'getMyProjectsList',
'getMyProjects',
'getMyOverdueTasks',
);
private $both_allowed_procedures = array(
@ -37,6 +39,8 @@ abstract class Base extends \Core\Base
'createTask',
'updateTask',
'getBoard',
'getProjectActivity',
'getOverdueTasksByProject',
);
public function checkProcedurePermission($is_user, $procedure)
@ -50,6 +54,8 @@ abstract class Base extends \Core\Base
else if (! $is_user && ! $is_both_procedure && $is_user_procedure) {
throw new AccessDeniedException('Permission denied');
}
$this->logger->debug('API call: '.$procedure);
}
public function checkProjectPermission($project_id)

View file

@ -2,6 +2,8 @@
namespace Api;
use Core\ObjectStorage\ObjectStorageException;
/**
* File API controller
*
@ -22,16 +24,17 @@ class File extends \Core\Base
public function downloadFile($file_id)
{
try {
$file = $this->file->getById($file_id);
if (! empty($file)) {
$filename = FILES_DIR.$file['path'];
if (file_exists($filename)) {
return base64_encode(file_get_contents($filename));
return base64_encode($this->objectStorage->get($file['path']));
}
}
catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
}
return '';
}

View file

@ -33,7 +33,8 @@ class Me extends Base
public function getMyActivityStream()
{
return $this->projectActivity->getProjects($this->projectPermission->getActiveMemberProjectIds($this->userSession->getId()), 100);
$project_ids = $this->projectPermission->getActiveMemberProjectIds($this->userSession->getId());
return $this->projectActivity->getProjects($project_ids, 100);
}
public function createMyPrivateProject($name, $description = null)
@ -52,4 +53,17 @@ class Me extends Base
{
return $this->projectPermission->getMemberProjects($this->userSession->getId());
}
public function getMyOverdueTasks()
{
return $this->taskFinder->getOverdueTasksByUser($this->userSession->getId());
}
public function getMyProjects()
{
$project_ids = $this->projectPermission->getActiveMemberProjectIds($this->userSession->getId());
$projects = $this->project->getAllByIds($project_ids);
return $this->formatProjects($projects);
}
}

View file

@ -58,6 +58,7 @@ class Project extends Base
public function getProjectActivity($project_id)
{
$this->checkProjectPermission($project_id);
return $this->projectActivity->getProject($project_id);
}

View file

@ -40,14 +40,18 @@ class Swimlane extends \Core\Base
return $this->swimlane->getDefault($project_id);
}
public function addSwimlane($project_id, $name)
public function addSwimlane($project_id, $name, $description = '')
{
return $this->swimlane->create($project_id, $name);
return $this->swimlane->create(array('project_id' => $project_id, 'name' => $name, 'description' => $description));
}
public function updateSwimlane($swimlane_id, $name)
public function updateSwimlane($swimlane_id, $name, $description = null)
{
return $this->swimlane->rename($swimlane_id, $name);
$values = array('id' => $swimlane_id, 'name' => $name);
if (!is_null($description)) {
$values['description'] = $description;
}
return $this->swimlane->update($values);
}
public function removeSwimlane($project_id, $swimlane_id)

View file

@ -35,6 +35,12 @@ class Task extends Base
return $this->taskFinder->getOverdueTasks();
}
public function getOverdueTasksByProject($project_id)
{
$this->checkProjectPermission($project_id);
return $this->taskFinder->getOverdueTasksByProject($project_id);
}
public function openTask($task_id)
{
$this->checkTaskPermission($task_id);

View file

@ -19,6 +19,190 @@ class Ldap extends Base
*/
const AUTH_NAME = 'LDAP';
/**
* Get LDAP server name
*
* @access public
* @return string
*/
public function getLdapServer()
{
return LDAP_SERVER;
}
/**
* Get LDAP bind type
*
* @access public
* @return integer
*/
public function getLdapBindType()
{
return LDAP_BIND_TYPE;
}
/**
* Get LDAP server port
*
* @access public
* @return integer
*/
public function getLdapPort()
{
return LDAP_PORT;
}
/**
* Get LDAP username (proxy auth)
*
* @access public
* @return string
*/
public function getLdapUsername()
{
return LDAP_USERNAME;
}
/**
* Get LDAP password (proxy auth)
*
* @access public
* @return string
*/
public function getLdapPassword()
{
return LDAP_PASSWORD;
}
/**
* Get LDAP Base DN
*
* @access public
* @return string
*/
public function getLdapBaseDn()
{
return LDAP_ACCOUNT_BASE;
}
/**
* Get LDAP account id attribute
*
* @access public
* @return string
*/
public function getLdapAccountId()
{
return LDAP_ACCOUNT_ID;
}
/**
* Get LDAP account email attribute
*
* @access public
* @return string
*/
public function getLdapAccountEmail()
{
return LDAP_ACCOUNT_EMAIL;
}
/**
* Get LDAP account name attribute
*
* @access public
* @return string
*/
public function getLdapAccountName()
{
return LDAP_ACCOUNT_FULLNAME;
}
/**
* Get LDAP account memberof attribute
*
* @access public
* @return string
*/
public function getLdapAccountMemberOf()
{
return LDAP_ACCOUNT_MEMBEROF;
}
/**
* Get LDAP admin group DN
*
* @access public
* @return string
*/
public function getLdapGroupAdmin()
{
return LDAP_GROUP_ADMIN_DN;
}
/**
* Get LDAP project admin group DN
*
* @access public
* @return string
*/
public function getLdapGroupProjectAdmin()
{
return LDAP_GROUP_PROJECT_ADMIN_DN;
}
/**
* Get LDAP username pattern
*
* @access public
* @param string $username
* @return string
*/
public function getLdapUserPattern($username)
{
return sprintf(LDAP_USER_PATTERN, $username);
}
/**
* Return true if the LDAP username is case sensitive
*
* @access public
* @return boolean
*/
public function isLdapAccountCaseSensitive()
{
return LDAP_USERNAME_CASE_SENSITIVE;
}
/**
* Return true if the automatic account creation is enabled
*
* @access public
* @return boolean
*/
public function isLdapAccountCreationEnabled()
{
return LDAP_ACCOUNT_CREATION;
}
/**
* Ge the list of attributes to fetch when reading the LDAP user entry
*
* Must returns array with index that start at 0 otherwise ldap_search returns a warning "Array initialization wrong"
*
* @access public
* @return array
*/
public function getProfileAttributes()
{
return array_values(array_filter(array(
$this->getLdapAccountId(),
$this->getLdapAccountName(),
$this->getLdapAccountEmail(),
$this->getLdapAccountMemberOf()
)));
}
/**
* Authenticate the user
*
@ -29,7 +213,7 @@ class Ldap extends Base
*/
public function authenticate($username, $password)
{
$username = LDAP_USERNAME_CASE_SENSITIVE ? $username : strtolower($username);
$username = $this->isLdapAccountCaseSensitive() ? $username : strtolower($username);
$result = $this->findUser($username, $password);
if (is_array($result)) {
@ -46,7 +230,7 @@ class Ldap extends Base
else {
// We create automatically a new user
if (LDAP_ACCOUNT_CREATION && $this->createUser($username, $result['name'], $result['email'])) {
if ($this->isLdapAccountCreationEnabled() && $this->user->create($result) !== false) {
$user = $this->user->getByUsername($username);
}
else {
@ -64,28 +248,6 @@ class Ldap extends Base
return false;
}
/**
* Create a new local user after the LDAP authentication
*
* @access public
* @param string $username Username
* @param string $name Name of the user
* @param string $email Email address
* @return bool
*/
public function createUser($username, $name, $email)
{
$values = array(
'username' => $username,
'name' => $name,
'email' => $email,
'is_admin' => 0,
'is_ldap_user' => 1,
);
return $this->user->create($values);
}
/**
* Find the user from the LDAP server
*
@ -98,8 +260,8 @@ class Ldap extends Base
{
$ldap = $this->connect();
if (is_resource($ldap) && $this->bind($ldap, $username, $password)) {
return $this->search($ldap, $username, $password);
if ($ldap !== false && $this->bind($ldap, $username, $password)) {
return $this->getProfile($ldap, $username, $password);
}
return false;
@ -108,13 +270,14 @@ class Ldap extends Base
/**
* LDAP connection
*
* @access private
* @return resource $ldap LDAP connection
* @access public
* @return resource|boolean
*/
private function connect()
public function connect()
{
if (! function_exists('ldap_connect')) {
die('The PHP LDAP extension is required');
$this->logger->error('LDAP: The PHP LDAP extension is required');
return false;
}
// Skip SSL certificate verification
@ -122,10 +285,11 @@ class Ldap extends Base
putenv('LDAPTLS_REQCERT=never');
}
$ldap = ldap_connect(LDAP_SERVER, LDAP_PORT);
$ldap = ldap_connect($this->getLdapServer(), $this->getLdapPort());
if (! is_resource($ldap)) {
die('Unable to connect to the LDAP server: "'.LDAP_SERVER.'"');
if ($ldap === false) {
$this->logger->error('LDAP: Unable to connect to the LDAP server');
return false;
}
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
@ -134,30 +298,31 @@ class Ldap extends Base
ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, 1);
if (LDAP_START_TLS && ! @ldap_start_tls($ldap)) {
die('Unable to use ldap_start_tls()');
$this->logger->error('LDAP: Unable to use ldap_start_tls()');
return false;
}
return $ldap;
}
/**
* LDAP bind
* LDAP authentication
*
* @access private
* @param resource $ldap LDAP connection
* @param string $username Username
* @param string $password Password
* @access public
* @param resource $ldap
* @param string $username
* @param string $password
* @return boolean
*/
private function bind($ldap, $username, $password)
public function bind($ldap, $username, $password)
{
if (LDAP_BIND_TYPE === 'user') {
$ldap_username = sprintf(LDAP_USERNAME, $username);
if ($this->getLdapBindType() === 'user') {
$ldap_username = sprintf($this->getLdapUsername(), $username);
$ldap_password = $password;
}
else if (LDAP_BIND_TYPE === 'proxy') {
$ldap_username = LDAP_USERNAME;
$ldap_password = LDAP_PASSWORD;
else if ($this->getLdapBindType() === 'proxy') {
$ldap_username = $this->getLdapUsername();
$ldap_password = $this->getLdapPassword();
}
else {
$ldap_username = null;
@ -165,6 +330,8 @@ class Ldap extends Base
}
if (! @ldap_bind($ldap, $ldap_username, $ldap_password)) {
$this->logger->error('LDAP: Unable to bind to server with: '.$ldap_username);
$this->logger->error('LDAP: bind type='.$this->getLdapBindType());
return false;
}
@ -172,118 +339,189 @@ class Ldap extends Base
}
/**
* LDAP user lookup
* Get LDAP user profile
*
* @access private
* @param resource $ldap LDAP connection
* @param string $username Username
* @param string $password Password
* @access public
* @param resource $ldap
* @param string $username
* @param string $password
* @return boolean|array
*/
private function search($ldap, $username, $password)
public function getProfile($ldap, $username, $password)
{
$sr = @ldap_search($ldap, LDAP_ACCOUNT_BASE, sprintf(LDAP_USER_PATTERN, $username), array(LDAP_ACCOUNT_FULLNAME, LDAP_ACCOUNT_EMAIL));
$user_pattern = $this->getLdapUserPattern($username);
$entries = $this->executeQuery($ldap, $user_pattern);
if ($sr === false) {
if ($entries === false) {
$this->logger->error('LDAP: Unable to get user profile: '.$user_pattern);
return false;
}
$info = ldap_get_entries($ldap, $sr);
// User not found
if (count($info) == 0 || $info['count'] == 0) {
return false;
if (@ldap_bind($ldap, $entries[0]['dn'], $password)) {
return $this->prepareProfile($ldap, $entries, $username);
}
// We got our user
if (@ldap_bind($ldap, $info[0]['dn'], $password)) {
return array(
'username' => $username,
'name' => $this->getFromInfo($info, LDAP_ACCOUNT_FULLNAME),
'email' => $this->getFromInfo($info, LDAP_ACCOUNT_EMAIL),
);
if (DEBUG) {
$this->logger->debug('LDAP: wrong password for '.$entries[0]['dn']);
}
return false;
}
/**
* Retrieve info on LDAP user
* Build user profile from LDAP information
*
* @param string $username Username
* @param string $email Email address
* @access public
* @param resource $ldap
* @param array $entries
* @param string $username
* @return boolean|array
*/
public function lookup($username = null, $email = null)
public function prepareProfile($ldap, array $entries, $username)
{
$query = $this->getQuery($username, $email);
if ($query === false) {
if ($this->getLdapAccountId() !== '') {
$username = $this->getEntry($entries, $this->getLdapAccountId(), $username);
}
return array(
'username' => $username,
'name' => $this->getEntry($entries, $this->getLdapAccountName()),
'email' => $this->getEntry($entries, $this->getLdapAccountEmail()),
'is_admin' => (int) $this->isMemberOf($this->getEntries($entries, $this->getLdapAccountMemberOf()), $this->getLdapGroupAdmin()),
'is_project_admin' => (int) $this->isMemberOf($this->getEntries($entries, $this->getLdapAccountMemberOf()), $this->getLdapGroupProjectAdmin()),
'is_ldap_user' => 1,
);
}
/**
* Check group membership
*
* @access public
* @param array $group_entries
* @param string $group_dn
* @return boolean
*/
public function isMemberOf(array $group_entries, $group_dn)
{
if (! isset($group_entries['count']) || empty($group_dn)) {
return false;
}
// Connect and attempt anonymous bind
for ($i = 0; $i < $group_entries['count']; $i++) {
if ($group_entries[$i] === $group_dn) {
return true;
}
}
return false;
}
/**
* Retrieve info on LDAP user by username or email
*
* @access public
* @param string $username
* @param string $email
* @return boolean|array
*/
public function lookup($username = null, $email = null)
{
$query = $this->getLookupQuery($username, $email);
if ($query === '') {
return false;
}
// Connect and attempt anonymous or proxy binding
$ldap = $this->connect();
if (! is_resource($ldap) || ! $this->bind($ldap, null, null)) {
if ($ldap === false || ! $this->bind($ldap, null, null)) {
return false;
}
// Try to find user
$sr = @ldap_search($ldap, LDAP_ACCOUNT_BASE, $query, array(LDAP_ACCOUNT_FULLNAME, LDAP_ACCOUNT_EMAIL, LDAP_ACCOUNT_ID));
if ($sr === false) {
return false;
}
$info = ldap_get_entries($ldap, $sr);
// User not found
if (count($info) == 0 || $info['count'] == 0) {
$entries = $this->executeQuery($ldap, $query);
if ($entries === false) {
return false;
}
// User id not retrieved: LDAP_ACCOUNT_ID not properly configured
if (empty($username) && ! isset($info[0][LDAP_ACCOUNT_ID][0])) {
if (empty($username) && ! isset($entries[0][$this->getLdapAccountId()][0])) {
return false;
}
return array(
'username' => $this->getFromInfo($info, LDAP_ACCOUNT_ID, $username),
'name' => $this->getFromInfo($info, LDAP_ACCOUNT_FULLNAME),
'email' => $this->getFromInfo($info, LDAP_ACCOUNT_EMAIL, $email),
);
return $this->prepareProfile($ldap, $entries, $username);
}
/**
* Execute LDAP query
*
* @access private
* @param resource $ldap
* @param string $query
* @return boolean|array
*/
private function executeQuery($ldap, $query)
{
$sr = @ldap_search($ldap, $this->getLdapBaseDn(), $query, $this->getProfileAttributes());
if ($sr === false) {
return false;
}
$entries = ldap_get_entries($ldap, $sr);
if ($entries === false || count($entries) === 0 || $entries['count'] == 0) {
return false;
}
return $entries;
}
/**
* Get the LDAP query to find a user
*
* @param string $username Username
* @param string $email Email address
* @access private
* @param string $username
* @param string $email
* @return string
*/
private function getQuery($username, $email)
private function getLookupQuery($username, $email)
{
if ($username && $email) {
return '(&('.sprintf(LDAP_USER_PATTERN, $username).')('.LDAP_ACCOUNT_EMAIL.'='.$email.'))';
if (! empty($username) && ! empty($email)) {
return '(&('.$this->getLdapUserPattern($username).')('.$this->getLdapAccountEmail().'='.$email.'))';
}
else if ($username) {
return sprintf(LDAP_USER_PATTERN, $username);
else if (! empty($username)) {
return $this->getLdapUserPattern($username);
}
else if ($email) {
return '('.LDAP_ACCOUNT_EMAIL.'='.$email.')';
}
else {
return false;
else if (! empty($email)) {
return '('.$this->getLdapAccountEmail().'='.$email.')';
}
return '';
}
/**
* Return a value from the LDAP info
* Return one entry from a list of entries
*
* @param array $info LDAP info
* @access private
* @param array $entries LDAP entries
* @param string $key Key
* @param string $default Default value if key not set in entry
* @return string
*/
private function getFromInfo($info, $key, $default = '')
private function getEntry(array $entries, $key, $default = '')
{
return isset($info[0][$key][0]) ? $info[0][$key][0] : $default;
return isset($entries[0][$key][0]) ? $entries[0][$key][0] : $default;
}
/**
* Return subset of entries
*
* @access private
* @param array $entries
* @param string $key
* @param array $default
* @return array
*/
private function getEntries(array $entries, $key, $default = array())
{
return isset($entries[0][$key]) ? $entries[0][$key] : $default;
}
}

View file

@ -28,7 +28,6 @@ class ReverseProxy extends Base
public function authenticate()
{
if (isset($_SERVER[REVERSE_PROXY_USER_HEADER])) {
$login = $_SERVER[REVERSE_PROXY_USER_HEADER];
$user = $this->user->getByUsername($login);

View file

@ -19,7 +19,7 @@ class TaskOverdueNotification extends Base
protected function execute(InputInterface $input, OutputInterface $output)
{
$tasks = $this->notification->sendOverdueTaskNotifications();
$tasks = $this->overdueNotification->sendOverdueTaskNotifications();
if ($input->getOption('show')) {
$this->showTable($output, $tasks);

View file

@ -3,7 +3,6 @@
namespace Controller;
use Model\Subtask as SubtaskModel;
use Model\Task as TaskModel;
/**
* Application controller
@ -188,6 +187,22 @@ class App extends Base
)));
}
/**
* My notifications
*
* @access public
*/
public function notifications()
{
$user = $this->getUser();
$this->response->html($this->layout('app/notifications', array(
'title' => t('My notifications'),
'notifications' => $this->webNotification->getAll($user['id']),
'user' => $user,
)));
}
/**
* Render Markdown text and reply with the HTML Code
*
@ -213,7 +228,7 @@ class App extends Base
{
$search = $this->request->getStringParam('term');
$filter = $this->taskFilter
$filter = $this->taskFilterAutoCompleteFormatter
->create()
->filterByProjects($this->projectPermission->getActiveMemberProjectIds($this->userSession->getId()))
->excludeTasks(array($this->request->getIntegerParam('exclude_task_id')));
@ -226,6 +241,6 @@ class App extends Base
$filter->filterByTitle($search);
}
$this->response->json($filter->toAutoCompletion());
$this->response->json($filter->format());
}
}

View file

@ -80,7 +80,7 @@ abstract class Base extends \Core\Base
private function sendHeaders($action)
{
// HTTP secure headers
$this->response->csp(array('style-src' => "'self' 'unsafe-inline'", 'img-src' => '* data:'));
$this->response->csp($this->container['cspRules']);
$this->response->nosniff();
$this->response->xss();

View file

@ -27,7 +27,7 @@ class Board extends Base
}
// Display the board with a specific layout
$this->response->html($this->template->layout('board/public_view', array(
$this->response->html($this->template->layout('board/view_public', array(
'project' => $project,
'swimlanes' => $this->board->getBoard($project['id']),
'title' => $project['name'],
@ -49,9 +49,10 @@ class Board extends Base
{
$params = $this->getProjectFilters('board', 'show');
$this->response->html($this->template->layout('board/private_view', array(
$this->response->html($this->template->layout('board/view_private', array(
'categories_list' => $this->category->getList($params['project']['id'], false),
'users_list' => $this->projectPermission->getMemberList($params['project']['id'], false),
'custom_filters_list' => $this->customFilter->getAll($params['project']['id'], $this->userSession->getId()),
'swimlanes' => $this->taskFilter->search($params['filters']['search'])->getBoard($params['project']['id']),
'description' => $params['project']['description'],
'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'),
@ -136,7 +137,7 @@ class Board extends Base
}
$values = $this->request->getJson();
$this->userSession->setFilters($project_id, $values['search']);
$this->userSession->setFilters($project_id, empty($values['search']) ? '' : $values['search']);
$this->response->html($this->renderBoard($project_id));
}
@ -320,6 +321,18 @@ class Board extends Base
)));
}
/**
* Display swimlane description in tooltip
*
* @access public
*/
public function swimlane()
{
$this->getProject();
$swimlane = $this->swimlane->getById($this->request->getIntegerParam('swimlane_id'));
$this->response->html($this->template->render('board/tooltip_description', array('task' => $swimlane)));
}
/**
* Enable collapsed mode
*

View file

@ -1,135 +0,0 @@
<?php
namespace Controller;
/**
* Budget
*
* @package controller
* @author Frederic Guillot
*/
class Budget extends Base
{
/**
* Budget index page
*
* @access public
*/
public function index()
{
$project = $this->getProject();
$this->response->html($this->projectLayout('budget/index', array(
'daily_budget' => $this->budget->getDailyBudgetBreakdown($project['id']),
'project' => $project,
'title' => t('Budget')
), 'budget/sidebar'));
}
/**
* Cost breakdown by users/subtasks/tasks
*
* @access public
*/
public function breakdown()
{
$project = $this->getProject();
$paginator = $this->paginator
->setUrl('budget', 'breakdown', array('project_id' => $project['id']))
->setMax(30)
->setOrder('start')
->setDirection('DESC')
->setQuery($this->budget->getSubtaskBreakdown($project['id']))
->calculate();
$this->response->html($this->projectLayout('budget/breakdown', array(
'paginator' => $paginator,
'project' => $project,
'title' => t('Budget')
), 'budget/sidebar'));
}
/**
* Create budget lines
*
* @access public
*/
public function create(array $values = array(), array $errors = array())
{
$project = $this->getProject();
if (empty($values)) {
$values['date'] = date('Y-m-d');
}
$this->response->html($this->projectLayout('budget/create', array(
'lines' => $this->budget->getAll($project['id']),
'values' => $values + array('project_id' => $project['id']),
'errors' => $errors,
'project' => $project,
'title' => t('Budget lines')
), 'budget/sidebar'));
}
/**
* Validate and save a new budget
*
* @access public
*/
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
list($valid, $errors) = $this->budget->validateCreation($values);
if ($valid) {
if ($this->budget->create($values['project_id'], $values['amount'], $values['comment'], $values['date'])) {
$this->session->flash(t('The budget line have been created successfully.'));
$this->response->redirect($this->helper->url->to('budget', 'create', array('project_id' => $project['id'])));
}
else {
$this->session->flashError(t('Unable to create the budget line.'));
}
}
$this->create($values, $errors);
}
/**
* Confirmation dialog before removing a budget
*
* @access public
*/
public function confirm()
{
$project = $this->getProject();
$this->response->html($this->projectLayout('budget/remove', array(
'project' => $project,
'budget_id' => $this->request->getIntegerParam('budget_id'),
'title' => t('Remove a budget line'),
), 'budget/sidebar'));
}
/**
* Remove a budget
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$project = $this->getProject();
if ($this->budget->remove($this->request->getIntegerParam('budget_id'))) {
$this->session->flash(t('Budget line removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this budget line.'));
}
$this->response->redirect($this->helper->url->to('budget', 'create', array('project_id' => $project['id'])));
}
}

View file

@ -37,20 +37,26 @@ class Calendar extends Base
$end = $this->request->getStringParam('end');
// Common filter
$filter = $this->taskFilter
$filter = $this->taskFilterCalendarFormatter
->search($this->userSession->getFilters($project_id))
->filterByProject($project_id);
// Tasks
if ($this->config->get('calendar_project_tasks', 'date_started') === 'date_creation') {
$events = $filter->copy()->filterByCreationDateRange($start, $end)->toDateTimeCalendarEvents('date_creation', 'date_completed');
$events = $filter->copy()->filterByCreationDateRange($start, $end)->setColumns('date_creation', 'date_completed')->format();
}
else {
$events = $filter->copy()->filterByStartDateRange($start, $end)->toDateTimeCalendarEvents('date_started', 'date_completed');
$events = $filter->copy()->filterByStartDateRange($start, $end)->setColumns('date_started', 'date_completed')->format();
}
// Tasks with due date
$events = array_merge($events, $filter->copy()->filterByDueDateRange($start, $end)->toAllDayCalendarEvents());
$events = array_merge($events, $filter->copy()->filterByDueDateRange($start, $end)->setColumns('date_due')->setFullDay()->format());
$events = $this->hook->merge('controller:calendar:project:events', $events, array(
'project_id' => $project_id,
'start' => $start,
'end' => $end,
));
$this->response->json($events);
}
@ -65,17 +71,17 @@ class Calendar extends Base
$user_id = $this->request->getIntegerParam('user_id');
$start = $this->request->getStringParam('start');
$end = $this->request->getStringParam('end');
$filter = $this->taskFilter->create()->filterByOwner($user_id)->filterByStatus(TaskModel::STATUS_OPEN);
$filter = $this->taskFilterCalendarFormatter->create()->filterByOwner($user_id)->filterByStatus(TaskModel::STATUS_OPEN);
// Task with due date
$events = $filter->copy()->filterByDueDateRange($start, $end)->toAllDayCalendarEvents();
$events = $filter->copy()->filterByDueDateRange($start, $end)->setColumns('date_due')->setFullDay()->format();
// Tasks
if ($this->config->get('calendar_user_tasks', 'date_started') === 'date_creation') {
$events = array_merge($events, $filter->copy()->filterByCreationDateRange($start, $end)->toDateTimeCalendarEvents('date_creation', 'date_completed'));
$events = array_merge($events, $filter->copy()->filterByCreationDateRange($start, $end)->setColumns('date_creation', 'date_completed')->format());
}
else {
$events = array_merge($events, $filter->copy()->filterByStartDateRange($start, $end)->toDateTimeCalendarEvents('date_started', 'date_completed'));
$events = array_merge($events, $filter->copy()->filterByStartDateRange($start, $end)->setColumns('date_started', 'date_completed')->format());
}
// Subtasks time tracking
@ -83,10 +89,11 @@ class Calendar extends Base
$events = array_merge($events, $this->subtaskTimeTracking->getUserCalendarEvents($user_id, $start, $end));
}
// Subtask estimates
if ($this->config->get('calendar_user_subtasks_forecast') == 1) {
$events = array_merge($events, $this->subtaskForecast->getCalendarEvents($user_id, $end));
}
$events = $this->hook->merge('controller:calendar:user:events', $events, array(
'user_id' => $user_id,
'start' => $start,
'end' => $end,
));
$this->response->json($events);
}

View file

@ -48,7 +48,7 @@ class Config extends Base
$values += array('integration_slack_webhook' => 0, 'integration_hipchat' => 0, 'integration_gravatar' => 0, 'integration_jabber' => 0);
break;
case 'calendar':
$values += array('calendar_user_subtasks_forecast' => 0, 'calendar_user_subtasks_time_tracking' => 0);
$values += array('calendar_user_subtasks_time_tracking' => 0);
break;
}
@ -77,6 +77,19 @@ class Config extends Base
)));
}
/**
* Display the plugin page
*
* @access public
*/
public function plugins()
{
$this->response->html($this->layout('config/plugins', array(
'plugins' => $this->pluginLoader->plugins,
'title' => t('Settings').' &gt; '.t('Plugins'),
)));
}
/**
* Display the application settings page
*

View file

@ -0,0 +1,142 @@
<?php
namespace Controller;
/**
* Custom Filter management
*
* @package controller
* @author Timo Litzbarski
*/
class Customfilter extends Base
{
/**
* Display list of filters
*
* @access public
*/
public function index(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$this->response->html($this->projectLayout('custom_filter/index', array(
'values' => $values + array('project_id' => $project['id']),
'errors' => $errors,
'project' => $project,
'custom_filters' => $this->customFilter->getAll($project['id'], $this->userSession->getId()),
'title' => t('Custom filters'),
)));
}
/**
* Save a new custom filter
*
* @access public
*/
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
$values['user_id'] = $this->userSession->getId();
list($valid, $errors) = $this->customFilter->validateCreation($values);
if ($valid) {
if ($this->customFilter->create($values)) {
$this->session->flash(t('Your custom filter have been created successfully.'));
$this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
}
else {
$this->session->flashError(t('Unable to create your custom filter.'));
}
}
$this->index($values, $errors);
}
/**
* Remove a custom filter
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$project = $this->getProject();
$filter = $this->customFilter->getById($this->request->getIntegerParam('filter_id'));
$this->checkPermission($project, $filter);
if ($this->customFilter->remove($filter['id'])) {
$this->session->flash(t('Custom filter removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this custom filter.'));
}
$this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
}
/**
* Edit a custom filter (display the form)
*
* @access public
*/
public function edit(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$filter = $this->customFilter->getById($this->request->getIntegerParam('filter_id'));
$this->checkPermission($project, $filter);
$this->response->html($this->projectLayout('custom_filter/edit', array(
'values' => empty($values) ? $filter : $values,
'errors' => $errors,
'project' => $project,
'filter' => $filter,
'title' => t('Edit custom filter')
)));
}
/**
* Edit a custom filter (validate the form and update the database)
*
* @access public
*/
public function update()
{
$project = $this->getProject();
$filter = $this->customFilter->getById($this->request->getIntegerParam('filter_id'));
$this->checkPermission($project, $filter);
$values = $this->request->getValues();
if (! isset($values['is_shared'])) {
$values += array('is_shared' => 0);
}
list($valid, $errors) = $this->customFilter->validateModification($values);
if ($valid) {
if ($this->customFilter->update($values)) {
$this->session->flash(t('Your custom filter have been updated successfully.'));
$this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
}
else {
$this->session->flashError(t('Unable to update custom filter.'));
}
}
$this->edit($values, $errors);
}
private function checkPermission(array $project, array $filter)
{
$user_id = $this->userSession->getId();
if ($filter['user_id'] != $user_id && (! $this->projectPermission->isManager($project['id'], $user_id) || ! $this->userSession->isAdmin())) {
$this->forbidden();
}
}
}

View file

@ -16,7 +16,7 @@ class Doc extends Base
{
$url = $this->helper->url;
$data = file_get_contents($filename);
list($title,, $content) = explode("\n", $data, 3);
list($title,) = explode("\n", $data, 2);
$replaceUrl = function (array $matches) use ($url) {
return '('.$url->to('doc', 'show', array('file' => str_replace('.markdown', '', $matches[1]))).')';
@ -32,16 +32,24 @@ class Doc extends Base
public function show()
{
$filename = $this->request->getStringParam('file', 'index');
$page = $this->request->getStringParam('file', 'index');
if (! preg_match('/^[a-z0-9\-]+/', $filename)) {
$filename = 'index';
if (! preg_match('/^[a-z0-9\-]+/', $page)) {
$page = 'index';
}
$filename = __DIR__.'/../../doc/'.$filename.'.markdown';
if (! file_exists($filename)) {
$filenames = array(__DIR__.'/../../doc/'.$page.'.markdown');
$filename = __DIR__.'/../../doc/index.markdown';
if ($this->config->getCurrentLanguage() === 'fr_FR') {
array_unshift($filenames, __DIR__.'/../../doc/fr/'.$page.'.markdown');
}
foreach ($filenames as $file) {
if (file_exists($file)) {
$filename = $file;
break;
}
}
$this->response->html($this->template->layout('doc/show', $this->readFile($filename) + array(

View file

@ -2,6 +2,8 @@
namespace Controller;
use Core\ObjectStorage\ObjectStorageException;
/**
* File controller
*
@ -60,7 +62,7 @@ class File extends Base
{
$task = $this->getTask();
if (! $this->file->upload($task['project_id'], $task['id'], 'files')) {
if (! $this->file->uploadFiles($task['project_id'], $task['id'], 'files')) {
$this->session->flashError(t('Unable to upload the file.'));
}
@ -74,16 +76,21 @@ class File extends Base
*/
public function download()
{
try {
$task = $this->getTask();
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
$filename = FILES_DIR.$file['path'];
if ($file['task_id'] == $task['id'] && file_exists($filename)) {
$this->response->forceDownload($file['name']);
$this->response->binary(file_get_contents($filename));
if ($file['task_id'] != $task['id']) {
$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'])));
$this->response->forceDownload($file['name']);
$this->objectStorage->output($file['path']);
}
catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
}
}
/**
@ -111,17 +118,20 @@ class File extends Base
*/
public function image()
{
try {
$task = $this->getTask();
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
$filename = FILES_DIR.$file['path'];
if ($file['task_id'] == $task['id'] && file_exists($filename)) {
$metadata = getimagesize($filename);
if (isset($metadata['mime'])) {
$this->response->contentType($metadata['mime']);
readfile($filename);
if ($file['task_id'] != $task['id']) {
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
}
$this->response->contentType($this->file->getImageMimeType($file['name']));
$this->objectStorage->output($file['path']);
}
catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
}
}
@ -132,18 +142,20 @@ class File extends Base
*/
public function thumbnail()
{
try {
$task = $this->getTask();
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
$filename = FILES_DIR.$file['path'];
if ($file['task_id'] == $task['id'] && file_exists($filename)) {
if ($file['task_id'] != $task['id']) {
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
}
$this->response->contentType('image/jpeg');
$this->file->generateThumbnail(
$filename,
$this->request->getIntegerParam('width'),
$this->request->getIntegerParam('height')
);
$this->objectStorage->output($this->file->getThumbnailPath($file['path']));
}
catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
}
}

View file

@ -25,7 +25,7 @@ class Gantt extends Base
}
$this->response->html($this->template->layout('gantt/projects', array(
'projects' => $this->project->getGanttBars($project_ids),
'projects' => $this->projectGanttFormatter->filter($project_ids)->format(),
'title' => t('Gantt chart for all projects'),
'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
)));
@ -57,7 +57,7 @@ class Gantt extends Base
public function project()
{
$params = $this->getProjectFilters('gantt', 'project');
$filter = $this->taskFilter->search($params['filters']['search'])->filterByProject($params['project']['id']);
$filter = $this->taskFilterGanttFormatter->search($params['filters']['search'])->filterByProject($params['project']['id']);
$sorting = $this->request->getStringParam('sorting', 'board');
if ($sorting === 'date') {
@ -70,7 +70,7 @@ class Gantt extends Base
$this->response->html($this->template->layout('gantt/project', $params + array(
'users_list' => $this->projectPermission->getMemberList($params['project']['id'], false),
'sorting' => $sorting,
'tasks' => $filter->toGanttBars(),
'tasks' => $filter->format(),
)));
}

View file

@ -1,89 +0,0 @@
<?php
namespace Controller;
/**
* Hourly Rate controller
*
* @package controller
* @author Frederic Guillot
*/
class Hourlyrate extends User
{
/**
* Display rate and form
*
* @access public
*/
public function index(array $values = array(), array $errors = array())
{
$user = $this->getUser();
$this->response->html($this->layout('hourlyrate/index', array(
'rates' => $this->hourlyRate->getAllByUser($user['id']),
'currencies_list' => $this->config->getCurrencies(),
'values' => $values + array('user_id' => $user['id']),
'errors' => $errors,
'user' => $user,
)));
}
/**
* Validate and save a new rate
*
* @access public
*/
public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->hourlyRate->validateCreation($values);
if ($valid) {
if ($this->hourlyRate->create($values['user_id'], $values['rate'], $values['currency'], $values['date_effective'])) {
$this->session->flash(t('Hourly rate created successfully.'));
$this->response->redirect($this->helper->url->to('hourlyrate', 'index', array('user_id' => $values['user_id'])));
}
else {
$this->session->flashError(t('Unable to save the hourly rate.'));
}
}
$this->index($values, $errors);
}
/**
* Confirmation dialag box to remove a row
*
* @access public
*/
public function confirm()
{
$user = $this->getUser();
$this->response->html($this->layout('hourlyrate/remove', array(
'rate_id' => $this->request->getIntegerParam('rate_id'),
'user' => $user,
)));
}
/**
* Remove a row
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$user = $this->getUser();
if ($this->hourlyRate->remove($this->request->getIntegerParam('rate_id'))) {
$this->session->flash(t('Rate removed successfully.'));
}
else {
$this->session->flash(t('Unable to remove this rate.'));
}
$this->response->redirect($this->helper->url->to('hourlyrate', 'index', array('user_id' => $user['id'])));
}
}

View file

@ -29,7 +29,7 @@ class Ical extends Base
}
// Common filter
$filter = $this->taskFilter
$filter = $this->taskFilterICalendarFormatter
->create()
->filterByOwner($user['id']);
@ -58,7 +58,7 @@ class Ical extends Base
}
// Common filter
$filter = $this->taskFilter
$filter = $this->taskFilterICalendarFormatter
->create()
->filterByProject($project['id']);
@ -83,16 +83,31 @@ class Ical extends Base
// Tasks
if ($this->config->get('calendar_project_tasks', 'date_started') === 'date_creation') {
$filter->copy()->filterByCreationDateRange($start, $end)->addDateTimeIcalEvents('date_creation', 'date_completed', $calendar);
$filter
->copy()
->filterByCreationDateRange($start, $end)
->setColumns('date_creation', 'date_completed')
->setCalendar($calendar)
->addDateTimeEvents();
}
else {
$filter->copy()->filterByStartDateRange($start, $end)->addDateTimeIcalEvents('date_started', 'date_completed', $calendar);
$filter
->copy()
->filterByStartDateRange($start, $end)
->setColumns('date_started', 'date_completed')
->setCalendar($calendar)
->addDateTimeEvents($calendar);
}
// Tasks with due date
$filter->copy()->filterByDueDateRange($start, $end)->addAllDayIcalEvents('date_due', $calendar);
$filter
->copy()
->filterByDueDateRange($start, $end)
->setColumns('date_due')
->setCalendar($calendar)
->addFullDayEvents($calendar);
$this->response->contentType('text/calendar; charset=utf-8');
echo $calendar->render();
echo $filter->setCalendar($calendar)->format();
}
}

View file

@ -59,13 +59,12 @@ class Swimlane extends Base
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
list($valid, $errors) = $this->swimlane->validateCreation($values);
if ($valid) {
if ($this->swimlane->create($project['id'], $values['name'])) {
if ($this->swimlane->create($values)) {
$this->session->flash(t('Your swimlane have been created successfully.'));
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
}
@ -134,8 +133,7 @@ class Swimlane extends Base
list($valid, $errors) = $this->swimlane->validateModification($values);
if ($valid) {
if ($this->swimlane->rename($values['id'], $values['name'])) {
if ($this->swimlane->update($values)) {
$this->session->flash(t('Swimlane updated successfully.'));
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
}

View file

@ -59,25 +59,29 @@ class Taskcreation extends Base
list($valid, $errors) = $this->taskValidator->validateCreation($values);
if ($valid) {
if ($this->taskCreation->create($values)) {
if ($valid && $this->taskCreation->create($values)) {
$this->session->flash(t('Task created successfully.'));
$this->afterSave($project, $values);
}
else {
$this->session->flashError(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'])));
}
}
else {
$this->session->flashError(t('Unable to create your task.'));
}
}
$this->create($values, $errors);
}
}

View file

@ -26,7 +26,7 @@ class Taskduplication extends Base
if ($task_id > 0) {
$this->session->flash(t('Task created successfully.'));
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task_id)));
} else {
$this->session->flashError(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'])));
@ -83,15 +83,16 @@ class Taskduplication extends Base
$values = $this->request->getValues();
list($valid,) = $this->taskValidator->validateProjectModification($values);
if ($valid && $this->taskDuplication->duplicateToProject($task['id'],
$values['project_id'],
$values['swimlane_id'],
$values['column_id'],
$values['category_id'],
$values['owner_id'])) {
if ($valid) {
$task_id = $this->taskDuplication->duplicateToProject(
$task['id'], $values['project_id'], $values['swimlane_id'],
$values['column_id'], $values['category_id'], $values['owner_id']
);
if ($task_id > 0) {
$this->session->flash(t('Task created successfully.'));
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $values['project_id'], 'task_id' => $task_id)));
}
}
$this->session->flashError(t('Unable to create your task.'));

View file

@ -126,11 +126,13 @@ class Taskmodification extends Base
);
if ($ajax) {
$this->response->html($this->template->render('task_modification/edit_task', $params));
$html = $this->template->render('task_modification/edit_task', $params);
}
else {
$this->response->html($this->taskLayout('task_modification/edit_task', $params));
$html = $this->taskLayout('task_modification/edit_task', $params);
}
$this->response->html($html);
}
/**
@ -145,12 +147,10 @@ class Taskmodification extends Base
list($valid, $errors) = $this->taskValidator->validateModification($values);
if ($valid) {
if ($this->taskModification->update($values)) {
if ($valid && $this->taskModification->update($values)) {
$this->session->flash(t('Task updated successfully.'));
if ($this->request->getIntegerParam('ajax')) {
if ($this->request->isAjax()) {
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
}
else {
@ -159,11 +159,9 @@ class Taskmodification extends Base
}
else {
$this->session->flashError(t('Unable to update your task.'));
}
}
$this->edit($values, $errors);
}
}
/**
* Edit recurrence form

View file

@ -18,36 +18,8 @@ class Taskstatus extends Base
public function close()
{
$task = $this->getTask();
$redirect = $this->request->getStringParam('redirect');
if ($this->request->getStringParam('confirmation') === 'yes') {
$this->checkCSRFParam();
if ($this->taskStatus->close($task['id'])) {
$this->session->flash(t('Task closed successfully.'));
} else {
$this->session->flashError(t('Unable to close this task.'));
}
if ($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'])));
}
if ($this->request->isAjax()) {
$this->response->html($this->template->render('task_status/close', array(
'task' => $task,
'redirect' => $redirect,
)));
}
$this->response->html($this->taskLayout('task_status/close', array(
'task' => $task,
'redirect' => $redirect,
)));
$this->changeStatus($task, 'close', t('Task closed successfully.'), t('Unable to close this task.'));
$this->renderTemplate($task, 'task_status/close');
}
/**
@ -58,22 +30,44 @@ 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');
}
private function changeStatus(array $task, $method, $success_message, $failure_message)
{
if ($this->request->getStringParam('confirmation') === 'yes') {
$this->checkCSRFParam();
if ($this->taskStatus->open($task['id'])) {
$this->session->flash(t('Task opened successfully.'));
if ($this->taskStatus->$method($task['id'])) {
$this->session->flash($success_message);
} else {
$this->session->flashError(t('Unable to open this task.'));
$this->session->flashError($failure_message);
}
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
if ($this->request->getStringParam('redirect') === 'board') {
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
}
$this->response->html($this->taskLayout('task_status/open', array(
$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,
)));
}
$this->response->html($this->taskLayout($template, array(
'task' => $task,
'redirect' => $redirect,
)));
}
}

View file

@ -1,39 +0,0 @@
<?php
namespace Controller;
use DateTime;
/**
* Timetable controller
*
* @package controller
* @author Frederic Guillot
*/
class Timetable extends User
{
/**
* Display timetable for the user
*
* @access public
*/
public function index()
{
$user = $this->getUser();
$from = $this->request->getStringParam('from', date('Y-m-d'));
$to = $this->request->getStringParam('to', date('Y-m-d', strtotime('next week')));
$timetable = $this->timetable->calculate($user['id'], new DateTime($from), new DateTime($to));
$this->response->html($this->layout('timetable/index', array(
'user' => $user,
'timetable' => $timetable,
'values' => array(
'from' => $from,
'to' => $to,
'controller' => 'timetable',
'action' => 'index',
'user_id' => $user['id'],
),
)));
}
}

View file

@ -1,88 +0,0 @@
<?php
namespace Controller;
/**
* Day Timetable controller
*
* @package controller
* @author Frederic Guillot
*/
class Timetableday extends User
{
/**
* Display timetable for the user
*
* @access public
*/
public function index(array $values = array(), array $errors = array())
{
$user = $this->getUser();
$this->response->html($this->layout('timetable_day/index', array(
'timetable' => $this->timetableDay->getByUser($user['id']),
'values' => $values + array('user_id' => $user['id']),
'errors' => $errors,
'user' => $user,
)));
}
/**
* Validate and save
*
* @access public
*/
public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->timetableDay->validateCreation($values);
if ($valid) {
if ($this->timetableDay->create($values['user_id'], $values['start'], $values['end'])) {
$this->session->flash(t('Time slot created successfully.'));
$this->response->redirect($this->helper->url->to('timetableday', 'index', array('user_id' => $values['user_id'])));
}
else {
$this->session->flashError(t('Unable to save this time slot.'));
}
}
$this->index($values, $errors);
}
/**
* Confirmation dialag box to remove a row
*
* @access public
*/
public function confirm()
{
$user = $this->getUser();
$this->response->html($this->layout('timetable_day/remove', array(
'slot_id' => $this->request->getIntegerParam('slot_id'),
'user' => $user,
)));
}
/**
* Remove a row
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$user = $this->getUser();
if ($this->timetableDay->remove($this->request->getIntegerParam('slot_id'))) {
$this->session->flash(t('Time slot removed successfully.'));
}
else {
$this->session->flash(t('Unable to remove this time slot.'));
}
$this->response->redirect($this->helper->url->to('timetableday', 'index', array('user_id' => $user['id'])));
}
}

View file

@ -1,16 +0,0 @@
<?php
namespace Controller;
/**
* Over-time Timetable controller
*
* @package controller
* @author Frederic Guillot
*/
class Timetableextra extends Timetableoff
{
protected $model = 'timetableExtra';
protected $controller_url = 'timetableextra';
protected $template_dir = 'timetable_extra';
}

View file

@ -1,107 +0,0 @@
<?php
namespace Controller;
/**
* Time-off Timetable controller
*
* @package controller
* @author Frederic Guillot
*/
class Timetableoff extends User
{
protected $model = 'timetableOff';
protected $controller_url = 'timetableoff';
protected $template_dir = 'timetable_off';
/**
* Display timetable for the user
*
* @access public
*/
public function index(array $values = array(), array $errors = array())
{
$user = $this->getUser();
$paginator = $this->paginator
->setUrl($this->controller_url, 'index', array('user_id' => $user['id']))
->setMax(10)
->setOrder('date')
->setDirection('desc')
->setQuery($this->{$this->model}->getUserQuery($user['id']))
->calculate();
$this->response->html($this->layout($this->template_dir.'/index', array(
'values' => $values + array('user_id' => $user['id']),
'errors' => $errors,
'paginator' => $paginator,
'user' => $user,
)));
}
/**
* Validate and save
*
* @access public
*/
public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->{$this->model}->validateCreation($values);
if ($valid) {
if ($this->{$this->model}->create(
$values['user_id'],
$values['date'],
isset($values['all_day']) && $values['all_day'] == 1,
$values['start'],
$values['end'],
$values['comment'])) {
$this->session->flash(t('Time slot created successfully.'));
$this->response->redirect($this->helper->url->to($this->controller_url, 'index', array('user_id' => $values['user_id'])));
}
else {
$this->session->flashError(t('Unable to save this time slot.'));
}
}
$this->index($values, $errors);
}
/**
* Confirmation dialag box to remove a row
*
* @access public
*/
public function confirm()
{
$user = $this->getUser();
$this->response->html($this->layout($this->template_dir.'/remove', array(
'slot_id' => $this->request->getIntegerParam('slot_id'),
'user' => $user,
)));
}
/**
* Remove a row
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$user = $this->getUser();
if ($this->{$this->model}->remove($this->request->getIntegerParam('slot_id'))) {
$this->session->flash(t('Time slot removed successfully.'));
}
else {
$this->session->flash(t('Unable to remove this time slot.'));
}
$this->response->redirect($this->helper->url->to($this->controller_url, 'index', array('user_id' => $user['id'])));
}
}

View file

@ -1,99 +0,0 @@
<?php
namespace Controller;
/**
* Week Timetable controller
*
* @package controller
* @author Frederic Guillot
*/
class Timetableweek extends User
{
/**
* Display timetable for the user
*
* @access public
*/
public function index(array $values = array(), array $errors = array())
{
$user = $this->getUser();
if (empty($values)) {
$day = $this->timetableDay->getByUser($user['id']);
$values = array(
'user_id' => $user['id'],
'start' => isset($day[0]['start']) ? $day[0]['start'] : null,
'end' => isset($day[0]['end']) ? $day[0]['end'] : null,
);
}
$this->response->html($this->layout('timetable_week/index', array(
'timetable' => $this->timetableWeek->getByUser($user['id']),
'values' => $values,
'errors' => $errors,
'user' => $user,
)));
}
/**
* Validate and save
*
* @access public
*/
public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->timetableWeek->validateCreation($values);
if ($valid) {
if ($this->timetableWeek->create($values['user_id'], $values['day'], $values['start'], $values['end'])) {
$this->session->flash(t('Time slot created successfully.'));
$this->response->redirect($this->helper->url->to('timetableweek', 'index', array('user_id' => $values['user_id'])));
}
else {
$this->session->flashError(t('Unable to save this time slot.'));
}
}
$this->index($values, $errors);
}
/**
* Confirmation dialag box to remove a row
*
* @access public
*/
public function confirm()
{
$user = $this->getUser();
$this->response->html($this->layout('timetable_week/remove', array(
'slot_id' => $this->request->getIntegerParam('slot_id'),
'user' => $user,
)));
}
/**
* Remove a row
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$user = $this->getUser();
if ($this->timetableWeek->remove($this->request->getIntegerParam('slot_id'))) {
$this->session->flash(t('Time slot removed successfully.'));
}
else {
$this->session->flash(t('Unable to remove this time slot.'));
}
$this->response->redirect($this->helper->url->to('timetableweek', 'index', array('user_id' => $user['id'])));
}
}

View file

@ -2,6 +2,8 @@
namespace Controller;
use Model\NotificationType;
/**
* User controller
*
@ -92,6 +94,11 @@ class User extends Base
if ($user_id !== false) {
$this->projectPermission->addMember($project_id, $user_id);
if (! empty($values['notifications_enabled'])) {
$this->notificationType->saveUserSelectedTypes($user_id, array(NotificationType::TYPE_EMAIL));
}
$this->session->flash(t('User created successfully.'));
$this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user_id)));
}
@ -202,6 +209,8 @@ class User extends Base
$this->response->html($this->layout('user/notifications', array(
'projects' => $this->projectPermission->getMemberProjects($user['id']),
'notifications' => $this->notification->readSettings($user['id']),
'types' => $this->notificationType->getTypes(),
'filters' => $this->notificationFilter->getFilters(),
'user' => $user,
)));
}

View file

@ -0,0 +1,39 @@
<?php
namespace Controller;
/**
* Web notification controller
*
* @package controller
* @author Frederic Guillot
*/
class Webnotification extends Base
{
/**
* Mark all notifications as read
*
* @access public
*/
public function flush()
{
$user_id = $this->userSession->getId();
$this->webNotification->markAllAsRead($user_id);
$this->response->redirect($this->helper->url->to('app', 'notifications', array('user_id' => $user_id)));
}
/**
* Mark a notification as read
*
* @access public
*/
public function remove()
{
$user_id = $this->userSession->getId();
$notification_id = $this->request->getIntegerParam('notification_id');
$this->webNotification->markAsRead($user_id, $notification_id);
$this->response->redirect($this->helper->url->to('app', 'notifications', array('user_id' => $user_id)));
}
}

View file

@ -17,10 +17,13 @@ use Pimple\Container;
* @property \Core\Request $request
* @property \Core\Session $session
* @property \Core\Template $template
* @property \Core\MemoryCache $memoryCache
* @property \Core\OAuth2 $oauth
* @property \Core\Router $router
* @property \Core\Lexer $lexer
* @property \Core\ObjectStorage\ObjectStorageInterface $objectStorage
* @property \Core\Cache\Cache $memoryCache
* @property \Core\Plugin\Hook $hook
* @property \Core\Plugin\Loader $pluginLoader
* @property \Integration\BitbucketWebhook $bitbucketWebhook
* @property \Integration\GithubWebhook $githubWebhook
* @property \Integration\GitlabWebhook $gitlabWebhook
@ -31,22 +34,30 @@ use Pimple\Container;
* @property \Integration\Sendgrid $sendgrid
* @property \Integration\SlackWebhook $slackWebhook
* @property \Integration\Smtp $smtp
* @property \Formatter\ProjectGanttFormatter $projectGanttFormatter
* @property \Formatter\TaskFilterGanttFormatter $taskFilterGanttFormatter
* @property \Formatter\TaskFilterAutoCompleteFormatter $taskFilterAutoCompleteFormatter
* @property \Formatter\TaskFilterCalendarFormatter $taskFilterCalendarFormatter
* @property \Formatter\TaskFilterICalendarFormatter $taskFilterICalendarFormatter
* @property \Model\Acl $acl
* @property \Model\Action $action
* @property \Model\Authentication $authentication
* @property \Model\Board $board
* @property \Model\Budget $budget
* @property \Model\Category $category
* @property \Model\Color $color
* @property \Model\Comment $comment
* @property \Model\Config $config
* @property \Model\Currency $currency
* @property \Model\CustomFilter $customFilter
* @property \Model\DateParser $dateParser
* @property \Model\File $file
* @property \Model\HourlyRate $hourlyRate
* @property \Model\LastLogin $lastLogin
* @property \Model\Link $link
* @property \Model\Notification $notification
* @property \Model\NotificationType $notificationType
* @property \Model\NotificationFilter $notificationFilter
* @property \Model\OverdueNotification $overdueNotification
* @property \Model\WebNotification $webNotification
* @property \Model\Project $project
* @property \Model\ProjectActivity $projectActivity
* @property \Model\ProjectAnalytic $projectAnalytic
@ -57,7 +68,6 @@ use Pimple\Container;
* @property \Model\ProjectPermission $projectPermission
* @property \Model\Subtask $subtask
* @property \Model\SubtaskExport $subtaskExport
* @property \Model\SubtaskForecast $subtaskForecast
* @property \Model\SubtaskTimeTracking $subtaskTimeTracking
* @property \Model\Swimlane $swimlane
* @property \Model\Task $task
@ -73,15 +83,13 @@ use Pimple\Container;
* @property \Model\TaskPosition $taskPosition
* @property \Model\TaskStatus $taskStatus
* @property \Model\TaskValidator $taskValidator
* @property \Model\Timetable $timetable
* @property \Model\TimetableDay $timetableDay
* @property \Model\TimetableExtra $timetableExtra
* @property \Model\TimetableOff $timetableOff
* @property \Model\TimetableWeek $timetableWeek
* @property \Model\Transition $transition
* @property \Model\User $user
* @property \Model\UserSession $userSession
* @property \Model\Webhook $webhook
* @property \Psr\Log\LoggerInterface $logger
* @property \League\HTMLToMarkdown\HtmlConverter $htmlConverter
* @property \PicoDb\Database $db
*/
abstract class Base
{

View file

@ -1,58 +0,0 @@
<?php
namespace Core;
use Pimple\Container;
abstract class Cache
{
/**
* Container instance
*
* @access protected
* @var \Pimple\Container
*/
protected $container;
abstract public function init();
abstract public function set($key, $value);
abstract public function get($key);
abstract public function flush();
abstract public function remove($key);
/**
* Constructor
*
* @access public
* @param \Pimple\Container $container
*/
public function __construct(Container $container)
{
$this->container = $container;
$this->init();
}
/**
* Proxy cache
*
* Note: Arguments must be scalar types
*
* @access public
* @param string $container Container name
* @param string $method Container method
* @return mixed
*/
public function proxy($container, $method)
{
$args = func_get_args();
$key = 'proxy_'.implode('_', $args);
$result = $this->get($key);
if ($result === null) {
$result = call_user_func_array(array($this->container[$container], $method), array_splice($args, 2));
$this->set($key, $result);
}
return $result;
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace Core\Cache;
/**
* Base class for cache drivers
*
* @package cache
* @author Frederic Guillot
*/
abstract class Base
{
/**
* Proxy cache
*
* Note: Arguments must be scalar types
*
* @access public
* @param string $class Class instance
* @param string $method Container method
* @return mixed
*/
public function proxy($class, $method)
{
$args = func_get_args();
array_shift($args);
$key = 'proxy:'.get_class($class).':'.implode(':', $args);
$result = $this->get($key);
if ($result === null) {
$result = call_user_func_array(array($class, $method), array_splice($args, 1));
$this->set($key, $result);
}
return $result;
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace Core\Cache;
/**
* Cache Interface
*
* @package cache
* @author Frederic Guillot
*/
interface CacheInterface
{
/**
* Save a new value in the cache
*
* @access public
* @param string $key
* @param string $value
*/
public function set($key, $value);
/**
* Fetch value from cache
*
* @access public
* @param string $key
* @return mixed Null when not found, cached value otherwise
*/
public function get($key);
/**
* Clear all cache
*
* @access public
*/
public function flush();
/**
* Remove cached value
*
* @access public
* @param string $key
*/
public function remove($key);
}

View file

@ -0,0 +1,65 @@
<?php
namespace Core\Cache;
/**
* Memory Cache
*
* @package cache
* @author Frederic Guillot
*/
class MemoryCache extends Base implements CacheInterface
{
/**
* Container
*
* @access private
* @var array
*/
private $storage = array();
/**
* Save a new value in the cache
*
* @access public
* @param string $key
* @param string $value
*/
public function set($key, $value)
{
$this->storage[$key] = $value;
}
/**
* Fetch value from cache
*
* @access public
* @param string $key
* @return mixed Null when not found, cached value otherwise
*/
public function get($key)
{
return isset($this->storage[$key]) ? $this->storage[$key] : null;
}
/**
* Clear all cache
*
* @access public
*/
public function flush()
{
$this->storage = array();
}
/**
* Remove cached value
*
* @access public
* @param string $key
*/
public function remove($key)
{
unset($this->storage[$key]);
}
}

View file

@ -99,9 +99,18 @@ class HttpClient extends Base
return '';
}
$headers = array_merge(array('User-Agent: '.self::HTTP_USER_AGENT, 'Connection: close'), $headers);
$default_headers = array(
'User-Agent: '.self::HTTP_USER_AGENT,
'Connection: close',
);
$context = stream_context_create(array(
if (HTTP_PROXY_USERNAME) {
$default_headers[] = 'Proxy-Authorization: Basic '.base64_encode(HTTP_PROXY_USERNAME.':'.HTTP_PROXY_PASSWORD);
}
$headers = array_merge($default_headers, $headers);
$context = array(
'http' => array(
'method' => $method,
'protocol_version' => 1.1,
@ -110,9 +119,14 @@ class HttpClient extends Base
'header' => implode("\r\n", $headers),
'content' => $content
)
));
);
$stream = @fopen(trim($url), 'r', false, $context);
if (HTTP_PROXY_HOSTNAME) {
$context['http']['proxy'] = 'tcp://'.HTTP_PROXY_HOSTNAME.':'.HTTP_PROXY_PORT;
$context['http']['request_fulluri'] = true;
}
$stream = @fopen(trim($url), 'r', false, stream_context_create($context));
$response = '';
if (is_resource($stream)) {

View file

@ -1,32 +0,0 @@
<?php
namespace Core;
class MemoryCache extends Cache
{
private $storage = array();
public function init()
{
}
public function set($key, $value)
{
$this->storage[$key] = $value;
}
public function get($key)
{
return isset($this->storage[$key]) ? $this->storage[$key] : null;
}
public function flush()
{
$this->storage = array();
}
public function remove($key)
{
unset($this->storage[$key]);
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace Core;
/**
* Notification Interface
*
* @package core
* @author Frederic Guillot
*/
interface NotificationInterface
{
/**
* Send notification to someone
*
* @access public
* @param array $user
* @param string $event_name
* @param array $event_data
*/
public function send(array $user, $event_name, array $event_data);
}

View file

@ -0,0 +1,149 @@
<?php
namespace Core\ObjectStorage;
/**
* Local File Storage
*
* @package ObjectStorage
* @author Frederic Guillot
*/
class FileStorage implements ObjectStorageInterface
{
/**
* Base path
*
* @access private
* @var string
*/
private $path = '';
/**
* Constructor
*
* @access public
* @param string $path
*/
public function __construct($path)
{
$this->path = $path;
}
/**
* Fetch object contents
*
* @access public
* @param string $key
* @return string
*/
public function get($key)
{
$filename = $this->path.DIRECTORY_SEPARATOR.$key;
if (! file_exists($filename)) {
throw new ObjectStorageException('File not found: '.$filename);
}
return file_get_contents($filename);
}
/**
* Save object
*
* @access public
* @param string $key
* @param string $blob
*/
public function put($key, &$blob)
{
$this->createFolder($key);
if (file_put_contents($this->path.DIRECTORY_SEPARATOR.$key, $blob) === false) {
throw new ObjectStorageException('Unable to write the file: '.$this->path.DIRECTORY_SEPARATOR.$key);
}
}
/**
* Output directly object content
*
* @access public
* @param string $key
*/
public function output($key)
{
$filename = $this->path.DIRECTORY_SEPARATOR.$key;
if (! file_exists($filename)) {
throw new ObjectStorageException('File not found: '.$filename);
}
readfile($filename);
}
/**
* Move local file to object storage
*
* @access public
* @param string $src_filename
* @param string $key
* @return boolean
*/
public function moveFile($src_filename, $key)
{
$this->createFolder($key);
$dst_filename = $this->path.DIRECTORY_SEPARATOR.$key;
if (! rename($src_filename, $dst_filename)) {
throw new ObjectStorageException('Unable to move the file: '.$src_filename.' to '.$dst_filename);
}
return true;
}
/**
* Move uploaded file to object storage
*
* @access public
* @param string $filename
* @param string $key
* @return boolean
*/
public function moveUploadedFile($filename, $key)
{
$this->createFolder($key);
return move_uploaded_file($filename, $this->path.DIRECTORY_SEPARATOR.$key);
}
/**
* Remove object
*
* @access public
* @param string $key
* @return boolean
*/
public function remove($key)
{
$filename = $this->path.DIRECTORY_SEPARATOR.$key;
if (file_exists($filename)) {
return unlink($filename);
}
return false;
}
/**
* Create object folder
*
* @access private
* @param string $key
*/
private function createFolder($key)
{
$folder = strpos($key, '/') !== false ? $this->path.DIRECTORY_SEPARATOR.dirname($key) : $this->path;
if (! is_dir($folder) && ! mkdir($folder, 0755, true)) {
throw new ObjectStorageException('Unable to create folder: '.$folder);
}
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Core\ObjectStorage;
use Exception;
class ObjectStorageException extends Exception
{
}

View file

@ -0,0 +1,67 @@
<?php
namespace Core\ObjectStorage;
/**
* Object Storage Interface
*
* @package ObjectStorage
* @author Frederic Guillot
*/
interface ObjectStorageInterface
{
/**
* Fetch object contents
*
* @access public
* @param string $key
* @return string
*/
public function get($key);
/**
* Save object
*
* @access public
* @param string $key
* @param string $blob
*/
public function put($key, &$blob);
/**
* Output directly object content
*
* @access public
* @param string $key
*/
public function output($key);
/**
* Move local file to object storage
*
* @access public
* @param string $filename
* @param string $key
* @return boolean
*/
public function moveFile($filename, $key);
/**
* Move uploaded file to object storage
*
* @access public
* @param string $filename
* @param string $key
* @return boolean
*/
public function moveUploadedFile($filename, $key);
/**
* Remove object
*
* @access public
* @param string $key
* @return boolean
*/
public function remove($key);
}

View file

@ -0,0 +1,123 @@
<?php
namespace Core\Plugin;
/**
* Plugin Base class
*
* @package plugin
* @author Frederic Guillot
*/
abstract class Base extends \Core\Base
{
/**
* Method called for each request
*
* @abstract
* @access public
*/
abstract public function initialize();
/**
* Override default CSP rules
*
* @access public
* @param array $rules
*/
public function setContentSecurityPolicy(array $rules)
{
$this->container['cspRules'] = $rules;
}
/**
* Returns all classes that needs to be stored in the DI container
*
* @access public
* @return array
*/
public function getClasses()
{
return array();
}
/**
* Listen on internal events
*
* @access public
* @param string $event
* @param callable $callback
*/
public function on($event, $callback)
{
$container = $this->container;
$this->container['dispatcher']->addListener($event, function() use ($container, $callback) {
call_user_func($callback, $container);
});
}
/**
* Get plugin name
*
* This method should be overrided by your Plugin class
*
* @access public
* @return string
*/
public function getPluginName()
{
return ucfirst(substr(get_called_class(), 7, -7));
}
/**
* Get plugin description
*
* This method should be overrided by your Plugin class
*
* @access public
* @return string
*/
public function getPluginDescription()
{
return '';
}
/**
* Get plugin author
*
* This method should be overrided by your Plugin class
*
* @access public
* @return string
*/
public function getPluginAuthor()
{
return '?';
}
/**
* Get plugin version
*
* This method should be overrided by your Plugin class
*
* @access public
* @return string
*/
public function getPluginVersion()
{
return '?';
}
/**
* Get plugin homepage
*
* This method should be overrided by your Plugin class
*
* @access public
* @return string
*/
public function getPluginHomepage()
{
return '';
}
}

View file

@ -0,0 +1,99 @@
<?php
namespace Core\Plugin;
/**
* Plugin Hooks Handler
*
* @package plugin
* @author Frederic Guillot
*/
class Hook
{
/**
* List of hooks
*
* @access private
* @var array
*/
private $hooks = array();
/**
* Bind something on a hook
*
* @access public
* @param string $hook
* @param mixed $value
*/
public function on($hook, $value)
{
if (! isset($this->hooks[$hook])) {
$this->hooks[$hook] = array();
}
$this->hooks[$hook][] = $value;
}
/**
* Get all bindings for a hook
*
* @access public
* @param string $hook
* @return array
*/
public function getListeners($hook)
{
return isset($this->hooks[$hook]) ? $this->hooks[$hook] : array();
}
/**
* Return true if the hook is used
*
* @access public
* @param string $hook
* @return boolean
*/
public function exists($hook)
{
return isset($this->hooks[$hook]);
}
/**
* Merge listener results with input array
*
* @access public
* @param string $hook
* @param array $values
* @param array $params
* @return array
*/
public function merge($hook, array &$values, array $params = array())
{
foreach ($this->getListeners($hook) as $listener) {
$result = call_user_func_array($listener, $params);
if (is_array($result) && ! empty($result)) {
$values = array_merge($values, $result);
}
}
return $values;
}
/**
* Execute only first listener
*
* @access public
* @param string $hook
* @param array $params
* @return mixed
*/
public function first($hook, array $params = array())
{
foreach ($this->getListeners($hook) as $listener) {
return call_user_func_array($listener, $params);
}
return null;
}
}

View file

@ -0,0 +1,148 @@
<?php
namespace Core\Plugin;
use DirectoryIterator;
use PDOException;
use Core\Tool;
/**
* Plugin Loader
*
* @package plugin
* @author Frederic Guillot
*/
class Loader extends \Core\Base
{
/**
* Schema version table for plugins
*
* @var string
*/
const TABLE_SCHEMA = 'plugin_schema_versions';
/**
* Plugin instances
*
* @access public
* @var array
*/
public $plugins = array();
/**
* Scan plugin folder and load plugins
*
* @access public
*/
public function scan()
{
if (file_exists(PLUGINS_DIR)) {
$dir = new DirectoryIterator(PLUGINS_DIR);
foreach ($dir as $fileinfo) {
if (! $fileinfo->isDot() && $fileinfo->isDir()) {
$plugin = $fileinfo->getFilename();
$this->loadSchema($plugin);
$this->load($plugin);
}
}
}
}
/**
* Load plugin
*
* @access public
* @param string $plugin
*/
public function load($plugin)
{
$class = '\Plugin\\'.$plugin.'\\Plugin';
$instance = new $class($this->container);
Tool::buildDic($this->container, $instance->getClasses());
$instance->initialize();
$this->plugins[] = $instance;
}
/**
* Load plugin schema
*
* @access public
* @param string $plugin
*/
public function loadSchema($plugin)
{
$filename = PLUGINS_DIR.'/'.$plugin.'/Schema/'.ucfirst(DB_DRIVER).'.php';
if (file_exists($filename)) {
require_once($filename);
$this->migrateSchema($plugin);
}
}
/**
* Execute plugin schema migrations
*
* @access public
* @param string $plugin
*/
public function migrateSchema($plugin)
{
$last_version = constant('\Plugin\\'.$plugin.'\Schema\VERSION');
$current_version = $this->getSchemaVersion($plugin);
try {
$this->db->startTransaction();
$this->db->getDriver()->disableForeignKeys();
for ($i = $current_version + 1; $i <= $last_version; $i++) {
$function_name = '\Plugin\\'.$plugin.'\Schema\version_'.$i;
if (function_exists($function_name)) {
call_user_func($function_name, $this->db->getConnection());
}
}
$this->db->getDriver()->enableForeignKeys();
$this->db->closeTransaction();
$this->setSchemaVersion($plugin, $i - 1);
}
catch (PDOException $e) {
$this->db->cancelTransaction();
$this->db->getDriver()->enableForeignKeys();
die('Unable to migrate schema for the plugin: '.$plugin.' => '.$e->getMessage());
}
}
/**
* Get current plugin schema version
*
* @access public
* @param string $plugin
* @return integer
*/
public function getSchemaVersion($plugin)
{
return (int) $this->db->table(self::TABLE_SCHEMA)->eq('plugin', strtolower($plugin))->findOneColumn('version');
}
/**
* Save last plugin schema version
*
* @access public
* @param string $plugin
* @param integer $version
* @return boolean
*/
public function setSchemaVersion($plugin, $version)
{
$dictionary = array(
strtolower($plugin) => $version
);
return $this->db->getDriver()->upsert(self::TABLE_SCHEMA, 'plugin', 'version', $dictionary);
}
}

View file

@ -66,7 +66,13 @@ class Response
*/
public function redirect($url)
{
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
header('X-Ajax-Redirect: '.$url);
}
else {
header('Location: '.$url);
}
exit;
}

View file

@ -80,7 +80,7 @@ class Router extends Base
$path = substr($path, 0, - strlen($query_string) - 1);
}
if ($path{0} === '/') {
if (! empty($path) && $path{0} === '/') {
$path = substr($path, 1);
}
@ -206,56 +206,22 @@ class Router extends Base
* @access public
* @param string $uri
* @param string $query_string
* @return boolean
*/
public function dispatch($uri, $query_string = '')
{
if (! empty($_GET['controller']) && ! empty($_GET['action'])) {
$controller = $this->sanitize($_GET['controller'], 'app');
$action = $this->sanitize($_GET['action'], 'index');
$this->controller = $this->sanitize($_GET['controller'], 'app');
$this->action = $this->sanitize($_GET['action'], 'index');
$plugin = ! empty($_GET['plugin']) ? $this->sanitize($_GET['plugin'], '') : '';
}
else {
list($controller, $action) = $this->findRoute($this->getPath($uri, $query_string));
list($this->controller, $this->action) = $this->findRoute($this->getPath($uri, $query_string)); // TODO: add plugin for routes
$plugin = '';
}
return $this->load(
__DIR__.'/../Controller/'.ucfirst($controller).'.php',
$controller,
'\Controller\\'.ucfirst($controller),
$action
);
}
/**
* Load a controller and execute the action
*
* @access private
* @param string $filename
* @param string $controller
* @param string $class
* @param string $method
* @return bool
*/
private function load($filename, $controller, $class, $method)
{
if (file_exists($filename)) {
require $filename;
if (! method_exists($class, $method)) {
return false;
}
$this->action = $method;
$this->controller = $controller;
$class = empty($plugin) ? '\Controller\\'.ucfirst($this->controller) : '\Plugin\\'.ucfirst($plugin).'\Controller\\'.ucfirst($this->controller);
$instance = new $class($this->container);
$instance->beforeAction($controller, $method);
$instance->$method();
return true;
}
return false;
$instance->beforeAction($this->controller, $this->action);
$instance->{$this->action}();
}
}

View file

@ -45,7 +45,9 @@ class Session implements ArrayAccess
ini_set('session.use_only_cookies', '1');
// Enable strict mode
if (version_compare(PHP_VERSION, '7.0.0') < 0) {
ini_set('session.use_strict_mode', '1');
}
// Ensure session ID integrity
ini_set('session.entropy_file', '/dev/urandom');

View file

@ -2,8 +2,6 @@
namespace Core;
use LogicException;
/**
* Template class
*
@ -13,11 +11,12 @@ use LogicException;
class Template extends Helper
{
/**
* Template path
* List of template overrides
*
* @var string
* @access private
* @var array
*/
const PATH = 'app/Template/';
private $overrides = array();
/**
* Render a template
@ -33,16 +32,10 @@ class Template extends Helper
*/
public function render($__template_name, array $__template_args = array())
{
$__template_file = self::PATH.$__template_name.'.php';
if (! file_exists($__template_file)) {
throw new LogicException('Unable to load the template: "'.$__template_name.'"');
}
extract($__template_args);
ob_start();
include $__template_file;
include $this->getTemplateFile($__template_name);
return ob_get_clean();
}
@ -62,4 +55,41 @@ class Template extends Helper
$template_args + array('content_for_layout' => $this->render($template_name, $template_args))
);
}
/**
* Define a new template override
*
* @access public
* @param string $original_template
* @param string $new_template
*/
public function setTemplateOverride($original_template, $new_template)
{
$this->overrides[$original_template] = $new_template;
}
/**
* Find template filename
*
* Core template name: 'task/show'
* Plugin template name: 'myplugin:task/show'
*
* @access public
* @param string $template_name
* @return string
*/
public function getTemplateFile($template_name)
{
$template_name = isset($this->overrides[$template_name]) ? $this->overrides[$template_name] : $template_name;
if (strpos($template_name, ':') !== false) {
list($plugin, $template) = explode(':', $template_name);
$path = __DIR__.'/../../plugins/'.ucfirst($plugin).'/Template/'.$template.'.php';
}
else {
$path = __DIR__.'/../Template/'.$template_name.'.php';
}
return $path;
}
}

View file

@ -2,6 +2,8 @@
namespace Core;
use Pimple\Container;
/**
* Tool class
*
@ -23,7 +25,6 @@ class Tool
$fp = fopen($filename, 'w');
if (is_resource($fp)) {
foreach ($rows as $fields) {
fputcsv($fp, $fields);
}
@ -51,4 +52,102 @@ class Tool
return $identifier;
}
/**
* Build dependency injection container from an array
*
* @static
* @access public
* @param Container $container
* @param array $namespaces
*/
public static function buildDIC(Container $container, array $namespaces)
{
foreach ($namespaces as $namespace => $classes) {
foreach ($classes as $name) {
$class = '\\'.$namespace.'\\'.$name;
$container[lcfirst($name)] = function ($c) use ($class) {
return new $class($c);
};
}
}
}
/**
* Generate a jpeg thumbnail from an image
*
* @static
* @access public
* @param string $src_file Source file image
* @param string $dst_file Destination file image
* @param integer $resize_width Desired image width
* @param integer $resize_height Desired image height
*/
public static function generateThumbnail($src_file, $dst_file, $resize_width = 250, $resize_height = 100)
{
$metadata = getimagesize($src_file);
$src_width = $metadata[0];
$src_height = $metadata[1];
$dst_y = 0;
$dst_x = 0;
if (empty($metadata['mime'])) {
return;
}
if ($resize_width == 0 && $resize_height == 0) {
$resize_width = 100;
$resize_height = 100;
}
if ($resize_width > 0 && $resize_height == 0) {
$dst_width = $resize_width;
$dst_height = floor($src_height * ($resize_width / $src_width));
$dst_image = imagecreatetruecolor($dst_width, $dst_height);
}
elseif ($resize_width == 0 && $resize_height > 0) {
$dst_width = floor($src_width * ($resize_height / $src_height));
$dst_height = $resize_height;
$dst_image = imagecreatetruecolor($dst_width, $dst_height);
}
else {
$src_ratio = $src_width / $src_height;
$resize_ratio = $resize_width / $resize_height;
if ($src_ratio <= $resize_ratio) {
$dst_width = $resize_width;
$dst_height = floor($src_height * ($resize_width / $src_width));
$dst_y = ($dst_height - $resize_height) / 2 * (-1);
}
else {
$dst_width = floor($src_width * ($resize_height / $src_height));
$dst_height = $resize_height;
$dst_x = ($dst_width - $resize_width) / 2 * (-1);
}
$dst_image = imagecreatetruecolor($resize_width, $resize_height);
}
switch ($metadata['mime']) {
case 'image/jpeg':
case 'image/jpg':
$src_image = imagecreatefromjpeg($src_file);
break;
case 'image/png':
$src_image = imagecreatefrompng($src_file);
break;
case 'image/gif':
$src_image = imagecreatefromgif($src_file);
break;
default:
return;
}
imagecopyresampled($dst_image, $src_image, $dst_x, $dst_y, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
imagejpeg($dst_image, $dst_file);
imagedestroy($dst_image);
}
}

View file

@ -15,7 +15,7 @@ class Translator
*
* @var string
*/
const PATH = 'app/Locale/';
const PATH = 'app/Locale';
/**
* Locale
@ -196,18 +196,27 @@ class Translator
* @static
* @access public
* @param string $language Locale code: fr_FR
* @param string $path Locale folder
*/
public static function load($language)
public static function load($language, $path = self::PATH)
{
setlocale(LC_TIME, $language.'.UTF-8', $language);
$filename = self::PATH.$language.DIRECTORY_SEPARATOR.'translations.php';
$filename = $path.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'translations.php';
if (file_exists($filename)) {
self::$locales = require $filename;
self::$locales = array_merge(self::$locales, require($filename));
}
else {
}
/**
* Clear locales stored in memory
*
* @static
* @access public
*/
public static function unload()
{
self::$locales = array();
}
}
}

View file

@ -0,0 +1,14 @@
<?php
namespace Formatter;
/**
* Formatter Interface
*
* @package formatter
* @author Frederic Guillot
*/
interface FormatterInterface
{
public function format();
}

View file

@ -0,0 +1,90 @@
<?php
namespace Formatter;
use Model\Project;
/**
* Gantt chart formatter for projects
*
* @package formatter
* @author Frederic Guillot
*/
class ProjectGanttFormatter extends Project implements FormatterInterface
{
/**
* List of projects
*
* @access private
* @var array
*/
private $projects = array();
/**
* Filter projects to generate the Gantt chart
*
* @access public
* @param int[] $project_ids
* @return ProjectGanttFormatter
*/
public function filter(array $project_ids)
{
if (empty($project_ids)) {
$this->projects = array();
}
else {
$this->projects = $this->db
->table(self::TABLE)
->asc('start_date')
->in('id', $project_ids)
->eq('is_active', self::ACTIVE)
->eq('is_private', 0)
->findAll();
}
return $this;
}
/**
* Format projects to be displayed in the Gantt chart
*
* @access public
* @return array
*/
public function format()
{
$colors = $this->color->getDefaultColors();
$bars = array();
foreach ($this->projects as $project) {
$start = empty($project['start_date']) ? time() : strtotime($project['start_date']);
$end = empty($project['end_date']) ? $start : strtotime($project['end_date']);
$color = next($colors) ?: reset($colors);
$bars[] = array(
'type' => 'project',
'id' => $project['id'],
'title' => $project['name'],
'start' => array(
(int) date('Y', $start),
(int) date('n', $start),
(int) date('j', $start),
),
'end' => array(
(int) date('Y', $end),
(int) date('n', $end),
(int) date('j', $end),
),
'link' => $this->helper->url->href('project', 'show', array('project_id' => $project['id'])),
'board_link' => $this->helper->url->href('board', 'show', array('project_id' => $project['id'])),
'gantt_link' => $this->helper->url->href('gantt', 'project', array('project_id' => $project['id'])),
'color' => $color,
'not_defined' => empty($project['start_date']) || empty($project['end_date']),
'users' => $this->projectPermission->getProjectUsers($project['id']),
);
}
return $bars;
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace Formatter;
use Model\Task;
use Model\TaskFilter;
/**
* Autocomplete formatter for task filter
*
* @package formatter
* @author Frederic Guillot
*/
class TaskFilterAutoCompleteFormatter extends TaskFilter implements FormatterInterface
{
/**
* Format the tasks for the ajax autocompletion
*
* @access public
* @return array
*/
public function format()
{
$tasks = $this->query->columns(Task::TABLE.'.id', Task::TABLE.'.title')->findAll();
foreach ($tasks as &$task) {
$task['value'] = $task['title'];
$task['label'] = '#'.$task['id'].' - '.$task['title'];
}
return $tasks;
}
}

View file

@ -0,0 +1,76 @@
<?php
namespace Formatter;
use Model\TaskFilter;
/**
* Common class to handle calendar events
*
* @package formatter
* @author Frederic Guillot
*/
abstract class TaskFilterCalendarEvent extends TaskFilter
{
/**
* Column used for event start date
*
* @access protected
* @var string
*/
protected $startColumn = 'date_started';
/**
* Column used for event end date
*
* @access protected
* @var string
*/
protected $endColumn = 'date_completed';
/**
* Full day event flag
*
* @access private
* @var boolean
*/
private $fullDay = false;
/**
* Transform results to calendar events
*
* @access public
* @param string $start_column Column name for the start date
* @param string $end_column Column name for the end date
* @return TaskFilterCalendarEvent
*/
public function setColumns($start_column, $end_column = '')
{
$this->startColumn = $start_column;
$this->endColumn = $end_column ?: $start_column;
return $this;
}
/**
* When called calendar events will be full day
*
* @access public
* @return TaskFilterCalendarEvent
*/
public function setFullDay()
{
$this->fullDay = true;
return $this;
}
/**
* Return true if the events are full day
*
* @access public
* @return boolean
*/
public function isFullDay()
{
return $this->fullDay;
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace Formatter;
/**
* Calendar event formatter for task filter
*
* @package formatter
* @author Frederic Guillot
*/
class TaskFilterCalendarFormatter extends TaskFilterCalendarEvent implements FormatterInterface
{
/**
* Transform tasks to calendar events
*
* @access public
* @return array
*/
public function format()
{
$events = array();
foreach ($this->query->findAll() as $task) {
$events[] = array(
'timezoneParam' => $this->config->getCurrentTimezone(),
'id' => $task['id'],
'title' => t('#%d', $task['id']).' '.$task['title'],
'backgroundColor' => $this->color->getBackgroundColor($task['color_id']),
'borderColor' => $this->color->getBorderColor($task['color_id']),
'textColor' => 'black',
'url' => $this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])),
'start' => date($this->getDateTimeFormat(), $task[$this->startColumn]),
'end' => date($this->getDateTimeFormat(), $task[$this->endColumn] ?: time()),
'editable' => $this->isFullDay(),
'allday' => $this->isFullDay(),
);
}
return $events;
}
/**
* Get DateTime format for event
*
* @access private
* @return string
*/
private function getDateTimeFormat()
{
return $this->isFullDay() ? 'Y-m-d' : 'Y-m-d\TH:i:s';
}
}

View file

@ -0,0 +1,78 @@
<?php
namespace Formatter;
use Model\TaskFilter;
/**
* Gantt chart formatter for task filter
*
* @package formatter
* @author Frederic Guillot
*/
class TaskFilterGanttFormatter extends TaskFilter implements FormatterInterface
{
/**
* Local cache for project columns
*
* @access private
* @var array
*/
private $columns = array();
/**
* Format tasks to be displayed in the Gantt chart
*
* @access public
* @return array
*/
public function format()
{
$bars = array();
foreach ($this->query->findAll() as $task) {
$bars[] = $this->formatTask($task);
}
return $bars;
}
/**
* Format a single task
*
* @access private
* @param array $task
* @return array
*/
private function formatTask(array $task)
{
if (! isset($this->columns[$task['project_id']])) {
$this->columns[$task['project_id']] = $this->board->getColumnsList($task['project_id']);
}
$start = $task['date_started'] ?: time();
$end = $task['date_due'] ?: $start;
return array(
'type' => 'task',
'id' => $task['id'],
'title' => $task['title'],
'start' => array(
(int) date('Y', $start),
(int) date('n', $start),
(int) date('j', $start),
),
'end' => array(
(int) date('Y', $end),
(int) date('n', $end),
(int) date('j', $end),
),
'column_title' => $task['column_name'],
'assignee' => $task['assignee_name'] ?: $task['assignee_username'],
'progress' => $this->task->getProgress($task, $this->columns[$task['project_id']]).'%',
'link' => $this->helper->url->href('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])),
'color' => $this->color->getColorProperties($task['color_id']),
'not_defined' => empty($task['date_due']) || empty($task['date_started']),
);
}
}

View file

@ -0,0 +1,135 @@
<?php
namespace Formatter;
use DateTime;
use Eluceo\iCal\Component\Calendar;
use Eluceo\iCal\Component\Event;
use Eluceo\iCal\Property\Event\Attendees;
/**
* iCal event formatter for task filter
*
* @package formatter
* @author Frederic Guillot
*/
class TaskFilterICalendarFormatter extends TaskFilterCalendarEvent implements FormatterInterface
{
/**
* Calendar object
*
* @access private
* @var \Eluceo\iCal\Component\Calendar
*/
private $vCalendar;
/**
* Get Ical events
*
* @access public
* @return string
*/
public function format()
{
return $this->vCalendar->render();
}
/**
* Set calendar object
*
* @access public
* @param \Eluceo\iCal\Component\Calendar $vCalendar
* @return TaskFilterICalendarFormatter
*/
public function setCalendar(Calendar $vCalendar)
{
$this->vCalendar = $vCalendar;
return $this;
}
/**
* Transform results to ical events
*
* @access public
* @return TaskFilterICalendarFormatter
*/
public function addDateTimeEvents()
{
foreach ($this->query->findAll() as $task) {
$start = new DateTime;
$start->setTimestamp($task[$this->startColumn]);
$end = new DateTime;
$end->setTimestamp($task[$this->endColumn] ?: time());
$vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-'.$this->startColumn.'-'.$this->endColumn);
$vEvent->setDtStart($start);
$vEvent->setDtEnd($end);
$this->vCalendar->addComponent($vEvent);
}
return $this;
}
/**
* Transform results to all day ical events
*
* @access public
* @return TaskFilterICalendarFormatter
*/
public function addFullDayEvents()
{
foreach ($this->query->findAll() as $task) {
$date = new DateTime;
$date->setTimestamp($task[$this->startColumn]);
$vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-'.$this->startColumn);
$vEvent->setDtStart($date);
$vEvent->setDtEnd($date);
$vEvent->setNoTime(true);
$this->vCalendar->addComponent($vEvent);
}
return $this;
}
/**
* Get common events for task ical events
*
* @access protected
* @param array $task
* @param string $uid
* @return Event
*/
protected function getTaskIcalEvent(array &$task, $uid)
{
$dateCreation = new DateTime;
$dateCreation->setTimestamp($task['date_creation']);
$dateModif = new DateTime;
$dateModif->setTimestamp($task['date_modification']);
$vEvent = new Event($uid);
$vEvent->setCreated($dateCreation);
$vEvent->setModified($dateModif);
$vEvent->setUseTimezone(true);
$vEvent->setSummary(t('#%d', $task['id']).' '.$task['title']);
$vEvent->setUrl($this->helper->url->base().$this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
if (! empty($task['owner_id'])) {
$vEvent->setOrganizer($task['assignee_name'] ?: $task['assignee_username'], $task['assignee_email']);
}
if (! empty($task['creator_id'])) {
$attendees = new Attendees;
$attendees->add('MAILTO:'.($task['creator_email'] ?: $task['creator_username'].'@kanboard.local'));
$vEvent->setAttendees($attendees);
}
return $vEvent;
}
}

View file

@ -67,9 +67,11 @@ class App extends \Core\Base
if (isset($this->session['flash_message'])) {
$html = '<div class="alert alert-success alert-fade-out">'.$this->helper->e($this->session['flash_message']).'</div>';
unset($this->session['flash_message']);
unset($this->session['flash_error_message']);
}
else if (isset($this->session['flash_error_message'])) {
$html = '<div class="alert alert-error">'.$this->helper->e($this->session['flash_error_message']).'</div>';
unset($this->session['flash_message']);
unset($this->session['flash_error_message']);
}

View file

@ -103,6 +103,26 @@ class Form extends \Core\Base
return '<label><input type="radio" name="'.$name.'" class="'.$class.'" value="'.$this->helper->e($value).'" '.($selected ? 'checked="checked"' : '').'> '.$this->helper->e($label).'</label>';
}
/**
* Display a checkboxes group
*
* @access public
* @param string $name Field name
* @param array $options Options
* @param array $values Form values
* @return string
*/
public function checkboxes($name, array $options, array $values = array())
{
$html = '';
foreach ($options as $value => $label) {
$html .= $this->checkbox($name.'['.$value.']', $label, $value, isset($values[$name]) && in_array($value, $values[$name]));
}
return $html;
}
/**
* Display a checkbox field
*

View file

@ -0,0 +1,64 @@
<?php
namespace Helper;
/**
* Template Hook helpers
*
* @package helper
* @author Frederic Guillot
*/
class Hook extends \Core\Base
{
/**
* Add assets JS or CSS
*
* @access public
* @param string $type
* @param string $hook
* @return string
*/
public function asset($type, $hook)
{
$buffer = '';
foreach ($this->hook->getListeners($hook) as $file) {
$buffer .= $this->helper->asset->$type($file);
}
return $buffer;
}
/**
* Render all attached hooks
*
* @access public
* @param string $hook
* @param array $variables
* @return string
*/
public function render($hook, array $variables = array())
{
$buffer = '';
foreach ($this->hook->getListeners($hook) as $template) {
$buffer .= $this->template->render($template, $variables);
}
return $buffer;
}
/**
* Attach a template to a hook
*
* @access public
* @param string $hook
* @param string $template
* @return \Helper\Hook
*/
public function attach($hook, $template)
{
$this->hook->on($hook, $template);
return $this;
}
}

View file

@ -10,6 +10,17 @@ namespace Helper;
*/
class User extends \Core\Base
{
/**
* Return true if the logged user as unread notifications
*
* @access public
* @return boolean
*/
public function hasNotifications()
{
return $this->webNotification->hasNotifications($this->userSession->getId());
}
/**
* Get initials from a user
*
@ -99,7 +110,7 @@ class User extends \Core\Base
return true;
}
return $this->memoryCache->proxy('acl', 'handleProjectAdminPermissions', $project_id);
return $this->memoryCache->proxy($this->container['acl'], 'handleProjectAdminPermissions', $project_id);
}
/**
@ -114,7 +125,7 @@ class User extends \Core\Base
return true;
}
return $this->memoryCache->proxy('acl', 'handleProjectManagerPermissions', $project_id);
return $this->memoryCache->proxy($this->container['acl'], 'handleProjectManagerPermissions', $project_id);
}
/**

View file

@ -2,7 +2,6 @@
namespace Integration;
use HTML_To_Markdown;
use Core\Tool;
/**
@ -76,8 +75,7 @@ class Mailgun extends \Core\Base
// Get the Markdown contents
if (! empty($payload['stripped-html'])) {
$markdown = new HTML_To_Markdown($payload['stripped-html'], array('strip_tags' => true));
$description = $markdown->output();
$description = $this->htmlConverter->convert($payload['stripped-html']);
}
else if (! empty($payload['stripped-text'])) {
$description = $payload['stripped-text'];

View file

@ -2,8 +2,6 @@
namespace Integration;
use HTML_To_Markdown;
/**
* Postmark integration
*
@ -76,8 +74,7 @@ class Postmark extends \Core\Base
// Get the Markdown contents
if (! empty($payload['HtmlBody'])) {
$markdown = new HTML_To_Markdown($payload['HtmlBody'], array('strip_tags' => true));
$description = $markdown->output();
$description = $this->htmlConverter->convert($payload['HtmlBody']);
}
else if (! empty($payload['TextBody'])) {
$description = $payload['TextBody'];

View file

@ -2,7 +2,6 @@
namespace Integration;
use HTML_To_Markdown;
use Core\Tool;
/**
@ -79,8 +78,7 @@ class Sendgrid extends \Core\Base
// Get the Markdown contents
if (! empty($payload['html'])) {
$markdown = new HTML_To_Markdown($payload['html'], array('strip_tags' => true));
$description = $markdown->output();
$description = $this->htmlConverter->convert($payload['html']);
}
else if (! empty($payload['text'])) {
$description = $payload['text'];

View file

@ -36,7 +36,7 @@ class SlackWebhook extends \Core\Base
}
$options = $this->projectIntegration->getParameters($project_id);
return $options['slack_webhook_url'];
return isset($options['slack_webhook_url']) ? $options['slack_webhook_url'] : '';
}
/**
@ -55,11 +55,11 @@ class SlackWebhook extends \Core\Base
}
$options = $this->projectIntegration->getParameters($project_id);
return $options['slack_webhook_channel'];
return isset($options['slack_webhook_channel']) ? $options['slack_webhook_channel'] : '';
}
/**
* Send message to the incoming Slack webhook
* Send notification to Slack
*
* @access public
* @param integer $project_id Project id
@ -76,23 +76,52 @@ class SlackWebhook extends \Core\Base
$event['event_name'] = $event_name;
$event['author'] = $this->user->getFullname($this->session['user']);
$message = '*['.$project['name'].']* ';
$message .= str_replace('&quot;', '"', $this->projectActivity->getTitle($event));
$message .= isset($event['task']['title']) ? ' ('.$event['task']['title'].')' : '';
if ($this->config->get('application_url')) {
$message .= ' - <'.$this->helper->url->href('task', 'show', array('task_id' => $task_id, 'project_id' => $project_id), false, '', true);
$message .= '|'.t('view the task on Kanboard').'>';
}
$this->sendMessage($project_id, $message);
}
}
/**
* Send message to Slack
*
* @access public
* @param integer $project_id
* @param string $message
*/
public function sendMessage($project_id, $message)
{
$payload = array(
'text' => '*['.$project['name'].']* '.str_replace('&quot;', '"', $this->projectActivity->getTitle($event)).(isset($event['task']['title']) ? ' ('.$event['task']['title'].')' : ''),
'text' => $message,
'username' => 'Kanboard',
'icon_url' => 'http://kanboard.net/assets/img/favicon.png',
);
if ($this->config->get('application_url')) {
$payload['text'] .= ' - <'.$this->helper->url->href('task', 'show', array('task_id' => $task_id, 'project_id' => $project_id), false, '', true);
$payload['text'] .= '|'.t('view the task on Kanboard').'>';
$this->sendPayload($project_id, $payload);
}
/**
* Send payload to Slack
*
* @access public
* @param integer $project_id
* @param array $payload
*/
public function sendPayload($project_id, array $payload)
{
$channel = $this->getChannel($project_id);
if (! empty($channel)) {
$payload['channel'] = $channel;
}
$this->httpClient->postJson($this->getWebhookUrl($project_id), $payload);
}
}
}

View file

@ -395,8 +395,6 @@ return array(
'Remote' => 'Vzdálený',
'Enabled' => 'Povoleno',
'Disabled' => 'Zakázáno',
'Google account linked' => 'Google účet byl propojen',
'Github account linked' => 'Mit Githubaccount verbunden',
'Username:' => 'Uživatelské jméno:',
'Name:' => 'Jméno:',
'Email:' => 'e-mail',
@ -667,75 +665,26 @@ return array(
'Horizontal scrolling' => 'Horizontální rolování',
'Compact/wide view' => 'Kompaktní/plné zobrazení',
'No results match:' => 'Žádná shoda:',
'Remove hourly rate' => 'Stundensatz entfernen',
'Do you really want to remove this hourly rate?' => 'Opravdu chcete odstranit tuto hodinovou sazbu?',
'Hourly rates' => 'Hodinové sazby',
'Hourly rate' => 'Hodinová sazba',
'Currency' => 'Měna',
'Effective date' => 'Datum účinnosti',
'Add new rate' => 'Přidat novou hodinovou sazbu',
'Rate removed successfully.' => 'Sazba byla úspěšně odstraněna',
'Unable to remove this rate.' => 'Sazbu nelze odstranit.',
'Unable to save the hourly rate.' => 'Hodinovou sazbu nelze uložit',
'Hourly rate created successfully.' => 'Hodinová sazba byla úspěšně vytvořena.',
'Start time' => 'Počáteční datum',
'End time' => 'Konečné datum',
'Comment' => 'Komentář',
'All day' => 'Všechny dny',
'Day' => 'Den',
'Manage timetable' => 'Spravovat pracovní dobu',
'Overtime timetable' => 'Přesčasy',
'Time off timetable' => 'Pracovní volno',
'Timetable' => 'Pracovní doba',
'Work timetable' => 'Pracovní doba',
'Week timetable' => 'Týdenní pracovní doba',
'Day timetable' => 'Denní pracovní doba',
'From' => 'Od',
'To' => 'Do',
'Time slot created successfully.' => 'Časový úsek byl úspěšně vytvořen.',
'Unable to save this time slot.' => 'Nelze uložit tento časový úsek.',
'Time slot removed successfully.' => 'Časový úsek byl odstraněn.',
'Unable to remove this time slot.' => 'Nelze odstranit tento časový úsek',
'Do you really want to remove this time slot?' => 'Opravdu chcete odstranit tento časový úsek?',
'Remove time slot' => 'Odstranit časový úsek',
'Add new time slot' => 'Přidat nový časový úsek',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Tato pracovní doba se použije když je zaškrtnuto políčko "Celý den" pro plánovanou pracovní dobu i přesčas .',
'Files' => 'Soubory',
'Images' => 'Obrázky',
'Private project' => 'Soukromý projekt',
'Amount' => 'Částka',
// 'AUD - Australian Dollar' => '',
'Budget' => 'Rozpočet',
'Budget line' => 'Položka rozpočtu',
'Budget line removed successfully.' => 'Položka rozpočtu byla odstraněna',
'Budget lines' => 'Položky rozpočtu',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
'Cost' => 'Cena',
'Cost breakdown' => 'Rozpis nákladů',
'Custom Stylesheet' => 'Vlastní šablony stylů',
'download' => 'Stáhnout',
'Do you really want to remove this budget line?' => 'Opravdu chcete odstranit tuto rozpočtovou řádku?',
'EUR - Euro' => 'EUR - Euro',
'Expenses' => 'Náklady',
'GBP - British Pound' => 'GBP - Britská Libra',
'INR - Indian Rupee' => 'INR - Indische Rupien',
'JPY - Japanese Yen' => 'JPY - Japanischer Yen',
'New budget line' => 'Nová položka rozpočtu',
'NZD - New Zealand Dollar' => 'NZD - Neuseeland-Dollar',
'Remove a budget line' => 'Budgetlinie entfernen',
'Remove budget line' => 'Budgetlinie entfernen',
'RSD - Serbian dinar' => 'RSD - Serbische Dinar',
'The budget line have been created successfully.' => 'Položka rozpočtu byla úspěšně vytvořena.',
'Unable to create the budget line.' => 'Nelze vytvořit rozpočtovou řádku.',
'Unable to remove this budget line.' => 'Nelze vyjmout rozpočtovou řádku.',
'USD - US Dollar' => 'USD - US Dollar',
'Remaining' => 'Zbývající',
'Destination column' => 'Cílový sloupec',
'Move the task to another column when assigned to a user' => 'Přesunout úkol do jiného sloupce, když je úkol přiřazen uživateli.',
'Move the task to another column when assignee is cleared' => 'Přesunout úkol do jiného sloupce, když je pověření uživatele vymazáno.',
'Source column' => 'Zdrojový sloupec',
// 'Show subtask estimates (forecast of future work)' => '',
'Transitions' => 'Změny etap',
'Executer' => 'Vykonavatel',
'Time spent in the column' => 'Trvání jednotlivých etap',
@ -746,7 +695,6 @@ return array(
'Rate' => 'Kurz',
'Change reference currency' => 'Změnit referenční měnu',
'Add a new currency rate' => 'Přidat nový směnný kurz',
'Currency rates are used to calculate project budget.' => 'Měnové sazby se používají k výpočtu rozpočtu projektu.',
'Reference currency' => 'Referenční měna',
'The currency rate have been added successfully.' => 'Směnný kurz byl úspěšně přidán.',
'Unable to add this currency rate.' => 'Nelze přidat tento směnný kurz',
@ -878,9 +826,6 @@ return array(
'%s moved the task #%d to the first swimlane' => '%s hat die Aufgabe #%d in die erste Swimlane verschoben',
'%s moved the task #%d to the swimlane "%s"' => '%s hat die Aufgabe #%d in die Swimlane "%s" verschoben',
// 'Swimlane' => '',
'Budget overview' => 'Budget Übersicht',
'Type' => 'Typ',
'There is not enough data to show something.' => 'Es gibt nicht genug Daten für die Anzeige',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
@ -1067,4 +1012,48 @@ return array(
// 'Table of contents' => '',
// 'Gantt' => '',
// 'Help with project permissions' => '',
// '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:' => '',
);

View file

@ -395,8 +395,6 @@ return array(
'Remote' => 'Remote',
'Enabled' => 'Aktiv',
'Disabled' => 'Deaktiveret',
'Google account linked' => 'Google-konto forbundet',
'Github account linked' => 'Github-konto forbundet',
'Username:' => 'Brugernavn',
'Name:' => 'Navn:',
'Email:' => 'Email:',
@ -667,75 +665,26 @@ return array(
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// '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' => '',
// 'Show subtask estimates (forecast of future work)' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
@ -746,7 +695,6 @@ return array(
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
@ -878,9 +826,6 @@ return array(
// '%s moved the task #%d to the first swimlane' => '',
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Budget overview' => '',
// 'Type' => '',
// 'There is not enough data to show something.' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
@ -1067,4 +1012,48 @@ return array(
// 'Table of contents' => '',
// 'Gantt' => '',
// 'Help with project permissions' => '',
// '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:' => '',
);

View file

@ -195,7 +195,7 @@ return array(
'Invalid date' => 'Ungültiges Datum',
'Must be done before %B %e, %Y' => 'Muss vor dem %d.%m.%Y erledigt werden',
'%B %e, %Y' => '%d.%m.%Y',
// '%b %e, %Y' => '',
'%b %e, %Y' => '%d.%m.%Y',
'Automatic actions' => 'Automatische Aktionen',
'Your automatic action have been created successfully.' => 'Die automatische Aktion wurde erfolgreich erstellt.',
'Unable to create your automatic action.' => 'Erstellen der automatischen Aktion nicht möglich.',
@ -263,10 +263,10 @@ return array(
'%d comments' => '%d Kommentare',
'%d comment' => '%d Kommentar',
'Email address invalid' => 'Ungültige E-Mail-Adresse',
// '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.' => 'Dein externer Account ist nicht mehr mit deinem Profil verbunden.',
'Unable to unlink your external account.' => 'Externer Account konnte nicht getrennt werden.',
'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',
@ -395,8 +395,6 @@ return array(
'Remote' => 'Remote',
'Enabled' => 'angeschaltet',
'Disabled' => 'abgeschaltet',
'Google account linked' => 'Mit Google-Account verbunden',
'Github account linked' => 'Mit Github-Account verbunden',
'Username:' => 'Benutzername',
'Name:' => 'Name',
'Email:' => 'E-Mail',
@ -608,7 +606,7 @@ return array(
'Time Tracking' => 'Zeiterfassung',
'You already have one subtask in progress' => 'Bereits eine Teilaufgabe in Bearbeitung',
'Which parts of the project do you want to duplicate?' => 'Welcher Teil des Projekts soll kopiert werden?',
// 'Disallow login form' => '',
'Disallow login form' => 'Verbiete Login-Formular',
'Bitbucket commit received' => 'Bitbucket-Commit erhalten',
'Bitbucket webhooks' => 'Bitbucket-Webhooks',
'Help on Bitbucket webhooks' => 'Hilfe für Bitbucket-Webhooks',
@ -667,75 +665,26 @@ return array(
'Horizontal scrolling' => 'Horizontales Scrollen',
'Compact/wide view' => 'Kompakt/Breite-Ansicht',
'No results match:' => 'Keine Ergebnisse:',
'Remove hourly rate' => 'Stundensatz entfernen',
'Do you really want to remove this hourly rate?' => 'Diesen Stundensatz wirklich entfernen?',
'Hourly rates' => 'Stundensätze',
'Hourly rate' => 'Stundensatz',
'Currency' => 'Währung',
'Effective date' => 'Inkraftsetzung',
'Add new rate' => 'Neue Rate hinzufügen',
'Rate removed successfully.' => 'Rate erfolgreich entfernt',
'Unable to remove this rate.' => 'Nicht in der Lage, diese Rate zu entfernen.',
'Unable to save the hourly rate.' => 'Nicht in der Lage, diese Rate zu speichern',
'Hourly rate created successfully.' => 'Stundensatz erfolgreich angelegt.',
'Start time' => 'Startzeit',
'End time' => 'Endzeit',
'Comment' => 'Kommentar',
'All day' => 'ganztägig',
'Day' => 'Tag',
'Manage timetable' => 'Zeitplan verwalten',
'Overtime timetable' => 'Überstunden Zeitplan',
'Time off timetable' => 'Freizeit Zeitplan',
'Timetable' => 'Zeitplan',
'Work timetable' => 'Arbeitszeitplan',
'Week timetable' => 'Wochenzeitplan',
'Day timetable' => 'Tageszeitplan',
'From' => 'von',
'To' => 'bis',
'Time slot created successfully.' => 'Zeitfenster erfolgreich erstellt.',
'Unable to save this time slot.' => 'Nicht in der Lage, dieses Zeitfenster zu speichern.',
'Time slot removed successfully.' => 'Zeitfenster erfolgreich entfernt.',
'Unable to remove this time slot.' => 'Nicht in der Lage, dieses Zeitfenster zu entfernen',
'Do you really want to remove this time slot?' => 'Soll diese Zeitfenster wirklich gelöscht werden?',
'Remove time slot' => 'Zeitfenster entfernen',
'Add new time slot' => 'Neues Zeitfenster hinzufügen',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Dieses Zeitfenster wird verwendet, wenn die Checkbox "ganztägig" für Freizeit und Überstunden angeklickt ist.',
'Files' => 'Dateien',
'Images' => 'Bilder',
'Private project' => 'privates Projekt',
'Amount' => 'Betrag',
'AUD - Australian Dollar' => 'AUD - Australische Dollar',
'Budget' => 'Budget',
'Budget line' => 'Budgetlinie',
'Budget line removed successfully.' => 'Budgetlinie erfolgreich entfernt',
'Budget lines' => 'Budgetlinien',
'CAD - Canadian Dollar' => 'CAD - Kanadische Dollar',
'CHF - Swiss Francs' => 'CHF - Schweizer Franken',
'Cost' => 'Kosten',
'Cost breakdown' => 'Kostenaufschlüsselung',
'Custom Stylesheet' => 'benutzerdefiniertes Stylesheet',
'download' => 'Download',
'Do you really want to remove this budget line?' => 'Soll diese Budgetlinie wirklich entfernt werden?',
'EUR - Euro' => 'EUR - Euro',
'Expenses' => 'Kosten',
'GBP - British Pound' => 'GBP - Britische Pfund',
'INR - Indian Rupee' => 'INR - Indische Rupien',
'JPY - Japanese Yen' => 'JPY - Japanische Yen',
'New budget line' => 'Neue Budgetlinie',
'NZD - New Zealand Dollar' => 'NZD - Neuseeland-Dollar',
'Remove a budget line' => 'Budgetlinie entfernen',
'Remove budget line' => 'Budgetlinie entfernen',
'RSD - Serbian dinar' => 'RSD - Serbische Dinar',
'The budget line have been created successfully.' => 'Die Budgetlinie wurde erfolgreich angelegt.',
'Unable to create the budget line.' => 'Budgetlinie konnte nicht erstellt werden.',
'Unable to remove this budget line.' => 'Budgetlinie konnte nicht gelöscht werden.',
'USD - US Dollar' => 'USD - US-Dollar',
'Remaining' => 'Verbleibend',
'Destination column' => 'Zielspalte',
'Move the task to another column when assigned to a user' => 'Aufgabe in eine andere Spalte verschieben, wenn ein User zugeordnet wurde.',
'Move the task to another column when assignee is cleared' => 'Aufgabe in eine andere Spalte verschieben, wenn die Zuordnung gelöscht wurde.',
'Source column' => 'Quellspalte',
'Show subtask estimates (forecast of future work)' => 'Teilaufgaben-Schätzungen anzeigen (Prognose)',
'Transitions' => 'Übergänge',
'Executer' => 'Ausführender',
'Time spent in the column' => 'Zeit in Spalte verbracht',
@ -746,7 +695,6 @@ return array(
'Rate' => 'Kurse',
'Change reference currency' => 'Referenzwährung ändern',
'Add a new currency rate' => 'Neuen Währungskurs hinzufügen',
'Currency rates are used to calculate project budget.' => 'Währungskurse werden verwendet, um das Projektbudget zu berechnen.',
'Reference currency' => 'Referenzwährung',
'The currency rate have been added successfully.' => 'Der Währungskurs wurde erfolgreich hinzugefügt.',
'Unable to add this currency rate.' => 'Währungskurs konnte nicht hinzugefügt werden',
@ -777,8 +725,8 @@ return array(
'uploaded by: %s' => 'Hochgeladen von: %s',
'uploaded on: %s' => 'Hochgeladen am: %s',
'size: %s' => 'Größe: %s',
'Burndown chart for "%s"' => 'Burndown-Chart für "%s"',
'Burndown chart' => 'Burndown-Chart',
'Burndown chart for "%s"' => 'Burndown-Diagramm für "%s"',
'Burndown chart' => 'Burndown-Diagramm',
'This chart show the task complexity over the time (Work Remaining).' => 'Dieses Diagramm zeigt die Aufgabenkomplexität über den Faktor Zeit (Verbleibende Arbeit).',
'Screenshot taken %s' => 'Screenshot aufgenommen %s ',
'Add a screenshot' => 'Füge einen Screenshot hinzu',
@ -827,7 +775,7 @@ return array(
'When task is moved from first column' => 'Wenn Aufgabe von erster Spalte verschoben wird',
'When task is moved to last column' => 'Wenn Aufgabe in letzte Spalte verschoben wird',
'Year(s)' => 'Jahr(e)',
// 'Jabber (XMPP)' => '',
'Jabber (XMPP)' => 'Jabber (XMPP)',
'Send notifications to Jabber' => 'Benachrichtigungen an Jabber senden',
'XMPP server address' => 'XMPP-Server-Adresse',
'Jabber domain' => 'Jabber-Domain',
@ -844,7 +792,7 @@ return array(
'Subtasks time tracking' => 'Teilaufgaben-Zeiterfassung',
'User calendar view' => 'Benutzer-Kalendersicht',
'Automatically update the start date' => 'Beginndatum automatisch aktualisieren',
// 'iCal feed' => '',
'iCal feed' => 'iCal Feed',
'Preferences' => 'Einstellungen',
'Security' => 'Sicherheit',
'Two factor authentication disabled' => 'Zwei-Faktor-Authentifizierung deaktiviert',
@ -854,21 +802,21 @@ return array(
'User that will receive the email' => 'Empfänger der E-Mail',
'Email subject' => 'E-Mail-Betreff',
'Date' => 'Datum',
// 'By @%s on Bitbucket' => '',
'By @%s on Bitbucket' => 'Durch @%s auf Bitbucket',
'Bitbucket Issue' => 'Bitbucket-Issue',
// 'Commit made by @%s on Bitbucket' => '',
// 'Commit made by @%s on Github' => '',
// 'By @%s on Github' => '',
// 'Commit made by @%s on Gitlab' => '',
'Commit made by @%s on Bitbucket' => 'Commit von @%s auf Bitbucket',
'Commit made by @%s on Github' => 'Commit von @%s auf Github',
'By @%s on Github' => 'Durch @%s auf Github',
'Commit made by @%s on Gitlab' => 'Commit von @%s auf Gitlab',
'Add a comment log when moving the task between columns' => 'Kommentar hinzufügen, wenn Aufgabe in andere Spalte verschoben wird',
'Move the task to another column when the category is changed' => 'Aufgabe in andere Spalte verschieben, wenn Kategorie geändert wird',
'Send a task by email to someone' => 'Aufgabe per E-Mail versenden',
'Reopen a task' => 'Aufgabe wieder öffnen',
// 'Bitbucket issue opened' => '',
// 'Bitbucket issue closed' => '',
// 'Bitbucket issue reopened' => '',
// 'Bitbucket issue assignee change' => '',
// 'Bitbucket issue comment created' => '',
'Bitbucket issue opened' => 'Bitbucket Ticket eröffnet',
'Bitbucket issue closed' => 'Bitbucket Ticket geschlossen',
'Bitbucket issue reopened' => 'Bitbucket Ticket wieder eröffnet',
'Bitbucket issue assignee change' => 'Bitbucket Ticket Zuordnung geändert',
'Bitbucket issue comment created' => 'Bitbucket Ticket Kommentar erstellt',
'Column change' => 'Spalte geändert',
'Position change' => 'Position geändert',
'Swimlane change' => 'Swimlane geändert',
@ -877,13 +825,10 @@ return array(
'Notification' => 'Benachrichtigungen',
'%s moved the task #%d to the first swimlane' => '%s hat die Aufgabe #%d in die erste Swimlane verschoben',
'%s moved the task #%d to the swimlane "%s"' => '%s hat die Aufgabe #%d in die Swimlane "%s" verschoben',
// 'Swimlane' => '',
'Budget overview' => 'Budget-Übersicht',
'Type' => 'Typ',
'There is not enough data to show something.' => 'Es gibt nicht genügend Daten für diese Anzeige',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'Hipchat' => 'Hipchat',
'Slack' => 'Slack',
'%s moved the task %s to the first swimlane' => '%s hat die Aufgabe %s in die erste Swimlane verschoben',
'%s moved the task %s to the swimlane "%s"' => '%s hat die Aufgaben %s in die Swimlane "%s" verschoben',
'This report contains all subtasks information for the given date range.' => 'Der Bericht beinhaltet alle Teilaufgaben im gewählten Zeitraum',
@ -908,22 +853,22 @@ return array(
'The field "%s" have been updated' => 'Das Feld "%s" wurde verändert',
'The description have been modified' => 'Die Beschreibung wurde geändert',
'Do you really want to close the task "%s" as well as all subtasks?' => 'Soll die Aufgabe "%s" wirklich geschlossen werden? (einschließlich Teilaufgaben)',
// 'Swimlane: %s' => '',
'Swimlane: %s' => 'Swimlane: %s',
'I want to receive notifications for:' => 'Ich möchte Benachrichtigungen erhalten für:',
'All tasks' => 'Alle Aufgaben',
'Only for tasks assigned to me' => 'nur mir zugeordnete Aufgane',
'Only for tasks created by me' => 'nur von mir erstellte Aufgaben',
'Only for tasks created by me and assigned to me' => 'nur mir zugeordnete und von mir erstellte Aufgaben',
// '%A' => '',
// '%b %e, %Y, %k:%M %p' => '',
'%A' => '%A',
'%b %e, %Y, %k:%M %p' => '%b %e, %Y, %k:%M %p',
'New due date: %B %e, %Y' => 'Neues Ablaufdatum: %B %e, %Y',
'Start date changed: %B %e, %Y' => 'Neues Beginndatum: %B %e, %Y',
// '%k:%M %p' => '',
// '%%Y-%%m-%%d' => '',
'%k:%M %p' => '%k:%M %p',
'%%Y-%%m-%%d' => '%%d.%%m.%%Y',
'Total for all columns' => 'Gesamt für alle Spalten',
'You need at least 2 days of data to show the chart.' => 'Es werden mindestens 2 Tage zur Darstellung benötigt',
// '<15m' => '',
// '<30m' => '',
'<15m' => '<15min',
'<30m' => '<30min',
'Stop timer' => 'Stoppe Timer',
'Start timer' => 'Starte Timer',
'Add project member' => 'Projektmitglied hinzufügen',
@ -943,7 +888,7 @@ return array(
'Not assigned' => 'Nicht zugewiesen',
'View advanced search syntax' => 'Zur erweiterten Suchsyntax',
'Overview' => 'Überblick',
// '%b %e %Y' => '',
'%b %e %Y' => '%b %e %Y',
'Board/Calendar/List view' => 'Board-/Kalender-/Listen-Ansicht',
'Switch to the board view' => 'Zur Board-Ansicht',
'Switch to the calendar view' => 'Zur Kalender-Ansicht',
@ -963,108 +908,152 @@ return array(
'Search by category: ' => 'Suche nach Kategorie: ',
'Search by description: ' => 'Suche nach Beschreibung: ',
'Search by due date: ' => 'Suche nach Fälligkeitsdatum: ',
// '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' => '',
// 'Channel/Group/User (Optional)' => '',
// '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.' => '',
// 'By @%s on Gitlab' => '',
// 'Gitlab issue comment created' => '',
// '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' => '',
// 'Project Administrator' => '',
// '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' => '',
// 'Managers' => '',
// 'Members' => '',
// 'Shared project' => '',
// 'Project managers' => '',
// 'Project members' => '',
// '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' => '',
// 'Help with project permissions' => '',
'Lead and Cycle time for "%s"' => 'Durchlauf und Zykluszeit für "%s"',
'Average time spent into each column for "%s"' => 'Durchschnittliche Zeit in jeder Spalte für "%s"',
'Average time spent into each column' => 'Durchschnittszeit in jeder Spalte',
'Average time spent' => 'Durchschnittlicher Zeitverbrauch',
'This chart show the average time spent into each column for the last %d tasks.' => 'Dieses Diagramm zeigt die durchschnittliche Zeit in jeder Spalte der letzten %d Aufgaben.',
'Average Lead and Cycle time' => 'Durchschnittliche Zyklus- und Durchlaufzeit',
'Average lead time: ' => 'Durchschnittliche Durchlaufzeit:',
'Average cycle time: ' => 'Durchschnittliche Zykluszeit:',
'Cycle Time' => 'Zykluszeit',
'Lead Time' => 'Durchlaufzeit',
'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',
'Channel/Group/User (Optional)' => 'Kanal/Gruppe/Benutzer (optional)',
'Lead time: ' => 'Durchlaufzeit:',
'Cycle time: ' => 'Zykluszeit:',
'Time spent into each column' => 'zeit verbracht in jeder Spalte',
'The lead time is the duration between the task creation and the completion.' => 'Die Durchlaufzeit ist die Dauer zwischen Erstellung und Fertigstellung.',
'The cycle time is the duration between the start date and the completion.' => 'Die Zykluszeit ist die Dauer zwischen Start und Fertigstellung.',
'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.',
'By @%s on Gitlab' => 'Durch @%s auf Gitlab',
'Gitlab issue comment created' => 'Gitlab Ticket Kommentar erstellt',
'New remote user' => 'Neuer Remote-Benutzer',
'New local user' => 'Neuer lokaler Benutzer',
'Default task color' => 'Voreingestellte Aufgabenfarbe',
'Hide sidebar' => 'Seitenleiste verstecken',
'Expand sidebar' => 'Seitenleiste ausklappen',
'This feature does not work with all browsers.' => 'Diese Funktion funktioniert nicht mit allen Browsern',
'There is no destination project available.' => 'Es ist kein Zielprojekt vorhanden.',
'Trigger automatically subtask time tracking' => 'Teilaufgaben Zeiterfassung automatisch starten',
'Include closed tasks in the cumulative flow diagram' => 'Geschlossen Aufgaben ins kumulative Flussdiagramm einschließen',
'Current swimlane: %s' => 'Aktuelle Swimlane: %s',
'Current column: %s' => 'Aktuelle Spalte: %s',
'Current category: %s' => 'Aktuelle Kategorie: %s',
'no category' => 'keine Kategorie',
'Current assignee: %s' => 'Aktuelle Zuordnung: %s',
'not assigned' => 'nicht zugeordnet',
'Author:' => 'Autor',
'contributors' => 'Mitwirkende',
'License:' => 'Lizenz:',
'License' => 'Lizenz',
'Project Administrator' => 'Projektadministrator',
'Enter the text below' => 'Text unten eingeben',
'Gantt chart for %s' => 'Gantt Diagramm für %s',
'Sort by position' => 'Nach Position sortieren',
'Sort by date' => 'Nach Datum sortieren',
'Add task' => 'Aufgabe hinzufügen',
'Start date:' => 'Startdatum:',
'Due date:' => 'Ablaufdatum:',
'There is no start date or due date for this task.' => 'Diese Aufgabe hat kein Start oder Ablaufdatum.',
'Moving or resizing a task will change the start and due date of the task.' => 'Aufgabe verschieben/ändern, ändert auch Start- und Ablaufdatum der Aufgabe.',
'There is no task in your project.' => 'Es gibt keine Aufgabe in deinem Projekt',
'Gantt chart' => 'Gantt Diagramm',
'People who are project managers' => 'Benutzer die Projektmanager sind',
'People who are project members' => 'Benutzer die Projektmitglieder sind',
'NOK - Norwegian Krone' => 'NOK - Norwegische Kronen',
'Show this column' => 'Spalte anzeigen',
'Hide this column' => 'Spalte verstecken',
'open file' => 'Datei öffnen',
'End date' => 'Endedatum',
'Users overview' => 'Benutzerübersicht',
'Managers' => 'Manager',
'Members' => 'Mitglieder',
'Shared project' => 'Geteiltes Projekt',
'Project managers' => 'Projektmanager',
'Project members' => 'Projektmitglieder',
'Gantt chart for all projects' => 'Gantt Diagramm für alle Projekte',
'Projects list' => 'Projektliste',
'Gantt chart for this project' => 'Gantt Diagramm für dieses Projekt',
'Project board' => 'Projekt Pinnwand',
'End date:' => 'Endedatum:',
'There is no start date or end date for this project.' => 'Es gibt kein Startdatum oder Endedatum für dieses Projekt',
'Projects Gantt chart' => 'Projekt Gantt Diagramm',
'Start date: %s' => 'Beginndatum: %s',
'End date: %s' => 'Enddatum: %s',
'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',
'Documentation' => 'Dokumentation',
'Table of contents' => 'Inhaltsverzeichnis',
'Gantt' => 'Gantt',
'Help with project permissions' => 'Hilfe bei Projektberechtigungen',
'Author' => 'Autor',
'Version' => 'Version',
'Plugins' => 'Plugins',
'There is no plugin loaded.' => 'Es ist kein Plugin geladen.',
'Set maximum column height' => 'Setze maximale Spaltenhöhe',
'Remove maximum column height' => 'Entferne maximale Spaltenhöhe',
'My notifications' => 'Meine Benachrichtigungen',
'Custom filters' => 'benutzerdefinierte Filter',
'Your custom filter have been created successfully.' => 'Benutzerdefinierten Filter erfolgreich erstellt.',
'Unable to create your custom filter.' => 'Benutzerdefinierter Filter konnte nicht erstellt werden.',
'Custom filter removed successfully.' => 'Benutzerdefinierten Filter erfolgreich entfernt.',
'Unable to remove this custom filter.' => 'Benutzerdefinierten Filter konnte nicht entfernt werden.',
'Edit custom filter' => 'Benutzerdefinierten Filter bearbeiten',
'Your custom filter have been updated successfully.' => 'Benutzerdefinierten Filter erfolgreich bearbeitet.',
'Unable to update custom filter.' => 'Benutzerdefinierter Filter konnte nicht geändert werden.',
'Web' => 'Web',
'New attachment on task #%d: %s' => 'Neuer Anhang für Aufgabe #%d: %s',
'New comment on task #%d' => 'Neuer Kommentar für Aufgabe #%d',
'Comment updated on task #%d' => 'Kommentar geändert für Aufgabe #%d',
'New subtask on task #%d' => 'Neue Teilaufgabe für Aufgabe #%d',
'Subtask updated on task #%d' => 'Teilaufgabe geändert für Aufgabe #%d',
'New task #%d: %s' => 'Neue Aufgabe #%d: %s',
'Task updated #%d' => 'Aufgabe bearbeitet #%d',
'Task #%d closed' => 'Aufgabe #%d geschlossen',
'Task #%d opened' => 'Aufgabe #%d eröffnet',
'Column changed for task #%d' => 'Spalte geändert von Aufgabe #%d',
'New position for task #%d' => 'Neue Position für Aufgabe #%d',
'Swimlane changed for task #%d' => 'Neue Swimlane für Aufgabe #%d',
'Assignee changed on task #%d' => 'Neue Zuordnung für Aufgabe #%d ',
'%d overdue tasks' => '%d überfällige Aufgaben',
'Task #%d is overdue' => 'Aufgabe #%d ist überfällig',
'No new notifications.' => 'Keine neuen Benachrichtigungen',
'Mark all as read' => 'Alles als gelesen markieren',
'Mark as read' => 'Als gelesen markieren',
'Total number of tasks in this column across all swimlanes' => 'Anzahl an Aufgaben in dieser Spalte über alle Swimlanes',
'Collapse swimlane' => 'Swimlane einklappen',
'Expand swimlane' => 'Swimlane ausklappen',
'Add a new filter' => 'Neuen Filter hinzufügen',
'Share with all project members' => 'Mit allen Projektmitgliedern teilen.',
'Shared' => 'Geteilt',
'Owner' => 'Eigentümer',
'Unread notifications' => 'Ungelesene Benachrichtigungen',
'My filters' => 'Meine Filter',
'Notification methods:' => 'Benachrichtigungs-Methoden:',
);

View file

@ -395,8 +395,6 @@ return array(
'Remote' => 'Remota',
'Enabled' => 'Activada',
'Disabled' => 'Desactivada',
'Google account linked' => 'Vinculada con Cuenta de Google',
'Github account linked' => 'Vinculada con Cuenta de Gitgub',
'Username:' => 'Nombre de Usuario:',
'Name:' => 'Nombre:',
'Email:' => 'Correo electrónico:',
@ -667,75 +665,26 @@ return array(
'Horizontal scrolling' => 'Desplazamiento horizontal',
'Compact/wide view' => 'Vista compacta/amplia',
'No results match:' => 'No hay resultados coincidentes:',
'Remove hourly rate' => 'Quitar cobro horario',
'Do you really want to remove this hourly rate?' => '¿Realmente quire quitar el cobro horario?',
'Hourly rates' => 'Cobros horarios',
'Hourly rate' => 'Cobro horario',
'Currency' => 'Moneda',
'Effective date' => 'Fecha efectiva',
'Add new rate' => 'Añadir nuevo cobro',
'Rate removed successfully.' => 'Cobro quitado con éxito.',
'Unable to remove this rate.' => 'No pude quitar este cobro.',
'Unable to save the hourly rate.' => 'No pude grabar el cobro horario.',
'Hourly rate created successfully.' => 'Cobro horario creado con éxito',
'Start time' => 'Tiempo de inicio',
'End time' => 'Tiempo de fin',
'Comment' => 'Comentario',
'All day' => 'Todos los días',
'Day' => 'Día',
'Manage timetable' => 'Gestionar horario',
'Overtime timetable' => 'Horario de tiempo extra',
'Time off timetable' => 'Horario de tiempo libre',
'Timetable' => 'Horario',
'Work timetable' => 'Horario de trabajo',
'Week timetable' => 'Horario semanal',
'Day timetable' => 'Horario diario',
'From' => 'De',
'To' => 'Para',
'Time slot created successfully.' => 'Intervalo de tiempo creado correctamente.',
'Unable to save this time slot.' => 'No pude grabar este intervalo de tiempo.',
'Time slot removed successfully.' => 'Intervalo de tiempo quitado correctamente.',
'Unable to remove this time slot.' => 'No pude quitar este intervalo de tiempo.',
'Do you really want to remove this time slot?' => '¿Realmente quiere quitar este intervalo de tiempo?',
'Remove time slot' => 'Quitar intervalo de tiempo',
'Add new time slot' => 'Añadir nuevo intervalo de tiempo',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Este horario se usa cuando se marca la casilla "todos los días" para calendario de tiempo libre y horas extras.',
'Files' => 'Ficheros',
'Images' => 'Imágenes',
'Private project' => 'Proyecto privado',
'Amount' => 'Cantidad',
'AUD - Australian Dollar' => 'AUD - Dólar australiano',
'Budget' => 'Presupuesto',
'Budget line' => 'Línea de presupuesto',
'Budget line removed successfully.' => 'Línea de presupuesto quitada con éxito',
'Budget lines' => 'Líneas de presupuesto',
'CAD - Canadian Dollar' => 'CAD - Dólar canadiense',
'CHF - Swiss Francs' => 'CHF - Francos suizos',
'Cost' => 'Costo',
'Cost breakdown' => 'Desglose de costes',
'Custom Stylesheet' => 'Hoja de estilo Personalizada',
'download' => 'descargar',
'Do you really want to remove this budget line?' => '¿Realmente quiere quitar esta línea de presupuesto?',
'EUR - Euro' => 'EUR - Euro',
'Expenses' => 'Gastos',
'GBP - British Pound' => 'GBP - Libra británica',
'INR - Indian Rupee' => 'INR - Rupias indúes',
'JPY - Japanese Yen' => 'JPY - Yen japonés',
'New budget line' => 'Nueva línea de presupuesto',
'NZD - New Zealand Dollar' => 'NZD - Dóloar neocelandés',
'Remove a budget line' => 'Quitar una línea de presupuesto',
'Remove budget line' => 'Quitar línea de presupuesto',
'RSD - Serbian dinar' => 'RSD - Dinar serbio',
'The budget line have been created successfully.' => 'Se ha creado la línea de presupuesto con éxito.',
'Unable to create the budget line.' => 'No pude crear la línea de presupuesto.',
'Unable to remove this budget line.' => 'No pude quitar esta línea de presupuesto.',
'USD - US Dollar' => 'USD - Dólar Estadounidense',
'Remaining' => 'Restante',
'Destination column' => 'Columna destino',
'Move the task to another column when assigned to a user' => 'Mover la tarea a otra columna al asignarse al usuario',
'Move the task to another column when assignee is cleared' => 'Mover la tarea a otra columna al quitar el concesionario',
'Source column' => 'Columna fuente',
'Show subtask estimates (forecast of future work)' => 'Mostrar estimaciones para la subtarea (pronóstico de trabajo futuro)',
'Transitions' => 'Transiciones',
'Executer' => 'Ejecutor',
'Time spent in the column' => 'Tiempo transcurrido en la columna',
@ -746,7 +695,6 @@ return array(
'Rate' => 'Cambio',
'Change reference currency' => 'Cambiar moneda de referencia',
'Add a new currency rate' => 'Añadir nuevo cambio de moneda',
'Currency rates are used to calculate project budget.' => 'Se usan los cambios de moneda para calcular el presupuesto del proyecto.',
'Reference currency' => 'Moneda de referencia',
'The currency rate have been added successfully.' => 'Se ha añadido el cambio de moneda con éxito',
'Unable to add this currency rate.' => 'No pude añadir este cambio de moneda.',
@ -878,9 +826,6 @@ return array(
'%s moved the task #%d to the first swimlane' => '%s movió la tarea #%d a la primera calle',
'%s moved the task #%d to the swimlane "%s"' => '%s movió la tarea #%d a la calle "%s"',
'Swimlane' => 'Calle',
'Budget overview' => 'Resumen del Presupuesto',
'Type' => 'Tipo',
'There is not enough data to show something.' => 'No hay datos suficientes como para mostrar algo.',
'Gravatar' => 'Gravatar',
'Hipchat' => 'Hipchat',
'Slack' => 'Desatendida',
@ -1040,31 +985,75 @@ return array(
'Shared project' => 'Proyecto compartido',
'Project managers' => 'Administradores de proyecto',
'Project members' => 'Miembros de proyecto',
// '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' => '',
// 'Help with project permissions' => '',
'Gantt chart for all projects' => 'Diagrama de Gantt para todos los proyectos',
'Projects list' => 'Lista de proyectos',
'Gantt chart for this project' => 'Diagrama de Gantt para este proyecto',
'Project board' => 'Tablero del proyecto',
'End date:' => 'Fecha final',
'There is no start date or end date for this project.' => 'No existe fecha de inicio o de fin para este proyecto.',
'Projects Gantt chart' => 'Diagramas de Gantt de los proyectos',
'Start date: %s' => 'Fecha inicial: %s',
'End date: %s' => 'Fecha final: %s',
'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',
'Documentation' => 'Documentación',
'Table of contents' => 'Tabla de contenido',
'Gantt' => 'Gantt',
'Help with project permissions' => 'Ayuda con permisos del proyecto',
// '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:' => '',
);

View file

@ -395,8 +395,6 @@ return array(
'Remote' => 'Etä',
'Enabled' => 'Käytössä',
'Disabled' => 'Pois käytöstä',
'Google account linked' => 'Google-tili liitetty',
'Github account linked' => 'Github-tili liitetty',
'Username:' => 'Käyttäjänimi:',
'Name:' => 'Nimi:',
'Email:' => 'Sähköpostiosoite:',
@ -667,75 +665,26 @@ return array(
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// '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' => '',
// 'Show subtask estimates (forecast of future work)' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
@ -746,7 +695,6 @@ return array(
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
@ -878,9 +826,6 @@ return array(
// '%s moved the task #%d to the first swimlane' => '',
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Budget overview' => '',
// 'Type' => '',
// 'There is not enough data to show something.' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
@ -1067,4 +1012,48 @@ return array(
// 'Table of contents' => '',
// 'Gantt' => '',
// 'Help with project permissions' => '',
// '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:' => '',
);

View file

@ -75,19 +75,19 @@ return array(
'Change columns' => 'Changer les colonnes',
'Add a new column' => 'Ajouter une nouvelle colonne',
'Title' => 'Titre',
'Nobody assigned' => 'Personne assigné',
'Nobody assigned' => 'Personne assignée',
'Assigned to %s' => 'Assigné à %s',
'Remove a column' => 'Supprimer une colonne',
'Remove a column from a board' => 'Supprimer une colonne d\'un tableau',
'Unable to remove this column.' => 'Impossible de supprimer cette colonne.',
'Do you really want to remove this column: "%s"?' => 'Voulez vraiment supprimer cette colonne : « %s » ?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Cette action va supprimer toutes les tâches associées à cette colonne !',
'This action will REMOVE ALL TASKS associated to this column!' => 'Cette action va supprimer toutes les tâches associées à cette colonne !',
'Settings' => 'Préférences',
'Application settings' => 'Paramètres de l\'application',
'Language' => 'Langue',
'Webhook token:' => 'Jeton de securité pour les webhooks :',
'API token:' => 'Jeton de securité pour l\'API :',
'Database size:' => 'Taille de la base de données :',
'Database size:' => 'Taille de la base de données :',
'Download the database' => 'Télécharger la base de données',
'Optimize the database' => 'Optimiser la base de données',
'(VACUUM command)' => '(Commande VACUUM)',
@ -96,15 +96,15 @@ return array(
'Edit a task' => 'Modifier une tâche',
'Column' => 'Colonne',
'Color' => 'Couleur',
'Assignee' => 'Personne assignée',
'Assignee' => 'Personne assigné',
'Create another task' => 'Créer une autre tâche',
'New task' => 'Nouvelle tâche',
'Open a task' => 'Ouvrir une tâche',
'Do you really want to open this task: "%s"?' => 'Voulez-vous vraiment ouvrir cette tâche : « %s » ?',
'Do you really want to open this task: "%s"?' => 'Voulez-vous vraiment ouvrir cette tâche : « %s » ?',
'Back to the board' => 'Retour au tableau',
'Created on %B %e, %Y at %k:%M %p' => 'Créé le %d/%m/%Y à %H:%M',
'There is nobody assigned' => 'Il n\'y a personne d\'assigné à cette tâche',
'Column on the board:' => 'Colonne sur le tableau : ',
'Column on the board:' => 'Colonne sur le tableau : ',
'Status is open' => 'État ouvert',
'Status is closed' => 'État fermé',
'Close this task' => 'Fermer cette tâche',
@ -142,7 +142,7 @@ return array(
'Unable to open this task.' => 'Impossible d\'ouvrir cette tâche.',
'Task opened successfully.' => 'Tâche ouverte avec succès.',
'Unable to close this task.' => 'Impossible de fermer cette tâche.',
'Task closed successfully.' => 'Tâche fermé avec succès.',
'Task closed successfully.' => 'Tâche fermée avec succès.',
'Unable to update your task.' => 'Impossible de modifier cette tâche.',
'Task updated successfully.' => 'Tâche mise à jour avec succès.',
'Unable to create your task.' => 'Impossible de créer cette tâche.',
@ -167,11 +167,11 @@ return array(
'%d closed tasks' => '%d tâches terminées',
'No task for this project' => 'Aucune tâche pour ce projet',
'Public link' => 'Lien public',
'There is no column in your project!' => 'Il n\'y a aucune colonne dans votre projet !',
'There is no column in your project!' => 'Il n\'y a aucune colonne dans votre projet !',
'Change assignee' => 'Changer la personne assignée',
'Change assignee for the task "%s"' => 'Changer la personne assignée pour la tâche « %s »',
'Timezone' => 'Fuseau horaire',
'Sorry, I didn\'t find this information in my database!' => 'Désolé, je n\'ai pas trouvé cette information dans ma base de données !',
'Sorry, I didn\'t find this information in my database!' => 'Désolé, je n\'ai pas trouvé cette information dans ma base de données !',
'Page not found' => 'Page introuvable',
'Complexity' => 'Complexité',
'Task limit' => 'Tâches Max.',
@ -197,7 +197,7 @@ return array(
'%B %e, %Y' => '%d %B %Y',
'%b %e, %Y' => '%d/%m/%Y',
'Automatic actions' => 'Actions automatisées',
'Your automatic action have been created successfully.' => 'Votre action automatisée a été ajouté avec succès.',
'Your automatic action have been created successfully.' => 'Votre action automatisée a été ajoutée avec succès.',
'Unable to create your automatic action.' => 'Impossible de créer votre action automatisée.',
'Remove an action' => 'Supprimer une action',
'Unable to remove this action.' => 'Impossible de supprimer cette action',
@ -210,7 +210,7 @@ return array(
'Action parameters' => 'Paramètres de l\'action',
'Action' => 'Action',
'Event' => 'Événement',
'When the selected event occurs execute the corresponding action.' => 'Lorsque l\'événement sélectionné se déclenche, executer l\'action correspondante.',
'When the selected event occurs execute the corresponding action.' => 'Lorsque l\'événement sélectionné se déclenche, exécuter l\'action correspondante.',
'Next step' => 'Étape suivante',
'Define action parameters' => 'Définition des paramètres de l\'action',
'Save this action' => 'Sauvegarder cette action',
@ -237,10 +237,10 @@ return array(
'Comment removed successfully.' => 'Commentaire supprimé avec succès.',
'Unable to remove this comment.' => 'Impossible de supprimer ce commentaire.',
'Do you really want to remove this comment?' => 'Voulez-vous vraiment supprimer ce commentaire ?',
'Only administrators or the creator of the comment can access to this page.' => 'Uniquement les administrateurs ou le créateur du commentaire peuvent accéder à cette page.',
'Only administrators or the creator of the comment can access to this page.' => 'Seuls les administrateurs ou le créateur du commentaire peuvent accéder à cette page.',
'Current password for the user "%s"' => 'Mot de passe actuel pour l\'utilisateur « %s »',
'The current password is required' => 'Le mot de passe actuel est obligatoire',
'Wrong password' => 'Mauvais mot de passe',
'Wrong password' => 'Mot de passe invalide',
'Unknown' => 'Inconnu',
'Last logins' => 'Dernières connexions',
'Login date' => 'Date de connexion',
@ -263,10 +263,10 @@ return array(
'%d comments' => '%d commentaires',
'%d comment' => '%d commentaire',
'Email address invalid' => 'Adresse email invalide',
'Your external account is not linked anymore to your profile.' => 'Votre compte externe n\'est plus relié à votre profile.',
'Your external account is not linked anymore to your profile.' => 'Votre compte externe n\'est plus relié à votre profil.',
'Unable to unlink your external account.' => 'Impossible de supprimer votre compte externe.',
'External authentication failed' => 'Authentification externe échouée',
'Your external account is linked to your profile successfully.' => 'Votre compte externe est désormais lié à votre profile.',
'External authentication failed' => 'Lauthentification 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',
@ -283,7 +283,7 @@ return array(
'Category:' => 'Catégorie :',
'Categories' => 'Catégories',
'Category not found.' => 'Catégorie introuvable',
'Your category have been created successfully.' => 'Votre catégorie a été créé avec succès.',
'Your category have been created successfully.' => 'Votre catégorie a été créée avec succès.',
'Unable to create your category.' => 'Impossible de créer votre catégorie.',
'Your category have been updated successfully.' => 'Votre catégorie a été mise à jour avec succès.',
'Unable to update your category.' => 'Impossible de mettre à jour votre catégorie.',
@ -311,13 +311,13 @@ return array(
'Summary' => 'Résumé',
'Time tracking' => 'Suivi du temps',
'Estimate:' => 'Estimation :',
'Spent:' => 'Passé :',
'Spent:' => 'Passé :',
'Do you really want to remove this sub-task?' => 'Voulez-vous vraiment supprimer cette sous-tâche ?',
'Remaining:' => 'Restant :',
'Remaining:' => 'Restant :',
'hours' => 'heures',
'spent' => 'passé',
'estimated' => 'estimé',
'Sub-Tasks' => 'Sous-Tâches',
'Sub-Tasks' => 'Sous-tâches',
'Add a sub-task' => 'Ajouter une sous-tâche',
'Original estimate' => 'Estimation originale',
'Create another sub-task' => 'Créer une autre sous-tâche',
@ -332,8 +332,8 @@ return array(
'Sub-task updated successfully.' => 'Sous-tâche mise à jour avec succès.',
'Unable to update your sub-task.' => 'Impossible de mettre à jour votre sous-tâche.',
'Unable to create your sub-task.' => 'Impossible de créer votre sous-tâche.',
'Sub-task added successfully.' => 'Sous-tâche ajouté avec succès.',
'Maximum size: ' => 'Taille maximum : ',
'Sub-task added successfully.' => 'Sous-tâche ajoutée avec succès.',
'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',
@ -397,8 +397,6 @@ return array(
'Remote' => 'Distant',
'Enabled' => 'Activé',
'Disabled' => 'Désactivé',
'Google account linked' => 'Compte Google attaché',
'Github account linked' => 'Compte Github attaché',
'Username:' => 'Nom d\'utilisateur :',
'Name:' => 'Nom :',
'Email:' => 'Email :',
@ -447,10 +445,10 @@ return array(
'%s moved the task #%d to the position %d in the column "%s"' => '%s a déplacé la tâche n°%d à la position n°%d dans la colonne « %s »',
'Activity' => 'Activité',
'Default values are "%s"' => 'Les valeurs par défaut sont « %s »',
'Default columns for new projects (Comma-separated)' => 'Colonnes par défaut pour les nouveaux projets (séparé par des virgules)',
'Task assignee change' => 'Modification de la personne assignée sur une tâche',
'%s change the assignee of the task #%d to %s' => '%s a changé la personne assignée sur la tâche n˚%d pour %s',
'%s changed the assignee of the task %s to %s' => '%s a changé la personne assignée sur la tâche %s pour %s',
'Default columns for new projects (Comma-separated)' => 'Colonnes par défaut pour les nouveaux projets (séparation par des virgules)',
'Task assignee change' => 'Modification de la personne assignée à une tâche',
'%s change the assignee of the task #%d to %s' => '%s a changé la personne assignée à la tâche n˚%d pour %s',
'%s changed the assignee of the task %s to %s' => '%s a changé la personne assignée à la tâche %s pour %s',
'New password for the user "%s"' => 'Nouveau mot de passe pour l\'utilisateur « %s »',
'Choose an event' => 'Choisir un événement',
'Github commit received' => 'Commit reçu via Github',
@ -466,7 +464,7 @@ return array(
'Reference: %s' => 'Référence : %s',
'Label' => 'Libellé',
'Database' => 'Base de données',
'About' => 'A propos',
'About' => 'À propos',
'Database driver:' => 'Type de base de données :',
'Board settings' => 'Paramètres du tableau',
'URL and token' => 'URL et jeton de sécurité',
@ -529,12 +527,12 @@ return array(
'Previous' => 'Précédent',
'The id must be an integer' => 'L\'id doit être un entier',
'The project id must be an integer' => 'L\'id du projet doit être un entier',
'The status must be an integer' => 'Le status doit être un entier',
'The status must be an integer' => 'Le statut doit être un entier',
'The subtask id is required' => 'L\'id de la sous-tâche est obligatoire',
'The subtask id must be an integer' => 'L\'id de la sous-tâche doit être en entier',
'The subtask id must be an integer' => 'L\'id de la sous-tâche doit être un entier',
'The task id is required' => 'L\'id de la tâche est obligatoire',
'The task id must be an integer' => 'L\'id de la tâche doit être en entier',
'The user id must be an integer' => 'L\'id de l\'utilisateur doit être en entier',
'The task id must be an integer' => 'L\'id de la tâche doit être un entier',
'The user id must be an integer' => 'L\'id de l\'utilisateur doit être un entier',
'This value is required' => 'Cette valeur est obligatoire',
'This value must be numeric' => 'Cette valeur doit être numérique',
'Unable to create this task.' => 'Impossible de créer cette tâche',
@ -552,7 +550,7 @@ return array(
'Add a new swimlane' => 'Ajouter une nouvelle swimlane',
'Change default swimlane' => 'Modifier la swimlane par défaut',
'Default swimlane' => 'Swimlane par défaut',
'Do you really want to remove this swimlane: "%s"?' => 'Voulez-vous vraiment supprimer cette swimlane : « %s » ?',
'Do you really want to remove this swimlane: "%s"?' => 'Voulez-vous vraiment supprimer cette swimlane : « %s » ?',
'Inactive swimlanes' => 'Swimlanes inactives',
'Set project manager' => 'Mettre chef de projet',
'Set project member' => 'Mettre membre du projet',
@ -570,7 +568,7 @@ return array(
'Unable to update this swimlane.' => 'Impossible de mettre à jour cette swimlane.',
'Your swimlane have been created successfully.' => 'Votre swimlane a été créée avec succès.',
'Example: "Bug, Feature Request, Improvement"' => 'Exemple: « Incident, Demande de fonctionnalité, Amélioration »',
'Default categories for new projects (Comma-separated)' => 'Catégories par défaut pour les nouveaux projets (séparé par des virgules)',
'Default categories for new projects (Comma-separated)' => 'Catégories par défaut pour les nouveaux projets (séparation par des virgules)',
'Gitlab commit received' => 'Commit reçu via Gitlab',
'Gitlab issue opened' => 'Ouverture d\'un ticket sur Gitlab',
'Gitlab issue closed' => 'Fermeture d\'un ticket sur Gitlab',
@ -604,25 +602,25 @@ return array(
'User dashboard' => 'Tableau de bord de l\'utilisateur',
'Allow only one subtask in progress at the same time for a user' => 'Autoriser une seule sous-tâche en progrès en même temps pour un utilisateur',
'Edit column "%s"' => 'Modifier la colonne « %s »',
'Select the new status of the subtask: "%s"' => 'Selectionnez le nouveau statut de la sous-tâche : « %s »',
'Select the new status of the subtask: "%s"' => 'Selectionnez le nouveau statut de la sous-tâche : « %s »',
'Subtask timesheet' => 'Feuille de temps des sous-tâches',
'There is nothing to show.' => 'Il n\'y a rien à montrer.',
'Time Tracking' => 'Feuille de temps',
'You already have one subtask in progress' => 'Vous avez déjà une sous-tâche en progrès',
'Which parts of the project do you want to duplicate?' => 'Quelles parties du projet voulez-vous dupliquer ?',
'Disallow login form' => 'Interdir le formulaire d\'authentification',
'Which parts of the project do you want to duplicate?' => 'Quelles parties du projet voulez-vous dupliquer ?',
'Disallow login form' => 'Interdire le formulaire d\'authentification',
'Bitbucket commit received' => 'Commit reçu via Bitbucket',
'Bitbucket webhooks' => 'Webhook Bitbucket',
'Help on Bitbucket webhooks' => 'Aide sur les webhooks Bitbucket',
'Start' => 'Début',
'End' => 'Fin',
'Task age in days' => 'Age de la tâche en jours',
'Task age in days' => 'Âge de la tâche en jours',
'Days in this column' => 'Jours dans cette colonne',
'%dd' => '%dj',
'Add a link' => 'Ajouter un lien',
'Add a new link' => 'Ajouter un nouveau lien',
'Do you really want to remove this link: "%s"?' => 'Voulez-vous vraiment supprimer ce lien : « %s » ?',
'Do you really want to remove this link with task #%d?' => 'Voulez-vous vraiment supprimer ce lien avec la tâche n°%d ?',
'Do you really want to remove this link: "%s"?' => 'Voulez-vous vraiment supprimer ce lien : « %s » ?',
'Do you really want to remove this link with task #%d?' => 'Voulez-vous vraiment supprimer ce lien avec la tâche n°%d ?',
'Field required' => 'Champ obligatoire',
'Link added successfully.' => 'Lien créé avec succès.',
'Link updated successfully.' => 'Lien mis à jour avec succès.',
@ -658,7 +656,7 @@ return array(
'Expand tasks' => 'Déplier les tâches',
'Collapse tasks' => 'Replier les tâches',
'Expand/collapse tasks' => 'Plier/déplier les tâches',
'Close dialog box' => 'Fermer une boite de dialogue',
'Close dialog box' => 'Fermer une boîte de dialogue',
'Submit a form' => 'Enregistrer un formulaire',
'Board view' => 'Page du tableau',
'Keyboard shortcuts' => 'Raccourcis clavier',
@ -669,75 +667,26 @@ return array(
'Horizontal scrolling' => 'Défilement horizontal',
'Compact/wide view' => 'Basculer entre la vue compacte et étendue',
'No results match:' => 'Aucun résultat :',
'Remove hourly rate' => 'Supprimer un taux horaire',
'Do you really want to remove this hourly rate?' => 'Voulez-vous vraiment supprimer ce taux horaire ?',
'Hourly rates' => 'Taux horaires',
'Hourly rate' => 'Taux horaire',
'Currency' => 'Devise',
'Effective date' => 'Date d\'effet',
'Add new rate' => 'Ajouter un nouveau taux horaire',
'Rate removed successfully.' => 'Taux horaire supprimé avec succès.',
'Unable to remove this rate.' => 'Impossible de supprimer ce taux horaire.',
'Unable to save the hourly rate.' => 'Impossible de sauvegarder ce taux horaire.',
'Hourly rate created successfully.' => 'Taux horaire créé avec succès.',
'Start time' => 'Date de début',
'End time' => 'Date de fin',
'Comment' => 'Commentaire',
'All day' => 'Toute la journée',
'Day' => 'Jour',
'Manage timetable' => 'Gérer les horaires',
'Overtime timetable' => 'Heures supplémentaires',
'Time off timetable' => 'Heures d\'absences',
'Timetable' => 'Horaires',
'Work timetable' => 'Horaires travaillés',
'Week timetable' => 'Horaires de la semaine',
'Day timetable' => 'Horaire d\'une journée',
'From' => 'Depuis',
'To' => 'À',
'Time slot created successfully.' => 'Créneau horaire créé avec succès.',
'Unable to save this time slot.' => 'Impossible de sauvegarder ce créneau horaire.',
'Time slot removed successfully.' => 'Créneau horaire supprimé avec succès.',
'Unable to remove this time slot.' => 'Impossible de supprimer ce créneau horaire.',
'Do you really want to remove this time slot?' => 'Voulez-vous vraiment supprimer ce créneau horaire ?',
'Remove time slot' => 'Supprimer un créneau horaire',
'Add new time slot' => 'Ajouter un créneau horaire',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Ces horaires sont utilisés lorsque la case « Toute la journée » est cochée pour les heures d\'absences ou supplémentaires programmées.',
'Files' => 'Fichiers',
'Images' => 'Images',
'Private project' => 'Projet privé',
'Amount' => 'Montant',
'AUD - Australian Dollar' => 'AUD - Dollar australien',
'Budget' => 'Budget',
'Budget line' => 'Ligne budgétaire',
'Budget line removed successfully.' => 'Ligne budgétaire supprimée avec succès.',
'Budget lines' => 'Lignes budgétaire',
'CAD - Canadian Dollar' => 'CAD - Dollar canadien',
'CHF - Swiss Francs' => 'CHF - Franc suisse',
'Cost' => 'Coût',
'Cost breakdown' => 'Détail des coûts',
'Custom Stylesheet' => 'Feuille de style personalisée',
'download' => 'télécharger',
'Do you really want to remove this budget line?' => 'Voulez-vous vraiment supprimer cette ligne budgétaire ?',
'EUR - Euro' => 'EUR - Euro',
'Expenses' => 'Dépenses',
'GBP - British Pound' => 'GBP - Livre sterling',
'INR - Indian Rupee' => 'INR - Roupie indienne',
'JPY - Japanese Yen' => 'JPY - Yen',
'New budget line' => 'Nouvelle ligne budgétaire',
'NZD - New Zealand Dollar' => 'NZD - Dollar néo-zélandais',
'Remove a budget line' => 'Supprimer une ligne budgétaire',
'Remove budget line' => 'Supprimer une ligne budgétaire',
'RSD - Serbian dinar' => 'RSD - Dinar serbe',
'The budget line have been created successfully.' => 'La ligne de budgétaire a été créée avec succès.',
'Unable to create the budget line.' => 'Impossible de créer cette ligne budgétaire.',
'Unable to remove this budget line.' => 'Impossible de supprimer cette ligne budgétaire.',
'USD - US Dollar' => 'USD - Dollar américain',
'Remaining' => 'Restant',
'Destination column' => 'Colonne de destination',
'Move the task to another column when assigned to a user' => 'Déplacer la tâche dans une autre colonne lorsque celle-ci est assignée à quelqu\'un',
'Move the task to another column when assignee is cleared' => 'Déplacer la tâche dans une autre colonne lorsque celle-ci n\'est plus assignée',
'Source column' => 'Colonne d\'origine',
'Show subtask estimates (forecast of future work)' => 'Afficher l\'estimation des sous-tâches (prévision du travail à venir)',
'Transitions' => 'Transitions',
'Executer' => 'Exécutant',
'Time spent in the column' => 'Temps passé dans la colonne',
@ -748,7 +697,6 @@ return array(
'Rate' => 'Taux',
'Change reference currency' => 'Changer la monnaie de référence',
'Add a new currency rate' => 'Ajouter un nouveau taux pour une devise',
'Currency rates are used to calculate project budget.' => 'Le cours des devises est utilisé pour calculer le budget des projets.',
'Reference currency' => 'Devise de référence',
'The currency rate have been added successfully.' => 'Le taux de change a été ajouté avec succès.',
'Unable to add this currency rate.' => 'Impossible d\'ajouter ce taux de change',
@ -769,8 +717,8 @@ return array(
'Code' => 'Code',
'Two factor authentication' => 'Authentification à deux-facteurs',
'Enable/disable two factor authentication' => 'Activer/désactiver l\'authentification à deux-facteurs',
'This QR code contains the key URI: ' => 'Ce code QR contient l\'url de la clé : ',
'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => 'Sauvegardez cette clé secrete dans votre logiciel TOTP (par exemple Google Authenticator ou FreeOTP).',
'This QR code contains the key URI: ' => 'Ce code QR contient l\'url de la clé : ',
'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => 'Sauvegardez cette clé secrète dans votre logiciel TOTP (par exemple Google Authenticator ou FreeOTP).',
'Check my code' => 'Vérifier mon code',
'Secret key: ' => 'Clé secrète : ',
'Test your device' => 'Testez votre appareil',
@ -796,9 +744,9 @@ return array(
'Sendgrid (incoming emails)' => 'Sendgrid (emails entrants)',
'Help on Sendgrid integration' => 'Aide sur l\'intégration avec Sendgrid',
'Disable two factor authentication' => 'Désactiver l\'authentification à deux facteurs',
'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Voulez-vous vraiment désactiver l\'authentification à deux facteurs pour cet utilisateur : « %s » ?',
'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Voulez-vous vraiment désactiver l\'authentification à deux facteurs pour cet utilisateur : « %s » ?',
'Edit link' => 'Modifier un lien',
'Start to type task title...' => 'Entrez le titre de la tâche...',
'Start to type task title...' => 'Entrez le titre de la tâche',
'A task cannot be linked to itself' => 'Une tâche ne peut être liée à elle-même',
'The exact same link already exists' => 'Un lien identique existe déjà',
'Recurrent task is scheduled to be generated' => 'La tâche récurrente est programmée pour être créée',
@ -814,17 +762,17 @@ return array(
'Timeframe to calculate new due date' => 'Échelle de temps pour calculer la nouvelle date d\'échéance',
'Base date to calculate new due date' => 'Date à utiliser pour calculer la nouvelle date d\'échéance',
'Action date' => 'Date de l\'action',
'Base date to calculate new due date: ' => 'Date utilisée pour calculer la nouvelle date d\'échéance : ',
'This task has created this child task: ' => 'Cette tâche a créée la tâche enfant : ',
'Base date to calculate new due date: ' => 'Date utilisée pour calculer la nouvelle date d\'échéance : ',
'This task has created this child task: ' => 'Cette tâche a créée la tâche enfant : ',
'Day(s)' => 'Jour(s)',
'Existing due date' => 'Date d\'échéance existante',
'Factor to calculate new due date: ' => 'Facteur pour calculer la nouvelle date d\'échéance : ',
'Factor to calculate new due date: ' => 'Facteur pour calculer la nouvelle date d\'échéance : ',
'Month(s)' => 'Mois',
'Recurrence' => 'Récurrence',
'This task has been created by: ' => 'Cette tâche a été créée par :',
'Recurrent task has been generated:' => 'Une tâche récurrente a été générée :',
'Timeframe to calculate new due date: ' => 'Échelle de temps pour calculer la nouvelle date d\'échéance : ',
'Trigger to generate recurrent task: ' => 'Déclencheur pour générer la tâche récurrente : ',
'This task has been created by: ' => 'Cette tâche a été créée par :',
'Recurrent task has been generated:' => 'Une tâche récurrente a été générée :',
'Timeframe to calculate new due date: ' => 'Échelle de temps pour calculer la nouvelle date d\'échéance : ',
'Trigger to generate recurrent task: ' => 'Déclencheur pour générer la tâche récurrente : ',
'When task is closed' => 'Lorsque la tâche est fermée',
'When task is moved from first column' => 'Lorsque la tâche est déplacée en dehors de la première colonne',
'When task is moved to last column' => 'Lorsque la tâche est déplacée dans la dernière colonne',
@ -836,7 +784,7 @@ return array(
'Jabber nickname' => 'Pseudonyme Jabber',
'Multi-user chat room' => 'Salon de discussion multi-utilisateurs',
'Help on Jabber integration' => 'Aide sur l\'intégration avec Jabber',
'The server address must use this format: "tcp://hostname:5222"' => 'L\'adresse du serveur doit utiliser le format suivant : « tcp://hostname:5222 »',
'The server address must use this format: "tcp://hostname:5222"' => 'L\'adresse du serveur doit utiliser le format suivant : « tcp://hostname:5222 »',
'Calendar settings' => 'Paramètres du calendrier',
'Project calendar view' => 'Vue en mode projet du calendrier',
'Project settings' => 'Paramètres du projet',
@ -849,7 +797,7 @@ return array(
'iCal feed' => 'Abonnement iCal',
'Preferences' => 'Préférences',
'Security' => 'Sécurité',
'Two factor authentication disabled' => 'Authentification à deux facteurs désactivé',
'Two factor authentication disabled' => 'Authentification à deux facteurs désactivée',
'Two factor authentication enabled' => 'Authentification à deux facteurs activée',
'Unable to update this user.' => 'Impossible de mettre à jour cet utilisateur.',
'There is no user management for private projects.' => 'Il n\'y a pas de gestion d\'utilisateurs pour les projets privés.',
@ -862,8 +810,8 @@ return array(
'Commit made by @%s on Github' => 'Commit fait par @%s sur Github',
'By @%s on Github' => 'Par @%s sur Github',
'Commit made by @%s on Gitlab' => 'Commit fait par @%s sur Gitlab',
'Add a comment log when moving the task between columns' => 'Ajouter un commentaire d\'information lorsque une tâche est déplacée dans une autre colonnes',
'Move the task to another column when the category is changed' => 'Déplacer une tâche vers une autre colonne lorsque la catégorie a changée',
'Add a comment log when moving the task between columns' => 'Ajouter un commentaire d\'information lorsque une tâche est déplacée dans une autre colonne',
'Move the task to another column when the category is changed' => 'Déplacer une tâche vers une autre colonne lorsque la catégorie a changé',
'Send a task by email to someone' => 'Envoyer une tâche par email à quelqu\'un',
'Reopen a task' => 'Rouvrir une tâche',
'Bitbucket issue opened' => 'Ticket Bitbucket ouvert',
@ -880,16 +828,13 @@ return array(
'%s moved the task #%d to the first swimlane' => '%s a déplacé la tâche n°%d dans la première swimlane',
'%s moved the task #%d to the swimlane "%s"' => '%s a déplacé la tâche n°%d dans la swimlane « %s »',
'Swimlane' => 'Swimlane',
'Budget overview' => 'Vue d\'ensemble du budget',
'Type' => 'Type',
'There is not enough data to show something.' => 'Il n\'y a pas assez de données pour montrer quelque chose.',
'Gravatar' => 'Gravatar',
'Hipchat' => 'Hipchat',
'Slack' => 'Slack',
'%s moved the task %s to the first swimlane' => '%s a déplacé la tâche %s dans la première swimlane',
'%s moved the task %s to the swimlane "%s"' => '%s a déplacé la tâche %s dans la swimlane « %s »',
'This report contains all subtasks information for the given date range.' => 'Ce rapport contient les informations de toutes les sous-tâches pour la période selectionnée.',
'This report contains all tasks information for the given date range.' => 'Ce rapport contient les informations de toutes les tâches pour la période selectionnée.',
'%s moved the task %s to the swimlane "%s"' => '%s a déplacé la tâche %s dans la swimlane « %s »',
'This report contains all subtasks information for the given date range.' => 'Ce rapport contient les informations de toutes les sous-tâches pour la période sélectionnée.',
'This report contains all tasks information for the given date range.' => 'Ce rapport contient les informations de toutes les tâches pour la période sélectionnée.',
'Project activities for %s' => 'Activité des projets pour « %s »',
'view the board on Kanboard' => 'voir le tableau sur Kanboard',
'The task have been moved to the first swimlane' => 'La tâche a été déplacée dans la première swimlane',
@ -907,15 +852,15 @@ return array(
'Recurrence settings have been modified' => 'Les réglages de la récurrence ont été modifiés',
'Time spent changed: %sh' => 'Le temps passé a été changé : %sh',
'Time estimated changed: %sh' => 'Le temps estimé a été changé : %sh',
'The field "%s" have been updated' => 'Le champ « %s » a été mis à jour',
'The field "%s" have been updated' => 'Le champ « %s » a été mis à jour',
'The description have been modified' => 'La description a été modifiée',
'Do you really want to close the task "%s" as well as all subtasks?' => 'Voulez-vous vraiment fermer la tâche « %s » ainsi que toutes ses sous-tâches ?',
'Do you really want to close the task "%s" as well as all subtasks?' => 'Voulez-vous vraiment fermer la tâche « %s » ainsi que toutes ses sous-tâches ?',
'Swimlane: %s' => 'Swimlane : %s',
'I want to receive notifications for:' => 'Je veux reçevoir les notifications pour :',
'All tasks' => 'Toutes les Tâches',
'Only for tasks assigned to me' => 'Seulement les tâches qui me sont assignées',
'Only for tasks created by me' => 'Seulement les tâches que j\'ai créées',
'Only for tasks created by me and assigned to me' => 'Seulement les tâches créées par moi-même et celles qui me sont asignées',
'Only for tasks created by me and assigned to me' => 'Seulement les tâches créées par moi-même et celles qui me sont assignées',
'%A' => '%A',
'%b %e, %Y, %k:%M %p' => '%d/%m/%Y %H:%M',
'New due date: %B %e, %Y' => 'Nouvelle date d\'échéance : %d/%m/%Y',
@ -995,7 +940,7 @@ return array(
'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 « Interdir le formulaire d\'authentification », les identifiants entrés dans le formulaire d\'authentification seront ignorés.',
'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.',
'By @%s on Gitlab' => 'Par @%s sur Gitlab',
'Gitlab issue comment created' => 'Commentaire créé sur un ticket Gitlab',
'New remote user' => 'Créer un utilisateur distant',
@ -1023,14 +968,14 @@ return array(
'Sort by position' => 'Trier par position',
'Sort by date' => 'Trier par date',
'Add task' => 'Ajouter une tâche',
'Start date:' => 'Date de début :',
'Due date:' => 'Date d\'échéance :',
'Start date:' => 'Date de début :',
'Due date:' => 'Date d\'échéance :',
'There is no start date or due date for this task.' => 'Il n\'y a pas de date de début ou de date de fin pour cette tâche.',
'Moving or resizing a task will change the start and due date of the task.' => 'Déplacer ou redimensionner une tâche va changer la date de début et la date de fin de la tâche.',
'There is no task in your project.' => 'Il n\'y a aucun tâche dans votre projet.',
'There is no task in your project.' => 'Il n\'y a aucune tâche dans votre projet.',
'Gantt chart' => 'Diagramme de Gantt',
'People who are project managers' => 'Personnes qui sont gestionnaire de projet',
'People who are project members' => 'Personnes qui sont membre de projet',
'People who are project managers' => 'Personnes qui sont gestionnaires de projet',
'People who are project members' => 'Personnes qui sont membres de projet',
'NOK - Norwegian Krone' => 'NOK - Couronne norvégienne',
'Show this column' => 'Montrer cette colonne',
'Hide this column' => 'Cacher cette colonne',
@ -1043,14 +988,14 @@ return array(
'Project managers' => 'Gestionnaires de projet',
'Project members' => 'Membres de projet',
'Gantt chart for all projects' => 'Diagramme de Gantt pour tous les projets',
'Projects list' => 'List des projets',
'Projects list' => 'Liste des projets',
'Gantt chart for this project' => 'Diagramme de Gantt pour ce projet',
'Project board' => 'Tableau du projet',
'End date:' => 'Date de fin :',
'End date:' => 'Date de fin :',
'There is no start date or end date for this project.' => 'Il n\'y a pas de date de début ou de date de fin pour ce projet.',
'Projects Gantt chart' => 'Diagramme de Gantt des projets',
'Start date: %s' => 'Date de début : %s',
'End date: %s' => 'Date de fin : %s',
'Start date: %s' => 'Date de début : %s',
'End date: %s' => 'Date de fin : %s',
'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',
@ -1069,4 +1014,48 @@ return array(
'Table of contents' => 'Table des matières',
'Gantt' => 'Gantt',
'Help with project permissions' => 'Aide avec les permissions des projets',
'Author' => 'Auteur',
'Version' => 'Version',
'Plugins' => 'Extensions',
'There is no plugin loaded.' => 'Il n\'y a aucune extension chargée.',
'Set maximum column height' => 'Définir la hauteur max. des colonnes',
'Remove maximum column height' => 'Enlever la hauteur max. des colonnes',
'My notifications' => 'Mes notifications',
'Custom filters' => 'Filtres personalisés',
'Your custom filter have been created successfully.' => 'Votre filter personalisé a été créé avec succès.',
'Unable to create your custom filter.' => 'Impossible de créer votre filter personalisé.',
'Custom filter removed successfully.' => 'Filtre personalisé supprimé avec succès.',
'Unable to remove this custom filter.' => 'Impossible de supprimer ce filter personalisé.',
'Edit custom filter' => 'Modification d\'un filtre personalisé',
'Your custom filter have been updated successfully.' => 'Votre filtre personalisé a été mis à jour avec succès.',
'Unable to update custom filter.' => 'Impossible de mettre à jour votre filtre personalisé.',
'Web' => 'Web',
'New attachment on task #%d: %s' => 'Nouveau fichier joint sur la tâche n°%d : %s',
'New comment on task #%d' => 'Nouveau commentaire sur la tâche n°%d',
'Comment updated on task #%d' => 'Commentaire mis à jour sur la tâche n°%d',
'New subtask on task #%d' => 'Nouvelle sous-tâche sur la tâche n°%d',
'Subtask updated on task #%d' => 'Sous-tâche mise à jour sur la tâche n°%d',
'New task #%d: %s' => 'Nouvelle tâche n°%d : %s',
'Task updated #%d' => 'Tâche n°%d mise à jour',
'Task #%d closed' => 'Tâche n°%d fermée',
'Task #%d opened' => 'Tâche n°%d ouverte',
'Column changed for task #%d' => 'Changement de colonne pour la tâche n°%d',
'New position for task #%d' => 'Nouvelle position pour la tâche n°%d',
'Swimlane changed for task #%d' => 'Changement de swimlane pour la tâche n°%d',
'Assignee changed on task #%d' => 'Changement de l\'assigné pour la tâche n°%d',
'%d overdue tasks' => '%d tâches en retard',
'Task #%d is overdue' => 'La tâche n°%d est retard',
'No new notifications.' => 'Aucune notification.',
'Mark all as read' => 'Tout marquer comme lu',
'Mark as read' => 'Marquer comme lu',
'Total number of tasks in this column across all swimlanes' => 'Nombre total de tâches dans cette colonne pour toutes les swimlanes',
'Collapse swimlane' => 'Replier la swimlane',
'Expand swimlane' => 'Déplier la swimlane',
'Add a new filter' => 'Ajouter un nouveau filtre',
'Share with all project members' => 'Partager avec tous les membres du projet',
'Shared' => 'Partagé',
'Owner' => 'Propriétaire',
'Unread notifications' => 'Notifications non lus',
'My filters' => 'Mes filtres',
'Notification methods:' => 'Méthodes de notifications :',
);

View file

@ -395,8 +395,6 @@ return array(
'Remote' => 'Távoli',
'Enabled' => 'Engedélyezve',
'Disabled' => 'Letiltva',
'Google account linked' => 'Google fiók összekapcsolva',
'Github account linked' => 'Github fiók összekapcsolva',
'Username:' => 'Felhasználónév:',
'Name:' => 'Név:',
'Email:' => 'E-mail:',
@ -667,75 +665,26 @@ return array(
'Horizontal scrolling' => 'Vízszintes görgetés',
'Compact/wide view' => 'Kompakt/széles nézet',
'No results match:' => 'Nincs találat:',
'Remove hourly rate' => 'Órabér törlése',
'Do you really want to remove this hourly rate?' => 'Valóban törölni kívánja az órabért?',
'Hourly rates' => 'Órabérek',
'Hourly rate' => 'Órabér',
'Currency' => 'Pénznem',
'Effective date' => 'Hatálybalépés ideje',
'Add new rate' => 'Új bér',
'Rate removed successfully.' => 'Bér sikeresen törölve.',
'Unable to remove this rate.' => 'Bér törlése sikertelen.',
'Unable to save the hourly rate.' => 'Órabér mentése sikertelen.',
'Hourly rate created successfully.' => 'Órabér sikeresen mentve.',
'Start time' => 'Kezdés ideje',
'End time' => 'Végzés ideje',
'Comment' => 'Megjegyzés',
'All day' => 'Egész nap',
'Day' => 'Nap',
'Manage timetable' => 'Időbeosztás kezelése',
'Overtime timetable' => 'Túlóra időbeosztás',
'Time off timetable' => 'Szabadság időbeosztás',
'Timetable' => 'Időbeosztás',
'Work timetable' => 'Munka időbeosztás',
'Week timetable' => 'Heti időbeosztás',
'Day timetable' => 'Napi időbeosztás',
'From' => 'Feladó:',
'To' => 'Címzett:',
'Time slot created successfully.' => 'Időszelet sikeresen létrehozva.',
'Unable to save this time slot.' => 'Időszelet mentése sikertelen.',
'Time slot removed successfully.' => 'Időszelet sikeresen törölve.',
'Unable to remove this time slot.' => 'Időszelet törlése sikertelen.',
'Do you really want to remove this time slot?' => 'Biztos törli ezt az időszeletet?',
'Remove time slot' => 'Időszelet törlése',
'Add new time slot' => 'Új Időszelet',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Ez az időbeosztás van használatban ha az "egész nap" jelölőnégyzet be van jelölve a tervezett szabadságnál és túlóránál.',
'Files' => 'Fájlok',
'Images' => 'Képek',
'Private project' => 'Privát projekt',
'Amount' => 'Összeg',
'AUD - Australian Dollar' => 'AUD - Ausztrál dollár',
'Budget' => 'Költségvetés',
'Budget line' => 'Költségvetési tétel',
'Budget line removed successfully.' => 'Költségvetési tétel sikeresen törölve.',
'Budget lines' => 'Költségvetési tételek',
'CAD - Canadian Dollar' => 'CAD - Kanadai dollár',
'CHF - Swiss Francs' => 'CHF - Svájci frank',
'Cost' => 'Költség',
'Cost breakdown' => 'Költség visszaszámlálás',
'Custom Stylesheet' => 'Egyéni sítluslap',
'download' => 'letöltés',
'Do you really want to remove this budget line?' => 'Biztos törölni akarja ezt a költségvetési tételt?',
'EUR - Euro' => 'EUR - Euro',
'Expenses' => 'Kiadások',
'GBP - British Pound' => 'GBP - Angol font',
'INR - Indian Rupee' => 'INR - Indiai rúpia',
'JPY - Japanese Yen' => 'JPY - Japán Yen',
'New budget line' => 'Új költségvetési tétel',
'NZD - New Zealand Dollar' => 'NZD - Új-Zélandi dollár',
'Remove a budget line' => 'Költségvetési tétel törlése',
'Remove budget line' => 'Költségvetési tétel törlése',
'RSD - Serbian dinar' => 'RSD - Szerb dínár',
'The budget line have been created successfully.' => 'Költségvetési tétel sikeresen létrehozva.',
'Unable to create the budget line.' => 'Költségvetési tétel létrehozása sikertelen.',
'Unable to remove this budget line.' => 'Költségvetési tétel törlése sikertelen.',
'USD - US Dollar' => 'USD - Amerikai ollár',
'Remaining' => 'Maradék',
'Destination column' => 'Cél oszlop',
'Move the task to another column when assigned to a user' => 'Feladat másik oszlopba helyezése felhasználóhoz rendélés után',
'Move the task to another column when assignee is cleared' => 'Feladat másik oszlopba helyezése felhasználóhoz rendélés törlésekor',
'Source column' => 'Forrás oszlop',
// 'Show subtask estimates (forecast of future work)' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
@ -746,7 +695,6 @@ return array(
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
@ -878,9 +826,6 @@ return array(
// '%s moved the task #%d to the first swimlane' => '',
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Budget overview' => '',
// 'Type' => '',
// 'There is not enough data to show something.' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
@ -1067,4 +1012,48 @@ return array(
// 'Table of contents' => '',
// 'Gantt' => '',
// 'Help with project permissions' => '',
// '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:' => '',
);

File diff suppressed because it is too large Load diff

View file

@ -395,8 +395,6 @@ return array(
'Remote' => 'Remoto',
'Enabled' => 'Abilitato',
'Disabled' => 'Disabilitato',
'Google account linked' => 'Account Google collegato',
'Github account linked' => 'Account Github collegato',
// 'Username:' => '',
'Name:' => 'Nome:',
// 'Email:' => '',
@ -667,75 +665,26 @@ return array(
'Horizontal scrolling' => 'Scrolling orizzontale',
'Compact/wide view' => 'Vista compatta/estesa',
'No results match:' => 'Nessun risultato trovato:',
'Remove hourly rate' => 'Rimuovi tariffa oraria',
'Do you really want to remove this hourly rate?' => 'Vuoi davvero rimuovere questa tariffa oraria?',
'Hourly rates' => 'Tariffe orarie',
'Hourly rate' => 'Tariffa oraria',
'Currency' => 'Valuta',
'Effective date' => 'Data effettiva',
'Add new rate' => 'Aggiungi una nuova tariffa',
'Rate removed successfully.' => 'Tariffa rimossa con successo.',
'Unable to remove this rate.' => 'Impossibile rimuovere questa tariffa.',
'Unable to save the hourly rate.' => 'Impossibile salvare la tariffa oraria.',
'Hourly rate created successfully.' => 'Tariffa oraria creata con successo.',
'Start time' => 'Data di inizio',
'End time' => 'Data di completamento',
'Comment' => 'Commento',
'All day' => 'Tutto il giorno',
'Day' => 'Giorno',
'Manage timetable' => 'Gestisci orario',
'Overtime timetable' => 'Straordinari',
'Time off timetable' => 'Fuori orario',
'Timetable' => 'Orario',
'Work timetable' => 'Orario di lavoro',
'Week timetable' => 'Orario settimanale',
'Day timetable' => 'Orario giornaliero',
'From' => 'Da',
'To' => 'A',
'Time slot created successfully.' => 'Fascia oraria creata con successo.',
'Unable to save this time slot.' => 'Impossibile creare questa fascia oraria.',
'Time slot removed successfully.' => 'Fascia oraria rimossa con successo.',
'Unable to remove this time slot.' => 'Impossibile rimuovere questa fascia oraria.',
'Do you really want to remove this time slot?' => 'Vuoi davvero rimuovere questa fascia oraria?',
'Remove time slot' => 'Rimuovi fascia oraria',
'Add new time slot' => 'Aggiungi nuova fascia oraria',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Questo orario è utilizzato quando la casella "tutto il giorno" è selezionata per i fuori orari e per gli straordinari',
// 'Files' => '',
'Images' => 'Immagini',
'Private project' => 'Progetto privato',
'Amount' => 'Totale',
'AUD - Australian Dollar' => 'AUD - Dollari Australiani',
'Budget' => 'Bilancio',
'Budget line' => 'Limite di bilancio',
'Budget line removed successfully.' => 'Limite al bilancio rimosso con successo.',
'Budget lines' => 'Limiti al bilancio',
'CAD - Canadian Dollar' => 'CAD - Dollari Canadesi',
'CHF - Swiss Francs' => 'CHF - Franchi Svizzeri',
'Cost' => 'Costi',
'Cost breakdown' => 'Abbattimento dei costi',
'Custom Stylesheet' => 'CSS personalizzato',
// 'download' => '',
'Do you really want to remove this budget line?' => 'Vuoi davvero rimuovere questo limite al bilancio?',
// 'EUR - Euro' => '',
'Expenses' => 'Spese',
'GBP - British Pound' => 'GBP - Pound Inglesi',
'INR - Indian Rupee' => 'INR - Rupie Indiani',
'JPY - Japanese Yen' => 'JPY - Yen Giapponesi',
'New budget line' => 'Nuovo limite al bilancio',
'NZD - New Zealand Dollar' => 'NZD - Dollari della Nuova Zelanda',
'Remove a budget line' => 'Rimuovi un limite al bilancio',
'Remove budget line' => 'Rimuovi limite di bilancio',
'RSD - Serbian dinar' => 'RSD - Dinar Serbi',
'The budget line have been created successfully.' => 'Il limite al bilancio è stato creato correttamente',
'Unable to create the budget line.' => 'Impossibile creare il limite al bilancio',
'Unable to remove this budget line.' => 'Impossibile rimuovere questo limite al bilancio.',
'USD - US Dollar' => 'USD - Dollari Americani',
'Remaining' => 'Restanti',
'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',
'Source column' => 'Colonna sorgente',
// 'Show subtask estimates (forecast of future work)' => '',
'Transitions' => 'Transizioni',
'Executer' => 'Esecutore',
'Time spent in the column' => 'Tempo trascorso nella colonna',
@ -746,7 +695,6 @@ return array(
'Rate' => 'Cambio',
'Change reference currency' => 'Cambia la valuta di riferimento',
'Add a new currency rate' => 'Aggiungi un nuovo tasso di cambio',
'Currency rates are used to calculate project budget.' => 'I tassi di cambio sono utilizzati per calcolare i bilanci dei progetti',
'Reference currency' => 'Valuta di riferimento',
'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.',
@ -878,9 +826,6 @@ return array(
// '%s moved the task #%d to the first swimlane' => '',
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Budget overview' => '',
// 'Type' => '',
// 'There is not enough data to show something.' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
@ -1067,4 +1012,48 @@ return array(
// 'Table of contents' => '',
// 'Gantt' => '',
// 'Help with project permissions' => '',
// '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:' => '',
);

View file

@ -395,8 +395,6 @@ return array(
'Remote' => 'リモート',
'Enabled' => '有効',
'Disabled' => '無効',
'Google account linked' => 'Google アカウントがリンク',
'Github account linked' => 'Github のアカウントがリンク',
'Username:' => 'ユーザ名:',
'Name:' => '名前:',
'Email:' => 'Email:',
@ -667,75 +665,26 @@ return array(
'Horizontal scrolling' => '縦スクロール',
'Compact/wide view' => 'コンパクト/ワイドビュー',
'No results match:' => '結果が一致しませんでした',
'Remove hourly rate' => '毎時レートを削除',
'Do you really want to remove this hourly rate?' => '毎時レートを削除しますか?',
'Hourly rates' => '毎時レート',
'Hourly rate' => '毎時レート',
'Currency' => '通貨',
'Effective date' => '有効期限',
'Add new rate' => '新しいレート',
'Rate removed successfully.' => 'レートの削除に成功しました。',
'Unable to remove this rate.' => 'レートを削除できませんでした。',
'Unable to save the hourly rate.' => '時間毎のレートを保存できませんでした。',
'Hourly rate created successfully.' => '時間毎のレートを作成しました。',
'Start time' => '開始時間',
'End time' => '終了時間',
'Comment' => 'コメント',
'All day' => '終日',
'Day' => '日',
'Manage timetable' => 'タイムテーブルの管理',
'Overtime timetable' => '残業タイムテーブル',
'Time off timetable' => '休暇タイムテーブル',
'Timetable' => 'タイムテーブル',
'Work timetable' => 'ワークタイムテーブル',
'Week timetable' => '週次タイムテーブル',
'Day timetable' => '日時タイムテーブル',
'From' => 'ここから',
'To' => 'ここまで',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
'Remove time slot' => 'タイムスロットの削除',
'Add new time slot' => 'タイムラインの追加',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'このタイムテーブルは、残業や休暇で全日がチェックされた場合に用いられます。',
'Files' => 'ファイル',
'Images' => '画像',
'Private project' => 'プライベートプロジェクト',
'Amount' => '数量',
'AUD - Australian Dollar' => 'AUD - 豪ドル',
'Budget' => '予算',
'Budget line' => '予算ライン',
'Budget line removed successfully.' => '予算ラインを削除しました.',
'Budget lines' => '予算ライン',
'CAD - Canadian Dollar' => 'CAD - 加ドル',
'CHF - Swiss Francs' => 'CHF - スイスフラン',
'Cost' => 'コスト',
'Cost breakdown' => 'コストブレークダウン',
'Custom Stylesheet' => 'カスタムスタイルシート',
'download' => 'ダウンロード',
'Do you really want to remove this budget line?' => 'この予算ラインを本当に削除しますか?',
'EUR - Euro' => 'EUR - ユーロ',
'Expenses' => '支出',
'GBP - British Pound' => 'GBP - 独ポンド',
'INR - Indian Rupee' => 'INR - 伊ルピー',
'JPY - Japanese Yen' => 'JPY - 日本円',
'New budget line' => '新しい予算ライン',
'NZD - New Zealand Dollar' => 'NZD - NZ ドル',
'Remove a budget line' => '予算ラインの削除',
'Remove budget line' => '予算ラインの削除',
'RSD - Serbian dinar' => 'RSD - セルビアデナール',
'The budget line have been created successfully.' => '予算ラインを作成しました',
'Unable to create the budget line.' => '予算ラインを作成できませんでした。',
'Unable to remove this budget line.' => '予算ラインを削除できませんでした。',
'USD - US Dollar' => 'USD - 米ドル',
'Remaining' => '残り',
'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' => '移動元のカラム',
// 'Show subtask estimates (forecast of future work)' => '',
'Transitions' => '履歴',
'Executer' => '実行者',
'Time spent in the column' => 'カラムでの時間消費',
@ -746,7 +695,6 @@ return array(
'Rate' => 'レート',
'Change reference currency' => '現在の基軸通貨',
'Add a new currency rate' => '新しい通貨レートを追加',
'Currency rates are used to calculate project budget.' => '通貨レートはプロジェクト予算の算出に利用されます。',
'Reference currency' => '基軸通貨',
// 'The currency rate have been added successfully.' => '',
'Unable to add this currency rate.' => 'この通貨レートを追加できません。',
@ -878,9 +826,6 @@ return array(
// '%s moved the task #%d to the first swimlane' => '',
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Budget overview' => '',
// 'Type' => '',
// 'There is not enough data to show something.' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
@ -1067,4 +1012,48 @@ return array(
// 'Table of contents' => '',
// 'Gantt' => '',
// 'Help with project permissions' => '',
// '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:' => '',
);

View file

@ -1,8 +1,8 @@
<?php
return array(
// 'number.decimals_separator' => '',
// 'number.thousands_separator' => '',
'number.decimals_separator' => ',',
'number.thousands_separator' => '.',
'None' => 'Ingen',
'edit' => 'rediger',
'Edit' => 'Rediger',
@ -79,7 +79,7 @@ return array(
'Assigned to %s' => 'Tildelt: %s',
'Remove a column' => 'Fjern en kolonne',
'Remove a column from a board' => 'Fjern en kolonne fra et board',
'Unable to remove this column.' => 'Ikke mulig fjerne denne kolonnen',
'Unable to remove this column.' => 'Ikke mulig ø fjerne denne kolonnen',
'Do you really want to remove this column: "%s"?' => 'Vil du fjerne denne kolonnen: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Denne handlingen vil SLETTE ALLE OPPGAVER tilknyttet denne kolonnen',
'Settings' => 'Innstillinger',
@ -100,7 +100,7 @@ return array(
'Create another task' => 'Opprett en annen oppgave',
'New task' => 'Ny oppgave',
'Open a task' => 'Åpne en oppgave',
'Do you really want to open this task: "%s"?' => 'Vil du åpe denne oppgaven: "%s"?',
'Do you really want to open this task: "%s"?' => 'Vil du åpne denne oppgaven: "%s"?',
'Back to the board' => 'Tilbake til prosjektsiden',
'Created on %B %e, %Y at %k:%M %p' => 'Opprettet %d.%m.%Y - %H:%M',
'There is nobody assigned' => 'Mangler tildeling',
@ -121,7 +121,7 @@ return array(
'Passwords don\'t match' => 'Passordene stemmer ikke overens',
'The confirmation is required' => 'Bekreftelse er nødvendig',
'The project is required' => 'Prosjektet er påkrevet',
'The id is required' => 'Id\'en er påkrevd',
'The id is required' => 'Id\'en er pøøkrevet',
'The project id is required' => 'Prosjektet-id er påkrevet',
'The project name is required' => 'Prosjektnavn er påkrevet',
'This project must be unique' => 'Prosjektnavnet skal være unikt',
@ -149,7 +149,7 @@ return array(
'Task created successfully.' => 'Oppgaven er opprettet.',
'User created successfully.' => 'Brukeren er opprettet.',
'Unable to create your user.' => 'Brukeren kunne ikke opprettes.',
'User updated successfully.' => 'Brukeren er opdateret',
'User updated successfully.' => 'Brukeren er oppdatert',
'Unable to update your user.' => 'Din bruker kunne ikke oppdateres.',
'User removed successfully.' => 'Brukeren er fjernet.',
'Unable to remove this user.' => 'Brukeren kunne ikke slettes.',
@ -175,10 +175,10 @@ return array(
'Page not found' => 'Siden er ikke funnet',
'Complexity' => 'Kompleksitet',
'Task limit' => 'Oppgave begrensning',
// 'Task count' => '',
'Task count' => 'Antall oppgaver',
'Edit project access list' => 'Endre tillatelser for prosjektet',
'Allow this user' => 'Tillat denne brukeren',
'Don\'t forget that administrators have access to everything.' => 'Hust at administratorer har tilgang til alt.',
'Don\'t forget that administrators have access to everything.' => 'Husk at administratorer har tilgang til alt.',
'Revoke' => 'Fjern',
'List of authorized users' => 'Liste over autoriserte brukere',
'User' => 'Bruker',
@ -205,16 +205,16 @@ return array(
'Automatic actions for the project "%s"' => 'Automatiske handlinger for prosjektet "%s"',
'Defined actions' => 'Definerte handlinger',
'Add an action' => 'Legg til en handling',
'Event name' => 'Begivenhet',
'Event name' => 'Hendelsehet',
'Action name' => 'Handling',
'Action parameters' => 'Handlingsparametre',
'Action' => 'Handling',
'Event' => 'Begivenhet',
'When the selected event occurs execute the corresponding action.' => 'Når den valgtebegivenheten oppstår, utføre tilsvarende handlin.',
'Event' => 'Hendelse',
'When the selected event occurs execute the corresponding action.' => 'Når den valgte hendelsen oppstår, utfør tilsvarende handling.',
'Next step' => 'Neste',
'Define action parameters' => 'Definer handlingsparametre',
'Save this action' => 'Lagre handlingen',
'Do you really want to remove this action: "%s"?' => 'Vil du virkelig slette denne handlingen: "%s"?',
'Do you really want to remove this action: "%s"?' => 'Vil du slette denne handlingen: "%s"?',
'Remove an automatic action' => 'Fjern en automatisk handling',
'Assign the task to a specific user' => 'Tildel oppgaven til en bestemt bruker',
'Assign the task to the person who does the action' => 'Tildel oppgaven til den person, som utfører handlingen',
@ -236,13 +236,13 @@ return array(
'Remove a comment' => 'Fjern en kommentar',
'Comment removed successfully.' => 'Kommentaren ble fjernet.',
'Unable to remove this comment.' => 'Kommentaren kunne ikke fjernes.',
'Do you really want to remove this comment?' => 'Vil du virkelig fjerne denne kommentaren?',
'Do you really want to remove this comment?' => 'Vil du fjerne denne kommentaren?',
'Only administrators or the creator of the comment can access to this page.' => 'Kun administrator eller brukeren, som har oprettet kommentaren har adgang til denne siden.',
'Current password for the user "%s"' => 'Aktivt passord for brukeren "%s"',
'The current password is required' => 'Passord er påkrevet',
'Wrong password' => 'Feil passord',
'Unknown' => 'Ukjent',
'Last logins' => 'Siste login',
'Last logins' => 'Siste innlogging',
'Login date' => 'Login dato',
'Authentication method' => 'Godkjenningsmetode',
'IP address' => 'IP Adresse',
@ -275,7 +275,7 @@ return array(
'Task removed successfully.' => 'Oppgaven er fjernet.',
'Unable to remove this task.' => 'Oppgaven kunne ikke fjernes.',
'Remove a task' => 'Fjern en oppgave',
'Do you really want to remove this task: "%s"?' => 'Vil du virkelig fjerne denne opgave: "%s"?',
'Do you really want to remove this task: "%s"?' => 'Vil du fjerne denne oppgaven: "%s"?',
'Assign automatically a color based on a category' => 'Tildel automatisk en farge baseret for en kategori',
'Assign automatically a category based on a color' => 'Tildel automatisk en kategori basert på en farve',
'Task creation or modification' => 'Oppgaveopprettelse eller endring',
@ -293,7 +293,7 @@ return array(
'Category modification for the project "%s"' => 'Endring av kategori for prosjektet "%s"',
'Category Name' => 'Kategorinavn',
'Add a new category' => 'Legg til ny kategori',
'Do you really want to remove this category: "%s"?' => 'Vil du virkelig fjerne kategorien: "%s"?',
'Do you really want to remove this category: "%s"?' => 'Vil du fjerne kategorien: "%s"?',
'All categories' => 'Alle kategorier',
'No category' => 'Ingen kategori',
'The name is required' => 'Navnet er påkrevet',
@ -301,9 +301,9 @@ return array(
'Unable to remove this file.' => 'Filen kunne ikke fjernes.',
'File removed successfully.' => 'Filen er fjernet.',
'Attach a document' => 'Legg til et dokument',
'Do you really want to remove this file: "%s"?' => 'Vil du virkelig fjerne filen: "%s"?',
'open' => 'åpen',
'Attachments' => 'Vedleggr',
'Do you really want to remove this file: "%s"?' => 'Vil du fjerne filen: "%s"?',
'open' => 'øpen',
'Attachments' => 'Vedlegg',
'Edit the task' => 'Rediger oppgaven',
'Edit the description' => 'Rediger beskrivelsen',
'Add a comment' => 'Legg til en kommentar',
@ -312,7 +312,7 @@ return array(
'Time tracking' => 'Tidsregistrering',
'Estimate:' => 'Estimat:',
'Spent:' => 'Brukt:',
'Do you really want to remove this sub-task?' => 'Vil du virkelig fjerne denne deloppgaven?',
'Do you really want to remove this sub-task?' => 'Vil du fjerne denne deloppgaven?',
'Remaining:' => 'Gjenværende:',
'hours' => 'timer',
'spent' => 'brukt',
@ -354,7 +354,7 @@ return array(
'Project cloned successfully.' => 'Prosjektet er kopiert.',
'Unable to clone this project.' => 'Prosjektet kunne ikke kopieres',
'Email notifications' => 'Epostvarslinger',
'Enable email notifications' => 'Aktiver eposvarslinger',
'Enable email notifications' => 'Aktiver epostvarslinger',
'Task position:' => 'Oppgaveposisjon:',
'The task #%d have been opened.' => 'Oppgaven #%d er åpnet.',
'The task #%d have been closed.' => 'Oppgaven #%d er lukket.',
@ -382,12 +382,12 @@ return array(
'Disable public access' => 'Deaktiver offentlig tilgang',
'Enable public access' => 'Aktiver offentlig tilgang',
'Public access disabled' => 'Offentlig tilgang er deaktivert',
'Do you really want to disable this project: "%s"?' => 'Vil du virkelig deaktivere prosjektet: "%s"?',
'Do you really want to enable this project: "%s"?' => 'Vil du virkelig aktivere prosjektet: "%s"?',
'Do you really want to disable this project: "%s"?' => 'Vil du deaktivere prosjektet: "%s"?',
'Do you really want to enable this project: "%s"?' => 'Vil du aktivere prosjektet: "%s"?',
'Project activation' => 'Prosjekt aktivering',
'Move the task to another project' => 'Flytt oppgaven til et annet prosjekt',
'Move to another project' => 'Flytt til et annet prosjekt',
'Do you really want to duplicate this task?' => 'Vil du virkelig kopiere denne oppgaven?',
'Do you really want to duplicate this task?' => 'Vil du kopiere denne oppgaven?',
'Duplicate a task' => 'Kopier en oppgave',
'External accounts' => 'Eksterne kontoer',
'Account type' => 'Kontotype',
@ -395,8 +395,6 @@ return array(
'Remote' => 'Fjernstyrt',
'Enabled' => 'Aktiv',
'Disabled' => 'Deaktivert',
'Google account linked' => 'Google-konto knyttet',
'Github account linked' => 'GitHub-konto knyttet',
'Username:' => 'Brukernavn',
'Name:' => 'Navn:',
'Email:' => 'Epost:',
@ -411,7 +409,7 @@ return array(
'External authentications' => 'Ekstern godkjenning',
'Google Account' => 'Google-konto',
'Github Account' => 'GitHub-konto',
'Never connected.' => 'Aldri knyttet.',
'Never connected.' => 'Aldri innlogget.',
'No account linked.' => 'Ingen kontoer knyttet.',
'Account linked.' => 'Konto knyttet.',
'No external authentication enabled.' => 'Ingen eksterne godkjenninger aktiveret.',
@ -466,7 +464,7 @@ return array(
'Database' => 'Database',
'About' => 'Om',
'Database driver:' => 'Database driver:',
'Board settings' => 'Innstillinger for ptosjektside',
'Board settings' => 'Innstillinger for prosjektside',
'URL and token' => 'URL og token',
'Webhook settings' => 'Webhook innstillinger',
'URL for task creation:' => 'URL for oppgaveopprettelse:',
@ -475,9 +473,9 @@ return array(
'Refresh interval for private board' => 'Oppdateringsintervall for privat hovedside',
'Refresh interval for public board' => 'Oppdateringsintervall for offentlig hovedside',
'Task highlight period' => 'Fremhevingsperiode for oppgave',
'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periode for å anta at en oppgave nylig ble endretg (0 for å deaktivere, 2 dager som standard)',
'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periode for ø anta at en oppgave nylig ble endretg (0 for å deaktivere, 2 dager som standard)',
'Frequency in second (60 seconds by default)' => 'Frekevens i sekunder (60 sekunder som standard)',
'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frekvens i sekunder (0 for å deaktivere denne funksjonen, 10 sekunder som standard)',
'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frekvens i sekunder (0 for øt deaktivere denne funksjonen, 10 sekunder som standard)',
'Application URL' => 'Applikasjons URL',
'Example: http://example.kanboard.net/ (used by email notifications)' => 'Eksempel: http://example.kanboard.net/ (bruges til email notifikationer)',
'Token regenerated.' => 'Token regenerert.',
@ -485,7 +483,7 @@ return array(
'ISO format is always accepted, example: "%s" and "%s"' => 'ISO format er alltid akseptert, eksempelvis: "%s" og "%s"',
'New private project' => 'Nytt privat prosjekt',
'This project is private' => 'Dette projektet er privat',
'Type here to create a new sub-task' => 'Skriv her for å opprette en ny deloppgave',
'Type here to create a new sub-task' => 'Skriv her for ø opprette en ny deloppgave',
'Add' => 'Legg til',
'Estimated time: %s hours' => 'Estimert tid: %s timer',
'Time spent: %s hours' => 'Tid brukt: %s timer',
@ -510,21 +508,21 @@ return array(
'Columns' => 'Kolonner',
'Task' => 'Oppgave',
// 'Your are not member of any project.' => '',
// 'Percentage' => '',
// 'Number of tasks' => '',
// 'Task distribution' => '',
// 'Reportings' => '',
'Percentage' => 'Prosent',
'Number of tasks' => 'Antall oppgaver',
'Task distribution' => 'Kolonnefordeling',
'Reportings' => 'Rapportering',
// 'Task repartition for "%s"' => '',
'Analytics' => 'Analyser',
// 'Subtask' => '',
'Subtask' => 'Deloppgave',
'My subtasks' => 'Mine deloppgaver',
// 'User repartition' => '',
'User repartition' => 'Brukerfordeling',
// 'User repartition for "%s"' => '',
'Clone this project' => 'Kopier dette prosjektet',
// 'Column removed successfully.' => '',
'Column removed successfully.' => 'Kolonne flyttet',
// 'Github Issue' => '',
// 'Not enough data to show the graph.' => '',
// 'Previous' => '',
'Previous' => 'Forrige',
// 'The id must be an integer' => '',
// 'The project id must be an integer' => '',
// 'The status must be an integer' => '',
@ -536,9 +534,9 @@ return array(
// 'This value is required' => '',
// 'This value must be numeric' => '',
// 'Unable to create this task.' => '',
// 'Cumulative flow diagram' => '',
'Cumulative flow diagram' => 'Kumulativt flytdiagram',
// 'Cumulative flow diagram for "%s"' => '',
// 'Daily project summary' => '',
'Daily project summary' => 'Daglig prosjektsammendrag',
// 'Daily project summary export' => '',
// 'Daily project summary export for "%s"' => '',
'Exports' => 'Eksporter',
@ -546,7 +544,7 @@ return array(
'Nothing to preview...' => 'Ingenting å forhåndsvise',
'Preview' => 'Forhåndsvisning',
'Write' => 'Skriv',
'Active swimlanes' => 'Aktive svæmmebaner',
'Active swimlanes' => 'Aktive svømmebaner',
'Add a new swimlane' => 'Legg til en ny svømmebane',
'Change default swimlane' => 'Endre standard svømmebane',
'Default swimlane' => 'Standard svømmebane',
@ -558,10 +556,10 @@ return array(
'Rename' => 'Endre navn',
'Show default swimlane' => 'Vis standard svømmebane',
// 'Swimlane modification for the project "%s"' => '',
// 'Swimlane not found.' => '',
// 'Swimlane removed successfully.' => '',
'Swimlane not found.' => 'Svømmebane ikke funnet',
'Swimlane removed successfully.' => 'Svømmebane fjernet',
'Swimlanes' => 'Svømmebaner',
'Swimlane updated successfully.' => 'Svæmmebane oppdatert',
'Swimlane updated successfully.' => 'Svømmebane oppdatert',
// 'The default swimlane have been updated successfully.' => '',
// 'Unable to create your swimlane.' => '',
// 'Unable to remove this swimlane.' => '',
@ -576,27 +574,27 @@ return array(
// 'Help on Gitlab webhooks' => '',
'Integrations' => 'Integrasjoner',
'Integration with third-party services' => 'Integrasjoner med tredje-parts tjenester',
// 'Role for this project' => '',
'Role for this project' => 'Rolle for dette prosjektet',
'Project manager' => 'Prosjektleder',
'Project member' => 'Prosjektmedlem',
'A project manager can change the settings of the project and have more privileges than a standard user.' => 'Prosjektlederen kan endre flere innstillinger for prosjektet enn den en vanlig bruker kan.',
// 'Gitlab Issue' => '',
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
'Subtask Id' => 'Deloppgave ID',
'Subtasks' => 'Deloppgaver',
'Subtasks Export' => 'Eksporter deloppgaver',
// 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
'Task Title' => 'Oppgavetittel',
// 'Untitled' => '',
'Application default' => 'Standardinstilling',
'Language:' => 'Språk',
'Timezone:' => 'Tidssone',
// 'All columns' => '',
'All columns' => 'Alle kolonner',
'Calendar' => 'Kalender',
// 'Next' => '',
'Next' => 'Neste',
// '#%d' => '',
// 'All swimlanes' => '',
// 'All colors' => '',
// 'All status' => '',
'All swimlanes' => 'Alle svømmebaner',
'All colors' => 'Alle farger',
'All status' => 'Alle statuser',
// 'Moved to column %s' => '',
'Change description' => 'Endre beskrivelse',
'User dashboard' => 'Brukerens hovedside',
@ -612,8 +610,8 @@ return array(
// 'Bitbucket commit received' => '',
// 'Bitbucket webhooks' => '',
// 'Help on Bitbucket webhooks' => '',
// 'Start' => '',
// 'End' => '',
'Start' => 'Start',
'End' => 'Slutt',
'Task age in days' => 'Dager siden oppgaven ble opprettet',
'Days in this column' => 'Dager siden oppgaven ble lagt i denne kolonnen',
// '%dd' => '',
@ -621,7 +619,7 @@ return array(
'Add a new link' => 'Legg til en ny relasjon',
// 'Do you really want to remove this link: "%s"?' => '',
// 'Do you really want to remove this link with task #%d?' => '',
// 'Field required' => '',
'Field required' => 'Feltet må fylles ut',
'Link added successfully.' => 'Ny relasjon er lagt til',
'Link updated successfully.' => 'Relasjon er oppdatert',
'Link removed successfully.' => 'Relasjon er fjernet',
@ -667,75 +665,26 @@ return array(
'Horizontal scrolling' => 'Bla horisontalt',
'Compact/wide view' => 'Kompakt/bred visning',
'No results match:' => 'Ingen resultater',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
'Hourly rates' => 'Timepriser',
'Hourly rate' => 'Timepris',
'Currency' => 'Valuta',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
'Manage timetable' => 'Tidstabell',
'Overtime timetable' => 'Overtidstabell',
'Time off timetable' => 'Fritidstabell',
'Timetable' => 'Tidstabell',
'Work timetable' => 'Arbeidstidstabell',
'Week timetable' => 'Uketidstabell',
'Day timetable' => 'Dagtidstabell',
'From' => 'Fra',
'To' => 'Til',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
'Files' => 'Filer',
'Images' => 'Bilder',
'Private project' => 'Privat prosjekt',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
'Budget' => 'Budsjett',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
'download' => 'last ned',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// 'Destination column' => '',
'Destination column' => 'Ny kolonne',
'Move the task to another column when assigned to a user' => 'Flytt oppgaven til en annen kolonne når den er tildelt en bruker',
'Move the task to another column when assignee is cleared' => 'Flytt oppgaven til en annen kolonne når ppgavetildeling fjernes ',
// 'Source column' => '',
// 'Show subtask estimates (forecast of future work)' => '',
'Source column' => 'Opprinnelig kolonne',
'Transitions' => 'Statusendringer',
// 'Executer' => '',
// 'Time spent in the column' => '',
@ -746,7 +695,6 @@ return array(
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
@ -782,7 +730,7 @@ return array(
// 'This chart show the task complexity over the time (Work Remaining).' => '',
// 'Screenshot taken %s' => '',
'Add a screenshot' => 'Legg til et skjermbilde',
'Take a screenshot and press CTRL+V or +V to paste here.' => 'Ta et skjermbilde og trykk CTRL+V for å lime det inn her.',
'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Ta et skjermbilde og trykk CTRL+V for å lime det inn her.',
'Screenshot uploaded successfully.' => 'Skjermbilde opplastet',
// 'SEK - Swedish Krona' => '',
'The project identifier is an optional alphanumeric code used to identify your project.' => 'Prosjektkoden er en alfanumerisk kode som kan brukes for å identifisere prosjektet',
@ -853,7 +801,7 @@ return array(
// 'There is no user management for private projects.' => '',
// 'User that will receive the email' => '',
// 'Email subject' => '',
// 'Date' => '',
'Date' => 'Dato',
// 'By @%s on Bitbucket' => '',
// 'Bitbucket Issue' => '',
// 'Commit made by @%s on Bitbucket' => '',
@ -877,10 +825,7 @@ return array(
'Notification' => 'Varsel',
// '%s moved the task #%d to the first swimlane' => '',
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Budget overview' => '',
// 'Type' => '',
// 'There is not enough data to show something.' => '',
'Swimlane' => 'Svømmebane',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
@ -908,19 +853,19 @@ return array(
// '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:' => '',
'Swimlane: %s' => 'Svømmebane: %s',
'I want to receive notifications for:' => 'Jeg vil motta varslinger om:',
'All tasks' => 'Alle oppgaver',
// 'Only for tasks assigned to me' => '',
// 'Only for tasks created by me' => '',
// 'Only for tasks created by me and assigned to me' => '',
'Only for tasks assigned to me' => 'Kun oppgaver som er tildelt meg',
'Only for tasks created by me' => 'Kun oppgaver som er opprettet av meg',
'Only for tasks created by me and assigned to me' => 'Kun oppgaver som er opprettet av meg og tildelt meg',
// '%A' => '',
// '%b %e, %Y, %k:%M %p' => '',
// 'New due date: %B %e, %Y' => '',
// 'Start date changed: %B %e, %Y' => '',
// '%k:%M %p' => '',
// '%%Y-%%m-%%d' => '',
// 'Total for all columns' => '',
'Total for all columns' => 'Totalt for alle kolonner',
// 'You need at least 2 days of data to show the chart.' => '',
// '<15m' => '',
// '<30m' => '',
@ -928,41 +873,41 @@ return array(
'Start timer' => 'Start timer',
'Add project member' => 'Legg til prosjektmedlem',
'Enable notifications' => 'Aktiver varslinger',
// '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' => '',
'My activity stream' => 'Aktivitetslogg',
'My calendar' => 'Min kalender',
'Search tasks' => 'Søk oppgave',
'Back to the calendar' => 'Tilbake til kalender',
'Filters' => 'Filtere',
'Reset filters' => 'Nullstill filter',
'My tasks due tomorrow' => 'Mine oppgaver med frist i morgen',
'Tasks due today' => 'Oppgaver med frist i dag',
'Tasks due tomorrow' => 'Oppgaver med frist i morgen',
'Tasks due yesterday' => 'Oppgaver med frist i går',
'Closed tasks' => 'Fullførte oppgaver',
'Open tasks' => 'Åpne oppgaver',
'Not assigned' => 'Ikke tildelt',
'View advanced search syntax' => 'Vis hjelp for avansert søk ',
'Overview' => 'Oversikt',
// '%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: ' => '',
'Board/Calendar/List view' => 'Oversikt/kalender/listevisning',
'Switch to the board view' => 'Oversiktsvisning',
'Switch to the calendar view' => 'Kalendevisning',
'Switch to the list view' => 'Listevisning',
'Go to the search/filter box' => 'Gå til søk/filter',
'There is no activity yet.' => 'Ingen aktiviteter ennå.',
'No tasks found.' => 'Ingen oppgaver funnet',
'Keyboard shortcut: "%s"' => 'Hurtigtaster: "%s"',
'List' => 'Liste',
'Filter' => 'Filter',
'Advanced search' => 'Avansert søk',
'Example of query: ' => 'Eksempel på spørring',
'Search by project: ' => 'Søk etter prosjekt',
'Search by column: ' => 'Søk etter kolonne',
'Search by assignee: ' => 'Søk etter tildelt',
'Search by color: ' => 'Søk etter farge',
'Search by category: ' => 'Søk etter kategori',
'Search by description: ' => 'Søk etter beskrivelse',
'Search by due date: ' => 'Søk etter frist',
// 'Lead and Cycle time for "%s"' => '',
// 'Average time spent into each column for "%s"' => '',
// 'Average time spent into each column' => '',
@ -996,75 +941,119 @@ return array(
// 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '',
// 'By @%s on Gitlab' => '',
// 'Gitlab issue comment created' => '',
// 'New remote user' => '',
// 'New local user' => '',
// 'Default task color' => '',
// 'Hide sidebar' => '',
// 'Expand sidebar' => '',
'New remote user' => 'Ny eksternbruker',
'New local user' => 'Ny internbruker',
'Default task color' => 'Standard oppgavefarge',
'Hide sidebar' => 'Skjul sidemeny',
'Expand sidebar' => 'Vis sidemeny',
// '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' => '',
// 'Project Administrator' => '',
// 'Enter the text below' => '',
// 'Gantt chart for %s' => '',
'Current swimlane: %s' => 'Nåværende svømmebane: %s',
'Current column: %s' => 'Nåværende kolonne: %s',
'Current category: %s' => ': %s',
'no category' => 'ingen kategori',
'Current assignee: %s' => 'Tildelt til %s',
'not assigned' => 'ikke tildelt',
'Author:' => 'Opprettet av',
'contributors' => 'bidragsytere',
'License:' => 'Lisens:',
'License' => 'Lisens',
'Project Administrator' => 'Prosjektadministrator',
'Enter the text below' => 'Legg inn teksten nedenfor',
'Gantt chart for %s' => 'Gantt skjema 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.' => '',
'Sort by date' => 'Sorter etter dato',
'Add task' => 'Legg til oppgave',
'Start date:' => 'Startdato:',
'Due date:' => 'Frist:',
'There is no start date or due date for this task.' => 'Det er ingen startdato eller frist for denne oppgaven',
// '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' => '',
'There is no task in your project.' => 'Det er ingen oppgaver i dette prosjektet',
'Gantt chart' => 'Gantt skjema',
'People who are project managers' => 'Prosjektledere',
'People who are project members' => 'Prosjektmedlemmer',
// 'NOK - Norwegian Krone' => '',
// 'Show this column' => '',
// 'Hide this column' => '',
// 'open file' => '',
// 'End date' => '',
// 'Users overview' => '',
// 'Managers' => '',
// 'Members' => '',
// 'Shared project' => '',
// 'Project managers' => '',
// 'Project members' => '',
'Show this column' => 'Vis denne kolonnen',
'Hide this column' => 'Skjul denne kolonnen',
'open file' => 'Åpne fil',
'End date' => 'Sluttdato',
'Users overview' => 'Brukeroversikt',
'Managers' => 'Ledere',
'Members' => 'Medlemmer',
'Shared project' => 'Delt prosjekt',
'Project managers' => 'Prosjektledere',
'Project members' => 'Prosjektmedlemmer',
// 'Gantt chart for all projects' => '',
// 'Projects list' => '',
// 'Gantt chart for this project' => '',
// 'Project board' => '',
// 'End date:' => '',
'Projects list' => 'Prosjektliste',
'Gantt chart for this project' => 'Gantt skjema for dette prosjektet',
'Project board' => 'Prosjektsiden',
'End date:' => 'Sluttdato:',
// 'There is no start date or end date for this project.' => '',
// 'Projects Gantt chart' => '',
// 'Start date: %s' => '',
// 'End date: %s' => '',
// 'Link type' => '',
'Projects Gantt chart' => 'Gantt skjema for prosjekter',
'Start date: %s' => 'Startdato: %s',
'End date: %s' => 'Sluttdato: %s',
'Link type' => 'Relasjonstype',
// 'Change task color when using a specific task link' => '',
// 'Task link creation or modification' => '',
// 'Login with my Gitlab Account' => '',
// 'Milestone' => '',
'Milestone' => 'Milepæl',
// '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' => '',
// 'Help with project permissions' => '',
'Documentation: %s' => 'Dokumentasjon: %s',
'Switch to the Gantt chart view' => 'Gantt skjema visning',
'Reset the search/filter box' => 'Nullstill søk/filter',
'Documentation' => 'Dokumentasjon',
'Table of contents' => 'Innholdsfortegnelse',
'Gantt' => 'Gantt',
'Help with project permissions' => 'Hjelp med prosjekttilganger',
// '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:' => '',
);

View file

@ -313,7 +313,7 @@ return array(
'Estimate:' => 'Schatting :',
'Spent:' => 'Besteed :',
'Do you really want to remove this sub-task?' => 'Weet u zeker dat u deze subtaak wil verwijderen ?',
'Remaining:' => 'Restant :',
'Remaining:' => 'Resterend :',
'hours' => 'uren',
'spent' => 'besteed',
'estimated' => 'geschat',
@ -395,8 +395,6 @@ return array(
'Remote' => 'Remote',
'Enabled' => 'Actief',
'Disabled' => 'Inactief',
'Google account linked' => 'Gelinkt Google Account',
'Github account linked' => 'Gelinkt Github Account',
'Username:' => 'Gebruikersnaam :',
'Name:' => 'Naam :',
'Email:' => 'Email :',
@ -667,75 +665,26 @@ return array(
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// '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' => '',
// 'Show subtask estimates (forecast of future work)' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
@ -746,7 +695,6 @@ return array(
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
@ -878,9 +826,6 @@ return array(
// '%s moved the task #%d to the first swimlane' => '',
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Budget overview' => '',
// 'Type' => '',
// 'There is not enough data to show something.' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
@ -1067,4 +1012,48 @@ return array(
// 'Table of contents' => '',
// 'Gantt' => '',
// 'Help with project permissions' => '',
// '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:' => '',
);

View file

@ -395,8 +395,6 @@ return array(
'Remote' => 'Zdalne',
'Enabled' => 'Odblokowane',
'Disabled' => 'Zablokowane',
'Google account linked' => 'Połączone konto Google',
'Github account linked' => 'Połączone konto Github',
'Username:' => 'Nazwa Użytkownika:',
'Name:' => 'Imię i Nazwisko',
'Email:' => 'Email: ',
@ -667,75 +665,26 @@ return array(
'Horizontal scrolling' => 'Przewijanie poziome',
'Compact/wide view' => 'Pełny/Kompaktowy widok',
'No results match:' => 'Brak wyników:',
'Remove hourly rate' => 'Usuń stawkę godzinową',
'Do you really want to remove this hourly rate?' => 'Czy na pewno chcesz usunąć stawkę godzinową?',
'Hourly rates' => 'Stawki godzinowe',
'Hourly rate' => 'Stawka godzinowa',
'Currency' => 'Waluta',
'Effective date' => 'Data efektywna',
'Add new rate' => 'Dodaj nową stawkę',
'Rate removed successfully.' => 'Stawka usunięta.',
'Unable to remove this rate.' => 'Nie można usunąć tej stawki.',
'Unable to save the hourly rate.' => 'Nie można zapisać tej stawki godzinowej.',
'Hourly rate created successfully.' => 'Stawka godzinowa utworzona pomyślnie.',
'Start time' => 'Rozpoczęto',
'End time' => 'Zakończono',
'Comment' => 'Komentarz',
'All day' => 'Cały dzień',
'Day' => 'Dzień',
'Manage timetable' => 'Zarządzaj rozkładami zajęć',
'Overtime timetable' => 'Rozkład zajęć - nadgodziny',
'Time off timetable' => 'Rozkład zajęć - czas wolny',
'Timetable' => 'Rozkład zajęć',
'Work timetable' => 'Rozkład zajęć - praca',
'Week timetable' => 'Tygodniowy rozkład zajęć',
'Day timetable' => 'Dzienny rozkład zajęć',
'From' => 'Od',
'To' => 'Do',
'Time slot created successfully.' => 'Przydział czasowy utworzony.',
'Unable to save this time slot.' => 'Nie można zapisać tego przydziału czasowego.',
'Time slot removed successfully.' => 'Przydział czasowy usunięty.',
'Unable to remove this time slot.' => 'Nie można usunąć tego przydziału czasowego.',
'Do you really want to remove this time slot?' => 'Czy na pewno chcesz usunąć ten przedział czasowy?',
'Remove time slot' => 'Usuń przedział czasowy',
'Add new time slot' => 'Dodaj przedział czasowy',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Ten rozkład zajęć jest używany przypadku zaznaczenia "cały dzień" dla zaplanowanego czasu wolnego i nadgodzin',
'Files' => 'Pliki',
'Images' => 'Obrazy',
'Private project' => 'Projekt prywatny',
'Amount' => 'Ilość',
'AUD - Australian Dollar' => 'AUD - Dolar australijski',
'Budget' => 'Budżet',
'Budget line' => 'Linia budżetowa',
'Budget line removed successfully.' => 'Linia budżetowa usunięta.',
'Budget lines' => 'Linie budżetowe',
'CAD - Canadian Dollar' => 'CAD - Dolar kanadyjski',
'CHF - Swiss Francs' => 'CHF - Frank szwajcarski',
'Cost' => 'Koszt',
'Cost breakdown' => 'Analiza kosztów',
'Custom Stylesheet' => 'Niestandardowy arkusz stylów',
'download' => 'pobierz',
'Do you really want to remove this budget line?' => 'Czy chcesz usunąć tą linię budżetową?',
// 'EUR - Euro' => '',
'Expenses' => 'Wydatki',
'GBP - British Pound' => 'GBP - Funt brytyjski',
'INR - Indian Rupee' => 'INR - Rupia indyjska',
'JPY - Japanese Yen' => 'JPY - Jen japoński',
'New budget line' => 'Nowa linia budżetowa',
'NZD - New Zealand Dollar' => 'NZD - Dolar nowozelandzki',
'Remove a budget line' => 'Usuń linię budżetową',
'Remove budget line' => 'Usuń linię budżetową',
'RSD - Serbian dinar' => 'RSD - Dinar serbski',
// 'The budget line have been created successfully.' => '',
'Unable to create the budget line.' => 'Nie można utworzyć linii budżetowej',
'Unable to remove this budget line.' => 'Nie można usunąć tej linii budżetowej',
'USD - US Dollar' => 'USD - Dolar amerykański',
'Remaining' => 'Pozostało',
'Destination column' => 'Kolumna docelowa',
'Move the task to another column when assigned to a user' => 'Przenieś zadanie do innej kolumny gdy zostanie przypisane do osoby',
'Move the task to another column when assignee is cleared' => 'Przenieś zadanie do innej kolumny gdy osoba odpowiedzialna zostanie usunięta',
'Source column' => 'Kolumna źródłowa',
'Show subtask estimates (forecast of future work)' => 'Pokaż planowane czasy wykonania pod-zadań',
'Transitions' => 'Przeniesienia',
'Executer' => 'Wykonał',
'Time spent in the column' => 'Czas spędzony w tej kolumnie',
@ -746,7 +695,6 @@ return array(
'Rate' => 'Kurs',
'Change reference currency' => 'Zmień walutę referencyjną',
'Add a new currency rate' => 'Dodaj nowy kurs waluty',
'Currency rates are used to calculate project budget.' => 'Kursy walut są używane do obliczeń budżetu projektu.',
'Reference currency' => 'Waluta referencyjna',
'The currency rate have been added successfully.' => 'Dodano kurs waluty',
'Unable to add this currency rate.' => 'Nie można dodać kursu waluty',
@ -878,9 +826,6 @@ return array(
// '%s moved the task #%d to the first swimlane' => '',
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Budget overview' => '',
// 'Type' => '',
// 'There is not enough data to show something.' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
@ -1067,4 +1012,48 @@ return array(
// 'Table of contents' => '',
// 'Gantt' => '',
// 'Help with project permissions' => '',
// '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:' => '',
);

View file

@ -395,8 +395,6 @@ return array(
'Remote' => 'Remoto',
'Enabled' => 'Habilitado',
'Disabled' => 'Desabilitado',
'Google account linked' => 'Conta do Google associada',
'Github account linked' => 'Conta do Github associada',
'Username:' => 'Usuário:',
'Name:' => 'Nome:',
'Email:' => 'E-mail:',
@ -667,75 +665,26 @@ return array(
'Horizontal scrolling' => 'Rolagem horizontal',
'Compact/wide view' => 'Alternar entre a vista compacta e ampliada',
'No results match:' => 'Nenhum resultado:',
'Remove hourly rate' => 'Retirar taxa horária',
'Do you really want to remove this hourly rate?' => 'Você deseja realmente remover esta taxa horária?',
'Hourly rates' => 'Taxas horárias',
'Hourly rate' => 'Taxa horária',
'Currency' => 'Moeda',
'Effective date' => 'Data efetiva',
'Add new rate' => 'Adicionar nova taxa',
'Rate removed successfully.' => 'Taxa removido com sucesso.',
'Unable to remove this rate.' => 'Impossível de remover esta taxa.',
'Unable to save the hourly rate.' => 'Impossível salvar a taxa horária.',
'Hourly rate created successfully.' => 'Taxa horária criada com sucesso.',
'Start time' => 'Horário de início',
'End time' => 'Horário de término',
'Comment' => 'comentário',
'All day' => 'Dia inteiro',
'Day' => 'Dia',
'Manage timetable' => 'Gestão dos horários',
'Overtime timetable' => 'Horas extras',
'Time off timetable' => 'Horas de ausência',
'Timetable' => 'Horários',
'Work timetable' => 'Horas trabalhadas',
'Week timetable' => 'Horário da semana',
'Day timetable' => 'Horário de un dia',
'From' => 'Desde',
'To' => 'A',
'Time slot created successfully.' => 'Intervalo de tempo criado com sucesso.',
'Unable to save this time slot.' => 'Impossível de guardar este intervalo de tempo.',
'Time slot removed successfully.' => 'Intervalo de tempo removido com sucesso.',
'Unable to remove this time slot.' => 'Impossível de remover esse intervalo de tempo.',
'Do you really want to remove this time slot?' => 'Você deseja realmente remover este intervalo de tempo?',
'Remove time slot' => 'Remover um intervalo de tempo',
'Add new time slot' => 'Adicionar um intervalo de tempo',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Esses horários são usados quando a caixa de seleção "Dia inteiro" está marcada para Horas de ausência ou Extras',
'Files' => 'Arquivos',
'Images' => 'Imagens',
'Private project' => 'Projeto privado',
'Amount' => 'Quantia',
'AUD - Australian Dollar' => 'AUD - Dólar australiano',
'Budget' => 'Orçamento',
'Budget line' => 'Rubrica orçamental',
'Budget line removed successfully.' => 'Rubrica orçamental removida com sucesso',
'Budget lines' => 'Rubricas orçamentais',
'CAD - Canadian Dollar' => 'CAD - Dólar canadense',
'CHF - Swiss Francs' => 'CHF - Francos Suíços',
'Cost' => 'Custo',
'Cost breakdown' => 'Repartição dos custos',
'Custom Stylesheet' => 'Folha de estilo personalizado',
'download' => 'baixar',
'Do you really want to remove this budget line?' => 'Você deseja realmente remover esta rubrica orçamental?',
'EUR - Euro' => 'EUR - Euro',
'Expenses' => 'Despesas',
'GBP - British Pound' => 'GBP - Libra Esterlina',
'INR - Indian Rupee' => 'INR - Rúpia indiana',
'JPY - Japanese Yen' => 'JPY - Iene japonês',
'New budget line' => 'Nova rubrica orçamental',
'NZD - New Zealand Dollar' => 'NZD - Dólar Neozelandês',
'Remove a budget line' => 'Remover uma rubrica orçamental',
'Remove budget line' => 'Remover uma rubrica orçamental',
'RSD - Serbian dinar' => 'RSD - Dinar sérvio',
'The budget line have been created successfully.' => 'A rubrica orçamental foi criada com sucesso.',
'Unable to create the budget line.' => 'Impossível de adicionar esta rubrica orçamental.',
'Unable to remove this budget line.' => 'Impossível de remover esta rubrica orçamental.',
'USD - US Dollar' => 'USD - Dólar norte-americano',
'Remaining' => 'Restante',
'Destination column' => 'Coluna de destino',
'Move the task to another column when assigned to a user' => 'Mover a tarefa para uma outra coluna quando esta está atribuída a um usuário',
'Move the task to another column when assignee is cleared' => 'Mover a tarefa para uma outra coluna quando esta não está atribuída',
'Source column' => 'Coluna de origem',
'Show subtask estimates (forecast of future work)' => 'Mostrar a estimativa das subtarefas (previsão para o trabalho futuro)',
'Transitions' => 'Transições',
'Executer' => 'Executor(a)',
'Time spent in the column' => 'Tempo gasto na coluna',
@ -746,7 +695,6 @@ return array(
'Rate' => 'Taxa',
'Change reference currency' => 'Mudar a moeda de referência',
'Add a new currency rate' => 'Adicionar uma nova taxa para uma moeda',
'Currency rates are used to calculate project budget.' => 'As taxas de câmbio são utilizadas para calcular o orçamento do projeto.',
'Reference currency' => 'Moeda de Referência',
'The currency rate have been added successfully.' => 'A taxa de câmbio foi adicionada com sucesso.',
'Unable to add this currency rate.' => 'Impossível de adicionar essa taxa de câmbio.',
@ -878,9 +826,6 @@ return array(
'%s moved the task #%d to the first swimlane' => '%s moveu a tarefa n° %d no primeiro swimlane',
'%s moved the task #%d to the swimlane "%s"' => '%s moveu a tarefa n° %d no swimlane "%s"',
'Swimlane' => 'Swimlane',
'Budget overview' => 'Visão geral do orçamento',
'Type' => 'Tipo',
'There is not enough data to show something.' => 'Não há dados suficientes para mostrar alguma coisa.',
'Gravatar' => 'Gravatar',
'Hipchat' => 'Hipchat',
'Slack' => 'Slack',
@ -1060,11 +1005,55 @@ return array(
'Gitlab Account' => 'Conta Gitlab',
'Link my Gitlab Account' => 'Vincular minha conta Gitlab',
'Unlink my Gitlab Account' => 'Desvincular minha conta Gitlab',
// 'Documentation: %s' => '',
// 'Switch to the Gantt chart view' => '',
// 'Reset the search/filter box' => '',
// 'Documentation' => '',
// 'Table of contents' => '',
// 'Gantt' => '',
// 'Help with project permissions' => '',
'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',
'Documentation' => 'Documentação',
'Table of contents' => 'Índice',
'Gantt' => 'Gantt',
'Help with project permissions' => 'Ajuda com as permissões dos projetos',
'Author' => 'Autor',
'Version' => 'Versão',
'Plugins' => 'Extensão',
'There is no plugin loaded.' => 'Não há nenhuma extensão carga.',
'Set maximum column height' => 'Definir a altura máxima das colunas',
'Remove maximum column height' => 'Retirar a altura máxima das colunas',
// '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:' => '',
);

View file

@ -395,8 +395,6 @@ return array(
'Remote' => 'Remoto',
'Enabled' => 'Activado',
'Disabled' => 'Desactivado',
'Google account linked' => 'Conta do Google associada',
'Github account linked' => 'Conta do Github associada',
'Username:' => 'Utilizador:',
'Name:' => 'Nome:',
'Email:' => 'E-mail:',
@ -667,75 +665,26 @@ return array(
'Horizontal scrolling' => 'Deslocamento horizontal',
'Compact/wide view' => 'Alternar entre a vista compacta e ampliada',
'No results match:' => 'Nenhum resultado:',
'Remove hourly rate' => 'Retirar taxa horária',
'Do you really want to remove this hourly rate?' => 'Tem a certeza que quer remover esta taxa horária?',
'Hourly rates' => 'Taxas horárias',
'Hourly rate' => 'Taxa horária',
'Currency' => 'Moeda',
'Effective date' => 'Data efectiva',
'Add new rate' => 'Adicionar nova taxa',
'Rate removed successfully.' => 'Taxa removido com sucesso.',
'Unable to remove this rate.' => 'Impossível de remover esta taxa.',
'Unable to save the hourly rate.' => 'Impossível salvar a taxa horária.',
'Hourly rate created successfully.' => 'Taxa horária criada com sucesso.',
'Start time' => 'Horário de início',
'End time' => 'Horário de término',
'Comment' => 'comentário',
'All day' => 'Dia inteiro',
'Day' => 'Dia',
'Manage timetable' => 'Gestão dos horários',
'Overtime timetable' => 'Horas extras',
'Time off timetable' => 'Horas de ausência',
'Timetable' => 'Horários',
'Work timetable' => 'Horas trabalhadas',
'Week timetable' => 'Horário da semana',
'Day timetable' => 'Horário de um dia',
'From' => 'Desde',
'To' => 'A',
'Time slot created successfully.' => 'Intervalo de tempo criado com sucesso.',
'Unable to save this time slot.' => 'Impossível guardar este intervalo de tempo.',
'Time slot removed successfully.' => 'Intervalo de tempo removido com sucesso.',
'Unable to remove this time slot.' => 'Impossível remover esse intervalo de tempo.',
'Do you really want to remove this time slot?' => 'Tem a certeza que quer remover este intervalo de tempo?',
'Remove time slot' => 'Remover um intervalo de tempo',
'Add new time slot' => 'Adicionar um intervalo de tempo',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Esses horários são usados quando a caixa de seleção "Dia inteiro" está marcada para Horas de ausência ou Extras',
'Files' => 'Arquivos',
'Images' => 'Imagens',
'Private project' => 'Projecto privado',
'Amount' => 'Quantia',
'AUD - Australian Dollar' => 'AUD - Dólar australiano',
'Budget' => 'Orçamento',
'Budget line' => 'Rubrica orçamental',
'Budget line removed successfully.' => 'Rubrica orçamental removida com sucesso',
'Budget lines' => 'Rubricas orçamentais',
'CAD - Canadian Dollar' => 'CAD - Dólar canadense',
'CHF - Swiss Francs' => 'CHF - Francos Suíços',
'Cost' => 'Custo',
'Cost breakdown' => 'Repartição dos custos',
'Custom Stylesheet' => 'Folha de estilos personalizada',
'download' => 'transferir',
'Do you really want to remove this budget line?' => 'Tem a certeza que quer remover esta rubrica orçamental?',
'EUR - Euro' => 'EUR - Euro',
'Expenses' => 'Despesas',
'GBP - British Pound' => 'GBP - Libra Esterlina',
'INR - Indian Rupee' => 'INR - Rúpia indiana',
'JPY - Japanese Yen' => 'JPY - Iene japonês',
'New budget line' => 'Nova rubrica orçamental',
'NZD - New Zealand Dollar' => 'NZD - Dólar Neozelandês',
'Remove a budget line' => 'Remover uma rubrica orçamental',
'Remove budget line' => 'Remover uma rubrica orçamental',
'RSD - Serbian dinar' => 'RSD - Dinar sérvio',
'The budget line have been created successfully.' => 'A rubrica orçamental foi criada com sucesso.',
'Unable to create the budget line.' => 'Impossível adicionar esta rubrica orçamental.',
'Unable to remove this budget line.' => 'Impossível remover esta rubrica orçamental.',
'USD - US Dollar' => 'USD - Dólar norte-americano',
'Remaining' => 'Restante',
'Destination column' => 'Coluna de destino',
'Move the task to another column when assigned to a user' => 'Mover a tarefa para uma outra coluna quando esta está atribuída a um utilizador',
'Move the task to another column when assignee is cleared' => 'Mover a tarefa para uma outra coluna quando esta não está atribuída',
'Source column' => 'Coluna de origem',
'Show subtask estimates (forecast of future work)' => 'Mostrar a estimativa das subtarefas (previsão para o trabalho futuro)',
'Transitions' => 'Transições',
'Executer' => 'Executor(a)',
'Time spent in the column' => 'Tempo gasto na coluna',
@ -746,7 +695,6 @@ return array(
'Rate' => 'Taxa',
'Change reference currency' => 'Mudar a moeda de referência',
'Add a new currency rate' => 'Adicionar uma nova taxa para uma moeda',
'Currency rates are used to calculate project budget.' => 'As taxas de câmbio são utilizadas para calcular o orçamento do projecto.',
'Reference currency' => 'Moeda de Referência',
'The currency rate have been added successfully.' => 'A taxa de câmbio foi adicionada com sucesso.',
'Unable to add this currency rate.' => 'Impossível adicionar essa taxa de câmbio.',
@ -878,9 +826,6 @@ return array(
'%s moved the task #%d to the first swimlane' => '%s moveu a tarefa n° %d no primeiro swimlane',
'%s moved the task #%d to the swimlane "%s"' => '%s moveu a tarefa n° %d no swimlane "%s"',
'Swimlane' => 'Swimlane',
'Budget overview' => 'Visão geral do orçamento',
'Type' => 'Tipo',
'There is not enough data to show something.' => 'Não há dados suficientes para mostrar alguma coisa.',
'Gravatar' => 'Gravatar',
'Hipchat' => 'Hipchat',
'Slack' => 'Slack',
@ -1060,11 +1005,55 @@ return array(
'Gitlab Account' => 'Conta Gitlab',
'Link my Gitlab Account' => 'Connectar a minha Conta Gitlab',
'Unlink my Gitlab Account' => 'Desconectar a minha Conta Gitlab',
// 'Documentation: %s' => '',
// 'Switch to the Gantt chart view' => '',
// 'Reset the search/filter box' => '',
// 'Documentation' => '',
// 'Table of contents' => '',
// 'Gantt' => '',
// 'Help with project permissions' => '',
'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',
'Documentation' => 'Documentação',
'Table of contents' => 'Tabela de conteúdos',
'Gantt' => 'Gantt',
'Help with project permissions' => 'Ajuda com permissões de projecto',
'Author' => 'Autor',
'Version' => 'Versão',
'Plugins' => 'Extras',
'There is no plugin loaded.' => 'Não existem extras carregados',
'Set maximum column height' => 'Definir altura máxima da coluna',
'Remove maximum column height' => 'Remover altura máxima da coluna',
'My notifications' => 'As minhas notificações',
'Custom filters' => 'Filtros personalizados',
'Your custom filter have been created successfully.' => 'O seu filtro personalizado foi criado com sucesso.',
'Unable to create your custom filter.' => 'Não foi possivel criar o seu filtro personalizado.',
'Custom filter removed successfully.' => 'Filtro personalizado removido com sucesso.',
'Unable to remove this custom filter.' => 'Não foi possivel remover este filtro personalizado.',
'Edit custom filter' => 'Editar filtro personalizado',
'Your custom filter have been updated successfully.' => 'O seu filtro personalizado foi actualizado com sucesso.',
'Unable to update custom filter.' => 'Não foi possivel actualizar o filtro personalizado.',
'Web' => 'Web',
'New attachment on task #%d: %s' => 'Novo anexo na tarefa #%d: %s',
'New comment on task #%d' => 'Novo comentário na tarefa #%d',
'Comment updated on task #%d' => 'Comentário actualizado na tarefa #%d',
'New subtask on task #%d' => 'Nova sub-tarefa na tarefa #%d',
'Subtask updated on task #%d' => 'Sub-tarefa actualizada na tarefa #%d',
'New task #%d: %s' => 'Nova tarefa #%d: %s',
'Task updated #%d' => 'Tarefa actualizada #%d',
'Task #%d closed' => 'Tarefa #%d fechada',
'Task #%d opened' => 'Tarefa #%d aberta',
'Column changed for task #%d' => 'Coluna alterada para tarefa #%d',
'New position for task #%d' => 'Nova posição para tarefa #%d',
'Swimlane changed for task #%d' => 'Swimlane alterado na tarefa #%d',
'Assignee changed on task #%d' => 'Assignado alterado na tarefa #%d',
'%d overdue tasks' => '%d tarefas em atraso',
'Task #%d is overdue' => 'Tarefa #%d está em atraso',
'No new notifications.' => 'Sem novas notificações.',
'Mark all as read' => 'Marcar tudo como lido',
'Mark as read' => 'Marcar como lido',
'Total number of tasks in this column across all swimlanes' => 'Número total de tarefas nesta coluna em todas as swimlanes',
'Collapse swimlane' => 'Colapsar swimlane',
'Expand swimlane' => 'Expandir swimlane',
'Add a new filter' => 'Adicionar um novo filtro',
'Share with all project members' => 'Partilhar com todos os membros do projecto',
'Shared' => 'Partilhado',
'Owner' => 'Dono',
'Unread notifications' => 'Notificações por ler',
'My filters' => 'Os meus filtros',
'Notification methods:' => 'Metodos de notificação:',
);

View file

@ -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:' => 'Официальный сайт:',
@ -61,27 +61,27 @@ return array(
'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' => 'Активировать',
'%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 » ?',
'Do you really want to remove this project: "%s"?' => 'Вы точно хотите удалить проект: "%s"?',
'Remove project' => 'Удалить проект',
'Edit the board for "%s"' => 'Изменить доску для « %s »',
'Edit the board for "%s"' => 'Изменить доску для "%s"',
'All projects' => 'Все проекты',
'Change columns' => 'Изменить колонки',
'Add a new column' => 'Добавить новую колонку',
'Title' => 'Название',
'Nobody assigned' => 'Никто не назначен',
'Assigned to %s' => 'Исполнитель: %s',
'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!' => 'Вы УДАЛИТЕ ВСЕ ЗАДАЧИ находящиеся в этой колонке !',
'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' => 'Язык',
@ -100,32 +100,32 @@ return array(
'Create another task' => 'Создать другую задачу',
'New task' => 'Новая задача',
'Open a task' => 'Открыть задачу',
'Do you really want to open this task: "%s"?' => 'Вы уверены что хотите открыть задачу: « %s » ?',
'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',
'Created on %B %e, %Y at %k:%M %p' => 'Создано %B /%e /%Y в %k:%M %p',
'There is nobody assigned' => 'Никто не назначен',
'Column on the board:' => 'Колонка на доске : ',
'Column on the board:' => 'Колонка на доске: ',
'Status is open' => 'Статус - открыт',
'Status is closed' => 'Статус - закрыт',
'Close this task' => 'Закрыть эту задачу',
'Open this task' => 'Открыть эту задачу',
'Close this task' => 'Закрыть задачу',
'Open this task' => 'Открыть задачу',
'There is no description.' => 'Нет описания.',
'Add a new task' => 'Добавить новую задачу',
'The username is required' => 'Требуется имя пользователя',
'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' => 'Требуется ID пользователя',
'The password is required' => 'Необходим пароль',
'This value must be an integer' => 'Это значение должно быть целым числом',
'The username must be unique' => 'Имя пользователя должно быть уникально',
'The user id is required' => 'Необходим ID пользователя',
'Passwords don\'t match' => 'Пароли не совпадают',
'The confirmation is required' => 'Требуется подтверждение',
'The project is required' => 'Требуется проект',
'The id is required' => 'Требуется ID',
'The project id is required' => 'Требуется ID проекта',
'The project name is required' => 'Требуется имя проекта',
'The confirmation is required' => 'Необходимо подтверждение',
'The project is required' => 'Необъодимо указать проект',
'The id is required' => 'Необходим ID',
'The project id is required' => 'Необходим ID проекта',
'The project name is required' => 'Необходимо имя проекта',
'This project must be unique' => 'Проект должен быть уникальным',
'The title is required' => 'Требуется заголовок',
'The title is required' => 'Необходим заголовок',
'Settings saved successfully.' => 'Параметры успешно сохранены.',
'Unable to save your settings.' => 'Невозможно сохранить параметры.',
'Database optimization done.' => 'База данных оптимизирована.',
@ -157,7 +157,7 @@ return array(
'Ready' => 'Готовые',
'Backlog' => 'Ожидающие',
'Work in progress' => 'В процессе',
'Done' => 'Выполнена',
'Done' => 'Выполнено',
'Application 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',
@ -193,9 +193,9 @@ return array(
'Edit this task' => 'Изменить задачу',
'Due Date' => 'Сделать до',
'Invalid date' => 'Неверная дата',
'Must be done before %B %e, %Y' => 'Должно быть сделано до %d/%m/%Y',
'%B %e, %Y' => '%d/%m/%Y',
// '%b %e, %Y' => '',
'Must be done before %B %e, %Y' => 'Должно быть сделано до %B %e %Y',
'%B %e, %Y' => '%B, %e, %Y',
'%b %e, %Y' => '%b %e, %Y',
'Automatic actions' => 'Автоматические действия',
'Your automatic action have been created successfully.' => 'Автоматика успешно настроена.',
'Unable to create your automatic action.' => 'Не удалось создать автоматизированное действие.',
@ -263,10 +263,10 @@ return array(
'%d comments' => '%d комментариев',
'%d comment' => '%d комментарий',
'Email address invalid' => 'Некорректный e-mail адрес',
// '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' => 'E-mail',
'Link my Google Account' => 'Привязать мой профиль к Google',
'Unlink my Google Account' => 'Отвязать мой профиль от Google',
@ -310,7 +310,7 @@ return array(
'Edit a comment' => 'Изменить комментарий',
'Summary' => 'Сводка',
'Time tracking' => 'Отслеживание времени',
'Estimate:' => 'Приблизительно:',
'Estimate:' => 'Запланировано:',
'Spent:' => 'Затрачено:',
'Do you really want to remove this sub-task?' => 'Вы точно хотите удалить подзадачу?',
'Remaining:' => 'Осталось:',
@ -319,7 +319,7 @@ return array(
'estimated' => 'расчетное',
'Sub-Tasks' => 'Подзадачи',
'Add a sub-task' => 'Добавить подзадачу',
'Original estimate' => 'Первичная оценка',
'Original estimate' => 'Запланировано',
'Create another sub-task' => 'Создать другую подзадачу',
'Time spent' => 'Времени затрачено',
'Edit a sub-task' => 'Изменить подзадачу',
@ -395,8 +395,6 @@ return array(
'Remote' => 'Удаленный',
'Enabled' => 'Включен',
'Disabled' => 'Выключены',
'Google account linked' => 'Профиль Google связан',
'Github account linked' => 'Профиль Github связан',
'Username:' => 'Имя пользователя:',
'Name:' => 'Имя:',
'Email:' => 'E-mail:',
@ -487,11 +485,11 @@ return array(
'This project is private' => 'Это проект с ограниченным доступом',
'Type here to create a new sub-task' => 'Печатайте сюда чтобы создать подзадачу',
'Add' => 'Добавить',
'Estimated time: %s hours' => 'Планируемое время: %s часов',
'Estimated time: %s hours' => 'Запланировано: %s часов',
'Time spent: %s hours' => 'Потрачено времени: %s часов',
'Started on %B %e, %Y' => 'Начато %B %e, %Y',
'Start date' => 'Дата начала',
'Time estimated' => 'Планируемое время',
'Time estimated' => 'Запланировано',
'There is nothing assigned to you.' => 'Вам ничего не назначено',
'My tasks' => 'Мои задачи',
'Activity stream' => 'Текущая активность',
@ -522,7 +520,7 @@ return array(
'User repartition for "%s"' => 'Перераспределение пользователей для "%s"',
'Clone this project' => 'Клонировать проект',
'Column removed successfully.' => 'Колонка успешно удалена.',
// 'Github Issue' => '',
'Github Issue' => 'Вопрос на Github',
'Not enough data to show the graph.' => 'Недостаточно данных, чтобы показать график.',
'Previous' => 'Предыдущий',
'The id must be an integer' => 'Этот id должен быть целочисленным',
@ -593,7 +591,7 @@ return array(
'All columns' => 'Все колонки',
'Calendar' => 'Календарь',
'Next' => 'Следующий',
// '#%d' => '',
'#%d' => '#%d',
'All swimlanes' => 'Все дорожки',
'All colors' => 'Все цвета',
'All status' => 'Все статусы',
@ -608,7 +606,7 @@ return array(
'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' => 'Запретить форму входа',
// 'Bitbucket commit received' => '',
'Bitbucket webhooks' => 'BitBucket webhooks',
'Help on Bitbucket webhooks' => 'Помощь по BitBucket webhooks',
@ -616,7 +614,7 @@ return array(
'End' => 'Конец',
'Task age in days' => 'Возраст задачи в днях',
'Days in this column' => 'Дней в этой колонке',
// '%dd' => '',
'%dd' => '%dd',
'Add a link' => 'Добавить ссылку на другие задачи',
'Add a new link' => 'Добавление новой ссылки',
'Do you really want to remove this link: "%s"?' => 'Вы уверены что хотите удалить ссылку: "%s"?',
@ -638,21 +636,21 @@ return array(
'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' => 'исправлено в',
'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' => 'исправляет',
'This task' => 'Эта задача',
'<1h' => '<1ч',
// '%dh' => '',
// '%b %e' => '',
'%dh' => '%dh',
'%b %e' => '%b %e',
'Expand tasks' => 'Развернуть задачи',
'Collapse tasks' => 'Свернуть задачи',
'Expand/collapse tasks' => 'Развернуть/свернуть задачи',
@ -667,75 +665,26 @@ return array(
'Horizontal scrolling' => 'Широкий вид',
'Compact/wide view' => 'Компактный/широкий вид',
'No results match:' => 'Отсутствуют результаты:',
'Remove hourly rate' => 'Удалить почасовую ставку',
'Do you really want to remove this hourly rate?' => 'Вы действительно хотите удалить эту почасовую ставку?',
'Hourly rates' => 'Почасовые ставки',
'Hourly rate' => 'Почасовая ставка',
'Currency' => 'Валюта',
'Effective date' => 'Дата вступления в силу',
'Add new rate' => 'Добавить новый показатель',
'Rate removed successfully.' => 'Показатель успешно удален.',
'Unable to remove this rate.' => 'Не удается удалить этот показатель.',
'Unable to save the hourly rate.' => 'Не удается сохранить почасовую ставку.',
'Hourly rate created successfully.' => 'Почасовая ставка успешно создана.',
'Start time' => 'Время начала',
'End time' => 'Время завершения',
'Comment' => 'Комментарий',
'All day' => 'Весь день',
'Day' => 'День',
'Manage timetable' => 'Управление графиками',
'Overtime timetable' => 'График сверхурочных',
'Time off timetable' => 'Время в графике',
'Timetable' => 'График',
'Work timetable' => 'Work timetable',
'Week timetable' => 'График на неделю',
'Day timetable' => 'График на день',
'From' => 'От кого',
'To' => 'Кому',
'Time slot created successfully.' => 'Временной интервал успешно создан.',
'Unable to save this time slot.' => 'Невозможно сохранить этот временной интервал.',
'Time slot removed successfully.' => 'Временной интервал успешно удален.',
'Unable to remove this time slot.' => 'Не удается удалить этот временной интервал.',
'Do you really want to remove this time slot?' => 'Вы действительно хотите удалить этот период времени?',
'Remove time slot' => 'Удалить новый интервал времени',
'Add new time slot' => 'Добавить новый интервал времени',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
'Files' => 'Файлы',
'Images' => 'Изображения',
'Private project' => 'Приватный проект',
'Amount' => 'Количество',
'AUD - Australian Dollar' => 'AUD - Австралийский доллар',
'Budget' => 'Бюджет',
'Budget line' => 'Статья бюджета',
'Budget line removed successfully.' => 'Бюджетная статья успешно удалена.',
'Budget lines' => 'Статьи бюджета',
'CAD - Canadian Dollar' => 'CAD - Канадский доллар',
'CHF - Swiss Francs' => 'CHF - Швейцарский Франк',
'Cost' => 'Стоимость',
'Cost breakdown' => 'Детализация затрат',
'Custom Stylesheet' => 'Пользовательский стиль',
'download' => 'загрузить',
'Do you really want to remove this budget line?' => 'Вы действительно хотите удалить эту статью бюджета?',
'EUR - Euro' => 'EUR - Евро',
'Expenses' => 'Расходы',
'GBP - British Pound' => 'GBP - Британский фунт',
'INR - Indian Rupee' => 'INR - Индийский рупий',
'JPY - Japanese Yen' => 'JPY - Японскай йена',
'New budget line' => 'Новая статья бюджета',
'NZD - New Zealand Dollar' => 'NZD - Новозеландский доллар',
'Remove a budget line' => 'Удалить строку в бюджете',
'Remove budget line' => 'Удалить статью бюджета',
'RSD - Serbian dinar' => 'RSD - Сербский динар',
'The budget line have been created successfully.' => 'Статья бюджета успешно создана.',
'Unable to create the budget line.' => 'Не удается создать эту статью бюджета.',
'Unable to remove this budget line.' => 'Не удается удалить эту статью бюджета.',
'USD - US Dollar' => 'USD - доллар США',
'Remaining' => 'Прочее',
'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' => 'Исходная колонка',
'Show subtask estimates (forecast of future work)' => 'Показать оценку подзадач (прогноз будущей работы)',
'Transitions' => 'Перемещения',
'Executer' => 'Исполнитель',
'Time spent in the column' => 'Время проведенное в колонке',
@ -745,8 +694,7 @@ return array(
'Currency rates' => 'Курсы валют',
'Rate' => 'Курс',
'Change reference currency' => 'Изменить справочник валют',
'Add a new currency rate' => 'Add a new currency rate',
'Currency rates are used to calculate project budget.' => 'Курсы валют используются для расчета бюджета проекта.',
'Add a new currency rate' => 'Добавить новый валютный курс',
'Reference currency' => 'Справочник валют',
'The currency rate have been added successfully.' => 'Курс валюты был успешно добавлен.',
'Unable to add this currency rate.' => 'Невозможно добавить этот курс валюты.',
@ -774,8 +722,8 @@ return array(
'Test your device' => 'Проверьте свое устройство',
'Assign a color when the task is moved to a specific column' => 'Назначить цвет, когда задача перемещается в определенную колонку',
'%s via Kanboard' => '%s через Канборд',
// 'uploaded by: %s' => '',
// 'uploaded on: %s' => '',
'uploaded by: %s' => 'загружено: %s',
'uploaded on: %s' => 'загружено в: %s',
'size: %s' => 'размер: %s',
'Burndown chart for "%s"' => 'Диаграмма сгорания для « %s »',
'Burndown chart' => 'Диаграмма сгорания',
@ -805,7 +753,7 @@ return array(
'The identifier must be unique' => 'Идентификатор должен быть уникальным',
'This linked task id doesn\'t exists' => 'Этот ID звязанной задачи не существует',
'This value must be alphanumeric' => 'Это значение должно быть буквенно-цифровым',
'Edit recurrence' => 'Завершить повторение',
'Edit recurrence' => 'Редактировать повторы',
'Generate recurrent task' => 'Создать повторяющуюся задачу',
'Trigger to generate recurrent task' => 'Триггер для генерации периодической задачи',
'Factor to calculate new due date' => 'Коэффициент для рассчета новой даты',
@ -850,10 +798,10 @@ return array(
'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' => '',
'There is no user management for private projects.' => 'Для приватных проектов управление пользователями не предусмотрено.',
'User that will receive the email' => 'Пользователь, который будет получать e-mail',
'Email subject' => 'Тема e-mail',
'Date' => 'Дата',
// 'By @%s on Bitbucket' => '',
// 'Bitbucket Issue' => '',
// 'Commit made by @%s on Bitbucket' => '',
@ -869,202 +817,243 @@ return array(
// 'Bitbucket issue reopened' => '',
// 'Bitbucket issue assignee change' => '',
// 'Bitbucket issue comment created' => '',
// 'Column change' => '',
// 'Position change' => '',
// 'Swimlane change' => '',
'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' => '',
// 'Budget overview' => '',
// 'Type' => '',
// 'There is not enough data to show something.' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
'[%s] Overdue tasks' => '[%s] просроченные задачи',
'Notification' => 'Уведомления',
'%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' => 'Граватар',
'Hipchat' => 'Hipchat',
'Slack' => 'Slack',
// '%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' => '',
'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"' => '',
// 'New title: %s' => '',
// 'The task is not assigned anymore' => '',
'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' => '',
// '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' => '',
// '%A' => '',
// '%b %e, %Y, %k:%M %p' => '',
// 'New due date: %B %e, %Y' => '',
// 'Start date changed: %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' => '',
// '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' => '',
// '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' => '',
// 'Channel/Group/User (Optional)' => '',
// '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.' => '',
'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' => '%b %e, %Y, %k:%M %p',
'New due date: %B %e, %Y' => 'Новая дата завершения: %B %e, %Y',
'Start date changed: %B %e, %Y' => 'Изменить дату начала: %B %e, %Y',
'%k:%M %p' => '%k:%M %p',
'%%Y-%%m-%%d' => '%%Y-%%m-%%d',
'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' => 'Лента моей активности',
'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"' => 'Затраченное время и время цикла для "%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 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',
'Channel/Group/User (Optional)' => 'Канал/Группа/Пользователь (опционально)',
'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.' => '',
// 'By @%s on Gitlab' => '',
// 'Gitlab issue comment created' => '',
// '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' => '',
// 'Project Administrator' => '',
// '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' => '',
// 'Managers' => '',
// 'Members' => '',
// 'Shared project' => '',
// 'Project managers' => '',
// 'Project members' => '',
// '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' => '',
// 'Help with project permissions' => '',
'Set automatically the start date' => 'Установить автоматическую дату начала',
'Edit Authentication' => 'Редактировать авторизацию',
'Google Id' => 'Google I',
'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.' => 'Если вы установите флажок "Запретить форму входа", учетные данные, введенные в форму входа будет игнорироваться.',
'By @%s on Gitlab' => 'От @%s на Gitlab',
'Gitlab issue comment created' => 'Был создан комментарий к задаче на Gitlab',
'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' => 'Текущая дорожка: %s',
'Current column: %s' => 'Текущая колонка: %s',
'Current category: %s' => 'Текущая категория: %s',
'no category' => 'без категории',
'Current assignee: %s' => 'Current assignee: %s',
'not assigned' => 'не назначен',
'Author:' => 'Автор:',
'contributors' => 'соавторы',
'License:' => 'Лицензия:',
'License' => 'Лицензия',
'Project Administrator' => 'Администратор проекта',
'Enter the text below' => 'Введите текст ниже',
'Gantt chart for %s' => 'Диаграмма Гантта для %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' => 'Обзор пользователей',
'Managers' => 'Менеджеры',
'Members' => 'Участники',
'Shared project' => 'Общие/публичные проекты',
'Project managers' => 'Менеджер проекта',
'Project members' => 'Участники проекта',
'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' => 'Ссылка на создание или модификацию задачи',
'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' => 'Переключиться в режим диаграммы Гантта',
'Reset the search/filter box' => 'Сбросить поиск/фильтр',
'Documentation' => 'Документация',
'Table of contents' => 'Сожержание',
'Gantt' => 'Гантт',
'Help with project permissions' => 'Помощь с правами доступа по проекту',
// '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:' => '',
);

View file

@ -395,8 +395,6 @@ return array(
'Remote' => 'Udaljno',
'Enabled' => 'Omogući',
'Disabled' => 'Onemogući',
'Google account linked' => 'Połączone konto Google',
'Github account linked' => 'Połączone konto Github',
'Username:' => 'Korisničko ime:',
'Name:' => 'Ime i Prezime',
'Email:' => 'Email: ',
@ -667,75 +665,26 @@ return array(
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// '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' => '',
// 'Show subtask estimates (forecast of future work)' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
@ -746,7 +695,6 @@ return array(
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
@ -878,9 +826,6 @@ return array(
// '%s moved the task #%d to the first swimlane' => '',
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Budget overview' => '',
// 'Type' => '',
// 'There is not enough data to show something.' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
@ -1067,4 +1012,48 @@ return array(
// 'Table of contents' => '',
// 'Gantt' => '',
// 'Help with project permissions' => '',
// '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:' => '',
);

View file

@ -395,8 +395,6 @@ return array(
'Remote' => 'Fjärr',
'Enabled' => 'Aktiverad',
'Disabled' => 'Inaktiverad',
'Google account linked' => 'Googlekonto länkat',
'Github account linked' => 'Githubkonto länkat',
'Username:' => 'Användarnam:',
'Name:' => 'Namn:',
'Email:' => 'E-post:',
@ -667,75 +665,26 @@ return array(
'Horizontal scrolling' => 'Horisontell scroll',
'Compact/wide view' => 'Kompakt/bred vy',
'No results match:' => 'Inga matchande resultat',
'Remove hourly rate' => 'Ta bort timtaxa',
'Do you really want to remove this hourly rate?' => 'Vill du verkligen ta bort denna timtaxa?',
'Hourly rates' => 'Timtaxor',
'Hourly rate' => 'Timtaxa',
'Currency' => 'Valuta',
'Effective date' => 'Giltighetsdatum',
'Add new rate' => 'Lägg till ny taxa',
'Rate removed successfully.' => 'Taxan togs bort.',
'Unable to remove this rate.' => 'Kunde inte ta bort taxan.',
'Unable to save the hourly rate.' => 'Kunde inte spara timtaxan.',
'Hourly rate created successfully.' => 'Timtaxan skapades.',
'Start time' => 'Starttid',
'End time' => 'Sluttid',
'Comment' => 'Kommentar',
'All day' => 'Hela dagen',
'Day' => 'Dag',
'Manage timetable' => 'Hantera timplan',
'Overtime timetable' => 'Övertidstimplan',
'Time off timetable' => 'Ledighetstimplan',
'Timetable' => 'Timplan',
'Work timetable' => 'Arbetstimplan',
'Week timetable' => 'Veckotidplan',
'Day timetable' => 'Dagstimplan',
'From' => 'Från',
'To' => 'Till',
'Time slot created successfully.' => 'Tidslucka skapad.',
'Unable to save this time slot.' => 'Kunde inte spara tidsluckan.',
'Time slot removed successfully.' => 'Tidsluckan tog bort.',
'Unable to remove this time slot.' => 'Kunde inte ta bort tidsluckan.',
'Do you really want to remove this time slot?' => 'Vill du verkligen ta bort tidsluckan?',
'Remove time slot' => 'Ta bort tidslucka',
'Add new time slot' => 'Lägg till ny tidslucka',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Denna tidslucka används när kryssrutan "hela dagen" är kryssad vid schemalagd ledighet eller övertid.',
'Files' => 'Filer',
'Images' => 'Bilder',
'Private project' => 'Privat projekt',
'Amount' => 'Belopp',
'AUD - Australian Dollar' => 'AUD - Australiska dollar',
'Budget' => 'Budget',
'Budget line' => 'Budgetlinje',
'Budget line removed successfully.' => 'Budgetlinjen togs bort.',
'Budget lines' => 'Budgetlinjer',
'CAD - Canadian Dollar' => 'CAD - Kanadensiska dollar',
'CHF - Swiss Francs' => 'CHF - Schweiziska Franc',
'Cost' => 'Kostnad',
'Cost breakdown' => 'Kostnadssammanställning',
'Custom Stylesheet' => 'Anpassad stilmall',
'download' => 'ladda ned',
'Do you really want to remove this budget line?' => 'Vill du verkligen ta bort budgetlinjen?',
'EUR - Euro' => 'EUR - Euro',
'Expenses' => 'Utgifter',
'GBP - British Pound' => 'GBP - Brittiska Pund',
'INR - Indian Rupee' => 'INR - Indiska Rupier',
'JPY - Japanese Yen' => 'JPY - Japanska Yen',
'New budget line' => 'Ny budgetlinje',
'NZD - New Zealand Dollar' => 'NZD - Nya Zeeländska Dollar',
'Remove a budget line' => 'Ta bort en budgetlinje',
'Remove budget line' => 'Ta bort budgetlinje',
'RSD - Serbian dinar' => 'RSD - Serbiska Dinarer',
'The budget line have been created successfully.' => 'Budgetlinjen har skapats.',
'Unable to create the budget line.' => 'Kunde inte skapa budgetlinjen.',
'Unable to remove this budget line.' => 'Kunde inte ta bort budgetlinjen.',
'USD - US Dollar' => 'USD - Amerikanska Dollar',
'Remaining' => 'Återstående',
'Destination column' => 'Målkolumn',
'Move the task to another column when assigned to a user' => 'Flytta uppgiften till en annan kolumn när den tilldelats en användare',
'Move the task to another column when assignee is cleared' => 'Flytta uppgiften till en annan kolumn när tilldelningen tas bort.',
'Source column' => 'Källkolumn',
'Show subtask estimates (forecast of future work)' => 'Visa uppskattningar för deluppgifter (prognos för framtida arbete)',
'Transitions' => 'Övergångar',
'Executer' => 'Verkställare',
'Time spent in the column' => 'Tid i kolumnen.',
@ -746,7 +695,6 @@ return array(
'Rate' => 'Kurs',
'Change reference currency' => 'Ändra referenskurs',
'Add a new currency rate' => 'Lägg till ny valutakurs',
'Currency rates are used to calculate project budget.' => 'Valutakurser används för att beräkna projektbudget.',
'Reference currency' => 'Referensvaluta',
'The currency rate have been added successfully.' => 'Valutakursen har lagts till.',
'Unable to add this currency rate.' => 'Kunde inte lägga till valutakursen.',
@ -878,9 +826,6 @@ return array(
'%s moved the task #%d to the first swimlane' => '%s flyttade uppgiften #%d till första swimlane',
'%s moved the task #%d to the swimlane "%s"' => '%s flyttade uppgiften #%d till swimlane "%s"',
'Swimlane' => 'Swimlane',
'Budget overview' => 'Budgetöversikt',
'Type' => 'Typ',
'There is not enough data to show something.' => 'Det finns inte tillräckligt mycket data för att visa något.',
'Gravatar' => 'Gravatar',
'Hipchat' => 'Hipchat',
'Slack' => 'Slack',
@ -1067,4 +1012,48 @@ return array(
// 'Table of contents' => '',
// 'Gantt' => '',
// 'Help with project permissions' => '',
// '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:' => '',
);

View file

@ -395,8 +395,6 @@ return array(
'Remote' => 'รีโมท',
'Enabled' => 'เปิดการใช้',
'Disabled' => 'ปิดการใช้',
'Google account linked' => 'เชื่อมกับกูเกิลแอคเคาท์',
'Github account linked' => 'เชื่อมกับกิทฮับแอคเคาท์',
'Username:' => 'ชื่อผู้ใช้:',
'Name:' => 'ชื่อ:',
'Email:' => 'อีเมล:',
@ -667,75 +665,26 @@ return array(
'Horizontal scrolling' => 'เลื่อนตามแนวนอน',
'Compact/wide view' => 'พอดี/กว้าง มุมมอง',
'No results match:' => 'ไม่มีผลลัพท์ที่ตรง',
'Remove hourly rate' => 'ลบอัตรารายชั่วโมง',
'Do you really want to remove this hourly rate?' => 'คุณต้องการลบอัตรารายชั่วโมง?',
'Hourly rates' => 'อัตรารายชั่วโมง',
'Hourly rate' => 'อัตรารายชั่วโมง',
'Currency' => 'สกุลเงิน',
'Effective date' => 'วันที่จ่าย',
'Add new rate' => 'เพิ่มอัตราใหม่',
'Rate removed successfully.' => 'ลบอัตราเรียบร้อยแล้ว',
'Unable to remove this rate.' => 'ไม่สามารถลบอัตรานี้ได้',
'Unable to save the hourly rate.' => 'ไม่สามารถบันทึกอัตรารายชั่วโมง',
'Hourly rate created successfully.' => 'อัตรารายชั่วโมงสร้างเรียบร้อยแล้ว',
'Start time' => 'เวลาเริ่มต้น',
'End time' => 'เวลาจบ',
'Comment' => 'ความคิดเห็น',
'All day' => 'ทั้งวัน',
'Day' => 'วัน',
'Manage timetable' => 'จัดการตารางเวลา',
'Overtime timetable' => 'ตารางเวลาโอที',
'Time off timetable' => 'ตารางเวลาวันหยุด',
'Timetable' => 'ตารางเวลา',
'Work timetable' => 'ตารางเวลางาน',
'Week timetable' => 'ตารางเวลาสัปดาห์',
'Day timetable' => 'ตารางเวลาวัน',
'From' => 'จาก',
'To' => 'ถึง',
'Time slot created successfully.' => 'สร้างช่วงเวลาเรียบร้อยแล้ว',
'Unable to save this time slot.' => 'ไม่สามารถบันทึกช่วงเวลานี้',
'Time slot removed successfully.' => 'ลบช่วงเวลาเรียบร้อยแล้ว',
'Unable to remove this time slot.' => 'ไม่สามารถลบช่วงเวลาได้',
'Do you really want to remove this time slot?' => 'คุณต้องการลบช่วงเวลานี้?',
'Remove time slot' => 'ลบช่วงเวลา',
'Add new time slot' => 'เพิ่มช่วงเวลาใหม่',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
'Files' => 'ไฟล์',
'Images' => 'รูปภาพ',
'Private project' => 'โปรเจคส่วนตัว',
'Amount' => 'จำนวนเงิน',
// 'AUD - Australian Dollar' => '',
'Budget' => 'งบประมาณ',
'Budget line' => 'วงเงินงบประมาณ',
'Budget line removed successfully.' => 'ลบวงเงินประมาณเรียบร้อยแล้ว',
'Budget lines' => 'วงเงินงบประมาณ',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
'Cost' => 'มูลค่า',
'Cost breakdown' => 'รายละเอียดค่าใช้จ่าย',
// 'Custom Stylesheet' => '',
'download' => 'ดาวน์โหลด',
'Do you really want to remove this budget line?' => 'คุณต้องการลบวงเงินงบประมาณนี้?',
// 'EUR - Euro' => '',
'Expenses' => 'รายจ่าย',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
'New budget line' => 'วงเงินงบประมาณใหม่',
// 'NZD - New Zealand Dollar' => '',
'Remove a budget line' => 'ลบวงเงินประมาณ',
'Remove budget line' => 'ลบวงเงินประมาณ',
// 'RSD - Serbian dinar' => '',
'The budget line have been created successfully.' => 'สร้างวงเงินงบประมาณเรียบร้อยแล้ว',
'Unable to create the budget line.' => 'ไม่สามารถสร้างวงเงินงบประมาณได้',
'Unable to remove this budget line.' => 'ไม่สามารถลบวงเงินงบประมาณนี้',
// 'USD - US Dollar' => '',
'Remaining' => 'เหลืออยู่',
'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' => 'คอลัมน์ต้นทาง',
// 'Show subtask estimates (forecast of future work)' => '',
'Transitions' => 'การเปลี่ยนคอลัมน์',
'Executer' => 'ผู้ประมวลผล',
'Time spent in the column' => 'เวลาที่ใช้ในคอลัมน์',
@ -746,7 +695,6 @@ return array(
'Rate' => 'อัตรา',
// 'Change reference currency' => '',
'Add a new currency rate' => 'เพิ่มอัตราแลกเปลี่ยนเงินตราใหม่',
'Currency rates are used to calculate project budget.' => 'อัตราแลกเปลี่ยนเงินตราถูกใช้ในการคำนวณงบประมาณของโปรเจค',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
@ -878,9 +826,6 @@ return array(
// '%s moved the task #%d to the first swimlane' => '',
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Budget overview' => '',
// 'Type' => '',
// 'There is not enough data to show something.' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
@ -1067,4 +1012,48 @@ return array(
// 'Table of contents' => '',
// 'Gantt' => '',
// 'Help with project permissions' => '',
// '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:' => '',
);

View file

@ -395,8 +395,6 @@ return array(
'Remote' => 'Uzak',
'Enabled' => 'Etkinleştirildi',
'Disabled' => 'Devre dışı bırakıldı',
'Google account linked' => 'Google hesabıyla bağlı',
'Github account linked' => 'Github hesabıyla bağlı',
'Username:' => 'Kullanıcı adı',
'Name:' => 'Ad',
'Email:' => 'E-Posta',
@ -667,75 +665,26 @@ return array(
'Horizontal scrolling' => 'Geniş görünüm',
'Compact/wide view' => 'Ekrana sığdır / Geniş görünüm',
// 'No results match:' => '',
// 'Remove hourly rate' => '',
// 'Do you really want to remove this hourly rate?' => '',
// 'Hourly rates' => '',
// 'Hourly rate' => '',
// 'Currency' => '',
// 'Effective date' => '',
// 'Add new rate' => '',
// 'Rate removed successfully.' => '',
// 'Unable to remove this rate.' => '',
// 'Unable to save the hourly rate.' => '',
// 'Hourly rate created successfully.' => '',
// 'Start time' => '',
// 'End time' => '',
// 'Comment' => '',
// 'All day' => '',
// 'Day' => '',
// 'Manage timetable' => '',
// 'Overtime timetable' => '',
// 'Time off timetable' => '',
// 'Timetable' => '',
// 'Work timetable' => '',
// 'Week timetable' => '',
// 'Day timetable' => '',
// 'From' => '',
// 'To' => '',
// 'Time slot created successfully.' => '',
// 'Unable to save this time slot.' => '',
// 'Time slot removed successfully.' => '',
// 'Unable to remove this time slot.' => '',
// 'Do you really want to remove this time slot?' => '',
// 'Remove time slot' => '',
// 'Add new time slot' => '',
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
// 'Files' => '',
// 'Images' => '',
// 'Private project' => '',
// 'Amount' => '',
// 'AUD - Australian Dollar' => '',
// 'Budget' => '',
// 'Budget line' => '',
// 'Budget line removed successfully.' => '',
// 'Budget lines' => '',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
// 'Cost' => '',
// 'Cost breakdown' => '',
// 'Custom Stylesheet' => '',
// 'download' => '',
// 'Do you really want to remove this budget line?' => '',
// 'EUR - Euro' => '',
// 'Expenses' => '',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
// 'New budget line' => '',
// 'NZD - New Zealand Dollar' => '',
// 'Remove a budget line' => '',
// 'Remove budget line' => '',
// 'RSD - Serbian dinar' => '',
// 'The budget line have been created successfully.' => '',
// 'Unable to create the budget line.' => '',
// 'Unable to remove this budget line.' => '',
// 'USD - US Dollar' => '',
// 'Remaining' => '',
// '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' => '',
// 'Show subtask estimates (forecast of future work)' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
@ -746,7 +695,6 @@ return array(
// 'Rate' => '',
// 'Change reference currency' => '',
// 'Add a new currency rate' => '',
// 'Currency rates are used to calculate project budget.' => '',
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
@ -878,9 +826,6 @@ return array(
// '%s moved the task #%d to the first swimlane' => '',
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Budget overview' => '',
// 'Type' => '',
// 'There is not enough data to show something.' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
@ -1067,4 +1012,48 @@ return array(
// 'Table of contents' => '',
// 'Gantt' => '',
// 'Help with project permissions' => '',
// '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:' => '',
);

View file

@ -395,8 +395,6 @@ return array(
'Remote' => '远程',
'Enabled' => '启用',
'Disabled' => '停用',
'Google account linked' => '已经链接谷歌账号',
'Github account linked' => '已经链接Github账号',
'Username:' => '用户名:',
'Name:' => '姓名:',
'Email:' => '电子邮件:',
@ -667,75 +665,26 @@ return array(
'Horizontal scrolling' => '水平滚动',
'Compact/wide view' => '紧凑/宽视图',
'No results match:' => '无匹配结果:',
'Remove hourly rate' => '删除小时工资',
'Do you really want to remove this hourly rate?' => '确定要删除此计时工资吗?',
'Hourly rates' => '小时工资',
'Hourly rate' => '小时工资',
'Currency' => '货币',
'Effective date' => '开始时间',
'Add new rate' => '添加小时工资',
'Rate removed successfully.' => '成功删除工资。',
'Unable to remove this rate.' => '无法删除此小时工资。',
'Unable to save the hourly rate.' => '无法删除小时工资。',
'Hourly rate created successfully.' => '成功创建小时工资。',
'Start time' => '开始时间',
'End time' => '结束时1间',
'Comment' => '注释',
'All day' => '全天',
'Day' => '日期',
'Manage timetable' => '管理时间表',
// 'Overtime timetable' => '',
'Time off timetable' => '加班时间表',
'Timetable' => '时间表',
'Work timetable' => '工作时间表',
'Week timetable' => '周时间表',
'Day timetable' => '日时间表',
'From' => '从',
'To' => '到',
'Time slot created successfully.' => '成功创建时间段。',
'Unable to save this time slot.' => '无法保存此时间段。',
'Time slot removed successfully.' => '成功删除时间段。',
'Unable to remove this time slot.' => '无法删除此时间段。',
'Do you really want to remove this time slot?' => '确认要删除此时间段吗?',
'Remove time slot' => '删除时间段',
'Add new time slot' => '添加新时间段',
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '如果在放假和加班计划中选择全天,则会使用这里配置的时间段。',
'Files' => '文件',
'Images' => '图片',
'Private project' => '私人项目',
'Amount' => '数量',
// 'AUD - Australian Dollar' => '',
'Budget' => '预算',
'Budget line' => '预算线',
'Budget line removed successfully.' => '成功删除预算线',
'Budget lines' => '预算线',
// 'CAD - Canadian Dollar' => '',
// 'CHF - Swiss Francs' => '',
'Cost' => '成本',
'Cost breakdown' => '成本分解',
'Custom Stylesheet' => '自定义样式表',
'download' => '下载',
'Do you really want to remove this budget line?' => '确定要删除此预算线吗?',
// 'EUR - Euro' => '',
'Expenses' => '花费',
// 'GBP - British Pound' => '',
// 'INR - Indian Rupee' => '',
// 'JPY - Japanese Yen' => '',
'New budget line' => '新预算线',
// 'NZD - New Zealand Dollar' => '',
'Remove a budget line' => '删除预算线',
'Remove budget line' => '删除预算线',
// 'RSD - Serbian dinar' => '',
'The budget line have been created successfully.' => '成功创建预算线。',
'Unable to create the budget line.' => '无法创建预算线。',
'Unable to remove this budget line.' => '无法删除此预算线。',
// 'USD - US Dollar' => '',
'Remaining' => '剩余',
'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' => '原栏目',
// 'Show subtask estimates (forecast of future work)' => '',
'Transitions' => '变更',
'Executer' => '执行者',
'Time spent in the column' => '栏目中的时间消耗',
@ -746,7 +695,6 @@ return array(
'Rate' => '汇率',
'Change reference currency' => '修改参考货币',
'Add a new currency rate' => '添加新汇率',
'Currency rates are used to calculate project budget.' => '汇率会用来计算项目预算。',
'Reference currency' => '参考货币',
'The currency rate have been added successfully.' => '成功添加汇率。',
'Unable to add this currency rate.' => '无法添加此汇率',
@ -878,9 +826,6 @@ return array(
// '%s moved the task #%d to the first swimlane' => '',
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
'Budget overview' => '预算概览',
'Type' => '类型',
// 'There is not enough data to show something.' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
@ -1067,4 +1012,48 @@ return array(
// 'Table of contents' => '',
// 'Gantt' => '',
// 'Help with project permissions' => '',
// '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:' => '',
);

View file

@ -47,6 +47,7 @@ class Acl extends Base
'taskstatus' => '*',
'tasklink' => '*',
'timer' => '*',
'customfilter' => '*',
'calendar' => array('show', 'project'),
);
@ -64,7 +65,6 @@ class Acl extends Base
'export' => '*',
'project' => array('edit', 'update', 'share', 'integration', 'users', 'alloweverybody', 'allow', 'setowner', 'revoke', 'duplicate', 'disable', 'enable'),
'swimlane' => '*',
'budget' => '*',
'gantt' => array('project', 'savetaskdate', 'task', 'savetask'),
);
@ -94,6 +94,18 @@ class Acl extends Base
'twofactor' => array('disable'),
);
/**
* Extend ACL rules
*
* @access public
* @param string $acl_name
* @param aray $rules
*/
public function extend($acl_name, array $rules)
{
$this->$acl_name = array_merge($this->$acl_name, $rules);
}
/**
* Return true if the specified controller/action match the given acl
*

View file

@ -30,6 +30,28 @@ class Action extends Base
*/
const TABLE_PARAMS = 'action_has_params';
/**
* Extended actions
*
* @access private
* @var array
*/
private $actions = array();
/**
* Extend the list of default actions
*
* @access public
* @param string $className
* @param string $description
* @return Action
*/
public function extendActions($className, $description)
{
$this->actions[$className] = $description;
return $this;
}
/**
* Return the name and description of available actions
*
@ -62,6 +84,8 @@ class Action extends Base
'TaskAssignColorLink' => t('Change task color when using a specific task link'),
);
$values = array_merge($values, $this->actions);
asort($values);
return $values;
@ -296,7 +320,7 @@ class Action extends Base
*/
public function load($name, $project_id, $event)
{
$className = '\Action\\'.$name;
$className = $name{0} !== '\\' ? '\Action\\'.$name : $name;
return new $className($this->container, $project_id, $event);
}

View file

@ -12,26 +12,6 @@ use Pimple\Container;
*/
abstract class Base extends \Core\Base
{
/**
* Database instance
*
* @access protected
* @var \PicoDb\Database
*/
protected $db;
/**
* Constructor
*
* @access public
* @param \Pimple\Container $container
*/
public function __construct(Container $container)
{
$this->container = $container;
$this->db = $this->container['db'];
}
/**
* Save a record in the database
*
@ -62,14 +42,30 @@ abstract class Base extends \Core\Base
public function removeFields(array &$values, array $keys)
{
foreach ($keys as $key) {
if (isset($values[$key])) {
if (array_key_exists($key, $values)) {
unset($values[$key]);
}
}
}
/**
* Force some fields to be at 0 if empty
* Remove keys from an array if empty
*
* @access public
* @param array $values Input array
* @param string[] $keys List of keys to remove
*/
public function removeEmptyFields(array &$values, array $keys)
{
foreach ($keys as $key) {
if (array_key_exists($key, $values) && empty($values[$key])) {
unset($values[$key]);
}
}
}
/**
* Force fields to be at 0 if empty
*
* @access public
* @param array $values Input array
@ -100,6 +96,22 @@ abstract class Base extends \Core\Base
}
}
/**
* Force some fields to be null if empty
*
* @access public
* @param array $values Input array
* @param string[] $keys List of keys
*/
public function convertNullFields(array &$values, array $keys)
{
foreach ($keys as $key) {
if (array_key_exists($key, $values) && empty($values[$key])) {
$values[$key] = null;
}
}
}
/**
* Build SQL condition for a given time range
*
@ -124,26 +136,6 @@ abstract class Base extends \Core\Base
return $start_column.' IS NOT NULL AND '.$start_column.' > 0 AND ('.implode(' OR ', $conditions).')';
}
/**
* Get common properties for task calendar events
*
* @access protected
* @param array $task
* @return array
*/
protected function getTaskCalendarProperties(array &$task)
{
return array(
'timezoneParam' => $this->config->getCurrentTimezone(),
'id' => $task['id'],
'title' => t('#%d', $task['id']).' '.$task['title'],
'backgroundColor' => $this->color->getBackgroundColor($task['color_id']),
'borderColor' => $this->color->getBorderColor($task['color_id']),
'textColor' => 'black',
'url' => $this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])),
);
}
/**
* Group a collection of records by a column
*

View file

@ -252,16 +252,24 @@ class Board extends Base
$swimlanes[$i]['columns'] = $columns;
$swimlanes[$i]['nb_columns'] = $nb_columns;
$swimlanes[$i]['nb_tasks'] = 0;
$swimlanes[$i]['nb_swimlanes'] = $ilen;
for ($j = 0; $j < $nb_columns; $j++) {
$column_id = $columns[$j]['id'];
$swimlane_id = $swimlanes[$i]['id'];
if (! isset($swimlanes[0]['columns'][$j]['nb_column_tasks'])) {
$swimlanes[0]['columns'][$j]['nb_column_tasks'] = 0;
$swimlanes[0]['columns'][$j]['total_score'] = 0;
}
$swimlanes[$i]['columns'][$j]['tasks'] = $callback === null ? $this->taskFinder->getTasksByColumnAndSwimlane($project_id, $column_id, $swimlane_id) : $callback($project_id, $column_id, $swimlane_id);
$swimlanes[$i]['columns'][$j]['nb_tasks'] = count($swimlanes[$i]['columns'][$j]['tasks']);
$swimlanes[$i]['columns'][$j]['score'] = $this->getColumnSum($swimlanes[$i]['columns'][$j]['tasks'], 'score');
$swimlanes[$i]['nb_tasks'] += $swimlanes[$i]['columns'][$j]['nb_tasks'];
$swimlanes[0]['columns'][$j]['nb_column_tasks'] += $swimlanes[$i]['columns'][$j]['nb_tasks'];
$swimlanes[0]['columns'][$j]['total_score'] += $swimlanes[$i]['columns'][$j]['score'];
}
}
@ -394,6 +402,18 @@ class Board extends Base
return (int) $this->db->table(self::TABLE)->eq('project_id', $project_id)->eq('title', $title)->findOneColumn('id');
}
/**
* Get a column title by the id
*
* @access public
* @param integer $column_id
* @return integer
*/
public function getColumnTitleById($column_id)
{
return $this->db->table(self::TABLE)->eq('id', $column_id)->findOneColumn('title');
}
/**
* Get the position of the last column for a given project
*

View file

@ -1,214 +0,0 @@
<?php
namespace Model;
use DateInterval;
use DateTime;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
/**
* Budget
*
* @package model
* @author Frederic Guillot
*/
class Budget extends Base
{
/**
* SQL table name
*
* @var string
*/
const TABLE = 'budget_lines';
/**
* Get all budget lines for a project
*
* @access public
* @param integer $project_id
* @return array
*/
public function getAll($project_id)
{
return $this->db->table(self::TABLE)->eq('project_id', $project_id)->desc('date')->findAll();
}
/**
* Get the current total of the budget
*
* @access public
* @param integer $project_id
* @return float
*/
public function getTotal($project_id)
{
$result = $this->db->table(self::TABLE)->columns('SUM(amount) as total')->eq('project_id', $project_id)->findOne();
return isset($result['total']) ? (float) $result['total'] : 0;
}
/**
* Get breakdown by tasks/subtasks/users
*
* @access public
* @param integer $project_id
* @return \PicoDb\Table
*/
public function getSubtaskBreakdown($project_id)
{
return $this->db
->table(SubtaskTimeTracking::TABLE)
->columns(
SubtaskTimeTracking::TABLE.'.id',
SubtaskTimeTracking::TABLE.'.user_id',
SubtaskTimeTracking::TABLE.'.subtask_id',
SubtaskTimeTracking::TABLE.'.start',
SubtaskTimeTracking::TABLE.'.time_spent',
Subtask::TABLE.'.task_id',
Subtask::TABLE.'.title AS subtask_title',
Task::TABLE.'.title AS task_title',
Task::TABLE.'.project_id',
User::TABLE.'.username',
User::TABLE.'.name'
)
->join(Subtask::TABLE, 'id', 'subtask_id')
->join(Task::TABLE, 'id', 'task_id', Subtask::TABLE)
->join(User::TABLE, 'id', 'user_id')
->eq(Task::TABLE.'.project_id', $project_id)
->callback(array($this, 'applyUserRate'));
}
/**
* Gather necessary information to display the budget graph
*
* @access public
* @param integer $project_id
* @return array
*/
public function getDailyBudgetBreakdown($project_id)
{
$out = array();
$in = $this->db->hashtable(self::TABLE)->eq('project_id', $project_id)->gt('amount', 0)->asc('date')->getAll('date', 'amount');
$time_slots = $this->getSubtaskBreakdown($project_id)->findAll();
foreach ($time_slots as $slot) {
$date = date('Y-m-d', $slot['start']);
if (! isset($out[$date])) {
$out[$date] = 0;
}
$out[$date] += $slot['cost'];
}
$start = key($in) ?: key($out);
$end = new DateTime;
$left = 0;
$serie = array();
for ($today = new DateTime($start); $today <= $end; $today->add(new DateInterval('P1D'))) {
$date = $today->format('Y-m-d');
$today_in = isset($in[$date]) ? (int) $in[$date] : 0;
$today_out = isset($out[$date]) ? (int) $out[$date] : 0;
if ($today_in > 0 || $today_out > 0) {
$left += $today_in;
$left -= $today_out;
$serie[] = array(
'date' => $date,
'in' => $today_in,
'out' => -$today_out,
'left' => $left,
);
}
}
return $serie;
}
/**
* Filter callback to apply the rate according to the effective date
*
* @access public
* @param array $records
* @return array
*/
public function applyUserRate(array $records)
{
$rates = $this->hourlyRate->getAllByProject($records[0]['project_id']);
foreach ($records as &$record) {
$hourly_price = 0;
foreach ($rates as $rate) {
if ($rate['user_id'] == $record['user_id'] && date('Y-m-d', $rate['date_effective']) <= date('Y-m-d', $record['start'])) {
$hourly_price = $this->currency->getPrice($rate['currency'], $rate['rate']);
break;
}
}
$record['cost'] = $hourly_price * $record['time_spent'];
}
return $records;
}
/**
* Add a new budget line in the database
*
* @access public
* @param integer $project_id
* @param float $amount
* @param string $comment
* @param string $date
* @return boolean|integer
*/
public function create($project_id, $amount, $comment, $date = '')
{
$values = array(
'project_id' => $project_id,
'amount' => $amount,
'comment' => $comment,
'date' => $date ?: date('Y-m-d'),
);
return $this->persist(self::TABLE, $values);
}
/**
* Remove a specific budget line
*
* @access public
* @param integer $budget_id
* @return boolean
*/
public function remove($budget_id)
{
return $this->db->table(self::TABLE)->eq('id', $budget_id)->remove();
}
/**
* Validate creation
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateCreation(array $values)
{
$v = new Validator($values, array(
new Validators\Required('project_id', t('Field required')),
new Validators\Required('amount', t('Field required')),
));
return array(
$v->execute(),
$v->getErrors()
);
}
}

Some files were not shown because too many files have changed in this diff Show more