mirror of
https://github.com/YunoHost-Apps/kanboard_ynh.git
synced 2024-09-03 19:36:17 +02:00
Update kanboard v1.0.8
This commit is contained in:
parent
a4efad8870
commit
e6f0a222fd
426 changed files with 37537 additions and 3720 deletions
6
sources/.gitignore
vendored
6
sources/.gitignore
vendored
|
@ -32,6 +32,8 @@
|
|||
######################
|
||||
.buildpath
|
||||
.project
|
||||
/.settings/
|
||||
.idea
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
|
@ -51,5 +53,5 @@ Thumbs.db
|
|||
|
||||
# App specific #
|
||||
################
|
||||
config.php
|
||||
data/files
|
||||
#config.php
|
||||
#data/files
|
||||
|
|
|
@ -7,4 +7,4 @@ php:
|
|||
- "5.3"
|
||||
|
||||
before_script: wget https://phar.phpunit.de/phpunit.phar
|
||||
script: php phpunit.phar
|
||||
script: php phpunit.phar -c tests/units.sqlite.xml
|
|
@ -20,24 +20,21 @@ Features
|
|||
--------
|
||||
|
||||
- Multiple boards/projects
|
||||
- Boards customization, rename or add columns
|
||||
- Tasks with different colors, categories, sub-tasks, attachments, Markdown support for the description
|
||||
- Automatic actions
|
||||
- Boards customization, rename/add/remove columns
|
||||
- Tasks with different colors, categories, sub-tasks, attachments, comments and Markdown support for the description
|
||||
- Automatic actions based on events
|
||||
- Users management with a basic privileges separation (administrator or regular user)
|
||||
- External authentication: Google and GitHub accounts as well as LDAP/ActiveDirectory
|
||||
- Email notifications
|
||||
- External authentication: Google, GitHub, LDAP/ActiveDirectory and Reverse-Proxy
|
||||
- Webhooks to create tasks from an external software
|
||||
- A basic command line interface
|
||||
- Host anywhere (shared hosting, VPS, Raspberry Pi or localhost)
|
||||
- No external dependencies
|
||||
- **Super easy setup**, copy and paste files and you are done!
|
||||
- Translations in English, French, Brazilian Portuguese, Spanish, German, Polish, Swedish and Chinese
|
||||
- Translations in English, French, Brazilian Portuguese, Spanish, German, Polish, Swedish, Finnish, Italian, Chinese, Russian...
|
||||
|
||||
Roadmap
|
||||
-------
|
||||
|
||||
Kanboard is under active development, have a look to the roadmap: <http://kanboard.net/#roadmap>
|
||||
|
||||
Known bugs
|
||||
----------
|
||||
Known bugs and feature requests
|
||||
-------------------------------
|
||||
|
||||
See Issues: <https://github.com/fguillot/kanboard/issues>
|
||||
|
||||
|
@ -46,56 +43,45 @@ License
|
|||
|
||||
GNU Affero General Public License version 3: <http://www.gnu.org/licenses/agpl-3.0.txt>
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
Original author: [Frédéric Guillot](http://fredericguillot.com/)
|
||||
|
||||
Contributors:
|
||||
|
||||
- Alex Butum: https://github.com/dZkF9RWJT6wN8ux
|
||||
- Claudio Lobo
|
||||
- Gavlepeter: https://github.com/gavlepeter
|
||||
- Jesusaplsoft: https://github.com/jesusaplsoft
|
||||
- Kiswa: https://github.com/kiswa
|
||||
- Levlaz: https://github.com/levlaz
|
||||
- Mathgl67: https://github.com/mathgl67
|
||||
- Matthieu Keller: https://github.com/maggick
|
||||
- Maxime: https://github.com/EpocDotFr
|
||||
- Moraxy: https://github.com/moraxy
|
||||
- Nala Ginrut: https://github.com/NalaGinrut
|
||||
- Nekohayo: https://github.com/nekohayo
|
||||
- Olivier Maridat: https://github.com/oliviermaridat
|
||||
- Poikilotherm: https://github.com/poikilotherm
|
||||
- Raphaël Doursenaud: https://github.com/rdoursenaud
|
||||
- Rzeka: https://github.com/rzeka
|
||||
- Sebastien pacilly: https://github.com/spacilly
|
||||
- Toomyem: https://github.com/Toomyem
|
||||
- Troloo: https://github.com/troloo
|
||||
- Typz: https://github.com/Typz
|
||||
|
||||
There is also many people who have reported bugs or proposed awesome ideas.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
### Using Kanboard
|
||||
|
||||
#### Introduction
|
||||
|
||||
- [Usage examples](docs/usage-examples.markdown)
|
||||
- [Manage users](docs/manage-users.markdown)
|
||||
- [Syntax guide](docs/syntax-guide.markdown)
|
||||
|
||||
#### Working with projects
|
||||
|
||||
- [Creating projects](docs/creating-projects.markdown)
|
||||
- [Editing projects](docs/editing-projects.markdown)
|
||||
- [Sharing boards and tasks](docs/sharing-projects.markdown)
|
||||
- [Automatic actions](docs/automatic-actions.markdown)
|
||||
|
||||
#### Working with tasks
|
||||
|
||||
- [Creating tasks](docs/creating-tasks.markdown)
|
||||
|
||||
#### Working with users
|
||||
|
||||
- [User management](docs/manage-users.markdown)
|
||||
|
||||
#### More
|
||||
|
||||
- [Syntax guide](docs/syntax-guide.markdown)
|
||||
|
||||
### Technical details
|
||||
|
||||
#### Installation
|
||||
|
||||
- [Installation instructions](docs/installation.markdown)
|
||||
- [Upgrade Kanboard to a new version](docs/update.markdown)
|
||||
- [Installation on Ubuntu](docs/ubuntu-installation.markdown)
|
||||
- [Installation on Debian](docs/debian-installation.markdown)
|
||||
- [Installation on Centos](docs/centos-installation.markdown)
|
||||
- [Upgrade Kanboard to a new version](docs/update.markdown)
|
||||
- [Secure connections (HTTPS)](docs/secure-connections.markdown)
|
||||
- [Installation on Windows Server with IIS](docs/windows-iis-installation.markdown)
|
||||
- [Example with Nginx + HTTPS + SPDY + PHP-FPM](docs/nginx-ssl-php-fpm.markdown)
|
||||
|
||||
#### Database
|
||||
|
||||
|
@ -108,12 +94,22 @@ Documentation
|
|||
- [LDAP authentication](docs/ldap-authentication.markdown)
|
||||
- [Google authentication](docs/google-authentication.markdown)
|
||||
- [GitHub authentication](docs/github-authentication.markdown)
|
||||
- [Reverse proxy authentication](docs/reverse-proxy-authentication.markdown)
|
||||
|
||||
#### Developers
|
||||
#### Developers and sysadmins
|
||||
|
||||
- [Board configuration](docs/board-configuration.markdown)
|
||||
- [Email configuration](docs/email-configuration.markdown)
|
||||
- [Command line interface](docs/cli.markdown)
|
||||
- [Json-RPC API](docs/api-json-rpc.markdown)
|
||||
- [How to use Kanboard with Vagrant](docs/vagrant.markdown)
|
||||
- [Webhooks](docs/webhooks.markdown)
|
||||
- [How to use Kanboard with Vagrant](docs/vagrant.markdown)
|
||||
|
||||
### Contributors
|
||||
|
||||
- [Translations](docs/translations.markdown)
|
||||
- [Coding standards](docs/coding-standards.markdown)
|
||||
- [Running tests](docs/tests.markdown)
|
||||
|
||||
The documentation is written in [Markdown](http://en.wikipedia.org/wiki/Markdown).
|
||||
If you want to improve the documentation, just send a pull-request.
|
||||
|
@ -122,3 +118,48 @@ FAQ
|
|||
---
|
||||
|
||||
Go to the official website: <http://kanboard.net/faq>
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
Original author: [Frédéric Guillot](http://fredericguillot.com/)
|
||||
|
||||
Contributors:
|
||||
|
||||
- Alex Butum
|
||||
- Ashish Kulkarni: https://github.com/ashkulz
|
||||
- Claudio Lobo
|
||||
- Cmer: https://github.com/chncsu
|
||||
- Floaltvater: https://github.com/floaltvater
|
||||
- Gavlepeter: https://github.com/gavlepeter
|
||||
- Janne Mäntyharju: https://github.com/JanneMantyharju
|
||||
- Jesusaplsoft: https://github.com/jesusaplsoft
|
||||
- Kiswa: https://github.com/kiswa
|
||||
- Kralo: https://github.com/kralo
|
||||
- Levlaz: https://github.com/levlaz
|
||||
- Lim Yuen Hoe: https://github.com/jasonmoofang
|
||||
- Mathgl67: https://github.com/mathgl67
|
||||
- Matthieu Keller: https://github.com/maggick
|
||||
- Mauro Mariño: https://github.com/moromarino
|
||||
- Maxime: https://github.com/EpocDotFr
|
||||
- Moraxy: https://github.com/moraxy
|
||||
- Nala Ginrut: https://github.com/NalaGinrut
|
||||
- Nekohayo: https://github.com/nekohayo
|
||||
- Nramel: https://github.com/nramel
|
||||
- Null-Kelvin: https://github.com/Null-Kelvin
|
||||
- Olivier Maridat: https://github.com/oliviermaridat
|
||||
- Poikilotherm: https://github.com/poikilotherm
|
||||
- Rafaelrossa: https://github.com/rafaelrossa
|
||||
- Raphaël Doursenaud: https://github.com/rdoursenaud
|
||||
- Rzeka: https://github.com/rzeka
|
||||
- Sebastien pacilly: https://github.com/spacilly
|
||||
- Sylvain Veyrié: https://github.com/turb
|
||||
- Toomyem: https://github.com/Toomyem
|
||||
- Tony G. Bolaño: https://github.com/tonybolanyo
|
||||
- Torsten: https://github.com/misterfu
|
||||
- Troloo: https://github.com/troloo
|
||||
- Typz: https://github.com/Typz
|
||||
- Vedovator: https://github.com/vedovator
|
||||
- Ybarc: https://github.com/ybarc
|
||||
|
||||
There is also many people who have reported bugs or proposed awesome ideas.
|
||||
|
|
|
@ -139,4 +139,15 @@ abstract class Base implements Listener
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return class information
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return get_called_class();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ class TaskAssignCategoryColor extends Base
|
|||
$this->task->update(array(
|
||||
'id' => $data['task_id'],
|
||||
'category_id' => $this->getParam('category_id'),
|
||||
));
|
||||
), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ class TaskAssignColorCategory extends Base
|
|||
$this->task->update(array(
|
||||
'id' => $data['task_id'],
|
||||
'color_id' => $this->getParam('color_id'),
|
||||
));
|
||||
), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ class TaskAssignColorUser extends Base
|
|||
$this->task->update(array(
|
||||
'id' => $data['task_id'],
|
||||
'color_id' => $this->getParam('color_id'),
|
||||
));
|
||||
), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ class TaskAssignCurrentUser extends Base
|
|||
$this->task->update(array(
|
||||
'id' => $data['task_id'],
|
||||
'owner_id' => $this->acl->getUserId(),
|
||||
));
|
||||
), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ class TaskAssignSpecificUser extends Base
|
|||
$this->task->update(array(
|
||||
'id' => $data['task_id'],
|
||||
'owner_id' => $this->getParam('user_id'),
|
||||
));
|
||||
), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -73,7 +73,8 @@ class TaskDuplicateAnotherProject extends Base
|
|||
{
|
||||
if ($data['column_id'] == $this->getParam('column_id') && $data['project_id'] != $this->getParam('project_id')) {
|
||||
|
||||
$this->task->duplicateToAnotherProject($data['task_id'], $this->getParam('project_id'));
|
||||
$task = $this->task->getById($data['task_id']);
|
||||
$this->task->duplicateToAnotherProject($this->getParam('project_id'), $task);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
84
sources/app/Action/TaskMoveAnotherProject.php
Normal file
84
sources/app/Action/TaskMoveAnotherProject.php
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Action;
|
||||
|
||||
use Model\Task;
|
||||
|
||||
/**
|
||||
* Move a task to another project
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskMoveAnotherProject extends Base
|
||||
{
|
||||
/**
|
||||
* Task model
|
||||
*
|
||||
* @accesss private
|
||||
* @var \Model\Task
|
||||
*/
|
||||
private $task;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param \Model\Task $task Task model instance
|
||||
*/
|
||||
public function __construct($project_id, Task $task)
|
||||
{
|
||||
parent::__construct($project_id);
|
||||
$this->task = $task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'column_id' => t('Column'),
|
||||
'project_id' => t('Project'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'column_id',
|
||||
'project_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
if ($data['column_id'] == $this->getParam('column_id') && $data['project_id'] != $this->getParam('project_id')) {
|
||||
|
||||
$task = $this->task->getById($data['task_id']);
|
||||
$this->task->moveToAnotherProject($this->getParam('project_id'), $task);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
59
sources/app/Auth/Base.php
Normal file
59
sources/app/Auth/Base.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Auth;
|
||||
|
||||
use Core\Tool;
|
||||
use Core\Registry;
|
||||
|
||||
/**
|
||||
* Base auth class
|
||||
*
|
||||
* @package auth
|
||||
* @author Frederic Guillot
|
||||
*
|
||||
* @property \Model\Acl $acl
|
||||
* @property \Model\LastLogin $lastLogin
|
||||
* @property \Model\User $user
|
||||
*/
|
||||
abstract class Base
|
||||
{
|
||||
/**
|
||||
* Database instance
|
||||
*
|
||||
* @access protected
|
||||
* @var \PicoDb\Database
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* Registry instance
|
||||
*
|
||||
* @access protected
|
||||
* @var \Core\Registry
|
||||
*/
|
||||
protected $registry;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Core\Registry $registry Registry instance
|
||||
*/
|
||||
public function __construct(Registry $registry)
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->db = $this->registry->shared('db');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load automatically models
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Model name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return Tool::loadModel($this->registry, $name);
|
||||
}
|
||||
}
|
52
sources/app/Auth/Database.php
Normal file
52
sources/app/Auth/Database.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace Auth;
|
||||
|
||||
use Model\User;
|
||||
|
||||
/**
|
||||
* Database authentication
|
||||
*
|
||||
* @package auth
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Database extends Base
|
||||
{
|
||||
/**
|
||||
* Backend name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const AUTH_NAME = 'Database';
|
||||
|
||||
/**
|
||||
* Authenticate a user
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Username
|
||||
* @param string $password Password
|
||||
* @return boolean
|
||||
*/
|
||||
public function authenticate($username, $password)
|
||||
{
|
||||
$user = $this->db->table(User::TABLE)->eq('username', $username)->eq('is_ldap_user', 0)->findOne();
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
|
||||
// Update user session
|
||||
$this->user->updateSession($user);
|
||||
|
||||
// Update login history
|
||||
$this->lastLogin->create(
|
||||
self::AUTH_NAME,
|
||||
$user['id'],
|
||||
$this->user->getIpAddress(),
|
||||
$this->user->getUserAgent()
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
namespace Auth;
|
||||
|
||||
require __DIR__.'/../../vendor/OAuth/bootstrap.php';
|
||||
|
||||
|
@ -11,12 +11,19 @@ use OAuth\ServiceFactory;
|
|||
use OAuth\Common\Http\Exception\TokenResponseException;
|
||||
|
||||
/**
|
||||
* GitHub model
|
||||
* GitHub backend
|
||||
*
|
||||
* @package model
|
||||
* @package auth
|
||||
*/
|
||||
class GitHub extends Base
|
||||
{
|
||||
/**
|
||||
* Backend name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const AUTH_NAME = 'Github';
|
||||
|
||||
/**
|
||||
* Authenticate a GitHub user
|
||||
*
|
||||
|
@ -26,22 +33,19 @@ class GitHub extends Base
|
|||
*/
|
||||
public function authenticate($github_id)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
$user = $userModel->getByGitHubId($github_id);
|
||||
$user = $this->user->getByGitHubId($github_id);
|
||||
|
||||
if ($user) {
|
||||
|
||||
// Create the user session
|
||||
$userModel->updateSession($user);
|
||||
$this->user->updateSession($user);
|
||||
|
||||
// Update login history
|
||||
$lastLogin = new LastLogin($this->db, $this->event);
|
||||
$lastLogin->create(
|
||||
LastLogin::AUTH_GITHUB,
|
||||
$this->lastLogin->create(
|
||||
self::AUTH_NAME,
|
||||
$user['id'],
|
||||
$userModel->getIpAddress(),
|
||||
$userModel->getUserAgent()
|
||||
$this->user->getIpAddress(),
|
||||
$this->user->getUserAgent()
|
||||
);
|
||||
|
||||
return true;
|
||||
|
@ -59,9 +63,7 @@ class GitHub extends Base
|
|||
*/
|
||||
public function unlink($user_id)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
return $userModel->update(array(
|
||||
return $this->user->update(array(
|
||||
'id' => $user_id,
|
||||
'github_id' => '',
|
||||
));
|
||||
|
@ -78,9 +80,7 @@ class GitHub extends Base
|
|||
*/
|
||||
public function updateUser($user_id, array $profile)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
return $userModel->update(array(
|
||||
return $this->user->update(array(
|
||||
'id' => $user_id,
|
||||
'github_id' => $profile['id'],
|
||||
'email' => $profile['email'],
|
||||
|
@ -147,8 +147,6 @@ class GitHub extends Base
|
|||
catch (TokenResponseException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,7 +170,5 @@ class GitHub extends Base
|
|||
catch (TokenResponseException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
namespace Auth;
|
||||
|
||||
require __DIR__.'/../../vendor/OAuth/bootstrap.php';
|
||||
|
||||
|
@ -11,13 +11,20 @@ use OAuth\ServiceFactory;
|
|||
use OAuth\Common\Http\Exception\TokenResponseException;
|
||||
|
||||
/**
|
||||
* Google model
|
||||
* Google backend
|
||||
*
|
||||
* @package model
|
||||
* @package auth
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Google extends Base
|
||||
{
|
||||
/**
|
||||
* Backend name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const AUTH_NAME = 'Google';
|
||||
|
||||
/**
|
||||
* Authenticate a Google user
|
||||
*
|
||||
|
@ -27,21 +34,19 @@ class Google extends Base
|
|||
*/
|
||||
public function authenticate($google_id)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
$user = $userModel->getByGoogleId($google_id);
|
||||
$user = $this->user->getByGoogleId($google_id);
|
||||
|
||||
if ($user) {
|
||||
|
||||
// Create the user session
|
||||
$userModel->updateSession($user);
|
||||
$this->user->updateSession($user);
|
||||
|
||||
// Update login history
|
||||
$lastLogin = new LastLogin($this->db, $this->event);
|
||||
$lastLogin->create(
|
||||
LastLogin::AUTH_GOOGLE,
|
||||
$this->lastLogin->create(
|
||||
self::AUTH_NAME,
|
||||
$user['id'],
|
||||
$userModel->getIpAddress(),
|
||||
$userModel->getUserAgent()
|
||||
$this->user->getIpAddress(),
|
||||
$this->user->getUserAgent()
|
||||
);
|
||||
|
||||
return true;
|
||||
|
@ -59,9 +64,7 @@ class Google extends Base
|
|||
*/
|
||||
public function unlink($user_id)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
return $userModel->update(array(
|
||||
return $this->user->update(array(
|
||||
'id' => $user_id,
|
||||
'google_id' => '',
|
||||
));
|
||||
|
@ -77,9 +80,7 @@ class Google extends Base
|
|||
*/
|
||||
public function updateUser($user_id, array $profile)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
return $userModel->update(array(
|
||||
return $this->user->update(array(
|
||||
'id' => $user_id,
|
||||
'google_id' => $profile['id'],
|
||||
'email' => $profile['email'],
|
||||
|
@ -146,7 +147,5 @@ class Google extends Base
|
|||
catch (TokenResponseException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
208
sources/app/Auth/Ldap.php
Normal file
208
sources/app/Auth/Ldap.php
Normal file
|
@ -0,0 +1,208 @@
|
|||
<?php
|
||||
|
||||
namespace Auth;
|
||||
|
||||
/**
|
||||
* LDAP model
|
||||
*
|
||||
* @package auth
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Ldap extends Base
|
||||
{
|
||||
/**
|
||||
* Backend name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const AUTH_NAME = 'LDAP';
|
||||
|
||||
/**
|
||||
* Authenticate the user
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Username
|
||||
* @param string $password Password
|
||||
* @return boolean
|
||||
*/
|
||||
public function authenticate($username, $password)
|
||||
{
|
||||
$result = $this->findUser($username, $password);
|
||||
|
||||
if (is_array($result)) {
|
||||
|
||||
$user = $this->user->getByUsername($username);
|
||||
|
||||
if ($user) {
|
||||
|
||||
// There is already a local user with that name
|
||||
if ($user['is_ldap_user'] == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// We create automatically a new user
|
||||
if ($this->createUser($username, $result['name'], $result['email'])) {
|
||||
$user = $this->user->getByUsername($username);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We open the session
|
||||
$this->user->updateSession($user);
|
||||
|
||||
// Update login history
|
||||
$this->lastLogin->create(
|
||||
self::AUTH_NAME,
|
||||
$user['id'],
|
||||
$this->user->getIpAddress(),
|
||||
$this->user->getUserAgent()
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new local user after the LDAP authentication
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Username
|
||||
* @param string $name Name of the user
|
||||
* @param string $email Email address
|
||||
* @return bool
|
||||
*/
|
||||
public function createUser($username, $name, $email)
|
||||
{
|
||||
$values = array(
|
||||
'username' => $username,
|
||||
'name' => $name,
|
||||
'email' => $email,
|
||||
'is_admin' => 0,
|
||||
'is_ldap_user' => 1,
|
||||
);
|
||||
|
||||
return $this->user->create($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the user from the LDAP server
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Username
|
||||
* @param string $password Password
|
||||
* @return boolean|array
|
||||
*/
|
||||
public function findUser($username, $password)
|
||||
{
|
||||
$ldap = $this->connect();
|
||||
|
||||
if ($this->bind($ldap, $username, $password)) {
|
||||
return $this->search($ldap, $username, $password);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP connection
|
||||
*
|
||||
* @access private
|
||||
* @return resource $ldap LDAP connection
|
||||
*/
|
||||
private function connect()
|
||||
{
|
||||
if (! function_exists('ldap_connect')) {
|
||||
die('The PHP LDAP extension is required');
|
||||
}
|
||||
|
||||
// Skip SSL certificate verification
|
||||
if (! LDAP_SSL_VERIFY) {
|
||||
putenv('LDAPTLS_REQCERT=never');
|
||||
}
|
||||
|
||||
$ldap = ldap_connect(LDAP_SERVER, LDAP_PORT);
|
||||
|
||||
if (! is_resource($ldap)) {
|
||||
die('Unable to connect to the LDAP server: "'.LDAP_SERVER.'"');
|
||||
}
|
||||
|
||||
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
|
||||
|
||||
return $ldap;
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP bind
|
||||
*
|
||||
* @access private
|
||||
* @param resource $ldap LDAP connection
|
||||
* @param string $username Username
|
||||
* @param string $password Password
|
||||
* @return boolean
|
||||
*/
|
||||
private function bind($ldap, $username, $password)
|
||||
{
|
||||
if (LDAP_BIND_TYPE === 'user') {
|
||||
$ldap_username = sprintf(LDAP_USERNAME, $username);
|
||||
$ldap_password = $password;
|
||||
}
|
||||
else if (LDAP_BIND_TYPE === 'proxy') {
|
||||
$ldap_username = LDAP_USERNAME;
|
||||
$ldap_password = LDAP_PASSWORD;
|
||||
}
|
||||
else {
|
||||
$ldap_username = null;
|
||||
$ldap_password = null;
|
||||
}
|
||||
|
||||
if (! @ldap_bind($ldap, $ldap_username, $ldap_password)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP user lookup
|
||||
*
|
||||
* @access private
|
||||
* @param resource $ldap LDAP connection
|
||||
* @param string $username Username
|
||||
* @param string $password Password
|
||||
* @return boolean|array
|
||||
*/
|
||||
private function search($ldap, $username, $password)
|
||||
{
|
||||
$sr = @ldap_search($ldap, LDAP_ACCOUNT_BASE, sprintf(LDAP_USER_PATTERN, $username), array(LDAP_ACCOUNT_FULLNAME, LDAP_ACCOUNT_EMAIL));
|
||||
|
||||
if ($sr === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$info = ldap_get_entries($ldap, $sr);
|
||||
|
||||
// User not found
|
||||
if (count($info) == 0 || $info['count'] == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We got our user
|
||||
if (@ldap_bind($ldap, $info[0]['dn'], $password)) {
|
||||
|
||||
return array(
|
||||
'username' => $username,
|
||||
'name' => isset($info[0][LDAP_ACCOUNT_FULLNAME][0]) ? $info[0][LDAP_ACCOUNT_FULLNAME][0] : '',
|
||||
'email' => isset($info[0][LDAP_ACCOUNT_EMAIL][0]) ? $info[0][LDAP_ACCOUNT_EMAIL][0] : '',
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,17 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
namespace Auth;
|
||||
|
||||
use Core\Security;
|
||||
use Core\Tool;
|
||||
|
||||
/**
|
||||
* RememberMe model
|
||||
*
|
||||
* @package model
|
||||
* @package auth
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class RememberMe extends Base
|
||||
{
|
||||
/**
|
||||
* Backend name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const AUTH_NAME = 'RememberMe';
|
||||
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
|
@ -92,11 +100,16 @@ class RememberMe extends Base
|
|||
);
|
||||
|
||||
// Create the session
|
||||
$user = new User($this->db, $this->event);
|
||||
$acl = new Acl($this->db, $this->event);
|
||||
$this->user->updateSession($this->user->getById($record['user_id']));
|
||||
$this->acl->isRememberMe(true);
|
||||
|
||||
$user->updateSession($user->getById($record['user_id']));
|
||||
$acl->isRememberMe(true);
|
||||
// Update last login infos
|
||||
$this->lastLogin->create(
|
||||
self::AUTH_NAME,
|
||||
$this->acl->getUserId(),
|
||||
$this->user->getIpAddress(),
|
||||
$this->user->getUserAgent()
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -297,7 +310,7 @@ class RememberMe extends Base
|
|||
$expiration,
|
||||
BASE_URL_DIRECTORY,
|
||||
null,
|
||||
! empty($_SERVER['HTTPS']),
|
||||
Tool::isHTTPS(),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
@ -330,7 +343,7 @@ class RememberMe extends Base
|
|||
time() - 3600,
|
||||
BASE_URL_DIRECTORY,
|
||||
null,
|
||||
! empty($_SERVER['HTTPS']),
|
||||
Tool::isHTTPS(),
|
||||
true
|
||||
);
|
||||
}
|
79
sources/app/Auth/ReverseProxy.php
Normal file
79
sources/app/Auth/ReverseProxy.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace Auth;
|
||||
|
||||
use Core\Security;
|
||||
|
||||
/**
|
||||
* ReverseProxy backend
|
||||
*
|
||||
* @package auth
|
||||
* @author Sylvain Veyrié
|
||||
*/
|
||||
class ReverseProxy extends Base
|
||||
{
|
||||
/**
|
||||
* Backend name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const AUTH_NAME = 'ReverseProxy';
|
||||
|
||||
/**
|
||||
* Authenticate the user with the HTTP header
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function authenticate()
|
||||
{
|
||||
if (isset($_SERVER[REVERSE_PROXY_USER_HEADER])) {
|
||||
|
||||
$login = $_SERVER[REVERSE_PROXY_USER_HEADER];
|
||||
$user = $this->user->getByUsername($login);
|
||||
|
||||
if (! $user) {
|
||||
$this->createUser($login);
|
||||
$user = $this->user->getByUsername($login);
|
||||
}
|
||||
|
||||
// Create the user session
|
||||
$this->user->updateSession($user);
|
||||
|
||||
// Update login history
|
||||
$this->lastLogin->create(
|
||||
self::AUTH_NAME,
|
||||
$user['id'],
|
||||
$this->user->getIpAddress(),
|
||||
$this->user->getUserAgent()
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create automatically a new local user after the authentication
|
||||
*
|
||||
* @access private
|
||||
* @param string $login Username
|
||||
* @return bool
|
||||
*/
|
||||
private function createUser($login)
|
||||
{
|
||||
$email = strpos($login, '@') !== false ? $login : '';
|
||||
|
||||
if (REVERSE_PROXY_DEFAULT_DOMAIN !== '' && empty($email)) {
|
||||
$email = $login.'@'.REVERSE_PROXY_DEFAULT_DOMAIN;
|
||||
}
|
||||
|
||||
return $this->user->create(array(
|
||||
'email' => $email,
|
||||
'username' => $login,
|
||||
'is_admin' => REVERSE_PROXY_DEFAULT_ADMIN === $login,
|
||||
'is_ldap_user' => 1,
|
||||
));
|
||||
}
|
||||
}
|
|
@ -17,15 +17,9 @@ class Action extends Base
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
$project = $this->getProject();
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('action_index', array(
|
||||
$this->response->html($this->projectLayout('action_index', array(
|
||||
'values' => array('project_id' => $project['id']),
|
||||
'project' => $project,
|
||||
'actions' => $this->action->getAllByProject($project['id']),
|
||||
|
@ -49,18 +43,11 @@ class Action extends Base
|
|||
*/
|
||||
public function params()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getValues();
|
||||
$action = $this->action->load($values['action_name'], $values['project_id']);
|
||||
|
||||
$this->response->html($this->template->layout('action_params', array(
|
||||
$this->response->html($this->projectLayout('action_params', array(
|
||||
'values' => $values,
|
||||
'action_params' => $action->getActionRequiredParameters(),
|
||||
'columns_list' => $this->board->getColumnsList($project['id']),
|
||||
|
@ -81,14 +68,7 @@ class Action extends Base
|
|||
*/
|
||||
public function create()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
list($valid,) = $this->action->validateCreation($values);
|
||||
|
@ -113,10 +93,13 @@ class Action extends Base
|
|||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$this->response->html($this->template->layout('action_remove', array(
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('action_remove', array(
|
||||
'action' => $this->action->getById($this->request->getIntegerParam('action_id')),
|
||||
'available_events' => $this->action->getAvailableEvents(),
|
||||
'available_actions' => $this->action->getAvailableActions(),
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Remove an action')
|
||||
)));
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Controller;
|
||||
|
||||
use Core\Tool;
|
||||
use Core\Registry;
|
||||
use Core\Security;
|
||||
use Core\Translator;
|
||||
|
@ -12,22 +13,25 @@ use Model\LastLogin;
|
|||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
* @property \Model\Acl $acl
|
||||
* @property \Model\Action $action
|
||||
* @property \Model\Board $board
|
||||
* @property \Model\Category $category
|
||||
* @property \Model\Comment $comment
|
||||
* @property \Model\Config $config
|
||||
* @property \Model\File $file
|
||||
* @property \Model\Google $google
|
||||
* @property \Model\GitHub $gitHub
|
||||
* @property \Model\LastLogin $lastLogin
|
||||
* @property \Model\Ldap $ldap
|
||||
* @property \Model\Project $project
|
||||
* @property \Model\RememberMe $rememberMe
|
||||
* @property \Model\SubTask $subTask
|
||||
* @property \Model\Task $task
|
||||
* @property \Model\User $user
|
||||
*
|
||||
* @property \Model\Acl $acl
|
||||
* @property \Model\Authentication $authentication
|
||||
* @property \Model\Action $action
|
||||
* @property \Model\Board $board
|
||||
* @property \Model\Category $category
|
||||
* @property \Model\Comment $comment
|
||||
* @property \Model\Config $config
|
||||
* @property \Model\File $file
|
||||
* @property \Model\LastLogin $lastLogin
|
||||
* @property \Model\Notification $notification
|
||||
* @property \Model\Project $project
|
||||
* @property \Model\SubTask $subTask
|
||||
* @property \Model\Task $task
|
||||
* @property \Model\TaskHistory $taskHistory
|
||||
* @property \Model\CommentHistory $commentHistory
|
||||
* @property \Model\SubtaskHistory $subtaskHistory
|
||||
* @property \Model\User $user
|
||||
* @property \Model\Webhook $webhook
|
||||
*/
|
||||
abstract class Base
|
||||
{
|
||||
|
@ -91,9 +95,7 @@ abstract class Base
|
|||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
$class = '\Model\\'.ucfirst($name);
|
||||
$this->registry->$name = new $class($this->registry->shared('db'), $this->registry->shared('event'));
|
||||
return $this->registry->shared($name);
|
||||
return Tool::loadModel($this->registry, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -121,26 +123,8 @@ abstract class Base
|
|||
date_default_timezone_set($this->config->get('timezone', 'UTC'));
|
||||
|
||||
// Authentication
|
||||
if (! $this->acl->isLogged() && ! $this->acl->isPublicAction($controller, $action)) {
|
||||
|
||||
// Try the remember me authentication first
|
||||
if (! $this->rememberMe->authenticate()) {
|
||||
|
||||
// Redirect to the login form if not authenticated
|
||||
$this->response->redirect('?controller=user&action=login');
|
||||
}
|
||||
else {
|
||||
|
||||
$this->lastLogin->create(
|
||||
LastLogin::AUTH_REMEMBER_ME,
|
||||
$this->acl->getUserId(),
|
||||
$this->user->getIpAddress(),
|
||||
$this->user->getUserAgent()
|
||||
);
|
||||
}
|
||||
}
|
||||
else if ($this->rememberMe->hasCookie()) {
|
||||
$this->rememberMe->refresh();
|
||||
if (! $this->authentication->isAuthenticated($controller, $action)) {
|
||||
$this->response->redirect('?controller=user&action=login');
|
||||
}
|
||||
|
||||
// Check if the user is allowed to see this page
|
||||
|
@ -149,28 +133,57 @@ abstract class Base
|
|||
}
|
||||
|
||||
// Attach events
|
||||
$this->action->attachEvents();
|
||||
$this->project->attachEvents();
|
||||
$this->attachEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach events
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private function attachEvents()
|
||||
{
|
||||
$models = array(
|
||||
'action',
|
||||
'project',
|
||||
'webhook',
|
||||
'notification',
|
||||
'taskHistory',
|
||||
'commentHistory',
|
||||
'subtaskHistory',
|
||||
);
|
||||
|
||||
foreach ($models as $model) {
|
||||
$this->$model->attachEvents();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Application not found page (404 error)
|
||||
*
|
||||
* @access public
|
||||
* @param boolean $no_layout Display the layout or not
|
||||
*/
|
||||
public function notfound()
|
||||
public function notfound($no_layout = false)
|
||||
{
|
||||
$this->response->html($this->template->layout('app_notfound', array('title' => t('Page not found'))));
|
||||
$this->response->html($this->template->layout('app_notfound', array(
|
||||
'title' => t('Page not found'),
|
||||
'no_layout' => $no_layout,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Application forbidden page
|
||||
*
|
||||
* @access public
|
||||
* @param boolean $no_layout Display the layout or not
|
||||
*/
|
||||
public function forbidden()
|
||||
public function forbidden($no_layout = false)
|
||||
{
|
||||
$this->response->html($this->template->layout('app_forbidden', array('title' => t('Access Forbidden'))));
|
||||
$this->response->html($this->template->layout('app_forbidden', array(
|
||||
'title' => t('Access Forbidden'),
|
||||
'no_layout' => $no_layout,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -228,6 +241,22 @@ abstract class Base
|
|||
return $this->template->layout('task_layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common layout for project views
|
||||
*
|
||||
* @access protected
|
||||
* @param string $template Template name
|
||||
* @param array $params Template parameters
|
||||
* @return string
|
||||
*/
|
||||
protected function projectLayout($template, array $params)
|
||||
{
|
||||
$content = $this->template->load($template, $params);
|
||||
$params['project_content_for_layout'] = $content;
|
||||
|
||||
return $this->template->layout('project_layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common method to get a task for task views
|
||||
*
|
||||
|
@ -246,4 +275,26 @@ abstract class Base
|
|||
|
||||
return $task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common method to get a project
|
||||
*
|
||||
* @access protected
|
||||
* @param integer $project_id Default project id
|
||||
* @return array
|
||||
*/
|
||||
protected function getProject($project_id = 0)
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id', $project_id);
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$this->checkProjectPermissions($project['id']);
|
||||
|
||||
return $project;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,39 +51,27 @@ class Board extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function assign()
|
||||
public function changeAssignee()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'));
|
||||
$task = $this->getTask();
|
||||
$project = $this->project->getById($task['project_id']);
|
||||
$projects = $this->project->getListByStatus(ProjectModel::ACTIVE);
|
||||
|
||||
if ($this->acl->isRegularUser()) {
|
||||
$projects = $this->project->filterListByAccess($projects, $this->acl->getUserId());
|
||||
}
|
||||
|
||||
if (! $project) $this->notfound();
|
||||
$this->checkProjectPermissions($project['id']);
|
||||
$projects = $this->project->getAvailableList($this->acl->getUserId());
|
||||
$params = array(
|
||||
'errors' => array(),
|
||||
'values' => $task,
|
||||
'users_list' => $this->project->getUsersList($project['id']),
|
||||
'projects' => $projects,
|
||||
'current_project_id' => $project['id'],
|
||||
'current_project_name' => $project['name'],
|
||||
);
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
|
||||
$this->response->html($this->template->load('board_assign', array(
|
||||
'errors' => array(),
|
||||
'values' => $task,
|
||||
'users_list' => $this->project->getUsersList($project['id']),
|
||||
'projects' => $projects,
|
||||
'current_project_id' => $project['id'],
|
||||
'current_project_name' => $project['name'],
|
||||
)));
|
||||
$this->response->html($this->template->load('board_assignee', $params));
|
||||
}
|
||||
else {
|
||||
|
||||
$this->response->html($this->template->layout('board_assign', array(
|
||||
'errors' => array(),
|
||||
'values' => $task,
|
||||
'users_list' => $this->project->getUsersList($project['id']),
|
||||
'projects' => $projects,
|
||||
'current_project_id' => $project['id'],
|
||||
'current_project_name' => $project['name'],
|
||||
$this->response->html($this->template->layout('board_assignee', $params + array(
|
||||
'menu' => 'boards',
|
||||
'title' => t('Change assignee').' - '.$task['title'],
|
||||
)));
|
||||
|
@ -95,7 +83,7 @@ class Board extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function assignTask()
|
||||
public function updateAssignee()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
$this->checkProjectPermissions($values['project_id']);
|
||||
|
@ -112,6 +100,60 @@ class Board extends Base
|
|||
$this->response->redirect('?controller=board&action=show&project_id='.$values['project_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a task category directly from the board
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function changeCategory()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$project = $this->project->getById($task['project_id']);
|
||||
$projects = $this->project->getAvailableList($this->acl->getUserId());
|
||||
$params = array(
|
||||
'errors' => array(),
|
||||
'values' => $task,
|
||||
'categories_list' => $this->category->getList($project['id']),
|
||||
'projects' => $projects,
|
||||
'current_project_id' => $project['id'],
|
||||
'current_project_name' => $project['name'],
|
||||
);
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
|
||||
$this->response->html($this->template->load('board_category', $params));
|
||||
}
|
||||
else {
|
||||
|
||||
$this->response->html($this->template->layout('board_category', $params + array(
|
||||
'menu' => 'boards',
|
||||
'title' => t('Change category').' - '.$task['title'],
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a category modification
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function updateCategory()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
$this->checkProjectPermissions($values['project_id']);
|
||||
|
||||
list($valid,) = $this->task->validateCategoryModification($values);
|
||||
|
||||
if ($valid && $this->task->update($values)) {
|
||||
$this->session->flash(t('Task updated successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update your task.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=board&action=show&project_id='.$values['project_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the public version of a board
|
||||
* Access checked by a simple token, no user login, read only, auto-refresh
|
||||
|
@ -125,7 +167,7 @@ class Board extends Base
|
|||
|
||||
// Token verification
|
||||
if (! $project) {
|
||||
$this->response->text('Not Authorized', 401);
|
||||
$this->forbidden(true);
|
||||
}
|
||||
|
||||
// Display the board with a specific layout
|
||||
|
@ -136,6 +178,7 @@ class Board extends Base
|
|||
'title' => $project['name'],
|
||||
'no_layout' => true,
|
||||
'auto_refresh' => true,
|
||||
'not_editable' => true,
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -146,62 +189,55 @@ class Board extends Base
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$projects = $this->project->getListByStatus(ProjectModel::ACTIVE);
|
||||
$project_id = 0;
|
||||
$project_name = '';
|
||||
$last_seen_project_id = $this->user->getLastSeenProjectId();
|
||||
$favorite_project_id = $this->user->getFavoriteProjectId();
|
||||
$project_id = $last_seen_project_id ?: $favorite_project_id;
|
||||
|
||||
if ($this->acl->isRegularUser()) {
|
||||
$projects = $this->project->filterListByAccess($projects, $this->acl->getUserId());
|
||||
}
|
||||
if (! $project_id) {
|
||||
$projects = $this->project->getAvailableList($this->acl->getUserId());
|
||||
|
||||
if (empty($projects)) {
|
||||
if (empty($projects)) {
|
||||
|
||||
if ($this->acl->isAdminUser()) {
|
||||
$this->redirectNoProject();
|
||||
if ($this->acl->isAdminUser()) {
|
||||
$this->redirectNoProject();
|
||||
}
|
||||
|
||||
$this->forbidden();
|
||||
}
|
||||
else {
|
||||
$this->response->redirect('?controller=project&action=forbidden');
|
||||
}
|
||||
}
|
||||
else if (! empty($_SESSION['user']['default_project_id']) && isset($projects[$_SESSION['user']['default_project_id']])) {
|
||||
$project_id = $_SESSION['user']['default_project_id'];
|
||||
$project_name = $projects[$_SESSION['user']['default_project_id']];
|
||||
}
|
||||
else {
|
||||
list($project_id, $project_name) = each($projects);
|
||||
|
||||
$project_id = key($projects);
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=board&action=show&project_id='.$project_id);
|
||||
$this->show($project_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a board for a given project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Default project id
|
||||
*/
|
||||
public function show()
|
||||
public function show($project_id = 0)
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$user_id = $this->request->getIntegerParam('user_id', UserModel::EVERYBODY_ID);
|
||||
|
||||
$this->checkProjectPermissions($project_id);
|
||||
$project = $this->getProject($project_id);
|
||||
$projects = $this->project->getAvailableList($this->acl->getUserId());
|
||||
|
||||
if (! isset($projects[$project_id])) {
|
||||
$this->notfound();
|
||||
}
|
||||
$board_selector = $projects;
|
||||
unset($board_selector[$project['id']]);
|
||||
|
||||
$this->user->storeLastSeenProjectId($project['id']);
|
||||
|
||||
$this->response->html($this->template->layout('board_index', array(
|
||||
'users' => $this->project->getUsersList($project_id, true, true),
|
||||
'filters' => array('user_id' => $user_id),
|
||||
'users' => $this->project->getUsersList($project['id'], true, true),
|
||||
'filters' => array('user_id' => UserModel::EVERYBODY_ID),
|
||||
'projects' => $projects,
|
||||
'current_project_id' => $project_id,
|
||||
'current_project_name' => $projects[$project_id],
|
||||
'board' => $this->board->get($project_id),
|
||||
'categories' => $this->category->getList($project_id, true, true),
|
||||
'current_project_id' => $project['id'],
|
||||
'current_project_name' => $projects[$project['id']],
|
||||
'board' => $this->board->get($project['id']),
|
||||
'categories' => $this->category->getList($project['id'], true, true),
|
||||
'menu' => 'boards',
|
||||
'title' => $projects[$project_id],
|
||||
'board_selector' => $projects,
|
||||
'title' => $projects[$project['id']],
|
||||
'board_selector' => $board_selector,
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -212,12 +248,8 @@ class Board extends Base
|
|||
*/
|
||||
public function edit()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) $this->notfound();
|
||||
|
||||
$columns = $this->board->getColumns($project_id);
|
||||
$project = $this->getProject();
|
||||
$columns = $this->board->getColumns($project['id']);
|
||||
$values = array();
|
||||
|
||||
foreach ($columns as $column) {
|
||||
|
@ -225,9 +257,9 @@ class Board extends Base
|
|||
$values['task_limit['.$column['id'].']'] = $column['task_limit'] ?: null;
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('board_edit', array(
|
||||
$this->response->html($this->projectLayout('board_edit', array(
|
||||
'errors' => array(),
|
||||
'values' => $values + array('project_id' => $project_id),
|
||||
'values' => $values + array('project_id' => $project['id']),
|
||||
'columns' => $columns,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
|
@ -242,12 +274,8 @@ class Board extends Base
|
|||
*/
|
||||
public function update()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) $this->notfound();
|
||||
|
||||
$columns = $this->board->getColumns($project_id);
|
||||
$project = $this->getProject();
|
||||
$columns = $this->board->getColumns($project['id']);
|
||||
$data = $this->request->getValues();
|
||||
$values = $columns_list = array();
|
||||
|
||||
|
@ -270,9 +298,9 @@ class Board extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('board_edit', array(
|
||||
$this->response->html($this->projectLayout('board_edit', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values + array('project_id' => $project_id),
|
||||
'values' => $values + array('project_id' => $project['id']),
|
||||
'columns' => $columns,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
|
@ -287,12 +315,8 @@ class Board extends Base
|
|||
*/
|
||||
public function add()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) $this->notfound();
|
||||
|
||||
$columns = $this->board->getColumnsList($project_id);
|
||||
$project = $this->getProject();
|
||||
$columns = $this->board->getColumnsList($project['id']);
|
||||
$data = $this->request->getValues();
|
||||
$values = array();
|
||||
|
||||
|
@ -304,7 +328,7 @@ class Board extends Base
|
|||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->board->add($data)) {
|
||||
if ($this->board->addColumn($project['id'], $data['title'])) {
|
||||
$this->session->flash(t('Board updated successfully.'));
|
||||
$this->response->redirect('?controller=board&action=edit&project_id='.$project['id']);
|
||||
}
|
||||
|
@ -313,7 +337,7 @@ class Board extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('board_edit', array(
|
||||
$this->response->html($this->projectLayout('board_edit', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values + $data,
|
||||
'columns' => $columns,
|
||||
|
@ -330,8 +354,11 @@ class Board extends Base
|
|||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$this->response->html($this->template->layout('board_remove', array(
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('board_remove', array(
|
||||
'column' => $this->board->getColumn($this->request->getIntegerParam('column_id')),
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Remove a column from a board')
|
||||
)));
|
||||
|
@ -363,27 +390,31 @@ class Board extends Base
|
|||
*/
|
||||
public function save()
|
||||
{
|
||||
if ($this->request->isAjax()) {
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
|
||||
if ($project_id > 0 && $this->request->isAjax()) {
|
||||
|
||||
if (! $this->project->isUserAllowed($project_id, $this->acl->getUserId())) {
|
||||
$this->response->status(401);
|
||||
}
|
||||
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if ($project_id > 0 && ! $this->project->isUserAllowed($project_id, $this->acl->getUserId())) {
|
||||
$this->response->text('Not Authorized', 401);
|
||||
}
|
||||
if ($this->task->movePosition($project_id, $values['task_id'], $values['column_id'], $values['position'])) {
|
||||
|
||||
if (isset($values['positions'])) {
|
||||
$this->board->saveTasksPosition($values['positions']);
|
||||
$this->response->html(
|
||||
$this->template->load('board_show', array(
|
||||
'current_project_id' => $project_id,
|
||||
'board' => $this->board->get($project_id),
|
||||
'categories' => $this->category->getList($project_id, false),
|
||||
)),
|
||||
201
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
||||
$this->response->html(
|
||||
$this->template->load('board_show', array(
|
||||
'current_project_id' => $project_id,
|
||||
'board' => $this->board->get($project_id),
|
||||
'categories' => $this->category->getList($project_id, false),
|
||||
)),
|
||||
201
|
||||
);
|
||||
$this->response->status(400);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->response->status(401);
|
||||
|
|
|
@ -10,25 +10,6 @@ namespace Controller;
|
|||
*/
|
||||
class Category extends Base
|
||||
{
|
||||
/**
|
||||
* Get the current project (common method between actions)
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function getProject()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
return $project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category (common method between actions)
|
||||
*
|
||||
|
@ -57,7 +38,7 @@ class Category extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->template->layout('category_index', array(
|
||||
$this->response->html($this->projectLayout('category_index', array(
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'values' => array('project_id' => $project['id']),
|
||||
'errors' => array(),
|
||||
|
@ -90,7 +71,7 @@ class Category extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('category_index', array(
|
||||
$this->response->html($this->projectLayout('category_index', array(
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
|
@ -110,7 +91,7 @@ class Category extends Base
|
|||
$project = $this->getProject();
|
||||
$category = $this->getCategory($project['id']);
|
||||
|
||||
$this->response->html($this->template->layout('category_edit', array(
|
||||
$this->response->html($this->projectLayout('category_edit', array(
|
||||
'values' => $category,
|
||||
'errors' => array(),
|
||||
'project' => $project,
|
||||
|
@ -142,7 +123,7 @@ class Category extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('category_edit', array(
|
||||
$this->response->html($this->projectLayout('category_edit', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
|
@ -161,7 +142,7 @@ class Category extends Base
|
|||
$project = $this->getProject();
|
||||
$category = $this->getCategory($project['id']);
|
||||
|
||||
$this->response->html($this->template->layout('category_remove', array(
|
||||
$this->response->html($this->projectLayout('category_remove', array(
|
||||
'project' => $project,
|
||||
'category' => $category,
|
||||
'menu' => 'projects',
|
||||
|
|
|
@ -25,25 +25,15 @@ class Comment extends Base
|
|||
}
|
||||
|
||||
if (! $this->acl->isAdminUser() && $comment['user_id'] != $this->acl->getUserId()) {
|
||||
$this->forbidden();
|
||||
$this->response->html($this->template->layout('comment_forbidden', array(
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Access Forbidden')
|
||||
)));
|
||||
}
|
||||
|
||||
return $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forbidden page for comments
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function forbidden()
|
||||
{
|
||||
$this->response->html($this->template->layout('comment_forbidden', array(
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Access Forbidden')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add comment form
|
||||
*
|
||||
|
|
|
@ -19,16 +19,13 @@ class Config extends Base
|
|||
{
|
||||
$this->response->html($this->template->layout('config_index', array(
|
||||
'db_size' => $this->config->getDatabaseSize(),
|
||||
'user' => $_SESSION['user'],
|
||||
'projects' => $this->project->getList(),
|
||||
'languages' => $this->config->getLanguages(),
|
||||
'values' => $this->config->getAll(),
|
||||
'errors' => array(),
|
||||
'menu' => 'config',
|
||||
'title' => t('Settings'),
|
||||
'timezones' => $this->config->getTimezones(),
|
||||
'remember_me_sessions' => $this->rememberMe->getAll($this->acl->getUserId()),
|
||||
'last_logins' => $this->lastLogin->getAll($this->acl->getUserId()),
|
||||
'default_columns' => implode(', ', $this->board->getDefaultColumns()),
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -56,16 +53,13 @@ class Config extends Base
|
|||
|
||||
$this->response->html($this->template->layout('config_index', array(
|
||||
'db_size' => $this->config->getDatabaseSize(),
|
||||
'user' => $_SESSION['user'],
|
||||
'projects' => $this->project->getList(),
|
||||
'languages' => $this->config->getLanguages(),
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'menu' => 'config',
|
||||
'title' => t('Settings'),
|
||||
'timezones' => $this->config->getTimezones(),
|
||||
'remember_me_sessions' => $this->rememberMe->getAll($this->acl->getUserId()),
|
||||
'last_logins' => $this->lastLogin->getAll($this->acl->getUserId()),
|
||||
'default_columns' => implode(', ', $this->board->getDefaultColumns()),
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -106,16 +100,4 @@ class Config extends Base
|
|||
$this->session->flash(t('All tokens have been regenerated.'));
|
||||
$this->response->redirect('?controller=config');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a "RememberMe" token
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function removeRememberMeToken()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$this->rememberMe->remove($this->request->getIntegerParam('id'));
|
||||
$this->response->redirect('?controller=config&action=index#remember-me');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Controller;
|
||||
|
||||
use Model\Task as TaskModel;
|
||||
use Core\Translator;
|
||||
|
||||
/**
|
||||
* Project controller
|
||||
|
@ -12,94 +13,6 @@ use Model\Task as TaskModel;
|
|||
*/
|
||||
class Project extends Base
|
||||
{
|
||||
/**
|
||||
* Task search for a given project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function search()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$search = $this->request->getStringParam('search');
|
||||
|
||||
$project = $this->project->getById($project_id);
|
||||
$tasks = array();
|
||||
$nb_tasks = 0;
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$this->checkProjectPermissions($project['id']);
|
||||
|
||||
if ($search !== '') {
|
||||
|
||||
$filters = array(
|
||||
array('column' => 'project_id', 'operator' => 'eq', 'value' => $project_id),
|
||||
'or' => array(
|
||||
array('column' => 'title', 'operator' => 'like', 'value' => '%'.$search.'%'),
|
||||
//array('column' => 'description', 'operator' => 'like', 'value' => '%'.$search.'%'),
|
||||
)
|
||||
);
|
||||
|
||||
$tasks = $this->task->find($filters);
|
||||
$nb_tasks = count($tasks);
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_search', array(
|
||||
'tasks' => $tasks,
|
||||
'nb_tasks' => $nb_tasks,
|
||||
'values' => array(
|
||||
'search' => $search,
|
||||
'controller' => 'project',
|
||||
'action' => 'search',
|
||||
'project_id' => $project['id'],
|
||||
),
|
||||
'menu' => 'projects',
|
||||
'project' => $project,
|
||||
'columns' => $this->board->getColumnsList($project_id),
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'title' => $project['name'].($nb_tasks > 0 ? ' ('.$nb_tasks.')' : '')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* List of completed tasks for a given project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function tasks()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$this->checkProjectPermissions($project['id']);
|
||||
|
||||
$filters = array(
|
||||
array('column' => 'project_id', 'operator' => 'eq', 'value' => $project_id),
|
||||
array('column' => 'is_active', 'operator' => 'eq', 'value' => TaskModel::STATUS_CLOSED),
|
||||
);
|
||||
|
||||
$tasks = $this->task->find($filters);
|
||||
$nb_tasks = count($tasks);
|
||||
|
||||
$this->response->html($this->template->layout('project_tasks', array(
|
||||
'menu' => 'projects',
|
||||
'project' => $project,
|
||||
'columns' => $this->board->getColumnsList($project_id),
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'tasks' => $tasks,
|
||||
'nb_tasks' => $nb_tasks,
|
||||
'title' => $project['name'].' ('.$nb_tasks.')'
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* List of projects
|
||||
*
|
||||
|
@ -107,11 +20,23 @@ class Project extends Base
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$projects = $this->project->getAll(true, $this->acl->isRegularUser());
|
||||
$projects = $this->project->getAll($this->acl->isRegularUser());
|
||||
$nb_projects = count($projects);
|
||||
$active_projects = array();
|
||||
$inactive_projects = array();
|
||||
|
||||
foreach ($projects as $project) {
|
||||
if ($project['is_active'] == 1) {
|
||||
$active_projects[] = $project;
|
||||
}
|
||||
else {
|
||||
$inactive_projects[] = $project;
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_index', array(
|
||||
'projects' => $projects,
|
||||
'active_projects' => $active_projects,
|
||||
'inactive_projects' => $inactive_projects,
|
||||
'nb_projects' => $nb_projects,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Projects').' ('.$nb_projects.')'
|
||||
|
@ -119,49 +44,108 @@ class Project extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Display a form to create a new project
|
||||
* Show the project information page
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
public function show()
|
||||
{
|
||||
$this->response->html($this->template->layout('project_new', array(
|
||||
'errors' => array(),
|
||||
'values' => array(),
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('project_show', array(
|
||||
'project' => $project,
|
||||
'stats' => $this->project->getStats($project['id']),
|
||||
'menu' => 'projects',
|
||||
'title' => t('New project')
|
||||
'title' => $project['name'],
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save a new project
|
||||
* Task export
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
public function export()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->project->validateCreation($values);
|
||||
$project = $this->getProject();
|
||||
$from = $this->request->getStringParam('from');
|
||||
$to = $this->request->getStringParam('to');
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->project->create($values)) {
|
||||
$this->session->flash(t('Your project have been created successfully.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to create your project.'));
|
||||
}
|
||||
if ($from && $to) {
|
||||
$data = $this->task->export($project['id'], $from, $to);
|
||||
$this->response->forceDownload('Export_'.date('Y_m_d_H_i_S').'.csv');
|
||||
$this->response->csv($data);
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_new', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
$this->response->html($this->projectLayout('project_export', array(
|
||||
'values' => array(
|
||||
'controller' => 'project',
|
||||
'action' => 'export',
|
||||
'project_id' => $project['id'],
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
),
|
||||
'errors' => array(),
|
||||
'menu' => 'projects',
|
||||
'title' => t('New Project')
|
||||
'project' => $project,
|
||||
'title' => t('Tasks Export')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Public access management
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function share()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('project_share', array(
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Public access'),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable public access for a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function enablePublic()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
|
||||
if ($project_id && $this->project->enablePublicAccess($project_id)) {
|
||||
$this->session->flash(t('Project updated successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to update this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=project&action=share&project_id='.$project_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable public access for a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function disablePublic()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
|
||||
if ($project_id && $this->project->disablePublicAccess($project_id)) {
|
||||
$this->session->flash(t('Project updated successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to update this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=project&action=share&project_id='.$project_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to edit a project
|
||||
*
|
||||
|
@ -169,16 +153,12 @@ class Project extends Base
|
|||
*/
|
||||
public function edit()
|
||||
{
|
||||
$project = $this->project->getById($this->request->getIntegerParam('project_id'));
|
||||
$project = $this->getProject();
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_edit', array(
|
||||
$this->response->html($this->projectLayout('project_edit', array(
|
||||
'errors' => array(),
|
||||
'values' => $project,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Edit project')
|
||||
)));
|
||||
|
@ -191,6 +171,7 @@ class Project extends Base
|
|||
*/
|
||||
public function update()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getValues() + array('is_active' => 0);
|
||||
list($valid, $errors) = $this->project->validateModification($values);
|
||||
|
||||
|
@ -198,114 +179,32 @@ class Project extends Base
|
|||
|
||||
if ($this->project->update($values)) {
|
||||
$this->session->flash(t('Project updated successfully.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
$this->response->redirect('?controller=project&action=edit&project_id='.$project['id']);
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update this project.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_edit', array(
|
||||
$this->response->html($this->projectLayout('project_edit', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Edit Project')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before to remove a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$project = $this->project->getById($this->request->getIntegerParam('project_id'));
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_remove', array(
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Remove project')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
|
||||
if ($project_id && $this->project->remove($project_id)) {
|
||||
$this->session->flash(t('Project removed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to remove this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function enable()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
|
||||
if ($project_id && $this->project->enable($project_id)) {
|
||||
$this->session->flash(t('Project activated successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to activate this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function disable()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
|
||||
if ($project_id && $this->project->disable($project_id)) {
|
||||
$this->session->flash(t('Project disabled successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to disable this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Users list for the selected project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
$project = $this->project->getById($this->request->getIntegerParam('project_id'));
|
||||
$project = $this->getProject();
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_users', array(
|
||||
$this->response->html($this->projectLayout('project_users', array(
|
||||
'project' => $project,
|
||||
'users' => $this->project->getAllUsers($project['id']),
|
||||
'menu' => 'projects',
|
||||
|
@ -364,4 +263,298 @@ class Project extends Base
|
|||
|
||||
$this->response->redirect('?controller=project&action=users&project_id='.$values['project_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before to remove a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirmRemove()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('project_remove', array(
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Remove project')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
|
||||
if ($project_id && $this->project->remove($project_id)) {
|
||||
$this->session->flash(t('Project removed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to remove this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before to clone a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirmDuplicate()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('project_duplicate', array(
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Clone this project')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a project
|
||||
*
|
||||
* @author Antonio Rabelo
|
||||
* @access public
|
||||
*/
|
||||
public function duplicate()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
|
||||
if ($project_id && $this->project->duplicate($project_id)) {
|
||||
$this->session->flash(t('Project cloned successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to clone this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before to disable a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirmDisable()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('project_disable', array(
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Project activation')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function disable()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
|
||||
if ($project_id && $this->project->disable($project_id)) {
|
||||
$this->session->flash(t('Project disabled successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to disable this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=project&action=show&project_id='.$project_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before to enable a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirmEnable()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('project_enable', array(
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Project activation')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function enable()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
|
||||
if ($project_id && $this->project->enable($project_id)) {
|
||||
$this->session->flash(t('Project activated successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to activate this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=project&action=show&project_id='.$project_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* RSS feed for a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function feed()
|
||||
{
|
||||
$token = $this->request->getStringParam('token');
|
||||
$project = $this->project->getByToken($token);
|
||||
|
||||
// Token verification
|
||||
if (! $project) {
|
||||
$this->forbidden(true);
|
||||
}
|
||||
|
||||
$this->response->xml($this->template->load('project_feed', array(
|
||||
'events' => $this->project->getActivity($project['id']),
|
||||
'project' => $project,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Activity page for a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function activity()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->template->layout('project_activity', array(
|
||||
'events' => $this->project->getActivity($project['id']),
|
||||
'menu' => 'projects',
|
||||
'project' => $project,
|
||||
'title' => t('%s\'s activity', $project['name'])
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Task search for a given project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function search()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$search = $this->request->getStringParam('search');
|
||||
$tasks = array();
|
||||
$nb_tasks = 0;
|
||||
|
||||
if ($search !== '') {
|
||||
|
||||
$filters = array(
|
||||
array('column' => 'project_id', 'operator' => 'eq', 'value' => $project['id']),
|
||||
'or' => array(
|
||||
array('column' => 'title', 'operator' => 'like', 'value' => '%'.$search.'%'),
|
||||
//array('column' => 'description', 'operator' => 'like', 'value' => '%'.$search.'%'),
|
||||
)
|
||||
);
|
||||
|
||||
$tasks = $this->task->find($filters);
|
||||
$nb_tasks = count($tasks);
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_search', array(
|
||||
'tasks' => $tasks,
|
||||
'nb_tasks' => $nb_tasks,
|
||||
'values' => array(
|
||||
'search' => $search,
|
||||
'controller' => 'project',
|
||||
'action' => 'search',
|
||||
'project_id' => $project['id'],
|
||||
),
|
||||
'menu' => 'projects',
|
||||
'project' => $project,
|
||||
'columns' => $this->board->getColumnsList($project['id']),
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'title' => $project['name'].($nb_tasks > 0 ? ' ('.$nb_tasks.')' : '')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* List of completed tasks for a given project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function tasks()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$filters = array(
|
||||
array('column' => 'project_id', 'operator' => 'eq', 'value' => $project['id']),
|
||||
array('column' => 'is_active', 'operator' => 'eq', 'value' => TaskModel::STATUS_CLOSED),
|
||||
);
|
||||
|
||||
$tasks = $this->task->find($filters);
|
||||
$nb_tasks = count($tasks);
|
||||
|
||||
$this->response->html($this->template->layout('project_tasks', array(
|
||||
'menu' => 'projects',
|
||||
'project' => $project,
|
||||
'columns' => $this->board->getColumnsList($project['id']),
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'tasks' => $tasks,
|
||||
'nb_tasks' => $nb_tasks,
|
||||
'title' => $project['name'].' ('.$nb_tasks.')'
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to create a new project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->response->html($this->template->layout('project_new', array(
|
||||
'errors' => array(),
|
||||
'values' => array(),
|
||||
'menu' => 'projects',
|
||||
'title' => t('New project')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save a new project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->project->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->project->create($values)) {
|
||||
$this->session->flash(t('Your project have been created successfully.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to create your project.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_new', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'menu' => 'projects',
|
||||
'title' => t('New Project')
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ class Subtask extends Base
|
|||
$task = $this->getTask();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
list($valid, $errors) = $this->subTask->validate($values);
|
||||
list($valid, $errors) = $this->subTask->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
|
@ -119,7 +119,7 @@ class Subtask extends Base
|
|||
$subtask = $this->getSubtask();
|
||||
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->subTask->validate($values);
|
||||
list($valid, $errors) = $this->subTask->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
|
|
|
@ -30,17 +30,13 @@ class Task extends Base
|
|||
$values = array(
|
||||
'title' => $this->request->getStringParam('title'),
|
||||
'description' => $this->request->getStringParam('description'),
|
||||
'color_id' => $this->request->getStringParam('color_id', 'blue'),
|
||||
'color_id' => $this->request->getStringParam('color_id'),
|
||||
'project_id' => $this->request->getIntegerParam('project_id', $defaultProject['id']),
|
||||
'owner_id' => $this->request->getIntegerParam('owner_id'),
|
||||
'column_id' => $this->request->getIntegerParam('column_id'),
|
||||
'category_id' => $this->request->getIntegerParam('category_id'),
|
||||
);
|
||||
|
||||
if ($values['column_id'] == 0) {
|
||||
$values['column_id'] = $this->board->getFirstColumn($values['project_id']);
|
||||
}
|
||||
|
||||
list($valid,) = $this->task->validateCreation($values);
|
||||
|
||||
if ($valid && $this->task->create($values)) {
|
||||
|
@ -50,6 +46,40 @@ class Task extends Base
|
|||
$this->response->text('FAILED');
|
||||
}
|
||||
|
||||
/**
|
||||
* Public access (display a task)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function readonly()
|
||||
{
|
||||
$project = $this->project->getByToken($this->request->getStringParam('token'));
|
||||
|
||||
// Token verification
|
||||
if (! $project) {
|
||||
$this->forbidden(true);
|
||||
}
|
||||
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true);
|
||||
|
||||
if (! $task) {
|
||||
$this->notfound(true);
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('task_public', array(
|
||||
'project' => $project,
|
||||
'comments' => $this->comment->getAll($task['id']),
|
||||
'subtasks' => $this->subTask->getAll($task['id']),
|
||||
'task' => $task,
|
||||
'columns_list' => $this->board->getColumnsList($task['project_id']),
|
||||
'colors_list' => $this->task->getColors(),
|
||||
'title' => $task['title'],
|
||||
'no_layout' => true,
|
||||
'auto_refresh' => true,
|
||||
'not_editable' => true,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a task
|
||||
*
|
||||
|
@ -60,6 +90,7 @@ class Task extends Base
|
|||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('task_show', array(
|
||||
'project' => $this->project->getById($task['project_id']),
|
||||
'files' => $this->file->getAll($task['id']),
|
||||
'comments' => $this->comment->getAll($task['id']),
|
||||
'subtasks' => $this->subTask->getAll($task['id']),
|
||||
|
@ -168,7 +199,6 @@ class Task extends Base
|
|||
'values' => $task,
|
||||
'errors' => array(),
|
||||
'task' => $task,
|
||||
'columns_list' => $this->board->getColumnsList($task['project_id']),
|
||||
'users_list' => $this->project->getUsersList($task['project_id']),
|
||||
'colors_list' => $this->task->getColors(),
|
||||
'categories_list' => $this->category->getList($task['project_id']),
|
||||
|
@ -233,27 +263,21 @@ class Task extends Base
|
|||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$task = $this->getTask();
|
||||
|
||||
if ($this->task->close($task['id'])) {
|
||||
$this->session->flash(t('Task closed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to close this task.'));
|
||||
if ($this->request->getStringParam('confirmation') === 'yes') {
|
||||
|
||||
$this->checkCSRFParam();
|
||||
|
||||
if ($this->task->close($task['id'])) {
|
||||
$this->session->flash(t('Task closed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to close this task.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id']);
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before to close a task
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirmClose()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('task_close', array(
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
|
@ -268,27 +292,21 @@ class Task extends Base
|
|||
*/
|
||||
public function open()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$task = $this->getTask();
|
||||
|
||||
if ($this->task->open($task['id'])) {
|
||||
$this->session->flash(t('Task opened successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to open this task.'));
|
||||
if ($this->request->getStringParam('confirmation') === 'yes') {
|
||||
|
||||
$this->checkCSRFParam();
|
||||
|
||||
if ($this->task->open($task['id'])) {
|
||||
$this->session->flash(t('Task opened successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to open this task.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id']);
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before to open a task
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirmOpen()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('task_open', array(
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
|
@ -303,27 +321,21 @@ class Task extends Base
|
|||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$task = $this->getTask();
|
||||
|
||||
if ($this->task->remove($task['id'])) {
|
||||
$this->session->flash(t('Task removed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to remove this task.'));
|
||||
if ($this->request->getStringParam('confirmation') === 'yes') {
|
||||
|
||||
$this->checkCSRFParam();
|
||||
|
||||
if ($this->task->remove($task['id'])) {
|
||||
$this->session->flash(t('Task removed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to remove this task.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']);
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before removing a task
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirmRemove()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('task_remove', array(
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
|
@ -332,7 +344,7 @@ class Task extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Duplicate a task (fill the form for a new task)
|
||||
* Duplicate a task
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
|
@ -340,26 +352,24 @@ class Task extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
if (! empty($task['date_due'])) {
|
||||
$task['date_due'] = date(t('m/d/Y'), $task['date_due']);
|
||||
}
|
||||
else {
|
||||
$task['date_due'] = '';
|
||||
if ($this->request->getStringParam('confirmation') === 'yes') {
|
||||
|
||||
$this->checkCSRFParam();
|
||||
$task_id = $this->task->duplicateSameProject($task);
|
||||
|
||||
if ($task_id) {
|
||||
$this->session->flash(t('Task created successfully.'));
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task_id);
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to create this task.'));
|
||||
$this->response->redirect('?controller=task&action=duplicate&task_id='.$task['id']);
|
||||
}
|
||||
}
|
||||
|
||||
$task['score'] = $task['score'] ?: '';
|
||||
|
||||
$this->response->html($this->template->layout('task_new', array(
|
||||
'errors' => array(),
|
||||
'values' => $task,
|
||||
'projects_list' => $this->project->getListByStatus(ProjectModel::ACTIVE),
|
||||
'columns_list' => $this->board->getColumnsList($task['project_id']),
|
||||
'users_list' => $this->project->getUsersList($task['project_id']),
|
||||
'colors_list' => $this->task->getColors(),
|
||||
'categories_list' => $this->category->getList($task['project_id']),
|
||||
'duplicate' => true,
|
||||
$this->response->html($this->taskLayout('task_duplicate', array(
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('New task')
|
||||
'title' => t('Duplicate a task')
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -368,19 +378,49 @@ class Task extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function editDescription()
|
||||
public function description()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
|
||||
$values = $this->request->getValues();
|
||||
|
||||
list($valid, $errors) = $this->task->validateDescriptionCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->task->update($values)) {
|
||||
$this->session->flash(t('Task updated successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update your task.'));
|
||||
}
|
||||
|
||||
if ($ajax) {
|
||||
$this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']);
|
||||
}
|
||||
else {
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$values = $task;
|
||||
$errors = array();
|
||||
}
|
||||
|
||||
$params = array(
|
||||
'values' => $task,
|
||||
'errors' => array(),
|
||||
'task' => $task,
|
||||
'ajax' => $this->request->isAjax(),
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Edit the description')
|
||||
);
|
||||
if ($this->request->isAjax()) {
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'ajax' => $ajax,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Edit the description'),
|
||||
);
|
||||
|
||||
if ($ajax) {
|
||||
$this->response->html($this->template->load('task_edit_description', $params));
|
||||
}
|
||||
else {
|
||||
|
@ -389,40 +429,63 @@ class Task extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Save and validation the description
|
||||
* Move a task to another project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function saveDescription()
|
||||
public function move()
|
||||
{
|
||||
$this->toAnotherProject('move');
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a task to another project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function copy()
|
||||
{
|
||||
$this->toAnotherProject('duplicate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Common methods between the actions "move" and "copy"
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private function toAnotherProject($action)
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$values = $this->request->getValues();
|
||||
$values = $task;
|
||||
$errors = array();
|
||||
$projects_list = $this->project->getAvailableList($this->acl->getUserId());
|
||||
|
||||
list($valid, $errors) = $this->task->validateDescriptionCreation($values);
|
||||
unset($projects_list[$task['project_id']]);
|
||||
|
||||
if ($valid) {
|
||||
if ($this->request->isPost()) {
|
||||
|
||||
if ($this->task->update($values)) {
|
||||
$this->session->flash(t('Task updated successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update your task.'));
|
||||
}
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->task->validateProjectModification($values);
|
||||
|
||||
if ($this->request->getIntegerParam('ajax')) {
|
||||
$this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']);
|
||||
}
|
||||
else {
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id']);
|
||||
if ($valid) {
|
||||
$task_id = $this->task->{$action.'ToAnotherProject'}($values['project_id'], $task);
|
||||
if ($task_id) {
|
||||
$this->session->flash(t('Task created successfully.'));
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task_id);
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to create your task.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('task_edit_description', array(
|
||||
$this->response->html($this->taskLayout('task_'.$action.'_project', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'projects_list' => $projects_list,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Edit the description')
|
||||
'title' => t(ucfirst($action).' the task to another project')
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ class User extends Base
|
|||
public function logout()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$this->rememberMe->destroy($this->acl->getUserId());
|
||||
$this->authentication->backend('rememberMe')->destroy($this->acl->getUserId());
|
||||
$this->session->close();
|
||||
$this->response->redirect('?controller=user&action=login');
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ class User extends Base
|
|||
*/
|
||||
public function login()
|
||||
{
|
||||
if (isset($_SESSION['user'])) {
|
||||
if ($this->acl->isLogged()) {
|
||||
$this->response->redirect('?controller=app');
|
||||
}
|
||||
|
||||
|
@ -50,10 +50,10 @@ class User extends Base
|
|||
public function check()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->user->validateLogin($values);
|
||||
list($valid, $errors) = $this->authentication->validateForm($values);
|
||||
|
||||
if ($valid) {
|
||||
$this->response->redirect('?controller=app');
|
||||
$this->response->redirect('?controller=board');
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('user_login', array(
|
||||
|
@ -64,6 +64,48 @@ class User extends Base
|
|||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Common layout for project views
|
||||
*
|
||||
* @access private
|
||||
* @param string $template Template name
|
||||
* @param array $params Template parameters
|
||||
* @return string
|
||||
*/
|
||||
private function layout($template, array $params)
|
||||
{
|
||||
$content = $this->template->load($template, $params);
|
||||
$params['user_content_for_layout'] = $content;
|
||||
$params['menu'] = 'users';
|
||||
|
||||
if (isset($params['user'])) {
|
||||
$params['title'] = $params['user']['name'] ?: $params['user']['username'];
|
||||
}
|
||||
|
||||
return $this->template->layout('user_layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common method to get the user
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function getUser()
|
||||
{
|
||||
$user = $this->user->getById($this->request->getIntegerParam('user_id'));
|
||||
|
||||
if (! $user) {
|
||||
$this->notfound();
|
||||
}
|
||||
|
||||
if ($this->acl->isRegularUser() && $this->acl->getUserId() != $user['id']) {
|
||||
$this->forbidden();
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* List all users
|
||||
*
|
||||
|
@ -130,6 +172,134 @@ class User extends Base
|
|||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display user information
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->response->html($this->layout('user_show', array(
|
||||
'projects' => $this->project->getAvailableList($user['id']),
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display last connections
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function last()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->response->html($this->layout('user_last', array(
|
||||
'last_logins' => $this->lastLogin->getAll($user['id']),
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display user sessions
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function sessions()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->response->html($this->layout('user_sessions', array(
|
||||
'sessions' => $this->authentication->backend('rememberMe')->getAll($user['id']),
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a "RememberMe" token
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function removeSession()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$user = $this->getUser();
|
||||
$this->authentication->backend('rememberMe')->remove($this->request->getIntegerParam('id'));
|
||||
$this->response->redirect('?controller=user&action=sessions&user_id='.$user['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display user notifications
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function notifications()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$values = $this->request->getValues();
|
||||
$this->notification->saveSettings($user['id'], $values);
|
||||
$this->session->flash(t('User updated successfully.'));
|
||||
$this->response->redirect('?controller=user&action=notifications&user_id='.$user['id']);
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('user_notifications', array(
|
||||
'projects' => $this->project->getAvailableList($user['id']),
|
||||
'notifications' => $this->notification->readSettings($user['id']),
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display external accounts
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function external()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->response->html($this->layout('user_external', array(
|
||||
'last_logins' => $this->lastLogin->getAll($user['id']),
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Password modification
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function password()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$values = array('id' => $user['id']);
|
||||
$errors = array();
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->user->validatePasswordModification($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->user->update($values)) {
|
||||
$this->session->flash(t('Password modified successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to change the password.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=user&action=show&user_id='.$user['id']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('user_password', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to edit a user
|
||||
*
|
||||
|
@ -137,85 +307,46 @@ class User extends Base
|
|||
*/
|
||||
public function edit()
|
||||
{
|
||||
$user = $this->user->getById($this->request->getIntegerParam('user_id'));
|
||||
$user = $this->getUser();
|
||||
$values = $user;
|
||||
$errors = array();
|
||||
|
||||
if (! $user) $this->notfound();
|
||||
unset($values['password']);
|
||||
|
||||
if ($this->acl->isRegularUser() && $this->acl->getUserId() != $user['id']) {
|
||||
$this->forbidden();
|
||||
}
|
||||
if ($this->request->isPost()) {
|
||||
|
||||
unset($user['password']);
|
||||
$values = $this->request->getValues();
|
||||
|
||||
$this->response->html($this->template->layout('user_edit', array(
|
||||
'projects' => $this->project->filterListByAccess($this->project->getList(), $user['id']),
|
||||
'errors' => array(),
|
||||
'values' => $user,
|
||||
'menu' => 'users',
|
||||
'title' => t('Edit user')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and update a user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if ($this->acl->isAdminUser()) {
|
||||
$values += array('is_admin' => 0);
|
||||
}
|
||||
else {
|
||||
|
||||
if ($this->acl->getUserId() != $values['id']) {
|
||||
$this->forbidden();
|
||||
}
|
||||
|
||||
if (isset($values['is_admin'])) {
|
||||
unset($values['is_admin']); // Regular users can't be admin
|
||||
}
|
||||
}
|
||||
|
||||
list($valid, $errors) = $this->user->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->user->update($values)) {
|
||||
$this->session->flash(t('User updated successfully.'));
|
||||
$this->response->redirect('?controller=user');
|
||||
if ($this->acl->isAdminUser()) {
|
||||
$values += array('is_admin' => 0);
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update your user.'));
|
||||
|
||||
if (isset($values['is_admin'])) {
|
||||
unset($values['is_admin']); // Regular users can't be admin
|
||||
}
|
||||
}
|
||||
|
||||
list($valid, $errors) = $this->user->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->user->update($values)) {
|
||||
$this->session->flash(t('User updated successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update your user.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=user&action=show&user_id='.$user['id']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('user_edit', array(
|
||||
'projects' => $this->project->filterListByAccess($this->project->getList(), $values['id']),
|
||||
'errors' => $errors,
|
||||
$this->response->html($this->layout('user_edit', array(
|
||||
'values' => $values,
|
||||
'menu' => 'users',
|
||||
'title' => t('Edit user')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before to remove a user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$user = $this->user->getById($this->request->getIntegerParam('user_id'));
|
||||
|
||||
if (! $user) $this->notfound();
|
||||
|
||||
$this->response->html($this->template->layout('user_remove', array(
|
||||
'errors' => $errors,
|
||||
'projects' => $this->project->filterListByAccess($this->project->getList(), $user['id']),
|
||||
'user' => $user,
|
||||
'menu' => 'users',
|
||||
'title' => t('Remove user')
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -226,16 +357,24 @@ class User extends Base
|
|||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$user_id = $this->request->getIntegerParam('user_id');
|
||||
$user = $this->getUser();
|
||||
|
||||
if ($user_id && $this->user->remove($user_id)) {
|
||||
$this->session->flash(t('User removed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to remove this user.'));
|
||||
if ($this->request->getStringParam('confirmation') === 'yes') {
|
||||
|
||||
$this->checkCSRFParam();
|
||||
|
||||
if ($this->user->remove($user['id'])) {
|
||||
$this->session->flash(t('User removed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to remove this user.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=user');
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=user');
|
||||
$this->response->html($this->layout('user_remove', array(
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -249,23 +388,23 @@ class User extends Base
|
|||
|
||||
if ($code) {
|
||||
|
||||
$profile = $this->google->getGoogleProfile($code);
|
||||
$profile = $this->authentication->backend('google')->getGoogleProfile($code);
|
||||
|
||||
if (is_array($profile)) {
|
||||
|
||||
// If the user is already logged, link the account otherwise authenticate
|
||||
if ($this->acl->isLogged()) {
|
||||
|
||||
if ($this->google->updateUser($this->acl->getUserId(), $profile)) {
|
||||
if ($this->authentication->backend('google')->updateUser($this->acl->getUserId(), $profile)) {
|
||||
$this->session->flash(t('Your Google Account is linked to your profile successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to link your Google Account.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=user');
|
||||
$this->response->redirect('?controller=user&action=external&user_id='.$this->acl->getUserId());
|
||||
}
|
||||
else if ($this->google->authenticate($profile['id'])) {
|
||||
else if ($this->authentication->backend('google')->authenticate($profile['id'])) {
|
||||
$this->response->redirect('?controller=app');
|
||||
}
|
||||
else {
|
||||
|
@ -279,7 +418,7 @@ class User extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->redirect($this->google->getAuthorizationUrl());
|
||||
$this->response->redirect($this->authentication->backend('google')->getAuthorizationUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -290,14 +429,14 @@ class User extends Base
|
|||
public function unlinkGoogle()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
if ($this->google->unlink($this->acl->getUserId())) {
|
||||
if ($this->authentication->backend('google')->unlink($this->acl->getUserId())) {
|
||||
$this->session->flash(t('Your Google Account is not linked anymore to your profile.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to unlink your Google Account.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=user');
|
||||
$this->response->redirect('?controller=user&action=external&user_id='.$this->acl->getUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -310,23 +449,23 @@ class User extends Base
|
|||
$code = $this->request->getStringParam('code');
|
||||
|
||||
if ($code) {
|
||||
$profile = $this->gitHub->getGitHubProfile($code);
|
||||
$profile = $this->authentication->backend('gitHub')->getGitHubProfile($code);
|
||||
|
||||
if (is_array($profile)) {
|
||||
|
||||
// If the user is already logged, link the account otherwise authenticate
|
||||
if ($this->acl->isLogged()) {
|
||||
|
||||
if ($this->gitHub->updateUser($this->acl->getUserId(), $profile)) {
|
||||
if ($this->authentication->backend('gitHub')->updateUser($this->acl->getUserId(), $profile)) {
|
||||
$this->session->flash(t('Your GitHub account was successfully linked to your profile.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to link your GitHub Account.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=user');
|
||||
$this->response->redirect('?controller=user&action=external&user_id='.$this->acl->getUserId());
|
||||
}
|
||||
else if ($this->gitHub->authenticate($profile['id'])) {
|
||||
else if ($this->authentication->backend('gitHub')->authenticate($profile['id'])) {
|
||||
$this->response->redirect('?controller=app');
|
||||
}
|
||||
else {
|
||||
|
@ -340,7 +479,7 @@ class User extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->redirect($this->gitHub->getAuthorizationUrl());
|
||||
$this->response->redirect($this->authentication->backend('gitHub')->getAuthorizationUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -352,15 +491,15 @@ class User extends Base
|
|||
{
|
||||
$this->checkCSRFParam();
|
||||
|
||||
$this->gitHub->revokeGitHubAccess();
|
||||
$this->authentication->backend('gitHub')->revokeGitHubAccess();
|
||||
|
||||
if ($this->gitHub->unlink($this->acl->getUserId())) {
|
||||
if ($this->authentication->backend('gitHub')->unlink($this->acl->getUserId())) {
|
||||
$this->session->flash(t('Your GitHub account is no longer linked to your profile.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to unlink your GitHub Account.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=user');
|
||||
$this->response->redirect('?controller=user&action=external&user_id='.$this->acl->getUserId());
|
||||
}
|
||||
}
|
||||
|
|
75
sources/app/Core/Cli.php
Normal file
75
sources/app/Core/Cli.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* CLI class
|
||||
*
|
||||
* @package core
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Cli
|
||||
{
|
||||
/**
|
||||
* Default command name
|
||||
*
|
||||
* @access public
|
||||
* @var string
|
||||
*/
|
||||
public $default_command = 'help';
|
||||
|
||||
/**
|
||||
* List of registered commands
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $commands = array();
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @access public
|
||||
* @param string $command Command name
|
||||
* @param Closure $callback Command callback
|
||||
*/
|
||||
public function register($command, Closure $callback)
|
||||
{
|
||||
$this->commands[$command] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command
|
||||
*
|
||||
* @access public
|
||||
* @param string $command Command name
|
||||
*/
|
||||
public function call($command)
|
||||
{
|
||||
if (isset($this->commands[$command])) {
|
||||
$this->commands[$command]();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine which command to execute
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
die('This script work only from the command line.');
|
||||
}
|
||||
|
||||
if ($GLOBALS['argc'] === 1) {
|
||||
$this->call($this->default_command);
|
||||
}
|
||||
|
||||
$this->call($GLOBALS['argv'][1]);
|
||||
$this->call($this->default_command);
|
||||
}
|
||||
}
|
|
@ -10,18 +10,30 @@ namespace Core;
|
|||
*/
|
||||
class Loader
|
||||
{
|
||||
/**
|
||||
* List of paths
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $paths = array();
|
||||
|
||||
/**
|
||||
* Load the missing class
|
||||
*
|
||||
* @access public
|
||||
* @param string $class Class name
|
||||
* @param string $class Class name with namespace
|
||||
*/
|
||||
public function load($class)
|
||||
{
|
||||
$filename = __DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php';
|
||||
foreach ($this->paths as $path) {
|
||||
|
||||
if (file_exists($filename)) {
|
||||
require $filename;
|
||||
$filename = $path.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php';
|
||||
|
||||
if (file_exists($filename)) {
|
||||
require $filename;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,4 +46,17 @@ class Loader
|
|||
{
|
||||
spl_autoload_register(array($this, 'load'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new path
|
||||
*
|
||||
* @access public
|
||||
* @param string $path Path
|
||||
* @return Core\Loader
|
||||
*/
|
||||
public function setPath($path)
|
||||
{
|
||||
$this->paths[] = $path;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
|
|
|
@ -70,6 +70,22 @@ class Response
|
|||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a CSV response
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Data to serialize in csv
|
||||
* @param integer $status_code HTTP status code
|
||||
*/
|
||||
public function csv(array $data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
$this->nocache();
|
||||
header('Content-Type: text/csv');
|
||||
Tool::csv($data);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Json response
|
||||
*
|
||||
|
@ -83,7 +99,6 @@ class Response
|
|||
$this->nocache();
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($data);
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
|
@ -100,7 +115,6 @@ class Response
|
|||
$this->nocache();
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
echo $data;
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
|
@ -117,7 +131,6 @@ class Response
|
|||
$this->nocache();
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
echo $data;
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
|
@ -134,7 +147,6 @@ class Response
|
|||
$this->nocache();
|
||||
header('Content-Type: text/xml; charset=utf-8');
|
||||
echo $data;
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
|
@ -169,7 +181,6 @@ class Response
|
|||
header('Content-Transfer-Encoding: binary');
|
||||
header('Content-Type: application/octet-stream');
|
||||
echo $data;
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
|
@ -235,7 +246,7 @@ class Response
|
|||
*/
|
||||
public function hsts()
|
||||
{
|
||||
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') {
|
||||
if (Tool::isHTTPS()) {
|
||||
header('Strict-Transport-Security: max-age=31536000');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,11 @@ class Session
|
|||
/**
|
||||
* Sesion lifetime
|
||||
*
|
||||
* http://php.net/manual/en/session.configuration.php#ini.session.cookie-lifetime
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const SESSION_LIFETIME = 7200; // 2 hours
|
||||
const SESSION_LIFETIME = 0; // Until the browser is closed
|
||||
|
||||
/**
|
||||
* Open a session
|
||||
|
@ -35,7 +37,7 @@ class Session
|
|||
self::SESSION_LIFETIME,
|
||||
$base_path ?: '/',
|
||||
null,
|
||||
! empty($_SERVER['HTTPS']),
|
||||
Tool::isHTTPS(),
|
||||
true
|
||||
);
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Core;
|
||||
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* Template class
|
||||
*
|
||||
|
@ -25,31 +27,22 @@ class Template
|
|||
* $template->load('template_name', ['bla' => 'value']);
|
||||
*
|
||||
* @access public
|
||||
* @params string $__template_name Template name
|
||||
* @params array $__template_args Key/Value map of template variables
|
||||
* @return string
|
||||
*/
|
||||
public function load()
|
||||
public function load($__template_name, array $__template_args = array())
|
||||
{
|
||||
if (func_num_args() < 1 || func_num_args() > 2) {
|
||||
die('Invalid template arguments');
|
||||
$__template_file = self::PATH.$__template_name.'.php';
|
||||
|
||||
if (! file_exists($__template_file)) {
|
||||
throw new LogicException('Unable to load the template: "'.$__template_name.'"');
|
||||
}
|
||||
|
||||
if (! file_exists(self::PATH.func_get_arg(0).'.php')) {
|
||||
die('Unable to load the template: "'.func_get_arg(0).'"');
|
||||
}
|
||||
|
||||
if (func_num_args() === 2) {
|
||||
|
||||
if (! is_array(func_get_arg(1))) {
|
||||
die('Template variables must be an array');
|
||||
}
|
||||
|
||||
extract(func_get_arg(1));
|
||||
}
|
||||
extract($__template_args);
|
||||
|
||||
ob_start();
|
||||
|
||||
include self::PATH.func_get_arg(0).'.php';
|
||||
|
||||
include $__template_file;
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
|
|
67
sources/app/Core/Tool.php
Normal file
67
sources/app/Core/Tool.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
/**
|
||||
* Tool class
|
||||
*
|
||||
* @package core
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Tool
|
||||
{
|
||||
/**
|
||||
* Write a CSV file
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param array $rows Array of rows
|
||||
* @param string $filename Output filename
|
||||
*/
|
||||
public static function csv(array $rows, $filename = 'php://output')
|
||||
{
|
||||
$fp = fopen($filename, 'w');
|
||||
|
||||
if (is_resource($fp)) {
|
||||
|
||||
foreach ($rows as $fields) {
|
||||
fputcsv($fp, $fields);
|
||||
}
|
||||
|
||||
fclose($fp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and register a model
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param Core\Registry $registry DPI container
|
||||
* @param string $name Model name
|
||||
* @return mixed
|
||||
*/
|
||||
public static function loadModel(Registry $registry, $name)
|
||||
{
|
||||
if (! isset($registry->$name)) {
|
||||
$class = '\Model\\'.ucfirst($name);
|
||||
$registry->$name = new $class($registry);
|
||||
}
|
||||
|
||||
return $registry->shared($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the page is requested through HTTPS
|
||||
*
|
||||
* Note: IIS return the value 'off' and other web servers an empty value when it's not HTTPS
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isHTTPS()
|
||||
{
|
||||
return isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== '' && $_SERVER['HTTPS'] !== 'off';
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ class Translator
|
|||
* $translator->translate('I have %d kids', 5);
|
||||
*
|
||||
* @access public
|
||||
* @param $identifier
|
||||
* @param string $identifier Default string
|
||||
* @return string
|
||||
*/
|
||||
public function translate($identifier)
|
||||
|
@ -52,6 +52,28 @@ class Translator
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a translation with no HTML escaping
|
||||
*
|
||||
* $translator->translateNoEscaping('I have %d kids', 5);
|
||||
*
|
||||
* @access public
|
||||
* @param string $identifier Default string
|
||||
* @return string
|
||||
*/
|
||||
public function translateNoEscaping($identifier)
|
||||
{
|
||||
$args = func_get_args();
|
||||
|
||||
array_shift($args);
|
||||
array_unshift($args, $this->get($identifier, $identifier));
|
||||
|
||||
return call_user_func_array(
|
||||
'sprintf',
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a formatted number
|
||||
*
|
||||
|
@ -119,7 +141,6 @@ class Translator
|
|||
|
||||
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
|
||||
$format = str_replace('%e', '%d', $format);
|
||||
$format = str_replace('%G', '%Y', $format);
|
||||
$format = str_replace('%k', '%H', $format);
|
||||
}
|
||||
|
||||
|
|
87
sources/app/Event/BaseNotificationListener.php
Normal file
87
sources/app/Event/BaseNotificationListener.php
Normal file
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
use Core\Listener;
|
||||
use Model\Notification;
|
||||
|
||||
/**
|
||||
* Base notification listener
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
abstract class BaseNotificationListener implements Listener
|
||||
{
|
||||
/**
|
||||
* Notification model
|
||||
*
|
||||
* @accesss protected
|
||||
* @var Model\Notification
|
||||
*/
|
||||
protected $notification;
|
||||
|
||||
/**
|
||||
* Template name
|
||||
*
|
||||
* @accesss private
|
||||
* @var string
|
||||
*/
|
||||
private $template = '';
|
||||
|
||||
/**
|
||||
* Fetch data for the mail template
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getTemplateData(array $data);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Model\Notification $notification Notification model instance
|
||||
* @param string $template Template name
|
||||
*/
|
||||
public function __construct(Notification $notification, $template)
|
||||
{
|
||||
$this->template = $template;
|
||||
$this->notification = $notification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return class information
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return get_called_class();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action was executed or false when not executed
|
||||
*/
|
||||
public function execute(array $data)
|
||||
{
|
||||
$values = $this->getTemplateData($data);
|
||||
|
||||
// Get the list of users to be notified
|
||||
$users = $this->notification->getUsersList($values['task']['project_id']);
|
||||
|
||||
// Send notifications
|
||||
if ($users) {
|
||||
$this->notification->sendEmails($this->template, $users, $values);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
73
sources/app/Event/CommentHistoryListener.php
Normal file
73
sources/app/Event/CommentHistoryListener.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
use Core\Listener;
|
||||
use Model\CommentHistory;
|
||||
|
||||
/**
|
||||
* Comment history listener
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class CommentHistoryListener implements Listener
|
||||
{
|
||||
/**
|
||||
* Comment History model
|
||||
*
|
||||
* @accesss private
|
||||
* @var \Model\CommentHistory
|
||||
*/
|
||||
private $model;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Model\CommentHistory $model Comment History model instance
|
||||
*/
|
||||
public function __construct(CommentHistory $model)
|
||||
{
|
||||
$this->model = $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return class information
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return get_called_class();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action was executed or false when not executed
|
||||
*/
|
||||
public function execute(array $data)
|
||||
{
|
||||
$creator_id = $this->model->acl->getUserId();
|
||||
|
||||
if ($creator_id && isset($data['task_id']) && isset($data['id'])) {
|
||||
|
||||
$task = $this->model->task->getById($data['task_id']);
|
||||
|
||||
$this->model->create(
|
||||
$task['project_id'],
|
||||
$data['task_id'],
|
||||
$data['id'],
|
||||
$creator_id,
|
||||
$this->model->event->getLastTriggeredEvent(),
|
||||
$data['comment']
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
30
sources/app/Event/CommentNotificationListener.php
Normal file
30
sources/app/Event/CommentNotificationListener.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
use Event\BaseNotificationListener;
|
||||
|
||||
/**
|
||||
* Comment notification listener
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class CommentNotificationListener extends BaseNotificationListener
|
||||
{
|
||||
/**
|
||||
* Fetch data for the mail template
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data
|
||||
* @return array
|
||||
*/
|
||||
public function getTemplateData(array $data)
|
||||
{
|
||||
$values = array();
|
||||
$values['comment'] = $this->notification->comment->getById($data['id']);
|
||||
$values['task'] = $this->notification->task->getById($values['comment']['task_id'], true);
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
30
sources/app/Event/FileNotificationListener.php
Normal file
30
sources/app/Event/FileNotificationListener.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
use Event\BaseNotificationListener;
|
||||
|
||||
/**
|
||||
* File notification listener
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class FileNotificationListener extends BaseNotificationListener
|
||||
{
|
||||
/**
|
||||
* Fetch data for the mail template
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data
|
||||
* @return array
|
||||
*/
|
||||
public function getTemplateData(array $data)
|
||||
{
|
||||
$values = array();
|
||||
$values['file'] = $data;
|
||||
$values['task'] = $this->notification->task->getById($data['task_id'], true);
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
|
@ -6,12 +6,14 @@ use Core\Listener;
|
|||
use Model\Project;
|
||||
|
||||
/**
|
||||
* Task modification listener
|
||||
* Project modification date listener
|
||||
*
|
||||
* @package events
|
||||
* Update the last modified field for a project
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskModification implements Listener
|
||||
class ProjectModificationDate implements Listener
|
||||
{
|
||||
/**
|
||||
* Project model
|
||||
|
@ -32,6 +34,17 @@ class TaskModification implements Listener
|
|||
$this->project = $project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return class information
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return get_called_class();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
|
@ -42,8 +55,7 @@ class TaskModification implements Listener
|
|||
public function execute(array $data)
|
||||
{
|
||||
if (isset($data['project_id'])) {
|
||||
$this->project->updateModificationDate($data['project_id']);
|
||||
return true;
|
||||
return $this->project->updateModificationDate($data['project_id']);
|
||||
}
|
||||
|
||||
return false;
|
30
sources/app/Event/SubTaskNotificationListener.php
Normal file
30
sources/app/Event/SubTaskNotificationListener.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
use Event\BaseNotificationListener;
|
||||
|
||||
/**
|
||||
* SubTask notification listener
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class SubTaskNotificationListener extends BaseNotificationListener
|
||||
{
|
||||
/**
|
||||
* Fetch data for the mail template
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data
|
||||
* @return array
|
||||
*/
|
||||
public function getTemplateData(array $data)
|
||||
{
|
||||
$values = array();
|
||||
$values['subtask'] = $this->notification->subtask->getById($data['id'], true);
|
||||
$values['task'] = $this->notification->task->getById($data['task_id'], true);
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
73
sources/app/Event/SubtaskHistoryListener.php
Normal file
73
sources/app/Event/SubtaskHistoryListener.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
use Core\Listener;
|
||||
use Model\SubtaskHistory;
|
||||
|
||||
/**
|
||||
* Subtask history listener
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class SubtaskHistoryListener implements Listener
|
||||
{
|
||||
/**
|
||||
* Comment History model
|
||||
*
|
||||
* @accesss private
|
||||
* @var \Model\SubtaskHistory
|
||||
*/
|
||||
private $model;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Model\SubtaskHistory $model Subtask History model instance
|
||||
*/
|
||||
public function __construct(SubtaskHistory $model)
|
||||
{
|
||||
$this->model = $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return class information
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return get_called_class();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action was executed or false when not executed
|
||||
*/
|
||||
public function execute(array $data)
|
||||
{
|
||||
$creator_id = $this->model->acl->getUserId();
|
||||
|
||||
if ($creator_id && isset($data['task_id']) && isset($data['id'])) {
|
||||
|
||||
$task = $this->model->task->getById($data['task_id']);
|
||||
|
||||
$this->model->create(
|
||||
$task['project_id'],
|
||||
$data['task_id'],
|
||||
$data['id'],
|
||||
$creator_id,
|
||||
$this->model->event->getLastTriggeredEvent(),
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
63
sources/app/Event/TaskHistoryListener.php
Normal file
63
sources/app/Event/TaskHistoryListener.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
use Core\Listener;
|
||||
use Model\TaskHistory;
|
||||
|
||||
/**
|
||||
* Task history listener
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskHistoryListener implements Listener
|
||||
{
|
||||
/**
|
||||
* Task History model
|
||||
*
|
||||
* @accesss private
|
||||
* @var \Model\TaskHistory
|
||||
*/
|
||||
private $model;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Model\TaskHistory $model Task History model instance
|
||||
*/
|
||||
public function __construct(TaskHistory $model)
|
||||
{
|
||||
$this->model = $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return class information
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return get_called_class();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action was executed or false when not executed
|
||||
*/
|
||||
public function execute(array $data)
|
||||
{
|
||||
$creator_id = $this->model->acl->getUserId();
|
||||
|
||||
if ($creator_id && isset($data['task_id']) && isset($data['project_id'])) {
|
||||
$this->model->create($data['project_id'], $data['task_id'], $creator_id, $this->model->event->getLastTriggeredEvent());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
29
sources/app/Event/TaskNotificationListener.php
Normal file
29
sources/app/Event/TaskNotificationListener.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
use Event\BaseNotificationListener;
|
||||
|
||||
/**
|
||||
* Task notification listener
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskNotificationListener extends BaseNotificationListener
|
||||
{
|
||||
/**
|
||||
* Fetch data for the mail template
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data
|
||||
* @return array
|
||||
*/
|
||||
public function getTemplateData(array $data)
|
||||
{
|
||||
$values = array();
|
||||
$values['task'] = $this->notification->task->getById($data['task_id'], true);
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
68
sources/app/Event/WebhookListener.php
Normal file
68
sources/app/Event/WebhookListener.php
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
use Core\Listener;
|
||||
use Model\Webhook;
|
||||
|
||||
/**
|
||||
* Webhook task events
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class WebhookListener implements Listener
|
||||
{
|
||||
/**
|
||||
* Webhook model
|
||||
*
|
||||
* @accesss private
|
||||
* @var \Model\Webhook
|
||||
*/
|
||||
private $webhook;
|
||||
|
||||
/**
|
||||
* Url to call
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $url = '';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param string $url URL to call
|
||||
* @param \Model\Webhook $webhook Webhook model instance
|
||||
*/
|
||||
public function __construct($url, Webhook $webhook)
|
||||
{
|
||||
$this->url = $url;
|
||||
$this->webhook = $webhook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return class information
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return get_called_class();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action was executed or false when not executed
|
||||
*/
|
||||
public function execute(array $data)
|
||||
{
|
||||
$this->webhook->notify($this->url, $data);
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,14 +1,7 @@
|
|||
<?php
|
||||
|
||||
return array(
|
||||
'English' => 'Englisch',
|
||||
'German' => 'Deutsch',
|
||||
'French' => 'Französisch',
|
||||
'Polish' => 'Polnisch',
|
||||
'Portuguese (Brazilian)' => 'Portugiesisch (Brasilien)',
|
||||
'Spanish' => 'Spanisch',
|
||||
'Chinese (Simplified)' => 'Chinesisch (vereinfacht)',
|
||||
'None' => 'Kein',
|
||||
'None' => 'Keines',
|
||||
'edit' => 'Bearbeiten',
|
||||
'Edit' => 'Bearbeiten',
|
||||
'remove' => 'Entfernen',
|
||||
|
@ -31,12 +24,12 @@ return array(
|
|||
'Unassigned' => 'Nicht zugeordnet',
|
||||
'View this task' => 'Aufgabe ansehen',
|
||||
'Remove user' => 'Benutzer löschen',
|
||||
'Do you really want to remove this user: "%s"?' => 'Soll dieser Benutzer wirklich gelöscht werden: «%s»?',
|
||||
'Do you really want to remove this user: "%s"?' => 'Soll dieser Benutzer wirklich gelöscht werden: "%s"?',
|
||||
'New user' => 'Neuer Benutzer',
|
||||
'All users' => 'Alle Benutzer',
|
||||
'Username' => 'Benutzername',
|
||||
'Password' => 'Passwort',
|
||||
'Default Project' => 'Standardprojekt',
|
||||
'Default project' => 'Standardprojekt',
|
||||
'Administrator' => 'Administrator',
|
||||
'Sign in' => 'Anmelden',
|
||||
'Users' => 'Benutzer',
|
||||
|
@ -57,35 +50,35 @@ return array(
|
|||
'Project' => 'Projekt',
|
||||
'Status' => 'Status',
|
||||
'Tasks' => 'Aufgabe',
|
||||
'Board' => 'Pinwand',
|
||||
'Board' => 'Pinnwand',
|
||||
'Actions' => 'Aktionen',
|
||||
'Inactive' => 'Inaktiv',
|
||||
'Active' => 'Aktiv',
|
||||
'Column %d' => 'Spalte %d',
|
||||
'Add this column' => 'Diese Spalte hinzufügen',
|
||||
'%d tasks on the board' => '%d Aufgaben auf dieser Pinwand',
|
||||
'%d tasks on the board' => '%d Aufgaben auf dieser Pinnwand',
|
||||
'%d tasks in total' => '%d Aufgaben gesamt',
|
||||
'Unable to update this board.' => 'Ändern dieser Pinwand nicht möglich.',
|
||||
'Edit board' => 'Pinwand bearbeiten',
|
||||
'Unable to update this board.' => 'Ändern dieser Pinnwand nicht möglich.',
|
||||
'Edit board' => 'Pinnwand bearbeiten',
|
||||
'Disable' => 'Deaktivieren',
|
||||
'Enable' => 'Aktivieren',
|
||||
'New project' => 'Neues Projekt',
|
||||
'Do you really want to remove this project: "%s"?' => 'Soll dieses Projekt wirklich gelöscht werden: «%s»?',
|
||||
'Do you really want to remove this project: "%s"?' => 'Soll dieses Projekt wirklich gelöscht werden: "%s"?',
|
||||
'Remove project' => 'Projekt löschen',
|
||||
'Boards' => 'Pinwände',
|
||||
'Edit the board for "%s"' => 'Pinwand für «%s» bearbeiten',
|
||||
'Boards' => 'Pinnwände',
|
||||
'Edit the board for "%s"' => 'Pinnwand für "%s" bearbeiten',
|
||||
'All projects' => 'Alle Projekte',
|
||||
'Change columns' => 'Spalten ändern',
|
||||
'Add a new column' => 'Neue Spalte hinzufügen',
|
||||
'Title' => 'Titel',
|
||||
'Add Column' => 'Neue Spalte',
|
||||
'Project "%s"' => 'Projekt «%s»',
|
||||
'Project "%s"' => 'Projekt "%s"',
|
||||
'Nobody assigned' => 'Nicht zugeordnet',
|
||||
'Assigned to %s' => 'Zuständiger: %s',
|
||||
'Assigned to %s' => 'Zuständig: %s',
|
||||
'Remove a column' => 'Spalte löschen',
|
||||
'Remove a column from a board' => 'Spalte einer Pinwand löschen',
|
||||
'Remove a column from a board' => 'Spalte einer Pinnwand löschen',
|
||||
'Unable to remove this column.' => 'Löschen dieser Spalte nicht möglich.',
|
||||
'Do you really want to remove this column: "%s"?' => 'Soll diese Spalte wirklich gelöscht werden: «%s»?',
|
||||
'Do you really want to remove this column: "%s"?' => 'Soll diese Spalte wirklich gelöscht werden: "%s"?',
|
||||
'This action will REMOVE ALL TASKS associated to this column!' => 'ALLE AUFGABEN dieser Spalte werden GELÖSCHT!',
|
||||
'Settings' => 'Einstellungen',
|
||||
'Application settings' => 'Anwendungskonfiguration',
|
||||
|
@ -94,25 +87,25 @@ return array(
|
|||
'API token:' => 'API Token:',
|
||||
'More information' => 'Mehr Informationen',
|
||||
'Database size:' => 'Datenbankgröße:',
|
||||
'Download the database' => 'Download der Datenbank',
|
||||
'Optimize the database' => 'Optimieren der Datenbank',
|
||||
'(VACUUM command)' => '(VACUUM Kommando)',
|
||||
'(Gzip compressed Sqlite file)' => '(Gzip komprimierte Sqlite Datei)',
|
||||
'Download the database' => 'Datenbank herunterladen',
|
||||
'Optimize the database' => 'Datenbank optimieren',
|
||||
'(VACUUM command)' => '(VACUUM Befehl)',
|
||||
'(Gzip compressed Sqlite file)' => '(Gzip-komprimierte Sqlite Datei)',
|
||||
'User settings' => 'Benutzereinstellungen',
|
||||
'My default project:' => 'Standardprojekt:',
|
||||
'Close a task' => 'Aufgabe abschließen',
|
||||
'Do you really want to close this task: "%s"?' => 'Soll diese Aufgabe wirklich abgeschlossen werden: «%s»?',
|
||||
'Do you really want to close this task: "%s"?' => 'Soll diese Aufgabe wirklich abgeschlossen werden: "%s"?',
|
||||
'Edit a task' => 'Aufgabe bearbeiten',
|
||||
'Column' => 'Spalte',
|
||||
'Color' => 'Farbe',
|
||||
'Assignee' => 'Zuständiger',
|
||||
'Assignee' => 'Zuständig',
|
||||
'Create another task' => 'Weitere Aufgabe erstellen',
|
||||
'New task' => 'Neue Aufgabe',
|
||||
'Open a task' => 'Öffne eine Aufgabe',
|
||||
'Do you really want to open this task: "%s"?' => 'Soll diese Aufgabe wirklich wieder geöffnet werden: «%s»?',
|
||||
'Back to the board' => 'Zurück zur Pinwand',
|
||||
'Created on %B %e, %G at %k:%M %p' => 'Erstellt am %d.%m.%Y um %H:%M',
|
||||
'There is nobody assigned' => 'Die Aufgabe wurde niemand zugewiesen',
|
||||
'Do you really want to open this task: "%s"?' => 'Soll diese Aufgabe wirklich wieder geöffnet werden: "%s"?',
|
||||
'Back to the board' => 'Zurück zur Pinnwand',
|
||||
'Created on %B %e, %Y at %k:%M %p' => 'Erstellt am %d.%m.%Y um %H:%M',
|
||||
'There is nobody assigned' => 'Die Aufgabe wurde niemandem zugewiesen',
|
||||
'Column on the board:' => 'Spalte:',
|
||||
'Status is open' => 'Status ist geöffnet',
|
||||
'Status is closed' => 'Status ist abgeschlossen',
|
||||
|
@ -120,14 +113,14 @@ return array(
|
|||
'Open this task' => 'Aufgabe wieder öffnen',
|
||||
'There is no description.' => 'Keine Beschreibung vorhanden.',
|
||||
'Add a new task' => 'Neue Aufgabe hinzufügen',
|
||||
'The username is required' => 'Der Benutzername ist obligatorisch',
|
||||
'The maximum length is %d characters' => 'Die maximale Länge sind %d Zeichen',
|
||||
'The minimum length is %d characters' => 'Die minimale Länge sind %d Zeichen',
|
||||
'The password is required' => 'Das Passwort ist obligatorisch',
|
||||
'This value must be an integer' => 'Dieser Wert muss eine Ganzzahl sein',
|
||||
'The username is required' => 'Der Benutzername wird benötigt',
|
||||
'The maximum length is %d characters' => 'Die maximale Länge beträgt %d Zeichen',
|
||||
'The minimum length is %d characters' => 'Die minimale Länge beträgt %d Zeichen',
|
||||
'The password is required' => 'Das Passwort wird benötigt',
|
||||
'This value must be an integer' => 'Dieser Wert muss eine ganze Zahl sein',
|
||||
'The username must be unique' => 'Der Benutzername muss eindeutig sein',
|
||||
'The username must be alphanumeric' => 'Der Benutzername muss alphanumerisch sein',
|
||||
'The user id is required' => 'Die Benutzer ID ist obligatorisch',
|
||||
'The user id is required' => 'Die Benutzer ID ist anzugeben',
|
||||
'Passwords don\'t match' => 'Passwörter nicht gleich',
|
||||
'The confirmation is required' => 'Die Bestätigung ist erforderlich',
|
||||
'The column is required' => 'Die Spalte ist anzugeben',
|
||||
|
@ -136,10 +129,10 @@ 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',
|
||||
'This project must be unique' => 'Der Projektname muss eindeutig sein',
|
||||
'The title is required' => 'Der Titel ist anzugeben',
|
||||
'The language is required' => 'Die Sprache ist erforderlich',
|
||||
'There is no active project, the first step is to create a new project.' => 'Es gibt kein aktives Projekt. Der erste Schritt ist ein Projekt zu erstellen.',
|
||||
'The language is required' => 'Die Sprache ist anzugeben',
|
||||
'There is no active project, the first step is to create a new project.' => 'Es gibt kein aktives Projekt. Zunächst muss ein Projekt erstellt werden.',
|
||||
'Settings saved successfully.' => 'Einstellungen erfolgreich gespeichert.',
|
||||
'Unable to save your settings.' => 'Speichern der Einstellungen nicht möglich.',
|
||||
'Database optimization done.' => 'Optimieren der Datenbank abgeschlossen.',
|
||||
|
@ -153,10 +146,10 @@ return array(
|
|||
'Unable to activate this project.' => 'Aktivieren des Projekts nicht möglich.',
|
||||
'Project disabled successfully.' => 'Projekt erfolgreich deaktiviert.',
|
||||
'Unable to disable this project.' => 'Deaktivieren des Projekts nicht möglich.',
|
||||
'Unable to open this task.' => 'Wieder eröffnen der Aufgabe nicht möglich.',
|
||||
'Unable to open this task.' => 'Wiedereröffnung der Aufgabe nicht möglich.',
|
||||
'Task opened successfully.' => 'Aufgabe erfolgreich wieder eröffnet.',
|
||||
'Unable to close this task.' => 'Abschließen der Aufgabe nicht möglich.',
|
||||
'Task closed successfully.' => 'Aufgabe erfolgreich geschlossen.',
|
||||
'Task closed successfully.' => 'Aufgabe erfolgreich abgeschlossen.',
|
||||
'Unable to update your task.' => 'Aktualisieren der Aufgabe nicht möglich.',
|
||||
'Task updated successfully.' => 'Aufgabe erfolgreich aktualisiert.',
|
||||
'Unable to create your task.' => 'Erstellen der Aufgabe nicht möglich.',
|
||||
|
@ -167,42 +160,41 @@ return array(
|
|||
'Unable to update your user.' => 'Änderung des Benutzers nicht möglich.',
|
||||
'User removed successfully.' => 'Benutzer erfolgreich gelöscht.',
|
||||
'Unable to remove this user.' => 'Löschen des Benutzers nicht möglich.',
|
||||
'Board updated successfully.' => 'Pinwand erfolgreich geändert.',
|
||||
'Board updated successfully.' => 'Pinnwand erfolgreich geändert.',
|
||||
'Ready' => 'Bereit',
|
||||
'Backlog' => 'Ideen',
|
||||
'Work in progress' => 'In Arbeit',
|
||||
'Done' => 'Erledigt',
|
||||
'Application version:' => 'Version:',
|
||||
'Completed on %B %e, %G at %k:%M %p' => 'Abgeschlossen am %d.%m.%Y um %H:%M',
|
||||
'%B %e, %G at %k:%M %p' => '%d.%m.%Y um %H:%M',
|
||||
'Completed on %B %e, %Y at %k:%M %p' => 'Abgeschlossen am %d.%m.%Y um %H:%M',
|
||||
'%B %e, %Y at %k:%M %p' => '%d.%m.%Y um %H:%M',
|
||||
'Date created' => 'Erstellt am',
|
||||
'Date completed' => 'Abgeschlossen am',
|
||||
'Id' => 'ID',
|
||||
'No task' => 'Keine Aufgabe',
|
||||
'Completed tasks' => 'Abgeschlossene Aufgaben',
|
||||
'List of projects' => 'Liste der Projekte',
|
||||
'Completed tasks for "%s"' => 'Abgeschlossene Aufgaben für «%s»',
|
||||
'Completed tasks for "%s"' => 'Abgeschlossene Aufgaben für "%s"',
|
||||
'%d closed tasks' => '%d abgeschlossene Aufgaben',
|
||||
'no task for this project' => 'Keine Aufgaben in diesem Projekt',
|
||||
'Public link' => 'Öffentlicher Link',
|
||||
'No task for this project' => 'Keine Aufgaben in diesem Projekt',
|
||||
'Public link' => 'Öffentlicher Link',
|
||||
'There is no column in your project!' => 'Es gibt keine Spalte in deinem Projekt!',
|
||||
'Change assignee' => 'Zuständigkeit ändern',
|
||||
'Change assignee for the task "%s"' => 'Zuständigkeit für diese Aufgabe ändern: «%s»',
|
||||
'Change assignee for the task "%s"' => 'Zuständigkeit für diese Aufgabe ändern: "%s"',
|
||||
'Timezone' => 'Zeitzone',
|
||||
'Sorry, I didn\'t found this information in my database!' => 'Diese Information wurde in der Datenbank nicht gefunden!',
|
||||
'Page not found' => 'Seite nicht gefunden',
|
||||
'Story Points' => 'Aufwand (Story Points)',
|
||||
// 'Complexity' => '',
|
||||
'limit' => 'Limit',
|
||||
'Task limit' => 'Maximale Anzahl von Aufgaben',
|
||||
'This value must be greater than %d' => 'Dieser Wert muss größer sein als %d',
|
||||
'Edit project access list' => 'Zugriffsberechtigungen des Projektes bearbeiten',
|
||||
'Edit users access' => 'Benutzerzugriff',
|
||||
'Allow this user' => 'Diesen Benutzer authorisieren',
|
||||
'Project access list for "%s"' => 'Zugriffsliste für Projekt «%s»',
|
||||
'Edit users access' => 'Benutzerzugriff ändern',
|
||||
'Allow this user' => 'Diesen Benutzer autorisieren',
|
||||
'Only those users have access to this project:' => 'Nur diese Benutzer haben Zugang zum Projekt:',
|
||||
'Don\'t forget that administrators have access to everything.' => 'Nicht vergessen: Administratoren haben überall Zugang.',
|
||||
'revoke' => 'entfernen',
|
||||
'List of authorized users' => 'Liste der authorisierten Benutzer',
|
||||
'List of authorized users' => 'Liste der autorisierten Benutzer',
|
||||
'User' => 'Benutzer',
|
||||
'Everybody have access to this project.' => 'Jeder hat Zugang zu diesem Projekt.',
|
||||
'You are not allowed to access to this project.' => 'Unzureichende Zugriffsrechte zu diesem Projekt.',
|
||||
|
@ -217,18 +209,18 @@ return array(
|
|||
'The description is required' => 'Eine Beschreibung wird benötigt',
|
||||
'Edit this task' => 'Aufgabe bearbeiten',
|
||||
'Due Date' => 'Fällig am',
|
||||
'm/d/Y' => 'd.m.Y', // Date format parsed with php
|
||||
'month/day/year' => 'TT.MM.JJJJ', // Help shown to the user
|
||||
'm/d/Y' => 'd.m.Y',
|
||||
'month/day/year' => 'TT.MM.JJJJ',
|
||||
'Invalid date' => 'Ungültiges Datum',
|
||||
'Must be done before %B %e, %G' => 'Muss vor dem %d.%m.%Y erledigt werden',
|
||||
'%B %e, %G' => '%d.%m.%Y',
|
||||
'Must be done before %B %e, %Y' => 'Muss vor dem %d.%m.%Y erledigt werden',
|
||||
'%B %e, %Y' => '%d.%m.%Y',
|
||||
'Automatic actions' => 'Automatische Aktionen',
|
||||
'Your automatic action have been created successfully.' => 'Die Automatische Aktion wurde erfolgreich erstellt.',
|
||||
'Unable to create your automatic action.' => 'Automatische Aktion konnte nicht erstellt werden.',
|
||||
'Unable to create your automatic action.' => 'Erstellen der automatischen Aktion nicht möglich.',
|
||||
'Remove an action' => 'Aktion löschen',
|
||||
'Unable to remove this action.' => 'Aktion konnte nicht gelöscht werden',
|
||||
'Unable to remove this action.' => 'Löschen der Aktion nicht möglich.',
|
||||
'Action removed successfully.' => 'Aktion erfolgreich gelöscht.',
|
||||
'Automatic actions for the project "%s"' => 'Automatische Aktionen für das Projekt «%s»',
|
||||
'Automatic actions for the project "%s"' => 'Automatische Aktionen für das Projekt "%s"',
|
||||
'Defined actions' => 'Definierte Aktionen',
|
||||
'Add an action' => 'Aktion hinzufügen',
|
||||
'Event name' => 'Ereignis',
|
||||
|
@ -240,17 +232,17 @@ return array(
|
|||
'Next step' => 'Weiter',
|
||||
'Define action parameters' => 'Aktionsparameter definieren',
|
||||
'Save this action' => 'Aktion speichern',
|
||||
'Do you really want to remove this action: "%s"?' => 'Soll diese Aktion wirklich gelöscht werden: «%s»?',
|
||||
'Do you really want to remove this action: "%s"?' => 'Soll diese Aktion wirklich gelöscht werden: "%s"?',
|
||||
'Remove an automatic action' => 'Löschen einer automatischen Aktion',
|
||||
'Close the task' => 'Aufgabe abschließen',
|
||||
'Assign the task to a specific user' => 'Aufgabe einem Benutzer zuordnen',
|
||||
'Assign the task to the person who does the action' => 'Aufgabe dem Benutzer zuordnen, der die Aktion ausgeführt hat',
|
||||
'Duplicate the task to another project' => 'Aufgabe in ein anderes Projekt kopieren',
|
||||
'Move a task to another column' => 'Aufgabe in andere Spalte verschoben',
|
||||
'Move a task to another position in the same column' => 'Aufgabe an andere Position in der gleichen Spalte verschoben',
|
||||
'Task modification' => 'Änderung einer Aufgabe',
|
||||
'Task creation' => 'Erstellung einer Aufgabe',
|
||||
'Open a closed task' => 'Abgeschlossenen Aufgabe wieder eröffnen',
|
||||
'Move a task to another column' => 'Aufgabe in andere Spalte verschieben',
|
||||
'Move a task to another position in the same column' => 'Aufgabe an andere Position in der gleichen Spalte verschieben',
|
||||
'Task modification' => 'Aufgabe ändern',
|
||||
'Task creation' => 'Aufgabe erstellen',
|
||||
'Open a closed task' => 'Abgeschlossene Aufgabe wieder eröffnen',
|
||||
'Closing a task' => 'Aufgabe abschließen',
|
||||
'Assign a color to a specific user' => 'Einem Benutzer eine Farbe zuordnen',
|
||||
'Column title' => 'Spaltentitel',
|
||||
|
@ -262,26 +254,26 @@ return array(
|
|||
'link' => 'Link',
|
||||
'Update this comment' => 'Kommentar aktualisieren',
|
||||
'Comment updated successfully.' => 'Kommentar erfolgreich aktualisiert.',
|
||||
'Unable to update your comment.' => 'Kommentar konnte nicht aktualisiert werden.',
|
||||
'Unable to update your comment.' => 'Aktualisierung des Kommentars nicht möglich.',
|
||||
'Remove a comment' => 'Kommentar löschen',
|
||||
'Comment removed successfully.' => 'Kommentar erfolgreich gelöscht.',
|
||||
'Unable to remove this comment.' => 'Kommentar konnte nicht gelöscht werden.',
|
||||
'Unable to remove this comment.' => 'Löschen des Kommentars nicht möglich.',
|
||||
'Do you really want to remove this comment?' => 'Soll dieser Kommentar wirklich gelöscht werden?',
|
||||
'Only administrators or the creator of the comment can access to this page.' => 'Nur Administratoren und der Ersteller des Kommentars könne diese Seite verwenden.',
|
||||
'Only administrators or the creator of the comment can access to this page.' => 'Nur Administratoren und der Ersteller des Kommentars haben Zugriff auf diese Seite.',
|
||||
'Details' => 'Details',
|
||||
'Current password for the user "%s"' => 'Aktuelles Passwort für den Benutzer «%s»',
|
||||
'Current password for the user "%s"' => 'Aktuelles Passwort für den Benutzer "%s"',
|
||||
'The current password is required' => 'Das aktuelle Passwort wird benötigt',
|
||||
'Wrong password' => 'Falsches Passwort',
|
||||
'Reset all tokens' => 'Alle Tokens zurücksetzten',
|
||||
'Reset all tokens' => 'Alle Tokens zurücksetzen',
|
||||
'All tokens have been regenerated.' => 'Alle Tokens wurden zurückgesetzt.',
|
||||
'Unknown' => 'Unbekannt',
|
||||
'Last logins' => 'Letzte Anmeldungen',
|
||||
'Login date' => 'Anmeldedatum',
|
||||
'Authentication method' => 'Anmeldemethode',
|
||||
'Authentication method' => 'Authentisierungsmethode',
|
||||
'IP address' => 'IP Adresse',
|
||||
'User agent' => 'User Agent',
|
||||
'Persistent connections' => 'Bestehende Verbindungen',
|
||||
'No session' => 'Keine Session',
|
||||
'No session.' => 'Keine Sitzung.',
|
||||
'Expiration date' => 'Ablaufdatum',
|
||||
'Remember Me' => 'Angemeldet bleiben',
|
||||
'Creation date' => 'Erstellungsdatum',
|
||||
|
@ -292,31 +284,31 @@ return array(
|
|||
'Closed' => 'Abgeschlossen',
|
||||
'Search' => 'Suchen',
|
||||
'Nothing found.' => 'Nichts gefunden.',
|
||||
'Search in the project "%s"' => 'Suche in Projekt «%s»',
|
||||
'Search in the project "%s"' => 'Suche in Projekt "%s"',
|
||||
'Due date' => 'Fälligkeitsdatum',
|
||||
'Others formats accepted: %s and %s' => 'Andere akzeptierte Formate: %s und %s',
|
||||
'Description' => 'Beschreibung',
|
||||
'%d comments' => '%d Kommentare',
|
||||
'%d comment' => '%d Kommentar',
|
||||
'Email address invalid' => 'Ungültige Email-Adresse',
|
||||
'Email address invalid' => 'Ungültige E-Mail-Adresse',
|
||||
'Your Google Account is not linked anymore to your profile.' => 'Google Account nicht mehr mit dem Profil verbunden.',
|
||||
'Unable to unlink your Google Account.' => 'Trennung der Verbindung zum Google Account nicht möglich.',
|
||||
'Google authentication failed' => 'Zugang mit Google fehl geschlagen',
|
||||
'Unable to link your Google Account.' => 'Verbindung mit diesem Google Account nicht möglich.',
|
||||
'Your Google Account is linked to your profile successfully.' => 'Der Google Account wurde erfolgreich verbunden.',
|
||||
'Email' => 'Email',
|
||||
'Email' => 'E-Mail',
|
||||
'Link my Google Account' => 'Verbinde meinen Google Account',
|
||||
'Unlink my Google Account' => 'Verbindung mit meinem Google Account trennen',
|
||||
'Login with my Google Account' => 'Anmelden mit meinem Google Account',
|
||||
'Project not found.' => 'Das Projekt wurde nicht gefunden.',
|
||||
'Task #%d' => 'Aufgabe #%d',
|
||||
'Task #%d' => 'Aufgabe Nr. %d',
|
||||
'Task removed successfully.' => 'Aufgabe erfolgreich gelöscht.',
|
||||
'Unable to remove this task.' => 'Löschen der Aufgabe nicht möglich.',
|
||||
'Remove a task' => 'Aufgabe löschen',
|
||||
'Do you really want to remove this task: "%s"?' => 'Soll diese Aufgabe wirklich gelöscht werden: «%s»?',
|
||||
'Assign automatically a color based on a category' => 'Automatisch eine Farbe anhand der Kategorie vergeben',
|
||||
'Assign automatically a category based on a color' => 'Automatisch eine Kategorie anhand der Farbe vergeben',
|
||||
'Task creation or modification' => 'Erstellung oder Änderung einer Aufgabe',
|
||||
'Do you really want to remove this task: "%s"?' => 'Soll diese Aufgabe wirklich gelöscht werden: "%s"?',
|
||||
'Assign automatically a color based on a category' => 'Automatisch eine Farbe anhand der Kategorie zuweisen',
|
||||
'Assign automatically a category based on a color' => 'Automatisch eine Kategorie anhand der Farbe zuweisen',
|
||||
'Task creation or modification' => 'Aufgabe erstellen oder ändern',
|
||||
'Category' => 'Kategorie',
|
||||
'Category:' => 'Kategorie:',
|
||||
'Categories' => 'Kategorien',
|
||||
|
@ -328,20 +320,20 @@ return array(
|
|||
'Remove a category' => 'Kategorie löschen',
|
||||
'Category removed successfully.' => 'Kategorie erfolgreich gelöscht.',
|
||||
'Unable to remove this category.' => 'Löschen der Kategorie nicht möglich.',
|
||||
'Category modification for the project "%s"' => 'Kategorie für das Projekt «%s» bearbeiten',
|
||||
'Category modification for the project "%s"' => 'Kategorie für das Projekt "%s" bearbeiten',
|
||||
'Category Name' => 'Kategoriename',
|
||||
'Categories for the project "%s"' => 'Kategorien des Projektes «%s»',
|
||||
'Categories for the project "%s"' => 'Kategorien des Projektes "%s"',
|
||||
'Add a new category' => 'Neue Kategorie',
|
||||
'Do you really want to remove this category: "%s"?' => 'Soll diese Kategorie wirklich gelöscht werden: «%s»?',
|
||||
'Do you really want to remove this category: "%s"?' => 'Soll diese Kategorie wirklich gelöscht werden: "%s"?',
|
||||
'Filter by category' => 'Kategorie filtern',
|
||||
'All categories' => 'Alle Kategorien',
|
||||
'No category' => 'keine Kategorie',
|
||||
'No category' => 'Keine Kategorie',
|
||||
'The name is required' => 'Der Name ist erforderlich',
|
||||
'Remove a file' => 'Datei löschen',
|
||||
'Unable to remove this file.' => 'Löschen der Datei nicht möglich.',
|
||||
'File removed successfully.' => 'Datei erfolgreich gelöscht.',
|
||||
'Attach a document' => 'Datei anhängen',
|
||||
'Do you really want to remove this file: "%s"?' => 'Soll diese Datei wirklich gelöscht werden: «%s»?',
|
||||
'Do you really want to remove this file: "%s"?' => 'Soll diese Datei wirklich gelöscht werden: "%s"?',
|
||||
'open' => 'öffnen',
|
||||
'Attachments' => 'Anhänge',
|
||||
'Edit the task' => 'Aufgabe bearbeiten',
|
||||
|
@ -352,7 +344,7 @@ return array(
|
|||
'Time tracking' => 'Zeiterfassung',
|
||||
'Estimate:' => 'Geschätzt:',
|
||||
'Spent:' => 'Aufgewendet:',
|
||||
'Do you really want to remove this sub-task?' => 'Soll diese Unteraufgabe wirklich gelöscht werden: «%s»?',
|
||||
'Do you really want to remove this sub-task?' => 'Soll diese Unteraufgabe wirklich gelöscht werden: "%s"?',
|
||||
'Remaining:' => 'Verbleibend:',
|
||||
'hours' => 'Stunden',
|
||||
'spent' => 'aufgewendet',
|
||||
|
@ -367,7 +359,6 @@ return array(
|
|||
'The time must be a numeric value' => 'Zeit nur als nummerische Angabe',
|
||||
'Todo' => 'Nicht gestartet',
|
||||
'In progress' => 'In Bearbeitung',
|
||||
'Done' => 'Erledigt',
|
||||
'Sub-task removed successfully.' => 'Unteraufgabe erfolgreich gelöscht.',
|
||||
'Unable to remove this sub-task.' => 'Löschen der Unteraufgabe nicht möglich.',
|
||||
'Sub-task updated successfully.' => 'Unteraufgabe erfolgreich aktualisiert.',
|
||||
|
@ -384,7 +375,132 @@ return array(
|
|||
'Unable to unlink your GitHub Account.' => 'Trennung der Verbindung zum GitHub Account nicht möglich.',
|
||||
'Login with my GitHub Account' => 'Anmelden mit meinem GitHub Account',
|
||||
'Link my GitHub Account' => 'Mit meinem GitHub Account verbinden',
|
||||
'Unlink my GitHub Account' => 'Verbindung mit meinem GitHub Account trennen',
|
||||
'Unlink my GitHub Account' => 'Verbindung mit meinem GitHub Account trennen',
|
||||
'Created by %s' => 'Erstellt durch %s',
|
||||
'Last modified on %B %e, %G at %k:%M %p' => 'Letzte Änderung am %d.%m.%Y um %H:%M',
|
||||
'Last modified on %B %e, %Y at %k:%M %p' => 'Letzte Änderung am %d.%m.%Y um %H:%M',
|
||||
'Tasks Export' => 'Aufgaben exportieren',
|
||||
'Tasks exportation for "%s"' => 'Aufgaben exportieren für "%s"',
|
||||
'Start Date' => 'Anfangsdatum',
|
||||
'End Date' => 'Enddatum',
|
||||
'Execute' => 'Ausführen',
|
||||
'Task Id' => 'Aufgaben ID',
|
||||
'Creator' => 'Erstellt von',
|
||||
'Modification date' => 'Änderungsdatum',
|
||||
'Completion date' => 'Abschlussdatum',
|
||||
'Webhook URL for task creation' => 'Webhook URL zur Aufgabenerstellung',
|
||||
'Webhook URL for task modification' => 'Webhook URL zur Aufgabenänderung',
|
||||
// 'Clone' => '',
|
||||
// 'Clone Project' => '',
|
||||
// 'Project cloned successfully.' => '',
|
||||
// 'Unable to clone this project.' => '',
|
||||
// 'Email notifications' => '',
|
||||
// 'Enable email notifications' => '',
|
||||
// 'Task position:' => '',
|
||||
// 'The task #%d have been opened.' => '',
|
||||
// 'The task #%d have been closed.' => '',
|
||||
// 'Sub-task updated' => '',
|
||||
// 'Title:' => '',
|
||||
// 'Status:' => '',
|
||||
// 'Assignee:' => '',
|
||||
// 'Time tracking:' => '',
|
||||
// 'New sub-task' => '',
|
||||
// 'New attachment added "%s"' => '',
|
||||
// 'Comment updated' => '',
|
||||
// 'New comment posted by %s' => '',
|
||||
// 'List of due tasks for the project "%s"' => '',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
// '[%s][New subtask] %s (#%d)' => '',
|
||||
// '[%s][Subtask updated] %s (#%d)' => '',
|
||||
// '[%s][New task] %s (#%d)' => '',
|
||||
// '[%s][Task updated] %s (#%d)' => '',
|
||||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
// '[Kanboard] Notification' => '',
|
||||
// 'I want to receive notifications only for those projects:' => '',
|
||||
// 'view the task on Kanboard' => '',
|
||||
// 'Public access' => '',
|
||||
// 'Categories management' => '',
|
||||
// 'Users management' => '',
|
||||
// 'Active tasks' => '',
|
||||
// 'Disable public access' => '',
|
||||
// 'Enable public access' => '',
|
||||
// 'Active projects' => '',
|
||||
// 'Inactive projects' => '',
|
||||
// 'Public access disabled' => '',
|
||||
// 'Do you really want to disable this project: "%s"?' => '',
|
||||
// 'Do you really want to duplicate this project: "%s"?' => '',
|
||||
// 'Do you really want to enable this project: "%s"?' => '',
|
||||
// 'Project activation' => '',
|
||||
'Move the task to another project' => 'Aufgabe in ein anderes Projekt verschieben',
|
||||
'Move to another project' => 'In anderes Projekt verschieben',
|
||||
// 'Do you really want to duplicate this task?' => '',
|
||||
// 'Duplicate a task' => '',
|
||||
// 'External accounts' => '',
|
||||
// 'Account type' => '',
|
||||
// 'Local' => '',
|
||||
// 'Remote' => '',
|
||||
// 'Enabled' => '',
|
||||
// 'Disabled' => '',
|
||||
// 'Google account linked' => '',
|
||||
// 'Github account linked' => '',
|
||||
// 'Username:' => '',
|
||||
// 'Name:' => '',
|
||||
// 'Email:' => '',
|
||||
// 'Default project:' => '',
|
||||
// 'Notifications:' => '',
|
||||
// 'Group:' => '',
|
||||
// 'Regular user' => '',
|
||||
// 'Account type:' => '',
|
||||
// 'Edit profile' => '',
|
||||
// 'Change password' => '',
|
||||
// 'Password modification' => '',
|
||||
// 'External authentications' => '',
|
||||
// 'Google Account' => '',
|
||||
// 'Github Account' => '',
|
||||
// 'Never connected.' => '',
|
||||
// 'No account linked.' => '',
|
||||
// 'Account linked.' => '',
|
||||
// 'No external authentication enabled.' => '',
|
||||
// 'Password modified successfully.' => '',
|
||||
// 'Unable to change the password.' => '',
|
||||
// 'Change category for the task "%s"' => '',
|
||||
// 'Change category' => '',
|
||||
// '%s updated the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s open the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the position #%d in the column "%s"' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the column "%s"' => '',
|
||||
// '%s created the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s closed the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s created a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s updated a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// 'Assigned to %s with an estimate of %s/%sh' => '',
|
||||
// 'Not assigned, estimate of %sh' => '',
|
||||
// '%s updated a comment on the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s commented the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s\'s activity' => '',
|
||||
// 'No activity.' => '',
|
||||
// 'RSS feed' => '',
|
||||
// '%s updated a comment on the task #%d' => '',
|
||||
// '%s commented on the task #%d' => '',
|
||||
// '%s updated a subtask for the task #%d' => '',
|
||||
// '%s created a subtask for the task #%d' => '',
|
||||
// '%s updated the task #%d' => '',
|
||||
// '%s created the task #%d' => '',
|
||||
// '%s closed the task #%d' => '',
|
||||
// '%s open the task #%d' => '',
|
||||
// '%s moved the task #%d to the column "%s"' => '',
|
||||
// '%s moved the task #%d to the position %d in the column "%s"' => '',
|
||||
// 'Activity' => '',
|
||||
// 'Default values are "%s"' => '',
|
||||
// 'Default columns for new projects (Comma-separated)' => '',
|
||||
// 'Task assignee change' => '',
|
||||
// '%s change the assignee of the task #%d' => '',
|
||||
// '%s change the assignee of the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '[%s][Column Change] %s (#%d)' => '',
|
||||
// '[%s][Position Change] %s (#%d)' => '',
|
||||
// '[%s][Assignee Change] %s (#%d)' => '',
|
||||
// 'New password for the user "%s"' => '',
|
||||
);
|
||||
|
|
|
@ -1,14 +1,6 @@
|
|||
<?php
|
||||
|
||||
return array(
|
||||
'English' => 'Inglés',
|
||||
'French' => 'Francés',
|
||||
'Polish' => 'Polaco',
|
||||
'Portuguese (Brazilian)' => 'Portugués (Brasil)',
|
||||
'Spanish' => 'Español',
|
||||
// 'German' => '',
|
||||
// 'Chinese (Simplified)' => '',
|
||||
// 'Swedish' => 'Suèdois',
|
||||
'None' => 'Ninguno',
|
||||
'edit' => 'modificar',
|
||||
'Edit' => 'Modificar',
|
||||
|
@ -37,7 +29,7 @@ return array(
|
|||
'All users' => 'Todos los usuarios',
|
||||
'Username' => 'Nombre de usuario',
|
||||
'Password' => 'Contraseña',
|
||||
'Default Project' => 'Proyecto por defecto',
|
||||
'Default project' => 'Proyecto por defecto',
|
||||
'Administrator' => 'Administrador',
|
||||
'Sign in' => 'Iniciar sesión',
|
||||
'Users' => 'Usuarios',
|
||||
|
@ -59,6 +51,7 @@ return array(
|
|||
'Status' => 'Estado',
|
||||
'Tasks' => 'Tareas',
|
||||
'Board' => 'Tablero',
|
||||
'Actions' => 'Acciones',
|
||||
'Inactive' => 'Inactivo',
|
||||
'Active' => 'Activo',
|
||||
'Column %d' => 'Columna %d',
|
||||
|
@ -73,7 +66,7 @@ return array(
|
|||
'Do you really want to remove this project: "%s"?' => '¿De verdad que deseas eliminar este proyecto: « %s » ?',
|
||||
'Remove project' => 'Suprimir el proyecto',
|
||||
'Boards' => 'Tableros',
|
||||
'Edit the board for "%s"' => 'Modificar el tablero por « %s »',
|
||||
'Edit the board for "%s"' => 'Modificar el tablero para « %s »',
|
||||
'All projects' => 'Todos los proyectos',
|
||||
'Change columns' => 'Cambiar las columnas',
|
||||
'Add a new column' => 'Añadir una nueva columna',
|
||||
|
@ -90,7 +83,8 @@ return array(
|
|||
'Settings' => 'Preferencias',
|
||||
'Application settings' => 'Parámetros de la aplicación',
|
||||
'Language' => 'Idioma',
|
||||
'Webhooks token:' => 'Identificador (token) para los webhooks :',
|
||||
'Webhooks token:' => 'Ficha de seguridad (token) para los webhooks :',
|
||||
'API token:' => 'Ficha de seguridad (token) para API:',
|
||||
'More information' => 'Más informaciones',
|
||||
'Database size:' => 'Tamaño de la base de datos:',
|
||||
'Download the database' => 'Descargar la base de datos',
|
||||
|
@ -110,7 +104,7 @@ return array(
|
|||
'Open a task' => 'Abrir una tarea',
|
||||
'Do you really want to open this task: "%s"?' => '¿Realmente desea abrir esta tarea: « %s » ?',
|
||||
'Back to the board' => 'Volver al tablero',
|
||||
'Created on %B %e, %G at %k:%M %p' => 'Creado el %d/%m/%Y a las %H:%M',
|
||||
'Created on %B %e, %Y at %k:%M %p' => 'Creado el %d/%m/%Y a las %H:%M',
|
||||
'There is nobody assigned' => 'No hay nadie asignado a esta tarea',
|
||||
'Column on the board:' => 'Columna en el tablero: ',
|
||||
'Status is open' => 'Estado abierto',
|
||||
|
@ -127,7 +121,7 @@ return array(
|
|||
'The username must be unique' => 'El nombre de usuario debe ser único',
|
||||
'The username must be alphanumeric' => 'El nombre de usuario debe ser alfanumérico',
|
||||
'The user id is required' => 'El identificador del usuario es obligatorio',
|
||||
'Passwords doesn\'t matches' => 'Las contraseñas no corresponden',
|
||||
'Passwords don\'t match' => 'Las contraseñas no coinciden',
|
||||
'The confirmation is required' => 'La confirmación es obligatoria',
|
||||
'The column is required' => 'La columna es obligatoria',
|
||||
'The project is required' => 'El proyecto es obligatorio',
|
||||
|
@ -170,34 +164,33 @@ return array(
|
|||
'Ready' => 'Listo',
|
||||
'Backlog' => 'En espera',
|
||||
'Work in progress' => 'En curso',
|
||||
'Done' => 'Terminado',
|
||||
'Done' => 'Hecho',
|
||||
'Application version:' => 'Versión de la aplicación:',
|
||||
'Completed on %B %e, %G at %k:%M %p' => 'Completado el %d/%m/%Y a las %H:%M',
|
||||
'%B %e, %G at %k:%M %p' => '%d/%m/%Y a las %H:%M',
|
||||
'Completed on %B %e, %Y at %k:%M %p' => 'Completado el %d/%m/%Y a las %H:%M',
|
||||
'%B %e, %Y at %k:%M %p' => '%d/%m/%Y a las %H:%M',
|
||||
'Date created' => 'Fecha de creación',
|
||||
'Date completed' => 'Fecha de terminación',
|
||||
'Id' => 'Identificador',
|
||||
'No task' => 'Ninguna tarea',
|
||||
'Completed tasks' => 'Tareas completadas',
|
||||
'List of projects' => 'Lista de los proyectos',
|
||||
'Completed tasks for "%s"' => 'Tarea completada por « %s »',
|
||||
'Completed tasks for "%s"' => 'Tareas completadas por « %s »',
|
||||
'%d closed tasks' => '%d tareas completadas',
|
||||
'no task for this project' => 'ninguna tarea para este proyecto',
|
||||
'Public link' => 'Enlace público',
|
||||
'No task for this project' => 'Ninguna tarea para este proyecto',
|
||||
'Public link' => 'Vinculación pública',
|
||||
'There is no column in your project!' => '¡No hay ninguna columna para este proyecto!',
|
||||
'Change assignee' => 'Cambiar la persona asignada',
|
||||
'Change assignee for the task "%s"' => 'Cambiar la persona asignada por la tarea « %s »',
|
||||
'Timezone' => 'Zona horaria',
|
||||
'Sorry, I didn\'t found this information in my database!' => 'Lo siento no he encontrado información en la base de datos!',
|
||||
'Page not found' => 'Página no encontrada',
|
||||
'Story Points' => 'Complejidad',
|
||||
'Complexity' => 'Complejidad',
|
||||
'limit' => 'límite',
|
||||
'Task limit' => 'Número máximo de tareas',
|
||||
'This value must be greater than %d' => 'Este valor no debe de ser más grande que %d',
|
||||
'Edit project access list' => 'Editar los permisos del proyecto',
|
||||
'Edit users access' => 'Editar los permisos de usuario',
|
||||
'Allow this user' => 'Autorizar este usuario',
|
||||
'Project access list for "%s"' => 'Permisos del proyecto « %s »',
|
||||
'Only those users have access to this project:' => 'Solo estos usuarios tienen acceso a este proyecto:',
|
||||
'Don\'t forget that administrators have access to everything.' => 'No olvide que los administradores tienen acceso a todo.',
|
||||
'revoke' => 'revocar',
|
||||
|
@ -216,11 +209,11 @@ return array(
|
|||
'The description is required' => 'La descripción es obligatoria',
|
||||
'Edit this task' => 'Editar esta tarea',
|
||||
'Due Date' => 'Fecha límite',
|
||||
'm/d/Y' => 'd/m/Y', // Date format parsed with php
|
||||
'month/day/year' => 'día/mes/año', // Help shown to the user
|
||||
'm/d/Y' => 'd/m/Y',
|
||||
'month/day/year' => 'día/mes/año',
|
||||
'Invalid date' => 'Fecha no válida',
|
||||
'Must be done before %B %e, %G' => 'Debe de estar hecho antes del %d/%m/%Y',
|
||||
'%B %e, %G' => '%d/%m/%Y',
|
||||
'Must be done before %B %e, %Y' => 'Debe de estar hecho antes del %d/%m/%Y',
|
||||
'%B %e, %Y' => '%d/%m/%Y',
|
||||
'Automatic actions' => 'Acciones automatizadas',
|
||||
'Your automatic action have been created successfully.' => 'La acción automatizada ha sido creada correctamente.',
|
||||
'Unable to create your automatic action.' => 'No se puede crear esta acción automatizada.',
|
||||
|
@ -229,6 +222,7 @@ return array(
|
|||
'Action removed successfully.' => 'La acción ha sido borrada correctamente.',
|
||||
'Automatic actions for the project "%s"' => 'Acciones automatizadas para este proyecto « %s »',
|
||||
'Defined actions' => 'Acciones definidas',
|
||||
'Add an action' => 'Agregar una acción',
|
||||
'Event name' => 'Nombre del evento',
|
||||
'Action name' => 'Nombre de la acción',
|
||||
'Action parameters' => 'Parámetros de la acción',
|
||||
|
@ -257,7 +251,7 @@ return array(
|
|||
'Move Down' => 'Mover hacia abajo',
|
||||
'Duplicate to another project' => 'Duplicar a otro proyecto',
|
||||
'Duplicate' => 'Duplicar',
|
||||
'link' => 'enlace',
|
||||
'link' => 'vinculación',
|
||||
'Update this comment' => 'Actualizar este comentario',
|
||||
'Comment updated successfully.' => 'El comentario ha sido actualizado correctamente.',
|
||||
'Unable to update your comment.' => 'No se puede actualizar este comentario.',
|
||||
|
@ -279,7 +273,7 @@ return array(
|
|||
'IP address' => 'Dirección IP',
|
||||
'User agent' => 'Agente de usuario',
|
||||
'Persistent connections' => 'Conexión persistente',
|
||||
'No session' => 'No existe sesión',
|
||||
'No session.' => 'No existe sesión.',
|
||||
'Expiration date' => 'Fecha de expiración',
|
||||
'Remember Me' => 'Recuérdame',
|
||||
'Creation date' => 'Fecha de creación',
|
||||
|
@ -297,15 +291,15 @@ return array(
|
|||
'%d comments' => '%d comentarios',
|
||||
'%d comment' => '%d comentario',
|
||||
'Email address invalid' => 'Dirección de correo inválida',
|
||||
'Your Google Account is not linked anymore to your profile.' => 'Tu Cuenta en Google ya no se encuentra enlazada con tu perfil',
|
||||
'Unable to unlink your Google Account.' => 'No puedo desenlazar tu Cuenta en Google.',
|
||||
'Your Google Account is not linked anymore to your profile.' => 'Tu Cuenta en Google ya no se encuentra vinculada con tu perfil',
|
||||
'Unable to unlink your Google Account.' => 'No puedo desvincular tu Cuenta en Google.',
|
||||
'Google authentication failed' => 'Ha fallado tu autenticación en Google',
|
||||
'Unable to link your Google Account.' => 'No puedo enlazar con tu Cuenta en Google.',
|
||||
'Your Google Account is linked to your profile successfully.' => 'Se ha enlazado correctamente tu Cuenta en Google con tu perfil.',
|
||||
'Unable to link your Google Account.' => 'No puedo vincular con tu Cuenta en Google.',
|
||||
'Your Google Account is linked to your profile successfully.' => 'Se ha vinculado correctamente tu Cuenta en Google con tu perfil.',
|
||||
'Email' => 'Correo',
|
||||
'Link my Google Account' => 'Enlaza con mi Cuenta en Google',
|
||||
'Unlink my Google Account' => 'Desenlaza con mi Cuenta en Google',
|
||||
'Login with my Google Account' => 'Ingresa con mi Cuenta en Google',
|
||||
'Link my Google Account' => 'Vincular con mi Cuenta en Google',
|
||||
'Unlink my Google Account' => 'Desvincular de mi Cuenta en Google',
|
||||
'Login with my Google Account' => 'Ingresar con mi Cuenta en Google',
|
||||
'Project not found.' => 'Proyecto no hallado.',
|
||||
'Task #%d' => 'Tarea número %d',
|
||||
'Task removed successfully.' => 'Tarea suprimida correctamente.',
|
||||
|
@ -365,7 +359,6 @@ return array(
|
|||
'The time must be a numeric value' => 'El tiempo debe de ser un valor numérico',
|
||||
'Todo' => 'Por hacer',
|
||||
'In progress' => 'En progreso',
|
||||
'Done' => 'Hecho',
|
||||
'Sub-task removed successfully.' => 'Sub-tarea suprimida correctamente.',
|
||||
'Unable to remove this sub-task.' => 'No pude suprimir esta sub-tarea.',
|
||||
'Sub-task updated successfully.' => 'Sub-tarea actualizada correctamente.',
|
||||
|
@ -374,16 +367,140 @@ return array(
|
|||
'Sub-task added successfully.' => 'Sub-tarea añadida correctamente.',
|
||||
'Maximum size: ' => 'Tamaño máximo',
|
||||
'Unable to upload the file.' => 'No pude cargar el fichero.',
|
||||
'Actions' => 'Acciones',
|
||||
// 'Display another project' => '',
|
||||
// 'Your GitHub account was successfully linked to your profile.' => '',
|
||||
// 'Unable to link your GitHub Account.' => '',
|
||||
// 'GitHub authentication failed' => '',
|
||||
// 'Your GitHub account is no longer linked to your profile.' => '',
|
||||
// 'Unable to unlink your GitHub Account.' => '',
|
||||
// 'Login with my GitHub Account' => '',
|
||||
// 'Link my GitHub Account' => '',
|
||||
// 'Unlink my GitHub Account' => '',
|
||||
// 'Created by %s' => 'Créé par %s',
|
||||
// 'Last modified on %B %e, %G at %k:%M %p' => '',
|
||||
'Display another project' => 'Mostrar otro proyecto',
|
||||
'Your GitHub account was successfully linked to your profile.' => 'Tu cuenta de GitHub ha sido correctamente vinculada con tu perfil',
|
||||
'Unable to link your GitHub Account.' => 'Imposible vincular tu cuenta de GitHub',
|
||||
'GitHub authentication failed' => 'Falló la autenticación de GitHub',
|
||||
'Your GitHub account is no longer linked to your profile.' => 'Tu cuenta de GitHub ya no está vinculada a tu perfil',
|
||||
'Unable to unlink your GitHub Account.' => 'Imposible desvincular tu cuenta de GitHub',
|
||||
'Login with my GitHub Account' => 'Ingresar con mi cuenta de GitHub',
|
||||
'Link my GitHub Account' => 'Vincular mi cuenta de GitHub',
|
||||
'Unlink my GitHub Account' => 'Desvincular mi cuenta de GitHub',
|
||||
'Created by %s' => 'Creado por %s',
|
||||
'Last modified on %B %e, %Y at %k:%M %p' => 'Última modificación %B %e, %Y a las %k:%M %p',
|
||||
'Tasks Export' => 'Exportar tareas',
|
||||
'Tasks exportation for "%s"' => 'Exportación de tareas para "%s"',
|
||||
'Start Date' => 'Fecha de inicio',
|
||||
'End Date' => 'Fecha final',
|
||||
'Execute' => 'Ejecutar',
|
||||
'Task Id' => 'ID de tarea',
|
||||
'Creator' => 'Creador',
|
||||
'Modification date' => 'Fecha de modificación',
|
||||
'Completion date' => 'Fecha de terminación',
|
||||
'Webhook URL for task creation' => 'Webhook para la creación de tareas',
|
||||
'Webhook URL for task modification' => 'Webhook para la modificación de tareas',
|
||||
'Clone' => 'Clonar',
|
||||
'Clone Project' => 'Clonar proyecto',
|
||||
'Project cloned successfully.' => 'Proyecto clonado correctamente',
|
||||
'Unable to clone this project.' => 'Impsible clonar proyecto',
|
||||
'Email notifications' => 'Notificaciones correo electrónico',
|
||||
'Enable email notifications' => 'Habilitar notificaciones por correo electrónico',
|
||||
'Task position:' => 'Posición de la tarea',
|
||||
'The task #%d have been opened.' => 'La tarea #%d ha sido abierta',
|
||||
'The task #%d have been closed.' => 'La tarea #%d ha sido cerrada',
|
||||
'Sub-task updated' => 'Subtarea actualizada',
|
||||
'Title:' => 'Título:',
|
||||
'Status:' => 'Estado:',
|
||||
'Assignee:' => 'Asignada a:',
|
||||
'Time tracking:' => 'Control de tiempo:',
|
||||
'New sub-task' => 'Nueva subtarea',
|
||||
'New attachment added "%s"' => 'Nuevo adjunto agregado "%s"',
|
||||
'Comment updated' => 'Comentario actualizado',
|
||||
'New comment posted by %s' => 'Nuevo comentario agregado por %s',
|
||||
'List of due tasks for the project "%s"' => 'Lista de tareas para el proyecto "%s"',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][uevo adjunto] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][Nuevo comentario] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][Comentario actualizado] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][Nueva subtarea] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][Subtarea actualizada] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][Nueva tarea] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][Tarea actualizada] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][Tarea cerrada] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][Tarea abierta] %s (#%d)',
|
||||
'[%s][Due tasks]' => '[%s][Tareas vencidas]',
|
||||
'[Kanboard] Notification' => '[Kanboard] Notificación',
|
||||
'I want to receive notifications only for those projects:' => 'Quiero recibir notificaciones sólo de estos proyectos:',
|
||||
'view the task on Kanboard' => 'ver la tarea en Kanboard',
|
||||
'Public access' => 'Acceso público',
|
||||
'Categories management' => 'Gestión de Categorías',
|
||||
'Users management' => 'Gestión de Usuarios',
|
||||
'Active tasks' => 'Tareas activas',
|
||||
'Disable public access' => 'Desactivar acceso público',
|
||||
'Enable public access' => 'Activar acceso público',
|
||||
'Active projects' => 'Proyectos activos',
|
||||
'Inactive projects' => 'Proyectos inactivos',
|
||||
'Public access disabled' => 'Acceso público desactivado',
|
||||
'Do you really want to disable this project: "%s"?' => '¿Realmente deseas desactivar este proyecto: "%s"?',
|
||||
'Do you really want to duplicate this project: "%s"?' => '¿Realmente deseas duplicar este proyecto: "%s"?',
|
||||
'Do you really want to enable this project: "%s"?' => '¿Realmente deseas activar este proyecto: "%s"?',
|
||||
'Project activation' => 'Activación de Proyecto',
|
||||
'Move the task to another project' => 'Mover la tarea a otro proyecto',
|
||||
'Move to another project' => 'Mover a otro proyecto',
|
||||
'Do you really want to duplicate this task?' => '¿Realmente deseas duplicar esta tarea?',
|
||||
'Duplicate a task' => 'Duplicar una tarea',
|
||||
'External accounts' => 'Cuentas externas',
|
||||
'Account type' => 'Tipo de Cuenta',
|
||||
'Local' => 'Local',
|
||||
'Remote' => 'Remota',
|
||||
'Enabled' => 'Activada',
|
||||
'Disabled' => 'Deactivada',
|
||||
'Google account linked' => 'Vinculada con Cuenta de Google',
|
||||
'Github account linked' => 'Vinculada con Cuenta de Gitgub',
|
||||
'Username:' => 'Nombre de Usuario:',
|
||||
'Name:' => 'Nombre:',
|
||||
'Email:' => 'Correo electrónico:',
|
||||
'Default project:' => 'Proyecto por defecto:',
|
||||
'Notifications:' => 'Notificaciones:',
|
||||
'Group:' => 'Grupo:',
|
||||
'Regular user' => 'Usuario regular:',
|
||||
'Account type:' => 'Tipo de Cuenta:',
|
||||
'Edit profile' => 'Editar perfil',
|
||||
'Change password' => 'Cambiar contraseña',
|
||||
'Password modification' => 'Modificacion de contraseña',
|
||||
'External authentications' => 'Autenticación externa',
|
||||
'Google Account' => 'Cuenta de Google',
|
||||
'Github Account' => 'Cuenta de Github',
|
||||
'Never connected.' => 'Nunca se ha conectado.',
|
||||
'No account linked.' => 'Sin vínculo con cuenta.',
|
||||
'Account linked.' => 'Vinculada con Cuenta.',
|
||||
'No external authentication enabled.' => 'Sin autenticación externa activa.',
|
||||
'Password modified successfully.' => 'Contraseña cambiada correctamente.',
|
||||
'Unable to change the password.' => 'No pude cambiar la contraseña.',
|
||||
'Change category for the task "%s"' => 'Cambiar la categoría de la tarea "%s"',
|
||||
'Change category' => 'Cambiar categoría',
|
||||
'%s updated the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s actualizó la tarea <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s open the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s abrió la tarea <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the position #%d in the column "%s"' => '%s movió la tarea <a href="?controller=task&action=show&task_id=%d">#%d</a> a la posición #%d de la columna "%s"',
|
||||
'%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the column "%s"' => '%s movió la tarea <a href="?controller=task&action=show&task_id=%d">#%d</a> a la columna "%s"',
|
||||
'%s created the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s creó la tarea <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s closed the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s cerró la tarea <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s created a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s creó una subtarea para la tarea <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s updated a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s actualizó una subtarea para la tarea <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'Assigned to %s with an estimate of %s/%sh' => 'Asignada a %s con una estimación de %s/%sh',
|
||||
'Not assigned, estimate of %sh' => 'No asignada, se estima en %sh',
|
||||
'%s updated a comment on the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s actualizó un comentario de la tarea <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s commented the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s comentó la tarea <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s\'s activity' => 'Actividad de %s',
|
||||
'No activity.' => 'Sin actividad',
|
||||
'RSS feed' => 'Fichero RSS',
|
||||
'%s updated a comment on the task #%d' => '%s actualizó un comentario de la tarea #%d',
|
||||
'%s commented on the task #%d' => '%s comentó la tarea #%d',
|
||||
'%s updated a subtask for the task #%d' => '%s actualizó una subtarea de la tarea #%d',
|
||||
'%s created a subtask for the task #%d' => '%s creó una subtarea de la tarea #%d',
|
||||
'%s updated the task #%d' => '%s actualizó la tarea #%d',
|
||||
'%s created the task #%d' => '%s creó la tarea #%d',
|
||||
'%s closed the task #%d' => '%s cerró la tarea #%d',
|
||||
'%s open the task #%d' => '%s abrió la tarea #%d',
|
||||
'%s moved the task #%d to the column "%s"' => '%s movió la tarea #%d a la columna "%s"',
|
||||
'%s moved the task #%d to the position %d in the column "%s"' => '%s movió la tarea #%d a la posición %d de la columna "%s"',
|
||||
'Activity' => 'Actividad',
|
||||
'Default values are "%s"' => 'Los valores por defecto son "%s"',
|
||||
'Default columns for new projects (Comma-separated)' => 'Columnas por defecto de los nuevos proyectos (Separadas mediante comas)',
|
||||
// 'Task assignee change' => '',
|
||||
// '%s change the assignee of the task #%d' => '',
|
||||
// '%s change the assignee of the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '[%s][Column Change] %s (#%d)' => '',
|
||||
// '[%s][Position Change] %s (#%d)' => '',
|
||||
// '[%s][Assignee Change] %s (#%d)' => '',
|
||||
// 'New password for the user "%s"' => '',
|
||||
);
|
||||
|
|
506
sources/app/Locales/fi_FI/translations.php
Normal file
506
sources/app/Locales/fi_FI/translations.php
Normal file
|
@ -0,0 +1,506 @@
|
|||
<?php
|
||||
|
||||
return array(
|
||||
'None' => 'Ei mikään',
|
||||
'edit' => 'muokkaa',
|
||||
'Edit' => 'Muokkaa',
|
||||
'remove' => 'poista',
|
||||
'Remove' => 'Poista',
|
||||
'Update' => 'Päivitä',
|
||||
'Yes' => 'Kyllä',
|
||||
'No' => 'Ei',
|
||||
'cancel' => 'peruuta',
|
||||
'or' => 'tai',
|
||||
'Yellow' => 'Keltainen',
|
||||
'Blue' => 'Sininen',
|
||||
'Green' => 'Vihreä',
|
||||
'Purple' => 'Violetti',
|
||||
'Red' => 'Punainen',
|
||||
'Orange' => 'Oranssi',
|
||||
'Grey' => 'Harmaa',
|
||||
'Save' => 'Tallenna',
|
||||
'Login' => 'Sisäänkirjautuminen',
|
||||
'Official website:' => 'Virallinen verkkosivu:',
|
||||
'Unassigned' => 'Ei suorittajaa',
|
||||
'View this task' => 'Näytä tämä tehtävä',
|
||||
'Remove user' => 'Poista käyttäjä',
|
||||
'Do you really want to remove this user: "%s"?' => 'Oletko varma että haluat poistaa käyttäjän "%s"?',
|
||||
'New user' => 'Uusi käyttäjä',
|
||||
'All users' => 'Kaikki käyttäjät',
|
||||
'Username' => 'Käyttäjänimi',
|
||||
'Password' => 'Salasana',
|
||||
'Default project' => 'Oletusprojekti',
|
||||
'Administrator' => 'Ylläpitäjä',
|
||||
'Sign in' => 'Kirjaudu sisään',
|
||||
'Users' => 'Käyttäjät',
|
||||
'No user' => 'Ei käyttäjää',
|
||||
'Forbidden' => 'Estetty',
|
||||
'Access Forbidden' => 'Pääsy estetty',
|
||||
'Only administrators can access to this page.' => 'Vain ylläpitäjillä on pääsy tälle sivulle.',
|
||||
'Edit user' => 'Muokkaa käyttäjää',
|
||||
'Logout' => 'Kirjaudu ulos',
|
||||
'Bad username or password' => 'Väärä käyttäjätunnus tai salasana',
|
||||
'users' => 'käyttäjät',
|
||||
'projects' => 'projektit',
|
||||
'Edit project' => 'Muokkaa projektia',
|
||||
'Name' => 'Nimi',
|
||||
'Activated' => 'Aktivoitu',
|
||||
'Projects' => 'Projektit',
|
||||
'No project' => 'Ei projektia',
|
||||
'Project' => 'Projekti',
|
||||
'Status' => 'Status',
|
||||
'Tasks' => 'Tehtävät',
|
||||
'Board' => 'Taulu',
|
||||
'Actions' => 'Toiminnot',
|
||||
'Inactive' => 'Ei aktiivinen',
|
||||
'Active' => 'Aktiivinen',
|
||||
'Column %d' => 'Sarake %d',
|
||||
'Add this column' => 'Lisää tämä sarake',
|
||||
'%d tasks on the board' => '%d tehtävää taululla',
|
||||
'%d tasks in total' => '%d tehtävää yhteensä',
|
||||
'Unable to update this board.' => 'Taulun muuttaminen ei onnistunut.',
|
||||
'Edit board' => 'Muuta taulua',
|
||||
'Disable' => 'Disabloi',
|
||||
'Enable' => 'Aktivoi',
|
||||
'New project' => 'Uusi projekti',
|
||||
'Do you really want to remove this project: "%s"?' => 'Haluatko varmasti poistaa projektin: "%s"?',
|
||||
'Remove project' => 'Poista projekti',
|
||||
'Boards' => 'Taulut',
|
||||
'Edit the board for "%s"' => 'Muokkaa taulua projektille "%s"',
|
||||
'All projects' => 'Kaikki projektit',
|
||||
'Change columns' => 'Muokkaa sarakkeita',
|
||||
'Add a new column' => 'Lisää uusi sarake',
|
||||
'Title' => 'Nimi',
|
||||
'Add Column' => 'Lisää sarake',
|
||||
'Project "%s"' => 'Projekti "%s"',
|
||||
'Nobody assigned' => 'Ei suorittajaa',
|
||||
'Assigned to %s' => 'Tekijä: %s',
|
||||
'Remove a column' => 'Poista sarake',
|
||||
'Remove a column from a board' => 'Poista sarake taulusta',
|
||||
'Unable to remove this column.' => 'Sarakkeen poistaminen ei onnistunut.',
|
||||
'Do you really want to remove this column: "%s"?' => 'Haluatko varmasti poistaa sarakkeen "%s"?',
|
||||
'This action will REMOVE ALL TASKS associated to this column!' => 'Tämä toiminto POISTAA KAIKKI TEHTÄVÄT tästä sarakkeesta!',
|
||||
'Settings' => 'Asetukset',
|
||||
'Application settings' => 'Ohjelman asetukset',
|
||||
'Language' => 'Kieli',
|
||||
'Webhooks token:' => 'Webhooks avain:',
|
||||
// 'API token:' => '',
|
||||
'More information' => 'Lisätietoja',
|
||||
'Database size:' => 'Tietokannan koko:',
|
||||
'Download the database' => 'Lataa tietokanta',
|
||||
'Optimize the database' => 'Optimoi tietokanta',
|
||||
'(VACUUM command)' => '(VACUUM-komento)',
|
||||
'(Gzip compressed Sqlite file)' => '(Gzip-pakattu Sqlite-tiedosto)',
|
||||
'User settings' => 'Käyttäjän asetukset',
|
||||
'My default project:' => 'Oletusprojektini: ',
|
||||
'Close a task' => 'Sulje tehtävä',
|
||||
'Do you really want to close this task: "%s"?' => 'Haluatko varmasti sulkea tehtävän: "%s"?',
|
||||
'Edit a task' => 'Muokkaa tehtävää',
|
||||
'Column' => 'Sarake',
|
||||
'Color' => 'Väri',
|
||||
'Assignee' => 'Suorittaja',
|
||||
'Create another task' => 'Luo toinen tehtävä',
|
||||
'New task' => 'Uusi tehtävä',
|
||||
'Open a task' => 'Avaa tehtävä',
|
||||
'Do you really want to open this task: "%s"?' => 'Haluatko varmasti avata tehtävän: "%s"?',
|
||||
'Back to the board' => 'Takaisin tauluun',
|
||||
'Created on %B %e, %Y at %k:%M %p' => 'Luotu %d.%m.%Y kello %H:%M',
|
||||
'There is nobody assigned' => 'Ei suorittajaa',
|
||||
'Column on the board:' => 'Sarake taululla: ',
|
||||
'Status is open' => 'Status on avoin',
|
||||
'Status is closed' => 'Status on suljettu',
|
||||
'Close this task' => 'Sulje tämä tehtävä',
|
||||
'Open this task' => 'Avaa tämä tehtävä',
|
||||
'There is no description.' => 'Ei kuvausta.',
|
||||
'Add a new task' => 'Lisää uusi tehtävä',
|
||||
'The username is required' => 'Käyttäjätunnut vaaditaan',
|
||||
'The maximum length is %d characters' => 'Maksimipituus on %d merkkiä',
|
||||
'The minimum length is %d characters' => 'Vähimmäispituus on %d merkkiä',
|
||||
'The password is required' => 'Salasana vaaditaan',
|
||||
'This value must be an integer' => 'Tämän arvon täytyy olla numero',
|
||||
'The username must be unique' => 'Käyttäjänimi täytyy olla uniikki',
|
||||
'The username must be alphanumeric' => 'Käyttäjänimen täytyy olla alfanumeerinen',
|
||||
'The user id is required' => 'Käyttäjän id on pakollinen',
|
||||
// 'Passwords don\'t match' => '',
|
||||
'The confirmation is required' => 'Varmistus vaaditaan',
|
||||
'The column is required' => 'Sarake on pakollinen',
|
||||
'The project is required' => 'Projekti on pakollinen',
|
||||
'The color is required' => 'Väri on pakollinen',
|
||||
'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',
|
||||
'The language is required' => 'Kieli on pakollinen',
|
||||
'There is no active project, the first step is to create a new project.' => 'Aktiivista projektia ei ole, ensimmäinen vaihe on luoda uusi projekti.',
|
||||
'Settings saved successfully.' => 'Asetukset tallennettu onnistuneesti.',
|
||||
'Unable to save your settings.' => 'Asetusten tallentaminen epäonnistui.',
|
||||
'Database optimization done.' => 'Tietokannan optimointi suoritettu.',
|
||||
'Your project have been created successfully.' => 'Projekti luotiin onnistuneesti.',
|
||||
'Unable to create your project.' => 'Projektin luominen epäonnistui.',
|
||||
'Project updated successfully.' => 'Projekti päivitettiin onnistuneesti.',
|
||||
'Unable to update this project.' => 'Projektin muuttaminen epäonnistui.',
|
||||
'Unable to remove this project.' => 'Projektin poistaminen epäonnistui.',
|
||||
'Project removed successfully.' => 'Projekti poistettiin onnistuneesti.',
|
||||
'Project activated successfully.' => 'Projekti aktivoitiin onnistuneesti.',
|
||||
'Unable to activate this project.' => 'Projektin aktivoiminen epäonnistui.',
|
||||
'Project disabled successfully.' => 'Projektin disabloiminen onnistui.',
|
||||
'Unable to disable this project.' => 'Projektin disabloiminen epäonnistui.',
|
||||
'Unable to open this task.' => 'Tehtävän avaus epäonnistui.',
|
||||
'Task opened successfully.' => 'Tehtävä avattiin onnistuneesti.',
|
||||
'Unable to close this task.' => 'Tehtävän sulkeminen epäonnistui.',
|
||||
'Task closed successfully.' => 'Tehtävä suljettiin onnistuneesti.',
|
||||
'Unable to update your task.' => 'Tehtävän muokkaaminen epäonnistui.',
|
||||
'Task updated successfully.' => 'Tehtävä päivitettiin onnistuneesti.',
|
||||
'Unable to create your task.' => 'Tehtävän luominen epäonnistui.',
|
||||
'Task created successfully.' => 'Tehtävä luotiin onnistuneesti.',
|
||||
'User created successfully.' => 'Käyttäjä lisättiin onnistuneesti.',
|
||||
'Unable to create your user.' => 'Käyttäjän lisäys epäonnistui.',
|
||||
'User updated successfully.' => 'Käyttäjätietojen päivitys onnistui.',
|
||||
'Unable to update your user.' => 'Käyttäjätietojen päivitys epäonnistui.',
|
||||
'User removed successfully.' => 'Käyttäjä poistettiin onnistuneesti.',
|
||||
'Unable to remove this user.' => 'Käyttäjän poistaminen epäonnistui.',
|
||||
'Board updated successfully.' => 'Taulu päivitettiin onnistuneesti.',
|
||||
'Ready' => 'Valmis',
|
||||
'Backlog' => 'Tehtäväjono',
|
||||
'Work in progress' => 'Työnalla',
|
||||
'Done' => 'Tehty',
|
||||
'Application version:' => 'Ohjelman versio:',
|
||||
'Completed on %B %e, %Y at %k:%M %p' => 'Valmistunut %d.%m.%Y kello %H:%M',
|
||||
'%B %e, %Y at %k:%M %p' => '%d.%m.%Y kello %H:%M',
|
||||
'Date created' => 'Luomispäivä',
|
||||
'Date completed' => 'Valmistumispäivä',
|
||||
'Id' => 'Id',
|
||||
'No task' => 'Ei tehtävää',
|
||||
'Completed tasks' => 'Valmiit tehtävät',
|
||||
'List of projects' => 'Projektit',
|
||||
'Completed tasks for "%s"' => 'Suoritetut tehtävät projektille %s',
|
||||
'%d closed tasks' => '%d suljettua tehtävää',
|
||||
'No task for this project' => 'Ei tehtävää tälle projektille',
|
||||
'Public link' => 'Julkinen linkki',
|
||||
'There is no column in your project!' => 'Projektilta puuttuu sarakkeet!',
|
||||
'Change assignee' => 'Vaihda suorittajaa',
|
||||
'Change assignee for the task "%s"' => 'Vaihda suorittajaa tehtävälle %s',
|
||||
'Timezone' => 'Aikavyöhyke',
|
||||
'Sorry, I didn\'t found this information in my database!' => 'Anteeksi, en löytänyt tätä tietoa tietokannastani',
|
||||
'Page not found' => 'Sivua ei löydy',
|
||||
'Complexity' => 'Monimutkaisuus',
|
||||
'limit' => 'raja',
|
||||
'Task limit' => 'Tehtävien maksimimäärä',
|
||||
'This value must be greater than %d' => 'Arvon täytyy olla suurempi kuin %d',
|
||||
'Edit project access list' => 'Muuta projektin käyttäjiä',
|
||||
'Edit users access' => 'Muuta käyttäjien pääsyä',
|
||||
'Allow this user' => 'Salli tämä projekti',
|
||||
'Only those users have access to this project:' => 'Vain näillä käyttäjillä on pääsy projektiin:',
|
||||
'Don\'t forget that administrators have access to everything.' => 'Muista että ylläpitäjät pääsevät kaikkialle.',
|
||||
'revoke' => 'poista',
|
||||
'List of authorized users' => 'Sallittujen käyttäjien lista',
|
||||
'User' => 'Käyttäjät',
|
||||
'Everybody have access to this project.' => 'Kaikilla on pääsy tähän projektiin.',
|
||||
'You are not allowed to access to this project.' => 'Sinulla ei ole pääsyä tähän projektiin.',
|
||||
'Comments' => 'Kommentit',
|
||||
'Post comment' => 'Lisää kommentti',
|
||||
'Write your text in Markdown' => 'Kirjoita kommenttisi Markdownilla',
|
||||
'Leave a comment' => 'Lisää kommentti',
|
||||
'Comment is required' => 'Kommentti vaaditaan',
|
||||
'Leave a description' => 'Lisää kuvaus',
|
||||
'Comment added successfully.' => 'Kommentti lisättiin onnistuneesti.',
|
||||
'Unable to create your comment.' => 'Kommentin lisäys epäonnistui.',
|
||||
'The description is required' => 'Kuvaus vaaditaan',
|
||||
'Edit this task' => 'Muokkaa tehtävää',
|
||||
'Due Date' => 'Deadline',
|
||||
'm/d/Y' => 'd.m.Y',
|
||||
'month/day/year' => 'päivä.kuukausi.vuosi',
|
||||
'Invalid date' => 'Virheellinen päiväys',
|
||||
'Must be done before %B %e, %Y' => 'Täytyy suorittaa ennen %d.%m.%Y',
|
||||
'%B %e, %Y' => '%d.%m.%Y',
|
||||
'Automatic actions' => 'Automaattiset toiminnot',
|
||||
'Your automatic action have been created successfully.' => 'Toiminto suoritettiin onnistuneesti.',
|
||||
'Unable to create your automatic action.' => 'Automaattisen toiminnon luominen epäonnistui.',
|
||||
'Remove an action' => 'Poista toiminto',
|
||||
'Unable to remove this action.' => 'Toiminnon poistaminen epäonnistui.',
|
||||
'Action removed successfully.' => 'Toiminto poistettiin onnistuneesti.',
|
||||
'Automatic actions for the project "%s"' => 'Automaattiset toiminnot projektille "%s"',
|
||||
'Defined actions' => 'Määritellyt toiminnot',
|
||||
// 'Add an action' => '',
|
||||
'Event name' => 'Tapahtuman nimi',
|
||||
'Action name' => 'Toiminnon nimi',
|
||||
'Action parameters' => 'Toiminnon parametrit',
|
||||
'Action' => 'Toiminto',
|
||||
'Event' => 'Tapahtuma',
|
||||
'When the selected event occurs execute the corresponding action.' => 'Kun valittu tapahtuma tapahtuu, suorita vastaava toiminto.',
|
||||
'Next step' => 'Seuraava vaihe',
|
||||
'Define action parameters' => 'Määrittele toiminnon parametrit',
|
||||
'Save this action' => 'Tallenna toiminto',
|
||||
'Do you really want to remove this action: "%s"?' => 'Oletko varma että haluat poistaa toiminnon "%s"?',
|
||||
'Remove an automatic action' => 'Poista automaattintn toiminto',
|
||||
'Close the task' => 'Sulje tehtävä',
|
||||
'Assign the task to a specific user' => 'Osoita tehtävä käyttäjälle',
|
||||
'Assign the task to the person who does the action' => 'Määritä suorittaja tehtävälle',
|
||||
'Duplicate the task to another project' => 'Monista tehtävä toiselle projektille',
|
||||
'Move a task to another column' => 'Siirrä tehtävä toiseen sarakkeeseen',
|
||||
'Move a task to another position in the same column' => 'Siirrä tehtävä eri järjestykseen samassa sarakkeessa',
|
||||
'Task modification' => 'Tehtävän muokkaus',
|
||||
'Task creation' => 'Tehtävän luominen',
|
||||
'Open a closed task' => 'Avaa jo suljettu tehtävä',
|
||||
'Closing a task' => 'Tehtävää suljetaan',
|
||||
'Assign a color to a specific user' => 'Valitse väri käyttäjälle',
|
||||
'Column title' => 'Sarakkeen nimi',
|
||||
'Position' => 'Positio',
|
||||
'Move Up' => 'Siirrä ylös',
|
||||
'Move Down' => 'Siirrä alas',
|
||||
'Duplicate to another project' => 'Kopioi toiseen projektiin',
|
||||
'Duplicate' => 'Monista',
|
||||
'link' => 'linkki',
|
||||
'Update this comment' => 'Muuta projektia',
|
||||
'Comment updated successfully.' => 'Kommentti päivitettiin onnistuneesti.',
|
||||
'Unable to update your comment.' => 'Kommentin päivitys epäonnistui.',
|
||||
'Remove a comment' => 'Poista kommentti',
|
||||
'Comment removed successfully.' => 'Kommentti poistettiin onnistuneesti.',
|
||||
'Unable to remove this comment.' => 'Kommentin poistaminen epäonnistui.',
|
||||
'Do you really want to remove this comment?' => 'Haluatko varmasti poistaa tämän kommentin?',
|
||||
'Only administrators or the creator of the comment can access to this page.' => 'Vain ylläpitäjillä tai kommentin jättäjällä on pääsy tälle sivulle.',
|
||||
'Details' => 'Tiedot',
|
||||
'Current password for the user "%s"' => 'Käyttäjän "%s" salasana',
|
||||
'The current password is required' => 'Salasana vaaditaan',
|
||||
'Wrong password' => 'Väärä salasana',
|
||||
'Reset all tokens' => 'Resetoi kaikki tokenit',
|
||||
'All tokens have been regenerated.' => 'Kaikki tokenit luotiin uudelleen.',
|
||||
'Unknown' => 'Tuntematon',
|
||||
'Last logins' => 'Viimeisimmät kirjautumiset',
|
||||
'Login date' => 'Kirjautumispäivä',
|
||||
'Authentication method' => 'Autentikointimenetelmä',
|
||||
'IP address' => 'IP-Osoite',
|
||||
'User agent' => 'Selain',
|
||||
'Persistent connections' => 'Voimassa olevat yhteydet',
|
||||
'No session.' => 'Ei sessioita.',
|
||||
'Expiration date' => 'Vanhentumispäivä',
|
||||
'Remember Me' => 'Muista minut',
|
||||
'Creation date' => 'Luomispäivä',
|
||||
'Filter by user' => 'Rajaa käyttäjän mukaan',
|
||||
'Filter by due date' => 'Rajaa deadlinen mukaan',
|
||||
'Everybody' => 'Kaikki',
|
||||
'Open' => 'Avoin',
|
||||
'Closed' => 'Suljettu',
|
||||
'Search' => 'Etsi',
|
||||
'Nothing found.' => 'Ei löytynyt.',
|
||||
'Search in the project "%s"' => 'Etsi projektista "%s"',
|
||||
'Due date' => 'Deadline',
|
||||
'Others formats accepted: %s and %s' => 'Muut hyväksytyt muodot: %s ja %s',
|
||||
'Description' => 'Kuvaus',
|
||||
'%d comments' => '%d kommenttia',
|
||||
'%d comment' => '%d kommentti',
|
||||
'Email address invalid' => 'Email ei kelpaa',
|
||||
'Your Google Account is not linked anymore to your profile.' => 'Google tunnustasi ei ole enää linkattu profiiliisi',
|
||||
'Unable to unlink your Google Account.' => 'Google tunnuksen linkkaamisen poistaminen epäonnistui.',
|
||||
'Google authentication failed' => 'Google autentikointi epäonnistui',
|
||||
'Unable to link your Google Account.' => 'Google tunnuksen linkkaaminen epäonnistui.',
|
||||
'Your Google Account is linked to your profile successfully.' => 'Google tunnuksesi linkitettiin profiiliisi onnistuneesti.',
|
||||
'Email' => 'Sähköposti',
|
||||
'Link my Google Account' => 'Linkitä Google-tili',
|
||||
'Unlink my Google Account' => 'Poista Google-tilin linkitys',
|
||||
'Login with my Google Account' => 'Kirjaudu Google tunnuksella',
|
||||
'Project not found.' => 'Projektia ei löytynyt.',
|
||||
'Task #%d' => 'Tehtävä #%d',
|
||||
'Task removed successfully.' => 'Tehtävä poistettiin onnistuneesti.',
|
||||
'Unable to remove this task.' => 'Tehtävän poistaminen epäonnistui.',
|
||||
'Remove a task' => 'Poista tehtävä',
|
||||
'Do you really want to remove this task: "%s"?' => 'Haluatko varmasti poistaa tehtävän: "%s"?',
|
||||
'Assign automatically a color based on a category' => 'Aseta väri automaattisesti kategorian mukaan',
|
||||
'Assign automatically a category based on a color' => 'Aseta kategoria automaattisesti värin mukaan',
|
||||
'Task creation or modification' => 'Tehtävän luonti tai muuttaminen',
|
||||
'Category' => 'Kategoria',
|
||||
'Category:' => 'Kategoria:',
|
||||
'Categories' => 'Kategoriat',
|
||||
'Category not found.' => 'Kategoriaa ei löytynyt.',
|
||||
'Your category have been created successfully.' => 'Kategoria luotiin onnistuneesti.',
|
||||
'Unable to create your category.' => 'Kategorian luonti epäonnistui.',
|
||||
'Your category have been updated successfully.' => 'Kategoriaa muokattiin onnistuneesti.',
|
||||
'Unable to update your category.' => 'Kategorian muokkaaminen epäonnistui.',
|
||||
'Remove a category' => 'Poista kategoria',
|
||||
'Category removed successfully.' => 'Kategoria poistettu onnistuneesti.',
|
||||
'Unable to remove this category.' => 'Kategorian poisto epäonnistui.',
|
||||
'Category modification for the project "%s"' => 'Kategorian muutos projektissa "%s"',
|
||||
'Category Name' => 'Kategorian nimi',
|
||||
'Categories for the project "%s"' => 'Kategoriat projektille "%s"',
|
||||
'Add a new category' => 'Lisää uusi kategoria',
|
||||
'Do you really want to remove this category: "%s"?' => 'Haluatko varmasti poistaa kategorian: "%s"?',
|
||||
'Filter by category' => 'Rajaa kategorian mukaan',
|
||||
'All categories' => 'Kaikki kategoriat',
|
||||
'No category' => 'Kategoriaa ei löydy',
|
||||
'The name is required' => 'Nimi vaaditaan',
|
||||
'Remove a file' => 'Poista tiedosto',
|
||||
'Unable to remove this file.' => 'Tiedoston poistaminen epäonnistui.',
|
||||
'File removed successfully.' => 'Tiedosto poistettiin onnistuneesti.',
|
||||
'Attach a document' => 'Liitä dokumentti',
|
||||
'Do you really want to remove this file: "%s"?' => 'Haluatko varmasti poistaa tiedoston: "%s"?',
|
||||
'open' => 'avaa',
|
||||
'Attachments' => 'Liitteet',
|
||||
'Edit the task' => 'Muokkaa tehtävää',
|
||||
'Edit the description' => 'Muokkaa kuvausta',
|
||||
'Add a comment' => 'Lisää kommentti',
|
||||
'Edit a comment' => 'Muokkaa kommenttia',
|
||||
'Summary' => 'Yhteenveto',
|
||||
'Time tracking' => 'Ajan seuranta',
|
||||
'Estimate:' => 'Arvio:',
|
||||
'Spent:' => 'Käytetty:',
|
||||
'Do you really want to remove this sub-task?' => 'Haluatko varmasti poistaa tämän alitehtävän?',
|
||||
'Remaining:' => 'Jäljellä',
|
||||
'hours' => 'tuntia',
|
||||
'spent' => 'käytetty',
|
||||
'estimated' => 'estimoitu',
|
||||
'Sub-Tasks' => 'Alitehtävät',
|
||||
'Add a sub-task' => 'Lisää alitehtävä',
|
||||
'Original Estimate' => 'Alkuperäinen estimaatti',
|
||||
'Create another sub-task' => 'Lisää toinen alitehtävä',
|
||||
'Time Spent' => 'Käytetty aika',
|
||||
'Edit a sub-task' => 'Muokkaa alitehtävää',
|
||||
'Remove a sub-task' => 'Poista alitehtävä',
|
||||
'The time must be a numeric value' => 'Ajan pitää olla numero',
|
||||
'Todo' => 'Todo',
|
||||
'In progress' => 'Työnalla',
|
||||
'Sub-task removed successfully.' => 'Alitehtävä poistettu onnistuneesti.',
|
||||
'Unable to remove this sub-task.' => 'Alitehtävän poistaminen epäonnistui.',
|
||||
'Sub-task updated successfully.' => 'Alitehtävä päivitettiin onnistuneesti.',
|
||||
'Unable to update your sub-task.' => 'Alitehtävän päivitys epäonnistui.',
|
||||
'Unable to create your sub-task.' => 'Alitehtävän luonti epäonnistui.',
|
||||
'Sub-task added successfully.' => 'Alitehtävä luotiin onnistuneesti.',
|
||||
'Maximum size: ' => 'Maksimikoko: ',
|
||||
'Unable to upload the file.' => 'Tiedoston lataus epäonnistui.',
|
||||
'Display another project' => 'Näytä toinen projekti',
|
||||
// 'Your GitHub account was successfully linked to your profile.' => '',
|
||||
// 'Unable to link your GitHub Account.' => '',
|
||||
// 'GitHub authentication failed' => '',
|
||||
// 'Your GitHub account is no longer linked to your profile.' => '',
|
||||
// 'Unable to unlink your GitHub Account.' => '',
|
||||
// 'Login with my GitHub Account' => '',
|
||||
// 'Link my GitHub Account' => '',
|
||||
// 'Unlink my GitHub Account' => '',
|
||||
'Created by %s' => 'Luonut: %s',
|
||||
'Last modified on %B %e, %Y at %k:%M %p' => 'Viimeksi muokattu %B %e, %Y kello %H:%M',
|
||||
'Tasks Export' => 'Tehtävien vienti',
|
||||
'Tasks exportation for "%s"' => 'Tehtävien vienti projektilta "%s"',
|
||||
'Start Date' => 'Aloituspäivä',
|
||||
'End Date' => 'Lopetuspäivä',
|
||||
'Execute' => 'Suorita',
|
||||
'Task Id' => 'Tehtävän ID',
|
||||
'Creator' => 'Luonut',
|
||||
'Modification date' => 'Muokkauspäivä',
|
||||
'Completion date' => 'Valmistumispäivä',
|
||||
'Webhook URL for task creation' => 'Webhook URL tehtävän luomiselle',
|
||||
'Webhook URL for task modification' => 'Webhook URL tehtävän muokkaamiselle',
|
||||
// 'Clone' => '',
|
||||
// 'Clone Project' => '',
|
||||
// 'Project cloned successfully.' => '',
|
||||
// 'Unable to clone this project.' => '',
|
||||
// 'Email notifications' => '',
|
||||
// 'Enable email notifications' => '',
|
||||
// 'Task position:' => '',
|
||||
// 'The task #%d have been opened.' => '',
|
||||
// 'The task #%d have been closed.' => '',
|
||||
// 'Sub-task updated' => '',
|
||||
// 'Title:' => '',
|
||||
// 'Status:' => '',
|
||||
// 'Assignee:' => '',
|
||||
// 'Time tracking:' => '',
|
||||
// 'New sub-task' => '',
|
||||
// 'New attachment added "%s"' => '',
|
||||
// 'Comment updated' => '',
|
||||
// 'New comment posted by %s' => '',
|
||||
// 'List of due tasks for the project "%s"' => '',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
// '[%s][New subtask] %s (#%d)' => '',
|
||||
// '[%s][Subtask updated] %s (#%d)' => '',
|
||||
// '[%s][New task] %s (#%d)' => '',
|
||||
// '[%s][Task updated] %s (#%d)' => '',
|
||||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
// '[Kanboard] Notification' => '',
|
||||
// 'I want to receive notifications only for those projects:' => '',
|
||||
// 'view the task on Kanboard' => '',
|
||||
// 'Public access' => '',
|
||||
// 'Categories management' => '',
|
||||
// 'Users management' => '',
|
||||
// 'Active tasks' => '',
|
||||
// 'Disable public access' => '',
|
||||
// 'Enable public access' => '',
|
||||
// 'Active projects' => '',
|
||||
// 'Inactive projects' => '',
|
||||
// 'Public access disabled' => '',
|
||||
// 'Do you really want to disable this project: "%s"?' => '',
|
||||
// 'Do you really want to duplicate this project: "%s"?' => '',
|
||||
// 'Do you really want to enable this project: "%s"?' => '',
|
||||
// 'Project activation' => '',
|
||||
// 'Move the task to another project' => '',
|
||||
// 'Move to another project' => '',
|
||||
// 'Do you really want to duplicate this task?' => '',
|
||||
// 'Duplicate a task' => '',
|
||||
// 'External accounts' => '',
|
||||
// 'Account type' => '',
|
||||
// 'Local' => '',
|
||||
// 'Remote' => '',
|
||||
// 'Enabled' => '',
|
||||
// 'Disabled' => '',
|
||||
// 'Google account linked' => '',
|
||||
// 'Github account linked' => '',
|
||||
// 'Username:' => '',
|
||||
// 'Name:' => '',
|
||||
// 'Email:' => '',
|
||||
// 'Default project:' => '',
|
||||
// 'Notifications:' => '',
|
||||
// 'Group:' => '',
|
||||
// 'Regular user' => '',
|
||||
// 'Account type:' => '',
|
||||
// 'Edit profile' => '',
|
||||
// 'Change password' => '',
|
||||
// 'Password modification' => '',
|
||||
// 'External authentications' => '',
|
||||
// 'Google Account' => '',
|
||||
// 'Github Account' => '',
|
||||
// 'Never connected.' => '',
|
||||
// 'No account linked.' => '',
|
||||
// 'Account linked.' => '',
|
||||
// 'No external authentication enabled.' => '',
|
||||
// 'Password modified successfully.' => '',
|
||||
// 'Unable to change the password.' => '',
|
||||
// 'Change category for the task "%s"' => '',
|
||||
// 'Change category' => '',
|
||||
// '%s updated the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s open the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the position #%d in the column "%s"' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the column "%s"' => '',
|
||||
// '%s created the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s closed the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s created a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s updated a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// 'Assigned to %s with an estimate of %s/%sh' => '',
|
||||
// 'Not assigned, estimate of %sh' => '',
|
||||
// '%s updated a comment on the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s commented the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s\'s activity' => '',
|
||||
// 'No activity.' => '',
|
||||
// 'RSS feed' => '',
|
||||
// '%s updated a comment on the task #%d' => '',
|
||||
// '%s commented on the task #%d' => '',
|
||||
// '%s updated a subtask for the task #%d' => '',
|
||||
// '%s created a subtask for the task #%d' => '',
|
||||
// '%s updated the task #%d' => '',
|
||||
// '%s created the task #%d' => '',
|
||||
// '%s closed the task #%d' => '',
|
||||
// '%s open the task #%d' => '',
|
||||
// '%s moved the task #%d to the column "%s"' => '',
|
||||
// '%s moved the task #%d to the position %d in the column "%s"' => '',
|
||||
// 'Activity' => '',
|
||||
// 'Default values are "%s"' => '',
|
||||
// 'Default columns for new projects (Comma-separated)' => '',
|
||||
// 'Task assignee change' => '',
|
||||
// '%s change the assignee of the task #%d' => '',
|
||||
// '%s change the assignee of the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '[%s][Column Change] %s (#%d)' => '',
|
||||
// '[%s][Position Change] %s (#%d)' => '',
|
||||
// '[%s][Assignee Change] %s (#%d)' => '',
|
||||
// 'New password for the user "%s"' => '',
|
||||
);
|
|
@ -1,14 +1,6 @@
|
|||
<?php
|
||||
|
||||
return array(
|
||||
'English' => 'Anglais',
|
||||
'French' => 'Français',
|
||||
'Polish' => 'Polonais',
|
||||
'Portuguese (Brazilian)' => 'Portugais (Brésil)',
|
||||
'Spanish' => 'Espagnol',
|
||||
'German' => 'Allemand',
|
||||
'Chinese (Simplified)' => 'Chinois simplifié',
|
||||
'Swedish' => 'Suèdois',
|
||||
'None' => 'Aucun',
|
||||
'edit' => 'modifier',
|
||||
'Edit' => 'Modifier',
|
||||
|
@ -35,9 +27,9 @@ return array(
|
|||
'Do you really want to remove this user: "%s"?' => 'Voulez-vous vraiment supprimer cet utilisateur : « %s » ?',
|
||||
'New user' => 'Ajouter un utilisateur',
|
||||
'All users' => 'Tous les utilisateurs',
|
||||
'Username' => 'Identifiant',
|
||||
'Username' => 'Nom d\'utilisateur',
|
||||
'Password' => 'Mot de passe',
|
||||
'Default Project' => 'Projet par défaut',
|
||||
'Default project' => 'Projet par défaut',
|
||||
'Administrator' => 'Administrateur',
|
||||
'Sign in' => 'Connexion',
|
||||
'Users' => 'Utilisateurs',
|
||||
|
@ -59,6 +51,7 @@ return array(
|
|||
'Status' => 'État',
|
||||
'Tasks' => 'Tâches',
|
||||
'Board' => 'Tableau',
|
||||
'Actions' => 'Actions',
|
||||
'Inactive' => 'Inactif',
|
||||
'Active' => 'Actif',
|
||||
'Column %d' => 'Colonne %d',
|
||||
|
@ -91,6 +84,7 @@ return array(
|
|||
'Application settings' => 'Paramètres de l\'application',
|
||||
'Language' => 'Langue',
|
||||
'Webhooks token:' => 'Jeton de securité pour les webhooks :',
|
||||
'API token:' => 'Jeton de securité pour l\'API :',
|
||||
'More information' => 'Plus d\'informations',
|
||||
'Database size:' => 'Taille de la base de données :',
|
||||
'Download the database' => 'Télécharger la base de données',
|
||||
|
@ -110,7 +104,7 @@ return array(
|
|||
'Open a task' => 'Ouvrir une tâche',
|
||||
'Do you really want to open this task: "%s"?' => 'Voulez-vous vraiment ouvrir cette tâche : « %s » ?',
|
||||
'Back to the board' => 'Retour au tableau',
|
||||
'Created on %B %e, %G at %k:%M %p' => 'Créé le %d/%m/%Y à %H:%M',
|
||||
'Created on %B %e, %Y at %k:%M %p' => 'Créé le %d/%m/%Y à %H:%M',
|
||||
'There is nobody assigned' => 'Il n\'y a personne d\'assigné à cette tâche',
|
||||
'Column on the board:' => 'Colonne sur le tableau : ',
|
||||
'Status is open' => 'État ouvert',
|
||||
|
@ -172,8 +166,8 @@ return array(
|
|||
'Work in progress' => 'En cours',
|
||||
'Done' => 'Terminé',
|
||||
'Application version:' => 'Version de l\'application :',
|
||||
'Completed on %B %e, %G at %k:%M %p' => 'Terminé le %d/%m/%Y à %H:%M',
|
||||
'%B %e, %G at %k:%M %p' => '%d/%m/%Y à %H:%M',
|
||||
'Completed on %B %e, %Y at %k:%M %p' => 'Terminé le %d/%m/%Y à %H:%M',
|
||||
'%B %e, %Y at %k:%M %p' => '%d/%m/%Y à %H:%M',
|
||||
'Date created' => 'Date de création',
|
||||
'Date completed' => 'Date de clôture',
|
||||
'Id' => 'Identifiant',
|
||||
|
@ -182,22 +176,21 @@ return array(
|
|||
'List of projects' => 'Liste des projets',
|
||||
'Completed tasks for "%s"' => 'Tâches terminées pour « %s »',
|
||||
'%d closed tasks' => '%d tâches terminées',
|
||||
'no task for this project' => 'aucune tâche pour ce projet',
|
||||
'Public link' => 'Accès public',
|
||||
'No task for this project' => 'Aucune tâche pour ce projet',
|
||||
'Public link' => 'Lien public',
|
||||
'There is no column in your project!' => 'Il n\'y a aucune colonne dans votre projet !',
|
||||
'Change assignee' => 'Changer la personne assignée',
|
||||
'Change assignee for the task "%s"' => 'Changer la personne assignée pour la tâche « %s »',
|
||||
'Timezone' => 'Fuseau horaire',
|
||||
'Sorry, I didn\'t found this information in my database!' => 'Désolé, je n\'ai pas trouvé cette information dans ma base de données !',
|
||||
'Page not found' => 'Page introuvable',
|
||||
'Story Points' => 'Complexité',
|
||||
'Complexity' => 'Complexité',
|
||||
'limit' => 'limite',
|
||||
'Task limit' => 'Nombre maximum de tâches',
|
||||
'This value must be greater than %d' => 'Cette valeur doit être plus grande que %d',
|
||||
'Edit project access list' => 'Modifier l\'accès au projet',
|
||||
'Edit users access' => 'Modifier les utilisateurs autorisés',
|
||||
'Allow this user' => 'Autoriser cet utilisateur',
|
||||
'Project access list for "%s"' => 'Liste des accès au projet « %s »',
|
||||
'Only those users have access to this project:' => 'Seulement ces utilisateurs ont accès à ce projet :',
|
||||
'Don\'t forget that administrators have access to everything.' => 'N\'oubliez pas que les administrateurs ont accès à tout.',
|
||||
'revoke' => 'révoquer',
|
||||
|
@ -219,8 +212,8 @@ return array(
|
|||
'm/d/Y' => 'd/m/Y', // Date format parsed with php
|
||||
'month/day/year' => 'jour/mois/année', // Help shown to the user
|
||||
'Invalid date' => 'Date invalide',
|
||||
'Must be done before %B %e, %G' => 'Doit être fait avant le %d/%m/%Y',
|
||||
'%B %e, %G' => '%d/%m/%Y',
|
||||
'Must be done before %B %e, %Y' => 'Doit être fait avant le %d/%m/%Y',
|
||||
'%B %e, %Y' => '%d/%m/%Y',
|
||||
'Automatic actions' => 'Actions automatisées',
|
||||
'Your automatic action have been created successfully.' => 'Votre action automatisée a été ajouté avec succès.',
|
||||
'Unable to create your automatic action.' => 'Impossible de créer votre action automatisée.',
|
||||
|
@ -229,6 +222,7 @@ return array(
|
|||
'Action removed successfully.' => 'Action supprimée avec succès.',
|
||||
'Automatic actions for the project "%s"' => 'Actions automatisées pour le projet « %s »',
|
||||
'Defined actions' => 'Actions définies',
|
||||
'Add an action' => 'Ajouter une action',
|
||||
'Event name' => 'Nom de l\'événement',
|
||||
'Action name' => 'Nom de l\'action',
|
||||
'Action parameters' => 'Paramètres de l\'action',
|
||||
|
@ -279,7 +273,7 @@ return array(
|
|||
'IP address' => 'Adresse IP',
|
||||
'User agent' => 'Agent utilisateur',
|
||||
'Persistent connections' => 'Connexions persistantes',
|
||||
'No session' => 'Aucune session',
|
||||
'No session.' => 'Aucune session.',
|
||||
'Expiration date' => 'Date d\'expiration',
|
||||
'Remember Me' => 'Connexion automatique',
|
||||
'Creation date' => 'Date de création',
|
||||
|
@ -383,5 +377,130 @@ return array(
|
|||
'Link my GitHub Account' => 'Lier mon compte Github',
|
||||
'Unlink my GitHub Account' => 'Ne plus utiliser mon compte Github',
|
||||
'Created by %s' => 'Créé par %s',
|
||||
'Last modified on %B %e, %G at %k:%M %p' => 'Modifié le %d/%m/%Y à %H:%M',
|
||||
'Last modified on %B %e, %Y at %k:%M %p' => 'Modifié le %d/%m/%Y à %H:%M',
|
||||
'Tasks Export' => 'Exportation des tâches',
|
||||
'Tasks exportation for "%s"' => 'Exportation des tâches pour « %s »',
|
||||
'Start Date' => 'Date de début',
|
||||
'End Date' => 'Date de fin',
|
||||
'Execute' => 'Exécuter',
|
||||
'Task Id' => 'Identifiant de la tâche',
|
||||
'Creator' => 'Créateur',
|
||||
'Modification date' => 'Date de modification',
|
||||
'Completion date' => 'Date de complétion',
|
||||
'Webhook URL for task creation' => 'URL du webhook pour la création de tâche',
|
||||
'Webhook URL for task modification' => 'URL du webhook pour la modification de tâche',
|
||||
'Clone' => 'Clone',
|
||||
'Clone Project' => 'Cloner le projet',
|
||||
'Project cloned successfully.' => 'Projet cloné avec succès.',
|
||||
'Unable to clone this project.' => 'Impossible de cloner ce projet.',
|
||||
'Email notifications' => 'Notifications par email',
|
||||
'Enable email notifications' => 'Activer les notifications par emails',
|
||||
'Task position:' => 'Position de la tâche :',
|
||||
'The task #%d have been opened.' => 'La tâche #%d a été ouverte.',
|
||||
'The task #%d have been closed.' => 'La tâche #%d a été fermée.',
|
||||
'Sub-task updated' => 'Sous-tâche mise à jour',
|
||||
'Title:' => 'Titre :',
|
||||
'Status:' => 'État :',
|
||||
'Assignee:' => 'Assigné :',
|
||||
'Time tracking:' => 'Gestion du temps :',
|
||||
'New sub-task' => 'Nouvelle sous-tâche',
|
||||
'New attachment added "%s"' => 'Nouvelle pièce-jointe ajoutée « %s »',
|
||||
'Comment updated' => 'Commentaire ajouté',
|
||||
'New comment posted by %s' => 'Nouveau commentaire ajouté par « %s »',
|
||||
'List of due tasks for the project "%s"' => 'Liste des tâches expirées pour le projet « %s »',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][Pièce-jointe] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][Nouveau commentaire] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][Commentaire mis à jour] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][Nouvelle sous-tâche] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][Sous-tâche mise à jour] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][Nouvelle tâche] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][Tâche mise à jour] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][Tâche fermée] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][Tâche ouverte] %s (#%d)',
|
||||
'[%s][Due tasks]' => '[%s][Tâches expirées]',
|
||||
'[Kanboard] Notification' => '[Kanboard] Notification',
|
||||
'I want to receive notifications only for those projects:' => 'Je souhaite reçevoir les notifications uniquement pour les projets sélectionnés :',
|
||||
'view the task on Kanboard' => 'voir la tâche sur Kanboard',
|
||||
'Public access' => 'Accès public',
|
||||
'Categories management' => 'Gestion des catégories',
|
||||
'Users management' => 'Gestion des utilisateurs',
|
||||
'Active tasks' => 'Tâches actives',
|
||||
'Disable public access' => 'Désactiver l\'accès public',
|
||||
'Enable public access' => 'Activer l\'accès public',
|
||||
'Active projects' => 'Projets activés',
|
||||
'Inactive projects' => 'Projets désactivés',
|
||||
'Public access disabled' => 'Accès public désactivé',
|
||||
'Do you really want to disable this project: "%s"?' => 'Voulez-vous vraiment désactiver ce projet : « %s » ?',
|
||||
'Do you really want to duplicate this project: "%s"?' => 'Voulez-vous vraiment dupliquer ce projet : « %s » ?',
|
||||
'Do you really want to enable this project: "%s"?' => 'Voulez-vous vraiment activer ce projet : « %s » ?',
|
||||
'Project activation' => 'Activation du projet',
|
||||
'Move the task to another project' => 'Déplacer la tâche vers un autre projet',
|
||||
'Move to another project' => 'Déplacer vers un autre projet',
|
||||
'Do you really want to duplicate this task?' => 'Voulez-vous vraiment dupliquer cette tâche ?',
|
||||
'Duplicate a task' => 'Dupliquer une tâche',
|
||||
'External accounts' => 'Comptes externes',
|
||||
'Account type' => 'Type de compte',
|
||||
'Local' => 'Local',
|
||||
'Remote' => 'Distant',
|
||||
'Enabled' => 'Activé',
|
||||
'Disabled' => 'Désactivé',
|
||||
'Google account linked' => 'Compte Google attaché',
|
||||
'Github account linked' => 'Compte Github attaché',
|
||||
'Username:' => 'Nom d\'utilisateur :',
|
||||
'Name:' => 'Nom :',
|
||||
'Email:' => 'Email :',
|
||||
'Default project:' => 'Projet par défaut :',
|
||||
'Notifications:' => 'Notifications :',
|
||||
'Group:' => 'Groupe :',
|
||||
'Regular user' => 'Utilisateur normal',
|
||||
'Account type:' => 'Type de compte :',
|
||||
'Edit profile' => 'Modifier le profile',
|
||||
'Change password' => 'Changer le mot de passe',
|
||||
'Password modification' => 'Changement de mot de passe',
|
||||
'External authentications' => 'Authentifications externe',
|
||||
'Google Account' => 'Compte Google',
|
||||
'Github Account' => 'Compte Github',
|
||||
'Never connected.' => 'Jamais connecté.',
|
||||
'No account linked.' => 'Aucun compte attaché.',
|
||||
'Account linked.' => 'Compte attaché.',
|
||||
'No external authentication enabled.' => 'Aucune authentication externe activée.',
|
||||
'Password modified successfully.' => 'Mot de passe changé avec succès.',
|
||||
'Unable to change the password.' => 'Impossible de changer le mot de passe.',
|
||||
'Change category for the task "%s"' => 'Changer la catégorie pour la tâche « %s »',
|
||||
'Change category' => 'Changer de catégorie',
|
||||
'%s updated the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s a mis à jour la tâche <a href="?controller=task&action=show&task_id=%d">n°%d</a>',
|
||||
'%s open the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s a ouvert la tâche <a href="?controller=task&action=show&task_id=%d">n°%d</a>',
|
||||
'%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the position #%d in the column "%s"' => '%s a déplacé la tâche <a href="?controller=task&action=show&task_id=%d">n°%d</a> à la position n°%d dans la colonne « %s »',
|
||||
'%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the column "%s"' => '%s a déplacé la tâche <a href="?controller=task&action=show&task_id=%d">n°%d</a> dans la colonne « %s »',
|
||||
'%s created the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s a créé la tâche <a href="?controller=task&action=show&task_id=%d">n°%d</a>',
|
||||
'%s closed the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s a fermé la tâche <a href="?controller=task&action=show&task_id=%d">n°%d</a>',
|
||||
'%s created a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s a créé une sous-tâche pour la tâche <a href="?controller=task&action=show&task_id=%d">n°%d</a>',
|
||||
'%s updated a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s a mis à jour une sous-tâche appartenant à la tâche <a href="?controller=task&action=show&task_id=%d">n°%d</a>',
|
||||
'Assigned to %s with an estimate of %s/%sh' => 'Assigné à %s avec un estimé de %s/%sh',
|
||||
'Not assigned, estimate of %sh' => 'Personne assigné, estimé de %sh',
|
||||
'%s updated a comment on the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s a mis à jour un commentaire appartenant à la tâche <a href="?controller=task&action=show&task_id=%d">n°%d</a>',
|
||||
'%s commented the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s a ajouté un commentaire sur la tâche <a href="?controller=task&action=show&task_id=%d">n°%d</a>',
|
||||
'%s\'s activity' => 'Activité du projet %s',
|
||||
'No activity.' => 'Aucune activité.',
|
||||
'RSS feed' => 'Flux RSS',
|
||||
'%s updated a comment on the task #%d' => '%s a mis à jour un commentaire sur la tâche n°%d',
|
||||
'%s commented on the task #%d' => '%s a ajouté un commentaire sur la tâche n°%d',
|
||||
'%s updated a subtask for the task #%d' => '%s a mis à jour une sous-tâche appartenant à la tâche n°%d',
|
||||
'%s created a subtask for the task #%d' => '%s a créé une sous-tâche pour la tâche n°%d',
|
||||
'%s updated the task #%d' => '%s a mis à jour la tâche n°%d',
|
||||
'%s created the task #%d' => '%s a créé la tâche n°%d',
|
||||
'%s closed the task #%d' => '%s a fermé la tâche n°%d',
|
||||
'%s open the task #%d' => '%s a ouvert la tâche n°%d',
|
||||
'%s moved the task #%d to the column "%s"' => '%s a déplacé la tâche n°%d dans la colonne « %s »',
|
||||
'%s moved the task #%d to the position %d in the column "%s"' => '%s a déplacé la tâche n°%d à la position n°%d dans la colonne « %s »',
|
||||
'Activity' => 'Activité',
|
||||
'Default values are "%s"' => 'Les valeurs par défaut sont « %s »',
|
||||
'Default columns for new projects (Comma-separated)' => 'Colonnes par défaut pour les nouveaux projets (séparé par des virgules)',
|
||||
'Task assignee change' => 'Modification de la personne assignée sur une tâche',
|
||||
'%s change the assignee of the task #%d' => '%s a changé la personne assignée sur la tâche #%d',
|
||||
'%s change the assignee of the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s a changé la personne assignée sur la tâche <a href="?controller=task&action=show&task_id=%d">n°%d</a>',
|
||||
'[%s][Column Change] %s (#%d)' => '[%s][Changement de colonne] %s (#%d)',
|
||||
'[%s][Position Change] %s (#%d)' => '[%s][Changement de position] %s (#%d)',
|
||||
'[%s][Assignee Change] %s (#%d)' => '[%s][Changement d\'assigné] %s (#%d)',
|
||||
'New password for the user "%s"' => 'Nouveau mot de passe pour l\'utilisateur « %s »',
|
||||
);
|
||||
|
|
506
sources/app/Locales/it_IT/translations.php
Normal file
506
sources/app/Locales/it_IT/translations.php
Normal file
|
@ -0,0 +1,506 @@
|
|||
<?php
|
||||
|
||||
return array(
|
||||
'None' => 'Nessuno',
|
||||
'edit' => 'modificare',
|
||||
'Edit' => 'Modificare',
|
||||
'remove' => 'cancellare',
|
||||
'Remove' => 'Cancellare',
|
||||
'Update' => 'Aggiornare',
|
||||
'Yes' => 'Si',
|
||||
'No' => 'No',
|
||||
'cancel' => 'annullare',
|
||||
'or' => 'o',
|
||||
'Yellow' => 'Giallo',
|
||||
'Blue' => 'Blu',
|
||||
'Green' => 'Verde',
|
||||
'Purple' => 'Porpora',
|
||||
'Red' => 'Rosso',
|
||||
'Orange' => 'Arancione',
|
||||
'Grey' => 'Grigio',
|
||||
'Save' => 'Salvare',
|
||||
'Login' => 'Entra',
|
||||
'Official website:' => 'Sito web ufficiale :',
|
||||
'Unassigned' => 'Non assegnato',
|
||||
'View this task' => 'Vedere questo compito',
|
||||
'Remove user' => 'Cancellare un utente',
|
||||
'Do you really want to remove this user: "%s"?' => 'Veramente vuoi cancellare questo utente: « %s » ?',
|
||||
'New user' => 'Aggiungere un utente',
|
||||
'All users' => 'Tutti gli utenti',
|
||||
'Username' => 'Nome utente',
|
||||
'Password' => 'Password',
|
||||
'Default project' => 'Progetto predefinito',
|
||||
'Administrator' => 'Amministratore',
|
||||
'Sign in' => 'Iscriversi',
|
||||
'Users' => 'Utenti',
|
||||
'No user' => 'Nessun utente',
|
||||
'Forbidden' => 'Vietato',
|
||||
'Access Forbidden' => 'Accesso vietato',
|
||||
'Only administrators can access to this page.' => 'Solo gli amministratori possono accedere a questa pagina.',
|
||||
'Edit user' => 'Modificare un utente',
|
||||
'Logout' => 'Uscire',
|
||||
'Bad username or password' => 'Utente o password sbagliato',
|
||||
'users' => 'utenti',
|
||||
'projects' => 'progetti',
|
||||
'Edit project' => 'Modificare progetto',
|
||||
'Name' => 'Nome',
|
||||
'Activated' => 'Attivo',
|
||||
'Projects' => 'Progetti',
|
||||
'No project' => 'Nessun progetto',
|
||||
'Project' => 'Progetto',
|
||||
'Status' => 'Stato',
|
||||
'Tasks' => 'Compiti',
|
||||
'Board' => 'Bacheca',
|
||||
// 'Actions' => '',
|
||||
'Inactive' => 'Inattivo',
|
||||
'Active' => 'Attivo',
|
||||
'Column %d' => 'Colonna %d',
|
||||
'Add this column' => 'Aggiungere questa colonna',
|
||||
'%d tasks on the board' => '%d compiti sulla bacheca',
|
||||
'%d tasks in total' => '%d compiti in totale',
|
||||
'Unable to update this board.' => 'Non si può aggiornare questa bacheca.',
|
||||
'Edit board' => 'Modificare questa bacheca',
|
||||
'Disable' => 'Disattivare',
|
||||
'Enable' => 'Attivare',
|
||||
'New project' => 'Nuovo progetto',
|
||||
'Do you really want to remove this project: "%s"?' => 'Vuoi veramente eliminare questo progetto: « %s » ?',
|
||||
'Remove project' => 'Cancellare il progetto',
|
||||
'Boards' => 'Bacheche',
|
||||
'Edit the board for "%s"' => 'Modificare la bacheca per « %s »',
|
||||
'All projects' => 'Tutti i progetti',
|
||||
'Change columns' => 'Cambiare le colonne',
|
||||
'Add a new column' => 'Aggiungere una nuova colonna',
|
||||
'Title' => 'Titolo',
|
||||
'Add Column' => 'Aggiungere colonna',
|
||||
'Project "%s"' => 'progetto « %s »',
|
||||
'Nobody assigned' => 'Nessuno assegnato',
|
||||
'Assigned to %s' => 'Assegnato a %s',
|
||||
'Remove a column' => 'Cancellare questa colonna',
|
||||
'Remove a column from a board' => 'Cancellare una colonna di una bacheca',
|
||||
'Unable to remove this column.' => 'Non si può cancellare questa colonna.',
|
||||
'Do you really want to remove this column: "%s"?' => 'Veramente desideri cancellare questa colonna : « %s » ?',
|
||||
'This action will REMOVE ALL TASKS associated to this column!' => 'Questa azione cancellerà TUTTI I COMPITI legati a questa colonna!',
|
||||
'Settings' => 'Impostazioni',
|
||||
'Application settings' => 'Impostazioni dell\'applicazione',
|
||||
'Language' => 'Lingua',
|
||||
'Webhooks token:' => 'Identificatore (token) per i webhooks :',
|
||||
// 'API token:' => '',
|
||||
'More information' => 'Più informazione',
|
||||
'Database size:' => 'Dimensioni della base dati:',
|
||||
'Download the database' => 'Scaricare la base dati',
|
||||
'Optimize the database' => 'Ottimizare la base dati',
|
||||
'(VACUUM command)' => '(Comando VACUUM)',
|
||||
'(Gzip compressed Sqlite file)' => '(File Sqlite compresso in Gzip)',
|
||||
'User settings' => 'Impostazioni di utente',
|
||||
'My default project:' => 'Il mio progetto predefinito: ',
|
||||
'Close a task' => 'Chiudere un compito',
|
||||
'Do you really want to close this task: "%s"?' => 'Veramente desidera chiudere questo compito: « %s » ?',
|
||||
'Edit a task' => 'Modificare un compito',
|
||||
'Column' => 'colonna',
|
||||
// 'Color' => '',
|
||||
'Assignee' => 'Persona assegnata',
|
||||
'Create another task' => 'Creare un nuovo compito',
|
||||
'New task' => 'Nuovo compito',
|
||||
'Open a task' => 'Aprire un compito',
|
||||
'Do you really want to open this task: "%s"?' => 'Veramente desidera aprire questo compito: « %s » ?',
|
||||
'Back to the board' => 'Tornare alla bacheca',
|
||||
// 'Created on %B %e, %Y at %k:%M %p' => '',
|
||||
'There is nobody assigned' => 'Non c\'è nessuno assegnato a questo compito',
|
||||
'Column on the board:' => 'Colonna sulla bacheca: ',
|
||||
'Status is open' => 'Stato aperto',
|
||||
'Status is closed' => 'stato chiuso',
|
||||
'Close this task' => 'Chiudere questo compito',
|
||||
'Open this task' => 'Aprire questo compito',
|
||||
'There is no description.' => 'Non c\'è descrizione.',
|
||||
'Add a new task' => 'Aggiungere un nuovo compito',
|
||||
'The username is required' => 'Si richiede un nome di utente',
|
||||
'The maximum length is %d characters' => 'La lunghezza massima è di %d caratteri',
|
||||
'The minimum length is %d characters' => 'La lunghezza minima è di %d caratteri',
|
||||
'The password is required' => 'Si richiede una password',
|
||||
'This value must be an integer' => 'questo valore deve essere un intero',
|
||||
'The username must be unique' => 'Il nome di utente deve essere unico',
|
||||
'The username must be alphanumeric' => 'Il nome di utente deve essere alfanumerico',
|
||||
'The user id is required' => 'Si richiede l\'identificatore dell\'utente',
|
||||
// 'Passwords don\'t match' => '',
|
||||
'The confirmation is required' => 'Si richiede una conferma',
|
||||
'The column is required' => 'Si richiede una colonna',
|
||||
'The project is required' => 'Si richiede il progetto',
|
||||
'The color is required' => 'Si richiede il colore',
|
||||
'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',
|
||||
'The language is required' => 'Si richiede una lingua',
|
||||
'There is no active project, the first step is to create a new project.' => 'Non ci sono progetti attivi, il primo passo consiste in creare un nuovo progetto.',
|
||||
'Settings saved successfully.' => 'Impostazioni salvate correttamente.',
|
||||
'Unable to save your settings.' => 'Non si possono salvare gli impostazioni.',
|
||||
'Database optimization done.' => 'Ottimizzazione della base dati conclusa.',
|
||||
'Your project have been created successfully.' => 'Il suo progetto è stato creato correttamente.',
|
||||
'Unable to create your project.' => 'Non si può creare il progetto.',
|
||||
'Project updated successfully.' => 'Progetto aggiornato correttamente.',
|
||||
'Unable to update this project.' => 'Non si può aggiornare il progetto.',
|
||||
'Unable to remove this project.' => 'Non si può cancellare questo progetto.',
|
||||
'Project removed successfully.' => 'Progetto cancellato correttamente.',
|
||||
'Project activated successfully.' => 'Progetto attivato correttamente.',
|
||||
'Unable to activate this project.' => 'Non si può attivare il progetto.',
|
||||
'Project disabled successfully.' => 'Progetto disattivato correttamente.',
|
||||
'Unable to disable this project.' => 'Non si può disattivare il progetto.',
|
||||
'Unable to open this task.' => 'Non si può aprire questo compito.',
|
||||
'Task opened successfully.' => 'Il compito è stato aperto correttamente.',
|
||||
'Unable to close this task.' => 'Non si può chiudere questo compito.',
|
||||
'Task closed successfully.' => 'Compito chiuso correttamente.',
|
||||
'Unable to update your task.' => 'Non si può modificare questo compito.',
|
||||
'Task updated successfully.' => 'Compito modificato correttamente.',
|
||||
'Unable to create your task.' => 'Non si può creare questo compito.',
|
||||
'Task created successfully.' => 'Compito creato correttamente.',
|
||||
'User created successfully.' => 'Utente creato correttamente.',
|
||||
'Unable to create your user.' => 'Non si può creare l\'utente.',
|
||||
'User updated successfully.' => 'Utente aggiornato correttamente.',
|
||||
'Unable to update your user.' => 'Non si può aggiornare questo utente.',
|
||||
'User removed successfully.' => 'Utente cancellato correttamente.',
|
||||
'Unable to remove this user.' => 'Non si può cancellare questo utente.',
|
||||
'Board updated successfully.' => 'Bacheca aggiornata correttamente.',
|
||||
'Ready' => 'Pronto',
|
||||
'Backlog' => 'In attesa',
|
||||
'Work in progress' => 'In corso',
|
||||
'Done' => 'Fatto',
|
||||
'Application version:' => 'Versione dell\'applicazione:',
|
||||
// 'Completed on %B %e, %Y at %k:%M %p' => '',
|
||||
// '%B %e, %Y at %k:%M %p' => '',
|
||||
'Date created' => 'Data di creazione',
|
||||
'Date completed' => 'Data di termine',
|
||||
'Id' => 'Identificatore',
|
||||
'No task' => 'Nessun compito',
|
||||
'Completed tasks' => 'Compiti fatti',
|
||||
'List of projects' => 'Lista di progetti',
|
||||
'Completed tasks for "%s"' => 'Compiti fatti da « %s »',
|
||||
'%d closed tasks' => '%d compiti chiusi',
|
||||
'No task for this project' => 'Nessun compito per questo progetto',
|
||||
'Public link' => 'Link pubblico',
|
||||
'There is no column in your project!' => 'Non c\'è nessuna colonna per questo progetto!',
|
||||
'Change assignee' => 'Cambiare la persona assegnata',
|
||||
'Change assignee for the task "%s"' => 'Cambiare la persona assegnata per il compito « %s »',
|
||||
'Timezone' => 'Fuso orario',
|
||||
'Sorry, I didn\'t found this information in my database!' => 'Mi dispiace, non ho trovato questa informazione sulla base dati!',
|
||||
'Page not found' => 'Página non trovata',
|
||||
// 'Complexity' => '',
|
||||
'limit' => 'limite',
|
||||
'Task limit' => 'Numero massimo di compiti',
|
||||
'This value must be greater than %d' => 'questo valore deve essere maggiore di %d',
|
||||
'Edit project access list' => 'Modificare i permessi del progetto',
|
||||
'Edit users access' => 'Modificare i permessi degli utenti',
|
||||
'Allow this user' => 'Permettere a questo utente',
|
||||
'Only those users have access to this project:' => 'Solo questi utenti hanno accesso a questo progetto:',
|
||||
'Don\'t forget that administrators have access to everything.' => 'Non dimenticare che gli amministratori hanno accesso a tutto.',
|
||||
'revoke' => 'revocare',
|
||||
'List of authorized users' => 'Lista di utenti autorizzati',
|
||||
'User' => 'Utente',
|
||||
'Everybody have access to this project.' => 'Tutti hanno accesso a questo progetto.',
|
||||
'You are not allowed to access to this project.' => 'Non hai l\'accesso a questo progetto.',
|
||||
'Comments' => 'Commenti',
|
||||
'Post comment' => 'Mandare commento',
|
||||
'Write your text in Markdown' => 'Scrivi il testo in Markdown',
|
||||
'Leave a comment' => 'Lasciare un commento',
|
||||
'Comment is required' => 'Si richiede un commento',
|
||||
'Leave a description' => 'Lasciare una descrizione',
|
||||
'Comment added successfully.' => 'Commenti aggiunti correttamente.',
|
||||
'Unable to create your comment.' => 'Non si può creare questo commento.',
|
||||
'The description is required' => 'Si richiede una descrizione',
|
||||
'Edit this task' => 'Modificare questo compito',
|
||||
'Due Date' => 'Data di scadenza',
|
||||
'm/d/Y' => 'd/m/Y',
|
||||
'month/day/year' => 'giorno/mese/anno',
|
||||
'Invalid date' => 'Data sbagliata',
|
||||
// 'Must be done before %B %e, %Y' => '',
|
||||
// '%B %e, %Y' => '',
|
||||
'Automatic actions' => 'Azioni automatiche',
|
||||
'Your automatic action have been created successfully.' => 'l\'azione automatica è stata creata correttamente.',
|
||||
'Unable to create your automatic action.' => 'Non si può creare quest\'azione automatica.',
|
||||
'Remove an action' => 'Cancellare un\'azione',
|
||||
'Unable to remove this action.' => 'Non si può cancellare questa azione.',
|
||||
'Action removed successfully.' => 'Azione cancellata correttamente.',
|
||||
'Automatic actions for the project "%s"' => 'Azioni automatiche per questo progetto « %s »',
|
||||
'Defined actions' => 'Azioni definite',
|
||||
// 'Add an action' => '',
|
||||
'Event name' => 'Nome dell\'evento',
|
||||
'Action name' => 'Nome dell\'azione',
|
||||
'Action parameters' => 'Parametri d\'azione',
|
||||
'Action' => 'Azione',
|
||||
'Event' => 'Evento',
|
||||
'When the selected event occurs execute the corresponding action.' => 'Quando accade l\'evento selezionato, eseguire l\'azione corrispondente.',
|
||||
'Next step' => 'Passo seguente',
|
||||
'Define action parameters' => 'Definire i parametri dell\'azione',
|
||||
'Save this action' => 'Salvare questa azione',
|
||||
'Do you really want to remove this action: "%s"?' => 'Veramente vuole cancellare questa azione « %s » ?',
|
||||
'Remove an automatic action' => 'Cancellare un\'azione automatica',
|
||||
'Close the task' => 'Chiudere questo compito',
|
||||
'Assign the task to a specific user' => 'Assegnare questo compito a un utente specifico',
|
||||
'Assign the task to the person who does the action' => 'Assegnare il compito all\'utente che svolge l\'azione',
|
||||
'Duplicate the task to another project' => 'Duplicare il compito in altro progetto',
|
||||
'Move a task to another column' => 'Muovere un compito ad un altra colonna',
|
||||
'Move a task to another position in the same column' => 'Muovere un compito ad altra posizione sulla stessa colonna',
|
||||
'Task modification' => 'Modifica di un compito',
|
||||
'Task creation' => 'Creazione di un compito',
|
||||
'Open a closed task' => 'Riaprire un compito',
|
||||
'Closing a task' => 'Chiudere un compito',
|
||||
// 'Assign a color to a specific user' => '',
|
||||
'Column title' => 'Titolo della colonna',
|
||||
'Position' => 'Posizione',
|
||||
'Move Up' => 'Alzare',
|
||||
'Move Down' => 'Abassare',
|
||||
'Duplicate to another project' => 'Duplicare in un altro progetto',
|
||||
'Duplicate' => 'Duplicare',
|
||||
'link' => 'link',
|
||||
'Update this comment' => 'Aggiornare questo commento',
|
||||
'Comment updated successfully.' => 'Commento aggiornato correttamente.',
|
||||
'Unable to update your comment.' => 'Non si può aggiornare questo commento.',
|
||||
'Remove a comment' => 'Cancellare un commento',
|
||||
'Comment removed successfully.' => 'Commento cancellato correttamente.',
|
||||
'Unable to remove this comment.' => 'Non si può cancellare questo commento.',
|
||||
'Do you really want to remove this comment?' => 'Desidera cancellare questo commento?',
|
||||
'Only administrators or the creator of the comment can access to this page.' => 'Solo gli amministratori o l\'autore del commento hanno accesso a questa pagina.',
|
||||
'Details' => 'Dettagli',
|
||||
'Current password for the user "%s"' => 'Password attuale per l\'utente: « %s »',
|
||||
'The current password is required' => 'Si richiede la password attuale',
|
||||
'Wrong password' => 'password sbagliata',
|
||||
'Reset all tokens' => 'Azzerare gli identificatori (tokens) di sicurezza ',
|
||||
'All tokens have been regenerated.' => 'Tutti gli identificatori (tokens) sono stati rigenerati.',
|
||||
'Unknown' => 'Sconociuto',
|
||||
'Last logins' => 'Ultimi ingressi',
|
||||
'Login date' => 'Data di ingresso',
|
||||
'Authentication method' => 'Metodo di autenticazzione',
|
||||
'IP address' => 'Indirizzo IP',
|
||||
'User agent' => 'Navigatore',
|
||||
'Persistent connections' => 'Conessioni persistenti',
|
||||
'No session.' => 'Non essiste sessione.',
|
||||
'Expiration date' => 'Data di scadenza',
|
||||
'Remember Me' => 'Riccordami',
|
||||
'Creation date' => 'Data di creazione',
|
||||
'Filter by user' => 'Filtrado mediante utente',
|
||||
'Filter by due date' => 'Filtrare attraverso data di scadenza',
|
||||
'Everybody' => 'Tutti',
|
||||
'Open' => 'Aperto',
|
||||
'Closed' => 'Chiuso',
|
||||
'Search' => 'Cercare',
|
||||
'Nothing found.' => 'Non si è trovato nulla.',
|
||||
'Search in the project "%s"' => 'Cercare sul progetto "%s"',
|
||||
'Due date' => 'Data di scadenza',
|
||||
'Others formats accepted: %s and %s' => 'Altri formati accettati: %s y %s',
|
||||
'Description' => 'Descrizione',
|
||||
'%d comments' => '%d commenti',
|
||||
'%d comment' => '%d commento',
|
||||
'Email address invalid' => 'Indirizzo e-mail sbagliato',
|
||||
'Your Google Account is not linked anymore to your profile.' => 'Il suo account Google non i più collegato col suo profilo',
|
||||
'Unable to unlink your Google Account.' => 'Non si può svincolare l\'account di Google.',
|
||||
'Google authentication failed' => 'Non si è riuscito ad ingressare su Google',
|
||||
'Unable to link your Google Account.' => 'Non si può collegare con il suo account di Google.',
|
||||
'Your Google Account is linked to your profile successfully.' => 'Il suo account di Google è stato collegato correttamente al suo profilo.',
|
||||
'Email' => 'E-mail',
|
||||
'Link my Google Account' => 'Collegare con il mio Account di Google',
|
||||
'Unlink my Google Account' => 'Svincolare con il mio account di Google',
|
||||
'Login with my Google Account' => 'Ingressa con il mio Account di Google',
|
||||
'Project not found.' => 'progetto non trovato.',
|
||||
'Task #%d' => 'Compito numero %d',
|
||||
'Task removed successfully.' => 'Compito cancellato correttamente.',
|
||||
'Unable to remove this task.' => 'Non si può cancellare questo compito.',
|
||||
'Remove a task' => 'Cancellare un compito',
|
||||
'Do you really want to remove this task: "%s"?' => 'Veramente vuoi cancellare questo compito: "%s"?',
|
||||
'Assign automatically a color based on a category' => 'Assegnare un colore in modo automatico basandosi sulla categoria',
|
||||
'Assign automatically a category based on a color' => 'Assegnare una categoria in modo automatico basandosi sul colore',
|
||||
'Task creation or modification' => 'Creazione o Modifica di compito',
|
||||
'Category' => 'Categoria',
|
||||
'Category:' => 'Categoria:',
|
||||
'Categories' => 'Categorie',
|
||||
'Category not found.' => 'Categoria non trovata.',
|
||||
'Your category have been created successfully.' => 'La sua categoria è stata creata correttamente.',
|
||||
'Unable to create your category.' => 'Non si può creare la sua categoria.',
|
||||
'Your category have been updated successfully.' => 'La sua categoria è stata aggiornata correttamente.',
|
||||
'Unable to update your category.' => 'Non si può aggiornare la sua categoria.',
|
||||
'Remove a category' => 'Cancellare una categoria',
|
||||
'Category removed successfully.' => 'Categoria cancellata correttamente.',
|
||||
'Unable to remove this category.' => 'Non si può cancellare questa categoria.',
|
||||
'Category modification for the project "%s"' => 'Modifica di categoria per il progetto "%s"',
|
||||
'Category Name' => 'Nome di categoria',
|
||||
'Categories for the project "%s"' => 'Categorie per il progetto',
|
||||
'Add a new category' => 'Aggiungere una nuova categoria',
|
||||
'Do you really want to remove this category: "%s"?' => 'Vuoi veramente cancellare questa categoria: "%s"?',
|
||||
'Filter by category' => 'Filtrare attraverso categoria',
|
||||
'All categories' => 'Tutte le categorie',
|
||||
'No category' => 'Senza categoria',
|
||||
'The name is required' => 'Si richiede un nome',
|
||||
'Remove a file' => 'Cancellare un file',
|
||||
'Unable to remove this file.' => 'Non si può cancellare questo file.',
|
||||
'File removed successfully.' => 'File cancellato correttamente.',
|
||||
'Attach a document' => 'Allegare un documento',
|
||||
'Do you really want to remove this file: "%s"?' => 'Vuoi veramente cancellare questo file: "%s"?',
|
||||
'open' => 'aprire',
|
||||
'Attachments' => 'Allegati',
|
||||
'Edit the task' => 'Modificare il compito',
|
||||
'Edit the description' => 'Modificare la descrizione',
|
||||
'Add a comment' => 'Aggiungere un commento',
|
||||
'Edit a comment' => 'Modificare un commento',
|
||||
'Summary' => 'Sommario',
|
||||
'Time tracking' => 'Time tracking',
|
||||
'Estimate:' => 'Stimato:',
|
||||
'Spent:' => 'Trascorso:',
|
||||
'Do you really want to remove this sub-task?' => 'Vuoi veramente cancellare questo sub-compito?',
|
||||
'Remaining:' => 'Rimangono',
|
||||
'hours' => 'ore',
|
||||
'spent' => 'trascorse',
|
||||
'estimated' => 'stimate',
|
||||
'Sub-Tasks' => 'Sub-Compiti',
|
||||
'Add a sub-task' => 'Aggiungere un sub-compito',
|
||||
'Original Estimate' => 'Stima originale',
|
||||
'Create another sub-task' => 'Crear un altro sub-compito',
|
||||
'Time Spent' => 'Tempo Trascorso',
|
||||
'Edit a sub-task' => 'Modificare un sub-compito',
|
||||
'Remove a sub-task' => 'Cancellare un sub-compito',
|
||||
'The time must be a numeric value' => 'Il tempo deve essere un valore numerico',
|
||||
'Todo' => 'Da fare',
|
||||
'In progress' => 'In corso',
|
||||
'Sub-task removed successfully.' => 'Sub-compito cancellato correttamente.',
|
||||
'Unable to remove this sub-task.' => 'Non si può cancellare questo sub-compito.',
|
||||
'Sub-task updated successfully.' => 'Sub-compito aggiornato correttamente.',
|
||||
'Unable to update your sub-task.' => 'Non si può aggiornare il suo sub-compito.',
|
||||
'Unable to create your sub-task.' => 'Non si può creare il suo sub-compito.',
|
||||
'Sub-task added successfully.' => 'Sub-compito aggiunto correttamente.',
|
||||
'Maximum size: ' => 'Dimensioni massime',
|
||||
'Unable to upload the file.' => 'Non si può caricare il file.',
|
||||
'Display another project' => 'Mostrare un altro progetto',
|
||||
'Your GitHub account was successfully linked to your profile.' => 'Il suo account di Github è stato collegato correttamente col suo profilo.',
|
||||
'Unable to link your GitHub Account.' => 'Non si può collegarre col suo account di Github.',
|
||||
'GitHub authentication failed' => 'L\'autenticazione non è stata possibile',
|
||||
'Your GitHub account is no longer linked to your profile.' => 'Il suo account di Github non è più vincolato al suo profilo.',
|
||||
'Unable to unlink your GitHub Account.' => 'Non si può svincolare il suo account di Github.',
|
||||
'Login with my GitHub Account' => 'Ingressare col suo account di Github',
|
||||
'Link my GitHub Account' => 'Lier mon compte Github',
|
||||
'Unlink my GitHub Account' => 'Non impiegare più l\'account di Github',
|
||||
'Created by %s' => 'Creato da %s',
|
||||
'Last modified on %B %e, %Y at %k:%M %p' => 'Ultima modifica il %d/%m/%Y alle %H:%M',
|
||||
'Tasks Export' => 'Esportazione di compiti',
|
||||
'Tasks exportation for "%s"' => 'Esportazione di compiti per « %s »',
|
||||
'Start Date' => 'Data d\'inizio',
|
||||
'End Date' => 'Data di fine',
|
||||
'Execute' => 'Essecutare',
|
||||
'Task Id' => 'Identificatore del compito',
|
||||
'Creator' => 'Creatore',
|
||||
'Modification date' => 'Data di modifica',
|
||||
'Completion date' => 'Data di termine',
|
||||
// 'Webhook URL for task creation' => '',
|
||||
// 'Webhook URL for task modification' => '',
|
||||
// 'Clone' => '',
|
||||
// 'Clone Project' => '',
|
||||
// 'Project cloned successfully.' => '',
|
||||
// 'Unable to clone this project.' => '',
|
||||
// 'Email notifications' => '',
|
||||
// 'Enable email notifications' => '',
|
||||
// 'Task position:' => '',
|
||||
// 'The task #%d have been opened.' => '',
|
||||
// 'The task #%d have been closed.' => '',
|
||||
// 'Sub-task updated' => '',
|
||||
// 'Title:' => '',
|
||||
// 'Status:' => '',
|
||||
// 'Assignee:' => '',
|
||||
// 'Time tracking:' => '',
|
||||
// 'New sub-task' => '',
|
||||
'New attachment added "%s"' => 'Nuovo allegato aggiunto « %s »',
|
||||
'Comment updated' => 'Commento aggiornato',
|
||||
'New comment posted by %s' => 'Nuovo commento aggiunto da « %s »',
|
||||
'List of due tasks for the project "%s"' => 'Lista dei compiti scaduti per il progetto « %s »',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][Nuovo allegato] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][Nuovo commento] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][Commento aggiornato] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][Nuovo sub-compito] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][Sub-compito aggiornato] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][Nuovo compito] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][Compito aggiornato] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][Compito chiuso] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][Compito aperto] %s (#%d)',
|
||||
'[%s][Due tasks]' => '[%s][Compiti scaduti]',
|
||||
'[Kanboard] Notification' => '[Kanboard] Notification',
|
||||
'I want to receive notifications only for those projects:' => 'Vorrei ricevere le notifiche solo da questi progetti:',
|
||||
'view the task on Kanboard' => 'vedi il compito su Kanboard',
|
||||
// 'Public access' => '',
|
||||
// 'Categories management' => '',
|
||||
// 'Users management' => '',
|
||||
// 'Active tasks' => '',
|
||||
// 'Disable public access' => '',
|
||||
// 'Enable public access' => '',
|
||||
// 'Active projects' => '',
|
||||
// 'Inactive projects' => '',
|
||||
// 'Public access disabled' => '',
|
||||
// 'Do you really want to disable this project: "%s"?' => '',
|
||||
// 'Do you really want to duplicate this project: "%s"?' => '',
|
||||
// 'Do you really want to enable this project: "%s"?' => '',
|
||||
// 'Project activation' => '',
|
||||
// 'Move the task to another project' => '',
|
||||
// 'Move to another project' => '',
|
||||
// 'Do you really want to duplicate this task?' => '',
|
||||
// 'Duplicate a task' => '',
|
||||
// 'External accounts' => '',
|
||||
// 'Account type' => '',
|
||||
// 'Local' => '',
|
||||
// 'Remote' => '',
|
||||
// 'Enabled' => '',
|
||||
// 'Disabled' => '',
|
||||
// 'Google account linked' => '',
|
||||
// 'Github account linked' => '',
|
||||
// 'Username:' => '',
|
||||
// 'Name:' => '',
|
||||
// 'Email:' => '',
|
||||
// 'Default project:' => '',
|
||||
// 'Notifications:' => '',
|
||||
// 'Group:' => '',
|
||||
// 'Regular user' => '',
|
||||
// 'Account type:' => '',
|
||||
// 'Edit profile' => '',
|
||||
// 'Change password' => '',
|
||||
// 'Password modification' => '',
|
||||
// 'External authentications' => '',
|
||||
// 'Google Account' => '',
|
||||
// 'Github Account' => '',
|
||||
// 'Never connected.' => '',
|
||||
// 'No account linked.' => '',
|
||||
// 'Account linked.' => '',
|
||||
// 'No external authentication enabled.' => '',
|
||||
// 'Password modified successfully.' => '',
|
||||
// 'Unable to change the password.' => '',
|
||||
// 'Change category for the task "%s"' => '',
|
||||
// 'Change category' => '',
|
||||
// '%s updated the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s open the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the position #%d in the column "%s"' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the column "%s"' => '',
|
||||
// '%s created the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s closed the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s created a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s updated a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// 'Assigned to %s with an estimate of %s/%sh' => '',
|
||||
// 'Not assigned, estimate of %sh' => '',
|
||||
// '%s updated a comment on the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s commented the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s\'s activity' => '',
|
||||
// 'No activity.' => '',
|
||||
// 'RSS feed' => '',
|
||||
// '%s updated a comment on the task #%d' => '',
|
||||
// '%s commented on the task #%d' => '',
|
||||
// '%s updated a subtask for the task #%d' => '',
|
||||
// '%s created a subtask for the task #%d' => '',
|
||||
// '%s updated the task #%d' => '',
|
||||
// '%s created the task #%d' => '',
|
||||
// '%s closed the task #%d' => '',
|
||||
// '%s open the task #%d' => '',
|
||||
// '%s moved the task #%d to the column "%s"' => '',
|
||||
// '%s moved the task #%d to the position %d in the column "%s"' => '',
|
||||
// 'Activity' => '',
|
||||
// 'Default values are "%s"' => '',
|
||||
// 'Default columns for new projects (Comma-separated)' => '',
|
||||
// 'Task assignee change' => '',
|
||||
// '%s change the assignee of the task #%d' => '',
|
||||
// '%s change the assignee of the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '[%s][Column Change] %s (#%d)' => '',
|
||||
// '[%s][Position Change] %s (#%d)' => '',
|
||||
// '[%s][Assignee Change] %s (#%d)' => '',
|
||||
// 'New password for the user "%s"' => '',
|
||||
);
|
|
@ -1,13 +1,6 @@
|
|||
<?php
|
||||
|
||||
return array(
|
||||
'English' => 'angielski',
|
||||
'French' => 'francuski',
|
||||
'Polish' => 'polski',
|
||||
'Portuguese (Brazilian)' => 'Portugalski (brazylijski)',
|
||||
'Spanish' => 'Hiszpański',
|
||||
// 'German' => '',
|
||||
// 'Chinese (Simplified)' => '',
|
||||
'None' => 'Brak',
|
||||
'edit' => 'edytuj',
|
||||
'Edit' => 'Edytuj',
|
||||
|
@ -36,7 +29,7 @@ return array(
|
|||
'All users' => 'Wszyscy użytkownicy',
|
||||
'Username' => 'Nazwa użytkownika',
|
||||
'Password' => 'Hasło',
|
||||
'Default Project' => 'Domyślny projekt',
|
||||
'Default project' => 'Domyślny projekt',
|
||||
'Administrator' => 'Administrator',
|
||||
'Sign in' => 'Zaloguj',
|
||||
'Users' => 'Użytkownicy',
|
||||
|
@ -58,6 +51,7 @@ return array(
|
|||
'Status' => 'Status',
|
||||
'Tasks' => 'Zadania',
|
||||
'Board' => 'Tablica',
|
||||
'Actions' => 'Akcje',
|
||||
'Inactive' => 'Nieaktywny',
|
||||
'Active' => 'Aktywny',
|
||||
'Column %d' => 'Kolumna %d',
|
||||
|
@ -90,6 +84,7 @@ return array(
|
|||
'Application settings' => 'Ustawienia aplikacji',
|
||||
'Language' => 'Język',
|
||||
'Webhooks token:' => 'Token :',
|
||||
// 'API token:' => '',
|
||||
'More information' => 'Więcej informacji',
|
||||
'Database size:' => 'Rozmiar bazy danych :',
|
||||
'Download the database' => 'Pobierz bazę danych',
|
||||
|
@ -109,7 +104,7 @@ return array(
|
|||
'Open a task' => 'Otwórz zadanie',
|
||||
'Do you really want to open this task: "%s"?' => 'Na pewno chcesz otworzyć zadanie: "%s"?',
|
||||
'Back to the board' => 'Powrót do tablicy',
|
||||
'Created on %B %e, %G at %k:%M %p' => 'Utworzono dnia %e %B %G o %k:%M',
|
||||
'Created on %B %e, %Y at %k:%M %p' => 'Utworzono dnia %e %B %Y o %k:%M',
|
||||
'There is nobody assigned' => 'Nikt nie jest przypisany',
|
||||
'Column on the board:' => 'Kolumna na tablicy:',
|
||||
'Status is open' => 'Status otwarty',
|
||||
|
@ -171,8 +166,8 @@ return array(
|
|||
'Work in progress' => 'W trakcie',
|
||||
'Done' => 'Zakończone',
|
||||
'Application version:' => 'Wersja aplikacji:',
|
||||
'Completed on %B %e, %G at %k:%M %p' => 'Zakończono dnia %e %B %G o %k:%M',
|
||||
'%B %e, %G at %k:%M %p' => '%e %B %G o %k:%M',
|
||||
'Completed on %B %e, %Y at %k:%M %p' => 'Zakończono dnia %e %B %Y o %k:%M',
|
||||
'%B %e, %Y at %k:%M %p' => '%e %B %Y o %k:%M',
|
||||
'Date created' => 'Data utworzenia',
|
||||
'Date completed' => 'Data zakończenia',
|
||||
'Id' => 'Ident',
|
||||
|
@ -181,25 +176,21 @@ return array(
|
|||
'List of projects' => 'Lista projektów',
|
||||
'Completed tasks for "%s"' => 'Zadania zakończone dla "%s"',
|
||||
'%d closed tasks' => '%d zamkniętych zadań',
|
||||
'no task for this project' => 'brak zadań dla tego projektu',
|
||||
'No task for this project' => 'Brak zadań dla tego projektu',
|
||||
'Public link' => 'Link publiczny',
|
||||
'There is no column in your project!' => 'Brak kolumny w Twoim projekcie',
|
||||
'Change assignee' => 'Zmień odpowiedzialną osobę',
|
||||
'Change assignee for the task "%s"' => 'Zmień odpowiedzialną osobę dla zadania "%s"',
|
||||
'Timezone' => 'Strefa czasowa',
|
||||
'Actions' => 'Akcje',
|
||||
'Confirmation' => 'Powtórzenie hasła',
|
||||
'Description' => 'Opis',
|
||||
'Sorry, I didn\'t found this information in my database!' => 'Niestety nie znaleziono tej informacji w bazie danych',
|
||||
'Page not found' => 'Strona nie istnieje',
|
||||
'Story Points' => 'Poziom trudności',
|
||||
'Complexity' => 'Poziom trudności',
|
||||
'limit' => 'limit',
|
||||
'Task limit' => 'Limit zadań',
|
||||
'This value must be greater than %d' => 'Wartość musi być większa niż %d',
|
||||
'Edit project access list' => 'Edycja list dostępu dla projektu',
|
||||
'Edit users access' => 'Edytuj dostęp',
|
||||
'Allow this user' => 'Dodaj użytkownika',
|
||||
'Project access list for "%s"' => 'Lista uprawnionych dla projektu "%s"',
|
||||
'Only those users have access to this project:' => 'Użytkownicy mający dostęp:',
|
||||
'Don\'t forget that administrators have access to everything.' => 'Pamiętaj: Administratorzy mają zawsze dostęp do wszystkiego!',
|
||||
'revoke' => 'odbierz dostęp',
|
||||
|
@ -212,16 +203,17 @@ return array(
|
|||
'Write your text in Markdown' => 'Możesz użyć Markdown',
|
||||
'Leave a comment' => 'Zostaw komentarz',
|
||||
'Comment is required' => 'Komentarz jest wymagany',
|
||||
// 'Leave a description' => '',
|
||||
'Comment added successfully.' => 'Komentarz dodany',
|
||||
'Unable to create your comment.' => 'Nie udało się dodać komentarza',
|
||||
'The description is required' => 'Opis jest wymagany',
|
||||
'Edit this task' => 'Edytuj zadanie',
|
||||
'Due Date' => 'Termin',
|
||||
'm/d/Y' => 'd/m/Y', // Date format parsed with php
|
||||
'month/day/year' => 'dzień/miesiąc/rok', // Help shown to the user
|
||||
'm/d/Y' => 'd/m/Y',
|
||||
'month/day/year' => 'dzień/miesiąc/rok',
|
||||
'Invalid date' => 'Błędna data',
|
||||
'Must be done before %B %e, %G' => 'Termin do %e %B %G',
|
||||
'%B %e, %G' => '%e %B %G',
|
||||
'Must be done before %B %e, %Y' => 'Termin do %e %B %Y',
|
||||
'%B %e, %Y' => '%e %B %Y',
|
||||
'Automatic actions' => 'Akcje automatyczne',
|
||||
'Your automatic action have been created successfully.' => 'Twoja akcja została dodana',
|
||||
'Unable to create your automatic action.' => 'Nie udało się utworzyć akcji',
|
||||
|
@ -230,6 +222,7 @@ return array(
|
|||
'Action removed successfully.' => 'Akcja usunięta',
|
||||
'Automatic actions for the project "%s"' => 'Akcje automatyczne dla projektu "%s"',
|
||||
'Defined actions' => 'Zdefiniowane akcje',
|
||||
'Add an action' => 'Nowa akcja',
|
||||
'Event name' => 'Nazwa zdarzenia',
|
||||
'Action name' => 'Nazwa akcji',
|
||||
'Action parameters' => 'Parametry akcji',
|
||||
|
@ -252,7 +245,6 @@ return array(
|
|||
'Open a closed task' => 'Otwarcie zamkniętego zadania',
|
||||
'Closing a task' => 'Zamknięcie zadania',
|
||||
'Assign a color to a specific user' => 'Przypisz kolor do wybranego użytkownika',
|
||||
'Add an action' => 'Nowa akcja',
|
||||
'Column title' => 'Tytuł kolumny',
|
||||
'Position' => 'Pozycja',
|
||||
'Move Up' => 'Przenieś wyżej',
|
||||
|
@ -281,12 +273,12 @@ return array(
|
|||
'IP address' => 'Adres IP',
|
||||
'User agent' => 'Przeglądarka',
|
||||
'Persistent connections' => 'Stałe połączenia',
|
||||
'No session' => 'Brak sesji',
|
||||
'No session.' => 'Brak sesji.',
|
||||
'Expiration date' => 'Data zakończenia',
|
||||
'Remember Me' => 'Pamiętaj mnie',
|
||||
'Creation date' => 'Data utworzenia',
|
||||
// 'Filter by user' => '',
|
||||
// 'Filter by due date' => ',
|
||||
// 'Filter by due date' => '',
|
||||
// 'Everybody' => '',
|
||||
// 'Open' => '',
|
||||
// 'Closed' => '',
|
||||
|
@ -295,7 +287,7 @@ return array(
|
|||
// 'Search in the project "%s"' => '',
|
||||
// 'Due date' => '',
|
||||
// 'Others formats accepted: %s and %s' => '',
|
||||
// 'Description' => '',
|
||||
'Description' => 'Opis',
|
||||
// '%d comments' => '',
|
||||
// '%d comment' => '',
|
||||
// 'Email address invalid' => '',
|
||||
|
@ -367,7 +359,6 @@ return array(
|
|||
// 'The time must be a numeric value' => '',
|
||||
// 'Todo' => '',
|
||||
// 'In progress' => '',
|
||||
// 'Done' => '',
|
||||
// 'Sub-task removed successfully.' => '',
|
||||
// 'Unable to remove this sub-task.' => '',
|
||||
// 'Sub-task updated successfully.' => '',
|
||||
|
@ -384,7 +375,132 @@ return array(
|
|||
// 'Unable to unlink your GitHub Account.' => '',
|
||||
// 'Login with my GitHub Account' => '',
|
||||
// 'Link my GitHub Account' => '',
|
||||
// 'Unlink my GitHub Account' => '',
|
||||
// 'Created by %s' => 'Créé par %s',
|
||||
// 'Last modified on %B %e, %G at %k:%M %p' => '',
|
||||
// 'Unlink my GitHub Account' => '',
|
||||
// 'Created by %s' => '',
|
||||
// 'Last modified on %B %e, %Y at %k:%M %p' => '',
|
||||
// 'Tasks Export' => '',
|
||||
// 'Tasks exportation for "%s"' => '',
|
||||
// 'Start Date' => '',
|
||||
// 'End Date' => '',
|
||||
// 'Execute' => '',
|
||||
// 'Task Id' => '',
|
||||
// 'Creator' => '',
|
||||
// 'Modification date' => '',
|
||||
// 'Completion date' => '',
|
||||
// 'Webhook URL for task creation' => '',
|
||||
// 'Webhook URL for task modification' => '',
|
||||
// 'Clone' => '',
|
||||
// 'Clone Project' => '',
|
||||
// 'Project cloned successfully.' => '',
|
||||
// 'Unable to clone this project.' => '',
|
||||
// 'Email notifications' => '',
|
||||
// 'Enable email notifications' => '',
|
||||
// 'Task position:' => '',
|
||||
// 'The task #%d have been opened.' => '',
|
||||
// 'The task #%d have been closed.' => '',
|
||||
// 'Sub-task updated' => '',
|
||||
// 'Title:' => '',
|
||||
// 'Status:' => '',
|
||||
// 'Assignee:' => '',
|
||||
// 'Time tracking:' => '',
|
||||
// 'New sub-task' => '',
|
||||
// 'New attachment added "%s"' => '',
|
||||
// 'Comment updated' => '',
|
||||
// 'New comment posted by %s' => '',
|
||||
// 'List of due tasks for the project "%s"' => '',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
// '[%s][New subtask] %s (#%d)' => '',
|
||||
// '[%s][Subtask updated] %s (#%d)' => '',
|
||||
// '[%s][New task] %s (#%d)' => '',
|
||||
// '[%s][Task updated] %s (#%d)' => '',
|
||||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
// '[Kanboard] Notification' => '',
|
||||
// 'I want to receive notifications only for those projects:' => '',
|
||||
// 'view the task on Kanboard' => '',
|
||||
// 'Public access' => '',
|
||||
// 'Categories management' => '',
|
||||
// 'Users management' => '',
|
||||
// 'Active tasks' => '',
|
||||
// 'Disable public access' => '',
|
||||
// 'Enable public access' => '',
|
||||
// 'Active projects' => '',
|
||||
// 'Inactive projects' => '',
|
||||
// 'Public access disabled' => '',
|
||||
// 'Do you really want to disable this project: "%s"?' => '',
|
||||
// 'Do you really want to duplicate this project: "%s"?' => '',
|
||||
// 'Do you really want to enable this project: "%s"?' => '',
|
||||
// 'Project activation' => '',
|
||||
// 'Move the task to another project' => '',
|
||||
// 'Move to another project' => '',
|
||||
// 'Do you really want to duplicate this task?' => '',
|
||||
// 'Duplicate a task' => '',
|
||||
// 'External accounts' => '',
|
||||
// 'Account type' => '',
|
||||
// 'Local' => '',
|
||||
// 'Remote' => '',
|
||||
// 'Enabled' => '',
|
||||
// 'Disabled' => '',
|
||||
// 'Google account linked' => '',
|
||||
// 'Github account linked' => '',
|
||||
// 'Username:' => '',
|
||||
// 'Name:' => '',
|
||||
// 'Email:' => '',
|
||||
// 'Default project:' => '',
|
||||
// 'Notifications:' => '',
|
||||
// 'Group:' => '',
|
||||
// 'Regular user' => '',
|
||||
// 'Account type:' => '',
|
||||
// 'Edit profile' => '',
|
||||
// 'Change password' => '',
|
||||
// 'Password modification' => '',
|
||||
// 'External authentications' => '',
|
||||
// 'Google Account' => '',
|
||||
// 'Github Account' => '',
|
||||
// 'Never connected.' => '',
|
||||
// 'No account linked.' => '',
|
||||
// 'Account linked.' => '',
|
||||
// 'No external authentication enabled.' => '',
|
||||
// 'Password modified successfully.' => '',
|
||||
// 'Unable to change the password.' => '',
|
||||
// 'Change category for the task "%s"' => '',
|
||||
// 'Change category' => '',
|
||||
// '%s updated the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s open the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the position #%d in the column "%s"' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the column "%s"' => '',
|
||||
// '%s created the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s closed the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s created a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s updated a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// 'Assigned to %s with an estimate of %s/%sh' => '',
|
||||
// 'Not assigned, estimate of %sh' => '',
|
||||
// '%s updated a comment on the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s commented the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s\'s activity' => '',
|
||||
// 'No activity.' => '',
|
||||
// 'RSS feed' => '',
|
||||
// '%s updated a comment on the task #%d' => '',
|
||||
// '%s commented on the task #%d' => '',
|
||||
// '%s updated a subtask for the task #%d' => '',
|
||||
// '%s created a subtask for the task #%d' => '',
|
||||
// '%s updated the task #%d' => '',
|
||||
// '%s created the task #%d' => '',
|
||||
// '%s closed the task #%d' => '',
|
||||
// '%s open the task #%d' => '',
|
||||
// '%s moved the task #%d to the column "%s"' => '',
|
||||
// '%s moved the task #%d to the position %d in the column "%s"' => '',
|
||||
// 'Activity' => '',
|
||||
// 'Default values are "%s"' => '',
|
||||
// 'Default columns for new projects (Comma-separated)' => '',
|
||||
// 'Task assignee change' => '',
|
||||
// '%s change the assignee of the task #%d' => '',
|
||||
// '%s change the assignee of the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '[%s][Column Change] %s (#%d)' => '',
|
||||
// '[%s][Position Change] %s (#%d)' => '',
|
||||
// '[%s][Assignee Change] %s (#%d)' => '',
|
||||
// 'New password for the user "%s"' => '',
|
||||
);
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
<?php
|
||||
|
||||
return array(
|
||||
'English' => 'Inglês',
|
||||
'French' => 'Francês',
|
||||
'Polish' => 'Polonês',
|
||||
'Portuguese (Brazilian)' => 'Português (Brasil)',
|
||||
'Spanish' => 'Espanhol',
|
||||
// 'German' => '',
|
||||
// 'Chinese (Simplified)' => '',
|
||||
'None' => 'Nenhum',
|
||||
'edit' => 'editar',
|
||||
'Edit' => 'Editar',
|
||||
|
@ -36,7 +29,7 @@ return array(
|
|||
'All users' => 'Todos os usuários',
|
||||
'Username' => 'Nome do usuário',
|
||||
'Password' => 'Senha',
|
||||
'Default Project' => 'Projeto default',
|
||||
'Default project' => 'Projeto default',
|
||||
'Administrator' => 'Administrador',
|
||||
'Sign in' => 'Logar',
|
||||
'Users' => 'Usuários',
|
||||
|
@ -58,6 +51,7 @@ return array(
|
|||
'Status' => 'Status',
|
||||
'Tasks' => 'Tarefas',
|
||||
'Board' => 'Quadro',
|
||||
'Actions' => 'Ações',
|
||||
'Inactive' => 'Inativo',
|
||||
'Active' => 'Ativo',
|
||||
'Column %d' => 'Coluna %d',
|
||||
|
@ -90,6 +84,7 @@ return array(
|
|||
'Application settings' => 'Preferências da aplicação',
|
||||
'Language' => 'Idioma',
|
||||
'Webhooks token:' => 'Token de webhooks:',
|
||||
'API token:' => 'API Token:',
|
||||
'More information' => 'Mais informação',
|
||||
'Database size:' => 'Tamanho do banco de dados:',
|
||||
'Download the database' => 'Download do banco de dados',
|
||||
|
@ -109,7 +104,7 @@ return array(
|
|||
'Open a task' => 'Abrir uma tarefa',
|
||||
'Do you really want to open this task: "%s"?' => 'Quer realmente abrir esta tarefa: "%s"?',
|
||||
'Back to the board' => 'Voltar ao quadro',
|
||||
'Created on %B %e, %G at %k:%M %p' => 'Criado em %d %B %G às %H:%M',
|
||||
'Created on %B %e, %Y at %k:%M %p' => 'Criado em %d %B %Y às %H:%M',
|
||||
'There is nobody assigned' => 'Não há ninguém designado',
|
||||
'Column on the board:' => 'Coluna no quadro:',
|
||||
'Status is open' => 'Status está aberto',
|
||||
|
@ -171,8 +166,8 @@ return array(
|
|||
'Work in progress' => 'Em andamento',
|
||||
'Done' => 'Encerrado',
|
||||
'Application version:' => 'Versão da aplicação:',
|
||||
'Completed on %B %e, %G at %k:%M %p' => 'Encerrado em %d %B %G às %H:%M',
|
||||
'%B %e, %G at %k:%M %p' => '%d %B %G às %H:%M',
|
||||
'Completed on %B %e, %Y at %k:%M %p' => 'Encerrado em %d %B %Y às %H:%M',
|
||||
'%B %e, %Y at %k:%M %p' => '%d %B %Y às %H:%M',
|
||||
'Date created' => 'Data de criação',
|
||||
'Date completed' => 'Data de encerramento',
|
||||
'Id' => 'Id',
|
||||
|
@ -181,7 +176,7 @@ return array(
|
|||
'List of projects' => 'Lista de projetos',
|
||||
'Completed tasks for "%s"' => 'Tarefas completadas por "%s"',
|
||||
'%d closed tasks' => '%d tarefas encerradas',
|
||||
'no task for this project' => 'nenhuma tarefa para este projeto',
|
||||
'No task for this project' => 'Nenhuma tarefa para este projeto',
|
||||
'Public link' => 'Link público',
|
||||
'There is no column in your project!' => 'Não há colunas no seu projeto!',
|
||||
'Change assignee' => 'Mudar a designação',
|
||||
|
@ -189,14 +184,13 @@ return array(
|
|||
'Timezone' => 'Fuso horário',
|
||||
'Sorry, I didn\'t found 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',
|
||||
'Story Points' => 'Complexidade',
|
||||
'Complexity' => 'Complexidade',
|
||||
'limit' => 'limite',
|
||||
'Task limit' => 'Limite da tarefa',
|
||||
'This value must be greater than %d' => 'Este valor deve ser maior que %d',
|
||||
'Edit project access list' => 'Editar lista de acesso ao projeto', // new translations to brazilian portuguese starts here
|
||||
'Edit project access list' => 'Editar lista de acesso ao projeto',
|
||||
'Edit users access' => 'Editar acesso de usuários',
|
||||
'Allow this user' => 'Permitir esse usuário',
|
||||
'Project access list for "%s"' => 'Lista de acesso ao projeto para "%s"',
|
||||
'Only those users have access to this project:' => 'Somente estes usuários têm acesso a este projeto:',
|
||||
'Don\'t forget that administrators have access to everything.' => 'Não esqueça que administradores têm acesso a tudo.',
|
||||
'revoke' => 'revogar',
|
||||
|
@ -215,11 +209,11 @@ return array(
|
|||
'The description is required' => 'A descrição é obrigatória',
|
||||
'Edit this task' => 'Editar esta tarefa',
|
||||
'Due Date' => 'Data de vencimento',
|
||||
'm/d/Y' => 'd/m/Y', // Date format parsed with php
|
||||
'month/day/year' => 'dia/mês/ano', // Help shown to the user
|
||||
'm/d/Y' => 'd/m/Y',
|
||||
'month/day/year' => 'dia/mês/ano',
|
||||
'Invalid date' => 'Data inválida',
|
||||
'Must be done before %B %e, %G' => 'Deve ser feito antes de %d %B %G',
|
||||
'%B %e, %G' => '%d %B %G',
|
||||
'Must be done before %B %e, %Y' => 'Deve ser feito antes de %d %B %Y',
|
||||
'%B %e, %Y' => '%d %B %Y',
|
||||
'Automatic actions' => 'Ações automáticas',
|
||||
'Your automatic action have been created successfully.' => 'Sua ação automética foi criada com sucesso.',
|
||||
'Unable to create your automatic action.' => 'Impossível criar sua ação automática.',
|
||||
|
@ -228,6 +222,7 @@ return array(
|
|||
'Action removed successfully.' => 'Ação removida com sucesso.',
|
||||
'Automatic actions for the project "%s"' => 'Ações automáticas para o projeto "%s"',
|
||||
'Defined actions' => 'Ações definidas',
|
||||
'Add an action' => 'Adicionar Ação',
|
||||
'Event name' => 'Nome do evento',
|
||||
'Action name' => 'Nome da ação',
|
||||
'Action parameters' => 'Parâmetros da ação',
|
||||
|
@ -250,138 +245,262 @@ return array(
|
|||
'Open a closed task' => 'Reabrir uma tarefa fechada',
|
||||
'Closing a task' => 'Fechando uma tarefa',
|
||||
'Assign a color to a specific user' => 'Designar uma cor para um usuário específico',
|
||||
// 'Column title' => '',
|
||||
// 'Position' => '',
|
||||
// 'Move Up' => '',
|
||||
// 'Move Down' => '',
|
||||
// 'Duplicate to another project' => '',
|
||||
// 'Duplicate' => '',
|
||||
// 'link' => '',
|
||||
// 'Update this comment' => '',
|
||||
// 'Comment updated successfully.' => '',
|
||||
// 'Unable to update your comment.' => '',
|
||||
// 'Remove a comment' => '',
|
||||
// 'Comment removed successfully.' => '',
|
||||
// 'Unable to remove this comment.' => '',
|
||||
// 'Do you really want to remove this comment?' => '',
|
||||
// 'Only administrators or the creator of the comment can access to this page.' => '',
|
||||
// 'Details' => '',
|
||||
// 'Current password for the user "%s"' => '',
|
||||
// 'The current password is required' => '',
|
||||
// 'Wrong password' => '',
|
||||
// 'Reset all tokens' => '',
|
||||
// 'All tokens have been regenerated.' => '',
|
||||
// 'Unknown' => '',
|
||||
// 'Last logins' => '',
|
||||
// 'Login date' => '',
|
||||
// 'Authentication method' => '',
|
||||
// 'IP address' => '',
|
||||
// 'User agent' => '',
|
||||
// 'Persistent connections' => '',
|
||||
// 'No session' => '',
|
||||
// 'Expiration date' => '',
|
||||
// 'Remember Me' => '',
|
||||
// 'Creation date' => '',
|
||||
// 'Filter by user' => '',
|
||||
// 'Filter by due date' => ',
|
||||
// 'Everybody' => '',
|
||||
// 'Open' => '',
|
||||
// 'Closed' => '',
|
||||
// 'Search' => '',
|
||||
// 'Nothing found.' => '',
|
||||
// 'Search in the project "%s"' => '',
|
||||
// 'Due date' => '',
|
||||
// 'Others formats accepted: %s and %s' => '',
|
||||
// 'Description' => '',
|
||||
// '%d comments' => '',
|
||||
// '%d comment' => '',
|
||||
// 'Email address invalid' => '',
|
||||
// 'Your Google Account is not linked anymore to your profile.' => '',
|
||||
// 'Unable to unlink your Google Account.' => '',
|
||||
// 'Google authentication failed' => '',
|
||||
// 'Unable to link your Google Account.' => '',
|
||||
// 'Your Google Account is linked to your profile successfully.' => '',
|
||||
// 'Email' => '',
|
||||
// 'Link my Google Account' => '',
|
||||
// 'Unlink my Google Account' => '',
|
||||
// 'Login with my Google Account' => '',
|
||||
// 'Project not found.' => '',
|
||||
// 'Task #%d' => '',
|
||||
// 'Task removed successfully.' => '',
|
||||
// 'Unable to remove this task.' => '',
|
||||
// 'Remove a task' => '',
|
||||
// 'Do you really want to remove this task: "%s"?' => '',
|
||||
// 'Assign automatically a color based on a category' => '',
|
||||
// 'Assign automatically a category based on a color' => '',
|
||||
// 'Task creation or modification' => '',
|
||||
// 'Category' => '',
|
||||
// 'Category:' => '',
|
||||
// 'Categories' => '',
|
||||
// 'Category not found.' => '',
|
||||
// 'Your category have been created successfully.' => '',
|
||||
// 'Unable to create your category.' => '',
|
||||
// 'Your category have been updated successfully.' => '',
|
||||
// 'Unable to update your category.' => '',
|
||||
// 'Remove a category' => '',
|
||||
// 'Category removed successfully.' => '',
|
||||
// 'Unable to remove this category.' => '',
|
||||
// 'Category modification for the project "%s"' => '',
|
||||
// 'Category Name' => '',
|
||||
// 'Categories for the project "%s"' => '',
|
||||
// 'Add a new category' => '',
|
||||
// 'Do you really want to remove this category: "%s"?' => '',
|
||||
// 'Filter by category' => '',
|
||||
// 'All categories' => '',
|
||||
// 'No category' => '',
|
||||
// 'The name is required' => '',
|
||||
// 'Remove a file' => '',
|
||||
// 'Unable to remove this file.' => '',
|
||||
// 'File removed successfully.' => '',
|
||||
// 'Attach a document' => '',
|
||||
// 'Do you really want to remove this file: "%s"?' => '',
|
||||
// 'open' => '',
|
||||
// 'Attachments' => '',
|
||||
// 'Edit the task' => '',
|
||||
// 'Edit the description' => '',
|
||||
// 'Add a comment' => '',
|
||||
// 'Edit a comment' => '',
|
||||
// 'Summary' => '',
|
||||
// 'Time tracking' => '',
|
||||
// 'Estimate:' => '',
|
||||
// 'Spent:' => '',
|
||||
// 'Do you really want to remove this sub-task?' => '',
|
||||
// 'Remaining:' => '',
|
||||
// 'hours' => '',
|
||||
// 'spent' => '',
|
||||
// 'estimated' => '',
|
||||
// 'Sub-Tasks' => '',
|
||||
// 'Add a sub-task' => '',
|
||||
// 'Original Estimate' => '',
|
||||
// 'Create another sub-task' => '',
|
||||
// 'Time Spent' => '',
|
||||
// 'Edit a sub-task' => '',
|
||||
// 'Remove a sub-task' => '',
|
||||
// 'The time must be a numeric value' => '',
|
||||
// 'Todo' => '',
|
||||
// 'In progress' => '',
|
||||
// 'Done' => '',
|
||||
// 'Sub-task removed successfully.' => '',
|
||||
// 'Unable to remove this sub-task.' => '',
|
||||
// 'Sub-task updated successfully.' => '',
|
||||
// 'Unable to update your sub-task.' => '',
|
||||
// 'Unable to create your sub-task.' => '',
|
||||
// 'Sub-task added successfully.' => '',
|
||||
// 'Maximum size: ' => '',
|
||||
// 'Unable to upload the file.' => '',
|
||||
// 'Display another project' => '',
|
||||
// 'Your GitHub account was successfully linked to your profile.' => '',
|
||||
// 'Unable to link your GitHub Account.' => '',
|
||||
// 'GitHub authentication failed' => '',
|
||||
// 'Your GitHub account is no longer linked to your profile.' => '',
|
||||
// 'Unable to unlink your GitHub Account.' => '',
|
||||
// 'Login with my GitHub Account' => '',
|
||||
// 'Link my GitHub Account' => '',
|
||||
// 'Unlink my GitHub Account' => '',
|
||||
// 'Created by %s' => 'Créé par %s',
|
||||
// 'Last modified on %B %e, %G at %k:%M %p' => '',
|
||||
'Column title' => 'Título da coluna',
|
||||
'Position' => 'Posição',
|
||||
'Move Up' => 'Mover para cima',
|
||||
'Move Down' => 'Mover para baixo',
|
||||
'Duplicate to another project' => 'Duplicar para outro projeto',
|
||||
'Duplicate' => 'Duplicar',
|
||||
'link' => 'link',
|
||||
'Update this comment' => 'Atualizar este comentário',
|
||||
'Comment updated successfully.' => 'Comentário atualizado com sucesso.',
|
||||
'Unable to update your comment.' => 'Impossível atualizar seu comentário.',
|
||||
'Remove a comment' => 'Remover um comentário.',
|
||||
'Comment removed successfully.' => 'Comentário removido com sucesso.',
|
||||
'Unable to remove this comment.' => 'Impossível remover este comentário',
|
||||
'Do you really want to remove this comment?' => 'Você tem certeza de que quer remover este comentário?',
|
||||
'Only administrators or the creator of the comment can access to this page.' => 'Somente administradores ou o criator deste comentário tem acesso a esta página.',
|
||||
'Details' => 'Detalhes',
|
||||
'Current password for the user "%s"' => 'Senha atual para o usuário "%s"',
|
||||
'The current password is required' => 'A senha atual é obrigatória',
|
||||
'Wrong password' => 'Senha errada',
|
||||
'Reset all tokens' => 'Reiniciar todos os tokens',
|
||||
'All tokens have been regenerated.' => 'Todos os tokens foram gerados novamente',
|
||||
'Unknown' => 'Desconhecido',
|
||||
'Last logins' => 'Últimos logins',
|
||||
'Login date' => 'Data de login',
|
||||
'Authentication method' => 'Método de autenticação',
|
||||
'IP address' => 'Endereço IP',
|
||||
'User agent' => 'Agente usuário',
|
||||
'Persistent connections' => 'Conexões persistentes',
|
||||
'No session.' => 'Sem sessão.',
|
||||
'Expiration date' => 'Data de expiração',
|
||||
'Remember Me' => 'Lembre-se de mim',
|
||||
'Creation date' => 'Data de criação',
|
||||
'Filter by user' => 'Filtrar por usuário',
|
||||
'Filter by due date' => 'Filtrar por data de vencimento',
|
||||
'Everybody' => 'Todos',
|
||||
'Open' => 'Abrir',
|
||||
'Closed' => 'Fechado',
|
||||
'Search' => 'Pesquisar',
|
||||
'Nothing found.' => 'Não encontrado.',
|
||||
'Search in the project "%s"' => 'Procure no projeto "%s"',
|
||||
'Due date' => 'Data de vencimento',
|
||||
'Others formats accepted: %s and %s' => 'Outros formatos permitidos: %s e %s',
|
||||
'Description' => 'Descrição',
|
||||
'%d comments' => '%d comentários',
|
||||
'%d comment' => '%d comentário',
|
||||
'Email address invalid' => 'Endereço de e-mail inválido',
|
||||
'Your Google Account is not linked anymore to your profile.' => 'Sua conta Google não está mais associada ao seu perfil.',
|
||||
'Unable to unlink your Google Account.' => 'Impossível desassociar sua conta Google.',
|
||||
'Google authentication failed' => 'Autenticação do Google falhou.',
|
||||
'Unable to link your Google Account.' => 'Impossível associar a sua conta do Google.',
|
||||
'Your Google Account is linked to your profile successfully.' => 'Sua Conta do Google está ligada ao seu perfil com sucesso.',
|
||||
'Email' => 'E-mail',
|
||||
'Link my Google Account' => 'Vincular minha conta Google',
|
||||
'Unlink my Google Account' => 'Desvincular minha conta do Google',
|
||||
'Login with my Google Account' => 'Entrar com minha conta do Google',
|
||||
'Project not found.' => 'Projeto não encontrado.',
|
||||
'Task #%d' => 'Tarefa #%d',
|
||||
'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"',
|
||||
'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',
|
||||
'Category' => 'Categoria',
|
||||
'Category:' => 'Categoria:',
|
||||
'Categories' => 'Categorias',
|
||||
'Category not found.' => 'Categoria não encontrada.',
|
||||
'Your category have been created successfully.' => 'Seu categoria foi criada com sucesso.',
|
||||
'Unable to create your category.' => 'Não é possível criar sua categoria.',
|
||||
'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.',
|
||||
'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',
|
||||
'Categories for the project "%s"' => 'Categorias para o projeto "%s"',
|
||||
'Add a new category' => 'Adicionar uma nova categoria',
|
||||
'Do you really want to remove this category: "%s"?' => 'Você realmente deseja remover esta categoria: "%s"',
|
||||
'Filter by category' => 'Filtrar por categoria',
|
||||
'All categories' => 'Todas as categorias',
|
||||
'No category' => 'Sem categoria',
|
||||
'The name is required' => 'O nome é obrigatório',
|
||||
'Remove a file' => 'Remover um arquivo',
|
||||
'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"',
|
||||
'open' => 'Aberto',
|
||||
'Attachments' => 'Anexos',
|
||||
'Edit the task' => 'Editar a tarefa',
|
||||
'Edit the description' => 'Editar a descrição',
|
||||
'Add a comment' => 'Adicionar um comentário',
|
||||
'Edit a comment' => 'Editar um comentário',
|
||||
'Summary' => 'Resumo',
|
||||
'Time tracking' => 'Rastreamento de tempo',
|
||||
'Estimate:' => 'Estimado:',
|
||||
'Spent:' => 'Gasto:',
|
||||
'Do you really want to remove this sub-task?' => 'Você realmente deseja remover esta sub-tarefa?',
|
||||
'Remaining:' => 'Restante:',
|
||||
'hours' => 'horas',
|
||||
'spent' => 'gasto',
|
||||
'estimated' => 'estimada',
|
||||
'Sub-Tasks' => 'Sub-tarefas',
|
||||
'Add a sub-task' => 'Adicionar uma sub-tarefa',
|
||||
'Original Estimate' => 'Estimativa original',
|
||||
'Create another sub-task' => 'Criar uma outra sub-tarefa',
|
||||
'Time Spent' => 'Tempo gasto',
|
||||
'Edit a sub-task' => 'Editar uma sub-tarefa',
|
||||
'Remove a sub-task' => 'Remover uma sub-tarefa',
|
||||
'The time must be a numeric value' => 'O tempo deve ser um valor numérico',
|
||||
'Todo' => 'A fazer',
|
||||
'In progress' => 'Em andamento',
|
||||
'Sub-task removed successfully.' => 'Sub-tarefa removido com sucesso.',
|
||||
'Unable to remove this sub-task.' => 'Não foi possível remover esta sub-tarefa.',
|
||||
'Sub-task updated successfully.' => 'Sub-tarefa atualizada com sucesso.',
|
||||
'Unable to update your sub-task.' => 'Não foi possível atualizar sua sub-tarefa.',
|
||||
'Unable to create your sub-task.' => 'Não é possível criar sua sub-tarefa.',
|
||||
'Sub-task added successfully.' => 'Sub-tarefa adicionada com sucesso.',
|
||||
'Maximum size: ' => 'O tamanho máximo:',
|
||||
'Unable to upload the file.' => 'Não foi possível carregar o arquivo.',
|
||||
'Display another project' => 'Mostrar um outro projeto',
|
||||
'Your GitHub account was successfully linked to your profile.' => 'A sua conta GitHub foi ligada com sucesso ao seu perfil.',
|
||||
'Unable to link your GitHub Account.' => 'Não foi possível vincular sua conta GitHub.',
|
||||
'GitHub authentication failed' => 'Falhou autenticação GitHub',
|
||||
'Your GitHub account is no longer linked to your profile.' => 'A sua conta GitHub já não está ligada ao seu perfil.',
|
||||
'Unable to unlink your GitHub Account.' => 'Não foi possível desvincular sua conta GitHub.',
|
||||
'Login with my GitHub Account' => 'Entrar com minha conta do GitHub',
|
||||
'Link my GitHub Account' => 'Vincular minha conta GitHub',
|
||||
'Unlink my GitHub Account' => 'Desvincular minha conta do GitHub',
|
||||
'Created by %s' => 'Criado por %s',
|
||||
'Last modified on %B %e, %Y at %k:%M %p' => 'Última modificação em %B %e, %Y às %k: %M %p',
|
||||
'Tasks Export' => 'Tarefas Export',
|
||||
'Tasks exportation for "%s"' => 'Tarefas exportação para "%s"',
|
||||
'Start Date' => 'Data inicial',
|
||||
'End Date' => 'Data final',
|
||||
'Execute' => 'Executar',
|
||||
'Task Id' => 'Id da Tarefa',
|
||||
'Creator' => 'Criador',
|
||||
'Modification date' => 'Data de modificação',
|
||||
'Completion date' => 'Data de conclusão',
|
||||
'Webhook URL for task creation' => 'Webhook URL para criação de tarefas',
|
||||
'Webhook URL for task modification' => 'Webhook URL para modificação tarefa',
|
||||
'Clone' => 'Clone',
|
||||
'Clone Project' => 'Clonar Projeto',
|
||||
'Project cloned successfully.' => 'Projeto clonado com sucesso.',
|
||||
'Unable to clone this project.' => 'Impossível clonar este projeto.',
|
||||
// 'Email notifications' => '',
|
||||
// 'Enable email notifications' => '',
|
||||
// 'Task position:' => '',
|
||||
// 'The task #%d have been opened.' => '',
|
||||
// 'The task #%d have been closed.' => '',
|
||||
// 'Sub-task updated' => '',
|
||||
// 'Title:' => '',
|
||||
// 'Status:' => '',
|
||||
// 'Assignee:' => '',
|
||||
// 'Time tracking:' => '',
|
||||
// 'New sub-task' => '',
|
||||
// 'New attachment added "%s"' => '',
|
||||
// 'Comment updated' => '',
|
||||
// 'New comment posted by %s' => '',
|
||||
// 'List of due tasks for the project "%s"' => '',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
// '[%s][New subtask] %s (#%d)' => '',
|
||||
// '[%s][Subtask updated] %s (#%d)' => '',
|
||||
// '[%s][New task] %s (#%d)' => '',
|
||||
// '[%s][Task updated] %s (#%d)' => '',
|
||||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
// '[Kanboard] Notification' => '',
|
||||
// 'I want to receive notifications only for those projects:' => '',
|
||||
// 'view the task on Kanboard' => '',
|
||||
// 'Public access' => '',
|
||||
// 'Categories management' => '',
|
||||
// 'Users management' => '',
|
||||
// 'Active tasks' => '',
|
||||
// 'Disable public access' => '',
|
||||
// 'Enable public access' => '',
|
||||
// 'Active projects' => '',
|
||||
// 'Inactive projects' => '',
|
||||
// 'Public access disabled' => '',
|
||||
// 'Do you really want to disable this project: "%s"?' => '',
|
||||
// 'Do you really want to duplicate this project: "%s"?' => '',
|
||||
// 'Do you really want to enable this project: "%s"?' => '',
|
||||
// 'Project activation' => '',
|
||||
// 'Move the task to another project' => '',
|
||||
// 'Move to another project' => '',
|
||||
// 'Do you really want to duplicate this task?' => '',
|
||||
// 'Duplicate a task' => '',
|
||||
// 'External accounts' => '',
|
||||
// 'Account type' => '',
|
||||
// 'Local' => '',
|
||||
// 'Remote' => '',
|
||||
// 'Enabled' => '',
|
||||
// 'Disabled' => '',
|
||||
// 'Google account linked' => '',
|
||||
// 'Github account linked' => '',
|
||||
// 'Username:' => '',
|
||||
// 'Name:' => '',
|
||||
// 'Email:' => '',
|
||||
// 'Default project:' => '',
|
||||
// 'Notifications:' => '',
|
||||
// 'Group:' => '',
|
||||
// 'Regular user' => '',
|
||||
// 'Account type:' => '',
|
||||
// 'Edit profile' => '',
|
||||
// 'Change password' => '',
|
||||
// 'Password modification' => '',
|
||||
// 'External authentications' => '',
|
||||
// 'Google Account' => '',
|
||||
// 'Github Account' => '',
|
||||
// 'Never connected.' => '',
|
||||
// 'No account linked.' => '',
|
||||
// 'Account linked.' => '',
|
||||
// 'No external authentication enabled.' => '',
|
||||
// 'Password modified successfully.' => '',
|
||||
// 'Unable to change the password.' => '',
|
||||
// 'Change category for the task "%s"' => '',
|
||||
// 'Change category' => '',
|
||||
// '%s updated the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s open the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the position #%d in the column "%s"' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the column "%s"' => '',
|
||||
// '%s created the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s closed the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s created a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s updated a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// 'Assigned to %s with an estimate of %s/%sh' => '',
|
||||
// 'Not assigned, estimate of %sh' => '',
|
||||
// '%s updated a comment on the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s commented the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s\'s activity' => '',
|
||||
// 'No activity.' => '',
|
||||
// 'RSS feed' => '',
|
||||
// '%s updated a comment on the task #%d' => '',
|
||||
// '%s commented on the task #%d' => '',
|
||||
// '%s updated a subtask for the task #%d' => '',
|
||||
// '%s created a subtask for the task #%d' => '',
|
||||
// '%s updated the task #%d' => '',
|
||||
// '%s created the task #%d' => '',
|
||||
// '%s closed the task #%d' => '',
|
||||
// '%s open the task #%d' => '',
|
||||
// '%s moved the task #%d to the column "%s"' => '',
|
||||
// '%s moved the task #%d to the position %d in the column "%s"' => '',
|
||||
// 'Activity' => '',
|
||||
// 'Default values are "%s"' => '',
|
||||
// 'Default columns for new projects (Comma-separated)' => '',
|
||||
// 'Task assignee change' => '',
|
||||
// '%s change the assignee of the task #%d' => '',
|
||||
// '%s change the assignee of the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '[%s][Column Change] %s (#%d)' => '',
|
||||
// '[%s][Position Change] %s (#%d)' => '',
|
||||
// '[%s][Assignee Change] %s (#%d)' => '',
|
||||
// 'New password for the user "%s"' => '',
|
||||
);
|
||||
|
|
506
sources/app/Locales/ru_RU/translations.php
Normal file
506
sources/app/Locales/ru_RU/translations.php
Normal file
|
@ -0,0 +1,506 @@
|
|||
<?php
|
||||
|
||||
return array(
|
||||
'None' => 'Отсутствует',
|
||||
'edit' => 'изменить',
|
||||
'Edit' => 'Изменить',
|
||||
'remove' => 'удалить',
|
||||
'Remove' => 'Удалить',
|
||||
'Update' => 'Обновить',
|
||||
'Yes' => 'Да',
|
||||
'No' => 'Нет',
|
||||
'cancel' => 'Отменить',
|
||||
'or' => 'или',
|
||||
'Yellow' => 'Желтый',
|
||||
'Blue' => 'Синий',
|
||||
'Green' => 'Зеленый',
|
||||
'Purple' => 'Фиолетовый',
|
||||
'Red' => 'Красный',
|
||||
'Orange' => 'Оранжевый',
|
||||
'Grey' => 'Серый',
|
||||
'Save' => 'Сохранить',
|
||||
'Login' => 'Вход',
|
||||
'Official website:' => 'Официальный сайт :',
|
||||
'Unassigned' => 'Не назначена',
|
||||
'View this task' => 'Посмотреть задачу',
|
||||
'Remove user' => 'Удалить пользователя',
|
||||
'Do you really want to remove this user: "%s"?' => 'Вы точно хотите удалить пользователя: « %s » ?',
|
||||
'New user' => 'Новый пользователь',
|
||||
'All users' => 'Все пользователи',
|
||||
'Username' => 'Имя пользователя',
|
||||
'Password' => 'Пароль',
|
||||
'Default project' => 'Проект по умолчанию',
|
||||
'Administrator' => 'Администратор',
|
||||
'Sign in' => 'Войти',
|
||||
'Users' => 'Пользователи',
|
||||
'No user' => 'Нет пользователя',
|
||||
'Forbidden' => 'Запрещено',
|
||||
'Access Forbidden' => 'Доступ запрещен',
|
||||
'Only administrators can access to this page.' => 'Только администраторы могут войти на эту страницу.',
|
||||
'Edit user' => 'Изменить пользователя',
|
||||
'Logout' => 'Выйти',
|
||||
'Bad username or password' => 'Неверное имя пользователя или пароль',
|
||||
'users' => 'пользователи',
|
||||
'projects' => 'проекты',
|
||||
'Edit project' => 'Изменить проект',
|
||||
'Name' => 'Имя',
|
||||
'Activated' => 'Активен',
|
||||
'Projects' => 'Проекты',
|
||||
'No project' => 'Нет проекта',
|
||||
'Project' => 'Проект',
|
||||
'Status' => 'Статус',
|
||||
'Tasks' => 'Задачи',
|
||||
'Board' => 'Доска',
|
||||
'Actions' => 'Действия',
|
||||
'Inactive' => 'Неактивен',
|
||||
'Active' => 'Активен',
|
||||
'Column %d' => 'Колонка %d',
|
||||
'Add this column' => 'Добавить колонку',
|
||||
'%d tasks on the board' => 'Задач на доске - %d',
|
||||
'%d tasks in total' => 'Задач всего - %d',
|
||||
'Unable to update this board.' => 'Не удалось обновить доску.',
|
||||
'Edit board' => 'Изменить доски',
|
||||
'Disable' => 'Деактивировать',
|
||||
'Enable' => 'Активировать',
|
||||
'New project' => 'Новый проект',
|
||||
'Do you really want to remove this project: "%s"?' => 'Вы точно хотите удалить этот проект? : « %s » ?',
|
||||
'Remove project' => 'Удалить проект',
|
||||
'Boards' => 'Доски',
|
||||
'Edit the board for "%s"' => 'Изменить доску для « %s »',
|
||||
'All projects' => 'Все проекты',
|
||||
'Change columns' => 'Изменить колонки',
|
||||
'Add a new column' => 'Добавить новую колонку',
|
||||
'Title' => 'Название',
|
||||
'Add Column' => 'Добавить колонку',
|
||||
'Project "%s"' => 'Проект « %s »',
|
||||
'Nobody assigned' => 'Никто не назначен',
|
||||
'Assigned to %s' => 'Исполнитель: %s',
|
||||
'Remove a column' => 'Удалить колонку',
|
||||
'Remove a column from a board' => 'Удалить колонку с доски',
|
||||
'Unable to remove this column.' => 'Не удалось удалить колонку.',
|
||||
'Do you really want to remove this column: "%s"?' => 'Вы точно хотите удалить эту колонку : « %s » ?',
|
||||
'This action will REMOVE ALL TASKS associated to this column!' => 'Вы УДАЛИТЕ ВСЕ ЗАДАЧИ находящиеся в этой колонке !',
|
||||
'Settings' => 'Настройки',
|
||||
'Application settings' => 'Настройки приложения',
|
||||
'Language' => 'Язык',
|
||||
'Webhooks token:' => 'Webhooks токен :',
|
||||
'API token:' => 'API токен :',
|
||||
'More information' => 'Подробнее',
|
||||
'Database size:' => 'Размер базы данных :',
|
||||
'Download the database' => 'Скачать базу данных',
|
||||
'Optimize the database' => 'Оптимизировать базу данных',
|
||||
'(VACUUM command)' => '(Команда VACUUM)',
|
||||
'(Gzip compressed Sqlite file)' => '(Сжать GZip файл SQLite)',
|
||||
'User settings' => 'Настройки пользователя',
|
||||
'My default project:' => 'Мой проект по умолчанию : ',
|
||||
'Close a task' => 'Закрыть задачу',
|
||||
'Do you really want to close this task: "%s"?' => 'Вы точно хотите закрыть задачу : « %s » ?',
|
||||
'Edit a task' => 'Изменить задачу',
|
||||
'Column' => 'Колонка',
|
||||
'Color' => 'Цвет',
|
||||
'Assignee' => 'Назначена',
|
||||
'Create another task' => 'Создать другую задачу',
|
||||
'New task' => 'Новая задача',
|
||||
'Open a task' => 'Открыть задачу',
|
||||
'Do you really want to open this task: "%s"?' => 'Вы уверены что хотите открыть задачу : « %s » ?',
|
||||
'Back to the board' => 'Вернуться на доску',
|
||||
'Created on %B %e, %Y at %k:%M %p' => 'Создано %d/%m/%Y в %H:%M',
|
||||
'There is nobody assigned' => 'Никто не назначен',
|
||||
'Column on the board:' => 'Колонка на доске : ',
|
||||
'Status is open' => 'Статус - открыт',
|
||||
'Status is closed' => 'Статус - закрыт',
|
||||
'Close this task' => 'Закрыть эту задачу',
|
||||
'Open this task' => 'Открыть эту задачу',
|
||||
'There is no description.' => 'Нет описания.',
|
||||
'Add a new task' => 'Добавить новую задачу',
|
||||
'The username is required' => 'Требуется имя пользователя',
|
||||
'The maximum length is %d characters' => 'Максимальная длина - %d знаков',
|
||||
'The minimum length is %d characters' => 'Минимальная длина - %d знаков',
|
||||
'The password is required' => 'Требуется пароль',
|
||||
'This value must be an integer' => 'Это значение должно быть целым',
|
||||
'The username must be unique' => 'Требуется уникальное имя пользователя',
|
||||
'The username must be alphanumeric' => 'Имя пользователя должно быть букво-цифровым',
|
||||
'The user id is required' => 'Требуется ID пользователя',
|
||||
'Passwords don\'t match' => 'Пароли не совпадают',
|
||||
'The confirmation is required' => 'Требуется подтверждение',
|
||||
'The column is required' => 'Требуется колонка',
|
||||
'The project is required' => 'Требуется проект',
|
||||
'The color is required' => 'Требуется цвет',
|
||||
'The id is required' => 'Требуется ID',
|
||||
'The project id is required' => 'Требуется ID проекта',
|
||||
'The project name is required' => 'Требуется имя проекта',
|
||||
'This project must be unique' => 'Проект должен быть уникальным',
|
||||
'The title is required' => 'Требуется заголовок',
|
||||
'The language is required' => 'Требуется язык',
|
||||
'There is no active project, the first step is to create a new project.' => 'Нет активного проекта, сначала создайте новый проект.',
|
||||
'Settings saved successfully.' => 'Параметры успешно сохранены.',
|
||||
'Unable to save your settings.' => 'Невозможно сохранить параметры.',
|
||||
'Database optimization done.' => 'База данных оптимизирована.',
|
||||
'Your project have been created successfully.' => 'Ваш проект успешно создан.',
|
||||
'Unable to create your project.' => 'Не удалось создать проект.',
|
||||
'Project updated successfully.' => 'Проект успешно обновлен.',
|
||||
'Unable to update this project.' => 'Не удалось обновить проект.',
|
||||
'Unable to remove this project.' => 'Не удалось удалить проект.',
|
||||
'Project removed successfully.' => 'Проект удален.',
|
||||
'Project activated successfully.' => 'Проект активирован.',
|
||||
'Unable to activate this project.' => 'Невозможно активировать проект.',
|
||||
'Project disabled successfully.' => 'Проект успешно выключен.',
|
||||
'Unable to disable this project.' => 'Не удалось выключить проект.',
|
||||
'Unable to open this task.' => 'Не удалось открыть задачу.',
|
||||
'Task opened successfully.' => 'Задача открыта.',
|
||||
'Unable to close this task.' => 'Не удалось закрыть задачу.',
|
||||
'Task closed successfully.' => 'Задача закрыта.',
|
||||
'Unable to update your task.' => 'Не удалось обновить вашу задачу.',
|
||||
'Task updated successfully.' => 'Задача обновлена.',
|
||||
'Unable to create your task.' => 'Не удалось создать задачу.',
|
||||
'Task created successfully.' => 'Задача создана.',
|
||||
'User created successfully.' => 'Пользователь создан.',
|
||||
'Unable to create your user.' => 'Не удалось создать пользователя.',
|
||||
'User updated successfully.' => 'Пользователь обновлен.',
|
||||
'Unable to update your user.' => 'Не удалось обновить пользователя.',
|
||||
'User removed successfully.' => 'Пользователь удален.',
|
||||
'Unable to remove this user.' => 'Не удалось удалить пользователя.',
|
||||
'Board updated successfully.' => 'Доска обновлена.',
|
||||
'Ready' => 'Готовые',
|
||||
'Backlog' => 'Ожидающие',
|
||||
'Work in progress' => 'В процессе',
|
||||
'Done' => 'Завершенные',
|
||||
'Application version:' => 'Версия приложения :',
|
||||
'Completed on %B %e, %Y at %k:%M %p' => 'Завершен %d/%m/%Y в %H:%M',
|
||||
'%B %e, %Y at %k:%M %p' => '%d/%m/%Y в %H:%M',
|
||||
'Date created' => 'Дата создания',
|
||||
'Date completed' => 'Дата завершения',
|
||||
'Id' => 'ID',
|
||||
'No task' => 'Нет задачи',
|
||||
'Completed tasks' => 'Завершенные задачи',
|
||||
'List of projects' => 'Список проектов',
|
||||
'Completed tasks for "%s"' => 'Задачи завершенные для « %s »',
|
||||
'%d closed tasks' => '%d завершенных задач',
|
||||
'No task for this project' => 'нет задач для этого проекта',
|
||||
'Public link' => 'Ссылка для просмотра',
|
||||
'There is no column in your project!' => 'Нет колонки в вашем проекте !',
|
||||
'Change assignee' => 'Сменить назначенного',
|
||||
'Change assignee for the task "%s"' => 'Сменить назначенного для задачи « %s »',
|
||||
'Timezone' => 'Часовой пояс',
|
||||
'Sorry, I didn\'t found this information in my database!' => 'К сожалению, информация в базе данных не найдена !',
|
||||
'Page not found' => 'Страница не найдена',
|
||||
'Complexity' => 'Сложность',
|
||||
'limit' => 'лимит',
|
||||
'Task limit' => 'Лимит задач',
|
||||
'This value must be greater than %d' => 'Это значение должно быть больше %d',
|
||||
'Edit project access list' => 'Изменить доступ к проекту',
|
||||
'Edit users access' => 'Изменить доступ пользователей',
|
||||
'Allow this user' => 'Разрешить этого пользователя',
|
||||
'Only those users have access to this project:' => 'Только эти пользователи имеют доступ к проекту :',
|
||||
'Don\'t forget that administrators have access to everything.' => 'Помните, администратор имеет доступ всюду.',
|
||||
'revoke' => 'отозвать',
|
||||
'List of authorized users' => 'Список авторизованных пользователей',
|
||||
'User' => 'Пользователь',
|
||||
'Everybody have access to this project.' => 'Кто угодно имеет доступ к этому проекту.',
|
||||
'You are not allowed to access to this project.' => 'Вам запрешен доступ к этому проекту.',
|
||||
'Comments' => 'Комментарии',
|
||||
'Post comment' => 'Оставить комментарий',
|
||||
'Write your text in Markdown' => 'Справка по синтаксису Markdown',
|
||||
'Leave a comment' => 'Оставить комментарий 2',
|
||||
'Comment is required' => 'Нужен комментарий',
|
||||
'Leave a description' => 'Оставьте описание',
|
||||
'Comment added successfully.' => 'Комментарий успешно добавлен.',
|
||||
'Unable to create your comment.' => 'Невозможно создать комментарий.',
|
||||
'The description is required' => 'Требуется описание',
|
||||
'Edit this task' => 'Изменить задачу',
|
||||
'Due Date' => 'Срок',
|
||||
'm/d/Y' => 'м/д/Г',
|
||||
'month/day/year' => 'месяц/день/год',
|
||||
'Invalid date' => 'Неверная дата',
|
||||
'Must be done before %B %e, %Y' => 'Должно быть сделано до %d/%m/%Y',
|
||||
'%B %e, %Y' => '%d/%m/%Y',
|
||||
'Automatic actions' => 'Автоматические действия',
|
||||
'Your automatic action have been created successfully.' => 'Автоматика настроена.',
|
||||
'Unable to create your automatic action.' => 'Не удалось создать автоматизированное действие.',
|
||||
'Remove an action' => 'Удалить действие',
|
||||
'Unable to remove this action.' => 'Не удалось удалить действие',
|
||||
'Action removed successfully.' => 'Действие удалено.',
|
||||
'Automatic actions for the project "%s"' => 'Автоматические действия для проекта « %s »',
|
||||
'Defined actions' => 'Заданные действия',
|
||||
'Add an action' => 'Добавить действие',
|
||||
'Event name' => 'Имя события',
|
||||
'Action name' => 'Имя действия',
|
||||
'Action parameters' => 'Параметры действия',
|
||||
'Action' => 'Действие',
|
||||
'Event' => 'Событие',
|
||||
'When the selected event occurs execute the corresponding action.' => 'Когда случится ВЫБРАННОЕ событие выполняется СООТВЕТСТВУЮЩЕЕ действие.',
|
||||
'Next step' => 'Следующий шаг',
|
||||
'Define action parameters' => 'Задать параметры действия',
|
||||
'Save this action' => 'Сохранить это действие',
|
||||
'Do you really want to remove this action: "%s"?' => 'Вы точно хотите удалить это действие: « %s » ?',
|
||||
'Remove an automatic action' => 'Удалить автоматическое действие',
|
||||
'Close the task' => 'Закрыть задачу',
|
||||
'Assign the task to a specific user' => 'Назначить задачу определенному пользователю',
|
||||
'Assign the task to the person who does the action' => 'Назначить задачу тому кто выполнит действие',
|
||||
'Duplicate the task to another project' => 'Создать дубликат задачи в другом проекте',
|
||||
'Move a task to another column' => 'Переместить задачу в другую колонку',
|
||||
'Move a task to another position in the same column' => 'Переместить задачу в другое место этой же колонки',
|
||||
'Task modification' => 'Изменение задачи',
|
||||
'Task creation' => 'Создание задачи',
|
||||
'Open a closed task' => 'Открыть завершенную задачу',
|
||||
'Closing a task' => 'Завершение задачи',
|
||||
'Assign a color to a specific user' => 'Назначить определенный цвет пользователю',
|
||||
'Column title' => 'Название колонки',
|
||||
'Position' => 'Расположение',
|
||||
'Move Up' => 'Сдвинуть вверх',
|
||||
'Move Down' => 'Сдвинуть вниз',
|
||||
'Duplicate to another project' => 'Клонировать в другой проект',
|
||||
'Duplicate' => 'Клонировать',
|
||||
'link' => 'связь',
|
||||
'Update this comment' => 'Обновить комментарий',
|
||||
'Comment updated successfully.' => 'Комментарий обновлен.',
|
||||
'Unable to update your comment.' => 'Не удалось обновить ваш комментарий.',
|
||||
'Remove a comment' => 'Удалить комментарий',
|
||||
'Comment removed successfully.' => 'Комментарий удален.',
|
||||
'Unable to remove this comment.' => 'Не удалось удалить этот комментарий.',
|
||||
'Do you really want to remove this comment?' => 'Вы точно хотите удалить этот комментарий ?',
|
||||
'Only administrators or the creator of the comment can access to this page.' => 'Только администратор или автор комментарий могут получить доступ.',
|
||||
'Details' => 'Подробности',
|
||||
'Current password for the user "%s"' => 'Текущий пароль для пользователя « %s »',
|
||||
'The current password is required' => 'Требуется текущий пароль',
|
||||
'Wrong password' => 'Неверный пароль',
|
||||
'Reset all tokens' => 'Сброс всех токенов',
|
||||
'All tokens have been regenerated.' => 'Все токены пересозданы.',
|
||||
'Unknown' => 'Неизвестно',
|
||||
'Last logins' => 'Последние посещения',
|
||||
'Login date' => 'Дата входа',
|
||||
'Authentication method' => 'Способ аутентификации',
|
||||
'IP address' => 'IP адрес',
|
||||
'User agent' => 'User agent',
|
||||
'Persistent connections' => 'Постоянные соединения',
|
||||
'No session.' => 'Нет сеанса',
|
||||
'Expiration date' => 'Дата окончания',
|
||||
'Remember Me' => 'Запомнить меня',
|
||||
'Creation date' => 'Дата создания',
|
||||
'Filter by user' => 'Фильтр по пользователям',
|
||||
'Filter by due date' => 'Фильтр по сроку',
|
||||
'Everybody' => 'Все',
|
||||
'Open' => 'Открытый',
|
||||
'Closed' => 'Закрытый',
|
||||
'Search' => 'Поиск',
|
||||
'Nothing found.' => 'Ничего не найдено.',
|
||||
'Search in the project "%s"' => 'Искать в проекте « %s »',
|
||||
'Due date' => 'Срок',
|
||||
'Others formats accepted: %s and %s' => 'Другой формат приемлем : %s и %s',
|
||||
'Description' => 'Описание',
|
||||
'%d comments' => '%d комментариев',
|
||||
'%d comment' => '%d комментарий',
|
||||
'Email address invalid' => 'Adresse email invalide',
|
||||
'Your Google Account is not linked anymore to your profile.' => 'Ваш аккаунт в Google больше не привязан к вашему профилю.',
|
||||
'Unable to unlink your Google Account.' => 'Не удалось отвязать ваш профиль от Google.',
|
||||
'Google authentication failed' => 'Аутентификация Google не удалась',
|
||||
'Unable to link your Google Account.' => 'Не удалось привязать ваш профиль к Google.',
|
||||
'Your Google Account is linked to your profile successfully.' => 'Ваш профиль успешно привязан к Google.',
|
||||
'Email' => 'Email',
|
||||
'Link my Google Account' => 'Привязать мой профиль к Google',
|
||||
'Unlink my Google Account' => 'Отвязать мой профиль от Google',
|
||||
'Login with my Google Account' => 'Аутентификация через Google',
|
||||
'Project not found.' => 'Проект не найден.',
|
||||
'Task #%d' => 'Задача n°%d',
|
||||
'Task removed successfully.' => 'Задача удалена.',
|
||||
'Unable to remove this task.' => 'Не удалось удалить эту задачу.',
|
||||
'Remove a task' => 'Удалить задачу',
|
||||
'Do you really want to remove this task: "%s"?' => 'Вы точно хотите удалить эту задачу « %s » ?',
|
||||
'Assign automatically a color based on a category' => 'Автоматически назначать цвет по категории',
|
||||
'Assign automatically a category based on a color' => 'Автоматически назначать категорию по цвету ',
|
||||
'Task creation or modification' => 'Создание или изменение задачи',
|
||||
'Category' => 'Категория',
|
||||
'Category:' => 'Категория :',
|
||||
'Categories' => 'Категории',
|
||||
'Category not found.' => 'Категория не найдена',
|
||||
'Your category have been created successfully.' => 'Категория создана.',
|
||||
'Unable to create your category.' => 'Не удалось создать категорию.',
|
||||
'Your category have been updated successfully.' => 'Категория обновлена.',
|
||||
'Unable to update your category.' => 'Не удалось обновить категорию.',
|
||||
'Remove a category' => 'Удалить категорию',
|
||||
'Category removed successfully.' => 'Категория удалена.',
|
||||
'Unable to remove this category.' => 'Не удалось удалить категорию.',
|
||||
'Category modification for the project "%s"' => 'Изменение категории для проекта « %s »',
|
||||
'Category Name' => 'Название категории',
|
||||
'Categories for the project "%s"' => 'Категории для проекта « %s »',
|
||||
'Add a new category' => 'Добавить новую категорию',
|
||||
'Do you really want to remove this category: "%s"?' => 'Вы точно хотите удалить категорию « %s » ?',
|
||||
'Filter by category' => 'Фильтр по категориям',
|
||||
'All categories' => 'Все категории',
|
||||
'No category' => 'Нет категории',
|
||||
'The name is required' => 'Требуется название',
|
||||
'Remove a file' => 'Удалить файл',
|
||||
'Unable to remove this file.' => 'Не удалось удалить файл.',
|
||||
'File removed successfully.' => 'Файл удален.',
|
||||
'Attach a document' => 'Приложить документ',
|
||||
'Do you really want to remove this file: "%s"?' => 'Вы точно хотите удалить этот файл « %s » ?',
|
||||
'open' => 'открыть',
|
||||
'Attachments' => 'Приложение',
|
||||
'Edit the task' => 'Изменить задачу',
|
||||
'Edit the description' => 'Изменить описание',
|
||||
'Add a comment' => 'Добавить комментарий',
|
||||
'Edit a comment' => 'Изменить комментарий',
|
||||
'Summary' => 'Сводка',
|
||||
'Time tracking' => 'Отслеживание времени',
|
||||
'Estimate:' => 'Приблизительно :',
|
||||
'Spent:' => 'Затрачено :',
|
||||
'Do you really want to remove this sub-task?' => 'Вы точно хотите удалить подзадачу ?',
|
||||
'Remaining:' => 'Осталось :',
|
||||
'hours' => 'часов',
|
||||
'spent' => 'затрачено',
|
||||
'estimated' => 'расчетное',
|
||||
'Sub-Tasks' => 'Подзадачи',
|
||||
'Add a sub-task' => 'Добавить подзадачу',
|
||||
'Original Estimate' => 'Начальная оценка',
|
||||
'Create another sub-task' => 'Создать другую подзадачу',
|
||||
'Time Spent' => 'Времени затрачено',
|
||||
'Edit a sub-task' => 'Изменить подзадачу',
|
||||
'Remove a sub-task' => 'Удалить подзадачу',
|
||||
'The time must be a numeric value' => 'Время должно быть числом!',
|
||||
'Todo' => 'TODO',
|
||||
'In progress' => 'В процессе',
|
||||
'Sub-task removed successfully.' => 'Подзадача удалена.',
|
||||
'Unable to remove this sub-task.' => 'Не удалось удалить подзадачу.',
|
||||
'Sub-task updated successfully.' => 'Подзадача обновлена.',
|
||||
'Unable to update your sub-task.' => 'Не удалось обновить подзадачу.',
|
||||
'Unable to create your sub-task.' => 'Не удалось создать подзадачу.',
|
||||
'Sub-task added successfully.' => 'Подзадача добавлена.',
|
||||
'Maximum size: ' => 'Максимальный размер : ',
|
||||
'Unable to upload the file.' => 'Не удалось загрузить файл.',
|
||||
'Display another project' => 'Показать другой проект',
|
||||
'Your GitHub account was successfully linked to your profile.' => 'Ваш GitHub привязан к вашему профилю.',
|
||||
'Unable to link your GitHub Account.' => 'Не удалось привязать ваш профиль к Github.',
|
||||
'GitHub authentication failed' => 'Аутентификация в GitHub не удалась',
|
||||
'Your GitHub account is no longer linked to your profile.' => 'Ваш GitHub отвязан от вашего профиля.',
|
||||
'Unable to unlink your GitHub Account.' => 'Не удалось отвязать ваш профиль от GitHub.',
|
||||
'Login with my GitHub Account' => 'Аутентификация через GitHub',
|
||||
'Link my GitHub Account' => 'Привязать мой профиль к GitHub',
|
||||
'Unlink my GitHub Account' => 'Отвязать мой профиль от GitHub',
|
||||
'Created by %s' => 'Создано %s',
|
||||
'Last modified on %B %e, %Y at %k:%M %p' => 'Последнее изменение %d/%m/%Y в %H:%M',
|
||||
'Tasks Export' => 'Экспорт задач',
|
||||
'Tasks exportation for "%s"' => 'Задача экспортирована для « %s »',
|
||||
'Start Date' => 'Дата начала',
|
||||
'End Date' => 'Дата завершения',
|
||||
'Execute' => 'Выполнить',
|
||||
'Task Id' => 'ID задачи',
|
||||
'Creator' => 'Автор',
|
||||
'Modification date' => 'Дата изменения',
|
||||
'Completion date' => 'Дата завершения',
|
||||
'Webhook URL for task creation' => 'Webhook URL для создания задачи',
|
||||
'Webhook URL for task modification' => 'Webhook URL для изменения задачи',
|
||||
'Clone' => 'Клонировать',
|
||||
'Clone Project' => 'Клонировать проект',
|
||||
'Project cloned successfully.' => 'Проект клонирован.',
|
||||
'Unable to clone this project.' => 'Не удалось клонировать проект.',
|
||||
'Email notifications' => 'Уведомления по email',
|
||||
'Enable email notifications' => 'Включить уведомления по email',
|
||||
'Task position:' => 'Позиция задачи :',
|
||||
'The task #%d have been opened.' => 'Задача #%d была открыта.',
|
||||
'The task #%d have been closed.' => 'Задача #%d была закрыта.',
|
||||
'Sub-task updated' => 'Подзадача обновлена',
|
||||
'Title:' => 'Название :',
|
||||
'Status:' => 'Статус :',
|
||||
'Assignee:' => 'Назначена :',
|
||||
'Time tracking:' => 'Отслеживание времени :',
|
||||
'New sub-task' => 'Новая подзадача',
|
||||
'New attachment added "%s"' => 'Добавлено вложение « %s »',
|
||||
'Comment updated' => 'Комментарий обновлен',
|
||||
'New comment posted by %s' => 'Новый комментарий написан « %s »',
|
||||
'List of due tasks for the project "%s"' => 'Список сроков к проекту « %s »',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][Новых вложений] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][Новых комментариев] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][Обновленых коментариев] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][Новых подзадач] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][Обновленных подзадач] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][Новых задач] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][Обновленных задач] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][Закрытых задач] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][Открытых задач] %s (#%d)',
|
||||
'[%s][Due tasks]' => '[%s][Текущие задачи]',
|
||||
'[Kanboard] Notification' => '[Kanboard] Оповещение',
|
||||
'I want to receive notifications only for those projects:' => 'Я хочу получать уведомления только по этим проектам :',
|
||||
'view the task on Kanboard' => 'посмотреть задачу на Kanboard',
|
||||
'Public access' => 'Общий доступ',
|
||||
'Categories management' => 'Управление категориями',
|
||||
'Users management' => 'Управление пользователями',
|
||||
'Active tasks' => 'Активные задачи',
|
||||
'Disable public access' => 'Отключить общий доступ',
|
||||
'Enable public access' => 'Включить общий доступ',
|
||||
'Active projects' => 'Активные проекты',
|
||||
'Inactive projects' => 'Неактивные проекты',
|
||||
'Public access disabled' => 'Общий доступ отключен',
|
||||
'Do you really want to disable this project: "%s"?' => 'Вы точно хотите отключить проект: "%s"?',
|
||||
'Do you really want to duplicate this project: "%s"?' => 'Вы точно хотите клонировать проект: "%s"?',
|
||||
'Do you really want to enable this project: "%s"?' => 'Вы точно хотите включить проект: "%s"?',
|
||||
'Project activation' => 'Активация проекта',
|
||||
'Move the task to another project' => 'Переместить задачу в другой проект',
|
||||
'Move to another project' => 'Переместить в другой проект',
|
||||
'Do you really want to duplicate this task?' => 'Вы точно хотите клонировать задачу?',
|
||||
'Duplicate a task' => 'Клонировать задачу',
|
||||
'External accounts' => 'Внешняя аутентификация',
|
||||
'Account type' => 'Тип профиля',
|
||||
'Local' => 'Локальный',
|
||||
'Remote' => 'Удаленный',
|
||||
'Enabled' => 'Включен',
|
||||
'Disabled' => 'Выключены',
|
||||
'Google account linked' => 'Профиль Google связан',
|
||||
'Github account linked' => 'Профиль GitHub связан',
|
||||
'Username:' => 'Имя пользователя:',
|
||||
'Name:' => 'Имя:',
|
||||
'Email:' => 'Email:',
|
||||
'Default project:' => 'Проект по умолчанию:',
|
||||
'Notifications:' => 'Уведомления:',
|
||||
'Group:' => 'Группа:',
|
||||
'Regular user' => 'Обычный пользователь',
|
||||
'Account type:' => 'Тип профиля:',
|
||||
'Edit profile' => 'Редактировать профиль:',
|
||||
'Change password' => 'Сменить пароль',
|
||||
'Password modification' => 'Изменение пароля',
|
||||
'External authentications' => 'Внешняя аутентификация',
|
||||
'Google Account' => 'Профиль Google',
|
||||
'Github Account' => 'Профиль GitHub',
|
||||
'Never connected.' => 'Ранее не соединялось.',
|
||||
'No account linked.' => 'Нет связанных профилей.',
|
||||
'Account linked.' => 'Профиль связан.',
|
||||
'No external authentication enabled.' => 'Нет активной внешней аутентификации.',
|
||||
'Password modified successfully.' => 'Пароль изменен.',
|
||||
'Unable to change the password.' => 'Не удалось сменить пароль.',
|
||||
'Change category for the task "%s"' => 'Сменить категорию для задачи "%s"',
|
||||
'Change category' => 'Смена категории',
|
||||
'%s updated the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s обновил задачу <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s open the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s открыл задачу <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the position #%d in the column "%s"' => '%s перместил задачу <a href="?controller=task&action=show&task_id=%d">#%d</a> на позицию #%d в колонке "%s"',
|
||||
'%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the column "%s"' => '%s переместил задачу <a href="?controller=task&action=show&task_id=%d">#%d</a> в колонку "%s"',
|
||||
'%s created the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s создал задачу <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s closed the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s закрыл задачу <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s created a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s создал подзадачу для задачи <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s updated a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s обновил подзадачу для задачи <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'Assigned to %s with an estimate of %s/%sh' => 'Назначено %s с окончанием %s/%sh',
|
||||
'Not assigned, estimate of %sh' => 'Не назначено, окончание %sh',
|
||||
'%s updated a comment on the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s обновил комментарий к задаче <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s commented the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s прокомментировал задачу <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s\'s activity' => '%s активность',
|
||||
'No activity.' => 'Нет активности',
|
||||
'RSS feed' => 'RSS лента',
|
||||
'%s updated a comment on the task #%d' => '%s обновил комментарий задачи #%d',
|
||||
'%s commented on the task #%d' => '%s откомментировал задачу #%d',
|
||||
'%s updated a subtask for the task #%d' => '%s обновил подзадачу задачи #%d',
|
||||
'%s created a subtask for the task #%d' => '%s создал подзадачу для задачи #%d',
|
||||
'%s updated the task #%d' => '%s обновил задачу #%d',
|
||||
'%s created the task #%d' => '%s создал задачу #%d',
|
||||
'%s closed the task #%d' => '%s закрыл задачу #%d',
|
||||
'%s open the task #%d' => '%s открыл задачу #%d',
|
||||
'%s moved the task #%d to the column "%s"' => '%s переместил задачу #%d в колонку "%s"',
|
||||
'%s moved the task #%d to the position %d in the column "%s"' => '%s переместил задачу #%d на позицию %d в колонке "%s"',
|
||||
'Activity' => 'Активность',
|
||||
'Default values are "%s"' => 'Колонки по умолчанию: "%s"',
|
||||
'Default columns for new projects (Comma-separated)' => 'Колонки по умолчанию для новых проектов (разделять запятой)',
|
||||
// 'Task assignee change' => '',
|
||||
// '%s change the assignee of the task #%d' => '',
|
||||
// '%s change the assignee of the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '[%s][Column Change] %s (#%d)' => '',
|
||||
// '[%s][Position Change] %s (#%d)' => '',
|
||||
// '[%s][Assignee Change] %s (#%d)' => '',
|
||||
// 'New password for the user "%s"' => '',
|
||||
);
|
|
@ -1,13 +1,6 @@
|
|||
<?php
|
||||
|
||||
return array(
|
||||
'English' => 'Engelska',
|
||||
'French' => 'Franska',
|
||||
'Polish' => 'Polska',
|
||||
'Portuguese (Brazilian)' => 'Portugisiska (Brasilien)',
|
||||
'Spanish' => 'Spanska',
|
||||
'German' => 'Tyska',
|
||||
'Swedish' => 'Svenska',
|
||||
'None' => 'Ingen',
|
||||
'edit' => 'redigera',
|
||||
'Edit' => 'Redigera',
|
||||
|
@ -36,7 +29,7 @@ return array(
|
|||
'All users' => 'Alla användare',
|
||||
'Username' => 'Användarnamn',
|
||||
'Password' => 'Lösenord',
|
||||
'Default Project' => 'Standardprojekt',
|
||||
'Default project' => 'Standardprojekt',
|
||||
'Administrator' => 'Administratör',
|
||||
'Sign in' => 'Logga in',
|
||||
'Users' => 'Användare',
|
||||
|
@ -58,7 +51,7 @@ return array(
|
|||
'Status' => 'Status',
|
||||
'Tasks' => 'Uppgifter',
|
||||
'Board' => 'Tavla',
|
||||
'Actions' => 'Åtgärder',
|
||||
'Actions' => 'Åtgärder',
|
||||
'Inactive' => 'Inaktiv',
|
||||
'Active' => 'Aktiv',
|
||||
'Column %d' => 'Kolumn %d',
|
||||
|
@ -91,6 +84,7 @@ return array(
|
|||
'Application settings' => 'Applikationsinställningar',
|
||||
'Language' => 'Språk',
|
||||
'Webhooks token:' => 'Token för webhooks:',
|
||||
'API token:' => 'API token:',
|
||||
'More information' => 'Mer information',
|
||||
'Database size:' => 'Databasstorlek:',
|
||||
'Download the database' => 'Ladda ner databasen',
|
||||
|
@ -110,7 +104,7 @@ return array(
|
|||
'Open a task' => 'Öppna en uppgift',
|
||||
'Do you really want to open this task: "%s"?' => 'Vill du verkligen öppna denna uppgift: "%s"?',
|
||||
'Back to the board' => 'Tillbaka till tavlan',
|
||||
'Created on %B %e, %G at %k:%M %p' => 'Skapad %d %B %G kl %H:%M',
|
||||
'Created on %B %e, %Y at %k:%M %p' => 'Skapad %d %B %Y kl %H:%M',
|
||||
'There is nobody assigned' => 'Det finns ingen tilldelad',
|
||||
'Column on the board:' => 'Kolumn på tavlan:',
|
||||
'Status is open' => 'Statusen är öppen',
|
||||
|
@ -127,7 +121,7 @@ return array(
|
|||
'The username must be unique' => 'Användarnamnet måste vara unikt',
|
||||
'The username must be alphanumeric' => 'Användarnamnet måste vara alfanumeriskt',
|
||||
'The user id is required' => 'Användar-ID måste anges',
|
||||
'Passwords doesn\'t matches' => 'Fel lösenord',
|
||||
'Passwords don\'t match' => 'Lösenorden matchar inte',
|
||||
'The confirmation is required' => 'Bekräftelse behövs.',
|
||||
'The column is required' => 'Kolumnen måste anges',
|
||||
'The project is required' => 'Projektet måste anges',
|
||||
|
@ -170,10 +164,10 @@ return array(
|
|||
'Ready' => 'Denna månad',
|
||||
'Backlog' => 'Att göra',
|
||||
'Work in progress' => 'Pågående',
|
||||
'Done' => 'Klart',
|
||||
'Done' => 'Slutfört',
|
||||
'Application version:' => 'Version:',
|
||||
'Completed on %B %e, %G at %k:%M %p' => 'Slutfört %d %B %G kl %H:%M',
|
||||
'%B %e, %G at %k:%M %p' => '%d %B %G kl %H:%M',
|
||||
'Completed on %B %e, %Y at %k:%M %p' => 'Slutfört %d %B %Y kl %H:%M',
|
||||
'%B %e, %Y at %k:%M %p' => '%d %B %Y kl %H:%M',
|
||||
'Date created' => 'Skapat datum',
|
||||
'Date completed' => 'Slutfört datum',
|
||||
'Id' => 'ID',
|
||||
|
@ -182,7 +176,7 @@ return array(
|
|||
'List of projects' => 'Lista med projekt',
|
||||
'Completed tasks for "%s"' => 'Slutföra uppgifter för "%s"',
|
||||
'%d closed tasks' => '%d stängda uppgifter',
|
||||
'no task for this project' => 'inga uppgifter i detta projekt',
|
||||
'No task for this project' => 'Inga uppgifter i detta projekt',
|
||||
'Public link' => 'Publik länk',
|
||||
'There is no column in your project!' => 'Det saknas kolumner i ditt projekt!',
|
||||
'Change assignee' => 'Ändra uppdragsinnehavare',
|
||||
|
@ -190,14 +184,13 @@ return array(
|
|||
'Timezone' => 'Tidszon',
|
||||
'Sorry, I didn\'t found this information in my database!' => 'Informationen kunde inte hittas i databasen.',
|
||||
'Page not found' => 'Sidan hittas inte',
|
||||
'Story Points' => 'Ungefärligt antal timmar',
|
||||
'Complexity' => 'Ungefärligt antal timmar',
|
||||
'limit' => 'max',
|
||||
'Task limit' => 'Uppgiftsbegränsning',
|
||||
'This value must be greater than %d' => 'Värdet måste vara större än %d',
|
||||
'Edit project access list' => 'Ändra projektåtkomst lista',
|
||||
'Edit users access' => 'Användaråtkomst',
|
||||
'Allow this user' => 'Tillåt användare',
|
||||
'Project access list for "%s"' => 'Behörighetslista för "%s"',
|
||||
'Only those users have access to this project:' => 'Bara de användarna har tillgång till detta projekt.',
|
||||
'Don\'t forget that administrators have access to everything.' => 'Glöm inte att administratörerna har rätt att göra allt.',
|
||||
'revoke' => 'Dra tillbaka behörighet',
|
||||
|
@ -205,7 +198,6 @@ return array(
|
|||
'User' => 'Användare',
|
||||
'Everybody have access to this project.' => 'Alla har tillgång till detta projekt.',
|
||||
'You are not allowed to access to this project.' => 'Du har inte tillgång till detta projekt.',
|
||||
'%B %e, %G at %k:%M %p' => '%d %B %G kl %H:%M',
|
||||
'Comments' => 'Kommentarer',
|
||||
'Post comment' => 'Ladda upp kommentar',
|
||||
'Write your text in Markdown' => 'Exempelsyntax för text',
|
||||
|
@ -217,11 +209,11 @@ return array(
|
|||
'The description is required' => 'En beskrivning måste lämnas',
|
||||
'Edit this task' => 'Ändra denna uppgift',
|
||||
'Due Date' => 'Måldatum',
|
||||
'm/d/Y' => 'd/m/Y', // Date format parsed with php
|
||||
'month/day/year' => 'dag/månad/år', // Help shown to the user
|
||||
'm/d/Y' => 'd/m/Y',
|
||||
'month/day/year' => 'dag/månad/år',
|
||||
'Invalid date' => 'Ej tillåtet datum',
|
||||
'Must be done before %B %e, %G' => 'Måste vara klart innan %B %e, %G',
|
||||
'%B %e, %G' => '%d %B %G',
|
||||
'Must be done before %B %e, %Y' => 'Måste vara klart innan %B %e, %Y',
|
||||
'%B %e, %Y' => '%d %B %Y',
|
||||
'Automatic actions' => 'Automatiska åtgärder',
|
||||
'Your automatic action have been created successfully.' => 'Din automatiska åtgärd har skapats.',
|
||||
'Unable to create your automatic action.' => 'Kunde inte skapa din automatiska åtgärd.',
|
||||
|
@ -230,6 +222,7 @@ return array(
|
|||
'Action removed successfully.' => 'Åtgärden har tagits bort.',
|
||||
'Automatic actions for the project "%s"' => 'Automatiska åtgärder för projektet "%s"',
|
||||
'Defined actions' => 'Definierade åtgärder',
|
||||
'Add an action' => 'Lägg till en åtgärd',
|
||||
'Event name' => 'Händelsenamn',
|
||||
'Action name' => 'Åtgärdsnamn',
|
||||
'Action parameters' => 'Åtgärdsparametrar',
|
||||
|
@ -256,7 +249,7 @@ return array(
|
|||
'Position' => 'Position',
|
||||
'Move Up' => 'Flytta upp',
|
||||
'Move Down' => 'Flytta ned',
|
||||
'Kopiera till ett annat projekt' => '',
|
||||
'Duplicate to another project' => 'Kopiera till ett annat projekt',
|
||||
'Duplicate' => 'Kopiera uppgiften',
|
||||
'link' => 'länk',
|
||||
'Update this comment' => 'Uppdatera kommentaren',
|
||||
|
@ -280,7 +273,7 @@ return array(
|
|||
'IP address' => 'IP-adress',
|
||||
'User agent' => 'Användaragent/webbläsare',
|
||||
'Persistent connections' => 'Beständiga anslutningar',
|
||||
'No session' => 'Ingen session',
|
||||
'No session.' => 'Ingen session.',
|
||||
'Expiration date' => 'Förfallodatum',
|
||||
'Remember Me' => 'Kom ihåg mig',
|
||||
'Creation date' => 'Skapatdatum',
|
||||
|
@ -366,7 +359,6 @@ return array(
|
|||
'The time must be a numeric value' => 'Tiden måste ha ett numeriskt värde',
|
||||
'Todo' => 'Att göra',
|
||||
'In progress' => 'Pågående',
|
||||
'Done' => 'Slutfört',
|
||||
'Sub-task removed successfully.' => 'Deluppgiften har tagits bort.',
|
||||
'Unable to remove this sub-task.' => 'Kunde inte ta bort denna deluppgift.',
|
||||
'Sub-task updated successfully.' => 'Deluppgiften har uppdaterats.',
|
||||
|
@ -383,7 +375,132 @@ return array(
|
|||
'Unable to unlink your GitHub Account.' => 'Kunde inte koppla ifrån ditt GitHub-konto.',
|
||||
'Login with my GitHub Account' => 'Logga in med mitt GitHub-konto',
|
||||
'Link my GitHub Account' => 'Anslut mitt GitHub-konto',
|
||||
'Unlink my GitHub Account' => 'Koppla ifrån mitt GitHub-konto',
|
||||
'Unlink my GitHub Account' => 'Koppla ifrån mitt GitHub-konto',
|
||||
'Created by %s' => 'Skapad av %s',
|
||||
'Last modified on %B %e, %G at %k:%M %p' => 'Senaste ändring %B %e, %G kl %k:%M %p'',
|
||||
'Last modified on %B %e, %Y at %k:%M %p' => 'Senaste ändring %B %e, %Y kl %k:%M %p',
|
||||
'Tasks Export' => 'Exportera uppgifter',
|
||||
'Tasks exportation for "%s"' => 'Exportera uppgifter för "%s"',
|
||||
'Start Date' => 'Startdatum',
|
||||
'End Date' => 'Slutdatum',
|
||||
'Execute' => 'Utför',
|
||||
'Task Id' => 'Uppgift ID',
|
||||
'Creator' => 'Skapare',
|
||||
'Modification date' => 'Ändringsdatum',
|
||||
'Completion date' => 'Slutfört datum',
|
||||
'Webhook URL for task creation' => 'Webhook URL för att skapa uppgift',
|
||||
'Webhook URL for task modification' => 'Webhook URL för att ändra uppgift',
|
||||
'Clone' => 'Klona',
|
||||
'Clone Project' => 'Klona projekt',
|
||||
'Project cloned successfully.' => 'Projektet har klonats.',
|
||||
'Unable to clone this project.' => 'Kunde inte klona projektet.',
|
||||
'Email notifications' => 'Epostnotiser',
|
||||
'Enable email notifications' => 'Aktivera epostnotiser',
|
||||
'Task position:' => 'Uppgiftsposition:',
|
||||
'The task #%d have been opened.' => 'Uppgiften #%d har öppnats.',
|
||||
'The task #%d have been closed.' => 'Uppgiften #%d har stängts.',
|
||||
'Sub-task updated' => 'Deluppgift uppdaterad',
|
||||
'Title:' => 'Titel:',
|
||||
'Status:' => 'Status:',
|
||||
'Assignee:' => 'Tilldelad:',
|
||||
'Time tracking:' => 'Tidsspårning',
|
||||
'New sub-task' => 'Ny deluppgift',
|
||||
'New attachment added "%s"' => 'Ny bifogning tillagd "%s"',
|
||||
'Comment updated' => 'Kommentaren har uppdaterats',
|
||||
'New comment posted by %s' => 'Ny kommentar postad av %s',
|
||||
'List of due tasks for the project "%s"' => 'Lista med uppgifter för projektet "%s"',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
// '[%s][New subtask] %s (#%d)' => '',
|
||||
// '[%s][Subtask updated] %s (#%d)' => '',
|
||||
// '[%s][New task] %s (#%d)' => '',
|
||||
// '[%s][Task updated] %s (#%d)' => '',
|
||||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
'[Kanboard] Notification' => '[Kanboard] Notis',
|
||||
'I want to receive notifications only for those projects:' => 'Jag vill endast få notiser för dessa projekt:',
|
||||
'view the task on Kanboard' => 'Visa uppgiften på Kanboard',
|
||||
// 'Public access' => '',
|
||||
// 'Categories management' => '',
|
||||
// 'Users management' => '',
|
||||
// 'Active tasks' => '',
|
||||
// 'Disable public access' => '',
|
||||
// 'Enable public access' => '',
|
||||
// 'Active projects' => '',
|
||||
// 'Inactive projects' => '',
|
||||
// 'Public access disabled' => '',
|
||||
// 'Do you really want to disable this project: "%s"?' => '',
|
||||
// 'Do you really want to duplicate this project: "%s"?' => '',
|
||||
// 'Do you really want to enable this project: "%s"?' => '',
|
||||
// 'Project activation' => '',
|
||||
// 'Move the task to another project' => '',
|
||||
// 'Move to another project' => '',
|
||||
// 'Do you really want to duplicate this task?' => '',
|
||||
// 'Duplicate a task' => '',
|
||||
// 'External accounts' => '',
|
||||
// 'Account type' => '',
|
||||
// 'Local' => '',
|
||||
// 'Remote' => '',
|
||||
// 'Enabled' => '',
|
||||
// 'Disabled' => '',
|
||||
// 'Google account linked' => '',
|
||||
// 'Github account linked' => '',
|
||||
// 'Username:' => '',
|
||||
// 'Name:' => '',
|
||||
// 'Email:' => '',
|
||||
// 'Default project:' => '',
|
||||
// 'Notifications:' => '',
|
||||
// 'Group:' => '',
|
||||
// 'Regular user' => '',
|
||||
// 'Account type:' => '',
|
||||
// 'Edit profile' => '',
|
||||
// 'Change password' => '',
|
||||
// 'Password modification' => '',
|
||||
// 'External authentications' => '',
|
||||
// 'Google Account' => '',
|
||||
// 'Github Account' => '',
|
||||
// 'Never connected.' => '',
|
||||
// 'No account linked.' => '',
|
||||
// 'Account linked.' => '',
|
||||
// 'No external authentication enabled.' => '',
|
||||
// 'Password modified successfully.' => '',
|
||||
// 'Unable to change the password.' => '',
|
||||
// 'Change category for the task "%s"' => '',
|
||||
// 'Change category' => '',
|
||||
// '%s updated the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s open the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the position #%d in the column "%s"' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the column "%s"' => '',
|
||||
// '%s created the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s closed the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s created a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s updated a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// 'Assigned to %s with an estimate of %s/%sh' => '',
|
||||
// 'Not assigned, estimate of %sh' => '',
|
||||
// '%s updated a comment on the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s commented the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s\'s activity' => '',
|
||||
// 'No activity.' => '',
|
||||
// 'RSS feed' => '',
|
||||
// '%s updated a comment on the task #%d' => '',
|
||||
// '%s commented on the task #%d' => '',
|
||||
// '%s updated a subtask for the task #%d' => '',
|
||||
// '%s created a subtask for the task #%d' => '',
|
||||
// '%s updated the task #%d' => '',
|
||||
// '%s created the task #%d' => '',
|
||||
// '%s closed the task #%d' => '',
|
||||
// '%s open the task #%d' => '',
|
||||
// '%s moved the task #%d to the column "%s"' => '',
|
||||
// '%s moved the task #%d to the position %d in the column "%s"' => '',
|
||||
// 'Activity' => '',
|
||||
// 'Default values are "%s"' => '',
|
||||
// 'Default columns for new projects (Comma-separated)' => '',
|
||||
// 'Task assignee change' => '',
|
||||
// '%s change the assignee of the task #%d' => '',
|
||||
// '%s change the assignee of the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '[%s][Column Change] %s (#%d)' => '',
|
||||
// '[%s][Position Change] %s (#%d)' => '',
|
||||
// '[%s][Assignee Change] %s (#%d)' => '',
|
||||
// 'New password for the user "%s"' => '',
|
||||
);
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
<?php
|
||||
|
||||
return array(
|
||||
'English' => '英语',
|
||||
'French' => '法语',
|
||||
'Polish' => '波兰语',
|
||||
'Portuguese (Brazilian)' => '葡萄牙语 (巴西)',
|
||||
'Spanish' => '西班牙语',
|
||||
'German' => '德语',
|
||||
'Chinese (Simplified)' => '中文(简体)',
|
||||
'None' => '未知',
|
||||
'edit' => '修改',
|
||||
'Edit' => '修改',
|
||||
|
@ -30,19 +23,15 @@ return array(
|
|||
'Official website:' => '官方网站:',
|
||||
'Unassigned' => '未指定',
|
||||
'View this task' => '查看该任务',
|
||||
'Add a sub-task' => '添加一个子任务',
|
||||
'Original Estimate' => '初步预计耗时',
|
||||
'hours' => '小时',
|
||||
'Create another sub-task' => '创建另一个子任务',
|
||||
'Remove user' => '移除用户',
|
||||
'Do you really want to remove this user: "%s"?' => '你确定要移除这个用户吗:"%s"?',
|
||||
'New user' => '新用户',
|
||||
'All users' => '所有用户',
|
||||
'Username' => '用户名',
|
||||
'Password' => '密码',
|
||||
'Default Project' => '默认项目',
|
||||
'Default project' => '默认项目',
|
||||
'Administrator' => '管理员',
|
||||
'Sign in' => '注册',
|
||||
'Sign in' => '登录',
|
||||
'Users' => '用户组',
|
||||
'No user' => '没有用户',
|
||||
'Forbidden' => '禁止',
|
||||
|
@ -62,6 +51,7 @@ return array(
|
|||
'Status' => '状态',
|
||||
'Tasks' => '任务群',
|
||||
'Board' => '看板',
|
||||
'Actions' => '行为',
|
||||
'Inactive' => '未激活',
|
||||
'Active' => '激活',
|
||||
'Column %d' => '第%d栏目',
|
||||
|
@ -94,6 +84,7 @@ return array(
|
|||
'Application settings' => '应用设置',
|
||||
'Language' => '语言',
|
||||
'Webhooks token:' => '页面钩子令牌:',
|
||||
// 'API token:' => '',
|
||||
'More information' => '更多信息',
|
||||
'Database size:' => '数据库大小:',
|
||||
'Download the database' => '下载数据库',
|
||||
|
@ -113,7 +104,7 @@ return array(
|
|||
'Open a task' => '开一个项目',
|
||||
'Do you really want to open this task: "%s"?' => '你确定要开这个项目吗?"%s"',
|
||||
'Back to the board' => '回到看板',
|
||||
'Created on %B %e, %G at %k:%M %p' => '在%d/%m/%Y %H:%M创建',
|
||||
'Created on %B %e, %Y at %k:%M %p' => '在%d/%m/%Y %H:%M创建',
|
||||
'There is nobody assigned' => '无人负责',
|
||||
'Column on the board:' => '看板上的栏目:',
|
||||
'Status is open' => '开放状态',
|
||||
|
@ -130,7 +121,7 @@ return array(
|
|||
'The username must be unique' => '用户名必须唯一',
|
||||
'The username must be alphanumeric' => '用户名必须是英文字符或数字组成',
|
||||
'The user id is required' => '用户id是必须的',
|
||||
'Passwords doesn\'t matches' => '密码不匹配',
|
||||
// 'Passwords don\'t match' => '',
|
||||
'The confirmation is required' => '需要确认',
|
||||
'The column is required' => '需要指定栏目',
|
||||
'The project is required' => '需要指定项目',
|
||||
|
@ -175,8 +166,8 @@ return array(
|
|||
'Work in progress' => '工作进行中',
|
||||
'Done' => '完成',
|
||||
'Application version:' => '应用程序版本',
|
||||
'Completed on %B %e, %G at %k:%M %p' => '于%Y年%m月%d日%H时%M分完成',
|
||||
'%B %e, %G at %k:%M %p' => '%Y年%m月%d日%H时%M分',
|
||||
'Completed on %B %e, %Y at %k:%M %p' => '于%Y年%m月%d日%H时%M分完成',
|
||||
'%B %e, %Y at %k:%M %p' => '%Y年%m月%d日%H时%M分',
|
||||
'Date created' => '创建时间',
|
||||
'Date completed' => '完成时间',
|
||||
'Id' => '昵称',
|
||||
|
@ -185,7 +176,7 @@ return array(
|
|||
'List of projects' => '项目列表',
|
||||
'Completed tasks for "%s"' => '任务因"%s"原因完成',
|
||||
'%d closed tasks' => '%d个已关闭任务',
|
||||
'no task for this project' => '该项目尚无任务',
|
||||
'No task for this project' => '该项目尚无任务',
|
||||
'Public link' => '公开链接',
|
||||
'There is no column in your project!' => '该项目尚无栏目项!',
|
||||
'Change assignee' => '被指派人变更',
|
||||
|
@ -193,14 +184,13 @@ return array(
|
|||
'Timezone' => '时区',
|
||||
'Sorry, I didn\'t found this information in my database!' => '抱歉,无法在数据库中找到该信息!',
|
||||
'Page not found' => '页面未找到',
|
||||
'Story Points' => '评估分值',
|
||||
'Complexity' => '评估分值',
|
||||
'limit' => '限制',
|
||||
'Task limit' => '任务限制',
|
||||
'This value must be greater than %d' => '该数值必须大于%d',
|
||||
'Edit project access list' => '编辑项目存取列表',
|
||||
'Edit users access' => '编辑用户存取权限',
|
||||
'Allow this user' => '允许该用户',
|
||||
'Project access list for "%s"' => '"%s"的项目存取列表',
|
||||
'Only those users have access to this project:' => '只有这些用户有该项目的存取权限:',
|
||||
'Don\'t forget that administrators have access to everything.' => '别忘了管理员有一切的权限。',
|
||||
'revoke' => '撤销',
|
||||
|
@ -219,15 +209,12 @@ return array(
|
|||
'The description is required' => '必须得有描述',
|
||||
'Edit this task' => '编辑该任务',
|
||||
'Due Date' => '到期',
|
||||
'm/d/Y' => 'Y/m/d', // Date format parsed with php
|
||||
'month/day/year' => '年/月/日', // Help shown to the user
|
||||
'm/d/Y' => 'Y/m/d',
|
||||
'month/day/year' => '年/月/日',
|
||||
'Invalid date' => '无效日期',
|
||||
'Must be done before %B %e, %G' => '必须在%Y年%m月%d日前完成',
|
||||
'%B %e, %G' => '%Y/%m/%d',
|
||||
'Must be done before %B %e, %Y' => '必须在%Y年%m月%d日前完成',
|
||||
'%B %e, %Y' => '%Y/%m/%d',
|
||||
'Automatic actions' => '自动行为',
|
||||
'Add an action' => '添加一个行为',
|
||||
'Assign automatically a color based on a category' => '基于一个分类自动指派颜色',
|
||||
'Assign automatically a category based on a color' => '基于一种颜色自动指派分类',
|
||||
'Your automatic action have been created successfully.' => '您的自动行为已成功创建',
|
||||
'Unable to create your automatic action.' => '无法为您创建自动行为。',
|
||||
'Remove an action' => '移除一个行为。',
|
||||
|
@ -235,11 +222,11 @@ return array(
|
|||
'Action removed successfully.' => '成功移除行为。',
|
||||
'Automatic actions for the project "%s"' => '项目"%s"的自动行为',
|
||||
'Defined actions' => '已定义的行为',
|
||||
'Add an action' => '添加一个行为',
|
||||
'Event name' => '事件名称',
|
||||
'Action name' => '行为名称',
|
||||
'Action parameters' => '行为参数',
|
||||
'Action' => '行为',
|
||||
'Actions' => '行为',
|
||||
'Event' => '事件',
|
||||
'When the selected event occurs execute the corresponding action.' => '当所选事件发生时执行相应行为。',
|
||||
'Next step' => '下一步',
|
||||
|
@ -277,7 +264,6 @@ return array(
|
|||
'Current password for the user "%s"' => '用户"%s"的当前密码',
|
||||
'The current password is required' => '需要输入当前密码',
|
||||
'Wrong password' => '密码错误',
|
||||
'Confirmation' => '再输一次新密码',
|
||||
'Reset all tokens' => '重置所有令牌',
|
||||
'All tokens have been regenerated.' => '所有令牌都重新生成了。',
|
||||
'Unknown' => '未知',
|
||||
|
@ -287,7 +273,7 @@ return array(
|
|||
'IP address' => 'IP地址',
|
||||
'User agent' => '用户代理',
|
||||
'Persistent connections' => '持续连接',
|
||||
'No session' => '无会话',
|
||||
'No session.' => '无会话',
|
||||
'Expiration date' => '过期',
|
||||
'Remember Me' => '记住我',
|
||||
'Creation date' => '创建日期',
|
||||
|
@ -320,7 +306,8 @@ return array(
|
|||
'Unable to remove this task.' => '无法移除该任务。',
|
||||
'Remove a task' => '移除一个任务',
|
||||
'Do you really want to remove this task: "%s"?' => '确定要溢出该任务"%s"吗?',
|
||||
'Assign a color to a specific category' => '指派颜色给一个特定分类',
|
||||
'Assign automatically a color based on a category' => '基于一个分类自动指派颜色',
|
||||
'Assign automatically a category based on a color' => '基于一种颜色自动指派分类',
|
||||
'Task creation or modification' => '任务创建或修改',
|
||||
'Category' => '分类',
|
||||
'Category:' => '分类:',
|
||||
|
@ -359,20 +346,19 @@ return array(
|
|||
// 'Spent:' => '',
|
||||
// 'Do you really want to remove this sub-task?' => '',
|
||||
// 'Remaining:' => '',
|
||||
// 'hours' => '',
|
||||
'hours' => '小时',
|
||||
// 'spent' => '',
|
||||
// 'estimated' => '',
|
||||
// 'Sub-Tasks' => '',
|
||||
// 'Add a sub-task' => '',
|
||||
// 'Original Estimate' => '',
|
||||
// 'Create another sub-task' => '',
|
||||
'Add a sub-task' => '添加一个子任务',
|
||||
'Original Estimate' => '初步预计耗时',
|
||||
'Create another sub-task' => '创建另一个子任务',
|
||||
// 'Time Spent' => '',
|
||||
// 'Edit a sub-task' => '',
|
||||
// 'Remove a sub-task' => '',
|
||||
// 'The time must be a numeric value' => '',
|
||||
// 'Todo' => '',
|
||||
// 'In progress' => '',
|
||||
// 'Done' => '',
|
||||
// 'Sub-task removed successfully.' => '',
|
||||
// 'Unable to remove this sub-task.' => '',
|
||||
// 'Sub-task updated successfully.' => '',
|
||||
|
@ -389,7 +375,132 @@ return array(
|
|||
// 'Unable to unlink your GitHub Account.' => '',
|
||||
// 'Login with my GitHub Account' => '',
|
||||
// 'Link my GitHub Account' => '',
|
||||
// 'Unlink my GitHub Account' => '',
|
||||
// 'Created by %s' => 'Créé par %s',
|
||||
// 'Last modified on %B %e, %G at %k:%M %p' => '',
|
||||
// 'Unlink my GitHub Account' => '',
|
||||
// 'Created by %s' => '',
|
||||
// 'Last modified on %B %e, %Y at %k:%M %p' => '',
|
||||
// 'Tasks Export' => '',
|
||||
// 'Tasks exportation for "%s"' => '',
|
||||
// 'Start Date' => '',
|
||||
// 'End Date' => '',
|
||||
// 'Execute' => '',
|
||||
// 'Task Id' => '',
|
||||
// 'Creator' => '',
|
||||
// 'Modification date' => '',
|
||||
// 'Completion date' => '',
|
||||
// 'Webhook URL for task creation' => '',
|
||||
// 'Webhook URL for task modification' => '',
|
||||
// 'Clone' => '',
|
||||
// 'Clone Project' => '',
|
||||
// 'Project cloned successfully.' => '',
|
||||
// 'Unable to clone this project.' => '',
|
||||
// 'Email notifications' => '',
|
||||
// 'Enable email notifications' => '',
|
||||
// 'Task position:' => '',
|
||||
// 'The task #%d have been opened.' => '',
|
||||
// 'The task #%d have been closed.' => '',
|
||||
// 'Sub-task updated' => '',
|
||||
// 'Title:' => '',
|
||||
// 'Status:' => '',
|
||||
// 'Assignee:' => '',
|
||||
// 'Time tracking:' => '',
|
||||
// 'New sub-task' => '',
|
||||
// 'New attachment added "%s"' => '',
|
||||
// 'Comment updated' => '',
|
||||
// 'New comment posted by %s' => '',
|
||||
// 'List of due tasks for the project "%s"' => '',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
// '[%s][New subtask] %s (#%d)' => '',
|
||||
// '[%s][Subtask updated] %s (#%d)' => '',
|
||||
// '[%s][New task] %s (#%d)' => '',
|
||||
// '[%s][Task updated] %s (#%d)' => '',
|
||||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
// '[Kanboard] Notification' => '',
|
||||
// 'I want to receive notifications only for those projects:' => '',
|
||||
// 'view the task on Kanboard' => '',
|
||||
// 'Public access' => '',
|
||||
// 'Categories management' => '',
|
||||
// 'Users management' => '',
|
||||
// 'Active tasks' => '',
|
||||
// 'Disable public access' => '',
|
||||
// 'Enable public access' => '',
|
||||
// 'Active projects' => '',
|
||||
// 'Inactive projects' => '',
|
||||
// 'Public access disabled' => '',
|
||||
// 'Do you really want to disable this project: "%s"?' => '',
|
||||
// 'Do you really want to duplicate this project: "%s"?' => '',
|
||||
// 'Do you really want to enable this project: "%s"?' => '',
|
||||
// 'Project activation' => '',
|
||||
// 'Move the task to another project' => '',
|
||||
// 'Move to another project' => '',
|
||||
// 'Do you really want to duplicate this task?' => '',
|
||||
// 'Duplicate a task' => '',
|
||||
// 'External accounts' => '',
|
||||
// 'Account type' => '',
|
||||
// 'Local' => '',
|
||||
// 'Remote' => '',
|
||||
// 'Enabled' => '',
|
||||
// 'Disabled' => '',
|
||||
// 'Google account linked' => '',
|
||||
// 'Github account linked' => '',
|
||||
// 'Username:' => '',
|
||||
// 'Name:' => '',
|
||||
// 'Email:' => '',
|
||||
// 'Default project:' => '',
|
||||
// 'Notifications:' => '',
|
||||
// 'Group:' => '',
|
||||
// 'Regular user' => '',
|
||||
// 'Account type:' => '',
|
||||
// 'Edit profile' => '',
|
||||
// 'Change password' => '',
|
||||
// 'Password modification' => '',
|
||||
// 'External authentications' => '',
|
||||
// 'Google Account' => '',
|
||||
// 'Github Account' => '',
|
||||
// 'Never connected.' => '',
|
||||
// 'No account linked.' => '',
|
||||
// 'Account linked.' => '',
|
||||
// 'No external authentication enabled.' => '',
|
||||
// 'Password modified successfully.' => '',
|
||||
// 'Unable to change the password.' => '',
|
||||
// 'Change category for the task "%s"' => '',
|
||||
// 'Change category' => '',
|
||||
// '%s updated the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s open the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the position #%d in the column "%s"' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the column "%s"' => '',
|
||||
// '%s created the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s closed the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s created a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s updated a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// 'Assigned to %s with an estimate of %s/%sh' => '',
|
||||
// 'Not assigned, estimate of %sh' => '',
|
||||
// '%s updated a comment on the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s commented the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s\'s activity' => '',
|
||||
// 'No activity.' => '',
|
||||
// 'RSS feed' => '',
|
||||
// '%s updated a comment on the task #%d' => '',
|
||||
// '%s commented on the task #%d' => '',
|
||||
// '%s updated a subtask for the task #%d' => '',
|
||||
// '%s created a subtask for the task #%d' => '',
|
||||
// '%s updated the task #%d' => '',
|
||||
// '%s created the task #%d' => '',
|
||||
// '%s closed the task #%d' => '',
|
||||
// '%s open the task #%d' => '',
|
||||
// '%s moved the task #%d to the column "%s"' => '',
|
||||
// '%s moved the task #%d to the position %d in the column "%s"' => '',
|
||||
// 'Activity' => '',
|
||||
// 'Default values are "%s"' => '',
|
||||
// 'Default columns for new projects (Comma-separated)' => '',
|
||||
// 'Task assignee change' => '',
|
||||
// '%s change the assignee of the task #%d' => '',
|
||||
// '%s change the assignee of the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '[%s][Column Change] %s (#%d)' => '',
|
||||
// '[%s][Position Change] %s (#%d)' => '',
|
||||
// '[%s][Assignee Change] %s (#%d)' => '',
|
||||
// 'New password for the user "%s"' => '',
|
||||
);
|
||||
|
|
|
@ -18,8 +18,9 @@ class Acl extends Base
|
|||
*/
|
||||
private $public_actions = array(
|
||||
'user' => array('login', 'check', 'google', 'github'),
|
||||
'task' => array('add'),
|
||||
'task' => array('add', 'readonly'),
|
||||
'board' => array('readonly'),
|
||||
'project' => array('feed'),
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -30,29 +31,13 @@ class Acl extends Base
|
|||
*/
|
||||
private $user_actions = array(
|
||||
'app' => array('index'),
|
||||
'board' => array('index', 'show', 'assign', 'assigntask', 'save', 'check'),
|
||||
'project' => array('tasks', 'index', 'forbidden', 'search'),
|
||||
'user' => array('index', 'edit', 'update', 'forbidden', 'logout', 'index', 'unlinkgoogle', 'unlinkgithub'),
|
||||
'config' => array('index', 'removeremembermetoken'),
|
||||
'board' => array('index', 'show', 'save', 'check', 'changeassignee', 'updateassignee', 'changecategory', 'updatecategory'),
|
||||
'project' => array('tasks', 'index', 'forbidden', 'search', 'export', 'show', 'activity'),
|
||||
'user' => array('index', 'edit', 'forbidden', 'logout', 'index', 'show', 'external', 'unlinkgoogle', 'unlinkgithub', 'sessions', 'removesession', 'last', 'notifications', 'password'),
|
||||
'comment' => array('create', 'save', 'confirm', 'remove', 'update', 'edit', 'forbidden'),
|
||||
'file' => array('create', 'save', 'download', 'confirm', 'remove', 'open', 'image'),
|
||||
'subtask' => array('create', 'save', 'edit', 'update', 'confirm', 'remove'),
|
||||
'task' => array(
|
||||
'show',
|
||||
'create',
|
||||
'save',
|
||||
'edit',
|
||||
'update',
|
||||
'close',
|
||||
'confirmclose',
|
||||
'open',
|
||||
'confirmopen',
|
||||
'duplicate',
|
||||
'remove',
|
||||
'confirmremove',
|
||||
'editdescription',
|
||||
'savedescription',
|
||||
),
|
||||
'task' => array('show', 'create', 'save', 'edit', 'update', 'close', 'open', 'duplicate', 'remove', 'description', 'move', 'copy'),
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
@ -41,6 +41,7 @@ class Action extends Base
|
|||
'TaskAssignSpecificUser' => t('Assign the task to a specific user'),
|
||||
'TaskAssignCurrentUser' => t('Assign the task to the person who does the action'),
|
||||
'TaskDuplicateAnotherProject' => t('Duplicate the task to another project'),
|
||||
'TaskMoveAnotherProject' => t('Move the task to another project'),
|
||||
'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'),
|
||||
|
@ -63,6 +64,7 @@ class Action extends Base
|
|||
Task::EVENT_OPEN => t('Open a closed task'),
|
||||
Task::EVENT_CLOSE => t('Closing a task'),
|
||||
Task::EVENT_CREATE_UPDATE => t('Task creation or modification'),
|
||||
Task::EVENT_ASSIGNEE_CHANGE => t('Task assignee change'),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -217,34 +219,81 @@ class Action extends Base
|
|||
* @param integer $project_id Project id
|
||||
* @throws \LogicException
|
||||
* @return \Core\Listener Action Instance
|
||||
* @throw LogicException
|
||||
*/
|
||||
public function load($name, $project_id)
|
||||
{
|
||||
switch ($name) {
|
||||
case 'TaskClose':
|
||||
$className = '\Action\TaskClose';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
case 'TaskAssignCurrentUser':
|
||||
$className = '\Action\TaskAssignCurrentUser';
|
||||
return new $className($project_id, new Task($this->db, $this->event), new Acl($this->db, $this->event));
|
||||
case 'TaskAssignSpecificUser':
|
||||
$className = '\Action\TaskAssignSpecificUser';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
case 'TaskDuplicateAnotherProject':
|
||||
$className = '\Action\TaskDuplicateAnotherProject';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
case 'TaskAssignColorUser':
|
||||
$className = '\Action\TaskAssignColorUser';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
case 'TaskAssignColorCategory':
|
||||
$className = '\Action\TaskAssignColorCategory';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
case 'TaskAssignCategoryColor':
|
||||
$className = '\Action\TaskAssignCategoryColor';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
$className = '\Action\\'.$name;
|
||||
|
||||
if ($name === 'TaskAssignCurrentUser') {
|
||||
return new $className($project_id, new Task($this->registry), new Acl($this->registry));
|
||||
}
|
||||
else {
|
||||
return new $className($project_id, new Task($this->registry));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy Actions and related Actions Parameters from a project to another one
|
||||
*
|
||||
* @author Antonio Rabelo
|
||||
* @param integer $project_from Project Template
|
||||
* @return integer $project_to Project that receives the copy
|
||||
* @return boolean
|
||||
*/
|
||||
public function duplicate($project_from, $project_to)
|
||||
{
|
||||
$actionTemplate = $this->action->getAllByProject($project_from);
|
||||
|
||||
foreach ($actionTemplate as $action) {
|
||||
|
||||
unset($action['id']);
|
||||
$action['project_id'] = $project_to;
|
||||
$actionParams = $action['params'];
|
||||
unset($action['params']);
|
||||
|
||||
if (! $this->db->table(self::TABLE)->save($action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$action_clone_id = $this->db->getConnection()->getLastId();
|
||||
|
||||
foreach ($actionParams as $param) {
|
||||
unset($param['id']);
|
||||
$param['value'] = $this->resolveDuplicatedParameters($param, $project_to);
|
||||
$param['action_id'] = $action_clone_id;
|
||||
|
||||
if (! $this->db->table(self::TABLE_PARAMS)->save($param)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve type of action value from a project to the respective value in another project
|
||||
*
|
||||
* @author Antonio Rabelo
|
||||
* @param integer $param An action parameter
|
||||
* @return integer $project_to Project to find the corresponding values
|
||||
* @return mixed The corresponding values from $project_to
|
||||
*/
|
||||
private function resolveDuplicatedParameters($param, $project_to)
|
||||
{
|
||||
switch($param['name']) {
|
||||
case 'project_id':
|
||||
return $project_to;
|
||||
case 'category_id':
|
||||
$categoryTemplate = $this->category->getById($param['value']);
|
||||
$categoryFromNewProject = $this->db->table(Category::TABLE)->eq('project_id', $project_to)->eq('name', $categoryTemplate['name'])->findOne();
|
||||
return $categoryFromNewProject['id'];
|
||||
case 'column_id':
|
||||
$boardTemplate = $this->board->getColumn($param['value']);
|
||||
$boardFromNewProject = $this->db->table(Board::TABLE)->eq('project_id', $project_to)->eq('title', $boardTemplate['title'])->findOne();
|
||||
return $boardFromNewProject['id'];
|
||||
default:
|
||||
throw new LogicException('Action not found: '.$name);
|
||||
return $param['value'];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
136
sources/app/Model/Authentication.php
Normal file
136
sources/app/Model/Authentication.php
Normal file
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use Auth\Database;
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
/**
|
||||
* Authentication model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Authentication extends Base
|
||||
{
|
||||
/**
|
||||
* Load automatically an authentication backend
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Backend class name
|
||||
* @return mixed
|
||||
*/
|
||||
public function backend($name)
|
||||
{
|
||||
if (! isset($this->registry->$name)) {
|
||||
$class = '\Auth\\'.ucfirst($name);
|
||||
$this->registry->$name = new $class($this->registry);
|
||||
}
|
||||
|
||||
return $this->registry->shared($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current user is authenticated
|
||||
*
|
||||
* @access public
|
||||
* @param string $controller Controller
|
||||
* @param string $action Action name
|
||||
* @return bool
|
||||
*/
|
||||
public function isAuthenticated($controller, $action)
|
||||
{
|
||||
// If the action is public we don't need to do any checks
|
||||
if ($this->acl->isPublicAction($controller, $action)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the user is already logged it's ok
|
||||
if ($this->acl->isLogged()) {
|
||||
|
||||
// We update each time the RememberMe cookie tokens
|
||||
if ($this->backend('rememberMe')->hasCookie()) {
|
||||
$this->backend('rememberMe')->refresh();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// We try first with the RememberMe cookie
|
||||
if ($this->backend('rememberMe')->authenticate()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Then with the ReverseProxy authentication
|
||||
if (REVERSE_PROXY_AUTH && $this->backend('reverseProxy')->authenticate()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate a user by different methods
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Username
|
||||
* @param string $password Password
|
||||
* @return boolean
|
||||
*/
|
||||
public function authenticate($username, $password)
|
||||
{
|
||||
// Try first the database auth and then LDAP if activated
|
||||
if ($this->backend('database')->authenticate($username, $password)) {
|
||||
return true;
|
||||
}
|
||||
else if (LDAP_AUTH && $this->backend('ldap')->authenticate($username, $password)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate user login form
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateForm(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('username', t('The username is required')),
|
||||
new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\Required('password', t('The password is required')),
|
||||
));
|
||||
|
||||
$result = $v->execute();
|
||||
$errors = $v->getErrors();
|
||||
|
||||
if ($result) {
|
||||
|
||||
if ($this->authenticate($values['username'], $values['password'])) {
|
||||
|
||||
// Setup the remember me feature
|
||||
if (! empty($values['remember_me'])) {
|
||||
|
||||
$credentials = $this->backend('rememberMe')
|
||||
->create($this->acl->getUserId(), $this->user->getIpAddress(), $this->user->getUserAgent());
|
||||
|
||||
$this->backend('rememberMe')->writeCookie($credentials['token'], $credentials['sequence'], $credentials['expiration']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$result = false;
|
||||
$errors['login'] = t('Bad username or password');
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
$result,
|
||||
$errors
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,21 +2,9 @@
|
|||
|
||||
namespace Model;
|
||||
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validator.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Base.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/Required.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/Unique.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/MaxLength.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/MinLength.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/Integer.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/Equals.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/AlphaNumeric.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/GreaterThan.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/Date.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/Email.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/Numeric.php';
|
||||
|
||||
use Core\Event;
|
||||
use Core\Tool;
|
||||
use Core\Registry;
|
||||
use PicoDb\Database;
|
||||
|
||||
/**
|
||||
|
@ -24,6 +12,23 @@ use PicoDb\Database;
|
|||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*
|
||||
* @property \Model\Acl $acl
|
||||
* @property \Model\Action $action
|
||||
* @property \Model\Authentication $authentication
|
||||
* @property \Model\Board $board
|
||||
* @property \Model\Category $category
|
||||
* @property \Model\Comment $comment
|
||||
* @property \Model\Config $config
|
||||
* @property \Model\File $file
|
||||
* @property \Model\LastLogin $lastLogin
|
||||
* @property \Model\Notification $notification
|
||||
* @property \Model\Project $project
|
||||
* @property \Model\SubTask $subTask
|
||||
* @property \Model\Task $task
|
||||
* @property \Model\TaskHistory $taskHistory
|
||||
* @property \Model\User $user
|
||||
* @property \Model\Webhook $webhook
|
||||
*/
|
||||
abstract class Base
|
||||
{
|
||||
|
@ -38,21 +43,41 @@ abstract class Base
|
|||
/**
|
||||
* Event dispatcher instance
|
||||
*
|
||||
* @access protected
|
||||
* @access public
|
||||
* @var \Core\Event
|
||||
*/
|
||||
protected $event;
|
||||
public $event;
|
||||
|
||||
/**
|
||||
* Registry instance
|
||||
*
|
||||
* @access protected
|
||||
* @var \Core\Registry
|
||||
*/
|
||||
protected $registry;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \PicoDb\Database $db Database instance
|
||||
* @param \Core\Event $event Event dispatcher instance
|
||||
* @param \Core\Registry $registry Registry instance
|
||||
*/
|
||||
public function __construct(Database $db, Event $event)
|
||||
public function __construct(Registry $registry)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->event = $event;
|
||||
$this->registry = $registry;
|
||||
$this->db = $this->registry->shared('db');
|
||||
$this->event = $this->registry->shared('event');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load automatically models
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Model name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return Tool::loadModel($this->registry, $name);
|
||||
}
|
||||
}
|
||||
|
|
70
sources/app/Model/BaseHistory.php
Normal file
70
sources/app/Model/BaseHistory.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use PDO;
|
||||
use Core\Template;
|
||||
|
||||
/**
|
||||
* Task history model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
abstract class BaseHistory extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $table = '';
|
||||
|
||||
/**
|
||||
* Remove old event entries to avoid a large table
|
||||
*
|
||||
* @access public
|
||||
* @param integer $max Maximum number of items to keep in the table
|
||||
*/
|
||||
public function cleanup($max)
|
||||
{
|
||||
if ($this->db->table($this->table)->count() > $max) {
|
||||
|
||||
$this->db->execute('
|
||||
DELETE FROM '.$this->table.'
|
||||
WHERE id <= (
|
||||
SELECT id FROM (
|
||||
SELECT id FROM '.$this->table.' ORDER BY id DESC LIMIT 1 OFFSET '.$max.'
|
||||
) foo
|
||||
)');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all events for a given project
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getAllByProjectId($project_id)
|
||||
{
|
||||
return $this->db->table($this->table)
|
||||
->eq('project_id', $project_id)
|
||||
->desc('id')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event html content
|
||||
*
|
||||
* @access public
|
||||
* @param array $params Event properties
|
||||
* @return string
|
||||
*/
|
||||
public function getContent(array $params)
|
||||
{
|
||||
$tpl = new Template;
|
||||
return $tpl->load('event_'.str_replace('.', '_', $params['event_name']), $params);
|
||||
}
|
||||
}
|
|
@ -21,28 +21,14 @@ class Board extends Base
|
|||
const TABLE = 'columns';
|
||||
|
||||
/**
|
||||
* Save task positions for each column
|
||||
* Get Kanboard default columns
|
||||
*
|
||||
* @access public
|
||||
* @param array $values [['task_id' => X, 'column_id' => X, 'position' => X], ...]
|
||||
* @return boolean
|
||||
* @return array
|
||||
*/
|
||||
public function saveTasksPosition(array $values)
|
||||
public function getDefaultColumns()
|
||||
{
|
||||
$taskModel = new Task($this->db, $this->event);
|
||||
|
||||
$this->db->startTransaction();
|
||||
|
||||
foreach ($values as $value) {
|
||||
if (! $taskModel->move($value['task_id'], $value['column_id'], $value['position'])) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
return array(t('Backlog'), t('Ready'), t('Work in progress'), t('Done'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,19 +36,20 @@ class Board extends Base
|
|||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param array $columns List of columns title ['column1', 'column2', ...]
|
||||
* @param array $columns Column parameters [ 'title' => 'boo', 'task_limit' => 2 ... ]
|
||||
* @return boolean
|
||||
*/
|
||||
public function create($project_id, array $columns)
|
||||
{
|
||||
$position = 0;
|
||||
|
||||
foreach ($columns as $title) {
|
||||
foreach ($columns as $column) {
|
||||
|
||||
$values = array(
|
||||
'title' => $title,
|
||||
'title' => $column['title'],
|
||||
'position' => ++$position,
|
||||
'project_id' => $project_id,
|
||||
'task_limit' => $column['task_limit'],
|
||||
);
|
||||
|
||||
if (! $this->db->table(self::TABLE)->save($values)) {
|
||||
|
@ -73,17 +60,42 @@ class Board extends Base
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy board columns from a project to another one
|
||||
*
|
||||
* @author Antonio Rabelo
|
||||
* @param integer $project_from Project Template
|
||||
* @return integer $project_to Project that receives the copy
|
||||
* @return boolean
|
||||
*/
|
||||
public function duplicate($project_from, $project_to)
|
||||
{
|
||||
$columns = $this->db->table(Board::TABLE)
|
||||
->columns('title', 'task_limit')
|
||||
->eq('project_id', $project_from)
|
||||
->asc('position')
|
||||
->findAll();
|
||||
|
||||
return $this->board->create($project_to, $columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new column to the board
|
||||
*
|
||||
* @access public
|
||||
* @param array $values ['title' => X, 'project_id' => X]
|
||||
* @param integer $project_id Project id
|
||||
* @param string $title Column title
|
||||
* @param integer $task_limit Task limit
|
||||
* @return boolean
|
||||
*/
|
||||
public function add(array $values)
|
||||
public function addColumn($project_id, $title, $task_limit = 0)
|
||||
{
|
||||
$values['position'] = $this->getLastColumnPosition($values['project_id']) + 1;
|
||||
return $this->db->table(self::TABLE)->save($values);
|
||||
return $this->db->table(self::TABLE)->save(array(
|
||||
'project_id' => $project_id,
|
||||
'title' => $title,
|
||||
'task_limit' => $task_limit,
|
||||
'position' => $this->getLastColumnPosition($project_id) + 1,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -95,19 +107,20 @@ class Board extends Base
|
|||
*/
|
||||
public function update(array $values)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
$columns = array();
|
||||
|
||||
foreach (array('title', 'task_limit') as $field) {
|
||||
foreach ($values[$field] as $column_id => $field_value) {
|
||||
|
||||
if ($field === 'task_limit' && empty($field_value)) {
|
||||
$field_value = 0;
|
||||
}
|
||||
|
||||
$this->updateColumn($column_id, array($field => $field_value));
|
||||
foreach ($values[$field] as $column_id => $value) {
|
||||
$columns[$column_id][$field] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->startTransaction();
|
||||
|
||||
foreach ($columns as $column_id => $values) {
|
||||
$this->updateColumn($column_id, $values['title'], (int) $values['task_limit']);
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
|
@ -117,13 +130,17 @@ class Board extends Base
|
|||
* Update a column
|
||||
*
|
||||
* @access public
|
||||
* @param integer $column_id Column id
|
||||
* @param array $values Form values
|
||||
* @param integer $column_id Column id
|
||||
* @param string $title Column title
|
||||
* @param integer $task_limit Task limit
|
||||
* @return boolean
|
||||
*/
|
||||
public function updateColumn($column_id, array $values)
|
||||
public function updateColumn($column_id, $title, $task_limit = 0)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $column_id)->update($values);
|
||||
return $this->db->table(self::TABLE)->eq('id', $column_id)->update(array(
|
||||
'title' => $title,
|
||||
'task_limit' => $task_limit,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -189,7 +206,7 @@ class Board extends Base
|
|||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param array $filters
|
||||
* @param array $filters
|
||||
* @return array
|
||||
*/
|
||||
public function get($project_id, array $filters = array())
|
||||
|
@ -201,8 +218,7 @@ class Board extends Base
|
|||
$filters[] = array('column' => 'project_id', 'operator' => 'eq', 'value' => $project_id);
|
||||
$filters[] = array('column' => 'is_active', 'operator' => 'eq', 'value' => Task::STATUS_OPEN);
|
||||
|
||||
$taskModel = new Task($this->db, $this->event);
|
||||
$tasks = $taskModel->find($filters);
|
||||
$tasks = $this->task->find($filters);
|
||||
|
||||
foreach ($columns as &$column) {
|
||||
|
||||
|
|
|
@ -20,6 +20,19 @@ class Category extends Base
|
|||
*/
|
||||
const TABLE = 'project_has_categories';
|
||||
|
||||
/**
|
||||
* Return true if a category exists for a given project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $category_id Category id
|
||||
* @param integer $project_id Project id
|
||||
* @return boolean
|
||||
*/
|
||||
public function exists($category_id, $project_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $category_id)->eq('project_id', $project_id)->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a category by the id
|
||||
*
|
||||
|
@ -110,11 +123,45 @@ class Category extends Base
|
|||
public function remove($category_id)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
$r1 = $this->db->table(Task::TABLE)->eq('category_id', $category_id)->update(array('category_id' => 0));
|
||||
$r2 = $this->db->table(self::TABLE)->eq('id', $category_id)->remove();
|
||||
|
||||
$this->db->table(Task::TABLE)->eq('category_id', $category_id)->update(array('category_id' => 0));
|
||||
|
||||
if (! $this->db->table(self::TABLE)->eq('id', $category_id)->remove()) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return $r1 && $r2;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate categories from a project to another one
|
||||
*
|
||||
* @author Antonio Rabelo
|
||||
* @param integer $project_from Project Template
|
||||
* @return integer $project_to Project that receives the copy
|
||||
* @return boolean
|
||||
*/
|
||||
public function duplicate($project_from, $project_to)
|
||||
{
|
||||
$categories = $this->db->table(self::TABLE)
|
||||
->columns('name')
|
||||
->eq('project_id', $project_from)
|
||||
->asc('name')
|
||||
->findAll();
|
||||
|
||||
foreach ($categories as $category) {
|
||||
|
||||
$category['project_id'] = $project_to;
|
||||
|
||||
if (! $this->category->create($category)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -126,12 +173,12 @@ class Category extends Base
|
|||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
$rules = array(
|
||||
new Validators\Required('project_id', t('The project id is required')),
|
||||
new Validators\Integer('project_id', t('The project id must be an integer')),
|
||||
new Validators\Required('name', t('The name is required')),
|
||||
new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50)
|
||||
));
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
|
@ -148,18 +195,31 @@ class Category extends Base
|
|||
*/
|
||||
public function validateModification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
$rules = array(
|
||||
new Validators\Required('id', t('The id is required')),
|
||||
new Validators\Integer('id', t('The id must be an integer')),
|
||||
new Validators\Required('project_id', t('The project id is required')),
|
||||
new Validators\Integer('project_id', t('The project id must be an integer')),
|
||||
new Validators\Required('name', t('The name is required')),
|
||||
new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50)
|
||||
));
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common validation rules
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function commonValidationRules()
|
||||
{
|
||||
return array(
|
||||
new Validators\Integer('id', t('The id must be an integer')),
|
||||
new Validators\Integer('project_id', t('The project id must be an integer')),
|
||||
new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,14 @@ class Comment extends Base
|
|||
*/
|
||||
const TABLE = 'comments';
|
||||
|
||||
/**
|
||||
* Events
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const EVENT_UPDATE = 'comment.update';
|
||||
const EVENT_CREATE = 'comment.create';
|
||||
|
||||
/**
|
||||
* Get all comments for a given task
|
||||
*
|
||||
|
@ -37,7 +45,8 @@ class Comment extends Base
|
|||
self::TABLE.'.task_id',
|
||||
self::TABLE.'.user_id',
|
||||
self::TABLE.'.comment',
|
||||
User::TABLE.'.username'
|
||||
User::TABLE.'.username',
|
||||
User::TABLE.'.name'
|
||||
)
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->orderBy(self::TABLE.'.date', 'ASC')
|
||||
|
@ -62,7 +71,8 @@ class Comment extends Base
|
|||
self::TABLE.'.user_id',
|
||||
self::TABLE.'.date',
|
||||
self::TABLE.'.comment',
|
||||
User::TABLE.'.username'
|
||||
User::TABLE.'.username',
|
||||
User::TABLE.'.name'
|
||||
)
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->eq(self::TABLE.'.id', $comment_id)
|
||||
|
@ -95,7 +105,14 @@ class Comment extends Base
|
|||
{
|
||||
$values['date'] = time();
|
||||
|
||||
return $this->db->table(self::TABLE)->save($values);
|
||||
if ($this->db->table(self::TABLE)->save($values)) {
|
||||
|
||||
$values['id'] = $this->db->getConnection()->getLastId();
|
||||
$this->event->trigger(self::EVENT_CREATE, $values);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,10 +124,14 @@ class Comment extends Base
|
|||
*/
|
||||
public function update(array $values)
|
||||
{
|
||||
return $this->db
|
||||
$result = $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $values['id'])
|
||||
->update(array('comment' => $values['comment']));
|
||||
|
||||
$this->event->trigger(self::EVENT_UPDATE, $values);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,13 +155,12 @@ class Comment extends Base
|
|||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('task_id', t('This value is required')),
|
||||
new Validators\Integer('task_id', t('This value must be an integer')),
|
||||
$rules = array(
|
||||
new Validators\Required('user_id', t('This value is required')),
|
||||
new Validators\Integer('user_id', t('This value must be an integer')),
|
||||
new Validators\Required('comment', t('Comment is required'))
|
||||
));
|
||||
new Validators\Required('task_id', t('This value is required')),
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
|
@ -157,15 +177,31 @@ class Comment extends Base
|
|||
*/
|
||||
public function validateModification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
$rules = array(
|
||||
new Validators\Required('id', t('This value is required')),
|
||||
new Validators\Integer('id', t('This value must be an integer')),
|
||||
new Validators\Required('comment', t('Comment is required'))
|
||||
));
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common validation rules
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function commonValidationRules()
|
||||
{
|
||||
return array(
|
||||
new Validators\Integer('id', t('This value must be an integer')),
|
||||
new Validators\Integer('task_id', t('This value must be an integer')),
|
||||
new Validators\Integer('user_id', t('This value must be an integer')),
|
||||
new Validators\Required('comment', t('Comment is required'))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
152
sources/app/Model/CommentHistory.php
Normal file
152
sources/app/Model/CommentHistory.php
Normal file
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use PDO;
|
||||
use Core\Registry;
|
||||
use Event\CommentHistoryListener;
|
||||
|
||||
/**
|
||||
* Comment history model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class CommentHistory extends BaseHistory
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'comment_has_events';
|
||||
|
||||
/**
|
||||
* Maximum number of events
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const MAX_EVENTS = 5000;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Core\Registry $registry Registry instance
|
||||
*/
|
||||
public function __construct(Registry $registry)
|
||||
{
|
||||
parent::__construct($registry);
|
||||
$this->table = self::TABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new event
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $comment_id Comment id
|
||||
* @param integer $creator_id Author of the event (user id)
|
||||
* @param string $event_name Task event name
|
||||
* @param string $data Current comment
|
||||
* @return boolean
|
||||
*/
|
||||
public function create($project_id, $task_id, $comment_id, $creator_id, $event_name, $data)
|
||||
{
|
||||
$values = array(
|
||||
'project_id' => $project_id,
|
||||
'task_id' => $task_id,
|
||||
'comment_id' => $comment_id,
|
||||
'creator_id' => $creator_id,
|
||||
'event_name' => $event_name,
|
||||
'date_creation' => time(),
|
||||
'data' => $data,
|
||||
);
|
||||
|
||||
$this->db->startTransaction();
|
||||
|
||||
$this->cleanup(self::MAX_EVENTS - 1);
|
||||
$result = $this->db->table(self::TABLE)->insert($values);
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all necessary content to display activity feed
|
||||
*
|
||||
* $author_name
|
||||
* $author_username
|
||||
* $task['id', 'title', 'position', 'column_name']
|
||||
*/
|
||||
public function getAllContentByProjectId($project_id, $limit = 50)
|
||||
{
|
||||
$sql = '
|
||||
SELECT
|
||||
comment_has_events.id,
|
||||
comment_has_events.date_creation,
|
||||
comment_has_events.event_name,
|
||||
comment_has_events.data as comment,
|
||||
comment_has_events.task_id,
|
||||
tasks.title as task_title,
|
||||
users.username as author_username,
|
||||
users.name as author_name
|
||||
FROM comment_has_events
|
||||
LEFT JOIN users ON users.id=comment_has_events.creator_id
|
||||
LEFT JOIN tasks ON tasks.id=comment_has_events.task_id
|
||||
WHERE comment_has_events.project_id = ?
|
||||
ORDER BY comment_has_events.id DESC
|
||||
LIMIT '.$limit.' OFFSET 0
|
||||
';
|
||||
|
||||
$rq = $this->db->execute($sql, array($project_id));
|
||||
$events = $rq->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($events as &$event) {
|
||||
$event['author'] = $event['author_name'] ?: $event['author_username'];
|
||||
$event['event_title'] = $this->getTitle($event);
|
||||
$event['event_content'] = $this->getContent($event);
|
||||
$event['event_type'] = 'comment';
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event title (translated)
|
||||
*
|
||||
* @access public
|
||||
* @param array $event Event properties
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(array $event)
|
||||
{
|
||||
$titles = array(
|
||||
Comment::EVENT_UPDATE => t('%s updated a comment on the task #%d', $event['author'], $event['task_id']),
|
||||
Comment::EVENT_CREATE => t('%s commented on the task #%d', $event['author'], $event['task_id']),
|
||||
);
|
||||
|
||||
return isset($titles[$event['event_name']]) ? $titles[$event['event_name']] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach events to be able to record the history
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function attachEvents()
|
||||
{
|
||||
$events = array(
|
||||
Comment::EVENT_UPDATE,
|
||||
Comment::EVENT_CREATE,
|
||||
);
|
||||
|
||||
$listener = new CommentHistoryListener($this);
|
||||
|
||||
foreach ($events as $event_name) {
|
||||
$this->event->attach($event_name, $listener);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,20 +42,20 @@ class Config extends Base
|
|||
*/
|
||||
public function getLanguages()
|
||||
{
|
||||
$languages = array(
|
||||
'de_DE' => t('German'),
|
||||
'en_US' => t('English'),
|
||||
'es_ES' => t('Spanish'),
|
||||
'fr_FR' => t('French'),
|
||||
'pl_PL' => t('Polish'),
|
||||
'pt_BR' => t('Portuguese (Brazilian)'),
|
||||
'sv_SE' => t('Swedish'),
|
||||
'zh_CN' => t('Chinese (Simplified)'),
|
||||
// Sorted by value
|
||||
return array(
|
||||
'de_DE' => 'Deutsch',
|
||||
'en_US' => 'English',
|
||||
'es_ES' => 'Español',
|
||||
'fr_FR' => 'Français',
|
||||
'it_IT' => 'Italiano',
|
||||
'pl_PL' => 'Polski',
|
||||
'pt_BR' => 'Português (Brasil)',
|
||||
'ru_RU' => 'Русский',
|
||||
'fi_FI' => 'Suomi',
|
||||
'sv_SE' => 'Svenska',
|
||||
'zh_CN' => '中文(简体)',
|
||||
);
|
||||
|
||||
asort($languages);
|
||||
|
||||
return $languages;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,7 +72,7 @@ class Config extends Base
|
|||
$_SESSION['config'] = $this->getAll();
|
||||
}
|
||||
|
||||
if (isset($_SESSION['config'][$name])) {
|
||||
if (! empty($_SESSION['config'][$name])) {
|
||||
return $_SESSION['config'][$name];
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,13 @@ class File extends Base
|
|||
*/
|
||||
const BASE_PATH = 'data/files/';
|
||||
|
||||
/**
|
||||
* Events
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const EVENT_CREATE = 'file.create';
|
||||
|
||||
/**
|
||||
* Get a file by the id
|
||||
*
|
||||
|
@ -82,6 +89,8 @@ class File extends Base
|
|||
*/
|
||||
public function create($task_id, $name, $path, $is_image)
|
||||
{
|
||||
$this->event->trigger(self::EVENT_CREATE, array('task_id' => $task_id, 'name' => $name));
|
||||
|
||||
return $this->db->table(self::TABLE)->save(array(
|
||||
'task_id' => $task_id,
|
||||
'name' => $name,
|
||||
|
|
|
@ -24,17 +24,6 @@ class LastLogin extends Base
|
|||
*/
|
||||
const NB_LOGINS = 10;
|
||||
|
||||
/**
|
||||
* Authentication methods
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const AUTH_DATABASE = 'database';
|
||||
const AUTH_REMEMBER_ME = 'remember_me';
|
||||
const AUTH_LDAP = 'ldap';
|
||||
const AUTH_GOOGLE = 'google';
|
||||
const AUTH_GITHUB = 'github';
|
||||
|
||||
/**
|
||||
* Create a new record
|
||||
*
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* LDAP model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Ldap extends Base
|
||||
{
|
||||
/**
|
||||
* Authenticate a user
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Username
|
||||
* @param string $password Password
|
||||
* @return null|boolean
|
||||
*/
|
||||
public function authenticate($username, $password)
|
||||
{
|
||||
if (! function_exists('ldap_connect')) {
|
||||
die('The PHP LDAP extension is required');
|
||||
}
|
||||
|
||||
// Skip SSL certificate verification
|
||||
if (! LDAP_SSL_VERIFY) {
|
||||
putenv('LDAPTLS_REQCERT=never');
|
||||
}
|
||||
|
||||
$ldap = ldap_connect(LDAP_SERVER, LDAP_PORT);
|
||||
|
||||
if (! is_resource($ldap)) {
|
||||
die('Unable to connect to the LDAP server: "'.LDAP_SERVER.'"');
|
||||
}
|
||||
|
||||
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
|
||||
|
||||
if (! @ldap_bind($ldap, LDAP_USERNAME, LDAP_PASSWORD)) {
|
||||
die('Unable to bind to the LDAP server: "'.LDAP_SERVER.'"');
|
||||
}
|
||||
|
||||
$sr = @ldap_search($ldap, LDAP_ACCOUNT_BASE, sprintf(LDAP_USER_PATTERN, $username), array(LDAP_ACCOUNT_FULLNAME, LDAP_ACCOUNT_EMAIL));
|
||||
|
||||
if ($sr === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$info = ldap_get_entries($ldap, $sr);
|
||||
|
||||
// User not found
|
||||
if (count($info) == 0 || $info['count'] == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (@ldap_bind($ldap, $info[0]['dn'], $password)) {
|
||||
return $this->create($username, $info[0][LDAP_ACCOUNT_FULLNAME][0], $info[0][LDAP_ACCOUNT_EMAIL][0]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create automatically a new local user after the LDAP authentication
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Username
|
||||
* @param string $name Name of the user
|
||||
* @param string $email Email address
|
||||
* @return bool
|
||||
*/
|
||||
public function create($username, $name, $email)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
$user = $userModel->getByUsername($username);
|
||||
|
||||
// There is an existing user account
|
||||
if ($user) {
|
||||
|
||||
if ($user['is_ldap_user'] == 1) {
|
||||
|
||||
// LDAP user already created
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
||||
// There is already a local user with that username
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a LDAP user
|
||||
$values = array(
|
||||
'username' => $username,
|
||||
'name' => $name,
|
||||
'email' => $email,
|
||||
'is_admin' => 0,
|
||||
'is_ldap_user' => 1,
|
||||
);
|
||||
|
||||
return $userModel->create($values);
|
||||
}
|
||||
}
|
239
sources/app/Model/Notification.php
Normal file
239
sources/app/Model/Notification.php
Normal file
|
@ -0,0 +1,239 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use Core\Translator;
|
||||
use Core\Template;
|
||||
use Event\TaskNotificationListener;
|
||||
use Event\CommentNotificationListener;
|
||||
use Event\FileNotificationListener;
|
||||
use Event\SubTaskNotificationListener;
|
||||
use Swift_Message;
|
||||
use Swift_Mailer;
|
||||
|
||||
/**
|
||||
* Notification model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Notification extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'user_has_notifications';
|
||||
|
||||
/**
|
||||
* Get the list of users to send the notification for a given project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getUsersList($project_id)
|
||||
{
|
||||
$users = $this->db->table(User::TABLE)
|
||||
->columns('id', 'username', 'name', 'email')
|
||||
->eq('notifications_enabled', '1')
|
||||
->neq('email', '')
|
||||
->findAll();
|
||||
|
||||
foreach ($users as $index => $user) {
|
||||
|
||||
$projects = $this->db->table(self::TABLE)
|
||||
->eq('user_id', $user['id'])
|
||||
->findAllByColumn('project_id');
|
||||
|
||||
// The user have selected only some projects
|
||||
if (! empty($projects)) {
|
||||
|
||||
// If the user didn't select this project we remove that guy from the list
|
||||
if (! in_array($project_id, $projects)) {
|
||||
unset($users[$index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach events
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function attachEvents()
|
||||
{
|
||||
$this->event->attach(File::EVENT_CREATE, new FileNotificationListener($this, 'notification_file_creation'));
|
||||
|
||||
$this->event->attach(Comment::EVENT_CREATE, new CommentNotificationListener($this, 'notification_comment_creation'));
|
||||
$this->event->attach(Comment::EVENT_UPDATE, new CommentNotificationListener($this, 'notification_comment_update'));
|
||||
|
||||
$this->event->attach(SubTask::EVENT_CREATE, new SubTaskNotificationListener($this, 'notification_subtask_creation'));
|
||||
$this->event->attach(SubTask::EVENT_UPDATE, new SubTaskNotificationListener($this, 'notification_subtask_update'));
|
||||
|
||||
$this->event->attach(Task::EVENT_CREATE, new TaskNotificationListener($this, 'notification_task_creation'));
|
||||
$this->event->attach(Task::EVENT_UPDATE, new TaskNotificationListener($this, 'notification_task_update'));
|
||||
$this->event->attach(Task::EVENT_CLOSE, new TaskNotificationListener($this, 'notification_task_close'));
|
||||
$this->event->attach(Task::EVENT_OPEN, new TaskNotificationListener($this, 'notification_task_open'));
|
||||
$this->event->attach(Task::EVENT_MOVE_COLUMN, new TaskNotificationListener($this, 'notification_task_move_column'));
|
||||
$this->event->attach(Task::EVENT_MOVE_POSITION, new TaskNotificationListener($this, 'notification_task_move_position'));
|
||||
$this->event->attach(Task::EVENT_ASSIGNEE_CHANGE, new TaskNotificationListener($this, 'notification_task_assignee_change'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the email notifications
|
||||
*
|
||||
* @access public
|
||||
* @param string $template Template name
|
||||
* @param array $users List of users
|
||||
* @param array $data Template data
|
||||
*/
|
||||
public function sendEmails($template, array $users, array $data)
|
||||
{
|
||||
$transport = $this->registry->shared('mailer');
|
||||
$mailer = Swift_Mailer::newInstance($transport);
|
||||
|
||||
$message = Swift_Message::newInstance()
|
||||
->setSubject($this->getMailSubject($template, $data))
|
||||
->setFrom(array(MAIL_FROM => 'Kanboard'))
|
||||
->setBody($this->getMailContent($template, $data), 'text/html');
|
||||
|
||||
foreach ($users as $user) {
|
||||
$message->setTo(array($user['email'] => $user['name'] ?: $user['username']));
|
||||
$mailer->send($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail subject for a given template name
|
||||
*
|
||||
* @access public
|
||||
* @param string $template Template name
|
||||
* @param array $data Template data
|
||||
*/
|
||||
public function getMailSubject($template, array $data)
|
||||
{
|
||||
switch ($template) {
|
||||
case 'notification_file_creation':
|
||||
$subject = e('[%s][New attachment] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_comment_creation':
|
||||
$subject = e('[%s][New comment] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_comment_update':
|
||||
$subject = e('[%s][Comment updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_subtask_creation':
|
||||
$subject = e('[%s][New subtask] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_subtask_update':
|
||||
$subject = e('[%s][Subtask updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_creation':
|
||||
$subject = e('[%s][New task] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_update':
|
||||
$subject = e('[%s][Task updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_close':
|
||||
$subject = e('[%s][Task closed] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_open':
|
||||
$subject = e('[%s][Task opened] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_move_column':
|
||||
$subject = e('[%s][Column Change] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_move_position':
|
||||
$subject = e('[%s][Position Change] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_assignee_change':
|
||||
$subject = e('[%s][Assignee Change] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_due':
|
||||
$subject = e('[%s][Due tasks]', $data['project']);
|
||||
break;
|
||||
default:
|
||||
$subject = e('[Kanboard] Notification');
|
||||
}
|
||||
|
||||
return $subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail content for a given template name
|
||||
*
|
||||
* @access public
|
||||
* @param string $template Template name
|
||||
* @param array $data Template data
|
||||
*/
|
||||
public function getMailContent($template, array $data)
|
||||
{
|
||||
$tpl = new Template;
|
||||
return $tpl->load($template, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save settings for the given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param array $values Form values
|
||||
*/
|
||||
public function saveSettings($user_id, array $values)
|
||||
{
|
||||
// Delete all selected projects
|
||||
$this->db->table(self::TABLE)->eq('user_id', $user_id)->remove();
|
||||
|
||||
if (isset($values['notifications_enabled']) && $values['notifications_enabled'] == 1) {
|
||||
|
||||
// Activate notifications
|
||||
$this->db->table(User::TABLE)->eq('id', $user_id)->update(array(
|
||||
'notifications_enabled' => '1'
|
||||
));
|
||||
|
||||
// Save selected projects
|
||||
if (! empty($values['projects'])) {
|
||||
|
||||
foreach ($values['projects'] as $project_id => $checkbox_value) {
|
||||
$this->db->table(self::TABLE)->insert(array(
|
||||
'user_id' => $user_id,
|
||||
'project_id' => $project_id,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Disable notifications
|
||||
$this->db->table(User::TABLE)->eq('id', $user_id)->update(array(
|
||||
'notifications_enabled' => '0'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read user settings to display the form
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function readSettings($user_id)
|
||||
{
|
||||
$values = array();
|
||||
$values['notifications_enabled'] = $this->db->table(User::TABLE)->eq('id', $user_id)->findOneColumn('notifications_enabled');
|
||||
|
||||
$projects = $this->db->table(self::TABLE)->eq('user_id', $user_id)->findAllByColumn('project_id');
|
||||
|
||||
foreach ($projects as $project_id) {
|
||||
$values['project_'.$project_id] = true;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ namespace Model;
|
|||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
use Event\TaskModification;
|
||||
use Event\ProjectModificationDate;
|
||||
use Core\Security;
|
||||
|
||||
/**
|
||||
|
@ -55,10 +55,9 @@ class Project extends Base
|
|||
public function getUsersList($project_id, $prepend_unassigned = true, $prepend_everybody = false)
|
||||
{
|
||||
$allowed_users = $this->getAllowedUsers($project_id);
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
if (empty($allowed_users)) {
|
||||
$allowed_users = $userModel->getList();
|
||||
$allowed_users = $this->user->getList();
|
||||
}
|
||||
|
||||
if ($prepend_unassigned) {
|
||||
|
@ -81,12 +80,23 @@ class Project extends Base
|
|||
*/
|
||||
public function getAllowedUsers($project_id)
|
||||
{
|
||||
return $this->db
|
||||
$users = $this->db
|
||||
->table(self::TABLE_USERS)
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->eq('project_id', $project_id)
|
||||
->asc('username')
|
||||
->listing('user_id', 'username');
|
||||
->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name')
|
||||
->findAll();
|
||||
|
||||
$result = array();
|
||||
|
||||
foreach ($users as $user) {
|
||||
$result[$user['id']] = $user['name'] ?: $user['username'];
|
||||
}
|
||||
|
||||
asort($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,8 +113,7 @@ class Project extends Base
|
|||
'not_allowed' => array(),
|
||||
);
|
||||
|
||||
$userModel = new User($this->db, $this->event);
|
||||
$all_users = $userModel->getList();
|
||||
$all_users = $this->user->getList();
|
||||
|
||||
$users['allowed'] = $this->getAllowedUsers($project_id);
|
||||
|
||||
|
@ -218,7 +227,7 @@ class Project extends Base
|
|||
*/
|
||||
public function getByToken($token)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('token', $token)->findOne();
|
||||
return $this->db->table(self::TABLE)->eq('token', $token)->eq('is_public', 1)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,50 +245,23 @@ class Project extends Base
|
|||
* Get all projects, optionaly fetch stats for each project and can check users permissions
|
||||
*
|
||||
* @access public
|
||||
* @param bool $fetch_stats If true, return metrics about each projects
|
||||
* @param bool $check_permissions If true, remove projects not allowed for the current user
|
||||
* @param bool $filter_permissions If true, remove projects not allowed for the current user
|
||||
* @return array
|
||||
*/
|
||||
public function getAll($fetch_stats = false, $check_permissions = false)
|
||||
public function getAll($filter_permissions = false)
|
||||
{
|
||||
if (! $fetch_stats) {
|
||||
return $this->db->table(self::TABLE)->asc('name')->findAll();
|
||||
}
|
||||
$projects = $this->db->table(self::TABLE)->asc('name')->findAll();
|
||||
|
||||
$this->db->startTransaction();
|
||||
if ($filter_permissions) {
|
||||
|
||||
$projects = $this->db
|
||||
->table(self::TABLE)
|
||||
->asc('name')
|
||||
->findAll();
|
||||
foreach ($projects as $key => $project) {
|
||||
|
||||
$boardModel = new Board($this->db, $this->event);
|
||||
$taskModel = new Task($this->db, $this->event);
|
||||
$aclModel = new Acl($this->db, $this->event);
|
||||
|
||||
foreach ($projects as $pkey => &$project) {
|
||||
|
||||
if ($check_permissions && ! $this->isUserAllowed($project['id'], $aclModel->getUserId())) {
|
||||
unset($projects[$pkey]);
|
||||
}
|
||||
else {
|
||||
|
||||
$columns = $boardModel->getcolumns($project['id']);
|
||||
$project['nb_active_tasks'] = 0;
|
||||
|
||||
foreach ($columns as &$column) {
|
||||
$column['nb_active_tasks'] = $taskModel->countByColumnId($project['id'], $column['id']);
|
||||
$project['nb_active_tasks'] += $column['nb_active_tasks'];
|
||||
if (! $this->isUserAllowed($project['id'], $this->acl->getUserId())) {
|
||||
unset($projects[$key]);
|
||||
}
|
||||
|
||||
$project['columns'] = $columns;
|
||||
$project['nb_tasks'] = $taskModel->countByProjectId($project['id']);
|
||||
$project['nb_inactive_tasks'] = $project['nb_tasks'] - $project['nb_active_tasks'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return $projects;
|
||||
}
|
||||
|
||||
|
@ -377,6 +359,124 @@ class Project extends Base
|
|||
return $this->filterListByAccess($this->getListByStatus(self::ACTIVE), $user_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather some task metrics for a given project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getStats($project_id)
|
||||
{
|
||||
$stats = array();
|
||||
$columns = $this->board->getcolumns($project_id);
|
||||
$stats['nb_active_tasks'] = 0;
|
||||
|
||||
foreach ($columns as &$column) {
|
||||
$column['nb_active_tasks'] = $this->task->countByColumnId($project_id, $column['id']);
|
||||
$stats['nb_active_tasks'] += $column['nb_active_tasks'];
|
||||
}
|
||||
|
||||
$stats['columns'] = $columns;
|
||||
$stats['nb_tasks'] = $this->task->countByProjectId($project_id);
|
||||
$stats['nb_inactive_tasks'] = $stats['nb_tasks'] - $stats['nb_active_tasks'];
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a project from another one.
|
||||
*
|
||||
* @author Antonio Rabelo
|
||||
* @param integer $project_id Project Id
|
||||
* @return integer Cloned Project Id
|
||||
*/
|
||||
public function createProjectFromAnotherProject($project_id)
|
||||
{
|
||||
$project_name = $this->db->table(self::TABLE)->eq('id', $project_id)->findOneColumn('name');
|
||||
|
||||
$project = array(
|
||||
'name' => $project_name.' ('.t('Clone').')',
|
||||
'is_active' => true,
|
||||
'last_modified' => 0,
|
||||
'token' => '',
|
||||
);
|
||||
|
||||
if (! $this->db->table(self::TABLE)->save($project)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->db->getConnection()->getLastId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy user access from a project to another one
|
||||
*
|
||||
* @author Antonio Rabelo
|
||||
* @param integer $project_from Project Template
|
||||
* @return integer $project_to Project that receives the copy
|
||||
* @return boolean
|
||||
*/
|
||||
public function duplicateUsers($project_from, $project_to)
|
||||
{
|
||||
$users = $this->getAllowedUsers($project_from);
|
||||
|
||||
foreach ($users as $user_id => $name) {
|
||||
if (! $this->allowUser($project_to, $user_id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone a project
|
||||
*
|
||||
* @author Antonio Rabelo
|
||||
* @param integer $project_id Project Id
|
||||
* @return integer Cloned Project Id
|
||||
*/
|
||||
public function duplicate($project_id)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
// Get the cloned project Id
|
||||
$clone_project_id = $this->createProjectFromAnotherProject($project_id);
|
||||
if (! $clone_project_id) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clone Board
|
||||
if (! $this->board->duplicate($project_id, $clone_project_id)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clone Categories
|
||||
if (! $this->category->duplicate($project_id, $clone_project_id)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clone Allowed Users
|
||||
if (! $this->duplicateUsers($project_id, $clone_project_id)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clone Actions
|
||||
if (! $this->action->duplicate($project_id, $clone_project_id)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return (int) $clone_project_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a project
|
||||
*
|
||||
|
@ -388,7 +488,8 @@ class Project extends Base
|
|||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
$values['token'] = Security::generateToken();
|
||||
$values['token'] = '';
|
||||
$values['last_modified'] = time();
|
||||
|
||||
if (! $this->db->table(self::TABLE)->save($values)) {
|
||||
$this->db->cancelTransaction();
|
||||
|
@ -396,15 +497,19 @@ class Project extends Base
|
|||
}
|
||||
|
||||
$project_id = $this->db->getConnection()->getLastId();
|
||||
$column_names = explode(',', $this->config->get('default_columns', implode(',', $this->board->getDefaultColumns())));
|
||||
$columns = array();
|
||||
|
||||
$boardModel = new Board($this->db, $this->event);
|
||||
$boardModel->create($project_id, array(
|
||||
t('Backlog'),
|
||||
t('Ready'),
|
||||
t('Work in progress'),
|
||||
t('Done'),
|
||||
));
|
||||
foreach ($column_names as $column_name) {
|
||||
|
||||
$column_name = trim($column_name);
|
||||
|
||||
if (! empty($column_name)) {
|
||||
$columns[] = array('title' => $column_name, 'task_limit' => 0);
|
||||
}
|
||||
}
|
||||
|
||||
$this->board->create($project_id, $columns);
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return (int) $project_id;
|
||||
|
@ -435,7 +540,7 @@ class Project extends Base
|
|||
*/
|
||||
public function updateModificationDate($project_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $project_id)->save(array(
|
||||
return $this->db->table(self::TABLE)->eq('id', $project_id)->update(array(
|
||||
'last_modified' => time()
|
||||
));
|
||||
}
|
||||
|
@ -449,7 +554,8 @@ class Project extends Base
|
|||
*/
|
||||
public function update(array $values)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $values['id'])->save($values);
|
||||
return $this->exists($values['id']) &&
|
||||
$this->db->table(self::TABLE)->eq('id', $values['id'])->save($values);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -464,6 +570,18 @@ class Project extends Base
|
|||
return $this->db->table(self::TABLE)->eq('id', $project_id)->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the project exists
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return boolean
|
||||
*/
|
||||
public function exists($project_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $project_id)->count() === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a project
|
||||
*
|
||||
|
@ -473,10 +591,11 @@ class Project extends Base
|
|||
*/
|
||||
public function enable($project_id)
|
||||
{
|
||||
return $this->db
|
||||
return $this->exists($project_id) &&
|
||||
$this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $project_id)
|
||||
->save(array('is_active' => 1));
|
||||
->update(array('is_active' => 1));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -488,10 +607,60 @@ class Project extends Base
|
|||
*/
|
||||
public function disable($project_id)
|
||||
{
|
||||
return $this->db
|
||||
return $this->exists($project_id) &&
|
||||
$this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $project_id)
|
||||
->save(array('is_active' => 0));
|
||||
->update(array('is_active' => 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable public access for a project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return bool
|
||||
*/
|
||||
public function enablePublicAccess($project_id)
|
||||
{
|
||||
return $this->exists($project_id) &&
|
||||
$this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $project_id)
|
||||
->save(array('is_public' => 1, 'token' => Security::generateToken()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable public access for a project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return bool
|
||||
*/
|
||||
public function disablePublicAccess($project_id)
|
||||
{
|
||||
return $this->exists($project_id) &&
|
||||
$this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $project_id)
|
||||
->save(array('is_public' => 0, 'token' => ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Common validation rules
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function commonValidationRules()
|
||||
{
|
||||
return array(
|
||||
new Validators\Integer('id', t('This value must be an integer')),
|
||||
new Validators\Integer('is_active', t('This value must be an integer')),
|
||||
new Validators\Required('name', t('The project name is required')),
|
||||
new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\Unique('name', t('This project must be unique'), $this->db->getConnection(), self::TABLE),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -503,11 +672,7 @@ class Project extends Base
|
|||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('name', t('The project name is required')),
|
||||
new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\Unique('name', t('This project must be unique'), $this->db->getConnection(), self::TABLE)
|
||||
));
|
||||
$v = new Validator($values, $this->commonValidationRules());
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
|
@ -524,14 +689,11 @@ class Project extends Base
|
|||
*/
|
||||
public function validateModification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('id', t('The project id is required')),
|
||||
new Validators\Integer('id', t('This value must be an integer')),
|
||||
new Validators\Required('name', t('The project name is required')),
|
||||
new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\Unique('name', t('This project must be unique'), $this->db->getConnection(), self::TABLE),
|
||||
new Validators\Integer('is_active', t('This value must be an integer'))
|
||||
));
|
||||
$rules = array(
|
||||
new Validators\Required('id', t('This value is required')),
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
|
@ -569,16 +731,49 @@ class Project extends Base
|
|||
public function attachEvents()
|
||||
{
|
||||
$events = array(
|
||||
Task::EVENT_UPDATE,
|
||||
Task::EVENT_CREATE,
|
||||
Task::EVENT_CREATE_UPDATE,
|
||||
Task::EVENT_CLOSE,
|
||||
Task::EVENT_OPEN,
|
||||
Task::EVENT_MOVE_COLUMN,
|
||||
Task::EVENT_MOVE_POSITION,
|
||||
Task::EVENT_ASSIGNEE_CHANGE,
|
||||
);
|
||||
|
||||
$listener = new TaskModification($this);
|
||||
$listener = new ProjectModificationDate($this);
|
||||
|
||||
foreach ($events as $event_name) {
|
||||
$this->event->attach($event_name, $listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project activity
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getActivity($project_id)
|
||||
{
|
||||
$activity = array();
|
||||
$tasks = $this->taskHistory->getAllContentByProjectId($project_id, 25);
|
||||
$comments = $this->commentHistory->getAllContentByProjectId($project_id, 25);
|
||||
$subtasks = $this->subtaskHistory->getAllContentByProjectId($project_id, 25);
|
||||
|
||||
foreach ($tasks as &$task) {
|
||||
$activity[$task['date_creation'].'-'.$task['id']] = $task;
|
||||
}
|
||||
|
||||
foreach ($subtasks as &$subtask) {
|
||||
$activity[$subtask['date_creation'].'-'.$subtask['id']] = $subtask;
|
||||
}
|
||||
|
||||
foreach ($comments as &$comment) {
|
||||
$activity[$comment['date_creation'].'-'.$comment['id']] = $comment;
|
||||
}
|
||||
|
||||
krsort($activity);
|
||||
|
||||
return $activity;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,14 @@ class SubTask extends Base
|
|||
*/
|
||||
const STATUS_TODO = 0;
|
||||
|
||||
/**
|
||||
* Events
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const EVENT_UPDATE = 'subtask.update';
|
||||
const EVENT_CREATE = 'subtask.create';
|
||||
|
||||
/**
|
||||
* Get available status
|
||||
*
|
||||
|
@ -72,7 +80,7 @@ class SubTask extends Base
|
|||
$status = $this->getStatusList();
|
||||
$subtasks = $this->db->table(self::TABLE)
|
||||
->eq('task_id', $task_id)
|
||||
->columns(self::TABLE.'.*', User::TABLE.'.username')
|
||||
->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name')
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->findAll();
|
||||
|
||||
|
@ -88,21 +96,37 @@ class SubTask extends Base
|
|||
*
|
||||
* @access public
|
||||
* @param integer $subtask_id Subtask id
|
||||
* @param bool $more Fetch more data
|
||||
* @return array
|
||||
*/
|
||||
public function getById($subtask_id)
|
||||
public function getById($subtask_id, $more = false)
|
||||
{
|
||||
if ($more) {
|
||||
|
||||
$subtask = $this->db->table(self::TABLE)
|
||||
->eq(self::TABLE.'.id', $subtask_id)
|
||||
->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name')
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->findOne();
|
||||
|
||||
if ($subtask) {
|
||||
$status = $this->getStatusList();
|
||||
$subtask['status_name'] = $status[$subtask['status']];
|
||||
}
|
||||
|
||||
return $subtask;
|
||||
}
|
||||
|
||||
return $this->db->table(self::TABLE)->eq('id', $subtask_id)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create
|
||||
* Prepare data before insert/update
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return bool
|
||||
*/
|
||||
public function create(array $values)
|
||||
public function prepare(array &$values)
|
||||
{
|
||||
if (isset($values['another_subtask'])) {
|
||||
unset($values['another_subtask']);
|
||||
|
@ -115,8 +139,26 @@ class SubTask extends Base
|
|||
if (isset($values['time_spent']) && empty($values['time_spent'])) {
|
||||
$values['time_spent'] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->db->table(self::TABLE)->save($values);
|
||||
/**
|
||||
* Create
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return bool
|
||||
*/
|
||||
public function create(array $values)
|
||||
{
|
||||
$this->prepare($values);
|
||||
$result = $this->db->table(self::TABLE)->save($values);
|
||||
|
||||
if ($result) {
|
||||
$values['id'] = $this->db->getConnection()->getLastId();
|
||||
$this->event->trigger(self::EVENT_CREATE, $values);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,15 +170,14 @@ class SubTask extends Base
|
|||
*/
|
||||
public function update(array $values)
|
||||
{
|
||||
if (isset($values['time_estimated']) && empty($values['time_estimated'])) {
|
||||
$values['time_estimated'] = 0;
|
||||
$this->prepare($values);
|
||||
$result = $this->db->table(self::TABLE)->eq('id', $values['id'])->save($values);
|
||||
|
||||
if ($result) {
|
||||
$this->event->trigger(self::EVENT_UPDATE, $values);
|
||||
}
|
||||
|
||||
if (isset($values['time_spent']) && empty($values['time_spent'])) {
|
||||
$values['time_spent'] = 0;
|
||||
}
|
||||
|
||||
return $this->db->table(self::TABLE)->eq('id', $values['id'])->save($values);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,28 +193,93 @@ class SubTask extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Validate creation/modification
|
||||
* Duplicate all subtasks to another task
|
||||
*
|
||||
* @access public
|
||||
* @param integer $src_task_id Source task id
|
||||
* @param integer $dst_task_id Destination task id
|
||||
* @return bool
|
||||
*/
|
||||
public function duplicate($src_task_id, $dst_task_id)
|
||||
{
|
||||
$subtasks = $this->db->table(self::TABLE)
|
||||
->columns('title', 'time_estimated')
|
||||
->eq('task_id', $src_task_id)
|
||||
->findAll();
|
||||
|
||||
foreach ($subtasks as &$subtask) {
|
||||
|
||||
$subtask['task_id'] = $dst_task_id;
|
||||
$subtask['time_spent'] = 0;
|
||||
|
||||
if (! $this->db->table(self::TABLE)->save($subtask)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate creation
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validate(array $values)
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
$rules = array(
|
||||
new Validators\Required('task_id', t('The task id is required')),
|
||||
new Validators\Integer('task_id', t('The task id must be an integer')),
|
||||
new Validators\Required('title', t('The title is required')),
|
||||
new Validators\MaxLength('title', t('The maximum length is %d characters', 100), 100),
|
||||
new Validators\Integer('user_id', t('The user id must be an integer')),
|
||||
new Validators\Integer('status', t('The status must be an integer')),
|
||||
new Validators\Numeric('time_estimated', t('The time must be a numeric value')),
|
||||
new Validators\Numeric('time_spent', t('The time must be a numeric value')),
|
||||
));
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateModification(array $values)
|
||||
{
|
||||
$rules = array(
|
||||
new Validators\Required('id', t('The subtask id is required')),
|
||||
new Validators\Required('task_id', t('The task id is required')),
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common validation rules
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function commonValidationRules()
|
||||
{
|
||||
return array(
|
||||
new Validators\Integer('id', t('The subtask id must be an integer')),
|
||||
new Validators\Integer('task_id', t('The task id must be an integer')),
|
||||
new Validators\MaxLength('title', t('The maximum length is %d characters', 100), 100),
|
||||
new Validators\Integer('user_id', t('The user id must be an integer')),
|
||||
new Validators\Integer('status', t('The status must be an integer')),
|
||||
new Validators\Numeric('time_estimated', t('The time must be a numeric value')),
|
||||
new Validators\Numeric('time_spent', t('The time must be a numeric value')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
161
sources/app/Model/SubtaskHistory.php
Normal file
161
sources/app/Model/SubtaskHistory.php
Normal file
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use PDO;
|
||||
use Core\Registry;
|
||||
use Event\SubtaskHistoryListener;
|
||||
|
||||
/**
|
||||
* Comment history model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class SubtaskHistory extends BaseHistory
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'subtask_has_events';
|
||||
|
||||
/**
|
||||
* Maximum number of events
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const MAX_EVENTS = 5000;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Core\Registry $registry Registry instance
|
||||
*/
|
||||
public function __construct(Registry $registry)
|
||||
{
|
||||
parent::__construct($registry);
|
||||
$this->table = self::TABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new event
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $subtask_id Subtask id
|
||||
* @param integer $creator_id Author of the event (user id)
|
||||
* @param string $event_name Task event name
|
||||
* @param string $data Current comment
|
||||
* @return boolean
|
||||
*/
|
||||
public function create($project_id, $task_id, $subtask_id, $creator_id, $event_name, $data)
|
||||
{
|
||||
$values = array(
|
||||
'project_id' => $project_id,
|
||||
'task_id' => $task_id,
|
||||
'subtask_id' => $subtask_id,
|
||||
'creator_id' => $creator_id,
|
||||
'event_name' => $event_name,
|
||||
'date_creation' => time(),
|
||||
'data' => $data,
|
||||
);
|
||||
|
||||
$this->db->startTransaction();
|
||||
|
||||
$this->cleanup(self::MAX_EVENTS - 1);
|
||||
$result = $this->db->table(self::TABLE)->insert($values);
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all necessary content to display activity feed
|
||||
*
|
||||
* $author_name
|
||||
* $author_username
|
||||
* $task['id', 'title', 'position', 'column_name']
|
||||
*/
|
||||
public function getAllContentByProjectId($project_id, $limit = 50)
|
||||
{
|
||||
$sql = '
|
||||
SELECT
|
||||
subtask_has_events.id,
|
||||
subtask_has_events.date_creation,
|
||||
subtask_has_events.event_name,
|
||||
subtask_has_events.task_id,
|
||||
tasks.title as task_title,
|
||||
users.username as author_username,
|
||||
users.name as author_name,
|
||||
assignees.name as subtask_assignee_name,
|
||||
assignees.username as subtask_assignee_username,
|
||||
task_has_subtasks.title as subtask_title,
|
||||
task_has_subtasks.status as subtask_status,
|
||||
task_has_subtasks.time_spent as subtask_time_spent,
|
||||
task_has_subtasks.time_estimated as subtask_time_estimated
|
||||
FROM subtask_has_events
|
||||
LEFT JOIN users ON users.id=subtask_has_events.creator_id
|
||||
LEFT JOIN tasks ON tasks.id=subtask_has_events.task_id
|
||||
LEFT JOIN task_has_subtasks ON task_has_subtasks.id=subtask_has_events.subtask_id
|
||||
LEFT JOIN users AS assignees ON assignees.id=task_has_subtasks.user_id
|
||||
WHERE subtask_has_events.project_id = ?
|
||||
ORDER BY subtask_has_events.id DESC
|
||||
LIMIT '.$limit.' OFFSET 0
|
||||
';
|
||||
|
||||
$rq = $this->db->execute($sql, array($project_id));
|
||||
$events = $rq->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($events as &$event) {
|
||||
$event['author'] = $event['author_name'] ?: $event['author_username'];
|
||||
$event['subtask_assignee'] = $event['subtask_assignee_name'] ?: $event['subtask_assignee_username'];
|
||||
$event['subtask_status_list'] = $this->subTask->getStatusList();
|
||||
$event['event_title'] = $this->getTitle($event);
|
||||
$event['event_content'] = $this->getContent($event);
|
||||
$event['event_type'] = 'subtask';
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event title (translated)
|
||||
*
|
||||
* @access public
|
||||
* @param array $event Event properties
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(array $event)
|
||||
{
|
||||
$titles = array(
|
||||
SubTask::EVENT_UPDATE => t('%s updated a subtask for the task #%d', $event['author'], $event['task_id']),
|
||||
SubTask::EVENT_CREATE => t('%s created a subtask for the task #%d', $event['author'], $event['task_id']),
|
||||
);
|
||||
|
||||
return isset($titles[$event['event_name']]) ? $titles[$event['event_name']] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach events to be able to record the history
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function attachEvents()
|
||||
{
|
||||
$events = array(
|
||||
SubTask::EVENT_UPDATE,
|
||||
SubTask::EVENT_CREATE,
|
||||
);
|
||||
|
||||
$listener = new SubtaskHistoryListener($this);
|
||||
|
||||
foreach ($events as $event_name) {
|
||||
$this->event->attach($event_name, $listener);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ namespace Model;
|
|||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
use DateTime;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Task model
|
||||
|
@ -34,13 +35,14 @@ class Task extends Base
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
const EVENT_MOVE_COLUMN = 'task.move.column';
|
||||
const EVENT_MOVE_POSITION = 'task.move.position';
|
||||
const EVENT_UPDATE = 'task.update';
|
||||
const EVENT_CREATE = 'task.create';
|
||||
const EVENT_CLOSE = 'task.close';
|
||||
const EVENT_OPEN = 'task.open';
|
||||
const EVENT_CREATE_UPDATE = 'task.create_update';
|
||||
const EVENT_MOVE_COLUMN = 'task.move.column';
|
||||
const EVENT_MOVE_POSITION = 'task.move.position';
|
||||
const EVENT_UPDATE = 'task.update';
|
||||
const EVENT_CREATE = 'task.create';
|
||||
const EVENT_CLOSE = 'task.close';
|
||||
const EVENT_OPEN = 'task.open';
|
||||
const EVENT_CREATE_UPDATE = 'task.create_update';
|
||||
const EVENT_ASSIGNEE_CHANGE = 'task.assignee_change';
|
||||
|
||||
/**
|
||||
* Get available colors
|
||||
|
@ -61,6 +63,35 @@ class Task extends Base
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of due tasks for all projects
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getOverdueTasks()
|
||||
{
|
||||
$tasks = $this->db->table(self::TABLE)
|
||||
->columns(
|
||||
self::TABLE.'.id',
|
||||
self::TABLE.'.title',
|
||||
self::TABLE.'.date_due',
|
||||
self::TABLE.'.project_id',
|
||||
Project::TABLE.'.name AS project_name',
|
||||
User::TABLE.'.username AS assignee_username',
|
||||
User::TABLE.'.name AS assignee_name'
|
||||
)
|
||||
->join(Project::TABLE, 'id', 'project_id')
|
||||
->join(User::TABLE, 'id', 'owner_id')
|
||||
->eq(Project::TABLE.'.is_active', 1)
|
||||
->eq(self::TABLE.'.is_active', 1)
|
||||
->neq(self::TABLE.'.date_due', 0)
|
||||
->lte(self::TABLE.'.date_due', mktime(23, 59, 59))
|
||||
->findAll();
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch one task
|
||||
*
|
||||
|
@ -95,7 +126,9 @@ class Task extends Base
|
|||
projects.name AS project_name,
|
||||
columns.title AS column_title,
|
||||
users.username AS assignee_username,
|
||||
creators.username AS creator_username
|
||||
users.name AS assignee_name,
|
||||
creators.username AS creator_username,
|
||||
creators.name AS creator_name
|
||||
FROM tasks
|
||||
LEFT JOIN users ON users.id = tasks.owner_id
|
||||
LEFT JOIN users AS creators ON creators.id = tasks.creator_id
|
||||
|
@ -106,7 +139,7 @@ class Task extends Base
|
|||
';
|
||||
|
||||
$rq = $this->db->execute($sql, array($task_id));
|
||||
return $rq->fetch(\PDO::FETCH_ASSOC);
|
||||
return $rq->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
else {
|
||||
|
||||
|
@ -118,16 +151,16 @@ class Task extends Base
|
|||
* Count all tasks for a given project and status
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param array $status List of status id
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $status_id Status id
|
||||
* @return array
|
||||
*/
|
||||
public function getAll($project_id, array $status = array(self::STATUS_OPEN, self::STATUS_CLOSED))
|
||||
public function getAll($project_id, $status_id = self::STATUS_OPEN)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->in('is_active', $status)
|
||||
->eq('is_active', $status_id)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
|
@ -163,23 +196,28 @@ class Task extends Base
|
|||
->columns(
|
||||
'(SELECT count(*) FROM comments WHERE task_id=tasks.id) AS nb_comments',
|
||||
'(SELECT count(*) FROM task_has_files WHERE task_id=tasks.id) AS nb_files',
|
||||
'(SELECT count(*) FROM task_has_subtasks WHERE task_id=tasks.id) AS nb_subtasks',
|
||||
'(SELECT count(*) FROM task_has_subtasks WHERE task_id=tasks.id AND status=2) AS nb_completed_subtasks',
|
||||
'tasks.id',
|
||||
'tasks.title',
|
||||
'tasks.description',
|
||||
'tasks.date_creation',
|
||||
'tasks.date_modification',
|
||||
'tasks.date_completed',
|
||||
'tasks.date_due',
|
||||
'tasks.color_id',
|
||||
'tasks.project_id',
|
||||
'tasks.column_id',
|
||||
'tasks.owner_id',
|
||||
'tasks.creator_id',
|
||||
'tasks.position',
|
||||
'tasks.is_active',
|
||||
'tasks.score',
|
||||
'tasks.category_id',
|
||||
'users.username'
|
||||
'users.username AS assignee_username',
|
||||
'users.name AS assignee_name'
|
||||
)
|
||||
->join('users', 'id', 'owner_id');
|
||||
->join(User::TABLE, 'id', 'owner_id');
|
||||
|
||||
foreach ($filters as $key => $filter) {
|
||||
|
||||
|
@ -228,90 +266,127 @@ class Task extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Duplicate a task
|
||||
* Generic method to duplicate a task
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return boolean
|
||||
* @param array $task Task data
|
||||
* @param array $override Task properties to override
|
||||
* @return integer|boolean
|
||||
*/
|
||||
public function duplicate($task_id)
|
||||
public function copy(array $task, array $override = array())
|
||||
{
|
||||
// Values to override
|
||||
if (! empty($override)) {
|
||||
$task = $override + $task;
|
||||
}
|
||||
|
||||
$this->db->startTransaction();
|
||||
|
||||
// Get the original task
|
||||
$task = $this->getById($task_id);
|
||||
|
||||
// Cleanup data
|
||||
unset($task['id']);
|
||||
unset($task['date_completed']);
|
||||
|
||||
// Assign new values
|
||||
$task['date_creation'] = time();
|
||||
$task['is_active'] = 1;
|
||||
$task['position'] = $this->countByColumnId($task['project_id'], $task['column_id']);
|
||||
$values = array();
|
||||
$values['title'] = $task['title'];
|
||||
$values['description'] = $task['description'];
|
||||
$values['date_creation'] = time();
|
||||
$values['date_modification'] = $values['date_creation'];
|
||||
$values['date_due'] = $task['date_due'];
|
||||
$values['color_id'] = $task['color_id'];
|
||||
$values['project_id'] = $task['project_id'];
|
||||
$values['column_id'] = $task['column_id'];
|
||||
$values['owner_id'] = 0;
|
||||
$values['creator_id'] = $task['creator_id'];
|
||||
$values['position'] = $this->countByColumnId($values['project_id'], $values['column_id']) + 1;
|
||||
$values['score'] = $task['score'];
|
||||
$values['category_id'] = 0;
|
||||
|
||||
// Check if the assigned user is allowed for the new project
|
||||
if ($task['owner_id'] && $this->project->isUserAllowed($values['project_id'], $task['owner_id'])) {
|
||||
$values['owner_id'] = $task['owner_id'];
|
||||
}
|
||||
|
||||
// Check if the category exists
|
||||
if ($task['category_id'] && $this->category->exists($task['category_id'], $task['project_id'])) {
|
||||
$values['category_id'] = $task['category_id'];
|
||||
}
|
||||
|
||||
// Save task
|
||||
if (! $this->db->table(self::TABLE)->save($task)) {
|
||||
if (! $this->db->table(self::TABLE)->save($values)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
$task_id = $this->db->getConnection()->getLastId();
|
||||
|
||||
// Duplicate subtasks
|
||||
if (! $this->subTask->duplicate($task['id'], $task_id)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
// Trigger events
|
||||
$this->event->trigger(self::EVENT_CREATE_UPDATE, array('task_id' => $task_id) + $task);
|
||||
$this->event->trigger(self::EVENT_CREATE, array('task_id' => $task_id) + $task);
|
||||
$this->event->trigger(self::EVENT_CREATE_UPDATE, array('task_id' => $task_id) + $values);
|
||||
$this->event->trigger(self::EVENT_CREATE, array('task_id' => $task_id) + $values);
|
||||
|
||||
return $task_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a task to the same project
|
||||
*
|
||||
* @access public
|
||||
* @param array $task Task data
|
||||
* @return integer|boolean
|
||||
*/
|
||||
public function duplicateSameProject($task)
|
||||
{
|
||||
return $this->copy($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a task to another project (always copy to the first column)
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $project_id Destination project id
|
||||
* @return boolean
|
||||
* @param integer $project_id Destination project id
|
||||
* @param array $task Task data
|
||||
* @return integer|boolean
|
||||
*/
|
||||
public function duplicateToAnotherProject($task_id, $project_id)
|
||||
public function duplicateToAnotherProject($project_id, array $task)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
return $this->copy($task, array(
|
||||
'project_id' => $project_id,
|
||||
'column_id' => $this->board->getFirstColumn($project_id),
|
||||
));
|
||||
}
|
||||
|
||||
$boardModel = new Board($this->db, $this->event);
|
||||
|
||||
// Get the original task
|
||||
$task = $this->getById($task_id);
|
||||
|
||||
// Cleanup data
|
||||
unset($task['id']);
|
||||
unset($task['date_completed']);
|
||||
|
||||
// Assign new values
|
||||
$task['date_creation'] = time();
|
||||
$task['owner_id'] = 0;
|
||||
$task['category_id'] = 0;
|
||||
$task['is_active'] = 1;
|
||||
$task['column_id'] = $boardModel->getFirstColumn($project_id);
|
||||
$task['project_id'] = $project_id;
|
||||
$task['position'] = $this->countByColumnId($task['project_id'], $task['column_id']);
|
||||
|
||||
// Save task
|
||||
if (! $this->db->table(self::TABLE)->save($task)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
/**
|
||||
* Prepare data before task creation or modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
*/
|
||||
public function prepare(array &$values)
|
||||
{
|
||||
if (isset($values['another_task'])) {
|
||||
unset($values['another_task']);
|
||||
}
|
||||
|
||||
$task_id = $this->db->getConnection()->getLastId();
|
||||
if (! empty($values['date_due']) && ! is_numeric($values['date_due'])) {
|
||||
$values['date_due'] = $this->parseDate($values['date_due']);
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
// Force integer fields at 0 (for Postgresql)
|
||||
if (isset($values['date_due']) && empty($values['date_due'])) {
|
||||
$values['date_due'] = 0;
|
||||
}
|
||||
|
||||
// Trigger events
|
||||
$this->event->trigger(self::EVENT_CREATE_UPDATE, array('task_id' => $task_id) + $task);
|
||||
$this->event->trigger(self::EVENT_CREATE, array('task_id' => $task_id) + $task);
|
||||
if (isset($values['score']) && empty($values['score'])) {
|
||||
$values['score'] = 0;
|
||||
}
|
||||
|
||||
return $task_id;
|
||||
if (isset($values['is_active'])) {
|
||||
$values['is_active'] = (int) $values['is_active'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -326,23 +401,20 @@ class Task extends Base
|
|||
$this->db->startTransaction();
|
||||
|
||||
// Prepare data
|
||||
if (isset($values['another_task'])) {
|
||||
unset($values['another_task']);
|
||||
$this->prepare($values);
|
||||
|
||||
if (empty($values['column_id'])) {
|
||||
$values['column_id'] = $this->board->getFirstColumn($values['project_id']);
|
||||
}
|
||||
|
||||
if (! empty($values['date_due']) && ! is_numeric($values['date_due'])) {
|
||||
$values['date_due'] = $this->parseDate($values['date_due']);
|
||||
}
|
||||
else {
|
||||
$values['date_due'] = 0;
|
||||
}
|
||||
|
||||
if (empty($values['score'])) {
|
||||
$values['score'] = 0;
|
||||
if (empty($values['color_id'])) {
|
||||
$colors = $this->getColors();
|
||||
$values['color_id'] = key($colors);
|
||||
}
|
||||
|
||||
$values['date_creation'] = time();
|
||||
$values['position'] = $this->countByColumnId($values['project_id'], $values['column_id']);
|
||||
$values['date_modification'] = $values['date_creation'];
|
||||
$values['position'] = $this->countByColumnId($values['project_id'], $values['column_id']) + 1;
|
||||
|
||||
// Save task
|
||||
if (! $this->db->table(self::TABLE)->save($values)) {
|
||||
|
@ -365,61 +437,77 @@ class Task extends Base
|
|||
* Update a task
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @param array $values Form values
|
||||
* @param boolean $trigger_Events Trigger events
|
||||
* @return boolean
|
||||
*/
|
||||
public function update(array $values)
|
||||
public function update(array $values, $trigger_events = true)
|
||||
{
|
||||
// Prepare data
|
||||
if (! empty($values['date_due']) && ! is_numeric($values['date_due'])) {
|
||||
$values['date_due'] = $this->parseDate($values['date_due']);
|
||||
}
|
||||
|
||||
// Force integer fields at 0 (for Postgresql)
|
||||
if (isset($values['date_due']) && empty($values['date_due'])) {
|
||||
$values['date_due'] = 0;
|
||||
}
|
||||
|
||||
if (isset($values['score']) && empty($values['score'])) {
|
||||
$values['score'] = 0;
|
||||
}
|
||||
|
||||
// Fetch original task
|
||||
$original_task = $this->getById($values['id']);
|
||||
|
||||
if ($original_task === false) {
|
||||
if (! $original_task) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare data
|
||||
$this->prepare($values);
|
||||
$updated_task = $values;
|
||||
$updated_task['date_modification'] = time();
|
||||
unset($updated_task['id']);
|
||||
|
||||
$result = $this->db->table(self::TABLE)->eq('id', $values['id'])->update($updated_task);
|
||||
|
||||
// Trigger events
|
||||
if ($result) {
|
||||
|
||||
$events = array(
|
||||
self::EVENT_CREATE_UPDATE,
|
||||
self::EVENT_UPDATE,
|
||||
);
|
||||
|
||||
if (isset($values['column_id']) && $original_task['column_id'] != $values['column_id']) {
|
||||
$events[] = self::EVENT_MOVE_COLUMN;
|
||||
}
|
||||
else if (isset($values['position']) && $original_task['position'] != $values['position']) {
|
||||
$events[] = self::EVENT_MOVE_POSITION;
|
||||
}
|
||||
|
||||
$event_data = array_merge($original_task, $values);
|
||||
$event_data['task_id'] = $original_task['id'];
|
||||
|
||||
foreach ($events as $event) {
|
||||
$this->event->trigger($event, $event_data);
|
||||
}
|
||||
if ($result && $trigger_events) {
|
||||
$this->triggerUpdateEvents($original_task, $updated_task);
|
||||
}
|
||||
|
||||
return $result;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger events for task modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $original_task Original task data
|
||||
* @param array $updated_task Updated task data
|
||||
*/
|
||||
public function triggerUpdateEvents(array $original_task, array $updated_task)
|
||||
{
|
||||
$events = array();
|
||||
|
||||
if (isset($updated_task['owner_id']) && $original_task['owner_id'] != $updated_task['owner_id']) {
|
||||
$events[] = self::EVENT_ASSIGNEE_CHANGE;
|
||||
}
|
||||
else if (isset($updated_task['column_id']) && $original_task['column_id'] != $updated_task['column_id']) {
|
||||
$events[] = self::EVENT_MOVE_COLUMN;
|
||||
}
|
||||
else if (isset($updated_task['position']) && $original_task['position'] != $updated_task['position']) {
|
||||
$events[] = self::EVENT_MOVE_POSITION;
|
||||
}
|
||||
else {
|
||||
$events[] = self::EVENT_CREATE_UPDATE;
|
||||
$events[] = self::EVENT_UPDATE;
|
||||
}
|
||||
|
||||
$event_data = array_merge($original_task, $updated_task);
|
||||
$event_data['task_id'] = $original_task['id'];
|
||||
|
||||
foreach ($events as $event) {
|
||||
$this->event->trigger($event, $event_data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the project exists
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return boolean
|
||||
*/
|
||||
public function exists($task_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $task_id)->count() === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -431,6 +519,10 @@ class Task extends Base
|
|||
*/
|
||||
public function close($task_id)
|
||||
{
|
||||
if (! $this->exists($task_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $task_id)
|
||||
|
@ -455,12 +547,16 @@ class Task extends Base
|
|||
*/
|
||||
public function open($task_id)
|
||||
{
|
||||
if (! $this->exists($task_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $task_id)
|
||||
->update(array(
|
||||
'is_active' => 1,
|
||||
'date_completed' => ''
|
||||
'date_completed' => 0
|
||||
));
|
||||
|
||||
if ($result) {
|
||||
|
@ -479,8 +575,11 @@ class Task extends Base
|
|||
*/
|
||||
public function remove($task_id)
|
||||
{
|
||||
$file = new File($this->db, $this->event);
|
||||
$file->removeAll($task_id);
|
||||
if (! $this->exists($task_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->file->removeAll($task_id);
|
||||
|
||||
return $this->db->table(self::TABLE)->eq('id', $task_id)->remove();
|
||||
}
|
||||
|
@ -489,20 +588,146 @@ class Task extends Base
|
|||
* Move a task to another column or to another position
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $column_id Column id
|
||||
* @param integer $position Position (must be greater than 1)
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $column_id Column id
|
||||
* @param integer $position Position (must be >= 1)
|
||||
* @return boolean
|
||||
*/
|
||||
public function move($task_id, $column_id, $position)
|
||||
public function movePosition($project_id, $task_id, $column_id, $position)
|
||||
{
|
||||
$this->event->clearTriggeredEvents();
|
||||
// The position can't be lower than 1
|
||||
if ($position < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->update(array(
|
||||
'id' => $task_id,
|
||||
'column_id' => $column_id,
|
||||
'position' => $position,
|
||||
));
|
||||
$board = $this->db->table(Board::TABLE)->eq('project_id', $project_id)->asc('position')->findAllByColumn('id');
|
||||
$columns = array();
|
||||
|
||||
// Prepare the columns
|
||||
foreach ($board as $board_column_id) {
|
||||
|
||||
$columns[$board_column_id] = $this->db->table(self::TABLE)
|
||||
->eq('is_active', 1)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('column_id', $board_column_id)
|
||||
->neq('id', $task_id)
|
||||
->asc('position')
|
||||
->findAllByColumn('id');
|
||||
}
|
||||
|
||||
// The column must exists
|
||||
if (! isset($columns[$column_id])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We put our task to the new position
|
||||
array_splice($columns[$column_id], $position - 1, 0, $task_id); // print_r($columns);
|
||||
|
||||
// We save the new positions for all tasks
|
||||
return $this->savePositions($task_id, $columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save task positions
|
||||
*
|
||||
* @access private
|
||||
* @param integer $moved_task_id Id of the moved task
|
||||
* @param array $columns Sorted tasks
|
||||
* @return boolean
|
||||
*/
|
||||
private function savePositions($moved_task_id, array $columns)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
foreach ($columns as $column_id => $column) {
|
||||
|
||||
$position = 1;
|
||||
|
||||
foreach ($column as $task_id) {
|
||||
|
||||
if ($task_id == $moved_task_id) {
|
||||
|
||||
// Events will be triggered only for that task
|
||||
$result = $this->update(array(
|
||||
'id' => $task_id,
|
||||
'position' => $position,
|
||||
'column_id' => $column_id
|
||||
));
|
||||
}
|
||||
else {
|
||||
$result = $this->db->table(self::TABLE)->eq('id', $task_id)->update(array(
|
||||
'position' => $position,
|
||||
'column_id' => $column_id
|
||||
));
|
||||
}
|
||||
|
||||
$position++;
|
||||
|
||||
if (! $result) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a task to another project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param array $task Task data
|
||||
* @return boolean
|
||||
*/
|
||||
public function moveToAnotherProject($project_id, array $task)
|
||||
{
|
||||
$values = array();
|
||||
|
||||
// Clear values (categories are different for each project)
|
||||
$values['category_id'] = 0;
|
||||
$values['owner_id'] = 0;
|
||||
|
||||
// Check if the assigned user is allowed for the new project
|
||||
if ($task['owner_id'] && $this->project->isUserAllowed($project_id, $task['owner_id'])) {
|
||||
$values['owner_id'] = $task['owner_id'];
|
||||
}
|
||||
|
||||
// We use the first column of the new project
|
||||
$values['column_id'] = $this->board->getFirstColumn($project_id);
|
||||
$values['position'] = $this->countByColumnId($project_id, $values['column_id']) + 1;
|
||||
$values['project_id'] = $project_id;
|
||||
|
||||
if ($this->db->table(self::TABLE)->eq('id', $task['id'])->update($values)) {
|
||||
return $task['id'];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common validation rules
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function commonValidationRules()
|
||||
{
|
||||
return array(
|
||||
new Validators\Integer('id', t('This value must be an integer')),
|
||||
new Validators\Integer('project_id', t('This value must be an integer')),
|
||||
new Validators\Integer('column_id', t('This value must be an integer')),
|
||||
new Validators\Integer('owner_id', t('This value must be an integer')),
|
||||
new Validators\Integer('creator_id', t('This value must be an integer')),
|
||||
new Validators\Integer('score', t('This value must be an integer')),
|
||||
new Validators\Integer('category_id', t('This value must be an integer')),
|
||||
new Validators\MaxLength('title', t('The maximum length is %d characters', 200), 200),
|
||||
new Validators\Date('date_due', t('Invalid date'), $this->getDateFormats()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -514,19 +739,12 @@ class Task extends Base
|
|||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('color_id', t('The color is required')),
|
||||
$rules = array(
|
||||
new Validators\Required('project_id', t('The project is required')),
|
||||
new Validators\Integer('project_id', t('This value must be an integer')),
|
||||
new Validators\Required('column_id', t('The column is required')),
|
||||
new Validators\Integer('column_id', t('This value must be an integer')),
|
||||
new Validators\Integer('owner_id', t('This value must be an integer')),
|
||||
new Validators\Integer('creator_id', t('This value must be an integer')),
|
||||
new Validators\Integer('score', t('This value must be an integer')),
|
||||
new Validators\Required('title', t('The title is required')),
|
||||
new Validators\MaxLength('title', t('The maximum length is %d characters', 200), 200),
|
||||
new Validators\Date('date_due', t('Invalid date'), $this->getDateFormats()),
|
||||
));
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
|
@ -543,11 +761,12 @@ class Task extends Base
|
|||
*/
|
||||
public function validateDescriptionCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
$rules = array(
|
||||
new Validators\Required('id', t('The id is required')),
|
||||
new Validators\Integer('id', t('This value must be an integer')),
|
||||
new Validators\Required('description', t('The description is required')),
|
||||
));
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
|
@ -564,20 +783,11 @@ class Task extends Base
|
|||
*/
|
||||
public function validateModification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
$rules = array(
|
||||
new Validators\Required('id', t('The id is required')),
|
||||
new Validators\Integer('id', t('This value must be an integer')),
|
||||
new Validators\Required('color_id', t('The color is required')),
|
||||
new Validators\Required('project_id', t('The project is required')),
|
||||
new Validators\Integer('project_id', t('This value must be an integer')),
|
||||
new Validators\Required('column_id', t('The column is required')),
|
||||
new Validators\Integer('column_id', t('This value must be an integer')),
|
||||
new Validators\Integer('owner_id', t('This value must be an integer')),
|
||||
new Validators\Integer('score', t('This value must be an integer')),
|
||||
new Validators\Required('title', t('The title is required')),
|
||||
new Validators\MaxLength('title', t('The maximum length is %d characters', 200), 200),
|
||||
new Validators\Date('date_due', t('Invalid date'), $this->getDateFormats()),
|
||||
));
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
|
@ -594,14 +804,59 @@ class Task extends Base
|
|||
*/
|
||||
public function validateAssigneeModification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
$rules = array(
|
||||
new Validators\Required('id', t('The id is required')),
|
||||
new Validators\Integer('id', t('This value must be an integer')),
|
||||
new Validators\Required('project_id', t('The project is required')),
|
||||
new Validators\Integer('project_id', t('This value must be an integer')),
|
||||
new Validators\Required('owner_id', t('This value is required')),
|
||||
new Validators\Integer('owner_id', t('This value must be an integer')),
|
||||
));
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate category change
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateCategoryModification(array $values)
|
||||
{
|
||||
$rules = array(
|
||||
new Validators\Required('id', t('The id is required')),
|
||||
new Validators\Required('project_id', t('The project is required')),
|
||||
new Validators\Required('category_id', t('This value is required')),
|
||||
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate project modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateProjectModification(array $values)
|
||||
{
|
||||
$rules = array(
|
||||
new Validators\Required('id', t('The id is required')),
|
||||
new Validators\Required('project_id', t('The project is required')),
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
|
@ -667,4 +922,112 @@ class Task extends Base
|
|||
'Y_m_d',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given timestamp, reset the date to midnight
|
||||
*
|
||||
* @access public
|
||||
* @param integer $timestamp Timestamp
|
||||
* @return integer
|
||||
*/
|
||||
public function resetDateToMidnight($timestamp)
|
||||
{
|
||||
return mktime(0, 0, 0, date('m', $timestamp), date('d', $timestamp), date('Y', $timestamp));
|
||||
}
|
||||
|
||||
/**
|
||||
* Export a list of tasks for a given project and date range
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param mixed $from Start date (timestamp or user formatted date)
|
||||
* @param mixed $to End date (timestamp or user formatted date)
|
||||
* @return array
|
||||
*/
|
||||
public function export($project_id, $from, $to)
|
||||
{
|
||||
$sql = '
|
||||
SELECT
|
||||
tasks.id,
|
||||
projects.name AS project_name,
|
||||
tasks.is_active,
|
||||
project_has_categories.name AS category_name,
|
||||
columns.title AS column_title,
|
||||
tasks.position,
|
||||
tasks.color_id,
|
||||
tasks.date_due,
|
||||
creators.username AS creator_username,
|
||||
users.username AS assignee_username,
|
||||
tasks.score,
|
||||
tasks.title,
|
||||
tasks.date_creation,
|
||||
tasks.date_modification,
|
||||
tasks.date_completed
|
||||
FROM tasks
|
||||
LEFT JOIN users ON users.id = tasks.owner_id
|
||||
LEFT JOIN users AS creators ON creators.id = tasks.creator_id
|
||||
LEFT JOIN project_has_categories ON project_has_categories.id = tasks.category_id
|
||||
LEFT JOIN columns ON columns.id = tasks.column_id
|
||||
LEFT JOIN projects ON projects.id = tasks.project_id
|
||||
WHERE tasks.date_creation >= ? AND tasks.date_creation <= ? AND tasks.project_id = ?
|
||||
';
|
||||
|
||||
if (! is_numeric($from)) {
|
||||
$from = $this->resetDateToMidnight($this->parseDate($from));
|
||||
}
|
||||
|
||||
if (! is_numeric($to)) {
|
||||
$to = $this->resetDateToMidnight(strtotime('+1 day', $this->parseDate($to)));
|
||||
}
|
||||
|
||||
$rq = $this->db->execute($sql, array($from, $to, $project_id));
|
||||
$tasks = $rq->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$columns = array(
|
||||
e('Task Id'),
|
||||
e('Project'),
|
||||
e('Status'),
|
||||
e('Category'),
|
||||
e('Column'),
|
||||
e('Position'),
|
||||
e('Color'),
|
||||
e('Due date'),
|
||||
e('Creator'),
|
||||
e('Assignee'),
|
||||
e('Complexity'),
|
||||
e('Title'),
|
||||
e('Creation date'),
|
||||
e('Modification date'),
|
||||
e('Completion date'),
|
||||
);
|
||||
|
||||
$results = array($columns);
|
||||
|
||||
foreach ($tasks as &$task) {
|
||||
$results[] = array_values($this->formatOutput($task));
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the output of a task array
|
||||
*
|
||||
* @access public
|
||||
* @param array $task Task properties
|
||||
* @return array
|
||||
*/
|
||||
public function formatOutput(array &$task)
|
||||
{
|
||||
$colors = $this->getColors();
|
||||
$task['score'] = $task['score'] ?: '';
|
||||
$task['is_active'] = $task['is_active'] == self::STATUS_OPEN ? e('Open') : e('Closed');
|
||||
$task['color_id'] = $colors[$task['color_id']];
|
||||
$task['date_creation'] = date('Y-m-d', $task['date_creation']);
|
||||
$task['date_due'] = $task['date_due'] ? date('Y-m-d', $task['date_due']) : '';
|
||||
$task['date_modification'] = $task['date_modification'] ? date('Y-m-d', $task['date_modification']) : '';
|
||||
$task['date_completed'] = $task['date_completed'] ? date('Y-m-d', $task['date_completed']) : '';
|
||||
|
||||
return $task;
|
||||
}
|
||||
}
|
||||
|
|
160
sources/app/Model/TaskHistory.php
Normal file
160
sources/app/Model/TaskHistory.php
Normal file
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use PDO;
|
||||
use Core\Registry;
|
||||
use Event\TaskHistoryListener;
|
||||
|
||||
/**
|
||||
* Task history model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskHistory extends BaseHistory
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'task_has_events';
|
||||
|
||||
/**
|
||||
* Maximum number of events
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const MAX_EVENTS = 5000;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Core\Registry $registry Registry instance
|
||||
*/
|
||||
public function __construct(Registry $registry)
|
||||
{
|
||||
parent::__construct($registry);
|
||||
$this->table = self::TABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new event
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $creator_id Author of the event (user id)
|
||||
* @param string $event_name Task event name
|
||||
* @return boolean
|
||||
*/
|
||||
public function create($project_id, $task_id, $creator_id, $event_name)
|
||||
{
|
||||
$values = array(
|
||||
'project_id' => $project_id,
|
||||
'task_id' => $task_id,
|
||||
'creator_id' => $creator_id,
|
||||
'event_name' => $event_name,
|
||||
'date_creation' => time(),
|
||||
);
|
||||
|
||||
$this->db->startTransaction();
|
||||
|
||||
$this->cleanup(self::MAX_EVENTS - 1);
|
||||
$result = $this->db->table(self::TABLE)->insert($values);
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all necessary content to display activity feed
|
||||
*
|
||||
* $author_name
|
||||
* $author_username
|
||||
* $task['id', 'title', 'position', 'column_name']
|
||||
*/
|
||||
public function getAllContentByProjectId($project_id, $limit = 50)
|
||||
{
|
||||
$sql = '
|
||||
SELECT
|
||||
task_has_events.id,
|
||||
task_has_events.date_creation,
|
||||
task_has_events.event_name,
|
||||
task_has_events.task_id,
|
||||
tasks.title as task_title,
|
||||
tasks.position as task_position,
|
||||
columns.title as task_column_name,
|
||||
users.username as author_username,
|
||||
users.name as author_name
|
||||
FROM task_has_events
|
||||
LEFT JOIN users ON users.id=task_has_events.creator_id
|
||||
LEFT JOIN tasks ON tasks.id=task_has_events.task_id
|
||||
LEFT JOIN columns ON columns.id=tasks.column_id
|
||||
WHERE task_has_events.project_id = ?
|
||||
ORDER BY task_has_events.id DESC
|
||||
LIMIT '.$limit.' OFFSET 0
|
||||
';
|
||||
|
||||
$rq = $this->db->execute($sql, array($project_id));
|
||||
$events = $rq->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($events as &$event) {
|
||||
$event['author'] = $event['author_name'] ?: $event['author_username'];
|
||||
$event['event_title'] = $this->getTitle($event);
|
||||
$event['event_content'] = $this->getContent($event);
|
||||
$event['event_type'] = 'task';
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event title (translated)
|
||||
*
|
||||
* @access public
|
||||
* @param array $event Event properties
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(array $event)
|
||||
{
|
||||
$titles = array(
|
||||
Task::EVENT_ASSIGNEE_CHANGE => t('%s change the assignee of the task #%d', $event['author'], $event['task_id']),
|
||||
Task::EVENT_UPDATE => t('%s updated the task #%d', $event['author'], $event['task_id']),
|
||||
Task::EVENT_CREATE => t('%s created the task #%d', $event['author'], $event['task_id']),
|
||||
Task::EVENT_CLOSE => t('%s closed the task #%d', $event['author'], $event['task_id']),
|
||||
Task::EVENT_OPEN => t('%s open the task #%d', $event['author'], $event['task_id']),
|
||||
Task::EVENT_MOVE_COLUMN => t('%s moved the task #%d to the column "%s"', $event['author'], $event['task_id'], $event['task_column_name']),
|
||||
Task::EVENT_MOVE_POSITION => t('%s moved the task #%d to the position %d in the column "%s"', $event['author'], $event['task_id'], $event['task_position'], $event['task_column_name']),
|
||||
);
|
||||
|
||||
return isset($titles[$event['event_name']]) ? $titles[$event['event_name']] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach events to be able to record the history
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function attachEvents()
|
||||
{
|
||||
$events = array(
|
||||
Task::EVENT_ASSIGNEE_CHANGE,
|
||||
Task::EVENT_UPDATE,
|
||||
Task::EVENT_CREATE,
|
||||
Task::EVENT_CLOSE,
|
||||
Task::EVENT_OPEN,
|
||||
Task::EVENT_MOVE_COLUMN,
|
||||
Task::EVENT_MOVE_POSITION,
|
||||
);
|
||||
|
||||
$listener = new TaskHistoryListener($this);
|
||||
|
||||
foreach ($events as $event_name) {
|
||||
$this->event->attach($event_name, $listener);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,6 +27,39 @@ class User extends Base
|
|||
*/
|
||||
const EVERYBODY_ID = -1;
|
||||
|
||||
/**
|
||||
* Get the default project from the session
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getFavoriteProjectId()
|
||||
{
|
||||
return isset($_SESSION['user']['default_project_id']) ? $_SESSION['user']['default_project_id'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last seen project from the session
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getLastSeenProjectId()
|
||||
{
|
||||
return empty($_SESSION['user']['last_show_project_id']) ? 0 : $_SESSION['user']['last_show_project_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last seen project from the session
|
||||
*
|
||||
* @access public
|
||||
* @@param integer $project_id Project id
|
||||
*/
|
||||
public function storeLastSeenProjectId($project_id)
|
||||
{
|
||||
$_SESSION['user']['last_show_project_id'] = (int) $project_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific user by id
|
||||
*
|
||||
|
@ -86,7 +119,7 @@ class User extends Base
|
|||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->asc('username')
|
||||
->columns('id', 'username', 'name', 'email', 'is_admin', 'default_project_id', 'is_ldap_user')
|
||||
->columns('id', 'username', 'name', 'email', 'is_admin', 'default_project_id', 'is_ldap_user', 'notifications_enabled', 'google_id', 'github_id')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
|
@ -98,7 +131,52 @@ class User extends Base
|
|||
*/
|
||||
public function getList()
|
||||
{
|
||||
return $this->db->table(self::TABLE)->asc('username')->listing('id', 'username');
|
||||
$users = $this->db->table(self::TABLE)->columns('id', 'username', 'name')->findAll();
|
||||
|
||||
$result = array();
|
||||
|
||||
foreach ($users as $user) {
|
||||
$result[$user['id']] = $user['name'] ?: $user['username'];
|
||||
}
|
||||
|
||||
asort($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare values before an update or a create
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
*/
|
||||
public function prepare(array &$values)
|
||||
{
|
||||
if (isset($values['password'])) {
|
||||
|
||||
if (! empty($values['password'])) {
|
||||
$values['password'] = \password_hash($values['password'], PASSWORD_BCRYPT);
|
||||
}
|
||||
else {
|
||||
unset($values['password']);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($values['confirmation'])) {
|
||||
unset($values['confirmation']);
|
||||
}
|
||||
|
||||
if (isset($values['current_password'])) {
|
||||
unset($values['current_password']);
|
||||
}
|
||||
|
||||
if (isset($values['is_admin']) && empty($values['is_admin'])) {
|
||||
$values['is_admin'] = 0;
|
||||
}
|
||||
|
||||
if (isset($values['is_ldap_user']) && empty($values['is_ldap_user'])) {
|
||||
$values['is_ldap_user'] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -110,22 +188,7 @@ class User extends Base
|
|||
*/
|
||||
public function create(array $values)
|
||||
{
|
||||
if (isset($values['confirmation'])) {
|
||||
unset($values['confirmation']);
|
||||
}
|
||||
|
||||
if (isset($values['password'])) {
|
||||
$values['password'] = \password_hash($values['password'], PASSWORD_BCRYPT);
|
||||
}
|
||||
|
||||
if (empty($values['is_admin'])) {
|
||||
$values['is_admin'] = 0;
|
||||
}
|
||||
|
||||
if (empty($values['is_ldap_user'])) {
|
||||
$values['is_ldap_user'] = 0;
|
||||
}
|
||||
|
||||
$this->prepare($values);
|
||||
return $this->db->table(self::TABLE)->save($values);
|
||||
}
|
||||
|
||||
|
@ -138,31 +201,10 @@ class User extends Base
|
|||
*/
|
||||
public function update(array $values)
|
||||
{
|
||||
if (! empty($values['password'])) {
|
||||
$values['password'] = \password_hash($values['password'], PASSWORD_BCRYPT);
|
||||
}
|
||||
else {
|
||||
unset($values['password']);
|
||||
}
|
||||
|
||||
if (isset($values['confirmation'])) {
|
||||
unset($values['confirmation']);
|
||||
}
|
||||
|
||||
if (isset($values['current_password'])) {
|
||||
unset($values['current_password']);
|
||||
}
|
||||
|
||||
if (empty($values['is_admin'])) {
|
||||
$values['is_admin'] = 0;
|
||||
}
|
||||
|
||||
if (empty($values['is_ldap_user'])) {
|
||||
$values['is_ldap_user'] = 0;
|
||||
}
|
||||
|
||||
$this->prepare($values);
|
||||
$result = $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values);
|
||||
|
||||
// If the user is connected refresh his session
|
||||
if (session_id() !== '' && $_SESSION['user']['id'] == $values['id']) {
|
||||
$this->updateSession();
|
||||
}
|
||||
|
@ -182,12 +224,12 @@ class User extends Base
|
|||
$this->db->startTransaction();
|
||||
|
||||
// All tasks assigned to this user will be unassigned
|
||||
$this->db->table(Task::TABLE)->eq('owner_id', $user_id)->update(array('owner_id' => ''));
|
||||
$this->db->table(self::TABLE)->eq('id', $user_id)->remove();
|
||||
$this->db->table(Task::TABLE)->eq('owner_id', $user_id)->update(array('owner_id' => 0));
|
||||
$result = $this->db->table(self::TABLE)->eq('id', $user_id)->remove();
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -214,6 +256,39 @@ class User extends Base
|
|||
$_SESSION['user'] = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common validation rules
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function commonValidationRules()
|
||||
{
|
||||
return array(
|
||||
new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), self::TABLE, 'id'),
|
||||
new Validators\Email('email', t('Email address invalid')),
|
||||
new Validators\Integer('default_project_id', t('This value must be an integer')),
|
||||
new Validators\Integer('is_admin', t('This value must be an integer')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common password validation rules
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function commonPasswordValidationRules()
|
||||
{
|
||||
return array(
|
||||
new Validators\Required('password', t('The password is required')),
|
||||
new Validators\MinLength('password', t('The minimum length is %d characters', 6), 6),
|
||||
new Validators\Required('confirmation', t('The confirmation is required')),
|
||||
new Validators\Equals('password', 'confirmation', t('Passwords don\'t match')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate user creation
|
||||
*
|
||||
|
@ -223,18 +298,11 @@ class User extends Base
|
|||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
$rules = array(
|
||||
new Validators\Required('username', t('The username is required')),
|
||||
new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), self::TABLE, 'id'),
|
||||
new Validators\Required('password', t('The password is required')),
|
||||
new Validators\MinLength('password', t('The minimum length is %d characters', 6), 6),
|
||||
new Validators\Required('confirmation', t('The confirmation is required')),
|
||||
new Validators\Equals('password', 'confirmation', t('Passwords don\'t match')),
|
||||
new Validators\Integer('default_project_id', t('This value must be an integer')),
|
||||
new Validators\Integer('is_admin', t('This value must be an integer')),
|
||||
new Validators\Email('email', t('Email address invalid')),
|
||||
));
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules(), $this->commonPasswordValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
|
@ -251,19 +319,33 @@ class User extends Base
|
|||
*/
|
||||
public function validateModification(array $values)
|
||||
{
|
||||
if (! empty($values['password'])) {
|
||||
return $this->validatePasswordModification($values);
|
||||
}
|
||||
|
||||
$v = new Validator($values, array(
|
||||
$rules = array(
|
||||
new Validators\Required('id', t('The user id is required')),
|
||||
new Validators\Required('username', t('The username is required')),
|
||||
new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), self::TABLE, 'id'),
|
||||
new Validators\Integer('default_project_id', t('This value must be an integer')),
|
||||
new Validators\Integer('is_admin', t('This value must be an integer')),
|
||||
new Validators\Email('email', t('Email address invalid')),
|
||||
));
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate user API modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateApiModification(array $values)
|
||||
{
|
||||
$rules = array(
|
||||
new Validators\Required('id', t('The user id is required')),
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
|
@ -280,27 +362,17 @@ class User extends Base
|
|||
*/
|
||||
public function validatePasswordModification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
$rules = array(
|
||||
new Validators\Required('id', t('The user id is required')),
|
||||
new Validators\Required('username', t('The username is required')),
|
||||
new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), self::TABLE, 'id'),
|
||||
new Validators\Required('current_password', t('The current password is required')),
|
||||
new Validators\Required('password', t('The password is required')),
|
||||
new Validators\MinLength('password', t('The minimum length is %d characters', 6), 6),
|
||||
new Validators\Required('confirmation', t('The confirmation is required')),
|
||||
new Validators\Equals('password', 'confirmation', t('Passwords don\'t match')),
|
||||
new Validators\Integer('default_project_id', t('This value must be an integer')),
|
||||
new Validators\Integer('is_admin', t('This value must be an integer')),
|
||||
new Validators\Email('email', t('Email address invalid')),
|
||||
));
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonPasswordValidationRules()));
|
||||
|
||||
if ($v->execute()) {
|
||||
|
||||
// Check password
|
||||
list($authenticated,) = $this->authenticate($_SESSION['user']['username'], $values['current_password']);
|
||||
|
||||
if ($authenticated) {
|
||||
if ($this->authentication->authenticate($_SESSION['user']['username'], $values['current_password'])) {
|
||||
return array(true, array());
|
||||
}
|
||||
else {
|
||||
|
@ -311,87 +383,6 @@ class User extends Base
|
|||
return array(false, $v->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate user login
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateLogin(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('username', t('The username is required')),
|
||||
new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\Required('password', t('The password is required')),
|
||||
));
|
||||
|
||||
$result = $v->execute();
|
||||
$errors = $v->getErrors();
|
||||
|
||||
if ($result) {
|
||||
|
||||
list($authenticated, $method) = $this->authenticate($values['username'], $values['password']);
|
||||
|
||||
if ($authenticated === true) {
|
||||
|
||||
// Create the user session
|
||||
$user = $this->getByUsername($values['username']);
|
||||
$this->updateSession($user);
|
||||
|
||||
// Update login history
|
||||
$lastLogin = new LastLogin($this->db, $this->event);
|
||||
$lastLogin->create(
|
||||
$method,
|
||||
$user['id'],
|
||||
$this->getIpAddress(),
|
||||
$this->getUserAgent()
|
||||
);
|
||||
|
||||
// Setup the remember me feature
|
||||
if (! empty($values['remember_me'])) {
|
||||
$rememberMe = new RememberMe($this->db, $this->event);
|
||||
$credentials = $rememberMe->create($user['id'], $this->getIpAddress(), $this->getUserAgent());
|
||||
$rememberMe->writeCookie($credentials['token'], $credentials['sequence'], $credentials['expiration']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$result = false;
|
||||
$errors['login'] = t('Bad username or password');
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
$result,
|
||||
$errors
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate a user
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Username
|
||||
* @param string $password Password
|
||||
* @return array
|
||||
*/
|
||||
public function authenticate($username, $password)
|
||||
{
|
||||
// Database authentication
|
||||
$user = $this->db->table(self::TABLE)->eq('username', $username)->eq('is_ldap_user', 0)->findOne();
|
||||
$authenticated = $user && \password_verify($password, $user['password']);
|
||||
$method = LastLogin::AUTH_DATABASE;
|
||||
|
||||
// LDAP authentication
|
||||
if (! $authenticated && LDAP_AUTH) {
|
||||
$ldap = new Ldap($this->db, $this->event);
|
||||
$authenticated = $ldap->authenticate($username, $password);
|
||||
$method = LastLogin::AUTH_LDAP;
|
||||
}
|
||||
|
||||
return array($authenticated, $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user agent of the connected user
|
||||
*
|
||||
|
|
144
sources/app/Model/Webhook.php
Normal file
144
sources/app/Model/Webhook.php
Normal file
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use Event\WebhookListener;
|
||||
|
||||
/**
|
||||
* Webhook model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Webhook extends Base
|
||||
{
|
||||
/**
|
||||
* HTTP connection timeout in seconds
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const HTTP_TIMEOUT = 1;
|
||||
|
||||
/**
|
||||
* Number of maximum redirections for the HTTP client
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const HTTP_MAX_REDIRECTS = 3;
|
||||
|
||||
/**
|
||||
* HTTP client user agent
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const HTTP_USER_AGENT = 'Kanboard Webhook';
|
||||
|
||||
/**
|
||||
* URL to call for task creation
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $url_task_creation = '';
|
||||
|
||||
/**
|
||||
* URL to call for task modification
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $url_task_modification = '';
|
||||
|
||||
/**
|
||||
* Webook token
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $token = '';
|
||||
|
||||
/**
|
||||
* Attach events
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function attachEvents()
|
||||
{
|
||||
$this->url_task_creation = $this->config->get('webhooks_url_task_creation');
|
||||
$this->url_task_modification = $this->config->get('webhooks_url_task_modification');
|
||||
$this->token = $this->config->get('webhooks_token');
|
||||
|
||||
if ($this->url_task_creation) {
|
||||
$this->attachCreateEvents();
|
||||
}
|
||||
|
||||
if ($this->url_task_modification) {
|
||||
$this->attachUpdateEvents();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach events for task modification
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function attachUpdateEvents()
|
||||
{
|
||||
$events = array(
|
||||
Task::EVENT_UPDATE,
|
||||
Task::EVENT_CLOSE,
|
||||
Task::EVENT_OPEN,
|
||||
);
|
||||
|
||||
$listener = new WebhookListener($this->url_task_modification, $this);
|
||||
|
||||
foreach ($events as $event_name) {
|
||||
$this->event->attach($event_name, $listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach events for task creation
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function attachCreateEvents()
|
||||
{
|
||||
$this->event->attach(Task::EVENT_CREATE, new WebhookListener($this->url_task_creation, $this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the external URL
|
||||
*
|
||||
* @access public
|
||||
* @param string $url URL to call
|
||||
* @param array $task Task data
|
||||
*/
|
||||
public function notify($url, array $task)
|
||||
{
|
||||
$headers = array(
|
||||
'Connection: close',
|
||||
'User-Agent: '.self::HTTP_USER_AGENT,
|
||||
);
|
||||
|
||||
$context = stream_context_create(array(
|
||||
'http' => array(
|
||||
'method' => 'POST',
|
||||
'protocol_version' => 1.1,
|
||||
'timeout' => self::HTTP_TIMEOUT,
|
||||
'max_redirects' => self::HTTP_MAX_REDIRECTS,
|
||||
'header' => implode("\r\n", $headers),
|
||||
'content' => json_encode($task)
|
||||
)
|
||||
));
|
||||
|
||||
if (strpos($url, '?') !== false) {
|
||||
$url .= '&token='.$this->token;
|
||||
}
|
||||
else {
|
||||
$url .= '?token='.$this->token;
|
||||
}
|
||||
|
||||
@file_get_contents($url, false, $context);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,98 @@ namespace Schema;
|
|||
|
||||
use Core\Security;
|
||||
|
||||
const VERSION = 21;
|
||||
const VERSION = 27;
|
||||
|
||||
function version_27($pdo)
|
||||
{
|
||||
$pdo->exec('CREATE UNIQUE INDEX users_username_idx ON users(username)');
|
||||
}
|
||||
|
||||
function version_26($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN default_columns VARCHAR(255) DEFAULT ''");
|
||||
}
|
||||
|
||||
function version_25($pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE task_has_events (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
date_creation INT NOT NULL,
|
||||
event_name TEXT NOT NULL,
|
||||
creator_id INT,
|
||||
project_id INT,
|
||||
task_id INT,
|
||||
data TEXT,
|
||||
FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB CHARSET=utf8
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE subtask_has_events (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
date_creation INT NOT NULL,
|
||||
event_name TEXT NOT NULL,
|
||||
creator_id INT,
|
||||
project_id INT,
|
||||
subtask_id INT,
|
||||
task_id INT,
|
||||
data TEXT,
|
||||
FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(subtask_id) REFERENCES task_has_subtasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB CHARSET=utf8
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE comment_has_events (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
date_creation INT NOT NULL,
|
||||
event_name TEXT NOT NULL,
|
||||
creator_id INT,
|
||||
project_id INT,
|
||||
comment_id INT,
|
||||
task_id INT,
|
||||
data TEXT,
|
||||
FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(comment_id) REFERENCES comments(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB CHARSET=utf8
|
||||
");
|
||||
}
|
||||
|
||||
function version_24($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE projects ADD COLUMN is_public TINYINT(1) DEFAULT '0'");
|
||||
}
|
||||
|
||||
function version_23($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN notifications_enabled TINYINT(1) DEFAULT '0'");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE user_has_notifications (
|
||||
user_id INT,
|
||||
project_id INT,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
UNIQUE(project_id, user_id)
|
||||
);
|
||||
");
|
||||
}
|
||||
|
||||
function version_22($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN webhooks_url_task_modification VARCHAR(255)");
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN webhooks_url_task_creation VARCHAR(255)");
|
||||
}
|
||||
|
||||
function version_21($pdo)
|
||||
{
|
||||
|
@ -19,7 +110,8 @@ function version_20($pdo)
|
|||
|
||||
function version_19($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN api_token VARCHAR(255) DEFAULT '".Security::generateToken()."'");
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN api_token VARCHAR(255) DEFAULT ''");
|
||||
$pdo->exec("UPDATE config SET api_token='".Security::generateToken()."'");
|
||||
}
|
||||
|
||||
function version_18($pdo)
|
||||
|
@ -31,7 +123,7 @@ function version_18($pdo)
|
|||
status INT DEFAULT 0,
|
||||
time_estimated INT DEFAULT 0,
|
||||
time_spent INT DEFAULT 0,
|
||||
task_id INT,
|
||||
task_id INT NOT NULL,
|
||||
user_id INT,
|
||||
PRIMARY KEY (id),
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
|
@ -119,52 +211,12 @@ function version_12($pdo)
|
|||
);
|
||||
}
|
||||
|
||||
function version_11($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_10($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_9($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_8($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_7($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_6($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_5($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_4($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_3($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_2($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_1($pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE config (
|
||||
language CHAR(5) DEFAULT 'en_US',
|
||||
webhooks_token VARCHAR(255),
|
||||
webhooks_token VARCHAR(255) DEFAULT '',
|
||||
timezone VARCHAR(50) DEFAULT 'UTC'
|
||||
) ENGINE=InnoDB CHARSET=utf8
|
||||
");
|
||||
|
|
|
@ -4,7 +4,95 @@ namespace Schema;
|
|||
|
||||
use Core\Security;
|
||||
|
||||
const VERSION = 2;
|
||||
const VERSION = 8;
|
||||
|
||||
function version_8($pdo)
|
||||
{
|
||||
$pdo->exec('CREATE UNIQUE INDEX users_username_idx ON users(username)');
|
||||
}
|
||||
|
||||
function version_7($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN default_columns VARCHAR(255) DEFAULT ''");
|
||||
}
|
||||
|
||||
function version_6($pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE task_has_events (
|
||||
id SERIAL PRIMARY KEY,
|
||||
date_creation INTEGER NOT NULL,
|
||||
event_name TEXT NOT NULL,
|
||||
creator_id INTEGER,
|
||||
project_id INTEGER,
|
||||
task_id INTEGER,
|
||||
data TEXT,
|
||||
FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
);
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE subtask_has_events (
|
||||
id SERIAL PRIMARY KEY,
|
||||
date_creation INTEGER NOT NULL,
|
||||
event_name TEXT NOT NULL,
|
||||
creator_id INTEGER,
|
||||
project_id INTEGER,
|
||||
subtask_id INTEGER,
|
||||
task_id INTEGER,
|
||||
data TEXT,
|
||||
FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(subtask_id) REFERENCES task_has_subtasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
);
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE comment_has_events (
|
||||
id SERIAL PRIMARY KEY,
|
||||
date_creation INTEGER NOT NULL,
|
||||
event_name TEXT NOT NULL,
|
||||
creator_id INTEGER,
|
||||
project_id INTEGER,
|
||||
comment_id INTEGER,
|
||||
task_id INTEGER,
|
||||
data TEXT,
|
||||
FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(comment_id) REFERENCES comments(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
);
|
||||
");
|
||||
}
|
||||
|
||||
function version_5($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE projects ADD COLUMN is_public BOOLEAN DEFAULT '0'");
|
||||
}
|
||||
|
||||
function version_4($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN notifications_enabled BOOLEAN DEFAULT '0'");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE user_has_notifications (
|
||||
user_id INTEGER,
|
||||
project_id INTEGER,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
UNIQUE(project_id, user_id)
|
||||
);
|
||||
");
|
||||
}
|
||||
|
||||
function version_3($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN webhooks_url_task_modification VARCHAR(255)");
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN webhooks_url_task_creation VARCHAR(255)");
|
||||
}
|
||||
|
||||
function version_2($pdo)
|
||||
{
|
||||
|
@ -17,9 +105,9 @@ function version_1($pdo)
|
|||
$pdo->exec("
|
||||
CREATE TABLE config (
|
||||
language CHAR(5) DEFAULT 'en_US',
|
||||
webhooks_token VARCHAR(255),
|
||||
webhooks_token VARCHAR(255) DEFAULT '',
|
||||
timezone VARCHAR(50) DEFAULT 'UTC',
|
||||
api_token VARCHAR(255)
|
||||
api_token VARCHAR(255) DEFAULT ''
|
||||
);
|
||||
|
||||
CREATE TABLE users (
|
||||
|
@ -117,7 +205,7 @@ function version_1($pdo)
|
|||
status SMALLINT DEFAULT 0,
|
||||
time_estimated INTEGER DEFAULT 0,
|
||||
time_spent INTEGER DEFAULT 0,
|
||||
task_id INTEGER,
|
||||
task_id INTEGER NOT NULL,
|
||||
user_id INTEGER,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
);
|
||||
|
|
|
@ -4,7 +4,95 @@ namespace Schema;
|
|||
|
||||
use Core\Security;
|
||||
|
||||
const VERSION = 21;
|
||||
const VERSION = 27;
|
||||
|
||||
function version_27($pdo)
|
||||
{
|
||||
$pdo->exec('CREATE UNIQUE INDEX users_username_idx ON users(username)');
|
||||
}
|
||||
|
||||
function version_26($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN default_columns TEXT DEFAULT ''");
|
||||
}
|
||||
|
||||
function version_25($pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE task_has_events (
|
||||
id INTEGER PRIMARY KEY,
|
||||
date_creation INTEGER NOT NULL,
|
||||
event_name TEXT NOT NULL,
|
||||
creator_id INTEGER,
|
||||
project_id INTEGER,
|
||||
task_id INTEGER,
|
||||
data TEXT,
|
||||
FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
);
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE subtask_has_events (
|
||||
id INTEGER PRIMARY KEY,
|
||||
date_creation INTEGER NOT NULL,
|
||||
event_name TEXT NOT NULL,
|
||||
creator_id INTEGER,
|
||||
project_id INTEGER,
|
||||
subtask_id INTEGER,
|
||||
task_id INTEGER,
|
||||
data TEXT,
|
||||
FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(subtask_id) REFERENCES task_has_subtasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
);
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE comment_has_events (
|
||||
id INTEGER PRIMARY KEY,
|
||||
date_creation INTEGER NOT NULL,
|
||||
event_name TEXT NOT NULL,
|
||||
creator_id INTEGER,
|
||||
project_id INTEGER,
|
||||
comment_id INTEGER,
|
||||
task_id INTEGER,
|
||||
data TEXT,
|
||||
FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(comment_id) REFERENCES comments(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
);
|
||||
");
|
||||
}
|
||||
|
||||
function version_24($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE projects ADD COLUMN is_public INTEGER DEFAULT "0"');
|
||||
}
|
||||
|
||||
function version_23($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN notifications_enabled INTEGER DEFAULT '0'");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE user_has_notifications (
|
||||
user_id INTEGER,
|
||||
project_id INTEGER,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
UNIQUE(project_id, user_id)
|
||||
);
|
||||
");
|
||||
}
|
||||
|
||||
function version_22($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN webhooks_url_task_modification TEXT");
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN webhooks_url_task_creation TEXT");
|
||||
}
|
||||
|
||||
function version_21($pdo)
|
||||
{
|
||||
|
@ -19,7 +107,8 @@ function version_20($pdo)
|
|||
|
||||
function version_19($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN api_token TEXT DEFAULT '".Security::generateToken()."'");
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN api_token TEXT DEFAULT ''");
|
||||
$pdo->exec("UPDATE config SET api_token='".Security::generateToken()."'");
|
||||
}
|
||||
|
||||
function version_18($pdo)
|
||||
|
@ -31,7 +120,7 @@ function version_18($pdo)
|
|||
status INTEGER DEFAULT 0,
|
||||
time_estimated INTEGER DEFAULT 0,
|
||||
time_spent INTEGER DEFAULT 0,
|
||||
task_id INTEGER,
|
||||
task_id INTEGER NOT NULL,
|
||||
user_id INTEGER,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
)"
|
||||
|
@ -217,19 +306,6 @@ function version_4($pdo)
|
|||
function version_3($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE projects ADD COLUMN token TEXT');
|
||||
|
||||
// For each existing project, assign a different token
|
||||
$rq = $pdo->prepare("SELECT id FROM projects WHERE token IS NULL");
|
||||
$rq->execute();
|
||||
$results = $rq->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
if ($results !== false) {
|
||||
|
||||
foreach ($results as &$result) {
|
||||
$rq = $pdo->prepare('UPDATE projects SET token=? WHERE id=?');
|
||||
$rq->execute(array(Security::generateToken(), $result['id']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function version_2($pdo)
|
||||
|
@ -242,8 +318,8 @@ function version_1($pdo)
|
|||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE config (
|
||||
language TEXT,
|
||||
webhooks_token TEXT
|
||||
language TEXT DEFAULT 'en_US',
|
||||
webhooks_token TEXT DEFAULT ''
|
||||
)
|
||||
");
|
||||
|
||||
|
@ -301,7 +377,7 @@ function version_1($pdo)
|
|||
|
||||
$pdo->exec("
|
||||
INSERT INTO config
|
||||
(language, webhooks_token)
|
||||
VALUES ('en_US', '".Security::generateToken()."')
|
||||
(webhooks_token)
|
||||
VALUES ('".Security::generateToken()."')
|
||||
");
|
||||
}
|
||||
|
|
|
@ -1,77 +1,70 @@
|
|||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Automatic actions for the project "%s"', $project['name']) ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=project"><?= t('All projects') ?></a></li>
|
||||
</ul>
|
||||
<div class="page-header">
|
||||
<h2><?= t('Automatic actions for the project "%s"', $project['name']) ?></h2>
|
||||
</div>
|
||||
|
||||
<?php if (! empty($actions)): ?>
|
||||
|
||||
<h3><?= t('Defined actions') ?></h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?= t('Event name') ?></th>
|
||||
<th><?= t('Action name') ?></th>
|
||||
<th><?= t('Action parameters') ?></th>
|
||||
<th><?= t('Action') ?></th>
|
||||
</tr>
|
||||
|
||||
<?php foreach ($actions as $action): ?>
|
||||
<tr>
|
||||
<td><?= Helper\in_list($action['event_name'], $available_events) ?></td>
|
||||
<td><?= Helper\in_list($action['action_name'], $available_actions) ?></td>
|
||||
<td>
|
||||
<ul>
|
||||
<?php foreach ($action['params'] as $param): ?>
|
||||
<li>
|
||||
<?= Helper\in_list($param['name'], $available_params) ?> =
|
||||
<strong>
|
||||
<?php if (Helper\contains($param['name'], 'column_id')): ?>
|
||||
<?= Helper\in_list($param['value'], $columns_list) ?>
|
||||
<?php elseif (Helper\contains($param['name'], 'user_id')): ?>
|
||||
<?= Helper\in_list($param['value'], $users_list) ?>
|
||||
<?php elseif (Helper\contains($param['name'], 'project_id')): ?>
|
||||
<?= Helper\in_list($param['value'], $projects_list) ?>
|
||||
<?php elseif (Helper\contains($param['name'], 'color_id')): ?>
|
||||
<?= Helper\in_list($param['value'], $colors_list) ?>
|
||||
<?php elseif (Helper\contains($param['name'], 'category_id')): ?>
|
||||
<?= Helper\in_list($param['value'], $categories_list) ?>
|
||||
<?php endif ?>
|
||||
</strong>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<a href="?controller=action&action=confirm&project_id=<?= $project['id'] ?>&action_id=<?= $action['id'] ?>"><?= t('Remove') ?></a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
|
||||
</table>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<h3><?= t('Add an action') ?></h3>
|
||||
<form method="post" action="?controller=action&action=params&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
|
||||
<?= Helper\form_label(t('Event'), 'event_name') ?>
|
||||
<?= Helper\form_select('event_name', $available_events, $values) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Action'), 'action_name') ?>
|
||||
<?= Helper\form_select('action_name', $available_actions, $values) ?><br/>
|
||||
|
||||
<div class="form-help">
|
||||
<?= t('When the selected event occurs execute the corresponding action.') ?>
|
||||
</div>
|
||||
<section>
|
||||
|
||||
<?php if (! empty($actions)): ?>
|
||||
|
||||
<h3><?= t('Defined actions') ?></h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?= t('Event name') ?></th>
|
||||
<th><?= t('Action name') ?></th>
|
||||
<th><?= t('Action parameters') ?></th>
|
||||
<th><?= t('Action') ?></th>
|
||||
</tr>
|
||||
|
||||
<?php foreach ($actions as $action): ?>
|
||||
<tr>
|
||||
<td><?= Helper\in_list($action['event_name'], $available_events) ?></td>
|
||||
<td><?= Helper\in_list($action['action_name'], $available_actions) ?></td>
|
||||
<td>
|
||||
<ul>
|
||||
<?php foreach ($action['params'] as $param): ?>
|
||||
<li>
|
||||
<?= Helper\in_list($param['name'], $available_params) ?> =
|
||||
<strong>
|
||||
<?php if (Helper\contains($param['name'], 'column_id')): ?>
|
||||
<?= Helper\in_list($param['value'], $columns_list) ?>
|
||||
<?php elseif (Helper\contains($param['name'], 'user_id')): ?>
|
||||
<?= Helper\in_list($param['value'], $users_list) ?>
|
||||
<?php elseif (Helper\contains($param['name'], 'project_id')): ?>
|
||||
<?= Helper\in_list($param['value'], $projects_list) ?>
|
||||
<?php elseif (Helper\contains($param['name'], 'color_id')): ?>
|
||||
<?= Helper\in_list($param['value'], $colors_list) ?>
|
||||
<?php elseif (Helper\contains($param['name'], 'category_id')): ?>
|
||||
<?= Helper\in_list($param['value'], $categories_list) ?>
|
||||
<?php endif ?>
|
||||
</strong>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<a href="?controller=action&action=confirm&action_id=<?= $action['id'] ?>"><?= t('Remove') ?></a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
|
||||
</table>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<h3><?= t('Add an action') ?></h3>
|
||||
<form method="post" action="?controller=action&action=params&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
|
||||
<?= Helper\form_label(t('Event'), 'event_name') ?>
|
||||
<?= Helper\form_select('event_name', $available_events, $values) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Action'), 'action_name') ?>
|
||||
<?= Helper\form_select('action_name', $available_actions, $values) ?><br/>
|
||||
|
||||
<div class="form-help">
|
||||
<?= t('When the selected event occurs execute the corresponding action.') ?>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Next step') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Next step') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
|
@ -1,43 +1,37 @@
|
|||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Automatic actions for the project "%s"', $project['name']) ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=project"><?= t('All projects') ?></a></li>
|
||||
</ul>
|
||||
<div class="page-header">
|
||||
<h2><?= t('Automatic actions for the project "%s"', $project['name']) ?></h2>
|
||||
</div>
|
||||
<section>
|
||||
|
||||
<h3><?= t('Define action parameters') ?></h3>
|
||||
<form method="post" action="?controller=action&action=create&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
<?= Helper\form_hidden('event_name', $values) ?>
|
||||
<?= Helper\form_hidden('action_name', $values) ?>
|
||||
|
||||
<?php foreach ($action_params as $param_name => $param_desc): ?>
|
||||
|
||||
<?php if (Helper\contains($param_name, 'column_id')): ?>
|
||||
<?= Helper\form_label($param_desc, $param_name) ?>
|
||||
<?= Helper\form_select('params['.$param_name.']', $columns_list, $values) ?><br/>
|
||||
<?php elseif (Helper\contains($param_name, 'user_id')): ?>
|
||||
<?= Helper\form_label($param_desc, $param_name) ?>
|
||||
<?= Helper\form_select('params['.$param_name.']', $users_list, $values) ?><br/>
|
||||
<?php elseif (Helper\contains($param_name, 'project_id')): ?>
|
||||
<?= Helper\form_label($param_desc, $param_name) ?>
|
||||
<?= Helper\form_select('params['.$param_name.']', $projects_list, $values) ?><br/>
|
||||
<?php elseif (Helper\contains($param_name, 'color_id')): ?>
|
||||
<?= Helper\form_label($param_desc, $param_name) ?>
|
||||
<?= Helper\form_select('params['.$param_name.']', $colors_list, $values) ?><br/>
|
||||
<?php elseif (Helper\contains($param_name, 'category_id')): ?>
|
||||
<?= Helper\form_label($param_desc, $param_name) ?>
|
||||
<?= Helper\form_select('params['.$param_name.']', $categories_list, $values) ?><br/>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save this action') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=action&action=index&project_id=<?= $project['id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
<section>
|
||||
|
||||
<h3><?= t('Define action parameters') ?></h3>
|
||||
<form method="post" action="?controller=action&action=create&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
<?= Helper\form_hidden('event_name', $values) ?>
|
||||
<?= Helper\form_hidden('action_name', $values) ?>
|
||||
|
||||
<?php foreach ($action_params as $param_name => $param_desc): ?>
|
||||
|
||||
<?php if (Helper\contains($param_name, 'column_id')): ?>
|
||||
<?= Helper\form_label($param_desc, $param_name) ?>
|
||||
<?= Helper\form_select('params['.$param_name.']', $columns_list, $values) ?><br/>
|
||||
<?php elseif (Helper\contains($param_name, 'user_id')): ?>
|
||||
<?= Helper\form_label($param_desc, $param_name) ?>
|
||||
<?= Helper\form_select('params['.$param_name.']', $users_list, $values) ?><br/>
|
||||
<?php elseif (Helper\contains($param_name, 'project_id')): ?>
|
||||
<?= Helper\form_label($param_desc, $param_name) ?>
|
||||
<?= Helper\form_select('params['.$param_name.']', $projects_list, $values) ?><br/>
|
||||
<?php elseif (Helper\contains($param_name, 'color_id')): ?>
|
||||
<?= Helper\form_label($param_desc, $param_name) ?>
|
||||
<?= Helper\form_select('params['.$param_name.']', $colors_list, $values) ?><br/>
|
||||
<?php elseif (Helper\contains($param_name, 'category_id')): ?>
|
||||
<?= Helper\form_label($param_desc, $param_name) ?>
|
||||
<?= Helper\form_select('params['.$param_name.']', $categories_list, $values) ?><br/>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save this action') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=action&action=index&project_id=<?= $project['id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
</form>
|
|
@ -1,16 +1,14 @@
|
|||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Remove an automatic action') ?></h2>
|
||||
</div>
|
||||
<div class="page-header">
|
||||
<h2><?= t('Remove an automatic action') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to remove this action: "%s"?', Helper\in_list($action['event_name'], $available_events).'/'.Helper\in_list($action['action_name'], $available_actions)) ?>
|
||||
</p>
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to remove this action: "%s"?', Helper\in_list($action['event_name'], $available_events).'/'.Helper\in_list($action['action_name'], $available_actions)) ?>
|
||||
</p>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="?controller=action&action=remove&action_id=<?= $action['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=action&action=index&project_id=<?= $action['project_id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<a href="?controller=action&action=remove&action_id=<?= $action['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=action&action=index&project_id=<?= $action['project_id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
|
@ -1,14 +1,12 @@
|
|||
<section id="main">
|
||||
|
||||
<div class="page-header board">
|
||||
<h2>
|
||||
<?= t('Project "%s"', $current_project_name) ?>
|
||||
</h2>
|
||||
<h2><?= t('Project "%s"', $current_project_name) ?></h2>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<h3><?= t('Change assignee for the task "%s"', $values['title']) ?></h3>
|
||||
<form method="post" action="?controller=board&action=assignTask" autocomplete="off">
|
||||
<form method="post" action="?controller=board&action=updateAssignee" autocomplete="off">
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?= Helper\form_hidden('id', $values) ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
24
sources/app/Templates/board_category.php
Normal file
24
sources/app/Templates/board_category.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<section id="main">
|
||||
|
||||
<div class="page-header board">
|
||||
<h2><?= t('Project "%s"', $current_project_name) ?></h2>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<h3><?= t('Change category for the task "%s"', $values['title']) ?></h3>
|
||||
<form method="post" action="?controller=board&action=updateCategory" autocomplete="off">
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?= Helper\form_hidden('id', $values) ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
|
||||
<?= Helper\form_label(t('Category'), 'category_id') ?>
|
||||
<?= Helper\form_select('category_id', $categories_list, $values, $errors) ?><br/>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=board&action=show&project_id=<?= $values['project_id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
</section>
|
|
@ -1,66 +1,58 @@
|
|||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Edit the board for "%s"', $project['name']) ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=project"><?= t('All projects') ?></a></li>
|
||||
</ul>
|
||||
<div class="page-header">
|
||||
<h2><?= t('Edit the board for "%s"', $project['name']) ?></h2>
|
||||
</div>
|
||||
<section>
|
||||
|
||||
<h3><?= t('Change columns') ?></h3>
|
||||
<form method="post" action="?controller=board&action=update&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?php $i = 0; ?>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?= t('Position') ?></th>
|
||||
<th><?= t('Column title') ?></th>
|
||||
<th><?= t('Task limit') ?></th>
|
||||
<th><?= t('Actions') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($columns as $column): ?>
|
||||
<tr>
|
||||
<td><?= Helper\form_label(t('Column %d', ++$i), 'title['.$column['id'].']', array('title="column_id='.$column['id'].'"')) ?></td>
|
||||
<td><?= Helper\form_text('title['.$column['id'].']', $values, $errors, array('required')) ?></td>
|
||||
<td><?= Helper\form_number('task_limit['.$column['id'].']', $values, $errors, array('placeholder="'.t('limit').'"')) ?></td>
|
||||
<td>
|
||||
<ul>
|
||||
<?php if ($column['position'] != 1): ?>
|
||||
<li>
|
||||
<a href="?controller=board&action=moveUp&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'].Helper\param_csrf() ?>"><?= t('Move Up') ?></a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($column['position'] != count($columns)): ?>
|
||||
<li>
|
||||
<a href="?controller=board&action=moveDown&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'].Helper\param_csrf() ?>"><?= t('Move Down') ?></a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<a href="?controller=board&action=confirm&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'] ?>"><?= t('Remove') ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Update') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
<section>
|
||||
</form>
|
||||
|
||||
<h3><?= t('Change columns') ?></h3>
|
||||
<form method="post" action="?controller=board&action=update&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?php $i = 0; ?>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?= t('Position') ?></th>
|
||||
<th><?= t('Column title') ?></th>
|
||||
<th><?= t('Task limit') ?></th>
|
||||
<th><?= t('Actions') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($columns as $column): ?>
|
||||
<tr>
|
||||
<td><?= Helper\form_label(t('Column %d', ++$i), 'title['.$column['id'].']', array('title="column_id='.$column['id'].'"')) ?></td>
|
||||
<td><?= Helper\form_text('title['.$column['id'].']', $values, $errors, array('required')) ?></td>
|
||||
<td><?= Helper\form_number('task_limit['.$column['id'].']', $values, $errors, array('placeholder="'.t('limit').'"')) ?></td>
|
||||
<td>
|
||||
<ul>
|
||||
<?php if ($column['position'] != 1): ?>
|
||||
<li>
|
||||
<a href="?controller=board&action=moveUp&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'].Helper\param_csrf() ?>"><?= t('Move Up') ?></a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($column['position'] != count($columns)): ?>
|
||||
<li>
|
||||
<a href="?controller=board&action=moveDown&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'].Helper\param_csrf() ?>"><?= t('Move Down') ?></a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<a href="?controller=board&action=confirm&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'] ?>"><?= t('Remove') ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
<h3><?= t('Add a new column') ?></h3>
|
||||
<form method="post" action="?controller=board&action=add&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
<?= Helper\form_label(t('Title'), 'title') ?>
|
||||
<?= Helper\form_text('title', $values, $errors, array('required')) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Update') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=project"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<h3><?= t('Add a new column') ?></h3>
|
||||
<form method="post" action="?controller=board&action=add&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
<?= Helper\form_label(t('Title'), 'title') ?>
|
||||
<?= Helper\form_text('title', $values, $errors, array('required')) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Add this column') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=project"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Add this column') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
|
@ -19,6 +19,7 @@
|
|||
<li><a href="#" id="filter-due-date"><?= t('Filter by due date') ?></a></li>
|
||||
<li><a href="?controller=project&action=search&project_id=<?= $current_project_id ?>"><?= t('Search') ?></a></li>
|
||||
<li><a href="?controller=project&action=tasks&project_id=<?= $current_project_id ?>"><?= t('Completed tasks') ?></a></li>
|
||||
<li><a href="?controller=project&action=activity&project_id=<?= $current_project_id ?>"><?= t('Activity') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<?php foreach ($column['tasks'] as $task): ?>
|
||||
<div class="task-board task-<?= $task['color_id'] ?>">
|
||||
|
||||
<?= Helper\template('board_task', array('task' => $task, 'categories' => $categories, 'not_editable' => true)) ?>
|
||||
<?= Helper\template('board_task', array('task' => $task, 'categories' => $categories, 'not_editable' => true, 'project' => $project)) ?>
|
||||
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Remove a column') ?></h2>
|
||||
</div>
|
||||
<div class="page-header">
|
||||
<h2><?= t('Remove a column') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to remove this column: "%s"?', $column['title']) ?>
|
||||
<?= t('This action will REMOVE ALL TASKS associated to this column!') ?>
|
||||
</p>
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to remove this column: "%s"?', $column['title']) ?>
|
||||
<?= t('This action will REMOVE ALL TASKS associated to this column!') ?>
|
||||
</p>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="?controller=board&action=remove&column_id=<?= $column['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=board&action=edit&project_id=<?= $column['project_id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<a href="?controller=board&action=remove&column_id=<?= $column['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=board&action=edit&project_id=<?= $column['project_id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
|
@ -32,7 +32,7 @@
|
|||
data-task-limit="<?= $column['task_limit'] ?>"
|
||||
>
|
||||
<?php foreach ($column['tasks'] as $task): ?>
|
||||
<div class="task-board draggable-item task-<?= $task['color_id'] ?>"
|
||||
<div class="task-board draggable-item task-<?= $task['color_id'] ?> <?= $task['date_modification'] > time() - RECENT_TASK_PERIOD ? 'task-board-recent' : '' ?>"
|
||||
data-task-id="<?= $task['id'] ?>"
|
||||
data-owner-id="<?= $task['owner_id'] ?>"
|
||||
data-category-id="<?= $task['category_id'] ?>"
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<?php if (isset($not_editable)): ?>
|
||||
|
||||
#<?= $task['id'] ?> -
|
||||
<a href="?controller=task&action=readonly&task_id=<?= $task['id'] ?>&token=<?= $project['token'] ?>">#<?= $task['id'] ?></a> -
|
||||
|
||||
<span class="task-board-user">
|
||||
<?php if (! empty($task['owner_id'])): ?>
|
||||
<?= t('Assigned to %s', $task['username']) ?>
|
||||
<?= t('Assigned to %s', $task['assignee_name'] ?: $task['assignee_username']) ?>
|
||||
<?php else: ?>
|
||||
<span class="task-board-nobody"><?= t('Nobody assigned') ?></span>
|
||||
<?php endif ?>
|
||||
|
@ -15,7 +15,9 @@
|
|||
<?php endif ?>
|
||||
|
||||
<div class="task-board-title">
|
||||
<?= Helper\escape($task['title']) ?>
|
||||
<a href="?controller=task&action=readonly&task_id=<?= $task['id'] ?>&token=<?= $project['token'] ?>">
|
||||
<?= Helper\escape($task['title']) ?>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
@ -23,11 +25,13 @@
|
|||
<a class="task-edit-popover" href="?controller=task&action=edit&task_id=<?= $task['id'] ?>" title="<?= t('Edit this task') ?>">#<?= $task['id'] ?></a> -
|
||||
|
||||
<span class="task-board-user">
|
||||
<?php if (! empty($task['owner_id'])): ?>
|
||||
<a class="assignee-popover" href="?controller=board&action=assign&task_id=<?= $task['id'] ?>" title="<?= t('Change assignee') ?>"><?= t('Assigned to %s', $task['username']) ?></a>
|
||||
<?php else: ?>
|
||||
<a class="assignee-popover" href="?controller=board&action=assign&task_id=<?= $task['id'] ?>" title="<?= t('Change assignee') ?>" class="task-board-nobody"><?= t('Nobody assigned') ?></a>
|
||||
<?php endif ?>
|
||||
<a class="assignee-popover" href="?controller=board&action=changeAssignee&task_id=<?= $task['id'] ?>" title="<?= t('Change assignee') ?>">
|
||||
<?php if (! empty($task['owner_id'])): ?>
|
||||
<?= t('Assigned to %s', $task['assignee_name'] ?: $task['assignee_username']) ?></a>
|
||||
<?php else: ?>
|
||||
<?= t('Nobody assigned') ?>
|
||||
<?php endif ?>
|
||||
</a>
|
||||
</span>
|
||||
|
||||
<?php if ($task['score']): ?>
|
||||
|
@ -44,32 +48,45 @@
|
|||
<?php if ($task['category_id']): ?>
|
||||
<div class="task-board-category-container">
|
||||
<span class="task-board-category">
|
||||
<?= Helper\in_list($task['category_id'], $categories) ?>
|
||||
<a class="category-popover" href="?controller=board&action=changeCategory&task_id=<?= $task['id'] ?>" title="<?= t('Change category') ?>">
|
||||
<?= Helper\in_list($task['category_id'], $categories) ?>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
|
||||
<?php if (! empty($task['date_due']) || ! empty($task['nb_files']) || ! empty($task['nb_comments']) || ! empty($task['description'])): ?>
|
||||
<?php if (! empty($task['date_due']) || ! empty($task['nb_files']) || ! empty($task['nb_comments']) || ! empty($task['description']) || ! empty($task['nb_subtasks'])): ?>
|
||||
<div class="task-board-footer">
|
||||
|
||||
<?php if (! empty($task['date_due'])): ?>
|
||||
<div class="task-board-date">
|
||||
<?= dt('%B %e, %G', $task['date_due']) ?>
|
||||
<div class="task-board-date <?= time() > $task['date_due'] ? 'task-board-date-overdue' : '' ?>">
|
||||
<?= dt('%B %e, %Y', $task['date_due']) ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="task-board-icons">
|
||||
|
||||
<?php if (! empty($task['nb_subtasks'])): ?>
|
||||
<span title="<?= t('Sub-Tasks') ?>"><?= $task['nb_completed_subtasks'].'/'.$task['nb_subtasks'] ?> <i class="fa fa-bars"></i></span>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['nb_files'])): ?>
|
||||
<?= $task['nb_files'] ?> <i class="fa fa-paperclip" title="<?= t('Attachments') ?>"></i>
|
||||
<span title="<?= t('Attachments') ?>"><?= $task['nb_files'] ?> <i class="fa fa-paperclip"></i></span>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['nb_comments'])): ?>
|
||||
<?= $task['nb_comments'] ?> <i class="fa fa-comment-o" title="<?= p($task['nb_comments'], t('%d comment', $task['nb_comments']), t('%d comments', $task['nb_comments'])) ?>"></i>
|
||||
<span title="<?= p($task['nb_comments'], t('%d comment', $task['nb_comments']), t('%d comments', $task['nb_comments'])) ?>"><?= $task['nb_comments'] ?> <i class="fa fa-comment-o"></i></span>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['description'])): ?>
|
||||
<a class="task-board-popover" href='?controller=task&action=editDescription&task_id=<?= $task['id'] ?>'><i class="fa fa-file-text-o" title="<?= t('Description') ?>"></i></a>
|
||||
<span title="<?= t('Description') ?>">
|
||||
<?php if (! isset($not_editable)): ?>
|
||||
<a class="task-description-popover" href="?controller=task&action=description&task_id=<?= $task['id'] ?>"><i class="fa fa-file-text-o" data-href="?controller=task&action=description&task_id=<?= $task['id'] ?>"></i></a>
|
||||
<?php else: ?>
|
||||
<i class="fa fa-file-text-o"></i>
|
||||
<?php endif ?>
|
||||
</span>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,24 +1,16 @@
|
|||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Category modification for the project "%s"', $project['name']) ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=project"><?= t('All projects') ?></a></li>
|
||||
</ul>
|
||||
<div class="page-header">
|
||||
<h2><?= t('Category modification for the project "%s"', $project['name']) ?></h2>
|
||||
</div>
|
||||
|
||||
<form method="post" action="?controller=category&action=update&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?= Helper\form_hidden('id', $values) ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
|
||||
<?= Helper\form_label(t('Category Name'), 'name') ?>
|
||||
<?= Helper\form_text('name', $values, $errors, array('autofocus required')) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
<section>
|
||||
|
||||
<form method="post" action="?controller=category&action=update&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?= Helper\form_hidden('id', $values) ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
|
||||
<?= Helper\form_label(t('Category Name'), 'name') ?>
|
||||
<?= Helper\form_text('name', $values, $errors, array('required')) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
</form>
|
|
@ -1,49 +1,41 @@
|
|||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Categories for the project "%s"', $project['name']) ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=project"><?= t('All projects') ?></a></li>
|
||||
</ul>
|
||||
<div class="page-header">
|
||||
<h2><?= t('Categories') ?></h2>
|
||||
</div>
|
||||
|
||||
<?php if (! empty($categories)): ?>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?= t('Category Name') ?></th>
|
||||
<th><?= t('Actions') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($categories as $category_id => $category_name): ?>
|
||||
<tr>
|
||||
<td><?= Helper\escape($category_name) ?></td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="?controller=category&action=edit&project_id=<?= $project['id'] ?>&category_id=<?= $category_id ?>"><?= t('Edit') ?></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=category&action=confirm&project_id=<?= $project['id'] ?>&category_id=<?= $category_id ?>"><?= t('Remove') ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
|
||||
<h3><?= t('Add a new category') ?></h3>
|
||||
<form method="post" action="?controller=category&action=save&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
|
||||
<?= Helper\form_label(t('Category Name'), 'name') ?>
|
||||
<?= Helper\form_text('name', $values, $errors, array('autofocus required')) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
<section>
|
||||
|
||||
<?php if (! empty($categories)): ?>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?= t('Category Name') ?></th>
|
||||
<th><?= t('Actions') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($categories as $category_id => $category_name): ?>
|
||||
<tr>
|
||||
<td><?= Helper\escape($category_name) ?></td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="?controller=category&action=edit&project_id=<?= $project['id'] ?>&category_id=<?= $category_id ?>"><?= t('Edit') ?></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=category&action=confirm&project_id=<?= $project['id'] ?>&category_id=<?= $category_id ?>"><?= t('Remove') ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
|
||||
<h3><?= t('Add a new category') ?></h3>
|
||||
<form method="post" action="?controller=category&action=save&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
|
||||
<?= Helper\form_label(t('Category Name'), 'name') ?>
|
||||
<?= Helper\form_text('name', $values, $errors, array('required')) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
</form>
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?= Helper\form_hidden('id', $values) ?>
|
||||
<?= Helper\form_hidden('task_id', $values) ?>
|
||||
<?= Helper\form_textarea('comment', $values, $errors, array('autofocus', 'required', 'placeholder="'.t('Leave a comment').'"'), 'comment-textarea') ?><br/>
|
||||
|
||||
<div class="form-actions">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div class="comment <?= isset($preview) ? 'comment-preview' : '' ?>" id="comment-<?= $comment['id'] ?>">
|
||||
|
||||
<p class="comment-title">
|
||||
<span class="comment-username"><?= Helper\escape($comment['username']) ?></span> @ <span class="comment-date"><?= dt('%B %e, %G at %k:%M %p', $comment['date']) ?></span>
|
||||
<span class="comment-username"><?= Helper\escape($comment['name'] ?: $comment['username']) ?></span> @ <span class="comment-date"><?= dt('%B %e, %Y at %k:%M %p', $comment['date']) ?></span>
|
||||
</p>
|
||||
|
||||
<div class="comment-inner">
|
||||
|
@ -9,7 +9,7 @@
|
|||
<?php if (! isset($preview)): ?>
|
||||
<ul class="comment-actions">
|
||||
<li><a href="#comment-<?= $comment['id'] ?>"><?= t('link') ?></a></li>
|
||||
<?php if (Helper\is_admin() || Helper\is_current_user($comment['user_id'])): ?>
|
||||
<?php if ((! isset($not_editable) || ! $not_editable) && (Helper\is_admin() || Helper\is_current_user($comment['user_id']))): ?>
|
||||
<li>
|
||||
<a href="?controller=comment&action=confirm&task_id=<?= $task['id'] ?>&comment_id=<?= $comment['id'] ?>"><?= t('remove') ?></a>
|
||||
</li>
|
||||
|
|
|
@ -1,126 +1,71 @@
|
|||
<section id="main">
|
||||
|
||||
<?php if ($user['is_admin']): ?>
|
||||
<div class="page-header">
|
||||
<h2><?= t('Application settings') ?></h2>
|
||||
<div class="page-header">
|
||||
<h2><?= t('Application settings') ?></h2>
|
||||
</div>
|
||||
<section>
|
||||
<form method="post" action="?controller=config&action=save" autocomplete="off">
|
||||
|
||||
<?= Helper\form_csrf() ?>
|
||||
|
||||
<?= Helper\form_label(t('Language'), 'language') ?>
|
||||
<?= Helper\form_select('language', $languages, $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Timezone'), 'timezone') ?>
|
||||
<?= Helper\form_select('timezone', $timezones, $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Webhook URL for task creation'), 'webhooks_url_task_creation') ?>
|
||||
<?= Helper\form_text('webhooks_url_task_creation', $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Webhook URL for task modification'), 'webhooks_url_task_modification') ?>
|
||||
<?= Helper\form_text('webhooks_url_task_modification', $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Default columns for new projects (Comma-separated)'), 'default_columns') ?>
|
||||
<?= Helper\form_text('default_columns', $values, $errors) ?><br/>
|
||||
<p class="form-help"><?= t('Default values are "%s"', $default_columns) ?></p>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
<section>
|
||||
<form method="post" action="?controller=config&action=save" autocomplete="off">
|
||||
|
||||
<?= Helper\form_csrf() ?>
|
||||
|
||||
<?= Helper\form_label(t('Language'), 'language') ?>
|
||||
<?= Helper\form_select('language', $languages, $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Timezone'), 'timezone') ?>
|
||||
<?= Helper\form_select('timezone', $timezones, $values, $errors) ?><br/>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<?php endif ?>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<div class="page-header">
|
||||
<h2><?= t('User settings') ?></h2>
|
||||
<h2><?= t('More information') ?></h2>
|
||||
</div>
|
||||
<section class="settings">
|
||||
<ul>
|
||||
<li><a href="?controller=config&action=tokens<?= Helper\param_csrf() ?>"><?= t('Reset all tokens') ?></a></li>
|
||||
<li>
|
||||
<strong><?= t('My default project:') ?> </strong>
|
||||
<?= (isset($user['default_project_id']) && isset($projects[$user['default_project_id']])) ? Helper\escape($projects[$user['default_project_id']]) : t('None') ?>,
|
||||
<a href="?controller=user&action=edit&user_id=<?= $user['id'] ?>"><?= t('edit') ?></a>
|
||||
<?= t('Webhooks token:') ?>
|
||||
<strong><?= Helper\escape($values['webhooks_token']) ?></strong>
|
||||
</li>
|
||||
<li>
|
||||
<?= t('API token:') ?>
|
||||
<strong><?= Helper\escape($values['api_token']) ?></strong>
|
||||
</li>
|
||||
<?php if (DB_DRIVER === 'sqlite'): ?>
|
||||
<li>
|
||||
<?= t('Database size:') ?>
|
||||
<strong><?= Helper\format_bytes($db_size) ?></strong>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=config&action=downloadDb<?= Helper\param_csrf() ?>"><?= t('Download the database') ?></a>
|
||||
<?= t('(Gzip compressed Sqlite file)') ?>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=config&action=optimizeDb <?= Helper\param_csrf() ?>"><?= t('Optimize the database') ?></a>
|
||||
<?= t('(VACUUM command)') ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<?= t('Official website:') ?>
|
||||
<a href="http://kanboard.net/" target="_blank" rel="noreferer">http://kanboard.net/</a>
|
||||
</li>
|
||||
<li>
|
||||
<?= t('Application version:') ?>
|
||||
<?= APP_VERSION ?>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<?php if ($user['is_admin']): ?>
|
||||
<div class="page-header">
|
||||
<h2><?= t('More information') ?></h2>
|
||||
</div>
|
||||
<section class="settings">
|
||||
<ul>
|
||||
<li><a href="?controller=config&action=tokens<?= Helper\param_csrf() ?>"><?= t('Reset all tokens') ?></a></li>
|
||||
<li>
|
||||
<?= t('Webhooks token:') ?>
|
||||
<strong><?= Helper\escape($values['webhooks_token']) ?></strong>
|
||||
</li>
|
||||
<li>
|
||||
<?= t('API token:') ?>
|
||||
<strong><?= Helper\escape($values['api_token']) ?></strong>
|
||||
</li>
|
||||
<?php if (DB_DRIVER === 'sqlite'): ?>
|
||||
<li>
|
||||
<?= t('Database size:') ?>
|
||||
<strong><?= Helper\format_bytes($db_size) ?></strong>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=config&action=downloadDb<?= Helper\param_csrf() ?>"><?= t('Download the database') ?></a>
|
||||
<?= t('(Gzip compressed Sqlite file)') ?>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=config&action=optimizeDb <?= Helper\param_csrf() ?>"><?= t('Optimize the database') ?></a>
|
||||
<?= t('(VACUUM command)') ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<?= t('Official website:') ?>
|
||||
<a href="http://kanboard.net/" target="_blank" rel="noreferer">http://kanboard.net/</a>
|
||||
</li>
|
||||
<li>
|
||||
<?= t('Application version:') ?>
|
||||
<?= APP_VERSION ?>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="page-header" id="last-logins">
|
||||
<h2><?= t('Last logins') ?></h2>
|
||||
</div>
|
||||
<?php if (! empty($last_logins)): ?>
|
||||
<table class="table-small table-hover">
|
||||
<tr>
|
||||
<th><?= t('Login date') ?></th>
|
||||
<th><?= t('Authentication method') ?></th>
|
||||
<th><?= t('IP address') ?></th>
|
||||
<th><?= t('User agent') ?></th>
|
||||
</tr>
|
||||
<?php foreach($last_logins as $login): ?>
|
||||
<tr>
|
||||
<td><?= dt('%B %e, %G at %k:%M %p', $login['date_creation']) ?></td>
|
||||
<td><?= Helper\escape($login['auth_type']) ?></td>
|
||||
<td><?= Helper\escape($login['ip']) ?></td>
|
||||
<td><?= Helper\escape($login['user_agent']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="page-header" id="remember-me">
|
||||
<h2><?= t('Persistent connections') ?></h2>
|
||||
</div>
|
||||
<?php if (empty($remember_me_sessions)): ?>
|
||||
<p class="alert alert-info"><?= t('No session') ?></p>
|
||||
<?php else: ?>
|
||||
<table class="table-small table-hover">
|
||||
<tr>
|
||||
<th><?= t('Creation date') ?></th>
|
||||
<th><?= t('Expiration date') ?></th>
|
||||
<th><?= t('IP address') ?></th>
|
||||
<th><?= t('User agent') ?></th>
|
||||
<th><?= t('Action') ?></th>
|
||||
</tr>
|
||||
<?php foreach($remember_me_sessions as $session): ?>
|
||||
<tr>
|
||||
<td><?= dt('%B %e, %G at %k:%M %p', $session['date_creation']) ?></td>
|
||||
<td><?= dt('%B %e, %G at %k:%M %p', $session['expiration']) ?></td>
|
||||
<td><?= Helper\escape($session['ip']) ?></td>
|
||||
<td><?= Helper\escape($session['user_agent']) ?></td>
|
||||
<td><a href="?controller=config&action=removeRememberMeToken&id=<?= $session['id'].Helper\param_csrf() ?>"><?= t('Remove') ?></a></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
</section>
|
||||
|
|
7
sources/app/Templates/event_comment_create.php
Normal file
7
sources/app/Templates/event_comment_create.php
Normal file
|
@ -0,0 +1,7 @@
|
|||
<p class="activity-title">
|
||||
<?= e('%s commented the task <a href="?controller=task&action=show&task_id=%d">#%d</a>', Helper\escape($author), $task_id, $task_id) ?>
|
||||
</p>
|
||||
<p class="activity-description">
|
||||
<em><?= Helper\escape($task_title) ?></em><br/>
|
||||
<div class="markdown"><?= Helper\parse($comment) ?></div>
|
||||
</p>
|
7
sources/app/Templates/event_comment_update.php
Normal file
7
sources/app/Templates/event_comment_update.php
Normal file
|
@ -0,0 +1,7 @@
|
|||
<p class="activity-title">
|
||||
<?= e('%s updated a comment on the task <a href="?controller=task&action=show&task_id=%d">#%d</a>', Helper\escape($author), $task_id, $task_id) ?>
|
||||
</p>
|
||||
<p class="activity-description">
|
||||
<em><?= Helper\escape($task_title) ?></em><br/>
|
||||
<div class="markdown"><?= Helper\parse($comment) ?></div>
|
||||
</p>
|
12
sources/app/Templates/event_subtask_create.php
Normal file
12
sources/app/Templates/event_subtask_create.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<p class="activity-title">
|
||||
<?= e('%s created a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>', Helper\escape($author), $task_id, $task_id) ?>
|
||||
</p>
|
||||
<p class="activity-description">
|
||||
<em><?= Helper\escape($task_title) ?></em><br/>
|
||||
<p><?= Helper\escape($subtask_title) ?> <strong>(<?= Helper\in_list($subtask_status, $subtask_status_list) ?>)</strong></p>
|
||||
<?php if ($subtask_assignee): ?>
|
||||
<p><?= t('Assigned to %s with an estimate of %s/%sh', $subtask_assignee, $subtask_time_spent, $subtask_time_estimated) ?></p>
|
||||
<?php else: ?>
|
||||
<p><?= t('Not assigned, estimate of %sh', $subtask_time_estimated) ?></p>
|
||||
<?php endif ?>
|
||||
</p>
|
12
sources/app/Templates/event_subtask_update.php
Normal file
12
sources/app/Templates/event_subtask_update.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<p class="activity-title">
|
||||
<?= e('%s updated a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>', Helper\escape($author), $task_id, $task_id) ?>
|
||||
</p>
|
||||
<p class="activity-description">
|
||||
<em><?= Helper\escape($task_title) ?></em><br/>
|
||||
<p><?= Helper\escape($subtask_title) ?> <strong>(<?= Helper\in_list($subtask_status, $subtask_status_list) ?>)</strong></p>
|
||||
<?php if ($subtask_assignee): ?>
|
||||
<p><?= t('Assigned to %s with an estimate of %s/%sh', $subtask_assignee, $subtask_time_spent, $subtask_time_estimated) ?></p>
|
||||
<?php else: ?>
|
||||
<p><?= t('Not assigned, estimate of %sh', $subtask_time_estimated) ?></p>
|
||||
<?php endif ?>
|
||||
</p>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue