diff options
64 files changed, 706 insertions, 244 deletions
diff --git a/Vagrantfile b/Vagrantfile index f1b7a6f3..94eb41b8 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -6,7 +6,7 @@ VAGRANTFILE_API_VERSION = "2" $script = <<SCRIPT # install packages apt-get update -apt-get install -y apache2 php5 php5-sqlite php5-ldap php5-xdebug +apt-get install -y apache2 php5 php5-sqlite php5-ldap php5-xdebug php5-curl php5-mysql php5-pgsql service apache2 restart rm -f /var/www/html/index.html date > /etc/vagrant_provisioned_at diff --git a/app/Controller/Base.php b/app/Controller/Base.php index e07aabf7..9c1416fc 100644 --- a/app/Controller/Base.php +++ b/app/Controller/Base.php @@ -5,7 +5,6 @@ namespace Controller; use Core\Tool; use Core\Registry; use Core\Security; -use Core\Translator; use Model\LastLogin; /** @@ -123,12 +122,8 @@ abstract class Base $this->response->hsts(); } - // Load translations - $language = $this->config->get('language', 'en_US'); - if ($language !== 'en_US') Translator::load($language); - - // Set timezone - date_default_timezone_set($this->config->get('timezone', 'UTC')); + $this->config->setupTranslations(); + $this->config->setupTimezone(); // Authentication if (! $this->authentication->isAuthenticated($controller, $action)) { diff --git a/app/Controller/Board.php b/app/Controller/Board.php index b56adca0..0e1dd3e1 100644 --- a/app/Controller/Board.php +++ b/app/Controller/Board.php @@ -177,8 +177,8 @@ class Board extends Base 'categories' => $this->category->getList($project['id'], false), 'title' => $project['name'], 'no_layout' => true, - 'auto_refresh' => true, 'not_editable' => true, + 'board_public_refresh_interval' => $this->config->get('board_public_refresh_interval'), ))); } @@ -238,6 +238,8 @@ class Board extends Base 'menu' => 'boards', 'title' => $projects[$project['id']], 'board_selector' => $board_selector, + 'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'), + 'board_highlight_period' => $this->config->get('board_highlight_period'), ))); } @@ -407,6 +409,8 @@ class Board extends Base 'current_project_id' => $project_id, 'board' => $this->board->get($project_id), 'categories' => $this->category->getList($project_id, false), + 'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'), + 'board_highlight_period' => $this->config->get('board_highlight_period'), )), 201 ); @@ -443,6 +447,8 @@ class Board extends Base 'current_project_id' => $project_id, 'board' => $this->board->get($project_id), 'categories' => $this->category->getList($project_id, false), + 'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'), + 'board_highlight_period' => $this->config->get('board_highlight_period'), )) ); } diff --git a/app/Controller/Config.php b/app/Controller/Config.php index 4c3018c1..869d4331 100644 --- a/app/Controller/Config.php +++ b/app/Controller/Config.php @@ -11,59 +11,118 @@ namespace Controller; class Config extends Base { /** - * Display the settings page + * Common layout for config views * - * @access public + * @access private + * @param string $template Template name + * @param array $params Template parameters + * @return string */ - public function index() + private function layout($template, array $params) { - $this->response->html($this->template->layout('config_index', array( - 'db_size' => $this->config->getDatabaseSize(), - 'languages' => $this->config->getLanguages(), - 'values' => $this->config->getAll(), - 'errors' => array(), - 'menu' => 'config', - 'title' => t('Settings'), - 'timezones' => $this->config->getTimezones(), - 'default_columns' => implode(', ', $this->board->getDefaultColumns()), - ))); + $params['values'] = $this->config->getAll(); + $params['errors'] = array(); + $params['menu'] = 'config'; + $params['config_content_for_layout'] = $this->template->load($template, $params); + + return $this->template->layout('config_layout', $params); } /** - * Validate and save settings + * Common method between pages * - * @access public + * @access private + * @param string $redirect Action to redirect after saving the form */ - public function save() + private function common($redirect) { - $values = $this->request->getValues(); - list($valid, $errors) = $this->config->validateModification($values); + if ($this->request->isPost()) { - if ($valid) { + $values = $this->request->getValues(); if ($this->config->save($values)) { $this->config->reload(); $this->session->flash(t('Settings saved successfully.')); - } else { + } + else { $this->session->flashError(t('Unable to save your settings.')); } - $this->response->redirect('?controller=config'); + $this->response->redirect('?controller=config&action='.$redirect); } + } - $this->response->html($this->template->layout('config_index', array( + /** + * Display the about page + * + * @access public + */ + public function index() + { + $this->response->html($this->layout('config_about', array( 'db_size' => $this->config->getDatabaseSize(), + 'title' => t('About'), + ))); + } + + /** + * Display the application settings page + * + * @access public + */ + public function application() + { + $this->common('application'); + + $this->response->html($this->layout('config_application', array( + 'title' => t('Application settings'), 'languages' => $this->config->getLanguages(), - 'values' => $values, - 'errors' => $errors, - 'menu' => 'config', - 'title' => t('Settings'), 'timezones' => $this->config->getTimezones(), + ))); + } + + /** + * Display the board settings page + * + * @access public + */ + public function board() + { + $this->common('board'); + + $this->response->html($this->layout('config_board', array( + 'title' => t('Board settings'), 'default_columns' => implode(', ', $this->board->getDefaultColumns()), ))); } /** + * Display the webhook settings page + * + * @access public + */ + public function webhook() + { + $this->common('webhook'); + + $this->response->html($this->layout('config_webhook', array( + 'title' => t('Webhook settings'), + ))); + } + + /** + * Display the api settings page + * + * @access public + */ + public function api() + { + $this->response->html($this->layout('config_api', array( + 'title' => t('API'), + ))); + } + + /** * Download the Sqlite database * * @access public @@ -89,15 +148,18 @@ class Config extends Base } /** - * Regenerate all application tokens + * Regenerate webhook token * * @access public */ - public function tokens() + public function token() { + $type = $this->request->getStringParam('type'); + $this->checkCSRFParam(); - $this->config->regenerateTokens(); - $this->session->flash(t('All tokens have been regenerated.')); - $this->response->redirect('?controller=config'); + $this->config->regenerateToken($type.'_token'); + + $this->session->flash(t('Token regenerated.')); + $this->response->redirect('?controller=config&action='.$type); } } diff --git a/app/Controller/User.php b/app/Controller/User.php index cc7464e6..bbed9f6f 100644 --- a/app/Controller/User.php +++ b/app/Controller/User.php @@ -73,7 +73,7 @@ class User extends Base } /** - * Common layout for project views + * Common layout for user views * * @access private * @param string $template Template name diff --git a/app/Controller/Webhook.php b/app/Controller/Webhook.php index c72dc983..71acab08 100644 --- a/app/Controller/Webhook.php +++ b/app/Controller/Webhook.php @@ -17,7 +17,7 @@ class Webhook extends Base */ public function task() { - if ($this->config->get('webhooks_token') !== $this->request->getStringParam('token')) { + if ($this->config->get('webhook_token') !== $this->request->getStringParam('token')) { $this->response->text('Not Authorized', 401); } @@ -49,7 +49,7 @@ class Webhook extends Base */ public function github() { - if ($this->config->get('webhooks_token') !== $this->request->getStringParam('token')) { + if ($this->config->get('webhook_token') !== $this->request->getStringParam('token')) { $this->response->text('Not Authorized', 401); } diff --git a/app/Locales/de_DE/translations.php b/app/Locales/de_DE/translations.php index ec570691..93721f65 100644 --- a/app/Locales/de_DE/translations.php +++ b/app/Locales/de_DE/translations.php @@ -83,7 +83,7 @@ return array( 'Settings' => 'Einstellungen', 'Application settings' => 'Anwendungskonfiguration', 'Language' => 'Sprache', - 'Webhooks token:' => 'Webhooks Token:', + // 'Webhook token:' => '', 'API token:' => 'API Token:', 'More information' => 'Mehr Informationen', 'Database size:' => 'Datenbankgröße:', @@ -516,4 +516,22 @@ return array( // 'Reference' => '', // 'Reference: %s' => '', // 'Label' => '', + // 'Database' => '', + // 'About' => '', + // 'Database driver:' => '', + // 'Board settings' => '', + // 'URL and token' => '', + // 'Webhook settings' => '', + // 'URL for task creation:' => '', + // 'Reset token' => '', + // 'API endpoint:' => '', + // 'Refresh interval for private board' => '', + // 'Refresh interval for public board' => '', + // 'Task highlight period' => '', + // 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '', + // 'Frequency in second (60 seconds by default)' => '', + // 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '', + // 'Application URL' => '', + // 'Example: http://example.kanboard.net/ (used by email notifications)' => '', + // 'Token regenerated.' => '', ); diff --git a/app/Locales/es_ES/translations.php b/app/Locales/es_ES/translations.php index 05646a74..8b92b9a5 100644 --- a/app/Locales/es_ES/translations.php +++ b/app/Locales/es_ES/translations.php @@ -83,7 +83,7 @@ return array( 'Settings' => 'Preferencias', 'Application settings' => 'Parámetros de la aplicación', 'Language' => 'Idioma', - 'Webhooks token:' => 'Ficha de seguridad (token) para los webhooks :', + 'Webhook 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:', @@ -516,4 +516,22 @@ return array( // 'Reference' => '', // 'Reference: %s' => '', // 'Label' => '', + // 'Database' => '', + // 'About' => '', + // 'Database driver:' => '', + // 'Board settings' => '', + // 'URL and token' => '', + // 'Webhook settings' => '', + // 'URL for task creation:' => '', + // 'Reset token' => '', + // 'API endpoint:' => '', + // 'Refresh interval for private board' => '', + // 'Refresh interval for public board' => '', + // 'Task highlight period' => '', + // 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '', + // 'Frequency in second (60 seconds by default)' => '', + // 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '', + // 'Application URL' => '', + // 'Example: http://example.kanboard.net/ (used by email notifications)' => '', + // 'Token regenerated.' => '', ); diff --git a/app/Locales/fi_FI/translations.php b/app/Locales/fi_FI/translations.php index 3bb2185e..923ba970 100644 --- a/app/Locales/fi_FI/translations.php +++ b/app/Locales/fi_FI/translations.php @@ -83,7 +83,7 @@ return array( 'Settings' => 'Asetukset', 'Application settings' => 'Ohjelman asetukset', 'Language' => 'Kieli', - 'Webhooks token:' => 'Webhooks avain:', + 'Webhook token:' => 'Webhooks avain:', // 'API token:' => '', 'More information' => 'Lisätietoja', 'Database size:' => 'Tietokannan koko:', @@ -516,4 +516,22 @@ return array( // 'Reference' => '', // 'Reference: %s' => '', // 'Label' => '', + // 'Database' => '', + // 'About' => '', + // 'Database driver:' => '', + // 'Board settings' => '', + // 'URL and token' => '', + // 'Webhook settings' => '', + // 'URL for task creation:' => '', + // 'Reset token' => '', + // 'API endpoint:' => '', + // 'Refresh interval for private board' => '', + // 'Refresh interval for public board' => '', + // 'Task highlight period' => '', + // 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '', + // 'Frequency in second (60 seconds by default)' => '', + // 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '', + // 'Application URL' => '', + // 'Example: http://example.kanboard.net/ (used by email notifications)' => '', + // 'Token regenerated.' => '', ); diff --git a/app/Locales/fr_FR/translations.php b/app/Locales/fr_FR/translations.php index c1723875..19c15f86 100644 --- a/app/Locales/fr_FR/translations.php +++ b/app/Locales/fr_FR/translations.php @@ -83,7 +83,7 @@ return array( 'Settings' => 'Préférences', 'Application settings' => 'Paramètres de l\'application', 'Language' => 'Langue', - 'Webhooks token:' => 'Jeton de securité pour les webhooks :', + 'Webhook 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 :', @@ -516,4 +516,22 @@ return array( 'Reference' => 'Référence', 'Reference: %s' => 'Référence : %s', 'Label' => 'Libellé', + 'Database' => 'Base de données', + 'About' => 'A propos', + 'Database driver:' => 'Type de base de données :', + 'Board settings' => 'Paramètres du tableau', + 'URL and token' => 'URL et jeton de sécurité', + 'Webhook settings' => 'Paramètres pour les webhooks', + 'URL for task creation:' => 'URL pour la création de tâche :', + 'Reset token' => 'Regénérer le jeton de sécurité', + 'API endpoint:' => 'URL de l\'API :', + 'Refresh interval for private board' => 'Intervalle pour rafraîchir un tableau privé', + 'Refresh interval for public board' => 'Intervalle pour rafraîchir un tableau public', + 'Task highlight period' => 'Durée pour mettre une tâche en évidence', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Durée en seconde pour considérer une tâche comme récemment modifiée (0 pour désactiver, 2 jours par défaut)', + 'Frequency in second (60 seconds by default)' => 'Fréquence en seconde (60 secondes par défaut)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Fréquence en seconde (0 pour désactiver, 10 secondes par défaut)', + 'Application URL' => 'URL de l\'application', + 'Example: http://example.kanboard.net/ (used by email notifications)' => 'Exemple : http://exemple.kanboard.net/ (utilisé pour les notifications)', + 'Token regenerated.' => 'Jeton de sécurité regénéré.', ); diff --git a/app/Locales/it_IT/translations.php b/app/Locales/it_IT/translations.php index 6acac109..e5b5eb57 100644 --- a/app/Locales/it_IT/translations.php +++ b/app/Locales/it_IT/translations.php @@ -83,7 +83,7 @@ return array( 'Settings' => 'Impostazioni', 'Application settings' => 'Impostazioni dell\'applicazione', 'Language' => 'Lingua', - 'Webhooks token:' => 'Identificatore (token) per i webhooks :', + 'Webhook token:' => 'Identificatore (token) per i webhooks :', // 'API token:' => '', 'More information' => 'Più informazioni', 'Database size:' => 'Dimensioni della base dati:', @@ -516,4 +516,22 @@ return array( // 'Reference' => '', // 'Reference: %s' => '', // 'Label' => '', + // 'Database' => '', + // 'About' => '', + // 'Database driver:' => '', + // 'Board settings' => '', + // 'URL and token' => '', + // 'Webhook settings' => '', + // 'URL for task creation:' => '', + // 'Reset token' => '', + // 'API endpoint:' => '', + // 'Refresh interval for private board' => '', + // 'Refresh interval for public board' => '', + // 'Task highlight period' => '', + // 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '', + // 'Frequency in second (60 seconds by default)' => '', + // 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '', + // 'Application URL' => '', + // 'Example: http://example.kanboard.net/ (used by email notifications)' => '', + // 'Token regenerated.' => '', ); diff --git a/app/Locales/pl_PL/translations.php b/app/Locales/pl_PL/translations.php index 3e33ce8b..cdf200b9 100644 --- a/app/Locales/pl_PL/translations.php +++ b/app/Locales/pl_PL/translations.php @@ -83,7 +83,7 @@ return array( 'Settings' => 'Ustawienia', 'Application settings' => 'Ustawienia aplikacji', 'Language' => 'Język', - 'Webhooks token:' => 'Token :', + 'Webhook token:' => 'Token :', // 'API token:' => '', 'More information' => 'Więcej informacji', 'Database size:' => 'Rozmiar bazy danych :', @@ -516,4 +516,22 @@ return array( // 'Reference' => '', // 'Reference: %s' => '', // 'Label' => '', + // 'Database' => '', + // 'About' => '', + // 'Database driver:' => '', + // 'Board settings' => '', + // 'URL and token' => '', + // 'Webhook settings' => '', + // 'URL for task creation:' => '', + // 'Reset token' => '', + // 'API endpoint:' => '', + // 'Refresh interval for private board' => '', + // 'Refresh interval for public board' => '', + // 'Task highlight period' => '', + // 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '', + // 'Frequency in second (60 seconds by default)' => '', + // 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '', + // 'Application URL' => '', + // 'Example: http://example.kanboard.net/ (used by email notifications)' => '', + // 'Token regenerated.' => '', ); diff --git a/app/Locales/pt_BR/translations.php b/app/Locales/pt_BR/translations.php index 68946936..1524355d 100644 --- a/app/Locales/pt_BR/translations.php +++ b/app/Locales/pt_BR/translations.php @@ -83,7 +83,7 @@ return array( 'Settings' => 'Preferências', 'Application settings' => 'Preferências da aplicação', 'Language' => 'Idioma', - 'Webhooks token:' => 'Token de webhooks:', + 'Webhook token:' => 'Token de webhooks:', 'API token:' => 'API Token:', 'More information' => 'Mais informação', 'Database size:' => 'Tamanho do banco de dados:', @@ -516,4 +516,22 @@ return array( // 'Reference' => '', // 'Reference: %s' => '', // 'Label' => '', + // 'Database' => '', + // 'About' => '', + // 'Database driver:' => '', + // 'Board settings' => '', + // 'URL and token' => '', + // 'Webhook settings' => '', + // 'URL for task creation:' => '', + // 'Reset token' => '', + // 'API endpoint:' => '', + // 'Refresh interval for private board' => '', + // 'Refresh interval for public board' => '', + // 'Task highlight period' => '', + // 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '', + // 'Frequency in second (60 seconds by default)' => '', + // 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '', + // 'Application URL' => '', + // 'Example: http://example.kanboard.net/ (used by email notifications)' => '', + // 'Token regenerated.' => '', ); diff --git a/app/Locales/ru_RU/translations.php b/app/Locales/ru_RU/translations.php index a949dbd0..e5e3e8bc 100644 --- a/app/Locales/ru_RU/translations.php +++ b/app/Locales/ru_RU/translations.php @@ -83,7 +83,7 @@ return array( 'Settings' => 'Настройки', 'Application settings' => 'Настройки приложения', 'Language' => 'Язык', - 'Webhooks token:' => 'Webhooks токен :', + 'Webhook token:' => 'Webhooks токен :', 'API token:' => 'API токен :', 'More information' => 'Подробнее', 'Database size:' => 'Размер базы данных :', @@ -516,4 +516,22 @@ return array( // 'Reference' => '', // 'Reference: %s' => '', // 'Label' => '', + // 'Database' => '', + // 'About' => '', + // 'Database driver:' => '', + // 'Board settings' => '', + // 'URL and token' => '', + // 'Webhook settings' => '', + // 'URL for task creation:' => '', + // 'Reset token' => '', + // 'API endpoint:' => '', + // 'Refresh interval for private board' => '', + // 'Refresh interval for public board' => '', + // 'Task highlight period' => '', + // 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '', + // 'Frequency in second (60 seconds by default)' => '', + // 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '', + // 'Application URL' => '', + // 'Example: http://example.kanboard.net/ (used by email notifications)' => '', + // 'Token regenerated.' => '', ); diff --git a/app/Locales/sv_SE/translations.php b/app/Locales/sv_SE/translations.php index 68c0597f..4c8aef81 100644 --- a/app/Locales/sv_SE/translations.php +++ b/app/Locales/sv_SE/translations.php @@ -83,7 +83,7 @@ return array( 'Settings' => 'Inställningar', 'Application settings' => 'Applikationsinställningar', 'Language' => 'Språk', - 'Webhooks token:' => 'Token för webhooks:', + 'Webhook token:' => 'Token för webhooks:', 'API token:' => 'API token:', 'More information' => 'Mer information', 'Database size:' => 'Databasstorlek:', @@ -516,4 +516,22 @@ return array( // 'Reference' => '', // 'Reference: %s' => '', // 'Label' => '', + // 'Database' => '', + // 'About' => '', + // 'Database driver:' => '', + // 'Board settings' => '', + // 'URL and token' => '', + // 'Webhook settings' => '', + // 'URL for task creation:' => '', + // 'Reset token' => '', + // 'API endpoint:' => '', + // 'Refresh interval for private board' => '', + // 'Refresh interval for public board' => '', + // 'Task highlight period' => '', + // 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '', + // 'Frequency in second (60 seconds by default)' => '', + // 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '', + // 'Application URL' => '', + // 'Example: http://example.kanboard.net/ (used by email notifications)' => '', + // 'Token regenerated.' => '', ); diff --git a/app/Locales/zh_CN/translations.php b/app/Locales/zh_CN/translations.php index ef33881e..3285e9a6 100644 --- a/app/Locales/zh_CN/translations.php +++ b/app/Locales/zh_CN/translations.php @@ -83,7 +83,7 @@ return array( 'Settings' => '设置', 'Application settings' => '应用设置', 'Language' => '语言', - 'Webhooks token:' => '页面钩子令牌:', + 'Webhook token:' => '页面钩子令牌:', // 'API token:' => '', 'More information' => '更多信息', 'Database size:' => '数据库大小:', @@ -516,4 +516,22 @@ return array( // 'Reference' => '', // 'Reference: %s' => '', // 'Label' => '', + // 'Database' => '', + // 'About' => '', + // 'Database driver:' => '', + // 'Board settings' => '', + // 'URL and token' => '', + // 'Webhook settings' => '', + // 'URL for task creation:' => '', + // 'Reset token' => '', + // 'API endpoint:' => '', + // 'Refresh interval for private board' => '', + // 'Refresh interval for public board' => '', + // 'Task highlight period' => '', + // 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '', + // 'Frequency in second (60 seconds by default)' => '', + // 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '', + // 'Application URL' => '', + // 'Example: http://example.kanboard.net/ (used by email notifications)' => '', + // 'Token regenerated.' => '', ); diff --git a/app/Model/Config.php b/app/Model/Config.php index f411e3e2..da261de6 100644 --- a/app/Model/Config.php +++ b/app/Model/Config.php @@ -6,6 +6,7 @@ use SimpleValidator\Validator; use SimpleValidator\Validators; use Core\Translator; use Core\Security; +use Core\Session; /** * Config model @@ -20,7 +21,7 @@ class Config extends Base * * @var string */ - const TABLE = 'config'; + const TABLE = 'settings'; /** * Get available timezones @@ -68,6 +69,11 @@ class Config extends Base */ public function get($name, $default_value = '') { + if (! Session::isOpen()) { + $value = $this->db->table(self::TABLE)->eq('option', $name)->findOneColumn('value'); + return $value ?: $default_value; + } + if (! isset($_SESSION['config'][$name])) { $_SESSION['config'] = $this->getAll(); } @@ -87,7 +93,7 @@ class Config extends Base */ public function getAll() { - return $this->db->table(self::TABLE)->findOne(); + return $this->db->table(self::TABLE)->listing('option', 'value'); } /** @@ -99,8 +105,16 @@ class Config extends Base */ public function save(array $values) { - $_SESSION['config'] = $values; - return $this->db->table(self::TABLE)->update($values); + foreach ($values as $option => $value) { + + $result = $this->db->table(self::TABLE)->eq('option', $option)->update(array('value' => $value)); + + if (! $result) { + return false; + } + } + + return true; } /** @@ -111,27 +125,31 @@ class Config extends Base public function reload() { $_SESSION['config'] = $this->getAll(); - Translator::load($this->get('language', 'en_US')); + $this->setupTranslations(); } /** - * Validate settings modification + * Load translations * * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors */ - public function validateModification(array $values) + public function setupTranslations() { - $v = new Validator($values, array( - new Validators\Required('language', t('The language is required')), - new Validators\Required('timezone', t('The timezone is required')), - )); + $language = $this->get('application_language', 'en_US'); - return array( - $v->execute(), - $v->getErrors() - ); + if ($language !== 'en_US') { + Translator::load($language); + } + } + + /** + * Set timezone + * + * @access public + */ + public function setupTimezone() + { + date_default_timezone_set($this->get('application_timezone', 'UTC')); } /** @@ -168,21 +186,15 @@ class Config extends Base } /** - * Regenerate all tokens (projects and webhooks) + * Regenerate a token * * @access public + * @param string $option Parameter name */ - public function regenerateTokens() + public function regenerateToken($option) { - $this->db->table(self::TABLE)->update(array( - 'webhooks_token' => Security::generateToken(), - 'api_token' => Security::generateToken(), - )); - - $projects = $this->db->table(Project::TABLE)->findAllByColumn('id'); - - foreach ($projects as $project_id) { - $this->db->table(Project::TABLE)->eq('id', $project_id)->update(array('token' => Security::generateToken())); - } + return $this->db->table(self::TABLE) + ->eq('option', $option) + ->update(array('value' => Security::generateToken())); } } diff --git a/app/Model/Notification.php b/app/Model/Notification.php index a7dd7fc0..3c14caf0 100644 --- a/app/Model/Notification.php +++ b/app/Model/Notification.php @@ -182,7 +182,7 @@ class Notification extends Base public function getMailContent($template, array $data) { $tpl = new Template; - return $tpl->load($template, $data); + return $tpl->load($template, $data + array('application_url' => $this->config->get('application_url'))); } /** diff --git a/app/Model/Project.php b/app/Model/Project.php index c3e8cc55..d2b769ed 100644 --- a/app/Model/Project.php +++ b/app/Model/Project.php @@ -287,7 +287,7 @@ class Project extends Base } $project_id = $this->db->getConnection()->getLastId(); - $column_names = explode(',', $this->config->get('default_columns', implode(',', $this->board->getDefaultColumns()))); + $column_names = explode(',', $this->config->get('board_columns', implode(',', $this->board->getDefaultColumns()))); $columns = array(); foreach ($column_names as $column_name) { diff --git a/app/Model/Webhook.php b/app/Model/Webhook.php index e03bdcb4..241806ba 100644 --- a/app/Model/Webhook.php +++ b/app/Model/Webhook.php @@ -64,9 +64,9 @@ class Webhook extends Base */ 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'); + $this->url_task_creation = $this->config->get('webhook_url_task_creation'); + $this->url_task_modification = $this->config->get('webhook_url_task_modification'); + $this->token = $this->config->get('webhook_token'); if ($this->url_task_creation) { $this->attachCreateEvents(); diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index 577fac80..e6a33ee5 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -2,9 +2,40 @@ namespace Schema; +use PDO; use Core\Security; -const VERSION = 28; +const VERSION = 29; + +function version_29($pdo) +{ + $pdo->exec(" + CREATE TABLE settings ( + option VARCHAR(100) PRIMARY KEY, + value VARCHAR(255) DEFAULT '' + ) + "); + + // Migrate old config parameters + $rq = $pdo->prepare('SELECT * FROM config'); + $rq->execute(); + $parameters = $rq->fetch(PDO::FETCH_ASSOC); + + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('board_highlight_period', defined('RECENT_TASK_PERIOD') ? RECENT_TASK_PERIOD : 48*60*60)); + $rq->execute(array('board_public_refresh_interval', defined('BOARD_PUBLIC_CHECK_INTERVAL') ? BOARD_PUBLIC_CHECK_INTERVAL : 60)); + $rq->execute(array('board_private_refresh_interval', defined('BOARD_CHECK_INTERVAL') ? BOARD_CHECK_INTERVAL : 10)); + $rq->execute(array('board_columns', $parameters['default_columns'])); + $rq->execute(array('webhook_url_task_creation', $parameters['webhooks_url_task_creation'])); + $rq->execute(array('webhook_url_task_modification', $parameters['webhooks_url_task_modification'])); + $rq->execute(array('webhook_token', $parameters['webhooks_token'])); + $rq->execute(array('api_token', $parameters['api_token'])); + $rq->execute(array('application_language', $parameters['language'])); + $rq->execute(array('application_timezone', $parameters['timezone'])); + $rq->execute(array('application_url', defined('KANBOARD_URL') ? KANBOARD_URL : '')); + + $pdo->exec('DROP TABLE config'); +} function version_28($pdo) { diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php index 33859513..0eeb2a72 100644 --- a/app/Schema/Postgres.php +++ b/app/Schema/Postgres.php @@ -2,9 +2,40 @@ namespace Schema; +use PDO; use Core\Security; -const VERSION = 9; +const VERSION = 10; + +function version_10($pdo) +{ + $pdo->exec(" + CREATE TABLE settings ( + option VARCHAR(100) PRIMARY KEY, + value VARCHAR(255) DEFAULT '' + ) + "); + + // Migrate old config parameters + $rq = $pdo->prepare('SELECT * FROM config'); + $rq->execute(); + $parameters = $rq->fetch(PDO::FETCH_ASSOC); + + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('board_highlight_period', defined('RECENT_TASK_PERIOD') ? RECENT_TASK_PERIOD : 48*60*60)); + $rq->execute(array('board_public_refresh_interval', defined('BOARD_PUBLIC_CHECK_INTERVAL') ? BOARD_PUBLIC_CHECK_INTERVAL : 60)); + $rq->execute(array('board_private_refresh_interval', defined('BOARD_CHECK_INTERVAL') ? BOARD_CHECK_INTERVAL : 10)); + $rq->execute(array('board_columns', $parameters['default_columns'])); + $rq->execute(array('webhook_url_task_creation', $parameters['webhooks_url_task_creation'])); + $rq->execute(array('webhook_url_task_modification', $parameters['webhooks_url_task_modification'])); + $rq->execute(array('webhook_token', $parameters['webhooks_token'])); + $rq->execute(array('api_token', $parameters['api_token'])); + $rq->execute(array('application_language', $parameters['language'])); + $rq->execute(array('application_timezone', $parameters['timezone'])); + $rq->execute(array('application_url', defined('KANBOARD_URL') ? KANBOARD_URL : '')); + + $pdo->exec('DROP TABLE config'); +} function version_9($pdo) { diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index d9b3787d..5986d327 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -3,8 +3,39 @@ namespace Schema; use Core\Security; +use PDO; -const VERSION = 28; +const VERSION = 29; + +function version_29($pdo) +{ + $pdo->exec(" + CREATE TABLE settings ( + option TEXT PRIMARY KEY, + value TEXT DEFAULT '' + ) + "); + + // Migrate old config parameters + $rq = $pdo->prepare('SELECT * FROM config'); + $rq->execute(); + $parameters = $rq->fetch(PDO::FETCH_ASSOC); + + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('board_highlight_period', defined('RECENT_TASK_PERIOD') ? RECENT_TASK_PERIOD : 48*60*60)); + $rq->execute(array('board_public_refresh_interval', defined('BOARD_PUBLIC_CHECK_INTERVAL') ? BOARD_PUBLIC_CHECK_INTERVAL : 60)); + $rq->execute(array('board_private_refresh_interval', defined('BOARD_CHECK_INTERVAL') ? BOARD_CHECK_INTERVAL : 10)); + $rq->execute(array('board_columns', $parameters['default_columns'])); + $rq->execute(array('webhook_url_task_creation', $parameters['webhooks_url_task_creation'])); + $rq->execute(array('webhook_url_task_modification', $parameters['webhooks_url_task_modification'])); + $rq->execute(array('webhook_token', $parameters['webhooks_token'])); + $rq->execute(array('api_token', $parameters['api_token'])); + $rq->execute(array('application_language', $parameters['language'])); + $rq->execute(array('application_timezone', $parameters['timezone'])); + $rq->execute(array('application_url', defined('KANBOARD_URL') ? KANBOARD_URL : '')); + + $pdo->exec('DROP TABLE config'); +} function version_28($pdo) { diff --git a/app/Templates/board_index.php b/app/Templates/board_index.php index da40468d..bff7dcc9 100644 --- a/app/Templates/board_index.php +++ b/app/Templates/board_index.php @@ -26,7 +26,13 @@ <?php if (empty($board)): ?> <p class="alert alert-error"><?= t('There is no column in your project!') ?></p> <?php else: ?> - <?= Helper\template('board_show', array('current_project_id' => $current_project_id, 'board' => $board, 'categories' => $categories)) ?> + <?= Helper\template('board_show', array( + 'current_project_id' => $current_project_id, + 'board' => $board, + 'categories' => $categories, + 'board_private_refresh_interval' => $board_private_refresh_interval, + 'board_highlight_period' => $board_highlight_period, + )) ?> <?php endif ?> </section> diff --git a/app/Templates/board_show.php b/app/Templates/board_show.php index e91ab4cf..e8c3c1ba 100644 --- a/app/Templates/board_show.php +++ b/app/Templates/board_show.php @@ -1,4 +1,4 @@ -<table id="board" data-project-id="<?= $current_project_id ?>" data-time="<?= time() ?>" data-check-interval="<?= BOARD_CHECK_INTERVAL ?>" data-csrf-token=<?= \Core\Security::getCSRFToken() ?>> +<table id="board" data-project-id="<?= $current_project_id ?>" data-time="<?= time() ?>" data-check-interval="<?= $board_private_refresh_interval ?>" data-csrf-token=<?= \Core\Security::getCSRFToken() ?>> <tr> <?php $column_with = round(100 / count($board), 2); ?> <?php foreach ($board as $column): ?> @@ -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'] ?> <?= $task['date_modification'] > time() - RECENT_TASK_PERIOD ? 'task-board-recent' : '' ?>" + <div class="task-board draggable-item task-<?= $task['color_id'] ?> <?= $task['date_modification'] > time() - $board_highlight_period ? 'task-board-recent' : '' ?>" data-task-id="<?= $task['id'] ?>" data-owner-id="<?= $task['owner_id'] ?>" data-category-id="<?= $task['category_id'] ?>" diff --git a/app/Templates/config_about.php b/app/Templates/config_about.php new file mode 100644 index 00000000..3f34f802 --- /dev/null +++ b/app/Templates/config_about.php @@ -0,0 +1,41 @@ +<div class="page-header"> + <h2><?= t('About') ?></h2> +</div> +<section class="listing"> + <ul> + <li> + <?= t('Official website:') ?> + <a href="http://kanboard.net/" target="_blank" rel="noreferer">http://kanboard.net/</a> + </li> + <li> + <?= t('Application version:') ?> + <strong><?= APP_VERSION ?></strong> + </li> + </ul> +</section> + +<div class="page-header"> + <h2><?= t('Database') ?></h2> +</div> +<section class="listing"> + <ul> + <li> + <?= t('Database driver:') ?> + <strong><?= Helper\escape(DB_DRIVER) ?></strong> + </li> + <?php if (DB_DRIVER === 'sqlite'): ?> + <li> + <?= t('Database size:') ?> + <strong><?= Helper\format_bytes($db_size) ?></strong> + </li> + <li> + <?= Helper\a(t('Download the database'), 'config', 'downloadDb', array(), true) ?> + <?= t('(Gzip compressed Sqlite file)') ?> + </li> + <li> + <?= Helper\a(t('Optimize the database'), 'config', 'optimizeDb', array(), true) ?> + <?= t('(VACUUM command)') ?> + </li> + <?php endif ?> + </ul> +</section>
\ No newline at end of file diff --git a/app/Templates/config_api.php b/app/Templates/config_api.php new file mode 100644 index 00000000..037ea08d --- /dev/null +++ b/app/Templates/config_api.php @@ -0,0 +1,18 @@ +<div class="page-header"> + <h2><?= t('API') ?></h2> +</div> +<section class="listing"> + <ul> + <li> + <?= t('API token:') ?> + <strong><?= Helper\escape($values['api_token']) ?></strong> + </li> + <li> + <?= t('API endpoint:') ?> + <input type="text" readonly="readonly" value="<?= Helper\get_current_base_url().'jsonrpc.php' ?>"> + </li> + <li> + <?= Helper\a(t('Reset token'), 'config', 'token', array('type' => 'api'), true) ?> + </li> + </ul> +</section>
\ No newline at end of file diff --git a/app/Templates/config_application.php b/app/Templates/config_application.php new file mode 100644 index 00000000..251a45dc --- /dev/null +++ b/app/Templates/config_application.php @@ -0,0 +1,23 @@ +<div class="page-header"> + <h2><?= t('Application settings') ?></h2> +</div> +<section> +<form method="post" action="<?= Helper\u('config', 'application') ?>" autocomplete="off"> + + <?= Helper\form_csrf() ?> + + <?= Helper\form_label(t('Application URL'), 'application_url') ?> + <?= Helper\form_text('application_url', $values, $errors, array('placeholder="http://example.kanboar.net/"')) ?><br/> + <p class="form-help"><?= t('Example: http://example.kanboard.net/ (used by email notifications)') ?></p> + + <?= Helper\form_label(t('Language'), 'application_language') ?> + <?= Helper\form_select('application_language', $languages, $values, $errors) ?><br/> + + <?= Helper\form_label(t('Timezone'), 'application_timezone') ?> + <?= Helper\form_select('application_timezone', $timezones, $values, $errors) ?><br/> + + <div class="form-actions"> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + </div> +</form> +</section>
\ No newline at end of file diff --git a/app/Templates/config_board.php b/app/Templates/config_board.php new file mode 100644 index 00000000..f260d084 --- /dev/null +++ b/app/Templates/config_board.php @@ -0,0 +1,29 @@ +<div class="page-header"> + <h2><?= t('Board settings') ?></h2> +</div> +<section> +<form method="post" action="<?= Helper\u('config', 'board') ?>" autocomplete="off"> + + <?= Helper\form_csrf() ?> + + <?= Helper\form_label(t('Task highlight period'), 'board_highlight_period') ?> + <?= Helper\form_number('board_highlight_period', $values, $errors) ?><br/> + <p class="form-help"><?= t('Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)') ?></p> + + <?= Helper\form_label(t('Refresh interval for public board'), 'board_public_refresh_interval') ?> + <?= Helper\form_number('board_public_refresh_interval', $values, $errors) ?><br/> + <p class="form-help"><?= t('Frequency in second (60 seconds by default)') ?></p> + + <?= Helper\form_label(t('Refresh interval for private board'), 'board_private_refresh_interval') ?> + <?= Helper\form_number('board_private_refresh_interval', $values, $errors) ?><br/> + <p class="form-help"><?= t('Frequency in second (0 to disable this feature, 10 seconds by default)') ?></p> + + <?= Helper\form_label(t('Default columns for new projects (Comma-separated)'), 'board_columns') ?> + <?= Helper\form_text('board_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> +</form> +</section>
\ No newline at end of file diff --git a/app/Templates/config_index.php b/app/Templates/config_index.php deleted file mode 100644 index 2d9ce116..00000000 --- a/app/Templates/config_index.php +++ /dev/null @@ -1,71 +0,0 @@ -<section id="main"> - - <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> - </form> - </section> - - <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> -</section> diff --git a/app/Templates/config_layout.php b/app/Templates/config_layout.php new file mode 100644 index 00000000..3aacb9b7 --- /dev/null +++ b/app/Templates/config_layout.php @@ -0,0 +1,13 @@ +<section id="main"> + <div class="page-header"> + <h2><?= t('Settings') ?></h2> + </div> + <section class="config-show" id="config-section"> + + <?= Helper\template('config_sidebar') ?> + + <div class="config-show-main"> + <?= $config_content_for_layout ?> + </div> + </section> +</section>
\ No newline at end of file diff --git a/app/Templates/config_sidebar.php b/app/Templates/config_sidebar.php new file mode 100644 index 00000000..d96159b8 --- /dev/null +++ b/app/Templates/config_sidebar.php @@ -0,0 +1,22 @@ +<div class="config-show-sidebar"> + <h2><?= t('Actions') ?></h2> + <div class="config-show-actions"> + <ul> + <li> + <?= Helper\a(t('About'), 'config', 'index') ?> + </li> + <li> + <?= Helper\a(t('Application settings'), 'config', 'application') ?> + </li> + <li> + <?= Helper\a(t('Board settings'), 'config', 'board') ?> + </li> + <li> + <?= Helper\a(t('Webhooks'), 'config', 'webhook') ?> + </li> + <li> + <?= Helper\a(t('API'), 'config', 'api') ?> + </li> + </ul> + </div> +</div>
\ No newline at end of file diff --git a/app/Templates/config_webhook.php b/app/Templates/config_webhook.php new file mode 100644 index 00000000..052a2a99 --- /dev/null +++ b/app/Templates/config_webhook.php @@ -0,0 +1,38 @@ +<div class="page-header"> + <h2><?= t('Webhook settings') ?></h2> +</div> +<section> +<form method="post" action="<?= Helper\u('config', 'webhook') ?>" autocomplete="off"> + + <?= Helper\form_csrf() ?> + + <?= Helper\form_label(t('Webhook URL for task creation'), 'webhook_url_task_creation') ?> + <?= Helper\form_text('webhook_url_task_creation', $values, $errors) ?><br/> + + <?= Helper\form_label(t('Webhook URL for task modification'), 'webhook_url_task_modification') ?> + <?= Helper\form_text('webhook_url_task_modification', $values, $errors) ?><br/> + + <div class="form-actions"> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + </div> +</form> +</section> + +<div class="page-header"> + <h2><?= t('URL and token') ?></h2> +</div> +<section class="listing"> + <ul> + <li> + <?= t('Webhook token:') ?> + <strong><?= Helper\escape($values['webhook_token']) ?></strong> + </li> + <li> + <?= t('URL for task creation:') ?> + <input type="text" readonly="readonly" value="<?= Helper\get_current_base_url().Helper\u('webhook', 'task', array('token' => $values['webhook_token'])) ?>"> + </li> + <li> + <?= Helper\a(t('Reset token'), 'config', 'token', array('type' => 'webhook'), true) ?> + </li> + </ul> +</section>
\ No newline at end of file diff --git a/app/Templates/layout.php b/app/Templates/layout.php index e0158dfb..434c5aca 100644 --- a/app/Templates/layout.php +++ b/app/Templates/layout.php @@ -6,8 +6,8 @@ <meta name="mobile-web-app-capable" content="yes"> <meta name="robots" content="noindex,nofollow"> - <?php if (isset($auto_refresh)): ?> - <meta http-equiv="refresh" content="<?= BOARD_PUBLIC_CHECK_INTERVAL ?>" > + <?php if (isset($board_public_refresh_interval)): ?> + <meta http-equiv="refresh" content="<?= $board_public_refresh_interval ?>" > <?php endif ?> <?php if (! isset($not_editable)): ?> diff --git a/app/Templates/notification_comment_creation.php b/app/Templates/notification_comment_creation.php index fac6ea25..5b334d76 100644 --- a/app/Templates/notification_comment_creation.php +++ b/app/Templates/notification_comment_creation.php @@ -4,4 +4,4 @@ <?= Helper\markdown($comment['comment']) ?> -<?= Helper\template('notification_footer', array('task' => $task)) ?>
\ No newline at end of file +<?= Helper\template('notification_footer', array('task' => $task, 'application_url' => $application_url)) ?>
\ No newline at end of file diff --git a/app/Templates/notification_comment_update.php b/app/Templates/notification_comment_update.php index 92c06ffb..04aafb85 100644 --- a/app/Templates/notification_comment_update.php +++ b/app/Templates/notification_comment_update.php @@ -4,4 +4,4 @@ <?= Helper\markdown($comment['comment']) ?> -<?= Helper\template('notification_footer', array('task' => $task)) ?>
\ No newline at end of file +<?= Helper\template('notification_footer', array('task' => $task, 'application_url' => $application_url)) ?>
\ No newline at end of file diff --git a/app/Templates/notification_file_creation.php b/app/Templates/notification_file_creation.php index 50f4a2d5..d8636820 100644 --- a/app/Templates/notification_file_creation.php +++ b/app/Templates/notification_file_creation.php @@ -2,4 +2,4 @@ <h3><?= t('New attachment added "%s"', $file['name']) ?></h3> -<?= Helper\template('notification_footer', array('task' => $task)) ?>
\ No newline at end of file +<?= Helper\template('notification_footer', array('task' => $task, 'application_url' => $application_url)) ?>
\ No newline at end of file diff --git a/app/Templates/notification_footer.php b/app/Templates/notification_footer.php index f0c9c0d4..533621f4 100644 --- a/app/Templates/notification_footer.php +++ b/app/Templates/notification_footer.php @@ -1,6 +1,6 @@ <hr/> Kanboard -<?php if (defined('KANBOARD_URL')): ?> - - <a href="<?= KANBOARD_URL.'?controller=task&action=show&task_id='.$task['id'] ?>"><?= t('view the task on Kanboard') ?></a>. +<?php if ($application_url): ?> + - <a href="<?= $application_url.'?controller=task&action=show&task_id='.$task['id'] ?>"><?= t('view the task on Kanboard') ?></a>. <?php endif ?> diff --git a/app/Templates/notification_subtask_creation.php b/app/Templates/notification_subtask_creation.php index c382355c..2ddfc649 100644 --- a/app/Templates/notification_subtask_creation.php +++ b/app/Templates/notification_subtask_creation.php @@ -14,4 +14,4 @@ </li> </ul> -<?= Helper\template('notification_footer', array('task' => $task)) ?>
\ No newline at end of file +<?= Helper\template('notification_footer', array('task' => $task, 'application_url' => $application_url)) ?>
\ No newline at end of file diff --git a/app/Templates/notification_subtask_update.php b/app/Templates/notification_subtask_update.php index ad6dfdc9..999edbf9 100644 --- a/app/Templates/notification_subtask_update.php +++ b/app/Templates/notification_subtask_update.php @@ -18,4 +18,4 @@ </li> </ul> -<?= Helper\template('notification_footer', array('task' => $task)) ?>
\ No newline at end of file +<?= Helper\template('notification_footer', array('task' => $task, 'application_url' => $application_url)) ?>
\ No newline at end of file diff --git a/app/Templates/notification_task_assignee_change.php b/app/Templates/notification_task_assignee_change.php index 65194f1e..d23f769e 100644 --- a/app/Templates/notification_task_assignee_change.php +++ b/app/Templates/notification_task_assignee_change.php @@ -17,4 +17,4 @@ <?= Helper\markdown($task['description']) ?: t('There is no description.') ?> <?php endif ?> -<?= Helper\template('notification_footer', array('task' => $task)) ?>
\ No newline at end of file +<?= Helper\template('notification_footer', array('task' => $task, 'application_url' => $application_url)) ?>
\ No newline at end of file diff --git a/app/Templates/notification_task_close.php b/app/Templates/notification_task_close.php index d9b2dce6..d56e71bb 100644 --- a/app/Templates/notification_task_close.php +++ b/app/Templates/notification_task_close.php @@ -2,4 +2,4 @@ <p><?= t('The task #%d have been closed.', $task['id']) ?></p> -<?= Helper\template('notification_footer', array('task' => $task)) ?>
\ No newline at end of file +<?= Helper\template('notification_footer', array('task' => $task, 'application_url' => $application_url)) ?>
\ No newline at end of file diff --git a/app/Templates/notification_task_creation.php b/app/Templates/notification_task_creation.php index 43b3b291..1b555096 100644 --- a/app/Templates/notification_task_creation.php +++ b/app/Templates/notification_task_creation.php @@ -40,4 +40,4 @@ <?= Helper\markdown($task['description']) ?> <?php endif ?> -<?= Helper\template('notification_footer', array('task' => $task)) ?>
\ No newline at end of file +<?= Helper\template('notification_footer', array('task' => $task, 'application_url' => $application_url)) ?>
\ No newline at end of file diff --git a/app/Templates/notification_task_due.php b/app/Templates/notification_task_due.php index 25ecc9f1..ae02f64e 100644 --- a/app/Templates/notification_task_due.php +++ b/app/Templates/notification_task_due.php @@ -12,4 +12,4 @@ <?php endforeach ?> </ul> -<?= Helper\template('notification_footer', array('task' => $task)) ?>
\ No newline at end of file +<?= Helper\template('notification_footer', array('task' => $task, 'application_url' => $application_url)) ?>
\ No newline at end of file diff --git a/app/Templates/notification_task_move_column.php b/app/Templates/notification_task_move_column.php index 026db776..c3f94df7 100644 --- a/app/Templates/notification_task_move_column.php +++ b/app/Templates/notification_task_move_column.php @@ -8,4 +8,4 @@ <li><?= t('Task position:').' '.Helper\escape($task['position']) ?></li> </ul> -<?= Helper\template('notification_footer', array('task' => $task)) ?>
\ No newline at end of file +<?= Helper\template('notification_footer', array('task' => $task, 'application_url' => $application_url)) ?>
\ No newline at end of file diff --git a/app/Templates/notification_task_move_position.php b/app/Templates/notification_task_move_position.php index 026db776..c3f94df7 100644 --- a/app/Templates/notification_task_move_position.php +++ b/app/Templates/notification_task_move_position.php @@ -8,4 +8,4 @@ <li><?= t('Task position:').' '.Helper\escape($task['position']) ?></li> </ul> -<?= Helper\template('notification_footer', array('task' => $task)) ?>
\ No newline at end of file +<?= Helper\template('notification_footer', array('task' => $task, 'application_url' => $application_url)) ?>
\ No newline at end of file diff --git a/app/Templates/notification_task_open.php b/app/Templates/notification_task_open.php index 7bf674cc..5d9f7d5b 100644 --- a/app/Templates/notification_task_open.php +++ b/app/Templates/notification_task_open.php @@ -2,4 +2,4 @@ <p><?= t('The task #%d have been opened.', $task['id']) ?></p> -<?= Helper\template('notification_footer', array('task' => $task)) ?>
\ No newline at end of file +<?= Helper\template('notification_footer', array('task' => $task, 'application_url' => $application_url)) ?>
\ No newline at end of file diff --git a/app/Templates/notification_task_update.php b/app/Templates/notification_task_update.php index 9fd61b57..b3c07911 100644 --- a/app/Templates/notification_task_update.php +++ b/app/Templates/notification_task_update.php @@ -40,4 +40,4 @@ <?= Helper\markdown($task['description']) ?: t('There is no description.') ?> <?php endif ?> -<?= Helper\template('notification_footer', array('task' => $task)) ?>
\ No newline at end of file +<?= Helper\template('notification_footer', array('task' => $task, 'application_url' => $application_url)) ?>
\ No newline at end of file diff --git a/app/Templates/project_share.php b/app/Templates/project_share.php index 6cfd85f6..f06d7671 100644 --- a/app/Templates/project_share.php +++ b/app/Templates/project_share.php @@ -4,7 +4,7 @@ <?php if ($project['is_public']): ?> - <div class="settings"> + <div class="listing"> <ul class="no-bullet"> <li><strong><i class="fa fa-share-alt"></i> <a href="?controller=board&action=readonly&token=<?= $project['token'] ?>" target="_blank"><?= t('Public link') ?></a></strong></li> <li><strong><i class="fa fa-rss-square"></i> <a href="?controller=project&action=feed&token=<?= $project['token'] ?>" target="_blank"><?= t('RSS feed') ?></a></strong></li> diff --git a/app/Templates/project_show.php b/app/Templates/project_show.php index 98ffb581..96b9e404 100644 --- a/app/Templates/project_show.php +++ b/app/Templates/project_show.php @@ -1,7 +1,7 @@ <div class="page-header"> <h2><?= t('Summary') ?></h2> </div> -<ul class="settings"> +<ul class="listing"> <li><strong><?= $project['is_active'] ? t('Active') : t('Inactive') ?></strong></li> <?php if ($project['is_public']): ?> diff --git a/app/Templates/user_external.php b/app/Templates/user_external.php index a67d886e..676b2c73 100644 --- a/app/Templates/user_external.php +++ b/app/Templates/user_external.php @@ -5,7 +5,7 @@ <?php if (GOOGLE_AUTH): ?> <h3><i class="fa fa-google"></i> <?= t('Google Account') ?></h3> - <p class="settings"> + <p class="listing"> <?php if (Helper\is_current_user($user['id'])): ?> <?php if (empty($user['google_id'])): ?> <a href="?controller=user&action=google<?= Helper\param_csrf() ?>"><?= t('Link my Google Account') ?></a> @@ -21,7 +21,7 @@ <?php if (GITHUB_AUTH): ?> <h3><i class="fa fa-github"></i> <?= t('Github Account') ?></h3> - <p class="settings"> + <p class="listing"> <?php if (Helper\is_current_user($user['id'])): ?> <?php if (empty($user['github_id'])): ?> <a href="?controller=user&action=gitHub<?= Helper\param_csrf() ?>"><?= t('Link my GitHub Account') ?></a> diff --git a/app/Templates/user_show.php b/app/Templates/user_show.php index 5d42d3cf..1c843751 100644 --- a/app/Templates/user_show.php +++ b/app/Templates/user_show.php @@ -1,7 +1,7 @@ <div class="page-header"> <h2><?= t('Summary') ?></h2> </div> -<ul class="settings"> +<ul class="listing"> <li><?= t('Username:') ?> <strong><?= Helper\escape($user['username']) ?></strong></li> <li><?= t('Name:') ?> <strong><?= Helper\escape($user['name']) ?></strong></li> <li><?= t('Email:') ?> <strong><?= Helper\escape($user['email']) ?></strong></li> diff --git a/app/constants.php b/app/constants.php index 777e6f0e..93075892 100644 --- a/app/constants.php +++ b/app/constants.php @@ -1,14 +1,5 @@ <?php -// Board refresh frequency in seconds for the public board view -defined('BOARD_PUBLIC_CHECK_INTERVAL') or define('BOARD_PUBLIC_CHECK_INTERVAL', 60); - -// Board refresh frequency in seconds (the value 0 disable this feature) -defined('BOARD_CHECK_INTERVAL') or define('BOARD_CHECK_INTERVAL', 10); - -// Period (in second) to consider a task was modified recently -defined('RECENT_TASK_PERIOD') or define('RECENT_TASK_PERIOD', 48*60*60); - // Custom session save path defined('SESSION_SAVE_PATH') or define('SESSION_SAVE_PATH', ''); diff --git a/app/helpers.php b/app/helpers.php index 5cb7b82e..1ab8586a 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -556,16 +556,37 @@ function form_numeric($name, $values = array(), array $errors = array(), array $ * @param string $controller Controller name * @param string $action Action name * @param array $params Url parameters + * @param boolean $csrf Add a CSRF token * @param string $class CSS class attribute * @return string */ -function a($label, $controller, $action, array $params = array(), $css = '') +function a($label, $controller, $action, array $params = array(), $csrf = false, $class = '') { - $html = '<a href="?controller='.$controller.'&action='.$action; + return '<a href="'.u($controller, $action, $params, $csrf).'" class="'.$class.'"/>'.$label.'</a>'; +} + +/** + * URL + * + * a('link', 'task', 'show', array('task_id' => $task_id)) + * + * @param string $controller Controller name + * @param string $action Action name + * @param array $params Url parameters + * @param boolean $csrf Add a CSRF token + * @return string + */ +function u($controller, $action, array $params = array(), $csrf = false) +{ + $html = '?controller='.$controller.'&action='.$action; + + if ($csrf) { + $params['csrf_token'] = Security::getCSRFToken(); + } foreach ($params as $key => $value) { $html .= '&'.$key.'='.$value; } - return '" class="'.$class.'"/>'.$label.'</a>'; -} + return $html; +}
\ No newline at end of file diff --git a/assets/css/app.css b/assets/css/app.css index ff859115..7fff0ead 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -724,18 +724,21 @@ a.task-board-nobody { } /* task view */ +.config-show, .user-show, .project-show, .task-show { position: relative; } +.config-show-main, .user-show-main, .project-show-main, .task-show-main { margin-left: 330px; } +.config-show-sidebar, .user-show-sidebar, .project-show-sidebar, .task-show-sidebar { @@ -750,6 +753,7 @@ a.task-board-nobody { border-radius: 5px; } +.config-show-sidebar li, .user-show-sidebar li, .project-show-sidebar li, .task-show-sidebar li { @@ -1004,9 +1008,8 @@ tr td.task-orange, border-color: rgb(255, 172, 98); } -/* config page */ -.listing, -.settings { +/* listing block */ +.listing { border-radius: 4px; padding: 8px 35px 8px 14px; margin-bottom: 20px; @@ -1015,8 +1018,7 @@ tr td.task-orange, background-color: #f0f0f0; } -.listing li, -.settings li { +.listing li { list-style-type: square; margin-left: 20px; margin-bottom: 3px; diff --git a/config.default.php b/config.default.php index f27672e6..e2b5cdf9 100644 --- a/config.default.php +++ b/config.default.php @@ -1,8 +1,5 @@ <?php -// Your Kanboard base URL, example: http://demo.kanboard.net/ (used by email notifications or CLI scripts) -define('KANBOARD_URL', ''); - // E-mail address for the "From" header (notifications) define('MAIL_FROM', 'notifications@kanboard.net'); @@ -19,15 +16,6 @@ define('MAIL_SMTP_ENCRYPTION', null); // Valid values are "null", "ssl" or "tls" // Sendmail command to use when the transport is "sendmail" define('MAIL_SENDMAIL_COMMAND', '/usr/sbin/sendmail -bs'); -// Auto-refresh frequency in seconds for the public board view (60 seconds by default) -define('BOARD_PUBLIC_CHECK_INTERVAL', 60); - -// Board refresh frequency in seconds (the value 0 disable this feature, 10 seconds by default) -define('BOARD_CHECK_INTERVAL', 10); - -// Period (in second) to consider a task was modified recently (0 to disable, 2 days by default) -define('RECENT_TASK_PERIOD', 48*60*60); - // Database driver: sqlite, mysql or postgres (sqlite by default) define('DB_DRIVER', 'sqlite'); diff --git a/jsonrpc.php b/jsonrpc.php index 0ddb1e14..f577ef82 100644 --- a/jsonrpc.php +++ b/jsonrpc.php @@ -2,7 +2,6 @@ require __DIR__.'/app/common.php'; -use Core\Translator; use JsonRPC\Server; use Model\Project; use Model\ProjectPermission; @@ -19,6 +18,9 @@ use Model\Webhook; use Model\Notification; $config = new Config($registry); +$config->setupTranslations(); +$config->setupTimezone(); + $project = new Project($registry); $projectPermission = new ProjectPermission($registry); $task = new Task($registry); @@ -37,14 +39,9 @@ $project->attachEvents(); $webhook->attachEvents(); $notification->attachEvents(); -// Load translations -$language = $config->get('language', 'en_US'); -if ($language !== 'en_US') Translator::load($language); - $server = new Server; $server->authentication(array('jsonrpc' => $config->get('api_token'))); - /** * Project procedures */ @@ -5,20 +5,14 @@ require __DIR__.'/app/common.php'; use Core\Cli; use Core\Tool; -use Core\Translator; use Model\Config; use Model\Task; use Model\TaskExport; use Model\Notification; $config = new Config($registry); - -// Load translations -$language = $config->get('language', 'en_US'); -if ($language !== 'en_US') Translator::load($language); - -// Set timezone -date_default_timezone_set($config->get('timezone', 'UTC')); +$config->setupTranslations(); +$config->setupTimezone(); // Setup CLI $cli = new Cli; diff --git a/tests/functionals.mysql.xml b/tests/functionals.mysql.xml index f667cafa..aa5b6ec1 100644 --- a/tests/functionals.mysql.xml +++ b/tests/functionals.mysql.xml @@ -5,7 +5,7 @@ </testsuite> </testsuites> <php> - <const name="API_URL" value="http://localhost:8000/jsonrpc.php" /> + <const name="API_URL" value="http://localhost:8080/jsonrpc.php" /> <const name="API_KEY" value="19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929" /> <const name="DB_DRIVER" value="mysql" /> <const name="DB_NAME" value="kanboard" /> diff --git a/tests/functionals.postgres.xml b/tests/functionals.postgres.xml index 38904d1a..ecfe72ef 100644 --- a/tests/functionals.postgres.xml +++ b/tests/functionals.postgres.xml @@ -5,7 +5,7 @@ </testsuite> </testsuites> <php> - <const name="API_URL" value="http://localhost:8000/jsonrpc.php" /> + <const name="API_URL" value="http://localhost:8080/jsonrpc.php" /> <const name="API_KEY" value="19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929" /> <const name="DB_DRIVER" value="postgres" /> <const name="DB_NAME" value="kanboard" /> diff --git a/tests/functionals.sqlite.xml b/tests/functionals.sqlite.xml index bf5d4117..62aa94c3 100644 --- a/tests/functionals.sqlite.xml +++ b/tests/functionals.sqlite.xml @@ -5,7 +5,7 @@ </testsuite> </testsuites> <php> - <const name="API_URL" value="http://localhost:8000/jsonrpc.php" /> + <const name="API_URL" value="http://localhost:8080/jsonrpc.php" /> <const name="API_KEY" value="19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929" /> <const name="DB_DRIVER" value="sqlite" /> <const name="DB_FILENAME" value="data/db.sqlite" /> diff --git a/tests/functionals/ApiTest.php b/tests/functionals/ApiTest.php index 13c25a7b..272e9b35 100644 --- a/tests/functionals/ApiTest.php +++ b/tests/functionals/ApiTest.php @@ -30,7 +30,7 @@ class Api extends PHPUnit_Framework_TestCase setup_db(); - $pdo->exec("UPDATE config SET api_token='".API_KEY."'"); + $pdo->exec("UPDATE settings SET value='".API_KEY."' WHERE option='api_token'"); $pdo = null; } diff --git a/tests/units/BoardTest.php b/tests/units/BoardTest.php index 21d65543..cdf23a82 100644 --- a/tests/units/BoardTest.php +++ b/tests/units/BoardTest.php @@ -27,8 +27,10 @@ class BoardTest extends Base $this->assertEquals('Done', $columns[4]); // Custom columns: spaces should be trimed and no empty columns + $input = ' column #1 , column #2, '; - $this->assertTrue($c->save(array('default_columns' => ' column #1 , column #2, '))); + $this->assertTrue($c->save(array('board_columns' => $input))); + $this->assertEquals($input, $c->get('board_columns')); $this->assertEquals(2, $p->create(array('name' => 'UnitTest2'))); $columns = $b->getColumnsList(2); diff --git a/tests/units/ConfigTest.php b/tests/units/ConfigTest.php index 7298936e..4992092b 100644 --- a/tests/units/ConfigTest.php +++ b/tests/units/ConfigTest.php @@ -10,14 +10,14 @@ class ConfigTest extends Base { $c = new Config($this->registry); - $this->assertEquals('en_US', $c->get('language')); - $this->assertEquals('UTC', $c->get('timezone')); + $this->assertEquals('en_US', $c->get('application_language')); + $this->assertEquals('UTC', $c->get('application_timezone')); - $this->assertEmpty($c->get('webhooks_url_task_modification')); - $this->assertEmpty($c->get('webhooks_url_task_creation')); - $this->assertEmpty($c->get('default_columns')); + $this->assertEmpty($c->get('webhook_url_task_modification')); + $this->assertEmpty($c->get('webhook_url_task_creation')); + $this->assertEmpty($c->get('board_columns')); - $this->assertNotEmpty($c->get('webhooks_token')); + $this->assertNotEmpty($c->get('webhook_token')); $this->assertNotEmpty($c->get('api_token')); } @@ -25,8 +25,8 @@ class ConfigTest extends Base { $c = new Config($this->registry); - $this->assertEquals('', $c->get('default_columns')); - $this->assertEquals('test', $c->get('default_columns', 'test')); - $this->assertEquals(0, $c->get('default_columns', 0)); + $this->assertEquals('', $c->get('board_columns')); + $this->assertEquals('test', $c->get('board_columns', 'test')); + $this->assertEquals(0, $c->get('board_columns', 0)); } } |