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

Update to v1.0.21

This commit is contained in:
mbugeia 2015-11-27 21:11:56 +01:00
parent ebc752ab9d
commit 04e1e77a7a
121 changed files with 2487 additions and 947 deletions

View file

@ -33,7 +33,7 @@ From command line:
Infos
-----
Kanboard v1.0.19
Kanboard v1.0.21
Yunohost forum thread: <https://forum.yunohost.org/t/kanboard-package/78>
@ -42,8 +42,8 @@ Kanboard and SSOwat
Kanboard use SSOwat for user authentification (it means it use the user that the web server (nginx) sent him throught SSOwat), but can't list all user of the system.
If you wish to add a user, just log in with that user into Kanboard so the software knows him and displays it.
Dev infos
---------
Developper infos
----------------
Update package:

View file

@ -4,13 +4,13 @@
define('DEBUG', false);
// Debug file path
define('DEBUG_FILE', __DIR__.'/data/debug.log');
define('DEBUG_FILE', __DIR__.DIRECTORY_SEPARATOR.'data'.DIRECTORY_SEPARATOR.'debug.log');
// Plugins directory
define('PLUGINS_DIR', 'data/plugins');
define('PLUGINS_DIR', 'plugins');
// Folder for uploaded files, don't forget the trailing slash
define('FILES_DIR', 'data/files/');
// Folder for uploaded files
define('FILES_DIR', 'data'.DIRECTORY_SEPARATOR.'files');
// E-mail address for the "From" header (notifications)
define('MAIL_FROM', 'yuno_email');
@ -146,7 +146,7 @@ define('GITHUB_OAUTH_AUTHORIZE_URL', 'https://github.com/login/oauth/authorize')
// Github oauth2 token url
define('GITHUB_OAUTH_TOKEN_URL', 'https://github.com/login/oauth/access_token');
// Github API url (don't forget the slash at the end)
// Github API url (don't forget the trailing slash)
define('GITHUB_API_URL', 'https://api.github.com/');
// Enable/disable Gitlab authentication
@ -164,7 +164,7 @@ define('GITLAB_OAUTH_AUTHORIZE_URL', 'https://gitlab.com/oauth/authorize');
// Gitlab oauth2 token url
define('GITLAB_OAUTH_TOKEN_URL', 'https://gitlab.com/oauth/token');
// Gitlab API url endpoint (don't forget the slash at the end)
// Gitlab API url endpoint (don't forget the trailing slash)
define('GITLAB_API_URL', 'https://gitlab.com/api/v3/');
// Enable/disable the reverse proxy authentication

View file

@ -1,11 +1,50 @@
Version 1.0.21
--------------
Breaking changes:
* Projects with duplicate name are now allowed:
For Postgres and Mysql the unique constraint is removed by database migration
However Sqlite does not support alter table, only new databases will have the unique constraint removed
New features:
* New automatic action: Assign a category based on a link
* Added Bosnian translation
Improvements:
* Dropdown menu entry are now clickable outside of the html link
* Improve error handling of plugins
* Use PHP7 function random_bytes() to generate tokens if available
* CSV task export show the assignee name in addition to the assignee username
* Add new hooks for plugins
* Remove workaround for "INSERT ON DUPLICATE KEY UPDATE..."
Internal code refactoring:
* Rewrite of session management
* Move some classes to a new namespace Kanboard\Core\Http
Bug fixes:
* Loading cs_CZ locale display the wrong language in datetime picker
* Datepicker is closed unexpectedly on blur event
* Fix bug in daily project summary CSV export
* Fix PHP error when adding a new user with email notification enabled
* Add missing template for activity stream to show event "file.create"
* Fix wrong value for PLUGINS_DIR in config.default.php
* Make CSV export compatible with PHP 5.3
* Avoid Safari to append .html at the end of downloaded files
Version 1.0.20
--------------
Breaking changes:
- Add namespace Kanboard (update your plugins)
- Move Mailgun, Sendgrid, Postmark, Slack, Hipchat and Jabber to plugins
- ReverseProxy authentication check for each request that the username match the user session
* Add namespace Kanboard (update your plugins)
* Move Mailgun, Sendgrid, Postmark, Slack, Hipchat and Jabber to plugins
* ReverseProxy authentication check for each request that the username match the user session
New features:

View file

@ -241,7 +241,7 @@ abstract class Base extends \Kanboard\Core\Base
}
if (DEBUG) {
$this->container['logger']->debug(get_called_class().' => '.($result ? 'true' : 'false'));
$this->logger->debug(get_called_class().' => '.($result ? 'true' : 'false'));
}
return $result;

View file

@ -0,0 +1,90 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskLink;
/**
* Set a category automatically according to a task link
*
* @package action
* @author Olivier Maridat
* @author Frederic Guillot
*/
class TaskAssignCategoryLink extends Base
{
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskLink::EVENT_CREATE_UPDATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'category_id' => t('Category'),
'link_id' => t('Link type'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'link_id',
);
}
/**
* Execute the action (change the category)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'category_id' => isset($data['category_id']) ? $data['category_id'] : $this->getParam('category_id'),
);
return $this->taskModification->update($values);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
if ($data['link_id'] == $this->getParam('link_id')) {
$task = $this->taskFinder->getById($data['task_id']);
return empty($task['category_id']);
}
return false;
}
}

View file

@ -28,7 +28,7 @@ class Auth extends Base
if ($username !== 'jsonrpc' && ! $this->authentication->hasCaptcha($username) && $this->authentication->authenticate($username, $password)) {
$this->checkProcedurePermission(true, $method);
$this->userSession->refresh($this->user->getByUsername($username));
$this->userSession->initialize($this->user->getByUsername($username));
} elseif ($username === 'jsonrpc' && $password === $this->config->get('api_token')) {
$this->checkProcedurePermission(false, $method);
} else {

View file

@ -14,7 +14,7 @@ class Me extends Base
{
public function getMe()
{
return $this->session['user'];
return $this->sessionStorage->user;
}
public function getMyDashboard()

View file

@ -39,7 +39,7 @@ class Database extends Base
->findOne();
if (is_array($user) && password_verify($password, $user['password'])) {
$this->userSession->refresh($user);
$this->userSession->initialize($user);
$this->container['dispatcher']->dispatch('auth.success', new AuthEvent(self::AUTH_NAME, $user['id']));
return true;
}

View file

@ -39,7 +39,7 @@ class Github extends Base
$user = $this->user->getByGithubId($github_id);
if (! empty($user)) {
$this->userSession->refresh($user);
$this->userSession->initialize($user);
$this->container['dispatcher']->dispatch('auth.success', new AuthEvent(self::AUTH_NAME, $user['id']));
return true;
}

View file

@ -39,7 +39,7 @@ class Gitlab extends Base
$user = $this->user->getByGitlabId($gitlab_id);
if (! empty($user)) {
$this->userSession->refresh($user);
$this->userSession->initialize($user);
$this->container['dispatcher']->dispatch('auth.success', new AuthEvent(self::AUTH_NAME, $user['id']));
return true;
}

View file

@ -40,7 +40,7 @@ class Google extends Base
$user = $this->user->getByGoogleId($google_id);
if (! empty($user)) {
$this->userSession->refresh($user);
$this->userSession->initialize($user);
$this->container['dispatcher']->dispatch('auth.success', new AuthEvent(self::AUTH_NAME, $user['id']));
return true;
}

View file

@ -237,7 +237,7 @@ class Ldap extends Base
}
// We open the session
$this->userSession->refresh($user);
$this->userSession->initialize($user);
$this->container['dispatcher']->dispatch('auth.success', new AuthEvent(self::AUTH_NAME, $user['id']));
return true;

View file

@ -3,9 +3,9 @@
namespace Kanboard\Auth;
use Kanboard\Core\Base;
use Kanboard\Core\Request;
use Kanboard\Core\Http\Request;
use Kanboard\Event\AuthEvent;
use Kanboard\Core\Security;
use Kanboard\Core\Security\Token;
/**
* RememberMe model
@ -101,10 +101,10 @@ class RememberMe extends Base
);
// Create the session
$this->userSession->refresh($this->user->getById($record['user_id']));
$this->userSession->initialize($this->user->getById($record['user_id']));
// Do not ask 2FA for remember me session
$this->session['2fa_validated'] = true;
$this->sessionStorage->postAuth['validated'] = true;
$this->container['dispatcher']->dispatch(
'auth.success',
@ -165,8 +165,8 @@ class RememberMe extends Base
*/
public function create($user_id, $ip, $user_agent)
{
$token = hash('sha256', $user_id.$user_agent.$ip.Security::generateToken());
$sequence = Security::generateToken();
$token = hash('sha256', $user_id.$user_agent.$ip.Token::getToken());
$sequence = Token::getToken();
$expiration = time() + self::EXPIRATION;
$this->cleanup($user_id);
@ -216,7 +216,7 @@ class RememberMe extends Base
*/
public function update($token)
{
$new_sequence = Security::generateToken();
$new_sequence = Token::getToken();
$this->db
->table(self::TABLE)

View file

@ -48,7 +48,7 @@ class ReverseProxy extends Base
$user = $this->user->getByUsername($login);
}
$this->userSession->refresh($user);
$this->userSession->initialize($user);
$this->container['dispatcher']->dispatch('auth.success', new AuthEvent(self::AUTH_NAME, $user['id']));
return true;

View file

@ -119,9 +119,9 @@ class Action extends Base
if ($valid) {
if ($this->action->create($values) !== false) {
$this->session->flash(t('Your automatic action have been created successfully.'));
$this->flash->success(t('Your automatic action have been created successfully.'));
} else {
$this->session->flashError(t('Unable to create your automatic action.'));
$this->flash->failure(t('Unable to create your automatic action.'));
}
}
@ -158,9 +158,9 @@ class Action extends Base
$action = $this->action->getById($this->request->getIntegerParam('action_id'));
if (! empty($action) && $this->action->remove($action['id'])) {
$this->session->flash(t('Action removed successfully.'));
$this->flash->success(t('Action removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this action.'));
$this->flash->failure(t('Unable to remove this action.'));
}
$this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id'])));

View file

@ -43,9 +43,11 @@ class Auth extends Base
list($valid, $errors) = $this->authentication->validateForm($values);
if ($valid) {
if (! empty($this->session['login_redirect']) && ! filter_var($this->session['login_redirect'], FILTER_VALIDATE_URL)) {
$redirect = $this->session['login_redirect'];
unset($this->session['login_redirect']);
if (isset($this->sessionStorage->redirectAfterLogin)
&& ! empty($this->sessionStorage->redirectAfterLogin)
&& ! filter_var($this->sessionStorage->redirectAfterLogin, FILTER_VALIDATE_URL)) {
$redirect = $this->sessionStorage->redirectAfterLogin;
unset($this->sessionStorage->redirectAfterLogin);
$this->response->redirect($redirect);
}
@ -63,7 +65,7 @@ class Auth extends Base
public function logout()
{
$this->authentication->backend('rememberMe')->destroy($this->userSession->getId());
$this->session->close();
$this->sessionManager->close();
$this->response->redirect($this->helper->url->to('auth', 'login'));
}
@ -78,7 +80,7 @@ class Auth extends Base
$builder = new CaptchaBuilder;
$builder->build();
$this->session['captcha'] = $builder->getPhrase();
$this->sessionStorage->captcha = $builder->getPhrase();
$builder->output();
}
}

View file

@ -3,9 +3,6 @@
namespace Kanboard\Controller;
use Pimple\Container;
use Kanboard\Core\Security;
use Kanboard\Core\Request;
use Kanboard\Core\Response;
use Symfony\Component\EventDispatcher\Event;
/**
@ -16,22 +13,6 @@ use Symfony\Component\EventDispatcher\Event;
*/
abstract class Base extends \Kanboard\Core\Base
{
/**
* Request instance
*
* @accesss protected
* @var \Kanboard\Core\Request
*/
protected $request;
/**
* Response instance
*
* @accesss protected
* @var \Kanboard\Core\Response
*/
protected $response;
/**
* Constructor
*
@ -41,11 +22,9 @@ abstract class Base extends \Kanboard\Core\Base
public function __construct(Container $container)
{
$this->container = $container;
$this->request = new Request;
$this->response = new Response;
if (DEBUG) {
$this->container['logger']->debug('START_REQUEST='.$_SERVER['REQUEST_URI']);
$this->logger->debug('START_REQUEST='.$_SERVER['REQUEST_URI']);
}
}
@ -57,14 +36,14 @@ abstract class Base extends \Kanboard\Core\Base
public function __destruct()
{
if (DEBUG) {
foreach ($this->container['db']->getLogMessages() as $message) {
$this->container['logger']->debug($message);
foreach ($this->db->getLogMessages() as $message) {
$this->logger->debug($message);
}
$this->container['logger']->debug('SQL_QUERIES={nb}', array('nb' => $this->container['db']->nbQueries));
$this->container['logger']->debug('RENDERING={time}', array('time' => microtime(true) - @$_SERVER['REQUEST_TIME_FLOAT']));
$this->container['logger']->debug('MEMORY='.$this->helper->text->bytes(memory_get_usage()));
$this->container['logger']->debug('END_REQUEST='.$_SERVER['REQUEST_URI']);
$this->logger->debug('SQL_QUERIES={nb}', array('nb' => $this->container['db']->nbQueries));
$this->logger->debug('RENDERING={time}', array('time' => microtime(true) - @$_SERVER['REQUEST_TIME_FLOAT']));
$this->logger->debug('MEMORY='.$this->helper->text->bytes(memory_get_usage()));
$this->logger->debug('END_REQUEST='.$_SERVER['REQUEST_URI']);
}
}
@ -97,8 +76,7 @@ abstract class Base extends \Kanboard\Core\Base
*/
public function beforeAction($controller, $action)
{
// Start the session
$this->session->open($this->helper->url->dir());
$this->sessionManager->open();
$this->sendHeaders($action);
$this->container['dispatcher']->dispatch('session.bootstrap', new Event);
@ -107,7 +85,7 @@ abstract class Base extends \Kanboard\Core\Base
$this->handle2FA($controller, $action);
$this->handleAuthorization($controller, $action);
$this->session['has_subtask_inprogress'] = $this->subtask->hasSubtaskInProgress($this->userSession->getId());
$this->sessionStorage->hasSubtaskInProgress = $this->subtask->hasSubtaskInProgress($this->userSession->getId());
}
}
@ -123,7 +101,7 @@ abstract class Base extends \Kanboard\Core\Base
$this->response->text('Not Authorized', 401);
}
$this->session['login_redirect'] = $this->request->getUri();
$this->sessionStorage->redirectAfterLogin = $this->request->getUri();
$this->response->redirect($this->helper->url->to('auth', 'login'));
}
}
@ -201,7 +179,7 @@ abstract class Base extends \Kanboard\Core\Base
*/
protected function checkCSRFParam()
{
if (! Security::validateCSRFToken($this->request->getStringParam('csrf_token'))) {
if (! $this->token->validateCSRFToken($this->request->getStringParam('csrf_token'))) {
$this->forbidden();
}
}
@ -290,7 +268,7 @@ abstract class Base extends \Kanboard\Core\Base
$project = $this->project->getById($project_id);
if (empty($project)) {
$this->session->flashError(t('Project not found.'));
$this->flash->failure(t('Project not found.'));
$this->response->redirect($this->helper->url->to('project', 'index'));
}

View file

@ -242,9 +242,9 @@ class Board extends Base
list($valid, ) = $this->taskValidator->validateAssigneeModification($values);
if ($valid && $this->taskModification->update($values)) {
$this->session->flash(t('Task updated successfully.'));
$this->flash->success(t('Task updated successfully.'));
} else {
$this->session->flashError(t('Unable to update your task.'));
$this->flash->failure(t('Unable to update your task.'));
}
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $values['project_id'])));
@ -279,9 +279,9 @@ class Board extends Base
list($valid, ) = $this->taskValidator->validateCategoryModification($values);
if ($valid && $this->taskModification->update($values)) {
$this->session->flash(t('Task updated successfully.'));
$this->flash->success(t('Task updated successfully.'));
} else {
$this->session->flashError(t('Unable to update your task.'));
$this->flash->failure(t('Unable to update your task.'));
}
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $values['project_id'])));

View file

@ -22,7 +22,7 @@ class Category extends Base
$category = $this->category->getById($this->request->getIntegerParam('category_id'));
if (empty($category)) {
$this->session->flashError(t('Category not found.'));
$this->flash->failure(t('Category not found.'));
$this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project_id)));
}
@ -61,10 +61,10 @@ class Category extends Base
if ($valid) {
if ($this->category->create($values)) {
$this->session->flash(t('Your category have been created successfully.'));
$this->flash->success(t('Your category have been created successfully.'));
$this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id'])));
} else {
$this->session->flashError(t('Unable to create your category.'));
$this->flash->failure(t('Unable to create your category.'));
}
}
@ -103,10 +103,10 @@ class Category extends Base
if ($valid) {
if ($this->category->update($values)) {
$this->session->flash(t('Your category have been updated successfully.'));
$this->flash->success(t('Your category have been updated successfully.'));
$this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id'])));
} else {
$this->session->flashError(t('Unable to update your category.'));
$this->flash->failure(t('Unable to update your category.'));
}
}
@ -142,9 +142,9 @@ class Category extends Base
$category = $this->getCategory($project['id']);
if ($this->category->remove($category['id'])) {
$this->session->flash(t('Category removed successfully.'));
$this->flash->success(t('Category removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this category.'));
$this->flash->failure(t('Unable to remove this category.'));
}
$this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id'])));

View file

@ -55,10 +55,10 @@ class Column extends Base
if ($valid) {
if ($this->board->addColumn($project['id'], $data['title'], $data['task_limit'], $data['description'])) {
$this->session->flash(t('Board updated successfully.'));
$this->flash->success(t('Board updated successfully.'));
$this->response->redirect($this->helper->url->to('column', 'index', array('project_id' => $project['id'])));
} else {
$this->session->flashError(t('Unable to update this board.'));
$this->flash->failure(t('Unable to update this board.'));
}
}
@ -98,10 +98,10 @@ class Column extends Base
if ($valid) {
if ($this->board->updateColumn($values['id'], $values['title'], $values['task_limit'], $values['description'])) {
$this->session->flash(t('Board updated successfully.'));
$this->flash->success(t('Board updated successfully.'));
$this->response->redirect($this->helper->url->to('column', 'index', array('project_id' => $project['id'])));
} else {
$this->session->flashError(t('Unable to update this board.'));
$this->flash->failure(t('Unable to update this board.'));
}
}
@ -155,9 +155,9 @@ class Column extends Base
$column = $this->board->getColumn($this->request->getIntegerParam('column_id'));
if (! empty($column) && $this->board->removeColumn($column['id'])) {
$this->session->flash(t('Column removed successfully.'));
$this->flash->success(t('Column removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this column.'));
$this->flash->failure(t('Unable to remove this column.'));
}
$this->response->redirect($this->helper->url->to('column', 'index', array('project_id' => $project['id'])));

View file

