summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrédéric Guillot <fred@kanboard.net>2014-07-05 16:32:24 -0300
committerFrédéric Guillot <fred@kanboard.net>2014-07-05 16:32:24 -0300
commit035294798d891d1d2447a79586401b097d0c2ae4 (patch)
treeff5198d42ca7e66892e0f523b1b5c4977d9bb98b
parent23341b2326b560ddf71c90915bc55f49bed81446 (diff)
Add Postgresql support
-rw-r--r--README.markdown3
-rw-r--r--app/Model/Board.php5
-rw-r--r--app/Model/Config.php5
-rw-r--r--app/Model/Task.php14
-rw-r--r--app/Model/User.php16
-rw-r--r--app/Schema/Mysql.php1
-rw-r--r--app/Schema/Postgres.php166
-rw-r--r--app/Schema/Sqlite.php1
-rw-r--r--app/common.php66
-rw-r--r--docs/postgresql-configuration.markdown38
-rw-r--r--vendor/PicoDb/Database.php5
-rw-r--r--vendor/PicoDb/Drivers/Postgres.php73
12 files changed, 367 insertions, 26 deletions
diff --git a/README.markdown b/README.markdown
index 2f8138b7..f98f909a 100644
--- a/README.markdown
+++ b/README.markdown
@@ -98,7 +98,8 @@ Documentation
#### Database
- [Sqlite database management](docs/sqlite-database.markdown)
-- [How to use Mysql instead of Sqlite](docs/mysql-configuration.markdown)
+- [How to use Mysql](docs/mysql-configuration.markdown)
+- [How to use Postgresql](docs/postgresql-configuration.markdown)
#### Authentication
diff --git a/app/Model/Board.php b/app/Model/Board.php
index 56094964..a4e0a345 100644
--- a/app/Model/Board.php
+++ b/app/Model/Board.php
@@ -99,6 +99,11 @@ class Board extends Base
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));
}
}
diff --git a/app/Model/Config.php b/app/Model/Config.php
index 2f7c660b..178093c4 100644
--- a/app/Model/Config.php
+++ b/app/Model/Config.php
@@ -174,7 +174,10 @@ class Config extends Base
*/
public function regenerateTokens()
{
- $this->db->table(self::TABLE)->update(array('webhooks_token' => Security::generateToken()));
+ $this->db->table(self::TABLE)->update(array(
+ 'webhooks_token' => Security::generateToken(),
+ 'api_token' => Security::generateToken(),
+ ));
$projects = $this->db->table(Project::TABLE)->findAllByColumn('id');
diff --git a/app/Model/Task.php b/app/Model/Task.php
index 09e2f4e4..469927b7 100644
--- a/app/Model/Task.php
+++ b/app/Model/Task.php
@@ -327,6 +327,13 @@ class Task extends Base
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;
+ }
$values['date_creation'] = time();
$values['position'] = $this->countByColumnId($values['project_id'], $values['column_id']);
@@ -361,6 +368,13 @@ class Task extends Base
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;
+ }
$original_task = $this->getById($values['id']);
diff --git a/app/Model/User.php b/app/Model/User.php
index ba1acb90..b5744c44 100644
--- a/app/Model/User.php
+++ b/app/Model/User.php
@@ -118,6 +118,14 @@ class User extends Base
$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;
+ }
+
return $this->db->table(self::TABLE)->save($values);
}
@@ -145,6 +153,14 @@ class User extends Base
unset($values['current_password']);
}
+ if (empty($values['is_admin'])) {
+ $values['is_admin'] = 0;
+ }
+
+ if (empty($values['is_ldap_user'])) {
+ $values['is_ldap_user'] = 0;
+ }
+
$result = $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values);
if (session_id() !== '' && $_SESSION['user']['id'] == $values['id']) {
diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php
index a30b0989..2c78d1e5 100644
--- a/app/Schema/Mysql.php
+++ b/app/Schema/Mysql.php
@@ -3,6 +3,7 @@
namespace Schema;
use Core\Security;
+
const VERSION = 20;
function version_20($pdo)
diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php
new file mode 100644
index 00000000..8ac6631c
--- /dev/null
+++ b/app/Schema/Postgres.php
@@ -0,0 +1,166 @@
+<?php
+
+namespace Schema;
+
+use Core\Security;
+
+const VERSION = 1;
+
+function version_1($pdo)
+{
+ $pdo->exec("
+ CREATE TABLE config (
+ language CHAR(5) DEFAULT 'en_US',
+ webhooks_token VARCHAR(255),
+ timezone VARCHAR(50) DEFAULT 'UTC',
+ api_token VARCHAR(255)
+ );
+
+ CREATE TABLE users (
+ id SERIAL PRIMARY KEY,
+ username VARCHAR(50),
+ password VARCHAR(255),
+ is_admin BOOLEAN DEFAULT '0',
+ default_project_id INTEGER DEFAULT 0,
+ is_ldap_user BOOLEAN DEFAULT '0',
+ name VARCHAR(255),
+ email VARCHAR(255),
+ google_id VARCHAR(255),
+ github_id VARCHAR(30)
+ );
+
+ CREATE TABLE remember_me (
+ id SERIAL PRIMARY KEY,
+ user_id INTEGER,
+ ip VARCHAR(40),
+ user_agent VARCHAR(255),
+ token VARCHAR(255),
+ sequence VARCHAR(255),
+ expiration INTEGER,
+ date_creation INTEGER,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
+ );
+
+ CREATE TABLE last_logins (
+ id SERIAL PRIMARY KEY,
+ auth_type VARCHAR(25),
+ user_id INTEGER,
+ ip VARCHAR(40),
+ user_agent VARCHAR(255),
+ date_creation INTEGER,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
+ );
+
+ CREATE TABLE projects (
+ id SERIAL PRIMARY KEY,
+ name VARCHAR(255) UNIQUE,
+ is_active BOOLEAN DEFAULT '1',
+ token VARCHAR(255),
+ last_modified INTEGER DEFAULT 0
+ );
+
+ CREATE TABLE project_has_users (
+ id SERIAL PRIMARY KEY,
+ project_id INTEGER,
+ user_id INTEGER,
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
+ UNIQUE(project_id, user_id)
+ );
+
+ CREATE TABLE project_has_categories (
+ id SERIAL PRIMARY KEY,
+ name VARCHAR(255),
+ project_id INTEGER,
+ UNIQUE (project_id, name),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
+ );
+
+ CREATE TABLE columns (
+ id SERIAL PRIMARY KEY,
+ title VARCHAR(255),
+ position INTEGER,
+ project_id INTEGER,
+ task_limit INTEGER DEFAULT 0,
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
+ UNIQUE (title, project_id)
+ );
+
+ CREATE TABLE tasks (
+ id SERIAL PRIMARY KEY,
+ title VARCHAR(255),
+ description TEXT,
+ date_creation INTEGER,
+ color_id VARCHAR(255),
+ project_id INTEGER,
+ column_id INTEGER,
+ owner_id INTEGER DEFAULT 0,
+ position INTEGER,
+ is_active BOOLEAN DEFAULT '1',
+ date_completed INTEGER,
+ score INTEGER,
+ date_due INTEGER,
+ category_id INTEGER DEFAULT 0,
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
+ FOREIGN KEY(column_id) REFERENCES columns(id) ON DELETE CASCADE
+ );
+
+ CREATE TABLE task_has_subtasks (
+ id SERIAL PRIMARY KEY,
+ title VARCHAR(255),
+ status SMALLINT DEFAULT 0,
+ time_estimated INTEGER DEFAULT 0,
+ time_spent INTEGER DEFAULT 0,
+ task_id INTEGER,
+ user_id INTEGER,
+ FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
+ );
+
+ CREATE TABLE task_has_files (
+ id SERIAL PRIMARY KEY,
+ name VARCHAR(255),
+ path VARCHAR(255),
+ is_image BOOLEAN DEFAULT '0',
+ task_id INTEGER,
+ FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
+ );
+
+ CREATE TABLE comments (
+ id SERIAL PRIMARY KEY,
+ task_id INTEGER,
+ user_id INTEGER,
+ date INTEGER,
+ comment TEXT,
+ FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
+ FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
+ );
+
+ CREATE TABLE actions (
+ id SERIAL PRIMARY KEY,
+ project_id INTEGER,
+ event_name VARCHAR(50),
+ action_name VARCHAR(50),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
+ );
+
+ CREATE TABLE action_has_params (
+ id SERIAL PRIMARY KEY,
+ action_id INTEGER,
+ name VARCHAR(50),
+ value VARCHAR(50),
+ FOREIGN KEY(action_id) REFERENCES actions(id) ON DELETE CASCADE
+ );
+ ");
+
+ $pdo->exec("
+ INSERT INTO users
+ (username, password, is_admin)
+ VALUES ('admin', '".\password_hash('admin', PASSWORD_BCRYPT)."', '1')
+ ");
+
+ $pdo->exec("
+ INSERT INTO config
+ (webhooks_token, api_token)
+ VALUES ('".Security::generateToken()."', '".Security::generateToken()."')
+ ");
+}
diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php
index 6ea0541c..e4de397d 100644
--- a/app/Schema/Sqlite.php
+++ b/app/Schema/Sqlite.php
@@ -1,6 +1,7 @@
<?php
namespace Schema;
+
use Core\Security;
const VERSION = 20;
diff --git a/app/common.php b/app/common.php
index d607cf92..c33d5592 100644
--- a/app/common.php
+++ b/app/common.php
@@ -66,32 +66,50 @@ $registry = new Registry;
$registry->db = function() use ($registry) {
require __DIR__.'/../vendor/PicoDb/Database.php';
- if (DB_DRIVER === 'sqlite') {
-
- require __DIR__.'/Schema/Sqlite.php';
-
- $db = new \PicoDb\Database(array(
- 'driver' => 'sqlite',
- 'filename' => DB_FILENAME
- ));
- }
- elseif (DB_DRIVER === 'mysql') {
-
- require __DIR__.'/Schema/Mysql.php';
-
- $db = new \PicoDb\Database(array(
- 'driver' => 'mysql',
- 'hostname' => DB_HOSTNAME,
- 'username' => DB_USERNAME,
- 'password' => DB_PASSWORD,
- 'database' => DB_NAME,
- 'charset' => 'utf8',
- ));
- }
- else {
- die('Database driver not supported');
+ switch (DB_DRIVER) {
+ case 'sqlite':
+ require __DIR__.'/Schema/Sqlite.php';
+
+ $params = array(
+ 'driver' => 'sqlite',
+ 'filename' => DB_FILENAME
+ );
+
+ break;
+
+ case 'mysql':
+ require __DIR__.'/Schema/Mysql.php';
+
+ $params = array(
+ 'driver' => 'mysql',
+ 'hostname' => DB_HOSTNAME,
+ 'username' => DB_USERNAME,
+ 'password' => DB_PASSWORD,
+ 'database' => DB_NAME,
+ 'charset' => 'utf8',
+ );
+
+ break;
+
+ case 'postgres':
+ require __DIR__.'/Schema/Postgres.php';
+
+ $params = array(
+ 'driver' => 'postgres',
+ 'hostname' => DB_HOSTNAME,
+ 'username' => DB_USERNAME,
+ 'password' => DB_PASSWORD,
+ 'database' => DB_NAME,
+ );
+
+ break;
+
+ default:
+ die('Database driver not supported');
}
+ $db = new \PicoDb\Database($params);
+
if ($db->schema()->check(Schema\VERSION)) {
return $db;
}
diff --git a/docs/postgresql-configuration.markdown b/docs/postgresql-configuration.markdown
new file mode 100644
index 00000000..04c6c2e7
--- /dev/null
+++ b/docs/postgresql-configuration.markdown
@@ -0,0 +1,38 @@
+Postgresql configuration
+========================
+
+By default, Kanboard use Sqlite to store its data but it's also possible to use Postgresql.
+
+Requirements
+------------
+
+- A Postgresql server already installed and configured
+- The PHP extension `pdo_pgsql` installed (Debian/Ubuntu: `apt-get install php5-pgsql`)
+
+Configuration
+-------------
+
+### Create an empty database with the command `pgsql`:
+
+```sql
+CREATE DATABASE kanboard;
+```
+
+### Create a config file
+
+Inside our config file write those lines:
+
+```php
+<?php
+
+// We choose to use Postgresql instead of Sqlite
+define('DB_DRIVER', 'postgres');
+
+// Mysql parameters
+define('DB_USERNAME', 'REPLACE_ME');
+define('DB_PASSWORD', 'REPLACE_ME');
+define('DB_HOSTNAME', 'REPLACE_ME');
+define('DB_NAME', 'kanboard');
+```
+
+Now, you are ready to use Postgresql.
diff --git a/vendor/PicoDb/Database.php b/vendor/PicoDb/Database.php
index 9201e9d2..4d7b7031 100644
--- a/vendor/PicoDb/Database.php
+++ b/vendor/PicoDb/Database.php
@@ -27,6 +27,11 @@ class Database
$this->pdo = new Mysql($settings);
break;
+ case 'postgres':
+ require_once __DIR__.'/Drivers/Postgres.php';
+ $this->pdo = new Postgres($settings);
+ break;
+
default:
throw new \LogicException('This database driver is not supported.');
}
diff --git a/vendor/PicoDb/Drivers/Postgres.php b/vendor/PicoDb/Drivers/Postgres.php
new file mode 100644
index 00000000..641727f3
--- /dev/null
+++ b/vendor/PicoDb/Drivers/Postgres.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace PicoDb;
+
+class Postgres extends \PDO {
+
+ private $schema_table = 'schema_version';
+
+
+ public function __construct(array $settings)
+ {
+ $required_atttributes = array(
+ 'hostname',
+ 'username',
+ 'password',
+ 'database',
+ );
+
+ foreach ($required_atttributes as $attribute) {
+ if (! isset($settings[$attribute])) {
+ throw new \LogicException('This configuration parameter is missing: "'.$attribute.'"');
+ }
+ }
+
+ $dsn = 'pgsql:host='.$settings['hostname'].';dbname='.$settings['database'];
+
+ parent::__construct($dsn, $settings['username'], $settings['password']);
+
+ if (isset($settings['schema_table'])) {
+ $this->schema_table = $settings['schema_table'];
+ }
+ }
+
+
+ public function getSchemaVersion()
+ {
+ $this->exec("CREATE TABLE IF NOT EXISTS ".$this->schema_table." (version SMALLINT DEFAULT 0)");
+
+ $rq = $this->prepare('SELECT version FROM '.$this->schema_table.'');
+ $rq->execute();
+ $result = $rq->fetch(\PDO::FETCH_ASSOC);
+
+ if (isset($result['version'])) {
+ return (int) $result['version'];
+ }
+ else {
+ $this->exec('INSERT INTO '.$this->schema_table.' VALUES(0)');
+ }
+
+ return 0;
+ }
+
+
+ public function setSchemaVersion($version)
+ {
+ $rq = $this->prepare('UPDATE '.$this->schema_table.' SET version=?');
+ $rq->execute(array($version));
+ }
+
+
+ public function getLastId()
+ {
+ $rq = $this->prepare('SELECT LASTVAL()');
+ $rq->execute();
+ return $rq->fetchColumn();
+ }
+
+
+ public function escapeIdentifier($value)
+ {
+ return $value;
+ }
+} \ No newline at end of file