From 41082c2866a48636f781d0477461984492121f82 Mon Sep 17 00:00:00 2001 From: h7x4 Date: Fri, 19 Dec 2025 14:47:07 +0900 Subject: [PATCH] fixup! WIP: revamp projects: redesign webpage --- dist/sql/pvv_mysql.sql | 30 +++-- dist/sql/pvv_sqlite.sql | 37 ++++-- src/pvv/side/projectmanager.php | 56 +++++---- www/admin/prosjekter/delete.php | 2 +- www/admin/prosjekter/edit.php | 21 ++-- www/css/projects.css | 195 +++++++++++--------------------- www/prosjekt/edit.php | 2 +- www/prosjekt/index.php | 68 ++++++++--- www/prosjekt/update.php | 107 +++++++++++++++--- 9 files changed, 297 insertions(+), 221 deletions(-) diff --git a/dist/sql/pvv_mysql.sql b/dist/sql/pvv_mysql.sql index 2e8ebcd..8d5d835 100644 --- a/dist/sql/pvv_mysql.sql +++ b/dist/sql/pvv_mysql.sql @@ -17,30 +17,42 @@ CREATE TABLE project_group ( `wiki_link` TEXT ); +INSERT INTO + project_group (title, description_en, description_no, gitea_link, wiki_link) +VALUES + ( + 'Projects', + 'Projects developed by members of PVV.', + 'Prosjekter utviklet av medlemmer i PVV.', + 'https://git.pvv.ntnu.no/Projects', + 'https://wiki.pvv.ntnu.no/wiki/Programvareutvikling' + ); + CREATE TABLE project ( `id` INTEGER PRIMARY KEY AUTO_INCREMENT, - `group_id` INTEGER NOT NULL REFERENCES project_group (id), + `group_id` INTEGER NOT NULL REFERENCES project_group (id) DEFAULT 1, `title` TEXT NOT NULL, `description_en` TEXT NOT NULL, `description_no` TEXT NOT NULL, - `gitea_link` TEXT NOT NULL, - `issue_board_link` TEXT NOT NULL, + `gitea_link` TEXT, + `issue_board_link` TEXT, `wiki_link` TEXT, `languages` TEXT, `technologies` TEXT, `keywords` TEXT, `license` TEXT, `logo_url` TEXT, - FOREIGN KEY (group_id) REFERENCES project_group (id) + FOREIGN KEY (group_id) REFERENCES project_group (id) ON DELETE SET DEFAULT ); CREATE TABLE project_maintainer ( - `uname` TEXT PRIMARY KEY, + `uname` TEXT NOT NULL, + `project_id` INTEGER NOT NULL, `name` TEXT NOT NULL, - `link` TEXT NOT NULL, - `mail` TEXT NOT NULL, - FOREIGN KEY (project_id) REFERENCES project (id), - FOREIGN KEY (uname) REFERENCES maintainer (uname), + `email` TEXT, + `is_owner` BOOLEAN DEFAULT FALSE, + PRIMARY KEY (uname, project_id), + FOREIGN KEY (project_id) REFERENCES project (id) ON DELETE CASCADE ); CREATE TABLE users (`uname` TEXT, `groups` INT DEFAULT 0); diff --git a/dist/sql/pvv_sqlite.sql b/dist/sql/pvv_sqlite.sql index 0faf88a..c3a07b3 100644 --- a/dist/sql/pvv_sqlite.sql +++ b/dist/sql/pvv_sqlite.sql @@ -8,6 +8,8 @@ CREATE TABLE "events" ( "description" TEXT ); +-- PROJECTS + CREATE TABLE "project_group" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT, "title" TEXT NOT NULL, @@ -17,34 +19,45 @@ CREATE TABLE "project_group" ( "wiki_link" TEXT ); +INSERT INTO + project_group (title, description_en, description_no, gitea_link, wiki_link) +VALUES + ( + 'Projects', + 'Projects developed by members of PVV.', + 'Prosjekter utviklet av medlemmer i PVV.', + 'https://git.pvv.ntnu.no/Projects', + 'https://wiki.pvv.ntnu.no/wiki/Programvareutvikling' + ); + CREATE TABLE "project" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT, - "group_id" INTEGER NOT NULL REFERENCES project_group (id), + "group_id" INTEGER NOT NULL DEFAULT 1, "title" TEXT NOT NULL, "description_en" TEXT NOT NULL, "description_no" TEXT NOT NULL, - "gitea_link" TEXT NOT NULL, - "issue_board_link" TEXT NOT NULL, + "gitea_link" TEXT, + "issue_board_link" TEXT, "wiki_link" TEXT, "languages" TEXT, "technologies" TEXT, "keywords" TEXT, "license" TEXT, - "logo_url" TEXT + "logo_url" TEXT, + FOREIGN KEY (group_id) REFERENCES project_group (id) ON DELETE SET DEFAULT ); CREATE TABLE "project_maintainer" ( - "uname" TEXT PRIMARY KEY, + "uname" TEXT NOT NULL, + "project_id" INTEGER NOT NULL, "name" TEXT NOT NULL, - "link" TEXT NOT NULL, - "mail" TEXT NOT NULL + "email" TEXT, + "is_owner" BOOLEAN DEFAULT FALSE, + PRIMARY KEY (uname, project_id), + FOREIGN KEY (project_id) REFERENCES project (id) ON DELETE CASCADE ); -CREATE TABLE "project__project_maintainer" ( - "project_id" INTEGER REFERENCES project (id), - "uname" TEXT REFERENCES maintainer (uname), - PRIMARY KEY (project_id, uname) -); +-- CREATE TABLE "users" ("uname" TEXT, "groups" INT DEFAULT 0); diff --git a/src/pvv/side/projectmanager.php b/src/pvv/side/projectmanager.php index 2b44b1e..7232f81 100644 --- a/src/pvv/side/projectmanager.php +++ b/src/pvv/side/projectmanager.php @@ -11,6 +11,8 @@ class ProjectManager { $this->pdo = $pdo; } + // TODO: groupid + /** * @return Project[] */ @@ -41,6 +43,7 @@ class ProjectManager { return $projects; } + // TODO: groupid public function getByID(int $id): ?Project { $query = 'SELECT * FROM project WHERE id=:id LIMIT 1'; $statement = $this->pdo->prepare($query); @@ -68,15 +71,17 @@ class ProjectManager { ); } + + // TODO: groupid /** * @return Project[] */ public function getByOwner(string $uname): array { $query = ' - SELECT projectid FROM project - JOIN project__project_maintainer ON project.id = project__project_maintainer.project_id - JOIN project_maintainer ON project__project_maintainer.uname = project_maintainer.uname + SELECT project.id FROM project + JOIN project_maintainer ON project.id = project_maintainer.project_id WHERE project_maintainer.uname = :uname + AND project_maintainer.is_owner = TRUE '; $statement = $this->pdo->prepare($query); @@ -116,27 +121,30 @@ class ProjectManager { } /** - * @return array + * @return {uname:string,name:string,link:string,email:string,is_owner:bool}[] */ - public function getProjectMembers(int $id): array { + public function getProjectMembers(int $project_id): array { $query = ' - SELECT id FROM project - JOIN project__project_maintainer ON project.id = project__project_maintainer.project_id - JOIN project_maintainer ON project__project_maintainer.uname = project_maintainer.uname - WHERE project.id = :id + SELECT + project_maintainer.uname, + project_maintainer.name, + project_maintainer.email, + project_maintainer.is_owner + FROM project_maintainer + WHERE project_maintainer.project_id = :id '; $statement = $this->pdo->prepare($query); - $statement->bindParam(':id', $id, \PDO::PARAM_STR); + $statement->bindParam(':id', $project_id, \PDO::PARAM_STR); $statement->execute(); $maintainers = []; foreach ($statement->fetchAll() as $dbUsr) { $maintainers[] = [ - 'name' => $dbUsr['name'], 'uname' => $dbUsr['uname'], - 'link' => $dbUsr['link'], - 'mail' => $dbUsr['mail'], + 'name' => $dbUsr['name'], + 'email' => $dbUsr['email'], + 'is_owner' => (bool)$dbUsr['is_owner'], ]; } @@ -144,19 +152,23 @@ class ProjectManager { } /** - * @return array{name:string,uname:string,link:string,mail:string}|null + * @return array{name:string,uname:string,email:string,is_owner:bool}|null */ - public function getProjectOwner(int $id): ?array { + public function getProjectOwner(int $project_id): ?array { $query = ' - SELECT name, uname, link, mail FROM project - JOIN project__project_maintainer ON project.id = project__project_maintainer.project_id - JOIN project_maintainer ON project__project_maintainer.uname = project_maintainer.uname - WHERE project.id = :id AND project__project_maintainer.owner = TRUE + SELECT + project_maintainer.name, + project_maintainer.uname, + project_maintainer.email, + project_maintainer.is_owner + FROM project_maintainer + WHERE project_maintainer.project_id = :id + AND project_maintainer.is_owner = TRUE LIMIT 1 '; $statement = $this->pdo->prepare($query); - $statement->bindParam(':id', $id, \PDO::PARAM_STR); + $statement->bindParam(':id', $project_id, \PDO::PARAM_STR); $statement->execute(); $owner = $statement->fetch(); @@ -167,8 +179,8 @@ class ProjectManager { return [ 'name' => $owner['name'], 'uname' => $owner['uname'], - 'link' => $owner['link'], - 'mail' => $owner['mail'], + 'email' => $owner['email'], + 'is_owner' => (bool)$owner['is_owner'], ]; } } diff --git a/www/admin/prosjekter/delete.php b/www/admin/prosjekter/delete.php index 84c67b4..2a42555 100644 --- a/www/admin/prosjekter/delete.php +++ b/www/admin/prosjekter/delete.php @@ -18,7 +18,7 @@ if (!$userManager->hasGroup($uname, 'prosjekt')) { $projectID = $_GET['id']; -$query = 'DELETE FROM projects WHERE id=\'' . $projectID . '\''; +$query = 'DELETE FROM project WHERE id=\'' . $projectID . '\''; $statement = $pdo->prepare($query); $statement->execute(); diff --git a/www/admin/prosjekter/edit.php b/www/admin/prosjekter/edit.php index 408c833..b480a55 100644 --- a/www/admin/prosjekter/edit.php +++ b/www/admin/prosjekter/edit.php @@ -37,13 +37,18 @@ if (isset($_GET['id'])) { } $project = new pvv\side\Project( - 0, - 'Kult Prosjekt', - '', - 'kåre knoll', - 'pvvadmin', - 'drift@pvv.ntnu.no', - 0 + id: 0, + title: 'Kult Prosjekt', + description_en: '', + description_no: '', + gitea_link: 'https://git.pvv.ntnu.no/Projects/kultprosjekt', + issue_board_link: 'https://git.pvv.ntnu.no/Projects/kultprosjekt/issues', + wiki_link: 'https://wiki.pvv.ntnu.no/wiki/Kult_Prosjekt', + programming_languages: 'PHP, HTML, CSS, JavaScript', + technologies: 'MySQL, REST, AJAX', + keywords: 'web, very-cool', + license: 'GPL-3.0', + logo_url: '', ); if ($new == 0) { $project = $projectManager->getByID($projectID); @@ -56,7 +61,7 @@ $owner = [ 'mail' => '', ]; foreach ($members as $i => $data) { - if ($data['owner']) { + if ($data['is_owner']) { $owner = $data; } } diff --git a/www/css/projects.css b/www/css/projects.css index fe0ab36..1d7cebc 100644 --- a/www/css/projects.css +++ b/www/css/projects.css @@ -1,161 +1,92 @@ -a.nostyle { - text-decoration: none; - color:inherit; - cursor: pointer; -} - .project-card { position: relative; + display: flex; + flex-direction: column; box-sizing: border-box; - border-radius: .15em; - border: 0 solid #048; - border-left-width: .3em; + border-radius: .2em; + border-left: .3em solid #048; box-shadow: 0 .1em .3em -.1em rgba(0,0,0,0.5); - overflow: hidden; - top: 0; - min-height: 6em; - margin: 0; - height: 100%; + background: #fff; + min-height: 8em; + transition: box-shadow .15s ease, transform .15s ease; } .project-card:hover { - box-shadow: 0.1em 0.2em 0.5em 0em rgba(0,0,0,0.5); + box-shadow: 0.2em 0.3em 0.6em rgba(0,0,0,0.45); + transform: translateY(-2px); +} + +/* Header */ +.project-header { + display: flex; + align-items: center; + gap: .6em; + padding: .6em; +} + +.project-logo { + width: 2.4em; + height: 2.4em; + object-fit: contain; + border-radius: .2em; + flex-shrink: 0; } .project-title { - padding-bottom: .1em; margin: 0; + font-size: 1.05em; text-overflow: ellipsis; -} - -.card-content { - display: block; - margin: .6em; - margin-bottom: 0; -} - -.card-content p { - line-height: 1.25em; -} - -.card-content * { - margin-top: 0; -} - -.project-organizer { - position: absolute; - bottom: 0; - right: 0; - margin: 0; - font-size: .8em; - text-align: right; - font-style: italic; - opacity: 0.5; - padding: 0.1em 0.4em; -} - -.projects-container { - margin-top: 2em; - margin-bottom: 3em; - display: grid; - grid-template-columns: 1fr; - grid-column-gap: 0.5em; - grid-row-gap: 1.3em; -} - -@media screen and (min-width: 60em) { - .projects-container { - grid-template-columns: 1fr 1fr; - } -} - -@media screen and (min-width: 50rem) { - .contentsplit { - display: grid; - grid-template-columns: 17em 2.7fr; - grid-template-areas: "left right"; - grid-column-gap: 0.9em; - } -} -@media screen and (max-width: 50rem) { - .contentsplit { - display: grid; - grid-template-rows: auto auto; - grid-template-areas: "right" - "left"; - } -} -@media screen and (min-width: 33rem) and (max-width: 50rem) { - .projectmember-container { - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - grid-template-areas: "organizers members" - "join join"; - } -} - -.gridl { - grid-area: left; -} - -.projectmember-container { - padding: 0.1em 1em; - box-shadow: 0 2px 10px 0 rgba(0,0,0,0.2); -} -.projectmember-container h2 { - text-align: center; -} -.projectmember-container >form { - text-align: center; - grid-area: join; -} - -.gridr { - border-left: 0; - grid-area: right; - padding:0; - margin:0; -} - -.projectmember { - margin-bottom: 1em; - padding: 0 .5em 0 .5em; overflow: hidden; - border-left: 4px solid #35a; + white-space: nowrap; } -.projectmember p { +/* Content */ +.card-content { + padding: 0 .6em .4em; + flex-grow: 1; +} + +.project-description { + line-height: 1.3em; margin: 0; + color: #333; } -.projectmember p { - font-size: .8em; +/* Tags */ +.project-tags { + margin-top: .4em; + display: flex; + flex-wrap: wrap; + gap: .3em; } -.projectmember p:first-child { - font-size: 1em; - margin-bottom: .2em; +.tag { + font-size: .7em; + padding: .15em .45em; + border-radius: .3em; + background: #eef3f7; + color: #345; + white-space: nowrap; } -.memberuname, .memberemail { - display: inline-block; - color: #888; +/* Footer */ +.project-footer { + display: flex; + justify-content: space-between; + align-items: center; + padding: .2em .5em; + font-size: .75em; + opacity: .7; } -.memberuname { - float: left; +.project-links a { + margin-left: .4em; + text-decoration: none; + opacity: .7; } -.memberemail { - float: right; -} - -@media screen and (max-width: 50rem) { - .projects { - display: inline-block; - - } +.project-links a:hover { + opacity: 1; } /* edit */ diff --git a/www/prosjekt/edit.php b/www/prosjekt/edit.php index 666ac40..66aa49f 100644 --- a/www/prosjekt/edit.php +++ b/www/prosjekt/edit.php @@ -86,7 +86,7 @@ if (!$project_is_new) {

