diff options
Diffstat (limited to 'app')
71 files changed, 1581 insertions, 2200 deletions
diff --git a/app/Auth/Ldap.php b/app/Auth/Ldap.php index 2f8f791e..0ccd09a4 100644 --- a/app/Auth/Ldap.php +++ b/app/Auth/Ldap.php @@ -20,6 +20,178 @@ class Ldap extends Base const AUTH_NAME = 'LDAP'; /** + * Get LDAP server name + * + * @access public + * @return string + */ + public function getLdapServer() + { + return LDAP_SERVER; + } + + /** + * 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 + * @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 * * @access public @@ -29,7 +201,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 +218,7 @@ class Ldap extends Base else { // We create automatically a new user - if (LDAP_ACCOUNT_CREATION && $this->user->create($result) !== false) { + if ($this->isLdapAccountCreationEnabled() && $this->user->create($result) !== false) { $user = $this->user->getByUsername($username); } else { @@ -87,11 +259,9 @@ class Ldap extends Base * LDAP connection * * @access public - * @param string $ldap_hostname - * @param integer $ldap_port * @return resource|boolean */ - public function connect($ldap_hostname = LDAP_SERVER, $ldap_port = LDAP_PORT) + public function connect() { if (! function_exists('ldap_connect')) { $this->logger->error('The PHP LDAP extension is required'); @@ -103,10 +273,10 @@ class Ldap extends Base putenv('LDAPTLS_REQCERT=never'); } - $ldap = ldap_connect($ldap_hostname, $ldap_port); + $ldap = ldap_connect($this->getLdapServer(), $this->getLdapPort()); if ($ldap === false) { - $this->logger->error('Unable to connect to the LDAP server: "'.LDAP_SERVER.'"'); + $this->logger->error('Unable to connect to the LDAP server'); return false; } @@ -131,19 +301,17 @@ class Ldap extends Base * @param string $username * @param string $password * @param string $ldap_type - * @param string $ldap_username - * @param string $ldap_password * @return boolean */ - public function bind($ldap, $username, $password, $ldap_type = LDAP_BIND_TYPE, $ldap_username = LDAP_USERNAME, $ldap_password = LDAP_PASSWORD) + public function bind($ldap, $username, $password, $ldap_type = LDAP_BIND_TYPE) { if ($ldap_type === 'user') { - $ldap_username = sprintf($ldap_username, $username); + $ldap_username = $this->getLdapUserPattern($username); $ldap_password = $password; } else if ($ldap_type === 'proxy') { - $ldap_username = $ldap_username; - $ldap_password = $ldap_password; + $ldap_username = $this->getLdapUsername(); + $ldap_password = $this->getLdapPassword(); } else { $ldap_username = null; @@ -164,21 +332,12 @@ class Ldap extends Base * @param resource $ldap * @param string $username * @param string $password - * @param string $base_dn - * @param string $user_pattern * @return boolean|array */ - public function getProfile($ldap, $username, $password, $base_dn = LDAP_ACCOUNT_BASE, $user_pattern = LDAP_USER_PATTERN) + public function getProfile($ldap, $username, $password) { - $sr = ldap_search($ldap, $base_dn, sprintf($user_pattern, $username), $this->getProfileAttributes()); - - if ($sr === false) { - return false; - } - - $entries = ldap_get_entries($ldap, $sr); - - if ($entries === false || count($entries) === 0 || $entries['count'] == 0) { + $entries = $this->executeQuery($ldap, $this->getLdapUserPattern($username)); + if ($entries === false) { return false; } @@ -200,28 +359,21 @@ class Ldap extends Base */ public function prepareProfile($ldap, array $entries, $username) { + if ($this->getLdapAccountId() !== '') { + $username = $this->getEntry($entries, $this->getLdapAccountId(), $username); + } + return array( 'username' => $username, - 'name' => $this->getEntry($entries, LDAP_ACCOUNT_FULLNAME), - 'email' => $this->getEntry($entries, LDAP_ACCOUNT_EMAIL), - 'is_admin' => (int) $this->isMemberOf($this->getEntries($entries, LDAP_ACCOUNT_MEMBEROF), LDAP_GROUP_ADMIN_DN), - 'is_project_admin' => (int) $this->isMemberOf($this->getEntries($entries, LDAP_ACCOUNT_MEMBEROF), LDAP_GROUP_PROJECT_ADMIN_DN), + '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, ); } /** - * Ge the list of attributes to fetch when reading the LDAP user entry - * - * @access public - * @return array - */ - public function getProfileAttributes() - { - return array(LDAP_ACCOUNT_FULLNAME, LDAP_ACCOUNT_EMAIL, LDAP_ACCOUNT_MEMBEROF); - } - - /** * Check group membership * * @access public @@ -245,69 +397,81 @@ class Ldap extends Base } /** - * Retrieve info on LDAP user + * Retrieve info on LDAP user by username or email * * @access public - * @param string $username Username - * @param string $email Email address + * @param string $username + * @param string $email * @return boolean|array */ public function lookup($username = null, $email = null) { - $query = $this->getQuery($username, $email); + $query = $this->getLookupQuery($username, $email); if ($query === '') { return false; } - // Connect and attempt anonymous bind + // Connect and attempt anonymous or proxy binding $ldap = $this->connect(); - if ($ldap === false || ! $this->bind($ldap, null, null, 'anonymous')) { + 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) { + $entries = $this->executeQuery($ldap, $query); + if ($entries === false) { + return false; + } + + // User id not retrieved: LDAP_ACCOUNT_ID not properly configured + if (empty($username) && ! isset($entries[0][$this->getLdapAccountId()][0])) { return false; } - $info = ldap_get_entries($ldap, $sr); + return $this->prepareProfile($ldap, $entries, $username); + } - // User not found - if (count($info) == 0 || $info['count'] == 0) { + /** + * 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; } - // User id not retrieved: LDAP_ACCOUNT_ID not properly configured - if (empty($username) && ! isset($info[0][LDAP_ACCOUNT_ID][0])) { + $entries = ldap_get_entries($ldap, $sr); + if ($entries === false || count($entries) === 0 || $entries['count'] == 0) { return false; } - return array( - 'username' => $this->getEntry($info, LDAP_ACCOUNT_ID, $username), - 'name' => $this->getEntry($info, LDAP_ACCOUNT_FULLNAME), - 'email' => $this->getEntry($info, LDAP_ACCOUNT_EMAIL, $email), - ); + return $entries; } /** * Get the LDAP query to find a user * * @access private - * @param string $username Username - * @param string $email Email address + * @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 if (! empty($email)) { + return '('.$this->getLdapAccountEmail().'='.$email.')'; } return ''; diff --git a/app/Controller/Budget.php b/app/Controller/Budget.php deleted file mode 100644 index a2f7e0db..00000000 --- a/app/Controller/Budget.php +++ /dev/null @@ -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']))); - } -} diff --git a/app/Controller/Doc.php b/app/Controller/Doc.php index d9f7b5e7..f9f0a675 100644 --- a/app/Controller/Doc.php +++ b/app/Controller/Doc.php @@ -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'; + $filenames = array(__DIR__.'/../../doc/'.$page.'.markdown'); + $filename = __DIR__.'/../../doc/index.markdown'; - if (! file_exists($filename)) { - $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( diff --git a/app/Controller/File.php b/app/Controller/File.php index f73a9de9..7b7c75ee 100644 --- a/app/Controller/File.php +++ b/app/Controller/File.php @@ -60,7 +60,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.')); } @@ -76,14 +76,13 @@ class File extends Base { $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->passthru($file['path']); } /** @@ -113,16 +112,13 @@ class File extends Base { $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->passthru($file['path']); } /** @@ -134,17 +130,13 @@ class File extends Base { $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->contentType('image/jpeg'); - $this->file->generateThumbnail( - $filename, - $this->request->getIntegerParam('width'), - $this->request->getIntegerParam('height') - ); + 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->objectStorage->passthru($this->file->getThumbnailPath($file['path'])); } /** diff --git a/app/Controller/Hourlyrate.php b/app/Controller/Hourlyrate.php deleted file mode 100644 index 19650ede..00000000 --- a/app/Controller/Hourlyrate.php +++ /dev/null @@ -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']))); - } -} diff --git a/app/Core/Base.php b/app/Core/Base.php index 3db0cf74..5ed8f40a 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -35,7 +35,6 @@ use Pimple\Container; * @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 @@ -43,7 +42,6 @@ use Pimple\Container; * @property \Model\Currency $currency * @property \Model\DateParser $dateParser * @property \Model\File $file - * @property \Model\HourlyRate $hourlyRate * @property \Model\LastLogin $lastLogin * @property \Model\Link $link * @property \Model\Notification $notification diff --git a/app/Core/ObjectStorage/FileStorage.php b/app/Core/ObjectStorage/FileStorage.php new file mode 100644 index 00000000..66c62334 --- /dev/null +++ b/app/Core/ObjectStorage/FileStorage.php @@ -0,0 +1,150 @@ +<?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 + * @return string + */ + 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 passthru($key) + { + $filename = $this->path.DIRECTORY_SEPARATOR.$key; + + if (! file_exists($filename)) { + throw new ObjectStorageException('File not found: '.$filename); + } + + return readfile($filename); + } + + /** + * Move local file to object storage + * + * @access public + * @param string $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 = $this->path.DIRECTORY_SEPARATOR.dirname($key); + + if (! is_dir($folder) && ! mkdir($folder, 0755, true)) { + throw new ObjectStorageException('Unable to create folder: '.$folder); + } + } +} diff --git a/app/Core/ObjectStorage/ObjectStorageException.php b/app/Core/ObjectStorage/ObjectStorageException.php new file mode 100644 index 00000000..e89aeb25 --- /dev/null +++ b/app/Core/ObjectStorage/ObjectStorageException.php @@ -0,0 +1,9 @@ +<?php + +namespace Core\ObjectStorage; + +use Exception; + +class ObjectStorageException extends Exception +{ +} diff --git a/app/Core/ObjectStorage/ObjectStorageInterface.php b/app/Core/ObjectStorage/ObjectStorageInterface.php new file mode 100644 index 00000000..5440cf2b --- /dev/null +++ b/app/Core/ObjectStorage/ObjectStorageInterface.php @@ -0,0 +1,68 @@ +<?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 + * @return string + */ + public function put($key, &$blob); + + /** + * Output directly object content + * + * @access public + * @param string $key + */ + public function passthru($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); +} diff --git a/app/Core/PluginBase.php b/app/Core/PluginBase.php new file mode 100644 index 00000000..457afa03 --- /dev/null +++ b/app/Core/PluginBase.php @@ -0,0 +1,47 @@ +<?php + +namespace Core; + +/** + * Plugin Base class + * + * @package core + * @author Frederic Guillot + */ +abstract class PluginBase extends Base +{ + /** + * Method called for each request + * + * @abstract + * @access public + */ + abstract public function initialize(); + + /** + * 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); + }); + } +} diff --git a/app/Core/PluginLoader.php b/app/Core/PluginLoader.php new file mode 100644 index 00000000..c7c254f7 --- /dev/null +++ b/app/Core/PluginLoader.php @@ -0,0 +1,137 @@ +<?php + +namespace Core; + +use DirectoryIterator; +use PDOException; + +/** + * Plugin Loader + * + * @package core + * @author Frederic Guillot + */ +class PluginLoader extends Base +{ + /** + * Schema version table for plugins + * + * @var string + */ + const TABLE_SCHEMA = 'plugin_schema_versions'; + + /** + * Scan plugin folder and load plugins + * + * @access public + */ + public function scan() + { + if (file_exists(__DIR__.'/../../plugins')) { + $dir = new DirectoryIterator(__DIR__.'/../../plugins'); + + foreach ($dir as $fileinfo) { + if (! $fileinfo->isDot() && $fileinfo->isDir()) { + $plugin = $fileinfo->getFilename(); + $this->loadSchema($plugin); + $this->load($plugin); + } + } + } + } + + /** + * Load plugin + * + * @access public + */ + public function load($plugin) + { + $class = '\Plugin\\'.$plugin.'\\Plugin'; + $instance = new $class($this->container); + + Tool::buildDic($this->container, $instance->getClasses()); + + $instance->initialize(); + } + + /** + * Load plugin schema + * + * @access public + * @param string $plugin + */ + public function loadSchema($plugin) + { + $filename = __DIR__.'/../../plugins/'.$plugin.'/Schema/'.ucfirst(DB_DRIVER).'.php'; + + if (file_exists($filename)) { + require($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); + } +} diff --git a/app/Core/Router.php b/app/Core/Router.php index 6e7576d6..36bbfd55 100644 --- a/app/Core/Router.php +++ b/app/Core/Router.php @@ -213,49 +213,17 @@ class Router extends Base if (! empty($_GET['controller']) && ! empty($_GET['action'])) { $controller = $this->sanitize($_GET['controller'], 'app'); $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($controller, $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; - - $instance = new $class($this->container); - $instance->beforeAction($controller, $method); - $instance->$method(); - - return true; - } + $class = empty($plugin) ? '\Controller\\'.ucfirst($controller) : '\Plugin\\'.ucfirst($plugin).'\Controller\\'.ucfirst($controller); - return false; + $instance = new $class($this->container); + $instance->beforeAction($controller, $action); + $instance->$action(); } } diff --git a/app/Core/Template.php b/app/Core/Template.php index ba869ee6..b75f7da1 100644 --- a/app/Core/Template.php +++ b/app/Core/Template.php @@ -13,11 +13,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 +34,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 +57,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; + } } diff --git a/app/Core/Tool.php b/app/Core/Tool.php index 84e42ba8..887c8fb3 100644 --- a/app/Core/Tool.php +++ b/app/Core/Tool.php @@ -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); + } } diff --git a/app/Core/Translator.php b/app/Core/Translator.php index e3d19692..e9aa1f3f 100644 --- a/app/Core/Translator.php +++ b/app/Core/Translator.php @@ -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; - } - else { - self::$locales = array(); + self::$locales = array_merge(self::$locales, require($filename)); } } + + /** + * Clear locales stored in memory + * + * @static + * @access public + */ + public static function unload() + { + self::$locales = array(); + } } diff --git a/app/Helper/Hook.php b/app/Helper/Hook.php new file mode 100644 index 00000000..77756757 --- /dev/null +++ b/app/Helper/Hook.php @@ -0,0 +1,49 @@ +<?php + +namespace Helper; + +/** + * Template Hook helpers + * + * @package helper + * @author Frederic Guillot + */ +class Hook extends \Core\Base +{ + private $hooks = array(); + + /** + * Render all attached hooks + * + * @access public + * @param string $hook + * @param array $variables + * @return string + */ + public function render($hook, array $variables = array()) + { + $buffer = ''; + + foreach ($this->hooks as $name => $template) { + if ($hook === $name) { + $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->hooks[$hook] = $template; + return $this; + } +} diff --git a/app/Integration/Mailgun.php b/app/Integration/Mailgun.php index 1451b211..076c311a 100644 --- a/app/Integration/Mailgun.php +++ b/app/Integration/Mailgun.php @@ -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']; diff --git a/app/Integration/Postmark.php b/app/Integration/Postmark.php index dbb70aee..05bdf58b 100644 --- a/app/Integration/Postmark.php +++ b/app/Integration/Postmark.php @@ -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']; diff --git a/app/Integration/Sendgrid.php b/app/Integration/Sendgrid.php index 902749f6..fd58342a 100644 --- a/app/Integration/Sendgrid.php +++ b/app/Integration/Sendgrid.php @@ -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']; diff --git a/app/Locale/cs_CZ/translations.php b/app/Locale/cs_CZ/translations.php index 557a62cc..8cea7367 100644 --- a/app/Locale/cs_CZ/translations.php +++ b/app/Locale/cs_CZ/translations.php @@ -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,17 +665,7 @@ 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ář', @@ -703,34 +691,18 @@ return array( '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.', @@ -746,7 +718,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 +849,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' => '', diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php index 6a41f065..027b22c5 100644 --- a/app/Locale/da_DK/translations.php +++ b/app/Locale/da_DK/translations.php @@ -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,17 +665,7 @@ 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' => '', @@ -703,34 +691,18 @@ return array( // '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' => '', @@ -746,7 +718,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 +849,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' => '', diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php index 7b38e9fc..0b1df2e7 100644 --- a/app/Locale/de_DE/translations.php +++ b/app/Locale/de_DE/translations.php @@ -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', @@ -667,17 +665,7 @@ 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', @@ -703,34 +691,18 @@ return array( '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.', @@ -746,7 +718,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', @@ -878,9 +849,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 genügend Daten für diese Anzeige', // 'Gravatar' => '', // 'Hipchat' => '', // 'Slack' => '', diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php index f2b744f3..1e15d8c0 100644 --- a/app/Locale/es_ES/translations.php +++ b/app/Locale/es_ES/translations.php @@ -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,17 +665,7 @@ 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', @@ -703,34 +691,18 @@ return array( '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', @@ -746,7 +718,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 +849,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', diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php index d8c749a3..da462831 100644 --- a/app/Locale/fi_FI/translations.php +++ b/app/Locale/fi_FI/translations.php @@ -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,17 +665,7 @@ 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' => '', @@ -703,34 +691,18 @@ return array( // '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' => '', @@ -746,7 +718,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 +849,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' => '', diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php index 93f7110d..848b7624 100644 --- a/app/Locale/fr_FR/translations.php +++ b/app/Locale/fr_FR/translations.php @@ -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 :', @@ -669,17 +667,7 @@ 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', @@ -705,34 +693,18 @@ return array( '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', @@ -748,7 +720,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', @@ -880,9 +851,6 @@ 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', diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php index b346f1e3..a3bbd8f5 100644 --- a/app/Locale/hu_HU/translations.php +++ b/app/Locale/hu_HU/translations.php @@ -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,17 +665,7 @@ 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', @@ -703,34 +691,18 @@ return array( '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', @@ -746,7 +718,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 +849,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' => '', diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php index 06e2c5ca..e27245f9 100644 --- a/app/Locale/it_IT/translations.php +++ b/app/Locale/it_IT/translations.php @@ -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,17 +665,7 @@ 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', @@ -703,34 +691,18 @@ return array( // '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', @@ -746,7 +718,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 +849,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' => '', diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php index cb8c550b..49f92f27 100644 --- a/app/Locale/ja_JP/translations.php +++ b/app/Locale/ja_JP/translations.php @@ -395,8 +395,6 @@ return array( 'Remote' => 'リモート', 'Enabled' => '有効', 'Disabled' => '無効', - 'Google account linked' => 'Google アカウントãŒãƒªãƒ³ã‚¯', - 'Github account linked' => 'Github ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒãƒªãƒ³ã‚¯', 'Username:' => 'ユーザå:', 'Name:' => 'åå‰:', 'Email:' => 'Email:', @@ -667,17 +665,7 @@ 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' => 'コメント', @@ -703,34 +691,18 @@ return array( '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' => 'ユーザã®å‰²ã‚Šå½“ã¦ãŒãªããªã£ãŸã‚‰ã‚¿ã‚¹ã‚¯ã‚’ä»–ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•', @@ -746,7 +718,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 +849,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' => '', diff --git a/app/Locale/nb_NO/translations.php b/app/Locale/nb_NO/translations.php index 155d49b4..9880b921 100644..100755 --- a/app/Locale/nb_NO/translations.php +++ b/app/Locale/nb_NO/translations.php @@ -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', @@ -14,12 +14,12 @@ return array( 'cancel' => 'avbryt', 'or' => 'eller', 'Yellow' => 'Gul', - 'Blue' => 'BlÃ¥', - 'Green' => 'Grønn', + 'Blue' => 'Blå', + 'Green' => 'Grønn', 'Purple' => 'Lilla', - 'Red' => 'Rød', + 'Red' => 'Rød', 'Orange' => 'Orange', - 'Grey' => 'GrÃ¥', + 'Grey' => 'Grå', // 'Brown' => '', // 'Deep Orange' => '', // 'Dark Grey' => '', @@ -61,7 +61,7 @@ return array( 'Inactive' => 'Inaktiv', 'Active' => 'Aktiv', 'Add this column' => 'Legg til denne kolonnen', - '%d tasks on the board' => '%d Oppgaver pÃ¥ hovedsiden', + '%d tasks on the board' => '%d Oppgaver på hovedsiden', '%d tasks in total' => '%d Oppgaver i alt', 'Unable to update this board.' => 'Ikke mulig at oppdatere hovedsiden', 'Edit board' => 'Endre prosjektsiden', @@ -79,15 +79,15 @@ 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', 'Application settings' => 'Applikasjonsinnstillinger', - 'Language' => 'SprÃ¥k', + 'Language' => 'Språk', 'Webhook token:' => 'Webhook token:', 'API token:' => 'API Token:', - 'Database size:' => 'Databasestørrelse:', + 'Database size:' => 'Databasestørrelse:', 'Download the database' => 'Last ned databasen', 'Optimize the database' => 'Optimaliser databasen', '(VACUUM command)' => '(VACUUM kommando)', @@ -99,36 +99,37 @@ return array( 'Assignee' => 'Tildelt', '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"?', + 'Open a task' => 'Åpne en oppgave', + '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', 'Column on the board:' => 'Kolonne:', - 'Status is open' => 'Status: Ã¥pen', + 'Status is open' => 'Status: åpen', 'Status is closed' => 'Status: lukket', 'Close this task' => 'Lukk oppgaven', - 'Open this task' => 'Ã…pne denne oppgaven', + 'Open this task' => 'Åpne denne oppgaven', 'There is no description.' => 'Det er ingen beskrivelse.', 'Add a new task' => 'Opprett ny oppgave', - 'The username is required' => 'Brukernavn er pÃ¥krevd', + 'The username is required' => 'Brukernavn er påkrevd', 'The maximum length is %d characters' => 'Den maksimale lengden er %d tegn', 'The minimum length is %d characters' => 'Den minimale lengden er %d tegn', - 'The password is required' => 'Passord er pÃ¥krevet', - 'This value must be an integer' => 'Denne verdien skal være et tall', - 'The username must be unique' => 'Brukernavnet skal være unikt', - 'The user id is required' => 'Bruker-id er pÃ¥krevet', + 'The password is required' => 'Passord er påkrevet', + 'This value must be an integer' => 'Denne verdien skal være et tall', + 'The username must be unique' => 'Brukernavnet skal være unikt', + 'The user id is required' => 'Bruker-id er påkrevet', '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 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', - 'The title is required' => 'Tittel er pÃ¥revet', + 'The confirmation is required' => 'Bekreftelse er nødvendig', + 'The project is required' => 'Prosjektet er påkrevet', + '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', + 'The title is required' => 'Tittel er pårevet', + 'There is no active project, the first step is to create a new project.' => 'Det er ingen aktive prosjekter. Førstesteg er åopprette et nytt prosjekt.', 'Settings saved successfully.' => 'Innstillinger lagret.', 'Unable to save your settings.' => 'Innstillinger kunne ikke lagres.', - 'Database optimization done.' => 'Databaseoptimering er fullført.', + 'Database optimization done.' => 'Databaseoptimering er fullført.', 'Your project have been created successfully.' => 'Ditt prosjekt er opprettet.', 'Unable to create your project.' => 'Prosjektet kunne ikke opprettes', 'Project updated successfully.' => 'Prosjektet er oppdatert.', @@ -139,9 +140,9 @@ return array( 'Unable to activate this project.' => 'Prosjektet kunne ikke aktiveres.', 'Project disabled successfully.' => 'Prosjektet er deaktiveret.', 'Unable to disable this project.' => 'Prosjektet kunne ikke deaktiveres.', - 'Unable to open this task.' => 'Oppgaven kunne ikke Ã¥pnes.', - 'Task opened successfully.' => 'Oppgaven er Ã¥pnet.', - 'Unable to close this task.' => 'Oppgaven kunne ikke Ã¥pnes.', + 'Unable to open this task.' => 'Oppgaven kunne ikke åpnes.', + 'Task opened successfully.' => 'Oppgaven er åpnet.', + 'Unable to close this task.' => 'Oppgaven kunne ikke åpnes.', 'Task closed successfully.' => 'Oppgaven er lukket.', 'Unable to update your task.' => 'Oppgaven kunne ikke oppdateres.', 'Task updated successfully.' => 'Oppgaven er oppdatert.', @@ -149,7 +150,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.', @@ -157,13 +158,15 @@ return array( 'Ready' => 'Klar', 'Backlog' => 'Backlog', 'Work in progress' => 'Under arbeid', - 'Done' => 'Utført', + 'Done' => 'Utført', 'Application version:' => 'Versjon:', - 'Completed on %B %e, %Y at %k:%M %p' => 'Fullført %d.%m.%Y - %H:%M', + 'Completed on %B %e, %Y at %k:%M %p' => 'Fullført %d.%m.%Y - %H:%M', '%B %e, %Y at %k:%M %p' => '%d.%m.%Y - %H:%M', 'Date created' => 'Dato for opprettelse', - 'Date completed' => 'Dato for fullført', + 'Date completed' => 'Dato for fullført', 'Id' => 'ID', + 'Completed tasks' => 'Fullførte oppgaver', + 'Completed tasks for "%s"' => 'Fullførte oppgaver for "%s"', '%d closed tasks' => '%d lukkede oppgaver', 'No task for this project' => 'Ingen oppgaver i dette prosjektet', 'Public link' => 'Offentligt lenke', @@ -175,10 +178,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', @@ -186,14 +189,14 @@ return array( 'Comments' => 'Kommentarer', 'Write your text in Markdown' => 'Skriv din tekst i markdown', 'Leave a comment' => 'Legg inn en kommentar', - 'Comment is required' => 'Kommentar mÃ¥ legges inn', + 'Comment is required' => 'Kommentar må legges inn', 'Leave a description' => 'Legg inn en beskrivelse...', 'Comment added successfully.' => 'Kommentaren er lagt til.', 'Unable to create your comment.' => 'Din kommentar kunne ikke opprettes.', 'Edit this task' => 'Rediger oppgaven', 'Due Date' => 'Forfallsdato', 'Invalid date' => 'Ugyldig dato', - 'Must be done before %B %e, %Y' => 'Skal være utført innen %d.%m.%Y', + 'Must be done before %B %e, %Y' => 'Skal være utført innen %d.%m.%Y', '%B %e, %Y' => '%d.%m.%Y', // '%b %e, %Y' => '', 'Automatic actions' => 'Automatiske handlinger', @@ -205,19 +208,19 @@ 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', + 'Assign the task to the person who does the action' => 'Tildel oppgaven til den person, som utfører handlingen', 'Duplicate the task to another project' => 'Kopier oppgaven til et annet prosjekt', 'Move a task to another column' => 'Flytt oppgaven til en annen kolonne', 'Task modification' => 'Oppgaveendring', @@ -236,37 +239,41 @@ 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', + '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', 'User agent' => 'User Agent', 'Persistent connections' => 'Varige forbindelser', 'No session.' => 'Ingen session.', - 'Expiration date' => 'Utløpsdato', + 'Expiration date' => 'Utløpsdato', 'Remember Me' => 'Husk meg', 'Creation date' => 'Opprettelsesdato', + 'Filter by user' => 'Filtrer efter bruker', + 'Filter by due date' => 'Filtrer etter forfallsdato', 'Everybody' => 'Alle', - 'Open' => 'Ã…pen', + 'Open' => 'Åpen', 'Closed' => 'Lukket', - 'Search' => 'Søk', + 'Search' => 'Søk', 'Nothing found.' => 'Intet funnet.', + 'Search in the project "%s"' => 'Søk i prosjektet "%s"', 'Due date' => 'Forfallsdato', 'Others formats accepted: %s and %s' => 'Andre formater: %s og %s', 'Description' => 'Beskrivelse', '%d comments' => '%d kommentarer', '%d comment' => '%d kommentar', 'Email address invalid' => 'Ugyldig epost', - // '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 Google Account is not linked anymore to your profile.' => 'Din Google-konto er ikke lengre knyttet til din profil.', + 'Unable to unlink your Google Account.' => 'Det var ikke mulig ø fjerne din Google-konto.', + 'Google authentication failed' => 'Google godjenning mislyktes', + 'Unable to link your Google Account.' => 'Det var ikke mulig åknytte opp til din Google-konto.', + 'Your Google Account is linked to your profile successfully.' => 'Din Google-konto er knyttet til din profil.', 'Email' => 'Epost', 'Link my Google Account' => 'Knytt til min Google-konto', 'Unlink my Google Account' => 'Fjern knytningen til min Google-konto', @@ -275,9 +282,9 @@ 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', + 'Assign automatically a category based on a color' => 'Tildel automatisk en kategori basert på en farve', 'Task creation or modification' => 'Oppgaveopprettelse eller endring', 'Category' => 'Kategori', 'Category:' => 'Kategori:', @@ -293,17 +300,18 @@ 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"?', + 'Filter by category' => 'Filter etter kategori', 'All categories' => 'Alle kategorier', 'No category' => 'Ingen kategori', - 'The name is required' => 'Navnet er pÃ¥krevet', + 'The name is required' => 'Navnet er påkrevet', 'Remove a file' => 'Fjern en fil', '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,8 +320,8 @@ return array( 'Time tracking' => 'Tidsregistrering', 'Estimate:' => 'Estimat:', 'Spent:' => 'Brukt:', - 'Do you really want to remove this sub-task?' => 'Vil du virkelig fjerne denne deloppgaven?', - 'Remaining:' => 'Gjenværende:', + 'Do you really want to remove this sub-task?' => 'Vil du fjerne denne deloppgaven?', + 'Remaining:' => 'Gjenværende:', 'hours' => 'timer', 'spent' => 'brukt', 'estimated' => 'estimat', @@ -324,8 +332,8 @@ return array( 'Time spent' => 'Tidsforbruk', 'Edit a sub-task' => 'Rediger en deloppgave', 'Remove a sub-task' => 'Fjern en deloppgave', - 'The time must be a numeric value' => 'Tiden skal være en nummerisk erdi', - 'Todo' => 'GjøremÃ¥l', + 'The time must be a numeric value' => 'Tiden skal være en nummerisk erdi', + 'Todo' => 'Gjøremål', 'In progress' => 'Under arbeid', 'Sub-task removed successfully.' => 'Deloppgaven er fjernet.', 'Unable to remove this sub-task.' => 'Deloppgaven kunne ikke fjernes.', @@ -333,19 +341,24 @@ return array( 'Unable to update your sub-task.' => 'Deloppgaven kunne ikke opdateres.', 'Unable to create your sub-task.' => 'Deloppgaven kunne ikke oprettes.', 'Sub-task added successfully.' => 'Deloppgaven er lagt til.', - 'Maximum size: ' => 'Maksimum størrelse: ', + 'Maximum size: ' => 'Maksimum størrelse: ', 'Unable to upload the file.' => 'Filen kunne ikke lastes opp.', 'Display another project' => 'Vis annet prosjekt...', - // 'Login with my Github Account' => '', - // 'Link my Github Account' => '', - // 'Unlink my Github Account' => '', + 'Your GitHub account was successfully linked to your profile.' => 'Din GitHub-konto er knyttet til din profil.', + 'Unable to link your GitHub Account.' => 'Det var ikke mulig å knytte din GitHub-konto.', + 'GitHub authentication failed' => 'GitHub godkjenning mislyktes', + 'Your GitHub account is no longer linked to your profile.' => 'Din GitHub-konto er ikke lengere knyttet til din profil.', + 'Unable to unlink your GitHub Account.' => 'Det var ikke muligt at fjerne forbindelsen til din GitHub-konto.', + 'Login with my GitHub Account' => 'Login med min GitHub-konto', + 'Link my GitHub Account' => 'Knytt min GitHub-konto', + 'Unlink my GitHub Account' => 'Fjern knytningen til min GitHub-konto', 'Created by %s' => 'Opprettet av %s', 'Last modified on %B %e, %Y at %k:%M %p' => 'Sist endret %d.%m.%Y - %H:%M', 'Tasks Export' => 'Oppgave eksport', 'Tasks exportation for "%s"' => 'Oppgaveeksportering for "%s"', 'Start Date' => 'Start-dato', 'End Date' => 'Slutt-dato', - 'Execute' => 'KKjør', + 'Execute' => 'KKjør', 'Task Id' => 'Oppgave ID', 'Creator' => 'Laget av', 'Modification date' => 'Endringsdato', @@ -354,15 +367,15 @@ 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 opened.' => 'Oppgaven #%d er åpnet.', 'The task #%d have been closed.' => 'Oppgaven #%d er lukket.', 'Sub-task updated' => 'Deloppgaven er oppdatert', 'Title:' => 'Tittel:', 'Status:' => 'Status:', 'Assignee:' => 'Ansvarlig:', - 'Time tracking:' => 'TidsmÃ¥ling:', + 'Time tracking:' => 'Tidsmåling:', 'New sub-task' => 'Ny deloppgave', 'New attachment added "%s"' => 'Nytt vedlegg er lagt tilet "%s"', 'Comment updated' => 'Kommentar oppdatert', @@ -373,21 +386,21 @@ return array( 'Subtask updated' => 'Deloppgave oppdatert', 'Task updated' => 'Oppgave oppdatert', 'Task closed' => 'Oppgave lukket', - 'Task opened' => 'Oppgave Ã¥pnet', + 'Task opened' => 'Oppgave åpnet', 'I want to receive notifications only for those projects:' => 'Jeg vil kun ha varslinger for disse prosjekter:', - 'view the task on Kanboard' => 'se oppgaven pÃ¥hovedsiden', + 'view the task on Kanboard' => 'se oppgaven påhovedsiden', 'Public access' => 'Offentlig tilgang', 'User management' => 'Brukere', 'Active tasks' => 'Aktive oppgaver', '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', @@ -411,7 +424,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.', @@ -420,18 +433,19 @@ return array( 'Change category for the task "%s"' => 'Endre kategori for oppgaven "%s"', 'Change category' => 'Endre kategori', '%s updated the task %s' => '%s oppdaterte oppgaven %s', - '%s opened the task %s' => '%s Ã¥pnet oppgaven %s', + '%s opened the task %s' => '%s åpnet oppgaven %s', '%s moved the task %s to the position #%d in the column "%s"' => '%s flyttet oppgaven %s til posisjonen #%d i kolonnen "%s"', '%s moved the task %s to the column "%s"' => '%s flyttet oppgaven %s til kolonnen "%s"', '%s created the task %s' => '%s opprettet oppgaven %s', '%s closed the task %s' => '%s lukket oppgaven %s', '%s created a subtask for the task %s' => '%s opprettet en deloppgave for oppgaven %s', '%s updated a subtask for the task %s' => '%s oppdaterte en deloppgave for oppgaven %s', - 'Assigned to %s with an estimate of %s/%sh' => 'Tildelt til %s med et estimat pÃ¥ %s/%sh', + 'Assigned to %s with an estimate of %s/%sh' => 'Tildelt til %s med et estimat på %s/%sh', 'Not assigned, estimate of %sh' => 'Ikke tildelt, estimert til %sh', '%s updated a comment on the task %s' => '%s oppdaterte en kommentar til oppgaven %s', '%s commented the task %s' => '%s har kommentert oppgaven %s', '%s\'s activity' => '%s\'s aktvitet', + 'No activity.' => 'Ingen aktivitet', 'RSS feed' => 'RSS feed', '%s updated a comment on the task #%d' => '%s oppdaterte en kommentar til oppgaven #%d', '%s commented on the task #%d' => '%s kommenterte oppgaven #%d', @@ -440,7 +454,7 @@ return array( '%s updated the task #%d' => '%s oppdaterte oppgaven #%d', '%s created the task #%d' => '%s opprettet oppgaven #%d', '%s closed the task #%d' => '%s lukket oppgaven #%d', - '%s open the task #%d' => '%s Ã¥pnet oppgaven #%d', + '%s open the task #%d' => '%s åpnet oppgaven #%d', '%s moved the task #%d to the column "%s"' => '%s flyttet oppgaven #%d til kolonnen "%s"', '%s moved the task #%d to the position %d in the column "%s"' => '%s flyttet oppgaven #%d til posisjonen %d i kolonnen "%s"', 'Activity' => 'Aktivitetslogg', @@ -452,21 +466,21 @@ return array( 'New password for the user "%s"' => 'Nytt passord for brukeren "%s"', 'Choose an event' => 'Velg en hendelse', 'Github commit received' => 'Github forpliktelse mottatt', - 'Github issue opened' => 'Github problem Ã¥pnet', + 'Github issue opened' => 'Github problem åpnet', 'Github issue closed' => 'Github problem lukket', - 'Github issue reopened' => 'Github problem gjenÃ¥pnet', + 'Github issue reopened' => 'Github problem gjenåpnet', 'Github issue assignee change' => 'Endre ansvarlig for Github problem', 'Github issue label change' => 'Endre etikett for Github problem', 'Create a task from an external provider' => 'Oppret en oppgave fra en ekstern tilbyder', - 'Change the assignee based on an external username' => 'Endre ansvarlige baseret pÃ¥ et eksternt brukernavn', - 'Change the category based on an external label' => 'Endre kategorien basert pÃ¥ en ekstern etikett', + 'Change the assignee based on an external username' => 'Endre ansvarlige baseret på et eksternt brukernavn', + 'Change the category based on an external label' => 'Endre kategorien basert på en ekstern etikett', 'Reference' => 'Referanse', 'Reference: %s' => 'Referanse: %s', 'Label' => 'Etikett', '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 +489,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 +499,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 +524,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,32 +550,32 @@ 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', // 'This export contains the number of tasks per column grouped per day.' => '', - 'Nothing to preview...' => 'Ingenting Ã¥ forhÃ¥ndsvise', - 'Preview' => 'ForhÃ¥ndsvisning', + 'Nothing to preview...' => 'Ingenting å forhåndsvise', + 'Preview' => 'Forhåndsvisning', 'Write' => 'Skriv', - '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', + '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', // 'Do you really want to remove this swimlane: "%s"?' => '', // 'Inactive swimlanes' => '', 'Set project manager' => 'Velg prosjektleder', 'Set project member' => 'Velg prosjektmedlem', - 'Remove a swimlane' => 'Fjern en svømmebane', + 'Remove a swimlane' => 'Fjern en svømmebane', 'Rename' => 'Endre navn', - 'Show default swimlane' => 'Vis standard svømmebane', + 'Show default swimlane' => 'Vis standard svømmebane', // 'Swimlane modification for the project "%s"' => '', - // 'Swimlane not found.' => '', - // 'Swimlane removed successfully.' => '', - 'Swimlanes' => 'Svømmebaner', - 'Swimlane updated successfully.' => 'Svæmmebane oppdatert', + 'Swimlane not found.' => 'Svømmebane ikke funnet', + 'Swimlane removed successfully.' => 'Svømmebane fjernet', + 'Swimlanes' => 'Svømmebaner', + '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 +590,32 @@ 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', + 'Language:' => 'Språk', 'Timezone:' => 'Tidssone', - // 'All columns' => '', + 'All columns' => 'Alle kolonner', + 'Calendar for "%s"' => 'Kalender for "%s"', + 'Filter by column' => 'Filtrer etter kolonne', + 'Filter by status' => 'Filtrer etter status', 'Calendar' => 'Kalender', - // 'Next' => '', + 'Next' => 'Neste', // '#%d' => '', - // 'All swimlanes' => '', - // 'All colors' => '', - // 'All status' => '', + 'Filter by color' => 'Filtrer etter farge', + 'Filter by swimlane' => 'Filtrer etter svømmebane', + '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', @@ -604,16 +623,23 @@ return array( // 'Edit column "%s"' => '', // 'Select the new status of the subtask: "%s"' => '', 'Subtask timesheet' => 'Tidsskjema for deloppgaver', - 'There is nothing to show.' => 'Ingen data Ã¥ vise', + 'There is nothing to show.' => 'Ingen data å vise', // 'Time Tracking' => '', // 'You already have one subtask in progress' => '', - 'Which parts of the project do you want to duplicate?' => 'Hvilke deler av dette prosjektet ønsker du Ã¥ kopiere?', - // 'Disallow login form' => '', + 'Which parts of the project do you want to duplicate?' => 'Hvilke deler av dette prosjektet ønsker du å kopiere?', + 'Change dashboard view' => 'Endre visning', + 'Show/hide activities' => 'Vis/skjul aktiviteter', + 'Show/hide projects' => 'Vis/skjul prosjekter', + 'Show/hide subtasks' => 'Vis/skjul deloppgaver', + 'Show/hide tasks' => 'Vis/skjul oppgaver', + 'Disable login form' => 'Deaktiver innlogging', + 'Show/hide calendar' => 'Vis/skjul kalender', + 'User calendar' => 'Brukerens kalender', // '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 +647,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', @@ -647,8 +673,8 @@ return array( 'is a parent of' => 'er en overordnet oppgave av', 'targets milestone' => 'milepel', 'is a milestone of' => 'er en milepel av', - 'fixes' => 'løser', - 'is fixed by' => 'løses av', + 'fixes' => 'løser', + 'is fixed by' => 'løses av', 'This task' => 'Denne oppgaven', // '<1h' => '', // '%dh' => '', @@ -662,7 +688,9 @@ return array( 'Keyboard shortcuts' => 'Hurtigtaster', // 'Open board switcher' => '', // 'Application' => '', + 'Filter recently updated' => 'Filter nylig oppdatert', 'since %B %e, %Y at %k:%M %p' => 'siden %B %e, %Y at %k:%M %p', + 'More filters' => 'Flere filtre', 'Compact view' => 'Kompakt visning', 'Horizontal scrolling' => 'Bla horisontalt', 'Compact/wide view' => 'Kompakt/bred visning', @@ -680,9 +708,9 @@ return array( // 'Hourly rate created successfully.' => '', // 'Start time' => '', // 'End time' => '', - // 'Comment' => '', - // 'All day' => '', - // 'Day' => '', + 'Comment' => 'Kommentar', + 'All day' => 'Alle dager', + 'Day' => 'Dag', 'Manage timetable' => 'Tidstabell', 'Overtime timetable' => 'Overtidstabell', 'Time off timetable' => 'Fritidstabell', @@ -703,18 +731,18 @@ return array( 'Files' => 'Filer', 'Images' => 'Bilder', 'Private project' => 'Privat prosjekt', - // 'Amount' => '', + 'Amount' => 'Beløp', // 'AUD - Australian Dollar' => '', 'Budget' => 'Budsjett', // 'Budget line' => '', // 'Budget line removed successfully.' => '', - // 'Budget lines' => '', + 'Budget lines' => 'Budsjettlinjer', // 'CAD - Canadian Dollar' => '', // 'CHF - Swiss Francs' => '', - // 'Cost' => '', - // 'Cost breakdown' => '', + 'Cost' => 'Kostnad', + 'Cost breakdown' => 'Kostnadsnedbryting', // 'Custom Stylesheet' => '', - // 'download' => '', + 'download' => 'last ned', // 'Do you really want to remove this budget line?' => '', // 'EUR - Euro' => '', // 'Expenses' => '', @@ -731,10 +759,10 @@ return array( // 'Unable to remove this budget line.' => '', // 'USD - US Dollar' => '', // 'Remaining' => '', - // 'Destination column' => '', - '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' => '', + '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' => 'Opprinnelig kolonne', // 'Show subtask estimates (forecast of future work)' => '', 'Transitions' => 'Statusendringer', // 'Executer' => '', @@ -782,10 +810,10 @@ 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', + 'The project identifier is an optional alphanumeric code used to identify your project.' => 'Prosjektkoden er en alfanumerisk kode som kan brukes for å identifisere prosjektet', 'Identifier' => 'Prosjektkode', // 'Postmark (incoming emails)' => '', // 'Help on Postmark integration' => '', @@ -807,26 +835,26 @@ return array( // 'This value must be alphanumeric' => '', 'Edit recurrence' => 'Endre gjentakelser', 'Generate recurrent task' => 'Opprett gjentagende oppgave', - 'Trigger to generate recurrent task' => 'Betingelse for Ã¥ generere gjentakende oppgave', - 'Factor to calculate new due date' => 'Faktor for Ã¥ beregne ny tidsfrist', - 'Timeframe to calculate new due date' => 'Tidsramme for Ã¥ beregne ny tidsfrist', - 'Base date to calculate new due date' => 'Grunnlagsdato for Ã¥ beregne ny tidsfrist', + 'Trigger to generate recurrent task' => 'Betingelse for å generere gjentakende oppgave', + 'Factor to calculate new due date' => 'Faktor for å beregne ny tidsfrist', + 'Timeframe to calculate new due date' => 'Tidsramme for å beregne ny tidsfrist', + 'Base date to calculate new due date' => 'Grunnlagsdato for å beregne ny tidsfrist', 'Action date' => 'Hendelsesdato', - 'Base date to calculate new due date: ' => 'Grunnlagsdato for Ã¥ beregne ny tidsfrist', + 'Base date to calculate new due date: ' => 'Grunnlagsdato for å beregne ny tidsfrist', 'This task has created this child task: ' => 'Denne oppgaven har opprettet denne relaterte oppgaven', 'Day(s)' => 'Dager', 'Existing due date' => 'Eksisterende forfallsdato', - 'Factor to calculate new due date: ' => 'Faktor for Ã¥ beregne ny tidsfrist', - 'Month(s)' => 'MÃ¥neder', + 'Factor to calculate new due date: ' => 'Faktor for å beregne ny tidsfrist', + 'Month(s)' => 'Måneder', 'Recurrence' => 'Gjentakelse', 'This task has been created by: ' => 'Denne oppgaven er opprettet av:', // 'Recurrent task has been generated:' => '', // 'Timeframe to calculate new due date: ' => '', // 'Trigger to generate recurrent task: ' => '', - 'When task is closed' => 'NÃ¥r oppgaven er lukket', - 'When task is moved from first column' => 'NÃ¥r oppgaven er flyttet fra første kolon', - 'When task is moved to last column' => 'NÃ¥r oppgaven er flyttet til siste kolonne', - 'Year(s)' => 'Ã¥r', + 'When task is closed' => 'Når oppgaven er lukket', + 'When task is moved from first column' => 'Når oppgaven er flyttet fra første kolon', + 'When task is moved to last column' => 'Når oppgaven er flyttet til siste kolonne', + 'Year(s)' => 'år', // 'Jabber (XMPP)' => '', // 'Send notifications to Jabber' => '', // 'XMPP server address' => '', @@ -853,16 +881,16 @@ 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' => '', // 'Commit made by @%s on Github' => '', // 'By @%s on Github' => '', // 'Commit made by @%s on Gitlab' => '', - 'Add a comment log when moving the task between columns' => 'Legg til en kommentar i loggen nÃ¥r en oppgave flyttes mellom kolonnene', - 'Move the task to another column when the category is changed' => 'Flytt oppgaven til en annen kolonne nÃ¥r kategorien endres', - 'Send a task by email to someone' => 'Send en oppgave pÃ¥ epost til noen', + 'Add a comment log when moving the task between columns' => 'Legg til en kommentar i loggen når en oppgave flyttes mellom kolonnene', + 'Move the task to another column when the category is changed' => 'Flytt oppgaven til en annen kolonne når kategorien endres', + 'Send a task by email to someone' => 'Send en oppgave på epost til noen', // 'Reopen a task' => '', // 'Bitbucket issue opened' => '', // 'Bitbucket issue closed' => '', @@ -871,14 +899,14 @@ return array( // 'Bitbucket issue comment created' => '', 'Column change' => 'Endret kolonne', 'Position change' => 'Posisjonsendring', - 'Swimlane change' => 'Endret svømmebane', + 'Swimlane change' => 'Endret svømmebane', 'Assignee change' => 'Endret eier', // '[%s] Overdue tasks' => '', 'Notification' => 'Varsel', // '%s moved the task #%d to the first swimlane' => '', // '%s moved the task #%d to the swimlane "%s"' => '', - // 'Swimlane' => '', - // 'Budget overview' => '', + 'Swimlane' => 'Svømmebane', + 'Budget overview' => 'Budsjettoversikt', // 'Type' => '', // 'There is not enough data to show something.' => '', // 'Gravatar' => '', @@ -893,6 +921,7 @@ return array( // 'The task have been moved to the first swimlane' => '', // 'The task have been moved to another swimlane:' => '', // 'Overdue tasks for the project "%s"' => '', + 'There is no completed tasks at the moment.' => 'Ingen fullførte oppgaver funnet.', // 'New title: %s' => '', // 'The task is not assigned anymore' => '', // 'New assignee: %s' => '', @@ -908,61 +937,62 @@ 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', + 'Project calendar' => 'Prosjektkalender', + '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' => '', - // 'You need at least 2 days of data to show the chart.' => '', + 'Total for all columns' => 'Totalt for alle kolonner', + //'You need at least 2 days of data to show the chart.' => '', // '<15m' => '', // '<30m' => '', 'Stop timer' => 'Stopp timer', '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 +1026,75 @@ 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', ); diff --git a/app/Locale/nl_NL/translations.php b/app/Locale/nl_NL/translations.php index 23d64163..8be0c61d 100644 --- a/app/Locale/nl_NL/translations.php +++ b/app/Locale/nl_NL/translations.php @@ -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,17 +665,7 @@ 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' => '', @@ -703,34 +691,18 @@ return array( // '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' => '', @@ -746,7 +718,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 +849,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' => '', diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php index 9947cf31..d9cfcdbc 100644 --- a/app/Locale/pl_PL/translations.php +++ b/app/Locale/pl_PL/translations.php @@ -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,17 +665,7 @@ 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', @@ -703,34 +691,18 @@ return array( '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', @@ -746,7 +718,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 +849,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' => '', diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php index ed0c9b15..5f849457 100644 --- a/app/Locale/pt_BR/translations.php +++ b/app/Locale/pt_BR/translations.php @@ -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,17 +665,7 @@ 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', @@ -703,34 +691,18 @@ return array( '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', @@ -746,7 +718,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 +849,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', diff --git a/app/Locale/pt_PT/translations.php b/app/Locale/pt_PT/translations.php index ae73af1e..9e20b112 100644 --- a/app/Locale/pt_PT/translations.php +++ b/app/Locale/pt_PT/translations.php @@ -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,17 +665,7 @@ 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', @@ -703,34 +691,18 @@ return array( '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', @@ -746,7 +718,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 +849,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', diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php index feb1b684..1619f308 100644 --- a/app/Locale/ru_RU/translations.php +++ b/app/Locale/ru_RU/translations.php @@ -395,8 +395,6 @@ return array( 'Remote' => 'Удаленный', 'Enabled' => 'Включен', 'Disabled' => 'Выключены', - 'Google account linked' => 'Профиль Google ÑвÑзан', - 'Github account linked' => 'Профиль Github ÑвÑзан', 'Username:' => 'Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ:', 'Name:' => 'ИмÑ:', 'Email:' => 'E-mail:', @@ -667,17 +665,7 @@ 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' => 'Комментарий', @@ -703,34 +691,18 @@ return array( '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' => 'ПеремеÑтить задачу в другую колонку, когда назначение ÑнÑто ', @@ -746,7 +718,6 @@ return array( 'Rate' => 'КурÑ', 'Change reference currency' => 'Изменить Ñправочник валют', 'Add a new currency rate' => '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.' => 'Ðевозможно добавить Ñтот ÐºÑƒÑ€Ñ Ð²Ð°Ð»ÑŽÑ‚Ñ‹.', @@ -805,7 +776,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' => 'КоÑффициент Ð´Ð»Ñ Ñ€Ð°ÑÑчета новой даты', @@ -878,9 +849,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' => '', diff --git a/app/Locale/sr_Latn_RS/translations.php b/app/Locale/sr_Latn_RS/translations.php index 0bc5c248..a05d67d3 100644 --- a/app/Locale/sr_Latn_RS/translations.php +++ b/app/Locale/sr_Latn_RS/translations.php @@ -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,17 +665,7 @@ 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' => '', @@ -703,34 +691,18 @@ return array( // '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' => '', @@ -746,7 +718,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 +849,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' => '', diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php index 9c769724..e8bdb9ce 100644 --- a/app/Locale/sv_SE/translations.php +++ b/app/Locale/sv_SE/translations.php @@ -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,17 +665,7 @@ 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', @@ -703,34 +691,18 @@ return array( '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.', @@ -746,7 +718,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 +849,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', diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php index a5ed2474..d94107ad 100644 --- a/app/Locale/th_TH/translations.php +++ b/app/Locale/th_TH/translations.php @@ -395,8 +395,6 @@ return array( 'Remote' => 'รีโมท', 'Enabled' => 'เปิดà¸à¸²à¸£à¹ƒà¸Šà¹‰', 'Disabled' => 'ปิดà¸à¸²à¸£à¹ƒà¸Šà¹‰', - 'Google account linked' => 'เชื่à¸à¸¡à¸à¸±à¸šà¸à¸¹à¹€à¸à¸´à¸¥à¹à¸à¸„เคาท์', - 'Github account linked' => 'เชื่à¸à¸¡à¸à¸±à¸šà¸à¸´à¸—ฮับà¹à¸à¸„เคาท์', 'Username:' => 'ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰:', 'Name:' => 'ชื่à¸:', 'Email:' => 'à¸à¸µà¹€à¸¡à¸¥:', @@ -667,17 +665,7 @@ 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' => 'ความคิดเห็น', @@ -703,34 +691,18 @@ return array( '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' => 'ย้ายงานไปคà¸à¸¥à¸±à¸¡à¸™à¹Œà¸à¸·à¹ˆà¸™à¹€à¸¡à¸·à¹ˆà¸à¹„ม่à¸à¸³à¸«à¸™à¸”บุคคลรับผิดชà¸à¸š', @@ -746,7 +718,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 +849,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' => '', diff --git a/app/Locale/tr_TR/translations.php b/app/Locale/tr_TR/translations.php index 9eb5c41e..87cccac7 100644 --- a/app/Locale/tr_TR/translations.php +++ b/app/Locale/tr_TR/translations.php @@ -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,17 +665,7 @@ 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' => '', @@ -703,34 +691,18 @@ return array( // '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' => '', @@ -746,7 +718,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 +849,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' => '', diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php index 910bc0b4..102252e5 100644 --- a/app/Locale/zh_CN/translations.php +++ b/app/Locale/zh_CN/translations.php @@ -395,8 +395,6 @@ return array( 'Remote' => '远程', 'Enabled' => 'å¯ç”¨', 'Disabled' => 'åœç”¨', - 'Google account linked' => 'å·²ç»é“¾æŽ¥è°·æŒè´¦å·', - 'Github account linked' => 'å·²ç»é“¾æŽ¥Githubè´¦å·', 'Username:' => '用户å:', 'Name:' => '姓å:', 'Email:' => '电å邮件:', @@ -667,17 +665,7 @@ 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' => '注释', @@ -703,34 +691,18 @@ return array( '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' => '移除负责人时移动到其它æ ç›®', @@ -746,7 +718,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 +849,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' => '', diff --git a/app/Model/Acl.php b/app/Model/Acl.php index 8c28cb1a..9a227cf5 100644 --- a/app/Model/Acl.php +++ b/app/Model/Acl.php @@ -64,7 +64,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'), ); @@ -95,6 +94,18 @@ class Acl extends Base ); /** + * 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 * * @access public diff --git a/app/Model/Budget.php b/app/Model/Budget.php deleted file mode 100644 index 76c42ca9..00000000 --- a/app/Model/Budget.php +++ /dev/null @@ -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() - ); - } -}
\ No newline at end of file diff --git a/app/Model/File.php b/app/Model/File.php index f884e460..7adab42b 100644 --- a/app/Model/File.php +++ b/app/Model/File.php @@ -3,6 +3,8 @@ namespace Model; use Event\FileEvent; +use Core\Tool; +use Core\ObjectStorage\ObjectStorageException; /** * File model @@ -47,14 +49,17 @@ class File extends Base */ public function remove($file_id) { - $file = $this->getbyId($file_id); + try { - if (! empty($file)) { - @unlink(FILES_DIR.$file['path']); - return $this->db->table(self::TABLE)->eq('id', $file_id)->remove(); - } + $file = $this->getbyId($file_id); + $this->objectStorage->remove($file['path']); - return false; + return $this->db->table(self::TABLE)->eq('id', $file['id'])->remove(); + } + catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + return false; + } } /** @@ -66,11 +71,11 @@ class File extends Base */ public function removeAll($task_id) { - $files = $this->getAll($task_id); + $file_ids = $this->db->table(self::TABLE)->eq('task_id', $task_id)->asc('id')->findAllByColumn('id'); $results = array(); - foreach ($files as $file) { - $results[] = $this->remove($file['id']); + foreach ($file_ids as $file_id) { + $results[] = $this->remove($file_id); } return ! in_array(false, $results, true); @@ -196,6 +201,30 @@ class File extends Base } /** + * Return the image mimetype based on the file extension + * + * @access public + * @param $filename + * @return string + */ + public function getImageMimeType($filename) + { + $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); + + switch ($extension) { + case 'jpeg': + case 'jpg': + return 'image/jpeg'; + case 'png': + return 'image/png'; + case 'gif': + return 'image/gif'; + default: + return 'image/jpeg'; + } + } + + /** * Generate the path for a new filename * * @access public @@ -210,6 +239,18 @@ class File extends Base } /** + * Generate the path for a thumbnails + * + * @access public + * @param string $key Storage key + * @return string + */ + public function getThumbnailPath($key) + { + return 'thumbnails'.DIRECTORY_SEPARATOR.$key; + } + + /** * Handle file upload * * @access public @@ -218,11 +259,13 @@ class File extends Base * @param string $form_name File form name * @return bool */ - public function upload($project_id, $task_id, $form_name) + public function uploadFiles($project_id, $task_id, $form_name) { - $results = array(); + try { - if (! empty($_FILES[$form_name])) { + if (empty($_FILES[$form_name])) { + return false; + } foreach ($_FILES[$form_name]['error'] as $key => $error) { @@ -232,22 +275,27 @@ class File extends Base $uploaded_filename = $_FILES[$form_name]['tmp_name'][$key]; $destination_filename = $this->generatePath($project_id, $task_id, $original_filename); - @mkdir(FILES_DIR.dirname($destination_filename), 0755, true); + if ($this->isImage($original_filename)) { + $this->generateThumbnailFromFile($uploaded_filename, $destination_filename); + } - if (@move_uploaded_file($uploaded_filename, FILES_DIR.$destination_filename)) { + $this->objectStorage->moveUploadedFile($uploaded_filename, $destination_filename); - $results[] = $this->create( - $task_id, - $original_filename, - $destination_filename, - $_FILES[$form_name]['size'][$key] - ); - } + $this->create( + $task_id, + $original_filename, + $destination_filename, + $_FILES[$form_name]['size'][$key] + ); } } - } - return ! in_array(false, $results, true); + return true; + } + catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + return false; + } } /** @@ -261,129 +309,77 @@ class File extends Base */ public function uploadScreenshot($project_id, $task_id, $blob) { - $data = base64_decode($blob); - - if (empty($data)) { - return false; - } - $original_filename = e('Screenshot taken %s', dt('%B %e, %Y at %k:%M %p', time())).'.png'; - $destination_filename = $this->generatePath($project_id, $task_id, $original_filename); - - @mkdir(FILES_DIR.dirname($destination_filename), 0755, true); - @file_put_contents(FILES_DIR.$destination_filename, $data); - - return $this->create( - $task_id, - $original_filename, - $destination_filename, - strlen($data) - ); + return $this->uploadContent($project_id, $task_id, $original_filename, $blob); } /** * Handle file upload (base64 encoded content) * * @access public - * @param integer $project_id Project id - * @param integer $task_id Task id - * @param string $filename Filename - * @param string $blob Base64 encoded image + * @param integer $project_id Project id + * @param integer $task_id Task id + * @param string $original_filename Filename + * @param string $blob Base64 encoded file * @return bool|integer */ - public function uploadContent($project_id, $task_id, $filename, $blob) + public function uploadContent($project_id, $task_id, $original_filename, $blob) { - $data = base64_decode($blob); + try { - if (empty($data)) { - return false; - } + $data = base64_decode($blob); - $destination_filename = $this->generatePath($project_id, $task_id, $filename); + if (empty($data)) { + return false; + } + + $destination_filename = $this->generatePath($project_id, $task_id, $original_filename); + $this->objectStorage->put($destination_filename, $data); - @mkdir(FILES_DIR.dirname($destination_filename), 0755, true); - @file_put_contents(FILES_DIR.$destination_filename, $data); + if ($this->isImage($original_filename)) { + $this->generateThumbnailFromData($destination_filename, $data); + } - return $this->create( - $task_id, - $filename, - $destination_filename, - strlen($data) - ); + return $this->create( + $task_id, + $original_filename, + $destination_filename, + strlen($data) + ); + } + catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + return false; + } } /** - * Generate a jpeg thumbnail from an image (output directly the image) + * Generate thumbnail from a blob * * @access public - * @param string $filename Source image - * @param integer $resize_width Desired image width - * @param integer $resize_height Desired image height + * @param string $destination_filename + * @param string $data */ - public function generateThumbnail($filename, $resize_width, $resize_height) + public function generateThumbnailFromData($destination_filename, &$data) { - $metadata = getimagesize($filename); - $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; - } + $temp_filename = tempnam(sys_get_temp_dir(), 'datafile'); - 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($filename); - break; - case 'image/png': - $src_image = imagecreatefrompng($filename); - break; - case 'image/gif': - $src_image = imagecreatefromgif($filename); - break; - default: - return; - } + file_put_contents($temp_filename, $data); + $this->generateThumbnailFromFile($temp_filename, $destination_filename); + unlink($temp_filename); + } - imagecopyresampled($dst_image, $src_image, $dst_x, $dst_y, 0, 0, $dst_width, $dst_height, $src_width, $src_height); - imagejpeg($dst_image); + /** + * Generate thumbnail from a blob + * + * @access public + * @param string $uploaded_filename + * @param string $destination_filename + */ + public function generateThumbnailFromFile($uploaded_filename, $destination_filename) + { + $thumbnail_filename = tempnam(sys_get_temp_dir(), 'thumbnail'); + Tool::generateThumbnail($uploaded_filename, $thumbnail_filename); + $this->objectStorage->moveFile($thumbnail_filename, $this->getThumbnailPath($destination_filename)); } } diff --git a/app/Model/HourlyRate.php b/app/Model/HourlyRate.php deleted file mode 100644 index 1550bdae..00000000 --- a/app/Model/HourlyRate.php +++ /dev/null @@ -1,121 +0,0 @@ -<?php - -namespace Model; - -use SimpleValidator\Validator; -use SimpleValidator\Validators; - -/** - * Hourly Rate - * - * @package model - * @author Frederic Guillot - */ -class HourlyRate extends Base -{ - /** - * SQL table name - * - * @var string - */ - const TABLE = 'hourly_rates'; - - /** - * Get all user rates for a given project - * - * @access public - * @param integer $project_id - * @return array - */ - public function getAllByProject($project_id) - { - $members = $this->projectPermission->getMembers($project_id); - - if (empty($members)) { - return array(); - } - - return $this->db->table(self::TABLE)->in('user_id', array_keys($members))->desc('date_effective')->findAll(); - } - - /** - * Get all rates for a given user - * - * @access public - * @param integer $user_id User id - * @return array - */ - public function getAllByUser($user_id) - { - return $this->db->table(self::TABLE)->eq('user_id', $user_id)->desc('date_effective')->findAll(); - } - - /** - * Get current rate for a given user - * - * @access public - * @param integer $user_id User id - * @return float - */ - public function getCurrentRate($user_id) - { - return $this->db->table(self::TABLE)->eq('user_id', $user_id)->desc('date_effective')->findOneColumn('rate') ?: 0; - } - - /** - * Add a new rate in the database - * - * @access public - * @param integer $user_id User id - * @param float $rate Hourly rate - * @param string $currency Currency code - * @param string $date ISO8601 date format - * @return boolean|integer - */ - public function create($user_id, $rate, $currency, $date) - { - $values = array( - 'user_id' => $user_id, - 'rate' => $rate, - 'currency' => $currency, - 'date_effective' => $this->dateParser->removeTimeFromTimestamp($this->dateParser->getTimestamp($date)), - ); - - return $this->persist(self::TABLE, $values); - } - - /** - * Remove a specific rate - * - * @access public - * @param integer $rate_id - * @return boolean - */ - public function remove($rate_id) - { - return $this->db->table(self::TABLE)->eq('id', $rate_id)->remove(); - } - - /** - * Validate creation - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateCreation(array $values) - { - $v = new Validator($values, array( - new Validators\Required('user_id', t('Field required')), - new Validators\Required('rate', t('Field required')), - new Validators\Numeric('rate', t('This value must be numeric')), - new Validators\Required('date_effective', t('Field required')), - new Validators\Required('currency', t('Field required')), - )); - - return array( - $v->execute(), - $v->getErrors() - ); - } -} diff --git a/app/Model/Subtask.php b/app/Model/Subtask.php index d8a44aff..24508c91 100644 --- a/app/Model/Subtask.php +++ b/app/Model/Subtask.php @@ -49,6 +49,7 @@ class Subtask extends Base */ const EVENT_UPDATE = 'subtask.update'; const EVENT_CREATE = 'subtask.create'; + const EVENT_DELETE = 'subtask.delete'; /** * Get available status @@ -174,6 +175,23 @@ class Subtask extends Base } /** + * Prepare data before insert + * + * @access public + * @param array $values Form values + */ + public function prepareCreation(array &$values) + { + $this->prepare($values); + + $values['position'] = $this->getLastPosition($values['task_id']) + 1; + $values['status'] = isset($values['status']) ? $values['status'] : self::STATUS_TODO; + $values['time_estimated'] = isset($values['time_estimated']) ? $values['time_estimated'] : 0; + $values['time_spent'] = isset($values['time_spent']) ? $values['time_spent'] : 0; + $values['user_id'] = isset($values['user_id']) ? $values['user_id'] : 0; + } + + /** * Get the position of the last column for a given project * * @access public @@ -198,9 +216,7 @@ class Subtask extends Base */ public function create(array $values) { - $this->prepare($values); - $values['position'] = $this->getLastPosition($values['task_id']) + 1; - + $this->prepareCreation($values); $subtask_id = $this->persist(self::TABLE, $values); if ($subtask_id) { @@ -223,14 +239,13 @@ class Subtask extends Base public function update(array $values) { $this->prepare($values); + $subtask = $this->getById($values['id']); $result = $this->db->table(self::TABLE)->eq('id', $values['id'])->save($values); if ($result) { - - $this->container['dispatcher']->dispatch( - self::EVENT_UPDATE, - new SubtaskEvent($values) - ); + $event = $subtask; + $event['changes'] = array_diff_assoc($values, $subtask); + $this->container['dispatcher']->dispatch(self::EVENT_UPDATE, new SubtaskEvent($event)); } return $result; @@ -302,7 +317,6 @@ class Subtask extends Base $positions = array_flip($subtasks); if (isset($subtasks[$subtask_id]) && $subtasks[$subtask_id] < count($subtasks)) { - $position = ++$subtasks[$subtask_id]; $subtasks[$positions[$position]]--; @@ -402,7 +416,14 @@ class Subtask extends Base */ public function remove($subtask_id) { - return $this->db->table(self::TABLE)->eq('id', $subtask_id)->remove(); + $subtask = $this->getById($subtask_id); + $result = $this->db->table(self::TABLE)->eq('id', $subtask_id)->remove(); + + if ($result) { + $this->container['dispatcher']->dispatch(self::EVENT_DELETE, new SubtaskEvent($subtask)); + } + + return $result; } /** diff --git a/app/Model/SubtaskTimeTracking.php b/app/Model/SubtaskTimeTracking.php index 997031e8..56998769 100644 --- a/app/Model/SubtaskTimeTracking.php +++ b/app/Model/SubtaskTimeTracking.php @@ -339,20 +339,7 @@ class SubtaskTimeTracking extends Base */ public function updateTaskTimeTracking($task_id) { - $result = $this->calculateSubtaskTime($task_id); - $values = array(); - - if ($result['total_spent'] > 0) { - $values['time_spent'] = $result['total_spent']; - } - - if ($result['total_estimated'] > 0) { - $values['time_estimated'] = $result['total_estimated']; - } - - if (empty($values)) { - return true; - } + $values = $this->calculateSubtaskTime($task_id); return $this->db ->table(Task::TABLE) @@ -373,8 +360,8 @@ class SubtaskTimeTracking extends Base ->table(Subtask::TABLE) ->eq('task_id', $task_id) ->columns( - 'SUM(time_spent) AS total_spent', - 'SUM(time_estimated) AS total_estimated' + 'SUM(time_spent) AS time_spent', + 'SUM(time_estimated) AS time_estimated' ) ->findOne(); } diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index efdb159b..e5dff0d5 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -6,7 +6,18 @@ use PDO; use Core\Security; use Model\Link; -const VERSION = 86; +const VERSION = 87; + +function version_87($pdo) +{ + $pdo->exec(" + CREATE TABLE plugin_schema_versions ( + plugin VARCHAR(80) NOT NULL, + version INT NOT NULL DEFAULT 0, + PRIMARY KEY(plugin) + ) ENGINE=InnoDB CHARSET=utf8 + "); +} function version_86($pdo) { @@ -311,19 +322,6 @@ function version_53($pdo) $pdo->exec("ALTER TABLE subtask_time_tracking ADD COLUMN time_spent FLOAT DEFAULT 0"); } -function version_52($pdo) -{ - $pdo->exec('CREATE TABLE budget_lines ( - `id` INT NOT NULL AUTO_INCREMENT, - `project_id` INT NOT NULL, - `amount` FLOAT NOT NULL, - `date` VARCHAR(10) NOT NULL, - `comment` TEXT, - FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, - PRIMARY KEY(id) - ) ENGINE=InnoDB CHARSET=utf8'); -} - function version_51($pdo) { $pdo->exec('CREATE TABLE timetable_day ( @@ -370,19 +368,6 @@ function version_51($pdo) ) ENGINE=InnoDB CHARSET=utf8'); } -function version_50($pdo) -{ - $pdo->exec("CREATE TABLE hourly_rates ( - id INT NOT NULL AUTO_INCREMENT, - user_id INT NOT NULL, - rate FLOAT DEFAULT 0, - date_effective INTEGER NOT NULL, - currency CHAR(3) NOT NULL, - FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, - PRIMARY KEY(id) - ) ENGINE=InnoDB CHARSET=utf8"); -} - function version_49($pdo) { $pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1'); diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php index a5d28dcf..e7422e8c 100644 --- a/app/Schema/Postgres.php +++ b/app/Schema/Postgres.php @@ -6,7 +6,17 @@ use PDO; use Core\Security; use Model\Link; -const VERSION = 66; +const VERSION = 67; + +function version_67($pdo) +{ + $pdo->exec(" + CREATE TABLE plugin_schema_versions ( + plugin VARCHAR(80) NOT NULL PRIMARY KEY, + version INTEGER NOT NULL DEFAULT 0 + ) + "); +} function version_66($pdo) { @@ -305,18 +315,6 @@ function version_34($pdo) $pdo->exec("ALTER TABLE subtask_time_tracking ADD COLUMN time_spent REAL DEFAULT 0"); } -function version_33($pdo) -{ - $pdo->exec('CREATE TABLE budget_lines ( - "id" SERIAL PRIMARY KEY, - "project_id" INTEGER NOT NULL, - "amount" REAL NOT NULL, - "date" VARCHAR(10) NOT NULL, - "comment" TEXT, - FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE - )'); -} - function version_32($pdo) { $pdo->exec('CREATE TABLE timetable_day ( @@ -359,18 +357,6 @@ function version_32($pdo) )'); } -function version_31($pdo) -{ - $pdo->exec("CREATE TABLE hourly_rates ( - id SERIAL PRIMARY KEY, - user_id INTEGER NOT NULL, - rate REAL DEFAULT 0, - date_effective INTEGER NOT NULL, - currency CHAR(3) NOT NULL, - FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE - )"); -} - function version_30($pdo) { $pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1'); diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index 8efa016c..b76e902a 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -6,7 +6,17 @@ use Core\Security; use PDO; use Model\Link; -const VERSION = 82; +const VERSION = 83; + +function version_83($pdo) +{ + $pdo->exec(" + CREATE TABLE plugin_schema_versions ( + plugin TEXT NOT NULL PRIMARY KEY, + version INTEGER NOT NULL DEFAULT 0 + ) + "); +} function version_82($pdo) { @@ -282,18 +292,6 @@ function version_52($pdo) $pdo->exec("ALTER TABLE subtask_time_tracking ADD COLUMN time_spent REAL DEFAULT 0"); } -function version_51($pdo) -{ - $pdo->exec('CREATE TABLE budget_lines ( - "id" INTEGER PRIMARY KEY, - "project_id" INTEGER NOT NULL, - "amount" REAL NOT NULL, - "date" TEXT NOT NULL, - "comment" TEXT, - FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE - )'); -} - function version_50($pdo) { $pdo->exec('CREATE TABLE timetable_day ( @@ -336,18 +334,6 @@ function version_50($pdo) )'); } -function version_49($pdo) -{ - $pdo->exec("CREATE TABLE hourly_rates ( - id INTEGER PRIMARY KEY, - user_id INTEGER NOT NULL, - rate REAL DEFAULT 0, - date_effective INTEGER NOT NULL, - currency TEXT NOT NULL, - FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE - )"); -} - function version_48($pdo) { $pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1'); diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php index ef7aa575..a5677948 100644 --- a/app/ServiceProvider/ClassProvider.php +++ b/app/ServiceProvider/ClassProvider.php @@ -2,13 +2,16 @@ namespace ServiceProvider; +use Core\ObjectStorage\FileStorage; use Core\Paginator; use Core\OAuth2; +use Core\Tool; use Model\Config; use Model\Project; use Model\Webhook; use Pimple\Container; use Pimple\ServiceProviderInterface; +use League\HTMLToMarkdown\HtmlConverter; class ClassProvider implements ServiceProviderInterface { @@ -18,7 +21,6 @@ class ClassProvider implements ServiceProviderInterface 'Action', 'Authentication', 'Board', - 'Budget', 'Category', 'Color', 'Comment', @@ -26,7 +28,6 @@ class ClassProvider implements ServiceProviderInterface 'Currency', 'DateParser', 'File', - 'HourlyRate', 'LastLogin', 'Link', 'Notification', @@ -93,17 +94,7 @@ class ClassProvider implements ServiceProviderInterface public function register(Container $container) { - foreach ($this->classes as $namespace => $classes) { - - foreach ($classes as $name) { - - $class = '\\'.$namespace.'\\'.$name; - - $container[lcfirst($name)] = function ($c) use ($class) { - return new $class($c); - }; - } - } + Tool::buildDIC($container, $this->classes); $container['paginator'] = $container->factory(function ($c) { return new Paginator($c); @@ -112,5 +103,13 @@ class ClassProvider implements ServiceProviderInterface $container['oauth'] = $container->factory(function ($c) { return new OAuth2($c); }); + + $container['htmlConverter'] = function($c) { + return new HtmlConverter(array('strip_tags' => true)); + }; + + $container['objectStorage'] = function($c) { + return new FileStorage(FILES_DIR); + }; } } diff --git a/app/Subscriber/SubtaskTimeTrackingSubscriber.php b/app/Subscriber/SubtaskTimeTrackingSubscriber.php index e45b2c93..2d3b5f99 100644 --- a/app/Subscriber/SubtaskTimeTrackingSubscriber.php +++ b/app/Subscriber/SubtaskTimeTrackingSubscriber.php @@ -12,6 +12,7 @@ class SubtaskTimeTrackingSubscriber extends \Core\Base implements EventSubscribe { return array( Subtask::EVENT_CREATE => array('updateTaskTime', 0), + Subtask::EVENT_DELETE => array('updateTaskTime', 0), Subtask::EVENT_UPDATE => array( array('logStartEnd', 10), array('updateTaskTime', 0), diff --git a/app/Template/app/sidebar.php b/app/Template/app/sidebar.php index 2d966009..f4a455f8 100644 --- a/app/Template/app/sidebar.php +++ b/app/Template/app/sidebar.php @@ -19,6 +19,7 @@ <li <?= $this->app->getRouterAction() === 'activity' ? 'class="active"' : '' ?>> <?= $this->url->link(t('My activity stream'), 'app', 'activity', array('user_id' => $user['id'])) ?> </li> + <?= $this->hook->render('dashboard:sidebar') ?> </ul> <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> diff --git a/app/Template/budget/breakdown.php b/app/Template/budget/breakdown.php deleted file mode 100644 index 92561188..00000000 --- a/app/Template/budget/breakdown.php +++ /dev/null @@ -1,30 +0,0 @@ -<div class="page-header"> - <h2><?= t('Cost breakdown') ?></h2> -</div> - -<?php if ($paginator->isEmpty()): ?> - <p class="alert"><?= t('There is nothing to show.') ?></p> -<?php else: ?> - <table class="table-fixed"> - <tr> - <th class="column-20"><?= $paginator->order(t('Task'), 'task_title') ?></th> - <th class="column-25"><?= $paginator->order(t('Subtask'), 'subtask_title') ?></th> - <th class="column-20"><?= $paginator->order(t('User'), 'username') ?></th> - <th class="column-10"><?= t('Cost') ?></th> - <th class="column-10"><?= $paginator->order(t('Time spent'), \Model\SubtaskTimeTracking::TABLE.'.time_spent') ?></th> - <th class="column-15"><?= $paginator->order(t('Date'), 'start') ?></th> - </tr> - <?php foreach ($paginator->getCollection() as $record): ?> - <tr> - <td><?= $this->url->link($this->e($record['task_title']), 'task', 'show', array('project_id' => $project['id'], 'task_id' => $record['task_id'])) ?></td> - <td><?= $this->url->link($this->e($record['subtask_title']), 'task', 'show', array('project_id' => $project['id'], 'task_id' => $record['task_id'])) ?></td> - <td><?= $this->url->link($this->e($record['name'] ?: $record['username']), 'user', 'show', array('user_id' => $record['user_id'])) ?></td> - <td><?= n($record['cost']) ?></td> - <td><?= n($record['time_spent']).' '.t('hours') ?></td> - <td><?= dt('%B %e, %Y', $record['start']) ?></td> - </tr> - <?php endforeach ?> - </table> - - <?= $paginator ?> -<?php endif ?>
\ No newline at end of file diff --git a/app/Template/budget/create.php b/app/Template/budget/create.php deleted file mode 100644 index a563796d..00000000 --- a/app/Template/budget/create.php +++ /dev/null @@ -1,47 +0,0 @@ -<div class="page-header"> - <h2><?= t('Budget lines') ?></h2> -</div> - -<?php if (! empty($lines)): ?> -<table class="table-fixed table-stripped"> - <tr> - <th class="column-20"><?= t('Budget line') ?></th> - <th class="column-20"><?= t('Date') ?></th> - <th><?= t('Comment') ?></th> - <th><?= t('Action') ?></th> - </tr> - <?php foreach ($lines as $line): ?> - <tr> - <td><?= n($line['amount']) ?></td> - <td><?= dt('%B %e, %Y', strtotime($line['date'])) ?></td> - <td><?= $this->e($line['comment']) ?></td> - <td> - <?= $this->url->link(t('Remove'), 'budget', 'confirm', array('project_id' => $project['id'], 'budget_id' => $line['id'])) ?> - </td> - </tr> - <?php endforeach ?> -</table> - -<h3><?= t('New budget line') ?></h3> -<?php endif ?> - -<form method="post" action="<?= $this->url->href('budget', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> - - <?= $this->form->csrf() ?> - - <?= $this->form->hidden('id', $values) ?> - <?= $this->form->hidden('project_id', $values) ?> - - <?= $this->form->label(t('Amount'), 'amount') ?> - <?= $this->form->text('amount', $values, $errors, array('required'), 'form-numeric') ?> - - <?= $this->form->label(t('Date'), 'date') ?> - <?= $this->form->text('date', $values, $errors, array('required'), 'form-date') ?> - - <?= $this->form->label(t('Comment'), 'comment') ?> - <?= $this->form->text('comment', $values, $errors) ?> - - <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> - </div> -</form>
\ No newline at end of file diff --git a/app/Template/budget/index.php b/app/Template/budget/index.php deleted file mode 100644 index 51ef3d87..00000000 --- a/app/Template/budget/index.php +++ /dev/null @@ -1,34 +0,0 @@ -<div class="page-header"> - <h2><?= t('Budget overview') ?></h2> -</div> - -<?php if (! empty($daily_budget)): ?> -<div id="budget-chart"> - <div id="chart" - data-date-format="<?= e('%%Y-%%m-%%d') ?>" - data-metrics='<?= json_encode($daily_budget, JSON_HEX_APOS) ?>' - data-labels='<?= json_encode(array('in' => t('Budget line'), 'out' => t('Expenses'), 'left' => t('Remaining'), 'value' => t('Amount'), 'date' => t('Date'), 'type' => t('Type')), JSON_HEX_APOS) ?>'></div> -</div> -<hr/> -<table class="table-fixed table-stripped"> - <tr> - <th><?= t('Date') ?></td> - <th><?= t('Budget line') ?></td> - <th><?= t('Expenses') ?></td> - <th><?= t('Remaining') ?></td> - </tr> - <?php foreach ($daily_budget as $line): ?> - <tr> - <td><?= dt('%B %e, %Y', strtotime($line['date'])) ?></td> - <td><?= n($line['in']) ?></td> - <td><?= n($line['out']) ?></td> - <td><?= n($line['left']) ?></td> - </tr> - <?php endforeach ?> -</table> -<?php else: ?> - <p class="alert"><?= t('There is not enough data to show something.') ?></p> -<?php endif ?> - -<?= $this->asset->js('assets/js/vendor/d3.v3.min.js') ?> -<?= $this->asset->js('assets/js/vendor/c3.min.js') ?>
\ No newline at end of file diff --git a/app/Template/budget/remove.php b/app/Template/budget/remove.php deleted file mode 100644 index a5b906a1..00000000 --- a/app/Template/budget/remove.php +++ /dev/null @@ -1,13 +0,0 @@ -<div class="page-header"> - <h2><?= t('Remove budget line') ?></h2> -</div> - -<div class="confirm"> - <p class="alert alert-info"><?= t('Do you really want to remove this budget line?') ?></p> - - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'budget', 'remove', array('project_id' => $project['id'], 'budget_id' => $budget_id), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'budget', 'create', array('project_id' => $project['id'])) ?> - </div> -</div>
\ No newline at end of file diff --git a/app/Template/budget/sidebar.php b/app/Template/budget/sidebar.php deleted file mode 100644 index 8477c052..00000000 --- a/app/Template/budget/sidebar.php +++ /dev/null @@ -1,16 +0,0 @@ -<div class="sidebar"> - <h2><?= t('Budget') ?></h2> - <ul> - <li <?= $this->app->getRouterAction() === 'index' ? 'class="active"' : '' ?>> - <?= $this->url->link(t('Budget overview'), 'budget', 'index', array('project_id' => $project['id'])) ?> - </li> - <li <?= $this->app->getRouterAction() === 'create' ? 'class="active"' : '' ?>> - <?= $this->url->link(t('Budget lines'), 'budget', 'create', array('project_id' => $project['id'])) ?> - </li> - <li <?= $this->app->getRouterAction() === 'breakdown' ? 'class="active"' : '' ?>> - <?= $this->url->link(t('Cost breakdown'), 'budget', 'breakdown', array('project_id' => $project['id'])) ?> - </li> - </ul> - <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> - <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> -</div>
\ No newline at end of file diff --git a/app/Template/config/sidebar.php b/app/Template/config/sidebar.php index 3617979a..083da283 100644 --- a/app/Template/config/sidebar.php +++ b/app/Template/config/sidebar.php @@ -34,6 +34,7 @@ <li> <?= $this->url->link(t('Documentation'), 'doc', 'show') ?> </li> + <?= $this->hook->render('config:sidebar') ?> </ul> <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> diff --git a/app/Template/currency/index.php b/app/Template/currency/index.php index f72c5700..1c78c47a 100644 --- a/app/Template/currency/index.php +++ b/app/Template/currency/index.php @@ -52,5 +52,3 @@ <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> </div> </form> - -<p class="alert alert-info"><?= t('Currency rates are used to calculate project budget.') ?></p> diff --git a/app/Template/export/sidebar.php b/app/Template/export/sidebar.php index f204d29d..7e39a5af 100644 --- a/app/Template/export/sidebar.php +++ b/app/Template/export/sidebar.php @@ -13,6 +13,7 @@ <li <?= $this->app->getRouterAction() === 'summary' ? 'class="active"' : '' ?>> <?= $this->url->link(t('Daily project summary'), 'export', 'summary', array('project_id' => $project['id'])) ?> </li> + <?= $this->hook->render('export:sidebar') ?> </ul> <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> diff --git a/app/Template/file/show.php b/app/Template/file/show.php index b1a0a813..a390c9fb 100644 --- a/app/Template/file/show.php +++ b/app/Template/file/show.php @@ -11,7 +11,7 @@ <li> <?php if (function_exists('imagecreatetruecolor')): ?> <div class="img_container"> - <img src="<?= $this->url->href('file', 'thumbnail', array('width' => 250, 'height' => 100, 'file_id' => $file['id'], 'project_id' => $task['project_id'], 'task_id' => $file['task_id'])) ?>" alt="<?= $this->e($file['name']) ?>"/> + <img src="<?= $this->url->href('file', 'thumbnail', array('file_id' => $file['id'], 'project_id' => $task['project_id'], 'task_id' => $file['task_id'])) ?>" alt="<?= $this->e($file['name']) ?>"/> </div> <?php endif ?> <p> diff --git a/app/Template/header.php b/app/Template/header.php new file mode 100644 index 00000000..0bcfdbbc --- /dev/null +++ b/app/Template/header.php @@ -0,0 +1,33 @@ +<header> + <nav> + <h1><?= $this->url->link('K<span>B</span>', 'app', 'index', array(), false, 'logo', t('Dashboard')).' '.$this->e($title) ?> + <?php if (! empty($description)): ?> + <span class="tooltip" title='<?= $this->e($this->text->markdown($description)) ?>'> + <i class="fa fa-info-circle"></i> + </span> + <?php endif ?> + </h1> + <ul> + <?php if (isset($board_selector) && ! empty($board_selector)): ?> + <li> + <select id="board-selector" + class="chosen-select select-auto-redirect" + tabindex="-1" + data-notfound="<?= t('No results match:') ?>" + data-placeholder="<?= t('Display another project') ?>" + data-redirect-regex="PROJECT_ID" + data-redirect-url="<?= $this->url->href('board', 'show', array('project_id' => 'PROJECT_ID')) ?>"> + <option value=""></option> + <?php foreach($board_selector as $board_id => $board_name): ?> + <option value="<?= $board_id ?>"><?= $this->e($board_name) ?></option> + <?php endforeach ?> + </select> + </li> + <?php endif ?> + <li> + <?= $this->url->link(t('Logout'), 'auth', 'logout') ?> + <span class="username hide-tablet">(<?= $this->user->getProfileLink() ?>)</span> + </li> + </ul> + </nav> +</header>
\ No newline at end of file diff --git a/app/Template/hourlyrate/index.php b/app/Template/hourlyrate/index.php deleted file mode 100644 index af305d07..00000000 --- a/app/Template/hourlyrate/index.php +++ /dev/null @@ -1,46 +0,0 @@ -<div class="page-header"> - <h2><?= t('Hourly rates') ?></h2> -</div> - -<?php if (! empty($rates)): ?> - -<table> - <tr> - <th><?= t('Hourly rate') ?></th> - <th><?= t('Currency') ?></th> - <th><?= t('Effective date') ?></th> - <th><?= t('Action') ?></th> - </tr> - <?php foreach ($rates as $rate): ?> - <tr> - <td><?= n($rate['rate']) ?></td> - <td><?= $rate['currency'] ?></td> - <td><?= dt('%b %e, %Y', $rate['date_effective']) ?></td> - <td> - <?= $this->url->link(t('Remove'), 'hourlyrate', 'confirm', array('user_id' => $user['id'], 'rate_id' => $rate['id'])) ?> - </td> - </tr> - <?php endforeach ?> -</table> - -<h3><?= t('Add new rate') ?></h3> -<?php endif ?> - -<form method="post" action="<?= $this->url->href('hourlyrate', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off"> - - <?= $this->form->hidden('user_id', $values) ?> - <?= $this->form->csrf() ?> - - <?= $this->form->label(t('Hourly rate'), 'rate') ?> - <?= $this->form->text('rate', $values, $errors, array('required'), 'form-numeric') ?> - - <?= $this->form->label(t('Currency'), 'currency') ?> - <?= $this->form->select('currency', $currencies_list, $values, $errors, array('required')) ?> - - <?= $this->form->label(t('Effective date'), 'date_effective') ?> - <?= $this->form->text('date_effective', $values, $errors, array('required'), 'form-date') ?> - - <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> - </div> -</form> diff --git a/app/Template/hourlyrate/remove.php b/app/Template/hourlyrate/remove.php deleted file mode 100644 index 121436e4..00000000 --- a/app/Template/hourlyrate/remove.php +++ /dev/null @@ -1,13 +0,0 @@ -<div class="page-header"> - <h2><?= t('Remove hourly rate') ?></h2> -</div> - -<div class="confirm"> - <p class="alert alert-info"><?= t('Do you really want to remove this hourly rate?') ?></p> - - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'hourlyrate', 'remove', array('user_id' => $user['id'], 'rate_id' => $rate_id), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'hourlyrate', 'index', array('user_id' => $user['id'])) ?> - </div> -</div>
\ No newline at end of file diff --git a/app/Template/layout.php b/app/Template/layout.php index 60108175..934fb62c 100644 --- a/app/Template/layout.php +++ b/app/Template/layout.php @@ -28,6 +28,8 @@ <link rel="apple-touch-icon" sizes="144x144" href="<?= $this->url->dir() ?>assets/img/touch-icon-ipad-retina.png"> <title><?= isset($title) ? $this->e($title) : 'Kanboard' ?></title> + + <?= $this->hook->render('layout:head') ?> </head> <body data-status-url="<?= $this->url->href('app', 'status') ?>" data-login-url="<?= $this->url->href('auth', 'login') ?>" @@ -38,43 +40,17 @@ <?php if (isset($no_layout) && $no_layout): ?> <?= $content_for_layout ?> <?php else: ?> - <header> - <nav> - <h1><?= $this->url->link('K<span>B</span>', 'app', 'index', array(), false, 'logo', t('Dashboard')).' '.$this->e($title) ?> - <?php if (! empty($description)): ?> - <span class="tooltip" title='<?= $this->e($this->text->markdown($description)) ?>'> - <i class="fa fa-info-circle"></i> - </span> - <?php endif ?> - </h1> - <ul> - <?php if (isset($board_selector) && ! empty($board_selector)): ?> - <li> - <select id="board-selector" - class="chosen-select select-auto-redirect" - tabindex="-1" - data-notfound="<?= t('No results match:') ?>" - data-placeholder="<?= t('Display another project') ?>" - data-redirect-regex="PROJECT_ID" - data-redirect-url="<?= $this->url->href('board', 'show', array('project_id' => 'PROJECT_ID')) ?>"> - <option value=""></option> - <?php foreach($board_selector as $board_id => $board_name): ?> - <option value="<?= $board_id ?>"><?= $this->e($board_name) ?></option> - <?php endforeach ?> - </select> - </li> - <?php endif ?> - <li> - <?= $this->url->link(t('Logout'), 'auth', 'logout') ?> - <span class="username hide-tablet">(<?= $this->user->getProfileLink() ?>)</span> - </li> - </ul> - </nav> - </header> + <?= $this->hook->render('layout:top') ?> + <?= $this->render('header', array( + 'title' => $title, + 'description' => isset($description) ? $description : '', + 'board_selector' => $board_selector, + )) ?> <section class="page"> <?= $this->app->flashMessage() ?> <?= $content_for_layout ?> </section> + <?= $this->hook->render('layout:bottom') ?> <?php endif ?> </body> </html> diff --git a/app/Template/project/dropdown.php b/app/Template/project/dropdown.php index 0a53cc05..0f1e1f6b 100644 --- a/app/Template/project/dropdown.php +++ b/app/Template/project/dropdown.php @@ -9,21 +9,19 @@ </li> <?php endif ?> +<?= $this->hook->render('project:dropdown', array('project' => $project)) ?> + <?php if ($this->user->isProjectManagementAllowed($project['id'])): ?> -<li> - <i class="fa fa-line-chart fa-fw"></i> - <?= $this->url->link(t('Analytics'), 'analytic', 'tasks', array('project_id' => $project['id'])) ?> -</li> -<li> - <i class="fa fa-pie-chart fa-fw"></i> - <?= $this->url->link(t('Budget'), 'budget', 'index', array('project_id' => $project['id'])) ?> -</li> -<li> - <i class="fa fa-download fa-fw"></i> - <?= $this->url->link(t('Exports'), 'export', 'tasks', array('project_id' => $project['id'])) ?> -</li> -<li> - <i class="fa fa-cog fa-fw"></i> - <?= $this->url->link(t('Settings'), 'project', 'show', array('project_id' => $project['id'])) ?> -</li> + <li> + <i class="fa fa-line-chart fa-fw"></i> + <?= $this->url->link(t('Analytics'), 'analytic', 'tasks', array('project_id' => $project['id'])) ?> + </li> + <li> + <i class="fa fa-download fa-fw"></i> + <?= $this->url->link(t('Exports'), 'export', 'tasks', array('project_id' => $project['id'])) ?> + </li> + <li> + <i class="fa fa-cog fa-fw"></i> + <?= $this->url->link(t('Settings'), 'project', 'show', array('project_id' => $project['id'])) ?> + </li> <?php endif ?> diff --git a/app/Template/project/sidebar.php b/app/Template/project/sidebar.php index 7b5d976f..84bbb6b1 100644 --- a/app/Template/project/sidebar.php +++ b/app/Template/project/sidebar.php @@ -48,6 +48,8 @@ </li> <?php endif ?> <?php endif ?> + + <?= $this->hook->render('project:sidebar') ?> </ul> <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> diff --git a/app/Template/project_user/sidebar.php b/app/Template/project_user/sidebar.php index 8cc3f41b..98219a87 100644 --- a/app/Template/project_user/sidebar.php +++ b/app/Template/project_user/sidebar.php @@ -24,5 +24,7 @@ <li <?= $this->app->getRouterAction() === 'closed' ? 'class="active"' : '' ?>> <?= $this->url->link(t('Closed tasks'), 'projectuser', 'closed', $filter) ?> </li> + + <?= $this->hook->render('project-user:sidebar') ?> </ul> </div>
\ No newline at end of file diff --git a/app/Template/task/sidebar.php b/app/Template/task/sidebar.php index 1f06ab8c..cf0e9f76 100644 --- a/app/Template/task/sidebar.php +++ b/app/Template/task/sidebar.php @@ -18,6 +18,8 @@ <?= $this->url->link(t('Time tracking'), 'task', 'timetracking', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <?php endif ?> + + <?= $this->hook->render('task:sidebar:information') ?> </ul> <h2><?= t('Actions') ?></h2> <ul> @@ -66,6 +68,8 @@ <?= $this->url->link(t('Remove'), 'task', 'remove', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <?php endif ?> + + <?= $this->hook->render('task:sidebar:actions') ?> </ul> <div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div> <div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div> diff --git a/app/Template/user/sidebar.php b/app/Template/user/sidebar.php index cd1c85c1..80fe8684 100644 --- a/app/Template/user/sidebar.php +++ b/app/Template/user/sidebar.php @@ -20,6 +20,8 @@ <?= $this->url->link(t('Persistent connections'), 'user', 'sessions', array('user_id' => $user['id'])) ?> </li> <?php endif ?> + + <?= $this->hook->render('user:sidebar:information') ?> </ul> <h2><?= t('Actions') ?></h2> @@ -60,14 +62,13 @@ <li <?= $this->app->getRouterController() === 'user' && $this->app->getRouterAction() === 'authentication' ? 'class="active"' : '' ?>> <?= $this->url->link(t('Edit Authentication'), 'user', 'authentication', array('user_id' => $user['id'])) ?> </li> - <li <?= $this->app->getRouterController() === 'hourlyrate' ? 'class="active"' : '' ?>> - <?= $this->url->link(t('Hourly rates'), 'hourlyrate', 'index', array('user_id' => $user['id'])) ?> - </li> <li <?= $this->app->getRouterController() === 'timetable' ? 'class="active"' : '' ?>> <?= $this->url->link(t('Manage timetable'), 'timetable', 'index', array('user_id' => $user['id'])) ?> </li> <?php endif ?> + <?= $this->hook->render('user:sidebar:actions', array('user' => $user)) ?> + <?php if ($this->user->isAdmin() && ! $this->user->isCurrentUser($user['id'])): ?> <li <?= $this->app->getRouterController() === 'user' && $this->app->getRouterAction() === 'remove' ? 'class="active"' : '' ?>> <?= $this->url->link(t('Remove'), 'user', 'remove', array('user_id' => $user['id'])) ?> diff --git a/app/common.php b/app/common.php index 1f1c7273..ea38ab36 100644 --- a/app/common.php +++ b/app/common.php @@ -30,120 +30,8 @@ $container->register(new ServiceProvider\ClassProvider); $container->register(new ServiceProvider\EventDispatcherProvider); if (ENABLE_URL_REWRITE) { - - // Dashboard - $container['router']->addRoute('dashboard', 'app', 'index'); - $container['router']->addRoute('dashboard/:user_id', 'app', 'index', array('user_id')); - $container['router']->addRoute('dashboard/:user_id/projects', 'app', 'projects', array('user_id')); - $container['router']->addRoute('dashboard/:user_id/tasks', 'app', 'tasks', array('user_id')); - $container['router']->addRoute('dashboard/:user_id/subtasks', 'app', 'subtasks', array('user_id')); - $container['router']->addRoute('dashboard/:user_id/calendar', 'app', 'calendar', array('user_id')); - $container['router']->addRoute('dashboard/:user_id/activity', 'app', 'activity', array('user_id')); - - // Search routes - $container['router']->addRoute('search', 'search', 'index'); - $container['router']->addRoute('search/:search', 'search', 'index', array('search')); - - // Project routes - $container['router']->addRoute('projects', 'project', 'index'); - $container['router']->addRoute('project/create', 'project', 'create'); - $container['router']->addRoute('project/create/:private', 'project', 'create', array('private')); - $container['router']->addRoute('project/:project_id', 'project', 'show', array('project_id')); - $container['router']->addRoute('p/:project_id', 'project', 'show', array('project_id')); - $container['router']->addRoute('project/:project_id/share', 'project', 'share', array('project_id')); - $container['router']->addRoute('project/:project_id/edit', 'project', 'edit', array('project_id')); - $container['router']->addRoute('project/:project_id/integration', 'project', 'integration', array('project_id')); - $container['router']->addRoute('project/:project_id/users', 'project', 'users', array('project_id')); - $container['router']->addRoute('project/:project_id/duplicate', 'project', 'duplicate', array('project_id')); - $container['router']->addRoute('project/:project_id/remove', 'project', 'remove', array('project_id')); - $container['router']->addRoute('project/:project_id/disable', 'project', 'disable', array('project_id')); - $container['router']->addRoute('project/:project_id/enable', 'project', 'enable', array('project_id')); - - // Action routes - $container['router']->addRoute('project/:project_id/actions', 'action', 'index', array('project_id')); - $container['router']->addRoute('project/:project_id/action/:action_id/confirm', 'action', 'confirm', array('project_id', 'action_id')); - - // Column routes - $container['router']->addRoute('project/:project_id/columns', 'column', 'index', array('project_id')); - $container['router']->addRoute('project/:project_id/column/:column_id/edit', 'column', 'edit', array('project_id', 'column_id')); - $container['router']->addRoute('project/:project_id/column/:column_id/confirm', 'column', 'confirm', array('project_id', 'column_id')); - $container['router']->addRoute('project/:project_id/column/:column_id/move/:direction', 'column', 'move', array('project_id', 'column_id', 'direction')); - - // Swimlane routes - $container['router']->addRoute('project/:project_id/swimlanes', 'swimlane', 'index', array('project_id')); - $container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/edit', 'swimlane', 'edit', array('project_id', 'swimlane_id')); - $container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/confirm', 'swimlane', 'confirm', array('project_id', 'swimlane_id')); - $container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/disable', 'swimlane', 'disable', array('project_id', 'swimlane_id')); - $container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/enable', 'swimlane', 'enable', array('project_id', 'swimlane_id')); - $container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/up', 'swimlane', 'moveup', array('project_id', 'swimlane_id')); - $container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/down', 'swimlane', 'movedown', array('project_id', 'swimlane_id')); - - // Category routes - $container['router']->addRoute('project/:project_id/categories', 'category', 'index', array('project_id')); - $container['router']->addRoute('project/:project_id/category/:category_id/edit', 'category', 'edit', array('project_id', 'category_id')); - $container['router']->addRoute('project/:project_id/category/:category_id/confirm', 'category', 'confirm', array('project_id', 'category_id')); - - // Task routes - $container['router']->addRoute('project/:project_id/task/:task_id', 'task', 'show', array('project_id', 'task_id')); - $container['router']->addRoute('t/:task_id', 'task', 'show', array('task_id')); - $container['router']->addRoute('public/task/:task_id/:token', 'task', 'readonly', array('task_id', 'token')); - - $container['router']->addRoute('project/:project_id/task/:task_id/activity', 'activity', 'task', array('project_id', 'task_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/screenshot', 'file', 'screenshot', array('project_id', 'task_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/upload', 'file', 'create', array('project_id', 'task_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/comment', 'comment', 'create', array('project_id', 'task_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/link', 'tasklink', 'create', array('project_id', 'task_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/transitions', 'task', 'transitions', array('project_id', 'task_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/analytics', 'task', 'analytics', array('project_id', 'task_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/remove', 'task', 'remove', array('project_id', 'task_id')); - - $container['router']->addRoute('project/:project_id/task/:task_id/edit', 'taskmodification', 'edit', array('project_id', 'task_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/description', 'taskmodification', 'description', array('project_id', 'task_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/recurrence', 'taskmodification', 'recurrence', array('project_id', 'task_id')); - - $container['router']->addRoute('project/:project_id/task/:task_id/close', 'taskstatus', 'close', array('task_id', 'project_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/open', 'taskstatus', 'open', array('task_id', 'project_id')); - - $container['router']->addRoute('project/:project_id/task/:task_id/duplicate', 'taskduplication', 'duplicate', array('task_id', 'project_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/copy', 'taskduplication', 'copy', array('task_id', 'project_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/copy/:dst_project_id', 'taskduplication', 'copy', array('task_id', 'project_id', 'dst_project_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/move', 'taskduplication', 'move', array('task_id', 'project_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/move/:dst_project_id', 'taskduplication', 'move', array('task_id', 'project_id', 'dst_project_id')); - - // Board routes - $container['router']->addRoute('board/:project_id', 'board', 'show', array('project_id')); - $container['router']->addRoute('b/:project_id', 'board', 'show', array('project_id')); - $container['router']->addRoute('public/board/:token', 'board', 'readonly', array('token')); - - // Calendar routes - $container['router']->addRoute('calendar/:project_id', 'calendar', 'show', array('project_id')); - $container['router']->addRoute('c/:project_id', 'calendar', 'show', array('project_id')); - - // Listing routes - $container['router']->addRoute('list/:project_id', 'listing', 'show', array('project_id')); - $container['router']->addRoute('l/:project_id', 'listing', 'show', array('project_id')); - - // Gantt routes - $container['router']->addRoute('gantt/:project_id', 'gantt', 'project', array('project_id')); - $container['router']->addRoute('gantt/:project_id/sort/:sorting', 'gantt', 'project', array('project_id', 'sorting')); - - // Subtask routes - $container['router']->addRoute('project/:project_id/task/:task_id/subtask/create', 'subtask', 'create', array('project_id', 'task_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/subtask/:subtask_id/remove', 'subtask', 'confirm', array('project_id', 'task_id', 'subtask_id')); - $container['router']->addRoute('project/:project_id/task/:task_id/subtask/:subtask_id/edit', 'subtask', 'edit', array('project_id', 'task_id', 'subtask_id')); - - // Feed routes - $container['router']->addRoute('feed/project/:token', 'feed', 'project', array('token')); - $container['router']->addRoute('feed/user/:token', 'feed', 'user', array('token')); - - // Ical routes - $container['router']->addRoute('ical/project/:token', 'ical', 'project', array('token')); - $container['router']->addRoute('ical/user/:token', 'ical', 'user', array('token')); - - // Auth routes - $container['router']->addRoute('oauth/google', 'oauth', 'google'); - $container['router']->addRoute('oauth/github', 'oauth', 'github'); - $container['router']->addRoute('oauth/gitlab', 'oauth', 'gitlab'); - $container['router']->addRoute('login', 'auth', 'login'); - $container['router']->addRoute('logout', 'auth', 'logout'); + require __DIR__.'/routes.php'; } + +$plugin = new Core\PluginLoader($container); +$plugin->scan(); diff --git a/app/routes.php b/app/routes.php new file mode 100644 index 00000000..159e8f6e --- /dev/null +++ b/app/routes.php @@ -0,0 +1,117 @@ +<?php + +// Dashboard +$container['router']->addRoute('dashboard', 'app', 'index'); +$container['router']->addRoute('dashboard/:user_id', 'app', 'index', array('user_id')); +$container['router']->addRoute('dashboard/:user_id/projects', 'app', 'projects', array('user_id')); +$container['router']->addRoute('dashboard/:user_id/tasks', 'app', 'tasks', array('user_id')); +$container['router']->addRoute('dashboard/:user_id/subtasks', 'app', 'subtasks', array('user_id')); +$container['router']->addRoute('dashboard/:user_id/calendar', 'app', 'calendar', array('user_id')); +$container['router']->addRoute('dashboard/:user_id/activity', 'app', 'activity', array('user_id')); + +// Search routes +$container['router']->addRoute('search', 'search', 'index'); +$container['router']->addRoute('search/:search', 'search', 'index', array('search')); + +// Project routes +$container['router']->addRoute('projects', 'project', 'index'); +$container['router']->addRoute('project/create', 'project', 'create'); +$container['router']->addRoute('project/create/:private', 'project', 'create', array('private')); +$container['router']->addRoute('project/:project_id', 'project', 'show', array('project_id')); +$container['router']->addRoute('p/:project_id', 'project', 'show', array('project_id')); +$container['router']->addRoute('project/:project_id/share', 'project', 'share', array('project_id')); +$container['router']->addRoute('project/:project_id/edit', 'project', 'edit', array('project_id')); +$container['router']->addRoute('project/:project_id/integration', 'project', 'integration', array('project_id')); +$container['router']->addRoute('project/:project_id/users', 'project', 'users', array('project_id')); +$container['router']->addRoute('project/:project_id/duplicate', 'project', 'duplicate', array('project_id')); +$container['router']->addRoute('project/:project_id/remove', 'project', 'remove', array('project_id')); +$container['router']->addRoute('project/:project_id/disable', 'project', 'disable', array('project_id')); +$container['router']->addRoute('project/:project_id/enable', 'project', 'enable', array('project_id')); + +// Action routes +$container['router']->addRoute('project/:project_id/actions', 'action', 'index', array('project_id')); +$container['router']->addRoute('project/:project_id/action/:action_id/confirm', 'action', 'confirm', array('project_id', 'action_id')); + +// Column routes +$container['router']->addRoute('project/:project_id/columns', 'column', 'index', array('project_id')); +$container['router']->addRoute('project/:project_id/column/:column_id/edit', 'column', 'edit', array('project_id', 'column_id')); +$container['router']->addRoute('project/:project_id/column/:column_id/confirm', 'column', 'confirm', array('project_id', 'column_id')); +$container['router']->addRoute('project/:project_id/column/:column_id/move/:direction', 'column', 'move', array('project_id', 'column_id', 'direction')); + +// Swimlane routes +$container['router']->addRoute('project/:project_id/swimlanes', 'swimlane', 'index', array('project_id')); +$container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/edit', 'swimlane', 'edit', array('project_id', 'swimlane_id')); +$container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/confirm', 'swimlane', 'confirm', array('project_id', 'swimlane_id')); +$container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/disable', 'swimlane', 'disable', array('project_id', 'swimlane_id')); +$container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/enable', 'swimlane', 'enable', array('project_id', 'swimlane_id')); +$container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/up', 'swimlane', 'moveup', array('project_id', 'swimlane_id')); +$container['router']->addRoute('project/:project_id/swimlane/:swimlane_id/down', 'swimlane', 'movedown', array('project_id', 'swimlane_id')); + +// Category routes +$container['router']->addRoute('project/:project_id/categories', 'category', 'index', array('project_id')); +$container['router']->addRoute('project/:project_id/category/:category_id/edit', 'category', 'edit', array('project_id', 'category_id')); +$container['router']->addRoute('project/:project_id/category/:category_id/confirm', 'category', 'confirm', array('project_id', 'category_id')); + +// Task routes +$container['router']->addRoute('project/:project_id/task/:task_id', 'task', 'show', array('project_id', 'task_id')); +$container['router']->addRoute('t/:task_id', 'task', 'show', array('task_id')); +$container['router']->addRoute('public/task/:task_id/:token', 'task', 'readonly', array('task_id', 'token')); + +$container['router']->addRoute('project/:project_id/task/:task_id/activity', 'activity', 'task', array('project_id', 'task_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/screenshot', 'file', 'screenshot', array('project_id', 'task_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/upload', 'file', 'create', array('project_id', 'task_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/comment', 'comment', 'create', array('project_id', 'task_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/link', 'tasklink', 'create', array('project_id', 'task_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/transitions', 'task', 'transitions', array('project_id', 'task_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/analytics', 'task', 'analytics', array('project_id', 'task_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/remove', 'task', 'remove', array('project_id', 'task_id')); + +$container['router']->addRoute('project/:project_id/task/:task_id/edit', 'taskmodification', 'edit', array('project_id', 'task_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/description', 'taskmodification', 'description', array('project_id', 'task_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/recurrence', 'taskmodification', 'recurrence', array('project_id', 'task_id')); + +$container['router']->addRoute('project/:project_id/task/:task_id/close', 'taskstatus', 'close', array('task_id', 'project_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/open', 'taskstatus', 'open', array('task_id', 'project_id')); + +$container['router']->addRoute('project/:project_id/task/:task_id/duplicate', 'taskduplication', 'duplicate', array('task_id', 'project_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/copy', 'taskduplication', 'copy', array('task_id', 'project_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/copy/:dst_project_id', 'taskduplication', 'copy', array('task_id', 'project_id', 'dst_project_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/move', 'taskduplication', 'move', array('task_id', 'project_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/move/:dst_project_id', 'taskduplication', 'move', array('task_id', 'project_id', 'dst_project_id')); + +// Board routes +$container['router']->addRoute('board/:project_id', 'board', 'show', array('project_id')); +$container['router']->addRoute('b/:project_id', 'board', 'show', array('project_id')); +$container['router']->addRoute('public/board/:token', 'board', 'readonly', array('token')); + +// Calendar routes +$container['router']->addRoute('calendar/:project_id', 'calendar', 'show', array('project_id')); +$container['router']->addRoute('c/:project_id', 'calendar', 'show', array('project_id')); + +// Listing routes +$container['router']->addRoute('list/:project_id', 'listing', 'show', array('project_id')); +$container['router']->addRoute('l/:project_id', 'listing', 'show', array('project_id')); + +// Gantt routes +$container['router']->addRoute('gantt/:project_id', 'gantt', 'project', array('project_id')); +$container['router']->addRoute('gantt/:project_id/sort/:sorting', 'gantt', 'project', array('project_id', 'sorting')); + +// Subtask routes +$container['router']->addRoute('project/:project_id/task/:task_id/subtask/create', 'subtask', 'create', array('project_id', 'task_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/subtask/:subtask_id/remove', 'subtask', 'confirm', array('project_id', 'task_id', 'subtask_id')); +$container['router']->addRoute('project/:project_id/task/:task_id/subtask/:subtask_id/edit', 'subtask', 'edit', array('project_id', 'task_id', 'subtask_id')); + +// Feed routes +$container['router']->addRoute('feed/project/:token', 'feed', 'project', array('token')); +$container['router']->addRoute('feed/user/:token', 'feed', 'user', array('token')); + +// Ical routes +$container['router']->addRoute('ical/project/:token', 'ical', 'project', array('token')); +$container['router']->addRoute('ical/user/:token', 'ical', 'user', array('token')); + +// Auth routes +$container['router']->addRoute('oauth/google', 'oauth', 'google'); +$container['router']->addRoute('oauth/github', 'oauth', 'github'); +$container['router']->addRoute('oauth/gitlab', 'oauth', 'gitlab'); +$container['router']->addRoute('login', 'auth', 'login'); +$container['router']->addRoute('logout', 'auth', 'logout'); |