Projects with duplicate name are now allowed

This commit is contained in:
Frederic Guillot 2015-11-15 19:29:31 -05:00
parent 968ae47454
commit adb35896d8
29 changed files with 57 additions and 44 deletions

View File

@ -1,6 +1,12 @@
Version 1.0.21 (unreleased)
---------------------------
Breaking changes:
* Projects with duplicate name are now allowed:
For Postgres and Mysql the unique constraint is removed by database migration
However Sqlite does not support alter table, only new databases will have the unique constraint removed
New features:
* New automatic action: Assign a category based on a link
@ -28,9 +34,9 @@ Version 1.0.20
Breaking changes:
- Add namespace Kanboard (update your plugins)
- Move Mailgun, Sendgrid, Postmark, Slack, Hipchat and Jabber to plugins
- ReverseProxy authentication check for each request that the username match the user session
* Add namespace Kanboard (update your plugins)
* Move Mailgun, Sendgrid, Postmark, Slack, Hipchat and Jabber to plugins
* ReverseProxy authentication check for each request that the username match the user session
New features:

View File

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

View File

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

View File

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

View File

@ -124,7 +124,6 @@ return array(
'The id is required' => 'El identificador es obligatorio',
'The project id is required' => 'El identificador del proyecto es obligatorio',
'The project name is required' => 'El nombre del proyecto es obligatorio',
'This project must be unique' => 'El nombre del proyecto debe ser único',
'The title is required' => 'El título es obligatorio',
'Settings saved successfully.' => 'Parámetros guardados correctamente.',
'Unable to save your settings.' => 'No se pueden guardar sus parámetros.',
@ -1065,4 +1064,5 @@ return array(
'Usernames must be lowercase and unique' => 'Los nombres de usuario deben ser únicos y contener sólo minúsculas',
'Passwords will be encrypted if present' => 'Las contraseñas serán cifradas si es que existen',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
);

View File

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

View File

@ -124,7 +124,6 @@ return array(
'The id is required' => 'L\'identifiant est obligatoire',
'The project id is required' => 'L\'identifiant du projet est obligatoire',
'The project name is required' => 'Le nom du projet est obligatoire',
'This project must be unique' => 'Le nom du projet doit être unique',
'The title is required' => 'Le titre est obligatoire',
'Settings saved successfully.' => 'Paramètres sauvegardés avec succès.',
'Unable to save your settings.' => 'Impossible de sauvegarder vos réglages.',

View File

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

View File

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

View File

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

View File

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

View File

@ -124,7 +124,6 @@ return array(
'The id is required' => 'Id\'en er pøøkrevet',
'The project id is required' => 'Prosjektet-id er påkrevet',
'The project name is required' => 'Prosjektnavn er påkrevet',
'This project must be unique' => 'Prosjektnavnet skal være unikt',
'The title is required' => 'Tittel er pårevet',
'Settings saved successfully.' => 'Innstillinger lagret.',
'Unable to save your settings.' => 'Innstillinger kunne ikke lagres.',
@ -1065,4 +1064,5 @@ return array(
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
);

View File

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

View File

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

View File

@ -124,7 +124,6 @@ return array(
'The id is required' => 'O ID é obrigatório',
'The project id is required' => 'O ID do projeto é obrigatório',
'The project name is required' => 'O nome do projeto é obrigatório',
'This project must be unique' => 'Este projeto deve ser único',
'The title is required' => 'O título é obrigatório',
'Settings saved successfully.' => 'Configurações salvas com sucesso.',
'Unable to save your settings.' => 'Não é possível salvar suas configurações.',
@ -1065,4 +1064,5 @@ return array(
'Usernames must be lowercase and unique' => 'Nomes de usuário devem ser únicos e em letras minúsculas',
'Passwords will be encrypted if present' => 'Senhas serão encriptadas, se presentes',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
);

View File

@ -124,7 +124,6 @@ return array(
'The id is required' => 'O ID é obrigatório',
'The project id is required' => 'O ID do projecto é obrigatório',
'The project name is required' => 'O nome do projecto é obrigatório',
'This project must be unique' => 'Este projecto deve ser único',
'The title is required' => 'O título é obrigatório',
'Settings saved successfully.' => 'Configurações guardadas com sucesso.',
'Unable to save your settings.' => 'Não é possível guardar as suas configurações.',
@ -1065,4 +1064,5 @@ return array(
// 'Usernames must be lowercase and unique' => '',
// 'Passwords will be encrypted if present' => '',
// '%s attached a new file to the task %s' => '',
// 'Assign automatically a category based on a link' => '',
);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -527,7 +527,6 @@ class Project extends Base
new Validators\MaxLength('start_date', t('The maximum length is %d characters', 10), 10),
new Validators\MaxLength('end_date', t('The maximum length is %d characters', 10), 10),
new Validators\AlphaNumeric('identifier', t('This value must be alphanumeric')) ,
new Validators\Unique('name', t('This project must be unique'), $this->db->getConnection(), self::TABLE),
new Validators\Unique('identifier', t('The identifier must be unique'), $this->db->getConnection(), self::TABLE),
);
}

View File

@ -5,7 +5,13 @@ namespace Schema;
use PDO;
use Kanboard\Core\Security\Token;
const VERSION = 93;
const VERSION = 94;
function version_94(PDO $pdo)
{
$pdo->exec('ALTER TABLE `projects` DROP INDEX `name`');
$pdo->exec('ALTER TABLE `projects` DROP INDEX `name_2`');
}
function version_93(PDO $pdo)
{

View File

@ -5,7 +5,12 @@ namespace Schema;
use PDO;
use Kanboard\Core\Security\Token;
const VERSION = 73;
const VERSION = 74;
function version_74(PDO $pdo)
{
$pdo->exec('ALTER TABLE projects DROP CONSTRAINT IF EXISTS projects_name_key');
}
function version_73(PDO $pdo)
{

View File

@ -270,9 +270,7 @@ CREATE TABLE `projects` (
`identifier` varchar(50) DEFAULT '',
`start_date` varchar(10) DEFAULT '',
`end_date` varchar(10) DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
UNIQUE KEY `name_2` (`name`)
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `remember_me`;
@ -551,7 +549,7 @@ CREATE TABLE `users` (
LOCK TABLES `settings` WRITE;
/*!40000 ALTER TABLE `settings` DISABLE KEYS */;
INSERT INTO `settings` VALUES ('api_token','ccff8d37146322410479c8c6707cdaddde840af28ccbd6fbb5a7d7908844'),('application_currency','USD'),('application_date_format','m/d/Y'),('application_language','en_US'),('application_stylesheet',''),('application_timezone','UTC'),('application_url',''),('board_columns',''),('board_highlight_period','172800'),('board_private_refresh_interval','10'),('board_public_refresh_interval','60'),('calendar_project_tasks','date_started'),('calendar_user_subtasks_time_tracking','0'),('calendar_user_tasks','date_started'),('cfd_include_closed_tasks','1'),('default_color','yellow'),('integration_gravatar','0'),('project_categories',''),('subtask_restriction','0'),('subtask_time_tracking','1'),('webhook_token','7a9d4cd8c7fc4d52c60f01b775d4f2bd6e186d2e44f5b3723157b8eb372b'),('webhook_url','');
INSERT INTO `settings` VALUES ('api_token','3783f814662e83f1ebe9ada40314f93a6e75688fe7e04a3820e4078966f0'),('application_currency','USD'),('application_date_format','m/d/Y'),('application_language','en_US'),('application_stylesheet',''),('application_timezone','UTC'),('application_url',''),('board_columns',''),('board_highlight_period','172800'),('board_private_refresh_interval','10'),('board_public_refresh_interval','60'),('calendar_project_tasks','date_started'),('calendar_user_subtasks_time_tracking','0'),('calendar_user_tasks','date_started'),('cfd_include_closed_tasks','1'),('default_color','yellow'),('integration_gravatar','0'),('project_categories',''),('subtask_restriction','0'),('subtask_time_tracking','1'),('webhook_token','c95cf0a67507ca68cc93f717bb78ac5dfaf0c73d38ab159fc73038aa19d9'),('webhook_url','');
/*!40000 ALTER TABLE `settings` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
@ -580,4 +578,4 @@ UNLOCK TABLES;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
INSERT INTO users (username, password, is_admin) VALUES ('admin', '$2y$10$fDbO.nKAjDxm70DyghADCuqIhF919BAkRTAq0bARDTGwcxZscqIZq', '1');INSERT INTO schema_version VALUES ('93');
INSERT INTO users (username, password, is_admin) VALUES ('admin', '$2y$10$4/2e1E1VIeZVc5PhRHQJmuOBI/UV7H73hRyH60IvpTpY05G9tD49W', '1');INSERT INTO schema_version VALUES ('94');

View File

@ -1314,14 +1314,6 @@ ALTER TABLE ONLY project_has_users
ADD CONSTRAINT project_has_users_project_id_user_id_key UNIQUE (project_id, user_id);
--
-- Name: projects_name_key; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
ALTER TABLE ONLY projects
ADD CONSTRAINT projects_name_key UNIQUE (name);
--
-- Name: projects_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
@ -1930,8 +1922,8 @@ INSERT INTO settings (option, value) VALUES ('board_highlight_period', '172800')
INSERT INTO settings (option, value) VALUES ('board_public_refresh_interval', '60');
INSERT INTO settings (option, value) VALUES ('board_private_refresh_interval', '10');
INSERT INTO settings (option, value) VALUES ('board_columns', '');
INSERT INTO settings (option, value) VALUES ('webhook_token', '29877f0b69d230e57bee9d02e0aa9034a69f7a2c0ba1e3b5d3b390241f36');
INSERT INTO settings (option, value) VALUES ('api_token', '5682955e965bd0cd7618559a25131fe6094d9fff3bb56c31291d64991353');
INSERT INTO settings (option, value) VALUES ('webhook_token', 'ca57fbbc9e17d00a1ca8c2e45d8dc5d1b54ede740f72709bae2e8de26fbd');
INSERT INTO settings (option, value) VALUES ('api_token', 'bc20677f12faa32d9426af9a31041b8576c13f4ab54b641bae955112ae79');
INSERT INTO settings (option, value) VALUES ('application_language', 'en_US');
INSERT INTO settings (option, value) VALUES ('application_timezone', 'UTC');
INSERT INTO settings (option, value) VALUES ('application_url', '');
@ -1995,4 +1987,4 @@ SELECT pg_catalog.setval('links_id_seq', 11, true);
-- PostgreSQL database dump complete
--
INSERT INTO users (username, password, is_admin) VALUES ('admin', '$2y$10$fDbO.nKAjDxm70DyghADCuqIhF919BAkRTAq0bARDTGwcxZscqIZq', '1');INSERT INTO schema_version VALUES ('73');
INSERT INTO users (username, password, is_admin) VALUES ('admin', '$2y$10$4/2e1E1VIeZVc5PhRHQJmuOBI/UV7H73hRyH60IvpTpY05G9tD49W', '1');INSERT INTO schema_version VALUES ('74');

View File

@ -1026,7 +1026,7 @@ function version_1(PDO $pdo)
$pdo->exec("
CREATE TABLE projects (
id INTEGER PRIMARY KEY,
name TEXT NOCASE NOT NULL UNIQUE,
name TEXT NOCASE NOT NULL,
is_active INTEGER DEFAULT 1
)
");

View File

@ -44,6 +44,14 @@ class ProjectTest extends Base
$this->assertEmpty($project['token']);
}
public function testCreationWithDuplicateName()
{
$p = new Project($this->container);
$this->assertEquals(1, $p->create(array('name' => 'UnitTest')));
$this->assertEquals(2, $p->create(array('name' => 'UnitTest')));
}
public function testCreationWithStartAndDate()
{
$p = new Project($this->container);