1
0
mirror of https://github.com/chylex/Lightning-Tracker.git synced 2025-06-02 13:34:10 +02:00

Minor fixes (code formatting, unused elements, future TODOs)

This commit is contained in:
chylex 2020-08-06 14:57:49 +02:00
parent ab75ece64c
commit ae25051aa8
28 changed files with 74 additions and 61 deletions

View File

@ -1,11 +1,11 @@
CREATE TABLE `system_role_perms` ( CREATE TABLE `system_role_perms` (
`role_id` SMALLINT NOT NULL, `role_id` SMALLINT NOT NULL,
`permission` ENUM ( `permission` ENUM (
'settings', 'settings',
'trackers.list', 'trackers.list',
'trackers.list.hidden', 'trackers.list.hidden',
'trackers.add', 'trackers.add',
'trackers.edit', 'trackers.edit',
'users.list', 'users.list',
'users.list.email', 'users.list.email',
'users.add', 'users.add',

View File

@ -1,9 +1,9 @@
CREATE TABLE `trackers` ( CREATE TABLE `trackers` (
`id` INT NOT NULL AUTO_INCREMENT, `id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL, `name` VARCHAR(32) NOT NULL,
`url` VARCHAR(32) NOT NULL, `url` VARCHAR(32) NOT NULL,
`owner_id` INT NOT NULL, `owner_id` INT NOT NULL,
`hidden` BOOL NOT NULL, `hidden` BOOL NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY (`url`), UNIQUE KEY (`url`),
FOREIGN KEY (`owner_id`) FOREIGN KEY (`owner_id`)

View File

@ -6,8 +6,8 @@ CREATE TABLE `user_logins` (
UNIQUE KEY (`token`), UNIQUE KEY (`token`),
FOREIGN KEY (`id`) FOREIGN KEY (`id`)
REFERENCES `users` (`id`) REFERENCES `users` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE ON UPDATE CASCADE
ON DELETE CASCADE
) ENGINE = InnoDB ) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE utf8mb4_general_ci COLLATE utf8mb4_general_ci

View File

@ -1,4 +1,4 @@
CREATE TABLE IF NOT EXISTS `users` ( CREATE TABLE `users` (
`id` INT NOT NULL AUTO_INCREMENT, `id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL, `name` VARCHAR(32) NOT NULL,
`email` VARCHAR(255) NOT NULL, `email` VARCHAR(255) NOT NULL,

View File

@ -15,6 +15,7 @@ abstract class AbstractTable{
/** /**
* Fetches the next result. * Fetches the next result.
*
* @param PDOStatement $stmt * @param PDOStatement $stmt
* @return mixed * @return mixed
*/ */
@ -24,6 +25,7 @@ abstract class AbstractTable{
/** /**
* Fetches the next result. * Fetches the next result.
*
* @param PDOStatement $stmt * @param PDOStatement $stmt
* @return mixed * @return mixed
*/ */
@ -33,6 +35,7 @@ abstract class AbstractTable{
/** /**
* Fetches one result and closes the cursor. * Fetches one result and closes the cursor.
*
* @param PDOStatement $stmt * @param PDOStatement $stmt
* @return mixed * @return mixed
*/ */
@ -44,6 +47,7 @@ abstract class AbstractTable{
/** /**
* Fetches one result and closes the cursor. * Fetches one result and closes the cursor.
*
* @param PDOStatement $stmt * @param PDOStatement $stmt
* @return mixed * @return mixed
*/ */

View File

@ -11,7 +11,7 @@ final class DB{
SET time_zone = "+00:00", SET time_zone = "+00:00",
sql_mode = "STRICT_ALL_TABLES,NO_ENGINE_SUBSTITUTION" sql_mode = "STRICT_ALL_TABLES,NO_ENGINE_SUBSTITUTION"
SQL; SQL;
/** /**
* @return PDO * @return PDO
* @throws PDOException * @throws PDOException

View File

@ -18,10 +18,6 @@ final class RoleInfo{
return $this->id; return $this->id;
} }
public function getTitle(): string{
return $this->title;
}
public function getTitleSafe(): string{ public function getTitleSafe(): string{
return protect($this->title); return protect($this->title);
} }

View File

@ -10,14 +10,14 @@ final class UserInfo{
private string $name; private string $name;
private string $email; private string $email;
private ?string $role_title; private ?string $role_title;
private string $registration_date; private string $date_registered;
public function __construct(int $id, string $name, string $email, ?string $role_title, string $registration_date){ public function __construct(int $id, string $name, string $email, ?string $role_title, string $date_registered){
$this->id = $id; $this->id = $id;
$this->name = $name; $this->name = $name;
$this->email = $email; $this->email = $email;
$this->role_title = $role_title; $this->role_title = $role_title;
$this->registration_date = $registration_date; $this->date_registered = $date_registered;
} }
public function getId(): int{ public function getId(): int{
@ -37,7 +37,7 @@ final class UserInfo{
} }
public function getRegistrationDate(): string{ public function getRegistrationDate(): string{
return $this->registration_date; return $this->date_registered;
} }
} }

View File

@ -13,7 +13,7 @@ final class UserLoginInfo{
*/ */
public static function hashPassword(string $password): string{ public static function hashPassword(string $password): string{
$hash = password_hash($password, PASSWORD_BCRYPT); $hash = password_hash($password, PASSWORD_BCRYPT);
if (!$hash){ if (!$hash){
throw new Exception('Fatal error, hashing function failed.'); throw new Exception('Fatal error, hashing function failed.');
} }

View File

@ -3,11 +3,10 @@ declare(strict_types = 1);
namespace Database\Tables; namespace Database\Tables;
use Database\Tables\Traits\PermTable;
use Database\AbstractTable; use Database\AbstractTable;
use Database\DB;
use Database\Objects\RoleInfo; use Database\Objects\RoleInfo;
use Database\Objects\UserProfile; use Database\Objects\UserProfile;
use Database\Tables\Traits\PermTable;
use PDO; use PDO;
use PDOException; use PDOException;

View File

@ -87,9 +87,9 @@ SQL;
} }
public function removeUserId(int $user_id){ public function removeUserId(int $user_id){
$stmt = $this->db->prepare('DELETE FROM tracker_members WHERE tracker_id = ? AND user_id = ?'); $stmt = $this->db->prepare('DELETE FROM tracker_members WHERE user_id = ? AND tracker_id = ?');
$stmt->bindValue(1, $this->getTrackerId(), PDO::PARAM_INT); $stmt->bindValue(1, $user_id, PDO::PARAM_INT);
$stmt->bindValue(2, $user_id, PDO::PARAM_INT); $stmt->bindValue(2, $this->getTrackerId(), PDO::PARAM_INT);
$stmt->execute(); $stmt->execute();
} }
} }

View File

@ -3,11 +3,11 @@ declare(strict_types = 1);
namespace Database\Tables; namespace Database\Tables;
use Database\Tables\Traits\PermTable;
use Database\AbstractTrackerTable; use Database\AbstractTrackerTable;
use Database\Objects\RoleInfo; use Database\Objects\RoleInfo;
use Database\Objects\TrackerInfo; use Database\Objects\TrackerInfo;
use Database\Objects\UserProfile; use Database\Objects\UserProfile;
use Database\Tables\Traits\PermTable;
use PDO; use PDO;
use PDOException; use PDOException;

View File

@ -69,7 +69,7 @@ final class TrackerTable extends AbstractTable{
$stmt->execute(); $stmt->execute();
} }
public function countTrackers(TrackerFilter $filter = null): ?int{ public function countTrackers(?TrackerFilter $filter = null): ?int{
$filter ??= TrackerFilter::empty(); $filter ??= TrackerFilter::empty();
$stmt = $this->db->prepare('SELECT COUNT(*) FROM trackers '.$filter->generateClauses(true)); $stmt = $this->db->prepare('SELECT COUNT(*) FROM trackers '.$filter->generateClauses(true));
@ -84,7 +84,7 @@ final class TrackerTable extends AbstractTable{
* @param TrackerFilter|null $filter * @param TrackerFilter|null $filter
* @return TrackerInfo[] * @return TrackerInfo[]
*/ */
public function listTrackers(TrackerFilter $filter = null): array{ public function listTrackers(?TrackerFilter $filter = null): array{
$filter ??= TrackerFilter::empty(); $filter ??= TrackerFilter::empty();
$stmt = $this->db->prepare('SELECT id, name, url, owner_id FROM trackers '.$filter->generateClauses()); $stmt = $this->db->prepare('SELECT id, name, url, owner_id FROM trackers '.$filter->generateClauses());

View File

@ -19,11 +19,11 @@ trait PermTable{
$values = implode(',', array_map(fn($ignore): string => '(LAST_INSERT_ID(), ?)', $perms)); $values = implode(',', array_map(fn($ignore): string => '(LAST_INSERT_ID(), ?)', $perms));
$stmt = $this->db->prepare(str_replace('()', $values, $sql_base)); $stmt = $this->db->prepare(str_replace('()', $values, $sql_base));
for($i = 0, $count = count($perms); $i < $count; $i++){ for($i = 0, $count = count($perms); $i < $count; $i++){
$stmt->bindValue($i + 1, $perms[$i]); $stmt->bindValue($i + 1, $perms[$i]);
} }
$stmt->execute(); $stmt->execute();
} }
@ -33,11 +33,11 @@ trait PermTable{
*/ */
protected final function fetchRoles(PDOStatement $stmt): array{ protected final function fetchRoles(PDOStatement $stmt): array{
$results = []; $results = [];
while(($res = $this->fetchNext($stmt)) !== false){ while(($res = $this->fetchNext($stmt)) !== false){
$results[] = new RoleInfo($res['id'], $res['title']); $results[] = new RoleInfo($res['id'], $res['title']);
} }
return $results; return $results;
} }
@ -47,11 +47,11 @@ trait PermTable{
*/ */
protected final function fetchPerms(PDOStatement $stmt): array{ protected final function fetchPerms(PDOStatement $stmt): array{
$results = []; $results = [];
while(($res = $this->fetchNextColumn($stmt)) !== false){ while(($res = $this->fetchNextColumn($stmt)) !== false){
$results[] = $res; $results[] = $res;
} }
return $results; return $results;
} }
} }

View File

@ -41,7 +41,7 @@ final class UserTable extends AbstractTable{
$stmt->execute(); $stmt->execute();
} }
public function countUsers(UserFilter $filter = null): ?int{ public function countUsers(?UserFilter $filter = null): ?int{
$filter ??= UserFilter::empty(); $filter ??= UserFilter::empty();
$stmt = $this->db->prepare('SELECT COUNT(*) FROM users '.$filter->generateClauses(true)); $stmt = $this->db->prepare('SELECT COUNT(*) FROM users '.$filter->generateClauses(true));
@ -56,7 +56,7 @@ final class UserTable extends AbstractTable{
* @param UserFilter|null $filter * @param UserFilter|null $filter
* @return UserInfo[] * @return UserInfo[]
*/ */
public function listUsers(UserFilter $filter = null): array{ public function listUsers(?UserFilter $filter = null): array{
$filter ??= UserFilter::empty(); $filter ??= UserFilter::empty();
$sql = <<<SQL $sql = <<<SQL

View File

@ -75,7 +75,7 @@ HTML;
echo '<option value="'.$option[0].'"'.$class.$selected.'>'.$option[1].'</option>'; echo '<option value="'.$option[0].'"'.$class.$selected.'>'.$option[1].'</option>';
} }
echo <<<HTML echo <<<HTML
</select> </select>
HTML; HTML;

View File

@ -136,6 +136,7 @@ HTML;
/** /**
* Fills form fields using the provided data. * Fills form fields using the provided data.
*
* @param array $data * @param array $data
*/ */
public function fill(array $data){ public function fill(array $data){
@ -154,6 +155,7 @@ HTML;
/** /**
* Refills form fields using the provided data, given that the form ID matches. * Refills form fields using the provided data, given that the form ID matches.
*
* @param array $data * @param array $data
* @return string|bool Truthy if all fields were present, indicating that the form is ready for validation. The truthy value is the submit button value if present, or true if no button had a set value. * @return string|bool Truthy if all fields were present, indicating that the form is ready for validation. The truthy value is the submit button value if present, or true if no button had a set value.
*/ */
@ -161,7 +163,7 @@ HTML;
if (!isset($data[self::ACTION_KEY]) || $data[self::ACTION_KEY] !== $this->id){ if (!isset($data[self::ACTION_KEY]) || $data[self::ACTION_KEY] !== $this->id){
return false; return false;
} }
$this->is_filled = true; $this->is_filled = true;
$filled_fields = 0; $filled_fields = 0;
@ -180,7 +182,7 @@ HTML;
if ($filled_fields !== count($this->fields)){ if ($filled_fields !== count($this->fields)){
return false; return false;
} }
if (isset($data[self::RELOADED_KEY])){ if (isset($data[self::RELOADED_KEY])){
if (isset($data[self::MESSAGES_KEY])){ if (isset($data[self::MESSAGES_KEY])){
array_push($this->messages, ...$data[self::MESSAGES_KEY]); array_push($this->messages, ...$data[self::MESSAGES_KEY]);
@ -194,6 +196,7 @@ HTML;
/** /**
* Reloads the form, saving data and form messages in a session. * Reloads the form, saving data and form messages in a session.
*
* @param array $data * @param array $data
* @return ReloadFormAction * @return ReloadFormAction
*/ */
@ -248,7 +251,7 @@ HTML;
} }
$element->echoBody(); $element->echoBody();
/** @noinspection PhpUnusedLocalVariableInspection */ /** @noinspection PhpUnusedLocalVariableInspection */
foreach($groups as $group){ foreach($groups as $group){
echo '</div>'; echo '</div>';

View File

@ -28,7 +28,7 @@ class RequireTracker implements IControlHandler{
if ($url === null){ if ($url === null){
$page_model = new BasicRootPageModel($req); $page_model = new BasicRootPageModel($req);
$error_model = new ErrorModel($page_model, 'Tracker Error', 'Tracker is missing in the URL.'); $error_model = new ErrorModel($page_model, 'Tracker Error', 'Tracker is missing in the URL.');
return view(new ErrorPage($error_model->load())); return view(new ErrorPage($error_model->load()));
} }

View File

@ -41,7 +41,7 @@ class RegisterController extends AbstractHandlerController{
if ($sess->isLoggedOn()){ if ($sess->isLoggedOn()){
return redirect([BASE_URL_ENC, $req->getBasePath()->encoded()]); return redirect([BASE_URL_ENC, $req->getBasePath()->encoded()]);
} }
$model = new RegisterModel($req, $this->tracker); $model = new RegisterModel($req, $this->tracker);
$data = $req->getData(); $data = $req->getData();

View File

@ -23,18 +23,18 @@ class SettingsController extends AbstractHandlerController{
protected function finally(Request $req, Session $sess): IAction{ protected function finally(Request $req, Session $sess): IAction{
$model = new SettingsModel($req); $model = new SettingsModel($req);
$data = $req->getData(); $data = $req->getData();
if (!empty($data)){ if (!empty($data)){
$form = $model->getForm(); $form = $model->getForm();
$action = $form->accept($data); $action = $form->accept($data);
if (($action === $model::ACTION_REMOVE_BACKUP && $model->removeBackupFile()) || if (($action === $model::ACTION_REMOVE_BACKUP && $model->removeBackupFile()) ||
($action === $model::ACTION_UPDATE_SETTINGS && $model->updateConfig($data)) ($action === $model::ACTION_UPDATE_SETTINGS && $model->updateConfig($data))
){ ){
return $form->reload($data); return $form->reload($data);
} }
} }
return view(new SettingsPage($model->load())); return view(new SettingsPage($model->load()));
} }
} }

View File

@ -8,6 +8,7 @@ use Pages\Components\Navigation\NavigationComponent;
interface IModel{ interface IModel{
/** /**
* Loads data into the model. * Loads data into the model.
*
* @return $this * @return $this
*/ */
public function load(): IModel; public function load(): IModel;

View File

@ -30,7 +30,7 @@ class AccountModel extends BasicMixedPageModel{
$this->menu_links->addLink(Text::withIcon('Profile', 'user'), '/account'); $this->menu_links->addLink(Text::withIcon('Profile', 'user'), '/account');
$this->menu_links->addLink(Text::withIcon('Security', 'key'), '/account/security'); $this->menu_links->addLink(Text::withIcon('Security', 'key'), '/account/security');
$this->menu_actions->addActionButton(Text::withIcon('Logout', 'switch'), self::ACTION_LOGOUT); $this->menu_actions->addActionButton(Text::withIcon('Logout', 'switch'), self::ACTION_LOGOUT);
return $this; return $this;

View File

@ -43,7 +43,7 @@ class AccountSecurityModel extends AccountModel{
$form->addButton('submit', 'Change Password') $form->addButton('submit', 'Change Password')
->icon('pencil'); ->icon('pencil');
$this->change_password_form = $form; $this->change_password_form = $form;
} }

View File

@ -25,7 +25,7 @@ use Validation\Validator;
class TrackersModel extends BasicRootPageModel{ class TrackersModel extends BasicRootPageModel{
public const ACTION_CREATE = 'Create'; public const ACTION_CREATE = 'Create';
public const ACTION_DELETE = 'Delete'; public const ACTION_DELETE = 'Delete';
public const PERM_LIST = 'trackers.list'; public const PERM_LIST = 'trackers.list';
public const PERM_LIST_HIDDEN = 'trackers.list.hidden'; public const PERM_LIST_HIDDEN = 'trackers.list.hidden';
public const PERM_ADD = 'trackers.add'; public const PERM_ADD = 'trackers.add';
@ -39,7 +39,7 @@ class TrackersModel extends BasicRootPageModel{
public function __construct(Request $req, Permissions $perms){ public function __construct(Request $req, Permissions $perms){
parent::__construct($req); parent::__construct($req);
$this->perms = $perms; $this->perms = $perms;
$this->table = new TableComponent(); $this->table = new TableComponent();
@ -130,11 +130,11 @@ class TrackersModel extends BasicRootPageModel{
$name = $data['Name']; $name = $data['Name'];
$url = $data['Url']; $url = $data['Url'];
$hidden = (bool)($data['Hidden'] ?? false); $hidden = (bool)($data['Hidden'] ?? false);
$validator = new Validator(); $validator = new Validator();
$validator->str('Name', $name)->notEmpty(); $validator->str('Name', $name)->notEmpty();
$validator->str('Url', $url)->notEmpty()->notContains('/')->notContains('\\'); $validator->str('Url', $url)->notEmpty()->notContains('/')->notContains('\\');
try{ try{
$validator->validate(); $validator->validate();
$trackers = new TrackerTable(DB::get()); $trackers = new TrackerTable(DB::get());
@ -156,12 +156,12 @@ class TrackersModel extends BasicRootPageModel{
return false; return false;
} }
} }
$this->form->onGeneralError($e); $this->form->onGeneralError($e);
}catch(Exception $e){ }catch(Exception $e){
$this->form->onGeneralError($e); $this->form->onGeneralError($e);
} }
return false; return false;
} }

View File

@ -179,9 +179,17 @@ class UsersModel extends BasicRootPageModel{
return false; return false;
} }
$users = new UserTable(DB::get()); try{
$users->deleteById((int)$data['User']); $users = new UserTable(DB::get());
return true; $users->deleteById((int)$data['User']);
return true;
}catch(PDOException $e){
if ($e->getCode() === SQL::CONSTRAINT_VIOLATION){
// TODO show message with reason which foreign key checks failed, i.e. cannot delete tracker owner
}
throw $e;
}
} }
} }

View File

@ -45,10 +45,10 @@ class MembersModel extends BasicTrackerPageModel{
$this->table = new TableComponent(); $this->table = new TableComponent();
$this->table->ifEmpty('No members found.'); $this->table->ifEmpty('No members found.');
$this->table->addColumn('Username'); $this->table->addColumn('Username')->bold();
$this->table->addColumn('Role'); $this->table->addColumn('Role');
if ($this->perms->checkTracker($tracker, self::PERM_MANAGE)){ if ($perms->checkTracker($tracker, self::PERM_MANAGE)){
$this->table->addColumn('Actions')->right()->tight(); $this->table->addColumn('Actions')->right()->tight();
$roles = (new TrackerPermTable(DB::get(), $tracker))->listRoles(); $roles = (new TrackerPermTable(DB::get(), $tracker))->listRoles();

View File

@ -22,7 +22,7 @@ class AccountSecurityPage extends AccountPage{
<h3>Change Password</h3> <h3>Change Password</h3>
<article> <article>
HTML; HTML;
$this->model->getChangePasswordForm()->echoBody(); $this->model->getChangePasswordForm()->echoBody();
echo <<<HTML echo <<<HTML

View File

@ -67,6 +67,8 @@ foreach(['&/', 'tracker/:tracker/&/'] as $base){
$router->add($base.'account/security', 'Mixed/AccountSecurityController'); $router->add($base.'account/security', 'Mixed/AccountSecurityController');
} }
// TODO CSRF
function handle_error(int $code, string $title, string $message, ?Request $req = null): void{ function handle_error(int $code, string $title, string $message, ?Request $req = null): void{
http_response_code($code); http_response_code($code);
$page_model = new BasicRootPageModel($req ?? new Request('', '', [])); $page_model = new BasicRootPageModel($req ?? new Request('', '', []));