diff --git a/app/Core/Listener.php b/app/Core/Listener.php index b8bdd6800..0df641ba6 100644 --- a/app/Core/Listener.php +++ b/app/Core/Listener.php @@ -11,6 +11,10 @@ namespace Core; interface Listener { /** + * Execute the listener + * + * @access public + * @param array $data Event data * @return boolean */ public function execute(array $data); diff --git a/app/Core/Request.php b/app/Core/Request.php index df8ea41ac..7e9f24acc 100644 --- a/app/Core/Request.php +++ b/app/Core/Request.php @@ -2,39 +2,92 @@ namespace Core; +/** + * Request class + * + * @package core + * @author Frederic Guillot + */ class Request { + /** + * Get URL string parameter + * + * @access public + * @param string $name Parameter name + * @param string $default_value Default value + * @return string + */ public function getStringParam($name, $default_value = '') { return isset($_GET[$name]) ? $_GET[$name] : $default_value; } + /** + * Get URL integer parameter + * + * @access public + * @param string $name Parameter name + * @param integer $default_value Default value + * @return integer + */ public function getIntegerParam($name, $default_value = 0) { return isset($_GET[$name]) && ctype_digit($_GET[$name]) ? (int) $_GET[$name] : $default_value; } + /** + * Get a form value + * + * @access public + * @param string $name Form field name + * @return string|null + */ public function getValue($name) { $values = $this->getValues(); return isset($values[$name]) ? $values[$name] : null; } + /** + * Get form values or unserialized json request + * + * @access public + * @return array + */ public function getValues() { - if (! empty($_POST)) return $_POST; + if (! empty($_POST)) { + return $_POST; + } $result = json_decode($this->getBody(), true); - if ($result) return $result; + + if ($result) { + return $result; + } return array(); } + /** + * Get the raw body of the HTTP request + * + * @access public + * @return string + */ public function getBody() { return file_get_contents('php://input'); } + /** + * Get the content of an uploaded file + * + * @access public + * @param string $name Form file name + * @return string + */ public function getFileContent($name) { if (isset($_FILES[$name])) { @@ -44,11 +97,23 @@ class Request return ''; } + /** + * Return true if the HTTP request is sent with the POST method + * + * @access public + * @return bool + */ public function isPost() { return isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST'; } + /** + * Return true if the HTTP request is an Ajax request + * + * @access public + * @return bool + */ public function isAjax() { return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest'; diff --git a/app/Core/Response.php b/app/Core/Response.php index ee98c9ed2..87d2fa4a0 100644 --- a/app/Core/Response.php +++ b/app/Core/Response.php @@ -2,20 +2,41 @@ namespace Core; +/** + * Response class + * + * @package core + * @author Frederic Guillot + */ class Response { + /** + * Send a custom Content-Type header + * + * @access public + * @param string $mimetype Mime-type + */ public function contentType($mimetype) { header('Content-Type: '.$mimetype); } + /** + * Force the browser to download an attachment + * + * @access public + * @param string $filename File name + */ public function forceDownload($filename) { header('Content-Disposition: attachment; filename="'.$filename.'"'); } /** - * @param integer $status_code + * Send a custom HTTP status code + * + * @access public + * @param integer $status_code HTTP status code */ public function status($status_code) { @@ -23,12 +44,25 @@ class Response header($_SERVER['SERVER_PROTOCOL'].' '.$status_code); } + /** + * Redirect to another URL + * + * @access public + * @param string $url Redirection URL + */ public function redirect($url) { header('Location: '.$url); exit; } + /** + * Send a Json response + * + * @access public + * @param array $data Data to serialize in json + * @param integer $status_code HTTP status code + */ public function json(array $data, $status_code = 200) { $this->status($status_code); @@ -39,6 +73,13 @@ class Response exit; } + /** + * Send a text response + * + * @access public + * @param string $data Raw data + * @param integer $status_code HTTP status code + */ public function text($data, $status_code = 200) { $this->status($status_code); @@ -49,6 +90,13 @@ class Response exit; } + /** + * Send a HTML response + * + * @access public + * @param string $data Raw data + * @param integer $status_code HTTP status code + */ public function html($data, $status_code = 200) { $this->status($status_code); @@ -59,6 +107,13 @@ class Response exit; } + /** + * Send a XML response + * + * @access public + * @param string $data Raw data + * @param integer $status_code HTTP status code + */ public function xml($data, $status_code = 200) { $this->status($status_code); @@ -69,6 +124,13 @@ class Response exit; } + /** + * Send a javascript response + * + * @access public + * @param string $data Raw data + * @param integer $status_code HTTP status code + */ public function js($data, $status_code = 200) { $this->status($status_code); @@ -79,6 +141,13 @@ class Response exit; } + /** + * Send a binary response + * + * @access public + * @param string $data Raw data + * @param integer $status_code HTTP status code + */ public function binary($data, $status_code = 200) { $this->status($status_code); @@ -90,6 +159,12 @@ class Response exit; } + /** + * Send the security header: Content-Security-Policy + * + * @access public + * @param array $policies CSP rules + */ public function csp(array $policies = array()) { $policies['default-src'] = "'self'"; @@ -119,16 +194,31 @@ class Response header('Content-Security-Policy: '.$values); } + /** + * Send the security header: X-Content-Type-Options + * + * @access public + */ public function nosniff() { header('X-Content-Type-Options: nosniff'); } + /** + * Send the security header: X-XSS-Protection + * + * @access public + */ public function xss() { header('X-XSS-Protection: 1; mode=block'); } + /** + * Send the security header: Strict-Transport-Security (only if we use HTTPS) + * + * @access public + */ public function hsts() { if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') { @@ -136,6 +226,13 @@ class Response } } + /** + * Send the security header: X-Frame-Options (deny by default) + * + * @access public + * @param string $mode Frame option mode + * @param array $urls Allowed urls for the given mode + */ public function xframe($mode = 'DENY', array $urls = array()) { header('X-Frame-Options: '.$mode.' '.implode(' ', $urls)); diff --git a/app/Core/Router.php b/app/Core/Router.php index a7c9764c7..406109964 100644 --- a/app/Core/Router.php +++ b/app/Core/Router.php @@ -38,7 +38,7 @@ class Router * Constructor * * @access public - * @param Core\Registry $registry Registry instance + * @param Registry $registry Registry instance * @param string $controller Controller name * @param string $action Action name */ diff --git a/app/Core/Session.php b/app/Core/Session.php index 0c3ec2d95..6ce1bd402 100644 --- a/app/Core/Session.php +++ b/app/Core/Session.php @@ -2,13 +2,33 @@ namespace Core; +/** + * Session class + * + * @package core + * @author Frederic Guillot + */ class Session { + /** + * Sesion lifetime + * + * @var integer + */ const SESSION_LIFETIME = 86400; // 1 day + /** + * Open a session + * + * @access public + * @param string $base_path Cookie path + * @param string $save_path Custom session save path + */ public function open($base_path = '/', $save_path = '') { - if ($save_path !== '') session_save_path($save_path); + if ($save_path !== '') { + session_save_path($save_path); + } // HttpOnly and secure flags for session cookie session_set_cookie_params( @@ -39,16 +59,33 @@ class Session } } + /** + * Destroy the session + * + * @access public + */ public function close() { session_destroy(); } + /** + * Register a flash message (success notification) + * + * @access public + * @param string $message Message + */ public function flash($message) { $_SESSION['flash_message'] = $message; } + /** + * Register a flash error message (error notification) + * + * @access public + * @param string $message Message + */ public function flashError($message) { $_SESSION['flash_error_message'] = $message; diff --git a/app/Core/Translator.php b/app/Core/Translator.php index be0be66ad..015a76cb0 100644 --- a/app/Core/Translator.php +++ b/app/Core/Translator.php @@ -121,7 +121,7 @@ class Translator * Get an identifier from the translations or return the default * * @access public - * @param string $idendifier Locale identifier + * @param string $identifier Locale identifier * @param string $default Default value * @return string */ diff --git a/app/Model/Action.php b/app/Model/Action.php index d1b97ebc1..7cd917e9b 100644 --- a/app/Model/Action.php +++ b/app/Model/Action.php @@ -213,7 +213,7 @@ class Action extends Base * @access public * @param string $name Action class name * @param integer $project_id Project id - * @return mixed Action Instance + * @return \Core\Listener Action Instance * @throw LogicException */ public function load($name, $project_id) diff --git a/app/Model/Category.php b/app/Model/Category.php index 9be37f9d5..58eba403c 100644 --- a/app/Model/Category.php +++ b/app/Model/Category.php @@ -106,7 +106,7 @@ class Category extends Base * Validate category creation * * @access public - * @param array $array Form values + * @param array $values Form values * @return array $valid, $errors [0] = Success or not, [1] = List of errors */ public function validateCreation(array $values) @@ -128,7 +128,7 @@ class Category extends Base * Validate category modification * * @access public - * @param array $array Form values + * @param array $values Form values * @return array $valid, $errors [0] = Success or not, [1] = List of errors */ public function validateModification(array $values) diff --git a/app/Model/Config.php b/app/Model/Config.php index 994f0bc8e..9748f38ed 100644 --- a/app/Model/Config.php +++ b/app/Model/Config.php @@ -57,9 +57,9 @@ class Config extends Base * Get a config variable from the session or the database * * @access public - * @param string $name Parameter name + * @param string $name Parameter name * @param string $default_value Default value of the parameter - * @return mixed + * @return string */ public function get($name, $default_value = '') { diff --git a/app/Model/File.php b/app/Model/File.php index b7015acce..41ecfba19 100644 --- a/app/Model/File.php +++ b/app/Model/File.php @@ -2,9 +2,6 @@ namespace Model; -use SimpleValidator\Validator; -use SimpleValidator\Validators; - /** * File model * @@ -111,7 +108,7 @@ class File extends Base * @param integer $project_id Project id * @param integer $task_id Task id * @param string $filename Filename - * @return bool + * @return string */ public function generatePath($project_id, $task_id, $filename) { diff --git a/app/Model/Project.php b/app/Model/Project.php index 852948309..9fbb0806a 100644 --- a/app/Model/Project.php +++ b/app/Model/Project.php @@ -473,7 +473,7 @@ class Project extends Base * Validate project creation * * @access public - * @param array $array Form values + * @param array $values Form values * @return array $valid, $errors [0] = Success or not, [1] = List of errors */ public function validateCreation(array $values) @@ -494,7 +494,7 @@ class Project extends Base * Validate project modification * * @access public - * @param array $array Form values + * @param array $values Form values * @return array $valid, $errors [0] = Success or not, [1] = List of errors */ public function validateModification(array $values) @@ -517,7 +517,7 @@ class Project extends Base * Validate allowed users * * @access public - * @param array $array Form values + * @param array $values Form values * @return array $valid, $errors [0] = Success or not, [1] = List of errors */ public function validateUserAccess(array $values) diff --git a/app/Templates/board_public.php b/app/Templates/board_public.php index 0808079e9..f90dc01b5 100644 --- a/app/Templates/board_public.php +++ b/app/Templates/board_public.php @@ -19,54 +19,9 @@ -
+
- # - - - - - - - - - - - - - - -
- -
- - -
- - - -
- - - - - + $task, 'categories' => $categories, 'not_editable' => true)) ?>
diff --git a/app/Templates/board_show.php b/app/Templates/board_show.php index 78f9dd50e..e5cd9ceba 100644 --- a/app/Templates/board_show.php +++ b/app/Templates/board_show.php @@ -26,63 +26,14 @@ data-task-limit="" > -
- # - - - - - - - - - - - - - - -
- -
- - -
- - - -
- - - - - + $task, 'categories' => $categories)) ?>
diff --git a/app/Templates/board_task.php b/app/Templates/board_task.php new file mode 100644 index 000000000..fa745ac18 --- /dev/null +++ b/app/Templates/board_task.php @@ -0,0 +1,76 @@ + + + # - + + + + + + + + + + + + + +
+ +
+ + + + # - + + + + + + + + + + + + + +
+ +
+ + + + + +
+ + + +
+ + + + + + \ No newline at end of file diff --git a/app/Templates/project_search.php b/app/Templates/project_search.php index 3594fd095..7826ba63d 100644 --- a/app/Templates/project_search.php +++ b/app/Templates/project_search.php @@ -24,69 +24,7 @@

- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - -
- - - - - - - -
-
- - - - - - - - - - - - - - - - - - - -
+ $tasks, 'categories' => $categories, 'columns' => $columns)) ?> diff --git a/app/Templates/project_tasks.php b/app/Templates/project_tasks.php index 9f4263b81..a820be132 100644 --- a/app/Templates/project_tasks.php +++ b/app/Templates/project_tasks.php @@ -11,61 +11,7 @@

- - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - -
- - - - - - - -
-
- - - - - - - - - - - - - -
+ $tasks, 'categories' => $categories, 'columns' => $columns)) ?> \ No newline at end of file diff --git a/app/Templates/task_table.php b/app/Templates/task_table.php new file mode 100644 index 000000000..10f79d29c --- /dev/null +++ b/app/Templates/task_table.php @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/assets/css/app.css b/assets/css/app.css index 78fc29bd1..fcfe6b1ac 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -478,7 +478,7 @@ nav .active a { padding-left: 0; } -/* boards */ +/* board */ .page-header.board { margin-bottom: 0; } @@ -506,6 +506,11 @@ nav .active a { font-size: 0.8em; } +a.filter-on { + font-weight: bold; + color: #333; +} + .public-board { margin-top: 5px; } @@ -523,74 +528,6 @@ nav .active a { background-color: red; } -a.filter-on { - font-weight: bold; - color: #333; -} - -.task-title { - margin-top: 10px; - font-size: 110%; -} - -.task-user { - font-size: 80%; -} - -.task a.task-nobody { - font-weight: normal; - font-style: italic; - color: #444; -} - -.task-category-container { - text-align: right; - padding-bottom: 2px; -} - -.task-category { - font-weight: bold; - font-size: 0.8em; - color: #000; - border: 1px solid #555; - border-radius: 4px; - padding: 2px; - padding-right: 5px; - padding-left: 5px; -} - -.task-date { - position: absolute; - bottom: 0; - left: 5px; - font-weight: bold; - color: #D90000; -} - -.task-icons { - position: absolute; - bottom: 0; - right: 5px; -} - -.task-footer { - height: 18px; -} - -.task { - border: 1px solid #000; - padding: 5px; - font-size: 95%; -} - -td.over { - background-color: #f0f0f0; -} - -td div.over { - border: 2px dashed #000; -} - .draggable-item { cursor: pointer; user-select: none; @@ -603,46 +540,95 @@ td div.over { margin-bottom: 10px; } -tr td.task a, -div.task a { +/* task inside the board */ +.task-board { + position: relative; + margin-right: 5px; + margin-bottom: 10px; + border: 1px solid #000; + padding: 5px; + font-size: 95%; +} + +.task-table a, +.task-board a { color: #000; text-decoration: none; font-weight: bold; } -tr td.task a:focus, -tr td.task a:hover, -div.task a:focus, -div.task a:hover { +.task-table a:focus, +.task-table a:hover, +.task-board a:focus, +.task-board a:hover { text-decoration: underline; } -div.task-title a { +.task-board-title { + margin-top: 10px; + font-size: 110%; +} + +.task-board-title a { font-weight: normal; } -div.task { - position: relative; - margin-right: 5px; - margin-bottom: 10px; +.task-board-user { + font-size: 80%; } +a.task-board-nobody { + font-weight: normal; + font-style: italic; + color: #444; +} + +.task-board-category-container { + text-align: right; + padding-bottom: 2px; +} + +.task-board-category { + font-weight: bold; + font-size: 0.8em; + color: #000; + border: 1px solid #555; + border-radius: 4px; + padding: 2px; + padding-right: 5px; + padding-left: 5px; +} + +.task-board-footer { + height: 18px; +} + +.task-board-date { + position: absolute; + bottom: 0; + left: 5px; + font-weight: bold; + color: #D90000; +} + +.task-board-icons { + position: absolute; + bottom: 0; + right: 5px; +} + +/* task score */ .task-score { font-weight: bold; position: absolute; } -div.task .task-score { +.task-board .task-score { font-size: 1.5em; right: 5px; top: 0; } -.task-table-icons { - float: right ; - text-align: right; -} - /* task view */ .task-show { position: relative; diff --git a/assets/js/board.js b/assets/js/board.js index 357849e63..f5547310d 100644 --- a/assets/js/board.js +++ b/assets/js/board.js @@ -15,7 +15,7 @@ }); // Open assignee popover - $(".task-user a").click(function(e) { + $(".task-boad-user a").click(function(e) { e.preventDefault(); e.stopPropagation(); @@ -60,7 +60,7 @@ $(".column").each(function() { var columnId = $(this).attr("data-column-id"); - $("#column-" + columnId + " .task").each(function(index) { + $("#column-" + columnId + " .task-board").each(function(index) { data.push({ "task_id": parseInt($(this).attr("data-task-id")), "position": index + 1,