@ -82,9 +82,9 @@ class Comment extends Base
if ($valid) {
if ($this->comment->create($values)) {
$this->session->flash(t('Comment added successfully.'));
$this->flash->success(t('Comment added successfully.'));
} else {
$this->session->flashError(t('Unable to create your comment.'));
$this->flash->failure(t('Unable to create your comment.'));
}
if ($ajax) {
@ -131,9 +131,9 @@ class Comment extends Base
if ($valid) {
if ($this->comment->update($values)) {
$this->session->flash(t('Comment updated successfully.'));
$this->flash->success(t('Comment updated successfully.'));
} else {
$this->session->flashError(t('Unable to update your comment.'));
$this->flash->failure(t('Unable to update your comment.'));
}
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comment-'.$comment['id']));
@ -171,9 +171,9 @@ class Comment extends Base
$comment = $this->getComment();
if ($this->comment->remove($comment['id'])) {
$this->session->flash(t('Comment removed successfully.'));
$this->flash->success(t('Comment removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this comment.'));
$this->flash->failure(t('Unable to remove this comment.'));
}
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'));

View file

@ -53,9 +53,9 @@ class Config extends Base
if ($this->config->save($values)) {
$this->config->reload();
$this->session->flash(t('Settings saved successfully.'));
$this->flash->success(t('Settings saved successfully.'));
} else {
$this->session->flashError(t('Unable to save your settings.'));
$this->flash->failure(t('Unable to save your settings.'));
}
$this->response->redirect($this->helper->url->to('config', $redirect));
@ -210,7 +210,7 @@ class Config extends Base
{
$this->checkCSRFParam();
$this->config->optimizeDatabase();
$this->session->flash(t('Database optimization done.'));
$this->flash->success(t('Database optimization done.'));
$this->response->redirect($this->helper->url->to('config', 'index'));
}
@ -226,7 +226,7 @@ class Config extends Base
$this->checkCSRFParam();
$this->config->regenerateToken($type.'_token');
$this->session->flash(t('Token regenerated.'));
$this->flash->success(t('Token regenerated.'));
$this->response->redirect($this->helper->url->to('config', $type));
}
}

View file

@ -55,10 +55,10 @@ class Currency extends Base
if ($valid) {
if ($this->currency->create($values['currency'], $values['rate'])) {
$this->session->flash(t('The currency rate have been added successfully.'));
$this->flash->success(t('The currency rate have been added successfully.'));
$this->response->redirect($this->helper->url->to('currency', 'index'));
} else {
$this->session->flashError(t('Unable to add this currency rate.'));
$this->flash->failure(t('Unable to add this currency rate.'));
}
}
@ -76,9 +76,9 @@ class Currency extends Base
if ($this->config->save($values)) {
$this->config->reload();
$this->session->flash(t('Settings saved successfully.'));
$this->flash->success(t('Settings saved successfully.'));
} else {
$this->session->flashError(t('Unable to save your settings.'));
$this->flash->failure(t('Unable to save your settings.'));
}
$this->response->redirect($this->helper->url->to('currency', 'index'));

View file

@ -44,10 +44,10 @@ class Customfilter extends Base
if ($valid) {
if ($this->customFilter->create($values)) {
$this->session->flash(t('Your custom filter have been created successfully.'));
$this->flash->success(t('Your custom filter have been created successfully.'));
$this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
} else {
$this->session->flashError(t('Unable to create your custom filter.'));
$this->flash->failure(t('Unable to create your custom filter.'));
}
}
@ -68,9 +68,9 @@ class Customfilter extends Base
$this->checkPermission($project, $filter);
if ($this->customFilter->remove($filter['id'])) {
$this->session->flash(t('Custom filter removed successfully.'));
$this->flash->success(t('Custom filter removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this custom filter.'));
$this->flash->failure(t('Unable to remove this custom filter.'));
}
$this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
@ -123,10 +123,10 @@ class Customfilter extends Base
if ($valid) {
if ($this->customFilter->update($values)) {
$this->session->flash(t('Your custom filter have been updated successfully.'));
$this->flash->success(t('Your custom filter have been updated successfully.'));
$this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
} else {
$this->session->flashError(t('Unable to update custom filter.'));
$this->flash->failure(t('Unable to update custom filter.'));
}
}

View file

@ -70,7 +70,7 @@ class Export extends Base
*/
public function summary()
{
$this->common('ProjectDailyColumnStats', 'getAggregatedMetrics', t('Summary'), 'summary', t('Daily project summary export'));
$this->common('projectDailyColumnStats', 'getAggregatedMetrics', t('Summary'), 'summary', t('Daily project summary export'));
}
/**

View file

@ -22,7 +22,7 @@ class File extends Base
$task = $this->getTask();
if ($this->request->isPost() && $this->file->uploadScreenshot($task['project_id'], $task['id'], $this->request->getValue('screenshot')) !== false) {
$this->session->flash(t('Screenshot uploaded successfully.'));
$this->flash->success(t('Screenshot uploaded successfully.'));
if ($this->request->getStringParam('redirect') === 'board') {
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
@ -62,7 +62,7 @@ class File extends Base
$task = $this->getTask();
if (! $this->file->uploadFiles($task['project_id'], $task['id'], 'files')) {
$this->session->flashError(t('Unable to upload the file.'));
$this->flash->failure(t('Unable to upload the file.'));
}
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
@ -166,9 +166,9 @@ class File extends Base
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
if ($file['task_id'] == $task['id'] && $this->file->remove($file['id'])) {
$this->session->flash(t('File removed successfully.'));
$this->flash->success(t('File removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this file.'));
$this->flash->failure(t('Unable to remove this file.'));
}
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));

View file

@ -135,10 +135,10 @@ class Gantt extends Base
$task_id = $this->taskCreation->create($values);
if ($task_id !== false) {
$this->session->flash(t('Task created successfully.'));
$this->flash->success(t('Task created successfully.'));
$this->response->redirect($this->helper->url->to('gantt', 'project', array('project_id' => $project['id'])));
} else {
$this->session->flashError(t('Unable to create your task.'));
$this->flash->failure(t('Unable to create your task.'));
}
}

View file

@ -71,10 +71,10 @@ class Link extends Base
if ($valid) {
if ($this->link->create($values['label'], $values['opposite_label']) !== false) {
$this->session->flash(t('Link added successfully.'));
$this->flash->success(t('Link added successfully.'));
$this->response->redirect($this->helper->url->to('link', 'index'));
} else {
$this->session->flashError(t('Unable to create your link.'));
$this->flash->failure(t('Unable to create your link.'));
}
}
@ -112,10 +112,10 @@ class Link extends Base
if ($valid) {
if ($this->link->update($values)) {
$this->session->flash(t('Link updated successfully.'));
$this->flash->success(t('Link updated successfully.'));
$this->response->redirect($this->helper->url->to('link', 'index'));
} else {
$this->session->flashError(t('Unable to update your link.'));
$this->flash->failure(t('Unable to update your link.'));
}
}
@ -148,9 +148,9 @@ class Link extends Base
$link = $this->getLink();
if ($this->link->remove($link['id'])) {
$this->session->flash(t('Link removed successfully.'));
$this->flash->success(t('Link removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this link.'));
$this->flash->failure(t('Unable to remove this link.'));
}
$this->response->redirect($this->helper->url->to('link', 'index'));

View file

@ -51,9 +51,9 @@ class Oauth extends Base
$this->checkCSRFParam();
if ($this->authentication->backend($backend)->unlink($this->userSession->getId())) {
$this->session->flash(t('Your external account is not linked anymore to your profile.'));
$this->flash->success(t('Your external account is not linked anymore to your profile.'));
} else {
$this->session->flashError(t('Unable to unlink your external account.'));
$this->flash->failure(t('Unable to unlink your external account.'));
}
$this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId())));
@ -99,9 +99,9 @@ class Oauth extends Base
private function link($backend, $profile)
{
if (empty($profile)) {
$this->session->flashError(t('External authentication failed'));
$this->flash->failure(t('External authentication failed'));
} else {
$this->session->flash(t('Your external account is linked to your profile successfully.'));
$this->flash->success(t('Your external account is linked to your profile successfully.'));
$this->authentication->backend($backend)->updateUser($this->userSession->getId(), $profile);
}

View file

@ -70,9 +70,9 @@ class Project extends Base
$this->checkCSRFParam();
if ($this->project->{$switch.'PublicAccess'}($project['id'])) {
$this->session->flash(t('Project updated successfully.'));
$this->flash->success(t('Project updated successfully.'));
} else {
$this->session->flashError(t('Unable to update this project.'));
$this->flash->failure(t('Unable to update this project.'));
}
$this->response->redirect($this->helper->url->to('project', 'share', array('project_id' => $project['id'])));
@ -95,7 +95,7 @@ class Project extends Base
if ($this->request->isPost()) {
$this->projectMetadata->save($project['id'], $this->request->getValues());
$this->session->flash(t('Project updated successfully.'));
$this->flash->success(t('Project updated successfully.'));
$this->response->redirect($this->helper->url->to('project', 'integrations', array('project_id' => $project['id'])));
}
@ -120,7 +120,7 @@ class Project extends Base
if ($this->request->isPost()) {
$values = $this->request->getValues();
$this->projectNotification->saveSettings($project['id'], $values);
$this->session->flash(t('Project updated successfully.'));
$this->flash->success(t('Project updated successfully.'));
$this->response->redirect($this->helper->url->to('project', 'notifications', array('project_id' => $project['id'])));
}
@ -173,10 +173,10 @@ class Project extends Base
if ($valid) {
if ($this->project->update($values)) {
$this->session->flash(t('Project updated successfully.'));
$this->flash->success(t('Project updated successfully.'));
$this->response->redirect($this->helper->url->to('project', 'edit', array('project_id' => $project['id'])));
} else {
$this->session->flashError(t('Unable to update this project.'));
$this->flash->failure(t('Unable to update this project.'));
}
}
@ -212,9 +212,9 @@ class Project extends Base
if ($valid) {
if ($this->project->update($values)) {
$this->session->flash(t('Project updated successfully.'));
$this->flash->success(t('Project updated successfully.'));
} else {
$this->session->flashError(t('Unable to update this project.'));
$this->flash->failure(t('Unable to update this project.'));
}
}
@ -233,9 +233,9 @@ class Project extends Base
if ($valid) {
if ($this->projectPermission->addMember($values['project_id'], $values['user_id'])) {
$this->session->flash(t('Project updated successfully.'));
$this->flash->success(t('Project updated successfully.'));
} else {
$this->session->flashError(t('Unable to update this project.'));
$this->flash->failure(t('Unable to update this project.'));
}
}
@ -261,9 +261,9 @@ class Project extends Base
if ($valid) {
if ($this->projectPermission->changeRole($values['project_id'], $values['user_id'], $values['is_owner'])) {
$this->session->flash(t('Project updated successfully.'));
$this->flash->success(t('Project updated successfully.'));
} else {
$this->session->flashError(t('Unable to update this project.'));
$this->flash->failure(t('Unable to update this project.'));
}
}
@ -288,9 +288,9 @@ class Project extends Base
if ($valid) {
if ($this->projectPermission->revokeMember($values['project_id'], $values['user_id'])) {
$this->session->flash(t('Project updated successfully.'));
$this->flash->success(t('Project updated successfully.'));
} else {
$this->session->flashError(t('Unable to update this project.'));
$this->flash->failure(t('Unable to update this project.'));
}
}
@ -310,9 +310,9 @@ class Project extends Base
$this->checkCSRFParam();
if ($this->project->remove($project['id'])) {
$this->session->flash(t('Project removed successfully.'));
$this->flash->success(t('Project removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this project.'));
$this->flash->failure(t('Unable to remove this project.'));
}
$this->response->redirect($this->helper->url->to('project', 'index'));
@ -338,9 +338,9 @@ class Project extends Base
if ($this->request->getStringParam('duplicate') === 'yes') {
$values = array_keys($this->request->getValues());
if ($this->projectDuplication->duplicate($project['id'], $values) !== false) {
$this->session->flash(t('Project cloned successfully.'));
$this->flash->success(t('Project cloned successfully.'));
} else {
$this->session->flashError(t('Unable to clone this project.'));
$this->flash->failure(t('Unable to clone this project.'));
}
$this->response->redirect($this->helper->url->to('project', 'index'));
@ -365,9 +365,9 @@ class Project extends Base
$this->checkCSRFParam();
if ($this->project->disable($project['id'])) {
$this->session->flash(t('Project disabled successfully.'));
$this->flash->success(t('Project disabled successfully.'));
} else {
$this->session->flashError(t('Unable to disable this project.'));
$this->flash->failure(t('Unable to disable this project.'));
}
$this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id'])));
@ -392,9 +392,9 @@ class Project extends Base
$this->checkCSRFParam();
if ($this->project->enable($project['id'])) {
$this->session->flash(t('Project activated successfully.'));
$this->flash->success(t('Project activated successfully.'));
} else {
$this->session->flashError(t('Unable to activate this project.'));
$this->flash->failure(t('Unable to activate this project.'));
}
$this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id'])));
@ -438,11 +438,11 @@ class Project extends Base
$project_id = $this->project->create($values, $this->userSession->getId(), true);
if ($project_id > 0) {
$this->session->flash(t('Your project have been created successfully.'));
$this->flash->success(t('Your project have been created successfully.'));
$this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id)));
}
$this->session->flashError(t('Unable to create your project.'));
$this->flash->failure(t('Unable to create your project.'));
}
$this->create($values, $errors);

View file

@ -67,9 +67,9 @@ class Subtask extends Base
if ($valid) {
if ($this->subtask->create($values)) {
$this->session->flash(t('Sub-task added successfully.'));
$this->flash->success(t('Sub-task added successfully.'));
} else {
$this->session->flashError(t('Unable to create your sub-task.'));
$this->flash->failure(t('Unable to create your sub-task.'));
}
if (isset($values['another_subtask']) && $values['another_subtask'] == 1) {
@ -117,9 +117,9 @@ class Subtask extends Base
if ($valid) {
if ($this->subtask->update($values)) {
$this->session->flash(t('Sub-task updated successfully.'));
$this->flash->success(t('Sub-task updated successfully.'));
} else {
$this->session->flashError(t('Unable to update your sub-task.'));
$this->flash->failure(t('Unable to update your sub-task.'));
}
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'));
@ -156,9 +156,9 @@ class Subtask extends Base
$subtask = $this->getSubtask();
if ($this->subtask->remove($subtask['id'])) {
$this->session->flash(t('Sub-task removed successfully.'));
$this->flash->success(t('Sub-task removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this sub-task.'));
$this->flash->failure(t('Unable to remove this sub-task.'));
}
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'));
@ -178,7 +178,7 @@ class Subtask extends Base
$this->subtask->toggleStatus($subtask['id']);
if ($redirect === 'board') {
$this->session['has_subtask_inprogress'] = $this->subtask->hasSubtaskInProgress($this->userSession->getId());
$this->sessionStorage->hasSubtaskInProgress = $this->subtask->hasSubtaskInProgress($this->userSession->getId());
$this->response->html($this->template->render('board/tooltip_subtasks', array(
'subtasks' => $this->subtask->getAll($task['id']),

View file

@ -24,7 +24,7 @@ class Swimlane extends Base
$swimlane = $this->swimlane->getById($this->request->getIntegerParam('swimlane_id'));
if (empty($swimlane)) {
$this->session->flashError(t('Swimlane not found.'));
$this->flash->failure(t('Swimlane not found.'));
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project_id)));
}
@ -64,10 +64,10 @@ class Swimlane extends Base
if ($valid) {
if ($this->swimlane->create($values)) {
$this->session->flash(t('Your swimlane have been created successfully.'));
$this->flash->success(t('Your swimlane have been created successfully.'));
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
} else {
$this->session->flashError(t('Unable to create your swimlane.'));
$this->flash->failure(t('Unable to create your swimlane.'));
}
}
@ -88,10 +88,10 @@ class Swimlane extends Base
if ($valid) {
if ($this->swimlane->updateDefault($values)) {
$this->session->flash(t('The default swimlane have been updated successfully.'));
$this->flash->success(t('The default swimlane have been updated successfully.'));
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
} else {
$this->session->flashError(t('Unable to update this swimlane.'));
$this->flash->failure(t('Unable to update this swimlane.'));
}
}
@ -130,10 +130,10 @@ class Swimlane extends Base
if ($valid) {
if ($this->swimlane->update($values)) {
$this->session->flash(t('Swimlane updated successfully.'));
$this->flash->success(t('Swimlane updated successfully.'));
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
} else {
$this->session->flashError(t('Unable to update this swimlane.'));
$this->flash->failure(t('Unable to update this swimlane.'));
}
}
@ -169,9 +169,9 @@ class Swimlane extends Base
$swimlane_id = $this->request->getIntegerParam('swimlane_id');
if ($this->swimlane->remove($project['id'], $swimlane_id)) {
$this->session->flash(t('Swimlane removed successfully.'));
$this->flash->success(t('Swimlane removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this swimlane.'));
$this->flash->failure(t('Unable to remove this swimlane.'));
}
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
@ -189,9 +189,9 @@ class Swimlane extends Base
$swimlane_id = $this->request->getIntegerParam('swimlane_id');
if ($this->swimlane->disable($project['id'], $swimlane_id)) {
$this->session->flash(t('Swimlane updated successfully.'));
$this->flash->success(t('Swimlane updated successfully.'));
} else {
$this->session->flashError(t('Unable to update this swimlane.'));
$this->flash->failure(t('Unable to update this swimlane.'));
}
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
@ -209,9 +209,9 @@ class Swimlane extends Base
$swimlane_id = $this->request->getIntegerParam('swimlane_id');
if ($this->swimlane->enable($project['id'], $swimlane_id)) {
$this->session->flash(t('Swimlane updated successfully.'));
$this->flash->success(t('Swimlane updated successfully.'));
} else {
$this->session->flashError(t('Unable to update this swimlane.'));
$this->flash->failure(t('Unable to update this swimlane.'));
}
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));

View file

@ -159,9 +159,9 @@ class Task extends Base
$this->checkCSRFParam();
if ($this->task->remove($task['id'])) {
$this->session->flash(t('Task removed successfully.'));
$this->flash->success(t('Task removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this task.'));
$this->flash->failure(t('Unable to remove this task.'));
}
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));

View file

@ -52,9 +52,9 @@ class TaskImport extends Base
$csv->read($filename, array($this->taskImport, 'import'));
if ($this->taskImport->counter > 0) {
$this->session->flash(t('%d task(s) have been imported successfully.', $this->taskImport->counter));
$this->flash->success(t('%d task(s) have been imported successfully.', $this->taskImport->counter));
} else {
$this->session->flashError(t('Nothing have been imported!'));
$this->flash->failure(t('Nothing have been imported!'));
}
$this->response->redirect($this->helper->url->to('taskImport', 'step1', array('project_id' => $project['id'])));

View file

@ -59,10 +59,10 @@ class Taskcreation extends Base
list($valid, $errors) = $this->taskValidator->validateCreation($values);
if ($valid && $this->taskCreation->create($values)) {
$this->session->flash(t('Task created successfully.'));
$this->flash->success(t('Task created successfully.'));
$this->afterSave($project, $values);
} else {
$this->session->flashError(t('Unable to create your task.'));
$this->flash->failure(t('Unable to create your task.'));
}
$this->create($values, $errors);

View file

@ -24,10 +24,10 @@ class Taskduplication extends Base
$task_id = $this->taskDuplication->duplicate($task['id']);
if ($task_id > 0) {
$this->session->flash(t('Task created successfully.'));
$this->flash->success(t('Task created successfully.'));
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task_id)));
} else {
$this->session->flashError(t('Unable to create this task.'));
$this->flash->failure(t('Unable to create this task.'));
$this->response->redirect($this->helper->url->to('taskduplication', 'duplicate', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
}
}
@ -56,11 +56,11 @@ class Taskduplication extends Base
$values['column_id'],
$values['category_id'],
$values['owner_id'])) {
$this->session->flash(t('Task updated successfully.'));
$this->flash->success(t('Task updated successfully.'));
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $values['project_id'], 'task_id' => $task['id'])));
}
$this->session->flashError(t('Unable to update your task.'));
$this->flash->failure(t('Unable to update your task.'));
}
$this->chooseDestination($task, 'task_duplication/move');
@ -86,12 +86,12 @@ class Taskduplication extends Base
);
if ($task_id > 0) {
$this->session->flash(t('Task created successfully.'));
$this->flash->success(t('Task created successfully.'));
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $values['project_id'], 'task_id' => $task_id)));
}
}
$this->session->flashError(t('Unable to create your task.'));
$this->flash->failure(t('Unable to create your task.'));
}
$this->chooseDestination($task, 'task_duplication/copy');

View file

@ -73,7 +73,7 @@ class Tasklink extends Base
if ($valid) {
if ($this->taskLink->create($values['task_id'], $values['opposite_task_id'], $values['link_id'])) {
$this->session->flash(t('Link added successfully.'));
$this->flash->success(t('Link added successfully.'));
if ($ajax) {
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
@ -83,7 +83,7 @@ class Tasklink extends Base
}
$errors = array('title' => array(t('The exact same link already exists')));
$this->session->flashError(t('Unable to create your link.'));
$this->flash->failure(t('Unable to create your link.'));
}
$this->create($values, $errors);
@ -129,11 +129,11 @@ class Tasklink extends Base
if ($valid) {
if ($this->taskLink->update($values['id'], $values['task_id'], $values['opposite_task_id'], $values['link_id'])) {
$this->session->flash(t('Link updated successfully.'));
$this->flash->success(t('Link updated successfully.'));
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');
}
$this->session->flashError(t('Unable to update your link.'));
$this->flash->failure(t('Unable to update your link.'));
}
$this->edit($values, $errors);
@ -166,9 +166,9 @@ class Tasklink extends Base
$task = $this->getTask();
if ($this->taskLink->remove($this->request->getIntegerParam('link_id'))) {
$this->session->flash(t('Link removed successfully.'));
$this->flash->success(t('Link removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this link.'));
$this->flash->failure(t('Unable to remove this link.'));
}
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');

View file

@ -35,9 +35,9 @@ class Taskmodification extends Base
list($valid, ) = $this->taskValidator->validateTimeModification($values);
if ($valid && $this->taskModification->update($values)) {
$this->session->flash(t('Task updated successfully.'));
$this->flash->success(t('Task updated successfully.'));
} else {
$this->session->flashError(t('Unable to update your task.'));
$this->flash->failure(t('Unable to update your task.'));
}
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
@ -60,9 +60,9 @@ class Taskmodification extends Base
if ($valid) {
if ($this->taskModification->update($values)) {
$this->session->flash(t('Task updated successfully.'));
$this->flash->success(t('Task updated successfully.'));
} else {
$this->session->flashError(t('Unable to update your task.'));
$this->flash->failure(t('Unable to update your task.'));
}
if ($ajax) {
@ -140,7 +140,7 @@ class Taskmodification extends Base
list($valid, $errors) = $this->taskValidator->validateModification($values);
if ($valid && $this->taskModification->update($values)) {
$this->session->flash(t('Task updated successfully.'));
$this->flash->success(t('Task updated successfully.'));
if ($this->request->isAjax()) {
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
@ -148,7 +148,7 @@ class Taskmodification extends Base
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
}
} else {
$this->session->flashError(t('Unable to update your task.'));
$this->flash->failure(t('Unable to update your task.'));
$this->edit($values, $errors);
}
}
@ -169,9 +169,9 @@ class Taskmodification extends Base
if ($valid) {
if ($this->taskModification->update($values)) {
$this->session->flash(t('Task updated successfully.'));
$this->flash->success(t('Task updated successfully.'));
} else {
$this->session->flashError(t('Unable to update your task.'));
$this->flash->failure(t('Unable to update your task.'));
}
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));

View file

@ -40,9 +40,9 @@ class Taskstatus extends Base
$this->checkCSRFParam();
if ($this->taskStatus->$method($task['id'])) {
$this->session->flash($success_message);
$this->flash->success($success_message);
} else {
$this->session->flashError($failure_message);
$this->flash->failure($failure_message);
}
if ($this->request->getStringParam('redirect') === 'board') {

View file

@ -72,9 +72,9 @@ class Twofactor extends User
}
// Allow the user to test or disable the feature
$_SESSION['user']['twofactor_activated'] = false;
$this->userSession->disable2FA();
$this->session->flash(t('User updated successfully.'));
$this->flash->success(t('User updated successfully.'));
$this->response->redirect($this->helper->url->to('twofactor', 'index', array('user_id' => $user['id'])));
}
@ -92,9 +92,9 @@ class Twofactor extends User
$values = $this->request->getValues();
if (! empty($values['code']) && $otp->checkTotp(Base32::decode($user['twofactor_secret']), $values['code'])) {
$this->session->flash(t('The two factor authentication code is valid.'));
$this->flash->success(t('The two factor authentication code is valid.'));
} else {
$this->session->flashError(t('The two factor authentication code is not valid.'));
$this->flash->failure(t('The two factor authentication code is not valid.'));
}
$this->response->redirect($this->helper->url->to('twofactor', 'index', array('user_id' => $user['id'])));
@ -114,11 +114,11 @@ class Twofactor extends User
$values = $this->request->getValues();
if (! empty($values['code']) && $otp->checkTotp(Base32::decode($user['twofactor_secret']), $values['code'])) {
$this->session['2fa_validated'] = true;
$this->session->flash(t('The two factor authentication code is valid.'));
$this->sessionStorage->postAuth['validated'] = true;
$this->flash->success(t('The two factor authentication code is valid.'));
$this->response->redirect($this->helper->url->to('app', 'index'));
} else {
$this->session->flashError(t('The two factor authentication code is not valid.'));
$this->flash->failure(t('The two factor authentication code is not valid.'));
$this->response->redirect($this->helper->url->to('twofactor', 'code'));
}
}

View file

@ -2,7 +2,7 @@
namespace Kanboard\Controller;
use Kanboard\Model\NotificationType;
use Kanboard\Notification\Mail as MailNotification;
/**
* User controller
@ -95,13 +95,13 @@ class User extends Base
$this->projectPermission->addMember($project_id, $user_id);
if (! empty($values['notifications_enabled'])) {
$this->userNotificationType->saveSelectedTypes($user_id, array(NotificationType::TYPE_EMAIL));
$this->userNotificationType->saveSelectedTypes($user_id, array(MailNotification::TYPE));
}
$this->session->flash(t('User created successfully.'));
$this->flash->success(t('User created successfully.'));
$this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user_id)));
} else {
$this->session->flashError(t('Unable to create your user.'));
$this->flash->failure(t('Unable to create your user.'));
$values['project_id'] = $project_id;
}
}
@ -200,7 +200,7 @@ class User extends Base
if ($this->request->isPost()) {
$values = $this->request->getValues();
$this->userNotification->saveSettings($user['id'], $values);
$this->session->flash(t('User updated successfully.'));
$this->flash->success(t('User updated successfully.'));
$this->response->redirect($this->helper->url->to('user', 'notifications', array('user_id' => $user['id'])));
}
@ -225,7 +225,7 @@ class User extends Base
if ($this->request->isPost()) {
$values = $this->request->getValues();
$this->userMetadata->save($user['id'], $values);
$this->session->flash(t('User updated successfully.'));
$this->flash->success(t('User updated successfully.'));
$this->response->redirect($this->helper->url->to('user', 'integrations', array('user_id' => $user['id'])));
}
@ -263,9 +263,9 @@ class User extends Base
$this->checkCSRFParam();
if ($this->user->{$switch.'PublicAccess'}($user['id'])) {
$this->session->flash(t('User updated successfully.'));
$this->flash->success(t('User updated successfully.'));
} else {
$this->session->flashError(t('Unable to update this user.'));
$this->flash->failure(t('Unable to update this user.'));
}
$this->response->redirect($this->helper->url->to('user', 'share', array('user_id' => $user['id'])));
@ -294,9 +294,9 @@ class User extends Base
if ($valid) {
if ($this->user->update($values)) {
$this->session->flash(t('Password modified successfully.'));
$this->flash->success(t('Password modified successfully.'));
} else {
$this->session->flashError(t('Unable to change the password.'));
$this->flash->failure(t('Unable to change the password.'));
}
$this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id'])));
@ -343,9 +343,9 @@ class User extends Base
if ($valid) {
if ($this->user->update($values)) {
$this->session->flash(t('User updated successfully.'));
$this->flash->success(t('User updated successfully.'));
} else {
$this->session->flashError(t('Unable to update your user.'));
$this->flash->failure(t('Unable to update your user.'));
}
$this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id'])));
@ -380,9 +380,9 @@ class User extends Base
if ($valid) {
if ($this->user->update($values)) {
$this->session->flash(t('User updated successfully.'));
$this->flash->success(t('User updated successfully.'));
} else {
$this->session->flashError(t('Unable to update your user.'));
$this->flash->failure(t('Unable to update your user.'));
}
$this->response->redirect($this->helper->url->to('user', 'authentication', array('user_id' => $user['id'])));
@ -409,9 +409,9 @@ class User extends Base
$this->checkCSRFParam();
if ($this->user->remove($user['id'])) {
$this->session->flash(t('User removed successfully.'));
$this->flash->success(t('User removed successfully.'));
} else {
$this->session->flashError(t('Unable to remove this user.'));
$this->flash->failure(t('Unable to remove this user.'));
}
$this->response->redirect($this->helper->url->to('user', 'index'));

View file

@ -46,9 +46,9 @@ class UserImport extends Base
$csv->read($filename, array($this->userImport, 'import'));
if ($this->userImport->counter > 0) {
$this->session->flash(t('%d user(s) have been imported successfully.', $this->userImport->counter));
$this->flash->success(t('%d user(s) have been imported successfully.', $this->userImport->counter));
} else {
$this->session->flashError(t('Nothing have been imported!'));
$this->flash->failure(t('Nothing have been imported!'));
}
$this->response->redirect($this->helper->url->to('userImport', 'step1'));

View file

@ -10,20 +10,24 @@ use Pimple\Container;
* @package core
* @author Frederic Guillot
*
* @property \Kanboard\Core\Session\SessionManager $sessionManager
* @property \Kanboard\Core\Session\SessionStorage $sessionStorage
* @property \Kanboard\Core\Session\FlashMessage $flash
* @property \Kanboard\Core\Helper $helper
* @property \Kanboard\Core\Mail\Client $emailClient
* @property \Kanboard\Core\HttpClient $httpClient
* @property \Kanboard\Core\Paginator $paginator
* @property \Kanboard\Core\Request $request
* @property \Kanboard\Core\Session $session
* @property \Kanboard\Core\Http\Client $httpClient
* @property \Kanboard\Core\Http\Request $request
* @property \Kanboard\Core\Http\Router $router
* @property \Kanboard\Core\Http\Response $response
* @property \Kanboard\Core\Template $template
* @property \Kanboard\Core\OAuth2 $oauth
* @property \Kanboard\Core\Router $router
* @property \Kanboard\Core\Lexer $lexer
* @property \Kanboard\Core\ObjectStorage\ObjectStorageInterface $objectStorage
* @property \Kanboard\Core\Cache\Cache $memoryCache
* @property \Kanboard\Core\Plugin\Hook $hook
* @property \Kanboard\Core\Plugin\Loader $pluginLoader
* @property \Kanboard\Core\Security\Token $token
* @property \Kanboard\Integration\BitbucketWebhook $bitbucketWebhook
* @property \Kanboard\Integration\GithubWebhook $githubWebhook
* @property \Kanboard\Integration\GitlabWebhook $gitlabWebhook

View file

@ -93,8 +93,7 @@ class Csv
{
if (! empty($value)) {
$value = trim(strtolower($value));
return $value === '1' || $value{0}
=== 't' ? 1 : 0;
return $value === '1' || $value{0} === 't' ? 1 : 0;
}
return 0;
@ -164,10 +163,14 @@ class Csv
*/
public function write($filename, array $rows)
{
$file = new SplFileObject($filename, 'w');
$fp = fopen($filename, 'w');
foreach ($rows as $row) {
$file->fputcsv($row, $this->delimiter, $this->enclosure);
if (is_resource($fp)) {
foreach ($rows as $row) {
fputcsv($fp, $row, $this->delimiter, $this->enclosure);
}
fclose($fp);
}
return $this;

View file

@ -1,14 +1,16 @@
<?php
namespace Kanboard\Core;
namespace Kanboard\Core\Http;
use Kanboard\Core\Base;
/**
* HTTP client
*
* @package core
* @package http
* @author Frederic Guillot
*/
class HttpClient extends Base
class Client extends Base
{
/**
* HTTP connection timeout in seconds
@ -99,6 +101,36 @@ class HttpClient extends Base
return '';
}
$stream = @fopen(trim($url), 'r', false, stream_context_create($this->getContext($method, $content, $headers)));
$response = '';
if (is_resource($stream)) {
$response = stream_get_contents($stream);
} else {
$this->logger->error('HttpClient: request failed');
}
if (DEBUG) {
$this->logger->debug('HttpClient: url='.$url);
$this->logger->debug('HttpClient: payload='.$content);
$this->logger->debug('HttpClient: metadata='.var_export(@stream_get_meta_data($stream), true));
$this->logger->debug('HttpClient: response='.$response);
}
return $response;
}
/**
* Get stream context
*
* @access private
* @param string $method
* @param string $content
* @param string[] $headers
* @return array
*/
private function getContext($method, $content, array $headers)
{
$default_headers = array(
'User-Agent: '.self::HTTP_USER_AGENT,
'Connection: close',
@ -126,22 +158,6 @@ class HttpClient extends Base
$context['http']['request_fulluri'] = true;
}
$stream = @fopen(trim($url), 'r', false, stream_context_create($context));
$response = '';
if (is_resource($stream)) {
$response = stream_get_contents($stream);
} else {
$this->container['logger']->error('HttpClient: request failed');
}
if (DEBUG) {
$this->container['logger']->debug('HttpClient: url='.$url);
$this->container['logger']->debug('HttpClient: payload='.$content);
$this->container['logger']->debug('HttpClient: metadata='.var_export(@stream_get_meta_data($stream), true));
$this->container['logger']->debug('HttpClient: response='.$response);
}
return $response;
return $context;
}
}

View file

@ -1,14 +1,16 @@
<?php
namespace Kanboard\Core;
namespace Kanboard\Core\Http;
use Kanboard\Core\Base;
/**
* Request class
*
* @package core
* @package http
* @author Frederic Guillot
*/
class Request
class Request extends Base
{
/**
* Get URL string parameter
@ -57,7 +59,8 @@ class Request
*/
public function getValues()
{
if (! empty($_POST) && Security::validateCSRFFormToken($_POST)) {
if (! empty($_POST) && ! empty($_POST['csrf_token']) && $this->token->validateCSRFToken($_POST['csrf_token'])) {
unset($_POST['csrf_token']);
return $_POST;
}

View file

@ -1,14 +1,17 @@
<?php
namespace Kanboard\Core;
namespace Kanboard\Core\Http;
use Kanboard\Core\Base;
use Kanboard\Core\Csv;
/**
* Response class
*
* @package core
* @package http
* @author Frederic Guillot
*/
class Response
class Response extends Base
{
/**
* Send no cache headers
@ -44,6 +47,8 @@ class Response
public function forceDownload($filename)
{
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Content-Transfer-Encoding: binary');
header('Content-Type: application/octet-stream');
}
/**

View file

@ -1,13 +1,14 @@
<?php
namespace Kanboard\Core;
namespace Kanboard\Core\Http;
use RuntimeException;
use Kanboard\Core\Base;
/**
* Router class
*
* @package core
* @package http
* @author Frederic Guillot
*/
class Router extends Base

View file

@ -51,7 +51,7 @@ class Client extends Base
$author = 'Kanboard';
if ($this->userSession->isLogged()) {
$author = e('%s via Kanboard', $this->user->getFullname($this->session['user']));
$author = e('%s via Kanboard', $this->helper->user->getFullname());
}
$this->getTransport(MAIL_TRANSPORT)->sendEmail($email, $name, $subject, $html, $author);

View file

@ -4,6 +4,7 @@ namespace Kanboard\Core\Plugin;
use DirectoryIterator;
use PDOException;
use LogicException;
use RuntimeException;
use Kanboard\Core\Tool;
@ -59,6 +60,11 @@ class Loader extends \Kanboard\Core\Base
public function load($plugin)
{
$class = '\Kanboard\Plugin\\'.$plugin.'\\Plugin';
if (! class_exists($class)) {
throw new LogicException('Unable to load this plugin class '.$class);
}
$instance = new $class($this->container);
Tool::buildDic($this->container, $instance->getClasses());

View file

@ -1,86 +0,0 @@
<?php
namespace Kanboard\Core;
/**
* Security class
*
* @package core
* @author Frederic Guillot
*/
class Security
{
/**
* Generate a random token with different methods: openssl or /dev/urandom or fallback to uniqid()
*
* @static
* @access public
* @return string Random token
*/
public static function generateToken()
{
if (function_exists('openssl_random_pseudo_bytes')) {
return bin2hex(\openssl_random_pseudo_bytes(30));
} elseif (ini_get('open_basedir') === '' && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
return hash('sha256', file_get_contents('/dev/urandom', false, null, 0, 30));
}
return hash('sha256', uniqid(mt_rand(), true));
}
/**
* Generate and store a CSRF token in the current session
*
* @static
* @access public
* @return string Random token
*/
public static function getCSRFToken()
{
$nonce = self::generateToken();
if (empty($_SESSION['csrf_tokens'])) {
$_SESSION['csrf_tokens'] = array();
}
$_SESSION['csrf_tokens'][$nonce] = true;
return $nonce;
}
/**
* Check if the token exists for the current session (a token can be used only one time)
*
* @static
* @access public
* @param string $token CSRF token
* @return bool
*/
public static function validateCSRFToken($token)
{
if (isset($_SESSION['csrf_tokens'][$token])) {
unset($_SESSION['csrf_tokens'][$token]);
return true;
}
return false;
}
/**
* Check if the token used in a form is correct and then remove the value
*
* @static
* @access public
* @param array $values Form values
* @return bool
*/
public static function validateCSRFFormToken(array &$values)
{
if (! empty($values['csrf_token']) && self::validateCSRFToken($values['csrf_token'])) {
unset($values['csrf_token']);
return true;
}
return false;
}
}

View file

@ -0,0 +1,69 @@
<?php
namespace Kanboard\Core\Security;
use Kanboard\Core\Base;
/**
* Token Handler
*
* @package security
* @author Frederic Guillot
*/
class Token extends Base
{
/**
* Generate a random token with different methods: openssl or /dev/urandom or fallback to uniqid()
*
* @static
* @access public
* @return string Random token
*/
public static function getToken()
{
if (function_exists('random_bytes')) {
return bin2hex(random_bytes(30));
} elseif (function_exists('openssl_random_pseudo_bytes')) {
return bin2hex(openssl_random_pseudo_bytes(30));
} elseif (ini_get('open_basedir') === '' && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
return hash('sha256', file_get_contents('/dev/urandom', false, null, 0, 30));
}
return hash('sha256', uniqid(mt_rand(), true));
}
/**
* Generate and store a CSRF token in the current session
*
* @access public
* @return string Random token
*/
public function getCSRFToken()
{
if (! isset($this->sessionStorage->csrf)) {
$this->sessionStorage->csrf = array();
}
$nonce = self::getToken();
$this->sessionStorage->csrf[$nonce] = true;
return $nonce;
}
/**
* Check if the token exists for the current session (a token can be used only one time)
*
* @access public
* @param string $token CSRF token
* @return bool
*/
public function validateCSRFToken($token)
{
if (isset($this->sessionStorage->csrf[$token])) {
unset($this->sessionStorage->csrf[$token]);
return true;
}
return false;
}
}

View file

@ -1,143 +0,0 @@
<?php
namespace Kanboard\Core;
use ArrayAccess;
/**
* Session class
*
* @package core
* @author Frederic Guillot
*/
class Session implements ArrayAccess
{
/**
* Return true if the session is open
*
* @static
* @access public
* @return boolean
*/
public static function isOpen()
{
return session_id() !== '';
}
/**
* Open a session
*
* @access public
* @param string $base_path Cookie path
*/
public function open($base_path = '/')
{
// HttpOnly and secure flags for session cookie
session_set_cookie_params(
SESSION_DURATION,
$base_path ?: '/',
null,
Request::isHTTPS(),
true
);
// Avoid session id in the URL
ini_set('session.use_only_cookies', '1');
// Enable strict mode
if (version_compare(PHP_VERSION, '7.0.0') < 0) {
ini_set('session.use_strict_mode', '1');
}
// Ensure session ID integrity
ini_set('session.entropy_file', '/dev/urandom');
ini_set('session.entropy_length', '32');
ini_set('session.hash_bits_per_character', 6);
// If the session was autostarted with session.auto_start = 1 in php.ini destroy it
if (isset($_SESSION)) {
session_destroy();
}
// Custom session name
session_name('__S');
// Start the session
session_start();
// Regenerate the session id to avoid session fixation issue
if (empty($_SESSION['__validated'])) {
session_regenerate_id(true);
$_SESSION['__validated'] = 1;
}
}
/**
* Destroy the session
*
* @access public
*/
public function close()
{
// Flush all sessions variables
$_SESSION = array();
// Destroy the session cookie
$params = session_get_cookie_params();
setcookie(
session_name(),
'',
time() - 42000,
$params['path'],
$params['domain'],
$params['secure'],
$params['httponly']
);
// Destroy session data
session_destroy();
}
/**
* Register a flash message (success notification)
*
* @access public
* @param string $message Message
*/
public function flash($message)
{
$_SESSION['flash_message'] = $message;
}
/**
* Register a flash error message (error notification)
*
* @access public
* @param string $message Message
*/
public function flashError($message)
{
$_SESSION['flash_error_message'] = $message;
}
public function offsetSet($offset, $value)
{
$_SESSION[$offset] = $value;
}
public function offsetExists($offset)
{
return isset($_SESSION[$offset]);
}
public function offsetUnset($offset)
{
unset($_SESSION[$offset]);
}
public function offsetGet($offset)
{
return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null;
}
}

View file

@ -0,0 +1,71 @@
<?php
namespace Kanboard\Core\Session;
use Kanboard\Core\Base;
/**
* Session Flash Message
*
* @package session
* @author Frederic Guillot
*/
class FlashMessage extends Base
{
/**
* Add success message
*
* @access public
* @param string $message
*/
public function success($message)
{
$this->setMessage('success', $message);
}
/**
* Add failure message
*
* @access public
* @param string $message
*/
public function failure($message)
{
$this->setMessage('failure', $message);
}
/**
* Add new flash message
*
* @access public
* @param string $key
* @param string $message
*/
public function setMessage($key, $message)
{
if (! isset($this->sessionStorage->flash)) {
$this->sessionStorage->flash = array();
}
$this->sessionStorage->flash[$key] = $message;
}
/**
* Get flash message
*
* @access public
* @param string $key
* @return string
*/
public function getMessage($key)
{
$message = '';
if (isset($this->sessionStorage->flash[$key])) {
$message = $this->sessionStorage->flash[$key];
unset($this->sessionStorage->flash[$key]);
}
return $message;
}
}

View file

@ -0,0 +1,102 @@
<?php
namespace Kanboard\Core\Session;
use Kanboard\Core\Base;
use Kanboard\Core\Http\Request;
/**
* Session Manager
*
* @package session
* @author Frederic Guillot
*/
class SessionManager extends Base
{
/**
* Return true if the session is open
*
* @static
* @access public
* @return boolean
*/
public static function isOpen()
{
return session_id() !== '';
}
/**
* Create a new session
*
* @access public
*/
public function open()
{
$this->configure();
if (ini_get('session.auto_start') == 1) {
session_destroy();
}
session_name('KB_SID');
session_start();
$this->container['sessionStorage']->setStorage($_SESSION);
}
/**
* Destroy the session
*
* @access public
*/
public function close()
{
// Destroy the session cookie
$params = session_get_cookie_params();
setcookie(
session_name(),
'',
time() - 42000,
$params['path'],
$params['domain'],
$params['secure'],
$params['httponly']
);
session_unset();
session_destroy();
}
/**
* Define session settings
*
* @access private
*/
private function configure()
{
// Session cookie: HttpOnly and secure flags
session_set_cookie_params(
SESSION_DURATION,
$this->helper->url->dir() ?: '/',
null,
Request::isHTTPS(),
true
);
// Avoid session id in the URL
ini_set('session.use_only_cookies', '1');
ini_set('session.use_trans_sid', '0');
// Enable strict mode
ini_set('session.use_strict_mode', '1');
// Better session hash
ini_set('session.hash_function', 'sha512');
ini_set('session.hash_bits_per_character', 6);
// Set an additional entropy
ini_set('session.entropy_file', '/dev/urandom');
ini_set('session.entropy_length', '256');
}
}

View file

@ -0,0 +1,72 @@
<?php
namespace Kanboard\Core\Session;
/**
* Session Storage
*
* @package session
* @author Frederic Guillot
*
* @property array $config
* @property array $user
* @property array $flash
* @property array $csrf
* @property array $postAuth
* @property array $filters
* @property string $redirectAfterLogin
* @property string $captcha
* @property string $commentSorting
* @property bool $hasSubtaskInProgress
* @property bool $boardCollapsed
*/
class SessionStorage
{
/**
* Pointer to external storage
*
* @access private
* @var array
*/
private $storage = array();
/**
* Set external storage
*
* @access public
* @param array $storage External session storage (example: $_SESSION)
*/
public function setStorage(array &$storage)
{
$this->storage =& $storage;
// Load dynamically existing session variables into object properties
foreach ($storage as $key => $value) {
$this->$key = $value;
}
}
/**
* Get all session variables
*
* @access public
* @return array
*/
public function getAll()
{
$session = get_object_vars($this);
unset($session['storage']);
return $session;
}
/**
* Copy class properties to external storage
*
* @access public
*/
public function __destruct()
{
$this->storage = $this->getAll();
}
}

View file

@ -39,6 +39,7 @@ class Tool
* @access public
* @param Container $container
* @param array $namespaces
* @return Container
*/
public static function buildDIC(Container $container, array $namespaces)
{
@ -50,6 +51,8 @@ class Tool
};
}
}
return $container;
}
/**

View file

@ -62,18 +62,17 @@ class App extends \Kanboard\Core\Base
*/
public function flashMessage()
{
$html = '';
$success_message = $this->flash->getMessage('success');
$failure_message = $this->flash->getMessage('failure');
if (isset($this->session['flash_message'])) {
$html = '<div class="alert alert-success alert-fade-out">'.$this->helper->e($this->session['flash_message']).'</div>';
unset($this->session['flash_message']);
unset($this->session['flash_error_message']);
} elseif (isset($this->session['flash_error_message'])) {
$html = '<div class="alert alert-error">'.$this->helper->e($this->session['flash_error_message']).'</div>';
unset($this->session['flash_message']);
unset($this->session['flash_error_message']);
if (! empty($success_message)) {
return '<div class="alert alert-success alert-fade-out">'.$this->helper->e($success_message).'</div>';
}
return $html;
if (! empty($failure_message)) {
return '<div class="alert alert-error">'.$this->helper->e($failure_message).'</div>';
}
return '';
}
}

View file

@ -2,7 +2,7 @@
namespace Kanboard\Helper;
use Kanboard\Core\Security;
use Kanboard\Core\Base;
/**
* Form helpers
@ -10,7 +10,7 @@ use Kanboard\Core\Security;
* @package helper
* @author Frederic Guillot
*/
class Form extends \Kanboard\Core\Base
class Form extends Base
{
/**
* Hidden CSRF token field
@ -20,7 +20,7 @@ class Form extends \Kanboard\Core\Base
*/
public function csrf()
{
return '<input type="hidden" name="csrf_token" value="'.Security::getCSRFToken().'"/>';
return '<input type="hidden" name="csrf_token" value="'.$this->token->getCSRFToken().'"/>';
}
/**

View file

@ -20,7 +20,7 @@ class Subtask extends \Kanboard\Core\Base
*/
public function toggleStatus(array $subtask, $redirect)
{
if ($subtask['status'] == 0 && isset($this->session['has_subtask_inprogress']) && $this->session['has_subtask_inprogress'] === true) {
if ($subtask['status'] == 0 && isset($this->sessionStorage->hasSubtaskInProgress) && $this->sessionStorage->hasSubtaskInProgress === true) {
return $this->helper->url->link(
trim($this->template->render('subtask/icons', array('subtask' => $subtask))) . $this->helper->e($subtask['title']),
'subtask',

View file

@ -2,8 +2,8 @@
namespace Kanboard\Helper;
use Kanboard\Core\Request;
use Kanboard\Core\Security;
use Kanboard\Core\Http\Request;
use Kanboard\Core\Base;
/**
* Url helpers
@ -11,7 +11,7 @@ use Kanboard\Core\Security;
* @package helper
* @author Frederic Guillot
*/
class Url extends \Kanboard\Core\Base
class Url extends Base
{
private $base = '';
private $directory = '';
@ -158,7 +158,7 @@ class Url extends \Kanboard\Core\Base
}
if ($csrf) {
$qs['csrf_token'] = Security::getCSRFToken();
$qs['csrf_token'] = $this->token->getCSRFToken();
}
if (! empty($qs)) {

View file

@ -136,7 +136,7 @@ class User extends \Kanboard\Core\Base
*/
public function getFullname(array $user = array())
{
return $this->user->getFullname(empty($user) ? $_SESSION['user'] : $user);
return $this->user->getFullname(empty($user) ? $this->sessionStorage->user : $user);
}
/**

File diff suppressed because it is too large Load diff

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'ID je vyžadováno',
'The project id is required' => 'ID projektu je vyžadováno',
'The project name is required' => 'Jméno projektu je vyžadováno',
'This project must be unique' => 'Jméno projektu musí být jedinečné',
'The title is required' => 'Nadpis je vyžadován',
'Settings saved successfully.' => 'Nastavení bylo úspěšně uloženo',
'Unable to save your settings.' => 'Vaše nastavení nelze uložit.',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'Id\'et er krævet',
'The project id is required' => 'Projektets id er krævet',
'The project name is required' => 'Projektets navn er krævet',
'This project must be unique' => 'Projektets navn skal være unikt',
'The title is required' => 'Titel er krævet',
'Settings saved successfully.' => 'Indstillinger gemt.',
'Unable to save your settings.' => 'Indstillinger kunne ikke gemmes.',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'Die ID ist anzugeben',
'The project id is required' => 'Die Projekt ID ist anzugeben',
'The project name is required' => 'Der Projektname ist anzugeben',
'This project must be unique' => 'Der Projektname muss eindeutig sein',
'The title is required' => 'Der Titel ist anzugeben',
'Settings saved successfully.' => 'Einstellungen erfolgreich gespeichert.',
'Unable to save your settings.' => 'Speichern der Einstellungen nicht möglich.',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'El identificador es obligatorio',
'The project id is required' => 'El identificador del proyecto es obligatorio',
'The project name is required' => 'El nombre del proyecto es obligatorio',
'This project must be unique' => 'El nombre del proyecto debe ser único',
'The title is required' => 'El título es obligatorio',
'Settings saved successfully.' => 'Parámetros guardados correctamente.',
'Unable to save your settings.' => 'No se pueden guardar sus parámetros.',
@ -984,84 +983,89 @@ return array(
'Table of contents' => 'Tabla de contenido',
'Gantt' => 'Gantt',
'Help with project permissions' => 'Ayuda con permisos del proyecto',
// 'Author' => '',
// 'Version' => '',
// 'Plugins' => '',
// 'There is no plugin loaded.' => '',
// 'Set maximum column height' => '',
// 'Remove maximum column height' => '',
// 'My notifications' => '',
// 'Custom filters' => '',
// 'Your custom filter have been created successfully.' => '',
// 'Unable to create your custom filter.' => '',
// 'Custom filter removed successfully.' => '',
// 'Unable to remove this custom filter.' => '',
// 'Edit custom filter' => '',
// 'Your custom filter have been updated successfully.' => '',
// 'Unable to update custom filter.' => '',
// 'Web' => '',
// 'New attachment on task #%d: %s' => '',
// 'New comment on task #%d' => '',
// 'Comment updated on task #%d' => '',
// 'New subtask on task #%d' => '',
// 'Subtask updated on task #%d' => '',
// 'New task #%d: %s' => '',
// 'Task updated #%d' => '',
// 'Task #%d closed' => '',
// 'Task #%d opened' => '',
// 'Column changed for task #%d' => '',
// 'New position for task #%d' => '',
// 'Swimlane changed for task #%d' => '',
// 'Assignee changed on task #%d' => '',
// '%d overdue tasks' => '',
// 'Task #%d is overdue' => '',
// 'No new notifications.' => '',
// 'Mark all as read' => '',
// 'Mark as read' => '',
// 'Total number of tasks in this column across all swimlanes' => '',
// 'Collapse swimlane' => '',
// 'Expand swimlane' => '',
// 'Add a new filter' => '',
// 'Share with all project members' => '',
// 'Shared' => '',
// 'Owner' => '',
// 'Unread notifications' => '',
// 'My filters' => '',
// 'Notification methods:' => '',
// 'Import tasks from CSV file' => '',
// 'Unable to read your file' => '',
// '%d task(s) have been imported successfully.' => '',
// 'Nothing have been imported!' => '',
// 'Import users from CSV file' => '',
// '%d user(s) have been imported successfully.' => '',
// 'Comma' => '',
// 'Semi-colon' => '',
// 'Tab' => '',
// 'Vertical bar' => '',
// 'Double Quote' => '',
// 'Single Quote' => '',
// '%s attached a file to the task #%d' => '',
// 'There is no column or swimlane activated in your project!' => '',
// 'Append filter (instead of replacement)' => '',
// 'Append/Replace' => '',
// 'Append' => '',
// 'Replace' => '',
// 'There is no notification method registered.' => '',
// 'Import' => '',
// 'change sorting' => '',
// 'Tasks Importation' => '',
// 'Delimiter' => '',
'Author' => 'Autor',
'Version' => 'Versión',
'Plugins' => 'Plugins',
'There is no plugin loaded.' => 'No hay ningún plugin cargado',
'Set maximum column height' => 'Establecer altura máxima de la columna',
'Remove maximum column height' => 'Eliminar altura máxima de la columna',
'My notifications' => 'Mis notificaciones',
'Custom filters' => 'Filtros personalizados',
'Your custom filter have been created successfully.' => 'Tus filtros personalizados han sido creados exitosamente',
'Unable to create your custom filter.' => 'No se ha podido crear tu filtro personalizado',
'Custom filter removed successfully.' => 'Filtro personalizado ha sido eliminado exitosamente',
'Unable to remove this custom filter.' => 'No se ha podido eliminar tu filtro personalizado',
'Edit custom filter' => 'Modificar filtro personalizado',
'Your custom filter have been updated successfully.' => 'Tu filtro personalizado ha sido actualizado exitosamente',
'Unable to update custom filter.' => 'No se ha podido actualizar tu filtro personalizado',
'Web' => 'Web',
'New attachment on task #%d: %s' => 'Nuevo adjunto en la tarea #%d: %s',
'New comment on task #%d' => 'Nuevo comentario en la tarea #%d',
'Comment updated on task #%d' => 'Comentario actualizado en la tarea #%d',
'New subtask on task #%d' => 'Nueva subtarea en la tarea #%d',
'Subtask updated on task #%d' => 'La subtarea en la tarea #%d ha sido actualizada',
'New task #%d: %s' => 'Nueva tarea #%d: %s',
'Task updated #%d' => 'Tarea actualizada #%d',
'Task #%d closed' => 'Tarea #%d ha sido cerrada',
'Task #%d opened' => 'Tarea #%d ha sido abierta',
'Column changed for task #%d' => 'Columna para tarea #%d ha sido cambiada',
'New position for task #%d' => 'Nueva posición para tarea #%d',
'Swimlane changed for task #%d' => 'Se cambió el swimlane de la tarea #%d',
'Assignee changed on task #%d' => 'Se cambió el asignado de la tarea #%d',
'%d overdue tasks' => '%d tareas atrasadas',
'Task #%d is overdue' => 'La tarea #%d está atrasada',
'No new notifications.' => 'No hay nuevas notificaciones',
'Mark all as read' => 'Marcar todo como leído',
'Mark as read' => 'Marcar como leído',
'Total number of tasks in this column across all swimlanes' => 'Número total de tareas en esta columna por todas las swimlanes',
'Collapse swimlane' => 'Contraer swimlane',
'Expand swimlane' => 'Ampliar swimlane',
'Add a new filter' => 'Añadir nuevo filtro',
'Share with all project members' => 'Compartir con todos los miembros del proyecto',
'Shared' => 'Compartido',
'Owner' => 'Dueño',
'Unread notifications' => 'Notificaciones sin leer',
'My filters' => 'Mis filtros',
'Notification methods:' => 'Métodos de notificación',
'Import tasks from CSV file' => 'Importar tareas desde archivo CSV',
'Unable to read your file' => 'No es posible leer el archivo',
'%d task(s) have been imported successfully.' => '%d tarea(s) han sido importadas exitosamente',
'Nothing have been imported!' => 'No se ha importado nada!',
'Import users from CSV file' => 'Importar usuarios desde archivo CSV',
'%d user(s) have been imported successfully.' => '%d usuario(s) se han importado exitosamente',
'Comma' => 'Coma',
'Semi-colon' => 'Punto y coma',
'Tab' => 'Tabulación',
'Vertical bar' => 'Pleca',
'Double Quote' => 'Comilla doble',
'Single Quote' => 'Comilla sencilla',
'%s attached a file to the task #%d' => '%s adjuntó un archivo a la tarea #%d',
'There is no column or swimlane activated in your project!' => 'No hay ninguna columna o swimlane activada en su proyecto!',
'Append filter (instead of replacement)' => 'Añadir filtro (en vez de reemplazar)',
'Append/Replace' => 'Añadir/Reemplazar',
'Append' => 'Añadir',
'Replace' => 'Reemplazar',
'There is no notification method registered.' => 'No hay método de notificación registrado',
'Import' => 'Importar',
'change sorting' => 'Cambiar orden',
'Tasks Importation' => 'Importación de tareas',
'Delimiter' => 'Delimitador',
// 'Enclosure' => '',
// 'CSV File' => '',
// 'Instructions' => '',
// 'Your file must use the predefined CSV format' => '',
// 'Your file must be encoded in UTF-8' => '',
// 'The first row must be the header' => '',
// 'Duplicates are not verified for you' => '',
// 'The due date must use the ISO format: YYYY-MM-DD' => '',
// 'Download CSV template' => '',
// 'No external integration registered.' => '',
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
'CSV File' => 'Archivo CSV',
'Instructions' => 'Indicaciones',
'Your file must use the predefined CSV format' => 'Su archivo debe utilizar el formato CSV predeterminado',
'Your file must be encoded in UTF-8' => 'Su archivo debe ser codificado en UTF-8',
'The first row must be the header' => 'La primera fila debe ser el encabezado',
'Duplicates are not verified for you' => 'Los duplicados no serán verificados',
'The due date must use the ISO format: YYYY-MM-DD' => 'La fecha de entrega debe utilizar el formato ISO: AAAA-MM-DD',
'Download CSV template' => 'Descargar plantilla CSV',
'No external integration registered.' => 'No se ha registrado integración externa',
'Duplicates are not imported' => 'Los duplicados no son importados',
'Usernames must be lowercase and unique' => 'Los nombres de usuario deben ser únicos y contener sólo minúsculas',
'Passwords will be encrypted if present' => 'Las contraseñas serán cifradas si es que existen',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'ID vaaditaan',
'The project id is required' => 'Projektin ID on pakollinen',
'The project name is required' => 'Projektin nimi on pakollinen',
'This project must be unique' => 'Projektin nimi täytyy olla uniikki',
'The title is required' => 'Otsikko vaaditaan',
'Settings saved successfully.' => 'Asetukset tallennettu onnistuneesti.',
'Unable to save your settings.' => 'Asetusten tallentaminen epäonnistui.',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'L\'identifiant est obligatoire',
'The project id is required' => 'L\'identifiant du projet est obligatoire',
'The project name is required' => 'Le nom du projet est obligatoire',
'This project must be unique' => 'Le nom du projet doit être unique',
'The title is required' => 'Le titre est obligatoire',
'Settings saved successfully.' => 'Paramètres sauvegardés avec succès.',
'Unable to save your settings.' => 'Impossible de sauvegarder vos réglages.',
@ -1066,4 +1065,10 @@ return array(
'Duplicates are not imported' => 'Les doublons ne sont pas importés',
'Usernames must be lowercase and unique' => 'Les noms d\'utilisateurs doivent être en minuscule et unique',
'Passwords will be encrypted if present' => 'Les mots de passe seront chiffrés si présent',
'%s attached a new file to the task %s' => '%s a attaché un nouveau fichier à la tâche %s',
'Link type' => 'Type de lien',
'Assign automatically a category based on a link' => 'Assigner automatiquement une catégorie en fonction d\'un lien',
'BAM - Konvertibile Mark' => 'BAM - Mark convertible',
'Assignee Username' => 'Utilisateur assigné',
'Assignee Name' => 'Nom de l\'assigné',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'Az ID-t (azonosítót) meg kell adni',
'The project id is required' => 'A projekt ID-t (azonosítót) meg kell adni',
'The project name is required' => 'A projekt nevét meg kell adni',
'This project must be unique' => 'A projekt nevének egyedinek kell lennie',
'The title is required' => 'A címet meg kell adni',
'Settings saved successfully.' => 'A beállítások sikeresen mentve.',
'Unable to save your settings.' => 'A beállítások mentése sikertelen.',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'Id diperlukan',
'The project id is required' => 'Id proyek diperlukan',
'The project name is required' => 'Nama proyek diperlukan',
'This project must be unique' => 'Proyek ini harus unik',
'The title is required' => 'Judul diperlukan',
'Settings saved successfully.' => 'Pengaturan berhasil disimpan.',
'Unable to save your settings.' => 'Tidak dapat menyimpan pengaturan anda.',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'Si richiede l\'identificatore',
'The project id is required' => 'Si richiede l\'identificatore del progetto',
'The project name is required' => 'Si richiede il nome del progetto',
'This project must be unique' => 'Il nome del progetto deve essere unico',
'The title is required' => 'Si richiede un titolo',
'Settings saved successfully.' => 'Impostazioni salvate correttamente.',
'Unable to save your settings.' => 'Non si possono salvare le impostazioni.',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'ID が必要です',
'The project id is required' => 'プロジェクト ID が必要です',
'The project name is required' => 'プロジェクト名が必要です',
'This project must be unique' => 'プロジェクト名がすでに使われています',
'The title is required' => 'タイトルが必要です',
'Settings saved successfully.' => '設定を保存しました。',
'Unable to save your settings.' => '設定の保存に失敗しました。',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'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',
'Settings saved successfully.' => 'Innstillinger lagret.',
'Unable to save your settings.' => 'Innstillinger kunne ikke lagres.',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'Het id is verplicht',
'The project id is required' => 'Het project id is verplicht',
'The project name is required' => 'De projectnaam is verplicht',
'This project must be unique' => 'Dit project moet uniek zijn',
'The title is required' => 'De titel is verplicht',
'Settings saved successfully.' => 'Instellingen succesvol opgeslagen.',
'Unable to save your settings.' => 'Instellingen opslaan niet gelukt.',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'ID jest wymagane',
'The project id is required' => 'ID projektu jest wymagane',
'The project name is required' => 'Nazwa projektu jest wymagana',
'This project must be unique' => 'Projekt musi być unikalny',
'The title is required' => 'Tutył jest wymagany',
'Settings saved successfully.' => 'Ustawienia zapisane.',
'Unable to save your settings.' => 'Nie udało się zapisać ustawień.',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -68,7 +68,7 @@ return array(
'Disable' => 'Desativar',
'Enable' => 'Ativar',
'New project' => 'Novo projeto',
'Do you really want to remove this project: "%s"?' => 'Você realmente deseja remover este projeto: "%s" ?',
'Do you really want to remove this project: "%s"?' => 'Você realmente deseja remover este projeto: "%s"?',
'Remove project' => 'Remover projeto',
'Edit the board for "%s"' => 'Editar o board para "%s"',
'All projects' => 'Todos os projetos',
@ -124,7 +124,6 @@ return array(
'The id is required' => 'O ID é obrigatório',
'The project id is required' => 'O ID do projeto é obrigatório',
'The project name is required' => 'O nome do projeto é obrigatório',
'This project must be unique' => 'Este projeto deve ser único',
'The title is required' => 'O título é obrigatório',
'Settings saved successfully.' => 'Configurações salvas com sucesso.',
'Unable to save your settings.' => 'Não é possível salvar suas configurações.',
@ -167,8 +166,8 @@ return array(
'%d closed tasks' => '%d tarefas finalizadas',
'No task for this project' => 'Não há tarefa para este projeto',
'Public link' => 'Link público',
'Change assignee' => 'Mudar a designação',
'Change assignee for the task "%s"' => 'Modificar designação para a tarefa "%s"',
'Change assignee' => 'Alterar designação',
'Change assignee for the task "%s"' => 'Alterar designação para a tarefa "%s"',
'Timezone' => 'Fuso horário',
'Sorry, I didn\'t find this information in my database!' => 'Desculpe, não encontrei esta informação no meu banco de dados!',
'Page not found' => 'Página não encontrada',
@ -217,7 +216,7 @@ return array(
'Remove an automatic action' => 'Remover uma ação automática',
'Assign the task to a specific user' => 'Designar a tarefa para um usuário específico',
'Assign the task to the person who does the action' => 'Designar a tarefa para a pessoa que executa a ação',
'Duplicate the task to another project' => 'Duplicar a tarefa para um outro projeto',
'Duplicate the task to another project' => 'Duplicar a tarefa para outro projeto',
'Move a task to another column' => 'Mover a tarefa para outra coluna',
'Task modification' => 'Modificação de tarefa',
'Task creation' => 'Criação de tarefa',
@ -274,7 +273,7 @@ return array(
'Task removed successfully.' => 'Tarefa removida com sucesso.',
'Unable to remove this task.' => 'Não foi possível remover esta tarefa.',
'Remove a task' => 'Remover uma tarefa',
'Do you really want to remove this task: "%s"?' => 'Você realmente deseja remover esta tarefa: "%s"',
'Do you really want to remove this task: "%s"?' => 'Você realmente deseja remover esta tarefa: "%s"?',
'Assign automatically a color based on a category' => 'Atribuir automaticamente uma cor com base em uma categoria',
'Assign automatically a category based on a color' => 'Atribuir automaticamente uma categoria com base em uma cor',
'Task creation or modification' => 'Criação ou modificação de tarefa',
@ -287,12 +286,12 @@ return array(
'Your category have been updated successfully.' => 'A sua categoria foi atualizada com sucesso.',
'Unable to update your category.' => 'Não foi possível atualizar a sua categoria.',
'Remove a category' => 'Remover uma categoria',
'Category removed successfully.' => 'Categoria removido com sucesso.',
'Category removed successfully.' => 'Categoria removida com sucesso.',
'Unable to remove this category.' => 'Não foi possível remover esta categoria.',
'Category modification for the project "%s"' => 'Modificação de categoria para o projeto "%s"',
'Category Name' => 'Nome da Categoria',
'Category Name' => 'Nome da categoria',
'Add a new category' => 'Adicionar uma nova categoria',
'Do you really want to remove this category: "%s"?' => 'Você realmente deseja remover esta categoria: "%s"',
'Do you really want to remove this category: "%s"?' => 'Você realmente deseja remover esta categoria: "%s"?',
'All categories' => 'Todas as categorias',
'No category' => 'Nenhum categoria',
'The name is required' => 'O nome é obrigatório',
@ -300,7 +299,7 @@ return array(
'Unable to remove this file.' => 'Não foi possível remover este arquivo.',
'File removed successfully.' => 'Arquivo removido com sucesso.',
'Attach a document' => 'Anexar um documento',
'Do you really want to remove this file: "%s"?' => 'Você realmente deseja remover este arquivo: "%s"',
'Do you really want to remove this file: "%s"?' => 'Você realmente deseja remover este arquivo: "%s"?',
'Attachments' => 'Anexos',
'Edit the task' => 'Editar a tarefa',
'Edit the description' => 'Editar a descrição',
@ -331,7 +330,7 @@ return array(
'Unable to update your sub-task.' => 'Não foi possível atualizar a sua subtarefa.',
'Unable to create your sub-task.' => 'Não é possível criar a sua subtarefa.',
'Sub-task added successfully.' => 'Subtarefa adicionada com sucesso.',
'Maximum size: ' => 'Tamanho máximo:',
'Maximum size: ' => 'Tamanho máximo: ',
'Unable to upload the file.' => 'Não foi possível carregar o arquivo.',
'Display another project' => 'Exibir outro projeto',
'Login with my Github Account' => 'Entrar com minha Conta do Github',
@ -461,7 +460,7 @@ return array(
'Database' => 'Banco de dados',
'About' => 'Sobre',
'Database driver:' => 'Driver do banco de dados:',
'Board settings' => 'Configurações do Board',
'Board settings' => 'Configurações do board',
'URL and token' => 'URL e token',
'Webhook settings' => 'Configurações do Webhook',
'URL for task creation:' => 'URL para a criação da tarefa:',
@ -475,7 +474,7 @@ return array(
'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frequência em segundos (0 para desativar este recurso, 10 segundos por padrão)',
'Application URL' => 'URL da Aplicação',
'Example: http://example.kanboard.net/ (used by email notifications)' => 'Exemplo: http://example.kanboard.net/ (utilizado nas notificações por e-mail)',
'Token regenerated.' => 'Token ',
'Token regenerated.' => 'Novo token gerado.',
'Date format' => 'Formato de data',
'ISO format is always accepted, example: "%s" and "%s"' => 'O formato ISO é sempre aceito, exemplo: "%s" e "%s"',
'New private project' => 'Novo projeto privado',
@ -562,7 +561,7 @@ return array(
'Unable to remove this swimlane.' => 'Não foi possível remover esta swimlane.',
'Unable to update this swimlane.' => 'Não foi possível atualizar esta swimlane.',
'Your swimlane have been created successfully.' => 'Sua swimlane foi criada com sucesso.',
'Example: "Bug, Feature Request, Improvement"' => 'Exemplo: "Bug, Feature Request, Improvement"',
'Example: "Bug, Feature Request, Improvement"' => 'Exemplo: "Bug, Solicitação de Recurso, Melhoria"',
'Default categories for new projects (Comma-separated)' => 'Categorias padrões para novos projetos (separadas por vírgula)',
'Gitlab commit received' => 'Gitlab commit received',
'Gitlab issue opened' => 'Gitlab issue opened',
@ -574,7 +573,7 @@ return array(
'Role for this project' => 'Função para este projeto',
'Project manager' => 'Gerente do projeto',
'Project member' => 'Membro do projeto',
'A project manager can change the settings of the project and have more privileges than a standard user.' => 'Um gerente do projeto pode alterar as configurações do projeto e ter mais privilégios que um usuário padrão.',
'A project manager can change the settings of the project and have more privileges than a standard user.' => 'Um gerente de projeto pode alterar as configurações do projeto e ter mais privilégios que um usuário padrão.',
'Gitlab Issue' => 'Gitlab Issue',
'Subtask Id' => 'ID da subtarefa',
'Subtasks' => 'Subtarefas',
@ -582,13 +581,13 @@ return array(
'Subtasks exportation for "%s"' => 'Subtarefas exportadas para "%s"',
'Task Title' => 'Título da Tarefa',
'Untitled' => 'Sem título',
'Application default' => 'Aplicação padrão',
'Application default' => 'Padrão da aplicação',
'Language:' => 'Idioma',
'Timezone:' => 'Fuso horário',
'All columns' => 'Todas as colunas',
'Calendar' => 'Calendário',
'Next' => 'Próximo',
// '#%d' => '',
'#%d' => '#%d',
'All swimlanes' => 'Todas as swimlanes',
'All colors' => 'Todas as cores',
'Moved to column %s' => 'Mover para a coluna %s',
@ -598,7 +597,7 @@ return array(
'Edit column "%s"' => 'Editar a coluna "%s"',
'Select the new status of the subtask: "%s"' => 'Selecionar um novo status para a subtarefa: "%s"',
'Subtask timesheet' => 'Gestão de tempo das subtarefas',
'There is nothing to show.' => 'Não há nada para mostrar',
'There is nothing to show.' => 'Não há nada para mostrar.',
'Time Tracking' => 'Gestão de tempo',
'You already have one subtask in progress' => 'Você já tem um subtarefa em andamento',
'Which parts of the project do you want to duplicate?' => 'Quais partes do projeto você deseja duplicar?',
@ -610,11 +609,11 @@ return array(
'End' => 'Fim',
'Task age in days' => 'Idade da tarefa em dias',
'Days in this column' => 'Dias nesta coluna',
// '%dd' => '',
'%dd' => '%dd',
'Add a link' => 'Adicionar uma associação',
'Add a new link' => 'Adicionar uma nova associação',
'Do you really want to remove this link: "%s"?' => 'Você realmente deseja remover esta associação: "%s"?',
'Do you really want to remove this link with task #%d?' => 'Você realmente deseja remover esta associação com a tarefa %d?',
'Do you really want to remove this link with task #%d?' => 'Você realmente deseja remover esta associação com a tarefa #%d?',
'Field required' => 'Campo requerido',
'Link added successfully.' => 'Associação criada com sucesso.',
'Link updated successfully.' => 'Associação atualizada com sucesso.',
@ -637,8 +636,8 @@ return array(
'is blocked by' => 'está bloqueada por',
'duplicates' => 'duplica',
'is duplicated by' => 'é duplicada por',
'is a child of' => um filho de',
'is a parent of' => um parente do',
'is a child of' => filha de',
'is a parent of' => mãe de',
'targets milestone' => 'visa um milestone',
'is a milestone of' => 'é um milestone de',
'fixes' => 'corrige',
@ -698,12 +697,12 @@ return array(
'%s remove the assignee of the task %s' => '%s removeu a pessoa designada para a tarefa %s',
'Enable Gravatar images' => 'Ativar imagens do Gravatar',
'Information' => 'Informações',
'Check two factor authentication code' => 'Verificação do código de autenticação à fator duplo',
'The two factor authentication code is not valid.' => 'O código de autenticação à fator duplo não é válido',
'The two factor authentication code is valid.' => 'O código de autenticação à fator duplo é válido',
'Check two factor authentication code' => 'Verifique o código de autenticação em duas etapas',
'The two factor authentication code is not valid.' => 'O código de autenticação em duas etapas não é válido.',
'The two factor authentication code is valid.' => 'O código de autenticação em duas etapas é válido.',
'Code' => 'Código',
'Two factor authentication' => 'Autenticação à fator duplo',
'Enable/disable two factor authentication' => 'Ativar/Desativar autenticação à fator duplo',
'Two factor authentication' => 'Autenticação em duas etapas',
'Enable/disable two factor authentication' => 'Ativar/desativar autenticação em duas etapas',
'This QR code contains the key URI: ' => 'Este Código QR contém a chave URI:',
'Save the secret key in your TOTP software (by example Google Authenticator or FreeOTP).' => 'Salve esta chave secreta no seu software TOTP (por exemplo Google Authenticator ou FreeOTP).',
'Check my code' => 'Verifique o meu código',
@ -717,19 +716,19 @@ return array(
'Burndown chart for "%s"' => 'Gráfico de Burndown para "%s"',
'Burndown chart' => 'Gráfico de Burndown',
'This chart show the task complexity over the time (Work Remaining).' => 'Este gráfico mostra a complexidade da tarefa ao longo do tempo (Trabalho Restante).',
'Screenshot taken %s' => 'Screenshot tomada em %s',
'Add a screenshot' => 'Adicionar uma Screenshot',
'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Tomar um screenshot e pressione CTRL + V ou ⌘ + V para colar aqui.',
'Screenshot uploaded successfully.' => 'Screenshot enviada com sucesso.',
'Screenshot taken %s' => 'Captura de tela tirada em %s',
'Add a screenshot' => 'Adicionar uma captura de tela',
'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Tire uma captura de tela e pressione CTRL + V ou ⌘ + V para colar aqui.',
'Screenshot uploaded successfully.' => 'Captura de tela enviada com sucesso.',
'SEK - Swedish Krona' => 'SEK - Coroa sueca',
'The project identifier is an optional alphanumeric code used to identify your project.' => 'O identificador de projeto é um código alfanumérico opcional utilizado para identificar o seu projeto.',
'Identifier' => 'Identificador',
'Disable two factor authentication' => 'Desativar autenticação à dois fatores',
'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Você deseja realmente desativar a autenticação à dois fatores para esse usuário: "%s"?',
'Disable two factor authentication' => 'Desativar autenticação em duas etapas',
'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Você realmente deseja desativar a autenticação em duas etapas para este usuário: "%s"?',
'Edit link' => 'Editar um link',
'Start to type task title...' => 'Digite o título do trabalho...',
'A task cannot be linked to itself' => 'Uma tarefa não pode ser ligada a si própria',
'The exact same link already exists' => 'Um link idêntico jà existe',
'A task cannot be linked to itself' => 'Uma tarefa não pode ser vinculada a si própria',
'The exact same link already exists' => 'Um link idêntico já existe',
'Recurrent task is scheduled to be generated' => 'A tarefa recorrente está programada para ser criada',
'Recurring information' => 'Informação sobre a recorrência',
'Score' => 'Complexidade',
@ -739,7 +738,7 @@ return array(
'Edit recurrence' => 'Modificar a recorrência',
'Generate recurrent task' => 'Gerar uma tarefa recorrente',
'Trigger to generate recurrent task' => 'Trigger para gerar tarefa recorrente',
'Factor to calculate new due date' => 'Fator para o cálculo do nova data limite',
'Factor to calculate new due date' => 'Fator para o cálculo da nova data limite',
'Timeframe to calculate new due date' => 'Escala de tempo para o cálculo da nova data limite',
'Base date to calculate new due date' => 'Data a ser utilizada para calcular a nova data limite',
'Action date' => 'Data da ação',
@ -770,8 +769,8 @@ return array(
'iCal feed' => 'Subscrição iCal',
'Preferences' => 'Preferências',
'Security' => 'Segurança',
'Two factor authentication disabled' => 'Autenticação à fator duplo desativado',
'Two factor authentication enabled' => 'Autenticação à fator duplo activado',
'Two factor authentication disabled' => 'Autenticação em duas etapas desativada',
'Two factor authentication enabled' => 'Autenticação em duas etapas ativada',
'Unable to update this user.' => 'Impossível de atualizar esse usuário.',
'There is no user management for private projects.' => 'Não há gerenciamento de usuários para projetos privados.',
'User that will receive the email' => 'O usuário que vai receber o e-mail',
@ -795,7 +794,7 @@ return array(
'Column change' => 'Mudança de coluna',
'Position change' => 'Mudança de posição',
'Swimlane change' => 'Mudança de swimlane',
'Assignee change' => 'Mudança do designado',
'Assignee change' => 'Mudança de designação',
'[%s] Overdue tasks' => '[%s] Tarefas atrasadas',
'Notification' => 'Notificação',
'%s moved the task #%d to the first swimlane' => '%s moveu a tarefa #%d para a primeira swimlane',
@ -804,7 +803,7 @@ return array(
'Gravatar' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s moveu a tarefa %s para a primeira swimlane',
'%s moved the task %s to the swimlane "%s"' => '%s moveu a tarefa %s para a swimlane "%s"',
'This report contains all subtasks information for the given date range.' => 'Este relatório contém informações de todas as sub-tarefas para o período selecionado.',
'This report contains all subtasks information for the given date range.' => 'Este relatório contém informações de todas as subtarefas para o período selecionado.',
'This report contains all tasks information for the given date range.' => 'Este relatório contém informações de todas as tarefas para o período selecionado.',
'Project activities for %s' => 'Atividade do projeto "%s"',
'view the board on Kanboard' => 'ver o painel no Kanboard',
@ -825,7 +824,7 @@ return array(
'Time estimated changed: %sh' => 'O tempo estimado foi mudado/ %sh',
'The field "%s" have been updated' => 'O campo "%s" foi atualizada',
'The description have been modified' => 'A descrição foi modificada',
'Do you really want to close the task "%s" as well as all subtasks?' => 'Você realmente quer fechar a tarefa "%s" e todas as suas sub-tarefas?',
'Do you really want to close the task "%s" as well as all subtasks?' => 'Você realmente deseja finalizar a tarefa "%s" e todas as suas subtarefas?',
'Swimlane: %s' => 'Swimlane: %s',
'I want to receive notifications for:' => 'Eu quero receber as notificações para:',
'All tasks' => 'Todas as tarefas',
@ -844,7 +843,7 @@ return array(
'<30m' => '<30m',
'Stop timer' => 'Stop timer',
'Start timer' => 'Start timer',
'Add project member' => 'Adicionar um membro ao projeto',
'Add project member' => 'Adicionar membro ao projeto',
'Enable notifications' => 'Ativar as notificações',
'My activity stream' => 'Meu feed de atividades',
'My calendar' => 'Minha agenda',
@ -868,7 +867,7 @@ return array(
'Switch to the list view' => 'Mudar par o modo Lista',
'Go to the search/filter box' => 'Ir para o campo de pesquisa',
'There is no activity yet.' => 'Não há nenhuma atividade ainda.',
'No tasks found.' => 'Nenhuma tarefa encontrada',
'No tasks found.' => 'Nenhuma tarefa encontrada.',
'Keyboard shortcut: "%s"' => 'Tecla de atalho: "%s"',
'List' => 'Lista',
'Filter' => 'Filtro',
@ -918,7 +917,7 @@ return array(
'Default task color' => 'Cor padrão para as tarefas',
'Hide sidebar' => 'Esconder a barra lateral',
'Expand sidebar' => 'Expandir a barra lateral',
'This feature does not work with all browsers.' => 'Esta funcionalidade não é compatível com todos os navegadores',
'This feature does not work with all browsers.' => 'Esta funcionalidade não é compatível com todos os navegadores.',
'There is no destination project available.' => 'Não há nenhum projeto de destino disponível.',
'Trigger automatically subtask time tracking' => 'Ativar automaticamente o monitoramento do tempo para as subtarefas',
'Include closed tasks in the cumulative flow diagram' => 'Incluir as tarefas fechadas no diagrama de fluxo acumulado',
@ -932,7 +931,7 @@ return array(
'contributors' => 'contribuidores',
'License:' => 'Licença:',
'License' => 'Licença',
'Project Administrator' => 'Administrador de Projeto',
'Project Administrator' => 'Administrador de projeto',
'Enter the text below' => 'Entre o texto abaixo',
'Gantt chart for %s' => 'Gráfico de Gantt para %s',
'Sort by position' => 'Ordenar por posição',
@ -959,7 +958,7 @@ return array(
'Project members' => 'Membros de projeto',
'Gantt chart for all projects' => 'Gráfico de Gantt para todos os projetos',
'Projects list' => 'Lista dos projetos',
'Gantt chart for this project' => 'Gráfico de Gantt para este projecto',
'Gantt chart for this project' => 'Gráfico de Gantt para este projeto',
'Project board' => 'Painel do projeto',
'End date:' => 'Data de término:',
'There is no start date or end date for this project.' => 'Não há data de início ou data de término para este projeto.',
@ -1028,40 +1027,45 @@ return array(
'Unread notifications' => 'Notificações não lidas',
'My filters' => 'Meus filtros',
'Notification methods:' => 'Métodos de notificação:',
// 'Import tasks from CSV file' => '',
// 'Unable to read your file' => '',
// '%d task(s) have been imported successfully.' => '',
// 'Nothing have been imported!' => '',
// 'Import users from CSV file' => '',
// '%d user(s) have been imported successfully.' => '',
// 'Comma' => '',
// 'Semi-colon' => '',
// 'Tab' => '',
// 'Vertical bar' => '',
// 'Double Quote' => '',
// 'Single Quote' => '',
// '%s attached a file to the task #%d' => '',
// 'There is no column or swimlane activated in your project!' => '',
// 'Append filter (instead of replacement)' => '',
// 'Append/Replace' => '',
// 'Append' => '',
// 'Replace' => '',
// 'There is no notification method registered.' => '',
// 'Import' => '',
// 'change sorting' => '',
// 'Tasks Importation' => '',
// 'Delimiter' => '',
// 'Enclosure' => '',
// 'CSV File' => '',
// 'Instructions' => '',
// 'Your file must use the predefined CSV format' => '',
// 'Your file must be encoded in UTF-8' => '',
// 'The first row must be the header' => '',
// 'Duplicates are not verified for you' => '',
// 'The due date must use the ISO format: YYYY-MM-DD' => '',
// 'Download CSV template' => '',
// 'No external integration registered.' => '',
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
'Import tasks from CSV file' => 'Importar tarefas a partir de arquivo CSV',
'Unable to read your file' => 'Não foi possível ler seu arquivo',
'%d task(s) have been imported successfully.' => '%d tarefa(s) importada(s) com sucesso.',
'Nothing have been imported!' => 'Nada foi importado!',
'Import users from CSV file' => 'Importar usuários a partir de arquivo CSV',
'%d user(s) have been imported successfully.' => '%d usuário(s) importado(s) com sucesso.',
'Comma' => 'Vírgula',
'Semi-colon' => 'Ponto e vírgula',
'Tab' => 'Tab',
'Vertical bar' => 'Barra vertical',
'Double Quote' => 'Aspas duplas',
'Single Quote' => 'Aspas simples',
'%s attached a file to the task #%d' => '%s anexou um arquivo à tarefa #%d',
'There is no column or swimlane activated in your project!' => 'Não há coluna ou swimlane ativa em seu projeto!',
'Append filter (instead of replacement)' => 'Adicionar filtro (em vez de substituir)',
'Append/Replace' => 'Adicionar/Substituir',
'Append' => 'Adicionar',
'Replace' => 'Substituir',
'There is no notification method registered.' => 'Não há metodo de notificação registrado.',
'Import' => 'Importar',
'change sorting' => 'alterar ordenação',
'Tasks Importation' => 'Importação de Tarefas',
'Delimiter' => 'Separador',
'Enclosure' => 'Delimitador de campos',
'CSV File' => 'Arquivo CSV',
'Instructions' => 'Instruções',
'Your file must use the predefined CSV format' => 'Seu arquivo deve utilizar o formato CSV pré-definido',
'Your file must be encoded in UTF-8' => 'Seu arquivo deve estar codificado em UTF-8',
'The first row must be the header' => 'A primeira linha deve ser o cabeçalho',
'Duplicates are not verified for you' => 'Registros duplicados não são verificados',
'The due date must use the ISO format: YYYY-MM-DD' => 'A data de vencimento deve utilizar o formato ISO: YYYY-MM-DD',
'Download CSV template' => 'Baixar modelo de arquivo CSV',
'No external integration registered.' => 'Nenhuma integração externa registrada.',
'Duplicates are not imported' => 'Registros duplicados não são importados',
'Usernames must be lowercase and unique' => 'Nomes de usuário devem ser únicos e em letras minúsculas',
'Passwords will be encrypted if present' => 'Senhas serão encriptadas, se presentes',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'O ID é obrigatório',
'The project id is required' => 'O ID do projecto é obrigatório',
'The project name is required' => 'O nome do projecto é obrigatório',
'This project must be unique' => 'Este projecto deve ser único',
'The title is required' => 'O título é obrigatório',
'Settings saved successfully.' => 'Configurações guardadas com sucesso.',
'Unable to save your settings.' => 'Não é possível guardar as suas configurações.',
@ -1027,41 +1026,46 @@ return array(
'Owner' => 'Dono',
'Unread notifications' => 'Notificações por ler',
'My filters' => 'Os meus filtros',
'Notification methods:' => 'Metodos de notificação:',
// 'Import tasks from CSV file' => '',
// 'Unable to read your file' => '',
// '%d task(s) have been imported successfully.' => '',
// 'Nothing have been imported!' => '',
// 'Import users from CSV file' => '',
// '%d user(s) have been imported successfully.' => '',
// 'Comma' => '',
// 'Semi-colon' => '',
// 'Tab' => '',
// 'Vertical bar' => '',
// 'Double Quote' => '',
// 'Single Quote' => '',
// '%s attached a file to the task #%d' => '',
// 'There is no column or swimlane activated in your project!' => '',
// 'Append filter (instead of replacement)' => '',
// 'Append/Replace' => '',
// 'Append' => '',
// 'Replace' => '',
// 'There is no notification method registered.' => '',
// 'Import' => '',
// 'change sorting' => '',
// 'Tasks Importation' => '',
// 'Delimiter' => '',
// 'Enclosure' => '',
// 'CSV File' => '',
// 'Instructions' => '',
// 'Your file must use the predefined CSV format' => '',
// 'Your file must be encoded in UTF-8' => '',
// 'The first row must be the header' => '',
// 'Duplicates are not verified for you' => '',
// 'The due date must use the ISO format: YYYY-MM-DD' => '',
// 'Download CSV template' => '',
// 'No external integration registered.' => '',
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
'Notification methods:' => 'Métodos de notificação:',
'Import tasks from CSV file' => 'Importar tarefas de um ficheiro CSV',
'Unable to read your file' => 'Não foi possivel ler o ficheiro',
'%d task(s) have been imported successfully.' => '%d tarefa(s) importada(s) com successo.',
'Nothing have been imported!' => 'Nada foi importado',
'Import users from CSV file' => 'Importar utilizadores de um ficheiro CSV',
'%d user(s) have been imported successfully.' => '%d utilizadore(s) importados com successo.',
'Comma' => 'Vírgula',
'Semi-colon' => 'Ponto e Vírgula',
'Tab' => 'Tabulação',
'Vertical bar' => 'Barra vertical',
'Double Quote' => 'Aspas',
'Single Quote' => 'Plica',
'%s attached a file to the task #%d' => '%s anexou um ficheiro à tarefa #%d',
'There is no column or swimlane activated in your project!' => 'Não existe nenhuma coluna ou swimlane activado no seu projecto!',
'Append filter (instead of replacement)' => 'Acrescentar filtro (em vez de substituir)',
'Append/Replace' => 'Acrescentar/Substituir',
'Append' => 'Acrescentar',
'Replace' => 'Substituir',
'There is no notification method registered.' => 'Não existe método de notificação registrado.',
'Import' => 'Importar',
'change sorting' => 'alterar ordernação',
'Tasks Importation' => 'Importação de Tarefas',
'Delimiter' => 'Delimitador',
'Enclosure' => 'Clausura',
'CSV File' => 'Ficheiro CSV',
'Instructions' => 'Instruções',
'Your file must use the predefined CSV format' => 'O seu ficheiro tem de usar um formato CSV pre-definido',
'Your file must be encoded in UTF-8' => 'O seu ficheiro tem de estar codificado como UTF-8',
'The first row must be the header' => 'A primeira linha tem de ser o cabeçalho',
'Duplicates are not verified for you' => 'Duplicados não são verificados por si',
'The due date must use the ISO format: YYYY-MM-DD' => 'A data de expiração tem de estar no formato ISO: AAAA-MM-DD',
'Download CSV template' => 'Descarregar template CSV',
'No external integration registered.' => 'Nenhuma integração externa registrada.',
'Duplicates are not imported' => 'Duplicados não são importados',
'Usernames must be lowercase and unique' => 'Utilizadores tem de estar em letra pequena e ser unicos',
'Passwords will be encrypted if present' => 'Senhas serão encriptadas se presentes',
'%s attached a new file to the task %s' => '%s anexou um novo ficheiro à tarefa %s',
'Assign automatically a category based on a link' => 'Assignar automáticamente a categoria baseada num link',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'Необходим ID',
'The project id is required' => 'Необходим ID проекта',
'The project name is required' => 'Необходимо имя проекта',
'This project must be unique' => 'Проект должен быть уникальным',
'The title is required' => 'Необходим заголовок',
'Settings saved successfully.' => 'Параметры успешно сохранены.',
'Unable to save your settings.' => 'Невозможно сохранить параметры.',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'ID je obavezan',
'The project id is required' => 'ID projekta je obavezan',
'The project name is required' => 'Naziv projekta je obavezan',
'This project must be unique' => 'Projekat mora biti jedinstven',
'The title is required' => 'Naslov je obavezan',
'Settings saved successfully.' => 'Podešavanja uspešno snimljena.',
'Unable to save your settings.' => 'Nemoguće snimanje podešavanja.',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'Aktuellt ID måste anges',
'The project id is required' => 'Projekt-ID måste anges',
'The project name is required' => 'Ett projektnamn måste anges',
'This project must be unique' => 'Detta projekt måste vara unikt',
'The title is required' => 'En titel måste anges.',
'Settings saved successfully.' => 'Inställningarna har sparats.',
'Unable to save your settings.' => 'Kunde inte spara dina ändringar',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'ต้องการไอดี',
'The project id is required' => 'ต้องการไอดีโปรเจค',
'The project name is required' => 'ต้องการชื่อโปรเจค',
'This project must be unique' => 'ชื่อโปรเจคต้องไม่ซ้ำ',
'The title is required' => 'ต้องการหัวเรื่อง',
'Settings saved successfully.' => 'บันทึกการตั้งค่าเรียบร้อยแล้ว',
'Unable to save your settings.' => 'ไม่สามารถบันทึกการตั้งค่าได้',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => 'Kod gerekli',
'The project id is required' => 'Proje kodu gerekli',
'The project name is required' => 'Proje adı gerekli',
'This project must be unique' => 'Bu projenin tekil olması gerekli',
'The title is required' => 'Başlık gerekli',
'Settings saved successfully.' => 'Ayarlar başarıyla kaydedildi.',
'Unable to save your settings.' => 'Ayarlarınız kaydedilemedi.',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -124,7 +124,6 @@ return array(
'The id is required' => '需要指定id',
'The project id is required' => '需要指定项目id',
'The project name is required' => '需要指定项目名称',
'This project must be unique' => '项目名称必须唯一',
'The title is required' => '需要指定标题',
'Settings saved successfully.' => '设置成功保存。',
'Unable to save your settings.' => '无法保存你的设置。',
@ -1064,4 +1063,9 @@ return array(
// 'Duplicates are not imported' => '',
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
// 'BAM - Konvertibile Mark' => '',
// 'Assignee Username' => '',
// 'Assignee Name' => '',
);

View file

@ -73,6 +73,7 @@ class Action extends Base
'TaskAssignColorUser' => t('Assign a color to a specific user'),
'TaskAssignColorCategory' => t('Assign automatically a color based on a category'),
'TaskAssignCategoryColor' => t('Assign automatically a category based on a color'),
'TaskAssignCategoryLink' => t('Assign automatically a category based on a link'),
'CommentCreation' => t('Create a comment from an external provider'),
'TaskCreation' => t('Create a task from an external provider'),
'TaskLogMoveAnotherColumn' => t('Add a comment log when moving the task between columns'),

View file

@ -2,7 +2,7 @@
namespace Kanboard\Model;
use Kanboard\Core\Request;
use Kanboard\Core\Http\Request;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
use Gregwar\Captcha\CaptchaBuilder;
@ -45,11 +45,11 @@ class Authentication extends Base
// Check if the user session match an existing user
$userNotFound = ! $this->user->exists($this->userSession->getId());
$reverseProxyWrongUser = REVERSE_PROXY_AUTH && $this->backend('reverseProxy')->getUsername() !== $_SESSION['user']['username'];
$reverseProxyWrongUser = REVERSE_PROXY_AUTH && $this->backend('reverseProxy')->getUsername() !== $this->userSession->getUsername();
if ($userNotFound || $reverseProxyWrongUser) {
$this->backend('rememberMe')->destroy($this->userSession->getId());
$this->session->close();
$this->sessionManager->close();
return false;
}
@ -176,8 +176,12 @@ class Authentication extends Base
public function validateFormCaptcha(array $values)
{
if ($this->hasCaptcha($values['username'])) {
if (! isset($this->sessionStorage->captcha)) {
return false;
}
$builder = new CaptchaBuilder;
$builder->setPhrase($this->session['captcha']);
$builder->setPhrase($this->sessionStorage->captcha);
return $builder->testPhrase(isset($values['captcha']) ? $values['captcha'] : '');
}

View file

@ -3,8 +3,8 @@
namespace Kanboard\Model;
use Kanboard\Core\Translator;
use Kanboard\Core\Security;
use Kanboard\Core\Session;
use Kanboard\Core\Security\Token;
use Kanboard\Core\Session\SessionManager;
/**
* Config model
@ -35,6 +35,7 @@ class Config extends Setting
'RSD' => t('RSD - Serbian dinar'),
'SEK' => t('SEK - Swedish Krona'),
'NOK' => t('NOK - Norwegian Krone'),
'BAM' => t('BAM - Konvertibile Mark'),
);
}
@ -69,6 +70,7 @@ class Config extends Setting
// Sorted by value
$languages = array(
'id_ID' => 'Bahasa Indonesia',
'bs_BA' => 'Bosanski',
'cs_CZ' => 'Čeština',
'da_DK' => 'Dansk',
'de_DE' => 'Deutsch',
@ -108,7 +110,7 @@ class Config extends Setting
public function getJsLanguageCode()
{
$languages = array(
'cs_CZ' => 'cz',
'cs_CZ' => 'cs',
'da_DK' => 'da',
'de_DE' => 'de',
'en_US' => 'en',
@ -145,8 +147,8 @@ class Config extends Setting
*/
public function getCurrentLanguage()
{
if ($this->userSession->isLogged() && ! empty($this->session['user']['language'])) {
return $this->session['user']['language'];
if ($this->userSession->isLogged() && ! empty($this->sessionStorage->user['language'])) {
return $this->sessionStorage->user['language'];
}
return $this->get('application_language', 'en_US');
@ -162,17 +164,17 @@ class Config extends Setting
*/
public function get($name, $default_value = '')
{
if (! Session::isOpen()) {
if (! SessionManager::isOpen()) {
return $this->getOption($name, $default_value);
}
// Cache config in session
if (! isset($this->session['config'][$name])) {
$this->session['config'] = $this->getAll();
if (! isset($this->sessionStorage->config[$name])) {
$this->sessionStorage->config = $this->getAll();
}
if (! empty($this->session['config'][$name])) {
return $this->session['config'][$name];
if (! empty($this->sessionStorage->config[$name])) {
return $this->sessionStorage->config[$name];
}
return $default_value;
@ -185,7 +187,7 @@ class Config extends Setting
*/
public function reload()
{
$this->session['config'] = $this->getAll();
$this->sessionStorage->config = $this->getAll();
$this->setupTranslations();
}
@ -207,8 +209,8 @@ class Config extends Setting
*/
public function getCurrentTimezone()
{
if ($this->userSession->isLogged() && ! empty($this->session['user']['timezone'])) {
return $this->session['user']['timezone'];
if ($this->userSession->isLogged() && ! empty($this->sessionStorage->user['timezone'])) {
return $this->sessionStorage->user['timezone'];
}
return $this->get('application_timezone', 'UTC');
@ -265,7 +267,7 @@ class Config extends Setting
*/
public function regenerateToken($option)
{
$this->save(array($option => Security::generateToken()));
$this->save(array($option => Token::getToken()));
}
/**

View file

@ -4,7 +4,7 @@ namespace Kanboard\Model;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
use Kanboard\Core\Security;
use Kanboard\Core\Security\Token;
/**
* Project model
@ -491,7 +491,7 @@ class Project extends Base
$this->db
->table(self::TABLE)
->eq('id', $project_id)
->save(array('is_public' => 1, 'token' => Security::generateToken()));
->save(array('is_public' => 1, 'token' => Token::getToken()));
}
/**
@ -527,7 +527,6 @@ class Project extends Base
new Validators\MaxLength('start_date', t('The maximum length is %d characters', 10), 10),
new Validators\MaxLength('end_date', t('The maximum length is %d characters', 10), 10),
new Validators\AlphaNumeric('identifier', t('This value must be alphanumeric')) ,
new Validators\Unique('name', t('This project must be unique'), $this->db->getConnection(), self::TABLE),
new Validators\Unique('identifier', t('The identifier must be unique'), $this->db->getConnection(), self::TABLE),
);
}

View file

@ -34,40 +34,51 @@ class ProjectDailyColumnStats extends Base
{
$status = $this->config->get('cfd_include_closed_tasks') == 1 ? array(Task::STATUS_OPEN, Task::STATUS_CLOSED) : array(Task::STATUS_OPEN);
return $this->db->transaction(function (Database $db) use ($project_id, $date, $status) {
$this->db->startTransaction();
$column_ids = $db->table(Board::TABLE)->eq('project_id', $project_id)->findAllByColumn('id');
$column_ids = $this->db->table(Board::TABLE)->eq('project_id', $project_id)->findAllByColumn('id');
foreach ($column_ids as $column_id) {
foreach ($column_ids as $column_id) {
// This call will fail if the record already exists
// (cross database driver hack for INSERT..ON DUPLICATE KEY UPDATE)
$db->table(ProjectDailyColumnStats::TABLE)->insert(array(
'day' => $date,
'project_id' => $project_id,
'column_id' => $column_id,
'total' => 0,
'score' => 0,
));
$exists = $this->db->table(ProjectDailyColumnStats::TABLE)
->eq('project_id', $project_id)
->eq('column_id', $column_id)
->eq('day', $date)
->exists();
$db->table(ProjectDailyColumnStats::TABLE)
$score = $this->db->table(Task::TABLE)
->eq('project_id', $project_id)
->eq('column_id', $column_id)
->eq('is_active', Task::STATUS_OPEN)
->sum('score');
$total = $this->db->table(Task::TABLE)
->eq('project_id', $project_id)
->eq('column_id', $column_id)
->in('is_active', $status)
->count();
if ($exists) {
$this->db->table(ProjectDailyColumnStats::TABLE)
->eq('project_id', $project_id)
->eq('column_id', $column_id)
->eq('day', $date)
->update(array(
'score' => $db->table(Task::TABLE)
->eq('project_id', $project_id)
->eq('column_id', $column_id)
->eq('is_active', Task::STATUS_OPEN)
->sum('score'),
'total' => $db->table(Task::TABLE)
->eq('project_id', $project_id)
->eq('column_id', $column_id)
->in('is_active', $status)
->count()
));
->update(array('score' => $score, 'total' => $total));
} else {
$this->db->table(ProjectDailyColumnStats::TABLE)->insert(array(
'day' => $date,
'project_id' => $project_id,
'column_id' => $column_id,
'total' => $total,
'score' => $score,
));
}
});
}
$this->db->closeTransaction();
return true;
}
/**

View file

@ -29,27 +29,35 @@ class ProjectDailyStats extends Base
*/
public function updateTotals($project_id, $date)
{
$this->db->startTransaction();
$lead_cycle_time = $this->projectAnalytic->getAverageLeadAndCycleTime($project_id);
return $this->db->transaction(function (Database $db) use ($project_id, $date, $lead_cycle_time) {
$exists = $this->db->table(ProjectDailyStats::TABLE)
->eq('day', $date)
->eq('project_id', $project_id)
->exists();
// This call will fail if the record already exists
// (cross database driver hack for INSERT..ON DUPLICATE KEY UPDATE)
$db->table(ProjectDailyStats::TABLE)->insert(array(
'day' => $date,
'project_id' => $project_id,
'avg_lead_time' => 0,
'avg_cycle_time' => 0,
));
$db->table(ProjectDailyStats::TABLE)
if ($exists) {
$this->db->table(ProjectDailyStats::TABLE)
->eq('project_id', $project_id)
->eq('day', $date)
->update(array(
'avg_lead_time' => $lead_cycle_time['avg_lead_time'],
'avg_cycle_time' => $lead_cycle_time['avg_cycle_time'],
));
});
} else {
$this->db->table(ProjectDailyStats::TABLE)->insert(array(
'day' => $date,
'project_id' => $project_id,
'avg_lead_time' => $lead_cycle_time['avg_lead_time'],
'avg_cycle_time' => $lead_cycle_time['avg_cycle_time'],
));
}
$this->db->closeTransaction();
return true;
}
/**

View file

@ -58,6 +58,7 @@ class TaskExport extends Base
tasks.date_due,
creators.username AS creator_username,
users.username AS assignee_username,
users.name AS assignee_name,
tasks.score,
tasks.title,
tasks.date_creation,
@ -129,7 +130,8 @@ class TaskExport extends Base
e('Color'),
e('Due date'),
e('Creator'),
e('Assignee'),
e('Assignee Username'),
e('Assignee Name'),
e('Complexity'),
e('Title'),
e('Creation date'),

View file

@ -5,8 +5,8 @@ namespace Kanboard\Model;
use PicoDb\Database;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
use Kanboard\Core\Session;
use Kanboard\Core\Security;
use Kanboard\Core\Session\SessionManager;
use Kanboard\Core\Security\Token;
/**
* User model
@ -320,8 +320,8 @@ class User extends Base
$result = $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values);
// If the user is connected refresh his session
if (Session::isOpen() && $this->userSession->getId() == $values['id']) {
$this->userSession->refresh();
if (SessionManager::isOpen() && $this->userSession->getId() == $values['id']) {
$this->userSession->initialize($this->getById($this->userSession->getId()));
}
return $result;
@ -383,7 +383,7 @@ class User extends Base
return $this->db
->table(self::TABLE)
->eq('id', $user_id)
->save(array('token' => Security::generateToken()));
->save(array('token' => Token::getToken()));
}
/**
@ -587,7 +587,7 @@ class User extends Base
if ($v->execute()) {
// Check password
if ($this->authentication->authenticate($this->session['user']['username'], $values['current_password'])) {
if ($this->authentication->authenticate($this->userSession->getUsername(), $values['current_password'])) {
return array(true, array());
} else {
return array(false, array('current_password' => array(t('Wrong password'))));

View file

@ -11,17 +11,13 @@ namespace Kanboard\Model;
class UserSession extends Base
{
/**
* Update user session information
* Update user session
*
* @access public
* @param array $user User data
* @param array $user
*/
public function refresh(array $user = array())
public function initialize(array $user)
{
if (empty($user)) {
$user = $this->user->getById($this->userSession->getId());
}
if (isset($user['password'])) {
unset($user['password']);
}
@ -31,12 +27,13 @@ class UserSession extends Base
}
$user['id'] = (int) $user['id'];
$user['is_admin'] = (bool) $user['is_admin'];
$user['is_project_admin'] = (bool) $user['is_project_admin'];
$user['is_ldap_user'] = (bool) $user['is_ldap_user'];
$user['twofactor_activated'] = (bool) $user['twofactor_activated'];
$user['is_admin'] = isset($user['is_admin']) ? (bool) $user['is_admin'] : false;
$user['is_project_admin'] = isset($user['is_project_admin']) ? (bool) $user['is_project_admin'] : false;
$user['is_ldap_user'] = isset($user['is_ldap_user']) ? (bool) $user['is_ldap_user'] : false;
$user['twofactor_activated'] = isset($user['twofactor_activated']) ? (bool) $user['twofactor_activated'] : false;
$this->session['user'] = $user;
$this->sessionStorage->user = $user;
$this->sessionStorage->postAuth = array('validated' => false);
}
/**
@ -47,7 +44,7 @@ class UserSession extends Base
*/
public function check2FA()
{
return isset($this->session['2fa_validated']) && $this->session['2fa_validated'] === true;
return isset($this->sessionStorage->postAuth['validated']) && $this->sessionStorage->postAuth['validated'] === true;
}
/**
@ -58,7 +55,17 @@ class UserSession extends Base
*/
public function has2FA()
{
return isset($this->session['user']['twofactor_activated']) && $this->session['user']['twofactor_activated'] === true;
return isset($this->sessionStorage->user['twofactor_activated']) && $this->sessionStorage->user['twofactor_activated'] === true;
}
/**
* Disable 2FA for the current session
*
* @access public
*/
public function disable2FA()
{
$this->sessionStorage->user['twofactor_activated'] = false;
}
/**
@ -69,7 +76,7 @@ class UserSession extends Base
*/
public function isAdmin()
{
return isset($this->session['user']['is_admin']) && $this->session['user']['is_admin'] === true;
return isset($this->sessionStorage->user['is_admin']) && $this->sessionStorage->user['is_admin'] === true;
}
/**
@ -80,7 +87,7 @@ class UserSession extends Base
*/
public function isProjectAdmin()
{
return isset($this->session['user']['is_project_admin']) && $this->session['user']['is_project_admin'] === true;
return isset($this->sessionStorage->user['is_project_admin']) && $this->sessionStorage->user['is_project_admin'] === true;
}
/**
@ -91,7 +98,18 @@ class UserSession extends Base
*/
public function getId()
{
return isset($this->session['user']['id']) ? (int) $this->session['user']['id'] : 0;
return isset($this->sessionStorage->user['id']) ? (int) $this->sessionStorage->user['id'] : 0;
}
/**
* Get username
*
* @access public
* @return integer
*/
public function getUsername()
{
return isset($this->sessionStorage->user['username']) ? $this->sessionStorage->user['username'] : '';
}
/**
@ -102,7 +120,7 @@ class UserSession extends Base
*/
public function isLogged()
{
return ! empty($this->session['user']);
return isset($this->sessionStorage->user) && ! empty($this->sessionStorage->user);
}
/**
@ -114,7 +132,7 @@ class UserSession extends Base
*/
public function getFilters($project_id)
{
return ! empty($_SESSION['filters'][$project_id]) ? $_SESSION['filters'][$project_id] : 'status:open';
return ! empty($this->sessionStorage->filters[$project_id]) ? $this->sessionStorage->filters[$project_id] : 'status:open';
}
/**
@ -126,7 +144,7 @@ class UserSession extends Base
*/
public function setFilters($project_id, $filters)
{
$_SESSION['filters'][$project_id] = $filters;
$this->sessionStorage->filters[$project_id] = $filters;
}
/**
@ -138,7 +156,7 @@ class UserSession extends Base
*/
public function isBoardCollapsed($project_id)
{
return ! empty($_SESSION['board_collapsed'][$project_id]) ? $_SESSION['board_collapsed'][$project_id] : false;
return ! empty($this->sessionStorage->boardCollapsed[$project_id]) ? $this->sessionStorage->boardCollapsed[$project_id] : false;
}
/**
@ -146,11 +164,11 @@ class UserSession extends Base
*
* @access public
* @param integer $project_id
* @param boolean $collapsed
* @param boolean $is_collapsed
*/
public function setBoardDisplayMode($project_id, $collapsed)
public function setBoardDisplayMode($project_id, $is_collapsed)
{
$_SESSION['board_collapsed'][$project_id] = $collapsed;
$this->sessionStorage->boardCollapsed[$project_id] = $is_collapsed;
}
/**
@ -161,7 +179,7 @@ class UserSession extends Base
*/
public function setCommentSorting($order)
{
$this->session['comment_sorting'] = $order;
$this->sessionStorage->commentSorting = $order;
}
/**
@ -172,6 +190,6 @@ class UserSession extends Base
*/
public function getCommentSorting()
{
return $this->session['comment_sorting'] ?: 'ASC';
return empty($this->sessionStorage->commentSorting) ? 'ASC' : $this->sessionStorage->commentSorting;
}
}

View file

@ -16,6 +16,13 @@ use Kanboard\Model\Subtask;
*/
class Mail extends Base implements NotificationInterface
{
/**
* Notification type
*
* @var string
*/
const TYPE = 'email';
/**
* Send notification to a user
*

View file

@ -12,6 +12,13 @@ use Kanboard\Core\Base;
*/
class Web extends Base implements NotificationInterface
{
/**
* Notification type
*
* @var string
*/
const TYPE = 'web';
/**
* Send notification to a user
*

View file

@ -3,9 +3,15 @@
namespace Schema;
use PDO;
use Kanboard\Core\Security;
use Kanboard\Core\Security\Token;
const VERSION = 93;
const VERSION = 94;
function version_94(PDO $pdo)
{
$pdo->exec('ALTER TABLE `projects` DROP INDEX `name`');
$pdo->exec('ALTER TABLE `projects` DROP INDEX `name_2`');
}
function version_93(PDO $pdo)
{
@ -869,7 +875,7 @@ function version_20(PDO $pdo)
function version_19(PDO $pdo)
{
$pdo->exec("ALTER TABLE config ADD COLUMN api_token VARCHAR(255) DEFAULT ''");
$pdo->exec("UPDATE config SET api_token='".Security::generateToken()."'");
$pdo->exec("UPDATE config SET api_token='".Token::getToken()."'");
}
function version_18(PDO $pdo)
@ -1091,6 +1097,6 @@ function version_1(PDO $pdo)
$pdo->exec("
INSERT INTO config
(webhooks_token)
VALUES ('".Security::generateToken()."')
VALUES ('".Token::getToken()."')
");
}

View file

@ -3,9 +3,14 @@
namespace Schema;
use PDO;
use Kanboard\Core\Security;
use Kanboard\Core\Security\Token;
const VERSION = 73;
const VERSION = 74;
function version_74(PDO $pdo)
{
$pdo->exec('ALTER TABLE projects DROP CONSTRAINT IF EXISTS projects_name_key');
}
function version_73(PDO $pdo)
{
@ -994,6 +999,6 @@ function version_1(PDO $pdo)
$pdo->exec("
INSERT INTO config
(webhooks_token, api_token)
VALUES ('".Security::generateToken()."', '".Security::generateToken()."')
VALUES ('".Token::getToken()."', '".Token::getToken()."')
");
}

View file

@ -2,7 +2,7 @@
namespace Schema;
use Kanboard\Core\Security;
use Kanboard\Core\Security\Token;
use PDO;
const VERSION = 88;
@ -799,7 +799,7 @@ function version_20(PDO $pdo)
function version_19(PDO $pdo)
{
$pdo->exec("ALTER TABLE config ADD COLUMN api_token TEXT DEFAULT ''");
$pdo->exec("UPDATE config SET api_token='".Security::generateToken()."'");
$pdo->exec("UPDATE config SET api_token='".Token::getToken()."'");
}
function version_18(PDO $pdo)
@ -1026,7 +1026,7 @@ function version_1(PDO $pdo)
$pdo->exec("
CREATE TABLE projects (
id INTEGER PRIMARY KEY,
name TEXT NOCASE NOT NULL UNIQUE,
name TEXT NOCASE NOT NULL,
is_active INTEGER DEFAULT 1
)
");
@ -1068,6 +1068,6 @@ function version_1(PDO $pdo)
$pdo->exec("
INSERT INTO config
(webhooks_token)
VALUES ('".Security::generateToken()."')
VALUES ('".Token::getToken()."')
");
}

View file

@ -11,8 +11,11 @@ use Kanboard\Core\ObjectStorage\FileStorage;
use Kanboard\Core\Paginator;
use Kanboard\Core\OAuth2;
use Kanboard\Core\Tool;
use Kanboard\Core\Http\Client as HttpClient;
use Kanboard\Model\UserNotificationType;
use Kanboard\Model\ProjectNotificationType;
use Kanboard\Notification\Mail as MailNotification;
use Kanboard\Notification\Web as WebNotification;
class ClassProvider implements ServiceProviderInterface
{
@ -81,19 +84,23 @@ class ClassProvider implements ServiceProviderInterface
'Core' => array(
'DateParser',
'Helper',
'HttpClient',
'Lexer',
'Request',
'Router',
'Session',
'Template',
),
'Core\Http' => array(
'Request',
'Response',
'Router',
),
'Core\Cache' => array(
'MemoryCache',
),
'Core\Plugin' => array(
'Hook',
),
'Core\Security' => array(
'Token',
),
'Integration' => array(
'BitbucketWebhook',
'GithubWebhook',
@ -113,6 +120,10 @@ class ClassProvider implements ServiceProviderInterface
return new OAuth2($c);
});
$container['httpClient'] = function ($c) {
return new HttpClient($c);
};
$container['htmlConverter'] = function () {
return new HtmlConverter(array('strip_tags' => true));
};
@ -131,8 +142,8 @@ class ClassProvider implements ServiceProviderInterface
$container['userNotificationType'] = function ($container) {
$type = new UserNotificationType($container);
$type->setType('email', t('Email'), '\Kanboard\Notification\Mail');
$type->setType('web', t('Web'), '\Kanboard\Notification\Web');
$type->setType(MailNotification::TYPE, t('Email'), '\Kanboard\Notification\Mail');
$type->setType(WebNotification::TYPE, t('Web'), '\Kanboard\Notification\Web');
return $type;
};
@ -146,5 +157,7 @@ class ClassProvider implements ServiceProviderInterface
$container['pluginLoader'] = new Loader($container);
$container['cspRules'] = array('style-src' => "'self' 'unsafe-inline'", 'img-src' => '* data:');
return $container;
}
}

View file

@ -15,6 +15,8 @@ class DatabaseProvider implements ServiceProviderInterface
$container['db'] = $this->getInstance();
$container['db']->stopwatch = DEBUG;
$container['db']->logQueries = DEBUG;
return $container;
}
/**

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