Beskrivelse (markdown)

Hva går prosjektet ditt ut på?

De første to linjene blir vist på prosjektkortet, prøv å gjøre de til et fint sammendrag eller intro!

- +

Beskrivelse på engelsk (markdown)

Gjenta på engelsk

diff --git a/www/prosjekt/index.php b/www/prosjekt/index.php index bc57464..0bb4810 100644 --- a/www/prosjekt/index.php +++ b/www/prosjekt/index.php @@ -93,26 +93,58 @@ $projects = $projectManager->getAll(); ?> -
-
-

getTitle(); ?>

-

- + +

+ getLogoURL())): ?> + + + +

+ getTitle()); ?> +

+
+ +
+

+ text(implode("\n", array_slice($project->getDescriptionNo(), 0, 2))); + echo $Parsedown->text( + implode("\n", $project->getDescriptionEn()) + ); ?> -

- getGiteaLink() ?> - getIssueBoardLink() ?> - getLogoURL() ?> - getProgrammingLanguages() ?> - getTechnologies() ?> - getKeywords() ?> - getLicense() ?> - getWikiLink() ?> -
-

Organisert av

-
+

+ + getTechnologies())): ?> +
+ getTechnologies() as $tech): ?> + + +
+ +
+ +
+ + diff --git a/www/prosjekt/update.php b/www/prosjekt/update.php index 448a3de..d32e3e6 100644 --- a/www/prosjekt/update.php +++ b/www/prosjekt/update.php @@ -6,9 +6,25 @@ require __DIR__ . '/../../config.php'; $pdo = new PDO($DB_DSN, $DB_USER, $DB_PASS); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); -if (!isset($_POST['title']) || !isset($_POST['desc']) || !isset($_POST['active'])) { - header('Location: ' . $_SERVER['HTTP_REFERER']); - exit; +$required_fields = [ + 'title', + 'desc_no', + 'desc_en', + 'gitea', + 'issue', + 'wiki', + 'langs', + 'techs', + 'keywords', + 'license', + 'logo' +]; + +foreach ($required_fields as $field) { + if (!isset($_POST[$field])) { + header('Location: ' . $_SERVER['HTTP_REFERER']); + exit; + } } require_once __DIR__ . '/../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php'; @@ -16,41 +32,96 @@ $as = new SimpleSAML\Auth\Simple('default-sp'); $as->requireAuth(); $attrs = $as->getAttributes(); -$id = $_POST['id']; +$id = $_POST['id'] ?? 0; $do_delete = isset($_POST['delete']); $do_join_or_leave = isset($_POST['join_or_leave']); -$active = $_POST['active']; - $title = $_POST['title']; -$desc = $_POST['desc']; +$desc_no = $_POST['desc_no']; +$desc_en = $_POST['desc_en']; +$gitea = $_POST['gitea']; +$issue = $_POST['issue']; +$wiki = $_POST['wiki']; +$langs = $_POST['langs']; +$techs = $_POST['techs']; +$keywords = $_POST['keywords']; +$license = $_POST['license']; +$logo = $_POST['logo']; + $name = $attrs['cn'][0]; $uname = $attrs['uid'][0]; $mail = $attrs['mail'][0]; - - if ($id == 0) { // Create new project $query = <<prepare($query); $statement->bindParam(':title', $title, PDO::PARAM_STR); - $statement->bindParam(':desc', $desc, PDO::PARAM_STR); + $statement->bindParam(':desc_no', $desc_no, PDO::PARAM_STR); + $statement->bindParam(':desc_en', $desc_en, PDO::PARAM_STR); + $statement->bindParam(':gitea', $gitea, PDO::PARAM_STR); + $statement->bindParam(':issue', $issue, PDO::PARAM_STR); + $statement->bindParam(':wiki', $wiki, PDO::PARAM_STR); + $statement->bindParam(':langs', $langs, PDO::PARAM_STR); + $statement->bindParam(':techs', $techs, PDO::PARAM_STR); + $statement->bindParam(':keywords', $keywords, PDO::PARAM_STR); + $statement->bindParam(':license', $license, PDO::PARAM_STR); + $statement->bindParam(':logo', $logo, PDO::PARAM_STR); $statement->execute(); - $new_id = $pdo->lastInsertId(); + $new_project_id = $pdo->lastInsertId(); + + $ownerQuery = <<prepare($ownerQuery); - $statement->bindParam(':id', $new_id, PDO::PARAM_STR); - $statement->bindParam(':owner', $name, PDO::PARAM_STR); - $statement->bindParam(':owneruname', $uname, PDO::PARAM_STR); - $statement->bindParam(':owneremail', $mail, PDO::PARAM_STR); + $statement->bindParam(':username', $uname, PDO::PARAM_STR); + $statement->bindParam(':project_id', $new_project_id, PDO::PARAM_INT); + $statement->bindParam(':user_real_name', $name, PDO::PARAM_STR); + $statement->bindParam(':user_email', $mail, PDO::PARAM_STR); $statement->execute(); } else { // Update existing project