diff options
-rw-r--r-- | controllers/base.php | 1 | ||||
-rw-r--r-- | controllers/board.php | 28 | ||||
-rw-r--r-- | index.php | 5 | ||||
-rw-r--r-- | locales/fr_FR/translations.php | 1 | ||||
-rw-r--r-- | locales/pl_PL/translations.php | 4 | ||||
-rw-r--r-- | models/base.php | 15 | ||||
-rw-r--r-- | models/config.php | 10 | ||||
-rw-r--r-- | models/project.php | 7 | ||||
-rw-r--r-- | models/schema.php | 22 | ||||
-rw-r--r-- | templates/board_public.php | 41 | ||||
-rw-r--r-- | templates/layout.php | 3 | ||||
-rw-r--r-- | templates/project_index.php | 3 |
12 files changed, 121 insertions, 19 deletions
diff --git a/controllers/base.php b/controllers/base.php index 5c48d927..c248ede1 100644 --- a/controllers/base.php +++ b/controllers/base.php @@ -45,6 +45,7 @@ abstract class Base $public = array( 'user' => array('login', 'check'), 'task' => array('add'), + 'board' => array('readonly'), ); if (isset($public[$controller])) { diff --git a/controllers/board.php b/controllers/board.php index ec32e8a0..e8b161e7 100644 --- a/controllers/board.php +++ b/controllers/board.php @@ -4,7 +4,29 @@ namespace Controller; class Board extends Base { - // Display current board + // Display the public version of a board + // Access checked by a simple token, no user login, read only, auto-refresh + public function readonly() + { + $token = $this->request->getStringParam('token'); + $project = $this->project->getByToken($token); + + // Token verification + if (! $project) { + $this->response->text('Not Authorized', 401); + } + + // Display the board with a specific layout + $this->response->html($this->template->layout('board_public', array( + 'project' => $project, + 'columns' => $this->board->get($project['id']), + 'title' => $project['name'], + 'no_layout' => true, + 'auto_refresh' => true, + ))); + } + + // Display the default user project or the first project public function index() { $projects = $this->project->getListByStatus(\Model\Project::ACTIVE); @@ -30,7 +52,7 @@ class Board extends Base ))); } - // Show a board + // Show a board for a given project public function show() { $projects = $this->project->getListByStatus(\Model\Project::ACTIVE); @@ -175,7 +197,7 @@ class Board extends Base $this->response->redirect('?controller=board&action=edit&project_id='.$column['project_id']); } - // Save the board (Ajax request made by drag and drop) + // Save the board (Ajax request made by the drag and drop) public function save() { $this->response->json(array( @@ -4,5 +4,10 @@ require __DIR__.'/check_setup.php'; require __DIR__.'/controllers/base.php'; require __DIR__.'/lib/router.php'; +if (file_exists('config.php')) require 'config.php'; + +// Auto-refresh frequency in seconds for the public board view +defined('AUTO_REFRESH_DURATION') or define('AUTO_REFRESH_DURATION', 60); + $router = new Router; $router->execute(); diff --git a/locales/fr_FR/translations.php b/locales/fr_FR/translations.php index 245825ad..7d021fcc 100644 --- a/locales/fr_FR/translations.php +++ b/locales/fr_FR/translations.php @@ -179,4 +179,5 @@ return array( 'Completed tasks for "%s"' => 'Tâches terminées pour "%s"', '%d closed tasks' => '%d tâches terminées', 'no task for this project' => 'aucune tâche pour ce projet', + 'Public link' => 'Accès public', ); diff --git a/locales/pl_PL/translations.php b/locales/pl_PL/translations.php index 32e9e3c8..c5e993f5 100644 --- a/locales/pl_PL/translations.php +++ b/locales/pl_PL/translations.php @@ -181,5 +181,7 @@ return array( 'List of projects' => '', 'Completed tasks for "%s"' => '', '%d closed tasks' => '', - 'no task for this project' => '',*/ + 'no task for this project' => '', + 'Public link' => '', + */ ); diff --git a/models/base.php b/models/base.php index 981c7839..3c071623 100644 --- a/models/base.php +++ b/models/base.php @@ -17,7 +17,7 @@ require __DIR__.'/schema.php'; abstract class Base { const APP_VERSION = 'master'; - const DB_VERSION = 2; + const DB_VERSION = 3; const DB_FILENAME = 'data/db.sqlite'; private static $dbInstance = null; @@ -46,4 +46,17 @@ abstract class Base die('Unable to migrate database schema!'); } } + + // Generate a random token from /dev/urandom or with uniqid() + public static function generateToken() + { + if (ini_get('open_basedir') === '') { + $token = file_get_contents('/dev/urandom', false, null, 0, 30); + } + else { + $token = uniqid(mt_rand(), true); + } + + return hash('crc32b', $token); + } } diff --git a/models/config.php b/models/config.php index fe4f6c99..f4d34986 100644 --- a/models/config.php +++ b/models/config.php @@ -66,16 +66,6 @@ class Config extends Base ); } - public static function generateToken() - { - if (ini_get('open_basedir') === '') { - return substr(base64_encode(file_get_contents('/dev/urandom', false, null, 0, 20)), 0, 15); - } - else { - return substr(base64_encode(uniqid(mt_rand(), true)), 0, 20); - } - } - public function optimizeDatabase() { $this->db->getconnection()->exec("VACUUM"); diff --git a/models/project.php b/models/project.php index 10d7c572..cb96dccd 100644 --- a/models/project.php +++ b/models/project.php @@ -16,6 +16,11 @@ class Project extends Base return $this->db->table(self::TABLE)->eq('id', $project_id)->findOne(); } + public function getByToken($token) + { + return $this->db->table(self::TABLE)->eq('token', $token)->findOne(); + } + public function getFirst() { return $this->db->table(self::TABLE)->findOne(); @@ -92,12 +97,12 @@ class Project extends Base { $this->db->startTransaction(); + $values['token'] = self::generateToken(); $this->db->table(self::TABLE)->save($values); $project_id = $this->db->getConnection()->getLastId(); $boardModel = new \Model\Board; - $boardModel->create($project_id, array( t('Backlog'), t('Ready'), diff --git a/models/schema.php b/models/schema.php index 9ccb500f..84926d73 100644 --- a/models/schema.php +++ b/models/schema.php @@ -2,11 +2,27 @@ namespace Schema; +function version_3($pdo) +{ + $pdo->exec('ALTER TABLE projects ADD column token TEXT'); + + // For each existing project, assign a different token + $rq = $pdo->prepare("SELECT id FROM projects WHERE token IS NULL"); + $rq->execute(); + $results = $rq->fetchAll(\PDO::FETCH_ASSOC); + + if ($results !== false) { + + foreach ($results as &$result) { + $rq = $pdo->prepare('UPDATE projects SET token=? WHERE id=?'); + $rq->execute(array(\Model\Base::generateToken(), $result['id'])); + } + } +} + function version_2($pdo) { $pdo->exec('ALTER TABLE tasks ADD column date_completed INTEGER'); - - // For all existing completed tasks, set the date of creation as a date of completion $pdo->exec('UPDATE tasks SET date_completed=date_creation WHERE is_active=0'); } @@ -74,6 +90,6 @@ function version_1($pdo) $pdo->exec(" INSERT INTO config (language, webhooks_token) - VALUES ('en_US', '".\Model\Config::generateToken()."') + VALUES ('en_US', '".\Model\Base::generateToken()."') "); } diff --git a/templates/board_public.php b/templates/board_public.php new file mode 100644 index 00000000..d01bae0f --- /dev/null +++ b/templates/board_public.php @@ -0,0 +1,41 @@ +<section id="main"> + + <table id="board"> + <tr> + <?php $column_with = round(100 / count($columns), 2); ?> + <?php foreach ($columns as $column): ?> + <th width="<?= $column_with ?>%"> + <?= Helper\escape($column['title']) ?> + </th> + <?php endforeach ?> + </tr> + <tr> + <?php foreach ($columns as $column): ?> + <td class="column"> + <?php foreach ($column['tasks'] as $task): ?> + <div class="draggable-item"> + <div class="task task-<?= $task['color_id'] ?>"> + + #<?= $task['id'] ?> - + + <span class="task-user"> + <?php if (! empty($task['owner_id'])): ?> + <?= t('Assigned to %s', $task['username']) ?> + <?php else: ?> + <span class="task-nobody"><?= t('No body assigned') ?></span> + <?php endif ?> + </span> + + <div class="task-title"> + <?= Helper\escape($task['title']) ?> + </div> + + </div> + </div> + <?php endforeach ?> + </td> + <?php endforeach ?> + </tr> + </table> + +</section>
\ No newline at end of file diff --git a/templates/layout.php b/templates/layout.php index 1c89f2ab..0791b910 100644 --- a/templates/layout.php +++ b/templates/layout.php @@ -10,6 +10,9 @@ <link rel="apple-touch-icon" sizes="114x114" href="assets/img/touch-icon-iphone-retina.png"> <link rel="apple-touch-icon" sizes="144x144" href="assets/img/touch-icon-ipad-retina.png"> <title><?= isset($title) ? Helper\escape($title) : 'Kanboard' ?></title> + <?php if (isset($auto_refresh)): ?> + <meta http-equiv="refresh" content="<?= AUTO_REFRESH_DURATION ?>" > + <?php endif ?> </head> <body> <?php if (isset($no_layout)): ?> diff --git a/templates/project_index.php b/templates/project_index.php index d144e41f..8c75ef0b 100644 --- a/templates/project_index.php +++ b/templates/project_index.php @@ -75,6 +75,9 @@ <li> <a href="?controller=project&action=confirm&project_id=<?= $project['id'] ?>"><?= t('Remove') ?></a> </li> + <li> + <a href="?controller=board&action=readonly&token=<?= $project['token'] ?>" target="_blank"><?= t('Public link') ?></a> + </li> </ul> </td> <?php endif ?> |