1 Commits

Author SHA1 Message Date
ecdb89c096 WIP 2025-12-17 12:57:57 +09:00
41 changed files with 606 additions and 2458 deletions

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

7
Dockerfile Normal file
View File

@@ -0,0 +1,7 @@
# this is a development container, not hardened for hosting
FROM php:7.4-cli
RUN apt-get update && \
apt-get install -y \
sqlite3 \
unzip \
git

View File

@@ -4,8 +4,36 @@
A website created with the latest and greatest web technologies. A website created with the latest and greatest web technologies.
May contain blackjack and other things one tends to include in awesome projects. May contain blackjack and other things one tends to include in awesome projects.
See [Getting Started](./docs/getting-started.md) for help to hack on the project. ## Installation
git clone --recursive https://github.com/Programvareverkstedet/nettsiden.git
Put it in a folder your webserver can find.
## Development setup
The development environment can be setup with:
nix develop
For this you will need to install the nix package manager and possibly set the experimental features in your nix config, likely located at /etc/nix/nix.conf or $HOME/.config/nix/nix.conf.
Installing nix with your package manager might not work without some tweaking, but the upstream script should just work which you can find [here](https://nixos.org/download/).
experimental-features = flakes nix-command
You can then run the server with:
runDev
### Admin account
Login goes through `idp.pvv.ntnu.no` via SAML, so you have to use your PVV account.
(This only works if you use access the local development site via the the hostname `localhost`)
To make your account into an admin account, run:
sqlite3 pvv.sqlite "INSERT INTO users (uname, groups) VALUES ('YOUR_USERNAME', 1);"
## Hosting ## Hosting
![](./docs/hosting.jpg) ![](./.gitea/hosting.jpg)

45
dist/pvv_mysql.sql vendored Normal file
View File

@@ -0,0 +1,45 @@
CREATE TABLE events (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`name` TEXT,
`start` TEXT,
`stop` TEXT,
`organiser` TEXT,
`location` TEXT,
`description` TEXT
);
CREATE TABLE projects (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`name` TEXT,
`description` TEXT,
`active` BOOLEAN
);
CREATE TABLE projectmembers (
`projectid` INTEGER,
`name` TEXT,
`uname` TEXT,
`mail` TEXT,
`role` TEXT,
`lead` BOOLEAN DEFAULT 0,
`owner` BOOLEAN DEFAULT 0
);
CREATE TABLE users (`uname` TEXT, `groups` INT DEFAULT 0);
CREATE TABLE motd (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`title` TEXT,
`content` TEXT
);
/*
INSERT INTO motd (title, content)
VALUES ("MOTD ./dev.sh", "du kan endre motd i admin panelet");
*/
CREATE TABLE door (`time` INTEGER PRIMARY KEY, `open` BOOLEAN);
INSERT INTO
door (time, open)
VALUES
(0, FALSE);

49
dist/pvv_postgresql.sql vendored Normal file
View File

@@ -0,0 +1,49 @@
CREATE TABLE events (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT,
start TEXT,
stop TEXT,
organiser TEXT,
location TEXT,
description TEXT
);
CREATE TABLE projects (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT,
description TEXT,
active BOOLEAN
);
CREATE TABLE projectmembers (
projectid INTEGER,
name TEXT,
uname TEXT,
mail TEXT,
role TEXT,
lead BOOLEAN DEFAULT FALSE,
owner BOOLEAN DEFAULT FALSE
);
CREATE TABLE users (
uname TEXT,
groups INT DEFAULT 0
);
CREATE TABLE motd (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
title TEXT,
content TEXT
);
-- INSERT example
-- INSERT INTO motd (title, content)
-- VALUES ('MOTD ./dev.sh', 'du kan endre motd i admin panelet');
CREATE TABLE door (
time INTEGER PRIMARY KEY,
open BOOLEAN
);
INSERT INTO door (time, open)
VALUES (0, FALSE);

54
dist/pvv_sqlite.sql vendored Normal file
View File

@@ -0,0 +1,54 @@
CREATE TABLE "events" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"name" TEXT,
"start" TEXT,
"stop" TEXT,
"organiser" TEXT,
"location" TEXT,
"description" TEXT
);
CREATE TABLE "projects" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"name" TEXT,
"description" TEXT,
"active" BOOLEAN
);
CREATE TABLE "projectmembers" (
"projectid" INTEGER,
"name" TEXT,
"uname" TEXT,
"mail" TEXT,
"role" TEXT,
"lead" BOOLEAN DEFAULT 0,
"owner" BOOLEAN DEFAULT 0
);
CREATE TABLE "users" ("uname" TEXT, "groups" INT DEFAULT 0);
CREATE TABLE "motd" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"title" TEXT,
"content" TEXT
);
INSERT INTO
motd (title, content)
VALUES
(
'MOTD ./dev.sh',
'du kan endre motd i admin panelet'
);
CREATE TABLE "door" ("time" INTEGER PRIMARY KEY, "open" BOOLEAN);
INSERT INTO
door (time, open)
VALUES
(0, FALSE);
INSERT INTO
users (uname, groups)
VALUES
('min_test_bruker', 1);

View File

@@ -1,36 +0,0 @@
<?php
declare(strict_types=1);
$config = [
// This is used by the service provider to contact the identity provider
'default-sp' => [
'saml:SP',
'entityID' => 'http://localhost:1080/simplesaml/sp',
'idp' => 'http://localhost:1080/simplesaml/idp',
],
// This is used by the identity provider to authenticate users
'example-userpass' => [
'exampleauth:UserPass',
'users' => [
'user:user' => [
'uid' => ['user'],
'group' => ['users'],
'cn' => 'Ole Petter',
'mail' => 'user+test@pvv.ntnu.no',
],
'admin:admin' => [
'uid' => ['admin'],
'group' => ['admin'],
'cn' => 'Admin Adminsson',
'mail' => 'admin+test@pvv.ntnu.no',
],
],
],
// This is also used by the identity provider to authenticate IDP admins
// See http://localhost:1080/simplesaml/admin/
'admin' => [
'core:AdminPassword',
],
];

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
$metadata['http://localhost:1080/simplesaml/idp'] = [
'host' => '__DEFAULT__',
'privatekey' => 'localhost.pem',
'certificate' => 'localhost.crt',
'auth' => 'example-userpass',
];

View File

@@ -1,50 +0,0 @@
<?php
declare(strict_types=1);
$metadata['https://idp.pvv.ntnu.no/'] = [
'metadata-set' => 'saml20-idp-remote',
'entityid' => 'https://idp.pvv.ntnu.no/',
'SingleSignOnService' => [
0 => [
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
'Location' => 'https://idp.pvv.ntnu.no/simplesaml/saml2/idp/SSOService.php',
],
],
'SingleLogoutService' => [
0 => [
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
'Location' => 'https://idp.pvv.ntnu.no/simplesaml/saml2/idp/SingleLogoutService.php',
],
],
'certData' => 'MIIDpTCCAo2gAwIBAgIJAJIgibrB7NvsMA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAk5PMR4wHAYDVQQKDBVQcm9ncmFtdmFyZXZlcmtzdGVkZXQxGDAWBgNVBAMMD2lkcC5wdnYubnRudS5ubzEgMB4GCSqGSIb3DQEJARYRZHJpZnRAcHZ2Lm50bnUubm8wHhcNMTcxMTEzMjI0NTQyWhcNMjcxMTEzMjI0NTQyWjBpMQswCQYDVQQGEwJOTzEeMBwGA1UECgwVUHJvZ3JhbXZhcmV2ZXJrc3RlZGV0MRgwFgYDVQQDDA9pZHAucHZ2Lm50bnUubm8xIDAeBgkqhkiG9w0BCQEWEWRyaWZ0QHB2di5udG51Lm5vMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAveLujCsgVCRA360y5yezy8FcSPhaqodggDqY12UTkYOMQLBFaph6uUL4oCUlXZqxScrAYVRt9yw+7BYpcm0p51VZzVCsfMxRVkn+O1eUvsaXq3f13f87QHKYP2f0uqkGf5PvnKIdSaI/ix8WJhD8XT+h0OkHEcaBvUtSG7zbEhvG21WPHwgw2rvZSneArQ8tOitZC0u8VXSfdhtf6ynRseo0xC95634UwQAZivhQ2v4A6Tp57QG5DCXIJ9/z3PkINx3KB/hOeh0EP6Dpbp+7V0/t9778E3whpm4llrH144kzROhA7EgUgkZOjAVjxGCYlcj3xQPnnItihVOZ5B5qLwIDAQABo1AwTjAdBgNVHQ4EFgQUPLhrB+Qb/Kzz7Car9GJkKmEkz6swHwYDVR0jBBgwFoAUPLhrB+Qb/Kzz7Car9GJkKmEkz6swDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAd+4E6t0j8/p8rbZE8y/gZ9GsiRhxkR4l6JbMRUfEpqHKi415qstChRcP2Lo3Yd5qdmj9tLDWoPsqet1QgyTTmQTgUmPhhMOQDqSh90LuqEJseKWafXGS/SfWLH6MWVmzDV5YofJEw2ThPiU58GiS06OLS2poq1eAesa2LQ22J8yYisXM4sxImIFte+LYQ1+1evfBWcvU1vrGsQ0VLJHdef9WoXp1swUFhq4Zk0c7gjHiB1CFVlExAAlk9L6W3CVXmKIYlf4eUnEBGkC061Ir42+uhAMWO9Y/L1NEuboTyd2KAI/6JdKdzpmfk7zPVxWlNxNCZ7OPNuvOKp6VlpB2EA==',
'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
];
function getCertData(string $path): string
{
$cert = file_get_contents($path);
$cert = str_replace("-----BEGIN CERTIFICATE-----", "", $cert);
$cert = str_replace("-----END CERTIFICATE-----", "", $cert);
$cert = str_replace(["\r", "\n"], "", $cert);
return $cert;
}
$metadata['http://localhost:1080/simplesaml/idp'] = [
'metadata-set' => 'saml20-idp-remote',
'entityid' => 'https://localhost:1080/simplesaml/idp',
'SingleSignOnService' => [
0 => [
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
'Location' => 'http://localhost:1080/simplesaml/saml2/idp/SSOService.php',
],
],
'SingleLogoutService' => [
0 => [
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
'Location' => 'http://localhost:1080/simplesaml/saml2/idp/SingleLogoutService.php',
],
],
'certData' => getCertData(__DIR__ . '/../cert/localhost.crt'),
'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
];

View File

@@ -1,16 +0,0 @@
<?php
$metadata['http://localhost:1080/simplesaml/sp'] = [
'AssertionConsumerService' => [
[
'Location' => 'http://localhost:1080/simplesaml/module.php/saml/sp/saml2-acs.php/default-sp',
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
],
],
'SingleLogoutService' => [
[
'Location' => 'http://localhost:1080/simplesaml/module.php/saml/sp/saml2-logout.php/default-sp',
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
],
],
];

View File

@@ -1,75 +0,0 @@
CREATE TABLE events (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`name` TEXT,
`start` TEXT,
`stop` TEXT,
`organiser` TEXT,
`location` TEXT,
`description` TEXT
);
CREATE TABLE project_group (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`title` TEXT NOT NULL,
`description_en` TEXT NOT NULL,
`description_no` TEXT NOT NULL,
`gitea_link` TEXT NOT NULL,
`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) DEFAULT 1,
`title` TEXT NOT NULL,
`description_en` TEXT NOT NULL,
`description_no` 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) ON DELETE SET DEFAULT
);
CREATE TABLE project_maintainer (
`uname` TEXT NOT NULL,
`project_id` INTEGER NOT NULL,
`name` 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 users (`uname` TEXT, `groups` INT DEFAULT 0);
CREATE TABLE motd (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`title` TEXT,
`content` TEXT
);
/*
INSERT INTO motd (title, content)
VALUES ("MOTD ./dev.sh", "du kan endre motd i admin panelet");
*/
CREATE TABLE door (`time` INTEGER PRIMARY KEY, `open` BOOLEAN);
INSERT INTO
door (time, open)
VALUES
(0, FALSE);

View File

@@ -1,88 +0,0 @@
CREATE TABLE "events" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"name" TEXT,
"start" TEXT,
"stop" TEXT,
"organiser" TEXT,
"location" TEXT,
"description" TEXT
);
-- PROJECTS
CREATE TABLE "project_group" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"title" TEXT NOT NULL,
"description_en" TEXT NOT NULL,
"description_no" TEXT NOT NULL,
"gitea_link" TEXT NOT NULL,
"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 DEFAULT 1,
"title" TEXT NOT NULL,
"description_en" TEXT NOT NULL,
"description_no" 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) ON DELETE SET DEFAULT
);
CREATE TABLE "project_maintainer" (
"uname" TEXT NOT NULL,
"project_id" INTEGER NOT NULL,
"name" 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 "users" ("uname" TEXT, "groups" INT DEFAULT 0);
CREATE TABLE "motd" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"title" TEXT,
"content" TEXT
);
INSERT INTO
motd (title, content)
VALUES
(
'MOTD ./dev.sh',
'du kan endre motd i admin panelet'
);
CREATE TABLE "door" ("time" INTEGER PRIMARY KEY, "open" BOOLEAN);
INSERT INTO
door (time, open)
VALUES
(0, FALSE);
INSERT INTO
users (uname, groups)
VALUES
('min_test_bruker', 1);

View File

@@ -1,7 +0,0 @@
-- See users in ../authsources.php
INSERT INTO
users (uname, groups)
VALUES
('admin', 1 | 2 | 4),
('user', 0);

21
docker-compose.yaml Normal file
View File

@@ -0,0 +1,21 @@
version: "3.9"
# cleanup:
# docker container prune -f && docker volume prune -f
# docker system prune -a
services:
nettside: # https://hub.docker.com/_/php
#image: php:7.4-cli
build: .
volumes:
- .:/usr/src/nettside
working_dir: /usr/src/nettside
command: ./dev.sh
environment:
- DOCKER_HOST=0.0.0.0
- DOCKER_PORT=1080
ports:
- 1080:1080
user: "${DOCKER_USER}"

View File

@@ -1,72 +0,0 @@
# Getting started
Let's get you up and running.
## List of dependencies
You will need to install the following pieces of software:
- Git
- SQLite3
- PHP
- Composer
- OpenSSL
If you are running Ubuntu or Debian, you can install these dependencies with:
```bash
sudo apt update
sudo apt install git sqlite3 php composer openssl
```
## Automatic setup
You can use the scripts in the `scripts/` directory to quickly set up a development environment.
By running the `./scripts/setup.sh`, all dependencies will be installed, in addition to other miscellaneous setup tasks. You can then run `./scripts/run.sh` to start the webserver.
You should now be able to access the site at [http://localhost:1080](http://localhost:1080).
Sometimes it is useful to completely reset the state of the project, deleting the data, redownloading dependencies, etc. You can do this by running `./scripts/reset.sh`. Be careful, as this will delete all data in the database!
> [!WARN]
> Even when resetting the project with the reset script, there are some situation where you need to clear your cookies or your browser cache to get a clean state.
> How to do this varies between browsers, so please refer to your browser's documentation for instructions.
## Setup with nix
We provide a devshell with all dependencies included. We do recommend still using the scripts for setup tasks.
```bash
nix develop
./scripts/setup.sh
./scripts/run.sh
```
## Logging in
We have a development configuration for SimpleSAMLphp (which we use as our authentication system), that lets you log in with dummy users while developing.
The available users are:
- `admin` (password: `admin`) - An admin user
- `user` (password: `user`) - A normal user
In addition, if you need to look into the SAML setup, you can log into the SimpleSAMLphp admin interface at [http://localhost:1080/simplesaml/admin](http://localhost:1080/simplesaml/admin) with username `admin` and password `123`.
## The codebase
In the codebase, you will find the following directories:
- `dist`: Contains files related to deployment, hosting and packaging.
- `docs`: Documentation for the project.
- `inc`: PHP include files, containing a base set of useful classes, functions and constants.
- `nix`: Nix config for packaging, devshells, NixOS modules, etc.
- `scripts`: Helper scripts for setting up development environments, running the server, etc.
- `src`: The main library code for the project. This contains raw PHP code with business logic and database access.
- `vendor`: Third-party dependencies installed with composer.
- `www`: The webroot for the project. This contains public assets, styling, javascript and PHP code concerned with routing and rendering webpages.
## How SimpleSAMLphp is set up in the development environment
It used to be the case that we would connect to our production instance of SimpleSAMLphp for authentication even in development environments. This is no longer the case, as we now use our local SimpleSAMLphp instance both as a service provider and as an identity provider in development. The `config.php` and `authsources.php` files are written in a way where one single instance of SimpleSAMLphp acts as both parts. It will send authentication requests to itself. See `dist/simplesaml-dev` for implementation details.

View File

Before

Width:  |  Height:  |  Size: 477 KiB

After

Width:  |  Height:  |  Size: 477 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@@ -4,22 +4,7 @@
}: }:
php.buildComposerProject rec { php.buildComposerProject rec {
src = lib.fileset.toSource { src = ./..;
root = ./..;
fileset = lib.fileset.difference
(lib.fileset.unions [
../dist
../inc
../src
../www
../composer.json
../composer.lock
])
(lib.fileset.unions [
(lib.fileset.maybeMissing ../www/simplesaml)
(lib.fileset.maybeMissing ../www/simplesaml-idp)
]);
};
pname = "pvv-nettsiden"; pname = "pvv-nettsiden";
version = "0.0.1"; version = "0.0.1";
vendorHash = "sha256-7I7Fdp5DvCwCdYY66Mv0hZ+a8xRzQt+WMUKG544k7Fc="; vendorHash = "sha256-7I7Fdp5DvCwCdYY66Mv0hZ+a8xRzQt+WMUKG544k7Fc=";
@@ -27,10 +12,10 @@ php.buildComposerProject rec {
passthru.simplesamlphpPath = "share/php/pvv-nettsiden/vendor/simplesamlphp/simplesamlphp"; passthru.simplesamlphpPath = "share/php/pvv-nettsiden/vendor/simplesamlphp/simplesamlphp";
postInstall = '' postInstall = ''
install -Dm644 dist/simplesaml-prod/config.php "$out"/${passthru.simplesamlphpPath}/config/config.php install -Dm644 dist/simplesamlphp-config.php $out/${passthru.simplesamlphpPath}/config/config.php
install -Dm644 dist/simplesaml-prod/authsources.php "$$out/${passthru.simplesamlphpPath}/config/authsources.php install -Dm644 dist/simplesamlphp-authsources.php $out/${passthru.simplesamlphpPath}/config/authsources.php
install -Dm644 dist/simplesaml-prod/saml20-idp-remote.php "$$out/${passthru.simplesamlphpPath}/metadata/saml20-idp-remote.php install -Dm644 dist/simplesamlphp-idp.php $out/${passthru.simplesamlphpPath}/metadata/saml20-idp-remote.php
install -Dm644 dist/config.source-env.php "$$out/share/php/pvv-nettsiden/config.php install -Dm644 dist/config.source-env.php $out/share/php/pvv-nettsiden/config.php
${lib.pipe extra_files [ ${lib.pipe extra_files [
(lib.mapAttrsToList (target_path: source_path: '' (lib.mapAttrsToList (target_path: source_path: ''

View File

@@ -1,4 +1,4 @@
{ pkgs }: { pkgs, lib }:
let let
phpEnv = pkgs.php84.buildEnv { phpEnv = pkgs.php84.buildEnv {
extensions = { enabled, all }: enabled ++ (with all; [ iconv mbstring pdo_mysql pdo_sqlite ]); extensions = { enabled, all }: enabled ++ (with all; [ iconv mbstring pdo_mysql pdo_sqlite ]);
@@ -12,6 +12,30 @@ pkgs.mkShellNoCC {
php84Packages.php-cs-fixer php84Packages.php-cs-fixer
sqlite-interactive sqlite-interactive
sql-formatter sql-formatter
openssl
]; ];
# Prepare dev environment with sqlite and config files
shellHook = ''
alias runDev='php -S localhost:1080 -d error_reporting=E_ALL -d display_errors=1 -t www/'
declare -a PROJECT_ROOT="$("${lib.getExe pkgs.git}" rev-parse --show-toplevel)"
mkdir -p "$PROJECT_ROOT/www/galleri/bilder/slideshow"
test -e "$PROJECT_ROOT/pvv.sqlite" || sqlite3 "$PROJECT_ROOT/pvv.sqlite" < "$PROJECT_ROOT/dist/pvv_sqlite.sql"
test -e "$PROJECT_ROOT/config.php" || cp -v "$PROJECT_ROOT/dist/config.local.php" "$PROJECT_ROOT/config.php"
if [ ! -d "$PROJECT_ROOT/vendor" ] ; then
pushd "$PROJECT_ROOT"
composer install || exit $?
cp dist/simplesamlphp-authsources.php vendor/simplesamlphp/simplesamlphp/config/authsources.php
cp dist/simplesamlphp-idp.php vendor/simplesamlphp/simplesamlphp/metadata/saml20-idp-remote.php
cp dist/simplesamlphp-config.php vendor/simplesamlphp/simplesamlphp/config/config.php
cp dist/config.local.php config.php
ln -s ../vendor/simplesamlphp/simplesamlphp/public/ www/simplesaml
popd
fi
'';
} }

View File

@@ -1,36 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
REQUIRED_COMMANDS=(git grep)
MISSING_COMMANDS=false
for cmd in "${REQUIRED_COMMANDS[@]}"; do
if ! command -v "$cmd" &> /dev/null; then
echo "$cmd could not be found" >&2
MISSING_COMMANDS=true
fi
done
if [ "$MISSING_COMMANDS" = true ]; then
exit 1
fi
declare -r GIT_TREE_IS_DIRTY="$(
if ! git diff --quiet --ignore-submodules \
|| git ls-files --others --exclude-standard | grep -q .; then
echo 1
else
echo 0
fi
)"
if [ "$GIT_TREE_IS_DIRTY" == "1" ]; then
echo "Git working tree is dirty, refusing to reset" >&2
exit 1
fi
declare -r PROJECT_ROOT="$(git rev-parse --show-toplevel)"
(
cd "$PROJECT_ROOT"
git clean -fdx
)

View File

@@ -1,21 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
REQUIRED_COMMANDS=(git)
MISSING_COMMANDS=false
for cmd in "${REQUIRED_COMMANDS[@]}"; do
if ! command -v "$cmd" &> /dev/null; then
echo "$cmd could not be found" >&2
MISSING_COMMANDS=true
fi
done
if [ "$MISSING_COMMANDS" = true ]; then
exit 1
fi
declare -r PROJECT_ROOT="$(git rev-parse --show-toplevel)"
"$PROJECT_ROOT/scripts/clean.sh"
"$PROJECT_ROOT/scripts/setup.sh"
"$PROJECT_ROOT/scripts/seed-test-data.sh"

View File

@@ -1,37 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
REQUIRED_COMMANDS=(
php
)
MISSING_COMMANDS=false
for cmd in "${REQUIRED_COMMANDS[@]}"; do
if ! command -v "$cmd" &> /dev/null; then
echo "$cmd could not be found" >&2
MISSING_COMMANDS=true
fi
done
if [ "$MISSING_COMMANDS" = true ]; then
exit 1
fi
declare -r PROJECT_ROOT="$(git rev-parse --show-toplevel)"
# Check for hints that our project might not be correctly set up
if [ ! -d "$PROJECT_ROOT/vendor" ] \
|| [ ! -f "$PROJECT_ROOT/config.php" ] \
|| [ ! -d "$PROJECT_ROOT/www/simplesaml" ] \
|| [ ! -d "$PROJECT_ROOT/www/galleri/bilder" ]; then
echo "It looks like the project is not correctly set up." >&2
exit 1
fi
declare -a PHP_ARGS=(
-S localhost:1080
-d error_reporting=E_ALL
-d display_errors=1
-t www/
)
(cd "$PROJECT_ROOT" && php "${PHP_ARGS[@]}")

View File

@@ -1,26 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
REQUIRED_COMMANDS=(
sqlite3
)
MISSING_COMMANDS=false
for cmd in "${REQUIRED_COMMANDS[@]}"; do
if ! command -v "$cmd" &> /dev/null; then
echo "$cmd could not be found" >&2
MISSING_COMMANDS=true
fi
done
if [ "$MISSING_COMMANDS" = true ]; then
exit 1
fi
declare -r PROJECT_ROOT="$(git rev-parse --show-toplevel)"
if [ ! -f "$PROJECT_ROOT/pvv.sqlite" ] ; then
echo "Database file $PROJECT_ROOT/pvv.sqlite does not exist. Please run setup.sh first." >&2
exit 1
fi
sqlite3 "$PROJECT_ROOT/pvv.sqlite" < "$PROJECT_ROOT/dist/sql/test_data_sqlite.sql"

View File

@@ -1,57 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
REQUIRED_COMMANDS=(
git
composer
sqlite3
openssl
install
)
MISSING_COMMANDS=false
for cmd in "${REQUIRED_COMMANDS[@]}"; do
if ! command -v "$cmd" &> /dev/null; then
echo "$cmd could not be found" >&2
MISSING_COMMANDS=true
fi
done
if [ "$MISSING_COMMANDS" = true ]; then
exit 1
fi
declare -r PROJECT_ROOT="$(git rev-parse --show-toplevel)"
mkdir -p "$PROJECT_ROOT/www/galleri/bilder/slideshow"
test -e "$PROJECT_ROOT/pvv.sqlite" || sqlite3 "$PROJECT_ROOT/pvv.sqlite" < "$PROJECT_ROOT/dist/sql/pvv_sqlite.sql"
test -e "$PROJECT_ROOT/config.php" || cp -v "$PROJECT_ROOT/dist/config.local.php" "$PROJECT_ROOT/config.php"
if [ ! -d "$PROJECT_ROOT/vendor" ] ; then
pushd "$PROJECT_ROOT"
composer install || exit $?
# Set up SimpleSAMLphp identity provider (for local testing)
install -m644 dist/simplesaml-dev/authsources.php -t vendor/simplesamlphp/simplesamlphp/config/
install -m644 dist/simplesaml-dev/config.php -t vendor/simplesamlphp/simplesamlphp/config/
install -m644 dist/simplesaml-dev/saml20-idp-remote.php -t vendor/simplesamlphp/simplesamlphp/metadata/
install -m644 dist/simplesaml-dev/saml20-idp-hosted.php -t vendor/simplesamlphp/simplesamlphp/metadata/
install -m644 dist/simplesaml-dev/saml20-sp-remote.php -t vendor/simplesamlphp/simplesamlphp/metadata/
# See session.phpsession.savepath in config.php
mkdir -p vendor/simplesamlphp/simplesamlphp/sessions/
openssl req \
-newkey rsa:4096 \
-new \
-x509 \
-days 3652 \
-nodes \
-out vendor/simplesamlphp/simplesamlphp/cert/localhost.crt \
-keyout vendor/simplesamlphp/simplesamlphp/cert/localhost.pem \
-subj "/C=NO/ST=Trondheim/L=Trondheim/O=Programvareverkstedet/CN=localhost"
cp dist/config.local.php config.php
ln -s ../vendor/simplesamlphp/simplesamlphp/public/ www/simplesaml
popd
fi

View File

@@ -78,7 +78,7 @@ class Door {
$query = 'INSERT INTO door(time, open) VALUES (:time, :open)'; $query = 'INSERT INTO door(time, open) VALUES (:time, :open)';
$statement = $this->pdo->prepare($query); $statement = $this->pdo->prepare($query);
$statement->bindParam(':time', $time, \PDO::PARAM_STR); $statement->bindParam(':time', $time, \PDO::PARAM_STR);
$statement->bindParam(':open', $open, \PDO::PARAM_BOOL); $statement->bindParam(':open', $open, \PDO::PARAM_STR);
$statement->execute(); $statement->execute();
$this->removeOld(); $this->removeOld();

View File

@@ -6,120 +6,35 @@ namespace pvv\side;
class Project { class Project {
private int $id; private int $id;
private string $title; private string $name;
private array $description_en; private array $descr;
private array $description_no; private bool $active;
private ?string $gitea_link;
private ?string $issue_board_link;
private ?string $wiki_link;
private array $programming_languages;
private array $technologies;
private array $keywords;
// NOTE: spdx identifier
private ?string $license;
private ?string $logo_url;
public function __construct( public function __construct(
int $id, int $id,
string $title, string $name,
?string $description_en, string $descr,
?string $description_no, bool $active,
?string $gitea_link,
?string $issue_board_link,
?string $wiki_link,
?string $programming_languages,
?string $technologies,
?string $keywords,
?string $license,
?string $logo_url,
) { ) {
$this->id = $id; $this->id = $id;
$this->title = $title; $this->name = $name;
$this->description_en $this->descr = explode("\n", $descr);
= $description_en === null || $description_en === '' $this->active = $active;
? []
: explode("\n", $description_en);
$this->description_no
= $description_no === null || $description_no === ''
? []
: explode("\n", $description_no);
$this->gitea_link = $gitea_link;
$this->issue_board_link = $issue_board_link;
$this->wiki_link = $wiki_link;
$this->programming_languages
= $programming_languages === null || $programming_languages === ''
? []
: explode(',', $programming_languages);
$this->technologies
= $technologies === null || $technologies === ''
? []
: explode(',', $technologies);
$this->keywords
= $keywords === null || $keywords === '' ? [] : explode(',', $keywords);
$this->license = $license;
$this->logo_url = $logo_url;
} }
public function getID(): int { public function getID(): int {
return $this->id; return $this->id;
} }
public function getTitle(): string { public function getName(): string {
return $this->title; return $this->name;
} }
/** public function getDescription(): array {
* @return string[] return $this->descr;
*/
public function getDescriptionEn(): array {
return $this->description_en;
} }
/** public function getActive(): bool {
* @return string[] return $this->active;
*/
public function getDescriptionNo(): array {
return $this->description_no;
}
public function getGiteaLink(): ?string {
return $this->gitea_link;
}
public function getIssueBoardLink(): ?string {
return $this->issue_board_link;
}
public function getWikiLink(): ?string {
return $this->wiki_link;
}
/**
* @return string[]
*/
public function getProgrammingLanguages(): array {
return $this->programming_languages;
}
/**
* @return string[]
*/
public function getTechnologies(): array {
return $this->technologies;
}
/**
* @return string[]
*/
public function getKeywords(): array {
return $this->keywords;
}
public function getLicense(): ?string {
return $this->license;
}
public function getLogoURL(): ?string {
return $this->logo_url;
} }
} }

View File

@@ -11,13 +11,11 @@ class ProjectManager {
$this->pdo = $pdo; $this->pdo = $pdo;
} }
// TODO: groupid
/** /**
* @return Project[] * @return Project[]
*/ */
public function getAll(): array { public function getAll(): array {
$query = 'SELECT * FROM project ORDER BY id ASC'; $query = 'SELECT * FROM projects ORDER BY id ASC';
$statement = $this->pdo->prepare($query); $statement = $this->pdo->prepare($query);
$statement->execute(); $statement->execute();
@@ -25,17 +23,9 @@ class ProjectManager {
foreach ($statement->fetchAll() as $dbProj) { foreach ($statement->fetchAll() as $dbProj) {
$project = new Project( $project = new Project(
$dbProj['id'], $dbProj['id'],
$dbProj['title'], $dbProj['name'],
$dbProj['description_en'], $dbProj['description'],
$dbProj['description_no'], $dbProj['active'],
$dbProj['gitea_link'],
$dbProj['issue_board_link'],
$dbProj['wiki_link'],
$dbProj['languages'],
$dbProj['technologies'],
$dbProj['keywords'],
$dbProj['license'],
$dbProj['logo_url']
); );
$projects[] = $project; $projects[] = $project;
} }
@@ -43,9 +33,8 @@ class ProjectManager {
return $projects; return $projects;
} }
// TODO: groupid
public function getByID(int $id): ?Project { public function getByID(int $id): ?Project {
$query = 'SELECT * FROM project WHERE id=:id LIMIT 1'; $query = 'SELECT * FROM projects WHERE id=:id LIMIT 1';
$statement = $this->pdo->prepare($query); $statement = $this->pdo->prepare($query);
$statement->bindParam(':id', $id, \PDO::PARAM_INT); $statement->bindParam(':id', $id, \PDO::PARAM_INT);
$statement->execute(); $statement->execute();
@@ -57,33 +46,17 @@ class ProjectManager {
return new Project( return new Project(
$dbProj['id'], $dbProj['id'],
$dbProj['title'], $dbProj['name'],
$dbProj['description_en'], $dbProj['description'],
$dbProj['description_no'], $dbProj['active'],
$dbProj['gitea_link'],
$dbProj['issue_board_link'],
$dbProj['wiki_link'],
$dbProj['languages'],
$dbProj['technologies'],
$dbProj['keywords'],
$dbProj['license'],
$dbProj['logo_url']
); );
} }
// TODO: groupid
/** /**
* @return Project[] * @return Project[]
*/ */
public function getByOwner(string $uname): array { public function getByOwner(string $uname): array {
$query = ' $query = 'SELECT projectid FROM projectmembers WHERE uname=: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); $statement = $this->pdo->prepare($query);
$statement->bindParam(':uname', $uname, \PDO::PARAM_STR); $statement->bindParam(':uname', $uname, \PDO::PARAM_STR);
$statement->execute(); $statement->execute();
@@ -101,17 +74,9 @@ class ProjectManager {
foreach ($statement->fetchAll() as $dbProj) { foreach ($statement->fetchAll() as $dbProj) {
$project = new Project( $project = new Project(
$dbProj['id'], $dbProj['id'],
$dbProj['title'], $dbProj['name'],
$dbProj['description_en'], $dbProj['description'],
$dbProj['description_no'], $dbProj['active'],
$dbProj['gitea_link'],
$dbProj['issue_board_link'],
$dbProj['wiki_link'],
$dbProj['languages'],
$dbProj['technologies'],
$dbProj['keywords'],
$dbProj['license'],
$dbProj['logo_url']
); );
$projects[] = $project; $projects[] = $project;
} }
@@ -121,66 +86,47 @@ class ProjectManager {
} }
/** /**
* @return {uname:string,name:string,link:string,email:string,is_owner:bool}[] * @return array<int,array>
*/ */
public function getProjectMembers(int $project_id): array { public function getProjectMembers(int $id): array {
$query = ' $query = 'SELECT * FROM projectmembers WHERE projectid=: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 = $this->pdo->prepare($query);
$statement->bindParam(':id', $project_id, \PDO::PARAM_STR); $statement->bindParam(':id', $id, \PDO::PARAM_STR);
$statement->execute(); $statement->execute();
$maintainers = []; $members = [];
foreach ($statement->fetchAll() as $dbUsr) { foreach ($statement->fetchAll() as $dbUsr) {
$maintainers[] = [ $members[] = [
'uname' => $dbUsr['uname'],
'name' => $dbUsr['name'], 'name' => $dbUsr['name'],
'email' => $dbUsr['email'], 'uname' => $dbUsr['uname'],
'is_owner' => (bool)$dbUsr['is_owner'], 'mail' => $dbUsr['mail'],
'role' => $dbUsr['role'],
'lead' => $dbUsr['lead'],
'owner' => $dbUsr['owner'],
]; ];
} }
return $maintainers; return $members;
} }
/** /**
* @return array{name:string,uname:string,email:string,is_owner:bool}|null * @return array<string,mixed>
*/ */
public function getProjectOwner(int $project_id): ?array { public function getProjectOwner(int $id): array {
$query = ' $query = 'SELECT * FROM projectmembers WHERE (projectid=:id AND owner=1)';
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 = $this->pdo->prepare($query);
$statement->bindParam(':id', $project_id, \PDO::PARAM_STR); $statement->bindParam(':id', $id, \PDO::PARAM_STR);
$statement->execute(); $statement->execute();
$owner = $statement->fetch(); $dbOwner = $statement->fetch();
if (!$owner) {
return null;
}
return [ return [
'name' => $owner['name'], 'name' => $dbOwner['name'],
'uname' => $owner['uname'], 'uname' => $dbOwner['uname'],
'email' => $owner['email'], 'mail' => $dbOwner['mail'],
'is_owner' => (bool)$owner['is_owner'], 'role' => $dbOwner['role'],
'lead' => $dbOwner['lead'],
'owner' => $dbOwner['owner'],
]; ];
} }
} }

View File

@@ -39,22 +39,22 @@ class BrettspillEvent extends Event {
'', '',
'## Vår samling', '## Vår samling',
'', '',
'* Dominion\*', '* Dominion\\*',
'* Three cheers for master', '* Three cheers for master',
'* Avalon', '* Avalon',
'* Hanabi', '* Hanabi',
'* Cards aginst humanity\*', '* Cards aginst humanity\\*',
'* Citadels', '* Citadels',
'* Munchkin\*\*', '* Munchkin\\*\\*',
'* Exploding kittens\*\*', '* Exploding kittens\\*\\*',
'* Aye dark overlord', '* Aye dark overlord',
'* Settlers of catan\*', '* Settlers of catan\\*',
'* Risk\*\*', '* Risk\\*\\*',
'* og mange flere...', '* og mange flere...',
'', '',
'\* Vi har flere ekspansjoner til spillet', '\\* Vi har flere ekspansjoner til spillet',
'', '',
'\*\* Vi har flere varianter av spillet', '\\*\\* Vi har flere varianter av spillet',
]; ];
} }

View File

@@ -18,7 +18,7 @@ if (!$userManager->hasGroup($uname, 'prosjekt')) {
$projectID = $_GET['id']; $projectID = $_GET['id'];
$query = 'DELETE FROM project WHERE id=\'' . $projectID . '\''; $query = 'DELETE FROM projects WHERE id=\'' . $projectID . '\'';
$statement = $pdo->prepare($query); $statement = $pdo->prepare($query);
$statement->execute(); $statement->execute();

View File

@@ -37,18 +37,13 @@ if (isset($_GET['id'])) {
} }
$project = new pvv\side\Project( $project = new pvv\side\Project(
id: 0, 0,
title: 'Kult Prosjekt', 'Kult Prosjekt',
description_en: '', '',
description_no: '', 'kåre knoll',
gitea_link: 'https://git.pvv.ntnu.no/Projects/kultprosjekt', 'pvvadmin',
issue_board_link: 'https://git.pvv.ntnu.no/Projects/kultprosjekt/issues', 'drift@pvv.ntnu.no',
wiki_link: 'https://wiki.pvv.ntnu.no/wiki/Kult_Prosjekt', 0
programming_languages: 'PHP, HTML, CSS, JavaScript',
technologies: 'MySQL, REST, AJAX',
keywords: 'web, very-cool',
license: 'GPL-3.0',
logo_url: '',
); );
if ($new == 0) { if ($new == 0) {
$project = $projectManager->getByID($projectID); $project = $projectManager->getByID($projectID);
@@ -61,7 +56,7 @@ $owner = [
'mail' => '', 'mail' => '',
]; ];
foreach ($members as $i => $data) { foreach ($members as $i => $data) {
if ($data['is_owner']) { if ($data['owner']) {
$owner = $data; $owner = $data;
} }
} }

View File

@@ -1,92 +1,161 @@
a.nostyle {
text-decoration: none;
color:inherit;
cursor: pointer;
}
.project-card { .project-card {
position: relative; position: relative;
display: flex;
flex-direction: column;
box-sizing: border-box; box-sizing: border-box;
border-radius: .2em; border-radius: .15em;
border-left: .3em solid #048; border: 0 solid #048;
border-left-width: .3em;
box-shadow: 0 .1em .3em -.1em rgba(0,0,0,0.5); box-shadow: 0 .1em .3em -.1em rgba(0,0,0,0.5);
background: #fff; overflow: hidden;
min-height: 8em; top: 0;
transition: box-shadow .15s ease, transform .15s ease; min-height: 6em;
margin: 0;
height: 100%;
} }
.project-card:hover { .project-card:hover {
box-shadow: 0.2em 0.3em 0.6em rgba(0,0,0,0.45); box-shadow: 0.1em 0.2em 0.5em 0em rgba(0,0,0,0.5);
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 { .project-title {
padding-bottom: .1em;
margin: 0; margin: 0;
font-size: 1.05em;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
} }
/* Content */
.card-content { .card-content {
padding: 0 .6em .4em; display: block;
flex-grow: 1; margin: .6em;
margin-bottom: 0;
} }
.project-description { .card-content p {
line-height: 1.3em; line-height: 1.25em;
}
.card-content * {
margin-top: 0;
}
.project-organizer {
position: absolute;
bottom: 0;
right: 0;
margin: 0; margin: 0;
color: #333; font-size: .8em;
text-align: right;
font-style: italic;
opacity: 0.5;
padding: 0.1em 0.4em;
} }
/* Tags */ .projects-container {
.project-tags { margin-top: 2em;
margin-top: .4em; margin-bottom: 3em;
display: flex; display: grid;
flex-wrap: wrap; grid-template-columns: 1fr;
gap: .3em; grid-column-gap: 0.5em;
grid-row-gap: 1.3em;
} }
.tag { @media screen and (min-width: 60em) {
font-size: .7em; .projects-container {
padding: .15em .45em; grid-template-columns: 1fr 1fr;
border-radius: .3em; }
background: #eef3f7;
color: #345;
white-space: nowrap;
} }
/* Footer */ @media screen and (min-width: 50rem) {
.project-footer { .contentsplit {
display: flex; display: grid;
justify-content: space-between; grid-template-columns: 17em 2.7fr;
align-items: center; grid-template-areas: "left right";
padding: .2em .5em; grid-column-gap: 0.9em;
font-size: .75em; }
opacity: .7; }
@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";
}
} }
.project-links a { .gridl {
margin-left: .4em; grid-area: left;
text-decoration: none;
opacity: .7;
} }
.project-links a:hover { .projectmember-container {
opacity: 1; 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;
}
.projectmember p {
margin: 0;
}
.projectmember p {
font-size: .8em;
}
.projectmember p:first-child {
font-size: 1em;
margin-bottom: .2em;
}
.memberuname, .memberemail {
display: inline-block;
color: #888;
}
.memberuname {
float: left;
}
.memberemail {
float: right;
}
@media screen and (max-width: 50rem) {
.projects {
display: inline-block;
}
} }
/* edit */ /* edit */

View File

@@ -14,37 +14,31 @@ $pdo = new PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$projectManager = new pvv\side\ProjectManager($pdo); $projectManager = new pvv\side\ProjectManager($pdo);
$project_is_new = false; $new = 0;
if (isset($_GET['new'])) { if (isset($_GET['new'])) {
$project_is_new = $_GET['new']; $new = $_GET['new'];
} }
$projectID = 0; $projectID = 0;
if (isset($_GET['id'])) { if (isset($_GET['id'])) {
$projectID = $_GET['id']; $projectID = $_GET['id'];
} elseif (!$project_is_new) { } elseif ($new == 0) {
echo "\nID not set"; echo "\nID not set";
exit; exit;
} }
$project = new pvv\side\Project( $project = new pvv\side\Project(
id: 0, 0,
title: 'Nytt Prosjekt', 'Nytt Prosjekt',
description_en: null, '',
description_no: null, $attrs['cn'][0],
gitea_link: null, $attrs['uid'][0],
issue_board_link: null, $attrs['mail'][0],
wiki_link: null, 1
programming_languages: null,
technologies: null,
keywords: null,
license: null,
logo_url: null
); );
if ($new == 0) {
if (!$project_is_new) {
$project = $projectManager->getByID($projectID); $project = $projectManager->getByID($projectID);
$maintainers = $projectManager->getProjectMaintainers($projectID); $owner = $projectManager->getProjectOwner($projectID);
if ($owner['uname'] != $attrs['uid'][0]) { if ($owner['uname'] != $attrs['uid'][0]) {
header('HTTP/1.0 403 Forbidden'); header('HTTP/1.0 403 Forbidden');
@@ -52,7 +46,6 @@ if (!$project_is_new) {
exit; exit;
} }
} }
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="no"> <html lang="no">
@@ -81,56 +74,20 @@ if (!$project_is_new) {
<form action="update.php", method="post"> <form action="update.php", method="post">
<p class="subtitle no-chin">Prosjektnavn</p> <p class="subtitle no-chin">Prosjektnavn</p>
<p class="subnote">Gi prosjektet ditt et passende navn</p> <p class="subnote">Gi prosjektet ditt et passende navn</p>
<input class="wide" type="text" name="title" value="<?php echo $project->getTitle(); ?>" class="boxinput" required><br> <input class="wide" type="text" name="title" value="<?php echo $project->getName(); ?>" class="boxinput"><br>
<p class="subtitle no-chin">Beskrivelse (<i style="opacity:0.5;">markdown</i>)</p> <p class="subtitle no-chin">Beskrivelse (<i style="opacity:0.5;">markdown</i>)</p>
<p class="subnote no-chin">Hva går prosjektet ditt ut på?</p> <p class="subnote no-chin">Hva går prosjektet ditt ut på?</p>
<p class="subnote">De første to linjene blir vist på prosjektkortet, prøv å gjøre de til et fint sammendrag eller intro!</p> <p class="subnote">De første to linjene blir vist på prosjektkortet, prøv å gjøre de til et fint sammendrag eller intro!</p>
<textarea class="tall" name="desc_no" style="width:100%" rows="8" class="boxinput" required><?php echo implode("\n", $project->getDescriptionNo()); ?></textarea> <textarea class="tall" name="desc" style="width:100%" rows="8" class="boxinput"><?php echo implode("\n", $project->getDescription()); ?></textarea>
<p class="subtitle no-chin">Beskrivelse på engelsk (<i style="opacity:0.5;">markdown</i>)</p>
<p class="subnote no-chin">Gjenta på engelsk</p>
<textarea class="tall" name="desc_en" style="width:100%" rows="8" class="boxinput" required><?php echo implode("\n", $project->getDescriptionEn()); ?></textarea>
<p class="subtitle no-chin">Gitea-link</p>
<p class="subnote">Link til prosjektet på Gitea</p>
<input class="wide" type="text" name="gitea" value="<?php echo $project->getGiteaLink(); ?>" class="boxinput"><br>
<p class="subtitle no-chin">Issue board-link</p>
<p class="subnote">Link til issue board på Gitea</p>
<input class="wide" type="text" name="issue" value="<?php echo $project->getIssueBoardLink(); ?>" class="boxinput"><br>
<p class="subtitle no-chin">Wiki-link</p>
<p class="subnote">Link til wiki-side</p>
<input class="wide" type="text" name="wiki" value="<?php echo $project->getWikiLink(); ?>" class="boxinput"><br>
<p class="subtitle no-chin">Programmeringsspråk</p>
<p class="subnote">Hvilke programmeringsspråk brukes i prosjektet?</p>
<input class="wide" type="text" name="langs" value="<?php echo implode("\n", $project->getProgrammingLanguages()); ?>" class="boxinput"><br>
<p class="subtitle no-chin">Teknologier</p>
<p class="subnote">Hvilke teknologier brukes i prosjektet?</p>
<input class="wide" type="text" name="techs" value="<?php echo implode("\n", $project->getTechnologies()); ?>" class="boxinput"><br>
<p class="subtitle no-chin">Nøkkelord</p>
<p class="subnote">Nøkkelord som beskriver prosjektet</p>
<input class="wide" type="text" name="keywords" value="<?php echo implode("\n", $project->getKeywords()); ?>" class="boxinput"><br>
<p class="subtitle no-chin">Lisens</p>
<p class="subnote">Hvilken lisens bruker prosjektet?</p>
<input class="wide" type="text" name="license" value="<?php echo $project->getLicense(); ?>" class="boxinput"><br>
<p class="subtitle no-chin">Logo-URL</p>
<p class="subnote">Link til logo for prosjektet</p>
<input class="wide" type="text" name="logo" value="<?php echo $project->getLogoURL(); ?>" class="boxinput"><br>
<?php echo '<input type="hidden" name="id" value="' . $project->getID() . '" />'; ?> <?php echo '<input type="hidden" name="id" value="' . $project->getID() . '" />'; ?>
<input type="hidden" name="active" value="1"/> <input type="hidden" name="active" value="1"/>
<div style="margin-top: 0.2em;"> <div style="margin-top: 0.2em;">
<hr class="ruler"> <hr class="ruler">
<input type="submit" class="btn" value="<?php echo $project_is_new ? 'Opprett prosjekt' : 'Lagre endringer'; ?>"></input> <input type="submit" class="btn" value="<?php echo $new ? 'Opprett prosjekt' : 'Lagre endringer'; ?>"></input>
<?php if (!$project_is_new) {?><input type="submit" class="btn" name="delete" value="Slett"></input><?php } ?> <?php if (!$new) {?><input type="submit" class="btn" name="delete" value="Slett"></input><?php } ?>
</div> </div>
</form> </form>
</main> </main>

View File

@@ -71,6 +71,7 @@ $projects = $projectManager->getAll();
<br> <br>
<center> <center>
<a class="btn" href="edit.php?new=1">Lag prosjekt</a> <a class="btn" href="edit.php?new=1">Lag prosjekt</a>
<a class="btn" href="mine.php">Mine prosjekter</a>
</center> </center>
<br> <br>
<?php <?php
@@ -83,7 +84,7 @@ $projects = $projectManager->getAll();
<div class="projects-container"> <div class="projects-container">
<?php <?php
$randProjects = array_rand($projects, min(8, count($projects))); $randProjects = array_rand($projects, min(6, count($projects)));
if (!is_array($randProjects)) { if (!is_array($randProjects)) {
$randProjects = [$randProjects]; $randProjects = [$randProjects];
} }
@@ -92,60 +93,16 @@ $projects = $projectManager->getAll();
$owner = $projectManager->getProjectOwner($project->getID()); $owner = $projectManager->getProjectOwner($project->getID());
?> ?>
<a class="nostyle" href="info.php?id=<?php echo $project->getID(); ?>"> <a class="nostyle" href="info.php?id=<?php echo $project->getID(); ?>"><div class="project-card">
<article class="project-card">
<header class="project-header">
<?php if (!empty($project->getLogoURL())): ?>
<img src="<?php echo htmlspecialchars($project->getLogoURL()); ?>"
alt=""
class="project-logo">
<?php endif; ?>
<h4 class="project-title">
<?php echo htmlspecialchars($project->getTitle()); ?>
</h4>
</header>
<div class="card-content"> <div class="card-content">
<p class="project-description"> <h4 class="project-title"><?php echo $project->getName(); ?></h4>
<?php <?php
$Parsedown = new Parsedown(); $Parsedown = new Parsedown();
echo $Parsedown->text( echo $Parsedown->text(implode("\n", array_slice($project->getDescription(), 0, 2)));
implode("\n", $project->getDescriptionEn())
);
?> ?>
</p>
<?php if (!empty($project->getTechnologies())): ?>
<div class="project-tags">
<?php foreach ($project->getTechnologies() as $tech): ?>
<span class="tag"><?php echo trim($tech); ?></span>
<?php endforeach; ?>
</div> </div>
<?php endif; ?> <p class="project-organizer">Organisert av <?php echo $owner['name']; ?></p>
</div> </div></a>
<footer class="project-footer">
<span class="project-organizer">
Organisert av <?php echo htmlspecialchars($owner['name']); ?>
</span>
<div class="project-links">
<?php if ($project->getGiteaLink()): ?>
<a href="<?php echo $project->getGiteaLink(); ?>" title="Repository"></a>
<?php endif; ?>
<?php if ($project->getIssueBoardLink()): ?>
<a href="<?php echo $project->getIssueBoardLink(); ?>" title="Issues">🐞</a>
<?php endif; ?>
<?php if ($project->getWikiLink()): ?>
<a href="<?php echo $project->getWikiLink(); ?>" title="Wiki">📖</a>
<?php endif; ?>
</div>
</footer>
</article>
</a>
<?php } ?> <?php } ?>
</div> </div>
<center> <center>

120
www/prosjekt/mine.php Normal file
View File

@@ -0,0 +1,120 @@
<?php
date_default_timezone_set('Europe/Oslo');
setlocale(\LC_ALL, 'nb_NO');
require __DIR__ . '/../../inc/navbar.php';
require __DIR__ . '/../../src/_autoload.php';
require __DIR__ . '/../../config.php';
require_once __DIR__ . '/../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php';
$as = new SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$pdo = new PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$projectManager = new pvv\side\ProjectManager($pdo);
$projects = $projectManager->getByOwner($attrs['uid'][0]);
$page = 1;
if (isset($_GET['page'])) {
$page = $_GET['page'];
}
$filter = '';
if (isset($_GET['filter'])) {
$filter = $_GET['filter'];
}
// filter
$projects = array_values(array_filter(
$projects,
static fn($project) => (preg_match('/.*' . $filter . '.*/i', $project->getName()) || preg_match('/.*' . $filter . '.*/i', implode(' ', $project->getDescription())))
));
?>
<!DOCTYPE html>
<html lang="no">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<link rel="shortcut icon" href="favicon.ico">
<link rel="stylesheet" href="../css/normalize.css">
<link rel="stylesheet" href="../css/style.css">
<link rel="stylesheet" href="../css/events.css">
<link rel="stylesheet" href="../css/admin.css">
<meta name="theme-color" content="#024" />
<title>Prosjektverkstedet</title>
<header>Prosjekt&shy;verk&shy;stedet</header>
<body>
<nav>
<?php echo navbar(1, 'prosjekt'); ?>
<?php echo loginbar(); ?>
</nav>
<main class="gridsplit">
<div class="gridl">
<h2 class="no-chin">Mine Prosjekter</h2>
<ul class="event-list">
<?php
$counter = 0;
$pageLimit = 8;
for ($i = ($pageLimit * ($page - 1)); $i < count($projects); ++$i) {
if ($counter == $pageLimit) {
break;
}
$project = $projects[$i];
$projectID = $project->getID();
$owner = $projectManager->getProjectOwner($projectID);
if ($owner['uname'] != $attrs['uid'][0]) {
continue;
}
?>
<li>
<div class="event">
<div class="event-info">
<a href="edit.php?id=<?php echo $project->getID(); ?>">
<h3 class="no-chin"><?php echo $project->getName(); ?></h3>
</a>
<p style="text-decoration: none;"><?php echo implode('<br>', array_slice($project->getDescription(), 0, 4)); ?></p>
</div>
</div>
</li>
<?php
++$counter;
}
?>
</ul>
<?php
if ($page != 1) {
echo '<a class="btn float-left" href="?page=' . ($page - 1) . '&filter=' . urlencode($filter) . '">Forrige side</a>';
}
if (($counter == $pageLimit) && (($pageLimit * $page) < count($projects))) {
echo '<a class="btn float-right" href="?page=' . ($page + 1) . '&filter=' . urlencode($filter) . '">Neste side</a>';
}
?>
</div>
<div class="gridr">
<h2>Verktøy</h2>
<a class="btn" href="edit.php?new=1">Lag prosjekt</a>
<h2>Filter</h2>
<form action="mine.php" method="get">
<p class="no-chin">Navn</p>
<?php echo '<input type="text" name="filter" class="boxinput" value="' . $filter . '">'; ?><br>
<div style="margin-top: 2em;">
<input type="submit" class="btn" value="Filtrer"></input>
</div>
</form>
</div>
</main>
</body>

View File

@@ -6,25 +6,9 @@ require __DIR__ . '/../../config.php';
$pdo = new PDO($DB_DSN, $DB_USER, $DB_PASS); $pdo = new PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$required_fields = [ if (!isset($_POST['title']) || !isset($_POST['desc']) || !isset($_POST['active'])) {
'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']); header('Location: ' . $_SERVER['HTTP_REFERER']);
exit; exit;
}
} }
require_once __DIR__ . '/../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php'; require_once __DIR__ . '/../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php';
@@ -32,99 +16,38 @@ $as = new SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth(); $as->requireAuth();
$attrs = $as->getAttributes(); $attrs = $as->getAttributes();
$id = $_POST['id'] ?? 0; $id = $_POST['id'];
$do_delete = isset($_POST['delete']); $do_delete = isset($_POST['delete']);
$do_join_or_leave = isset($_POST['join_or_leave']); $do_join_or_leave = isset($_POST['join_or_leave']);
$title = $_POST['title']; $active = $_POST['active'];
$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'];
$title = $_POST['title'];
$desc = $_POST['desc'];
$name = $attrs['cn'][0]; $name = $attrs['cn'][0];
$uname = $attrs['uid'][0]; $uname = $attrs['uid'][0];
$mail = $attrs['mail'][0]; $mail = $attrs['mail'][0];
if ($id == 0) { // Create new project
$query = <<<END if ($id == 0) {
INSERT INTO $query = 'INSERT INTO projects (name, description, active) VALUES (:title, :desc, 1)';
project(
title,
description_no,
description_en,
gitea_link,
issue_board_link,
wiki_link,
languages,
technologies,
keywords,
license,
logo_url
)
VALUES
(
:title,
:desc_no,
:desc_en,
:gitea,
:issue,
:wiki,
:langs,
:techs,
:keywords,
:license,
:logo
)
END;
$statement = $pdo->prepare($query); $statement = $pdo->prepare($query);
$statement->bindParam(':title', $title, PDO::PARAM_STR); $statement->bindParam(':title', $title, PDO::PARAM_STR);
$statement->bindParam(':desc_no', $desc_no, PDO::PARAM_STR); $statement->bindParam(':desc', $desc, 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(); $statement->execute();
$new_project_id = $pdo->lastInsertId(); $new_id = $pdo->lastInsertId();
$ownerQuery = <<<END
INSERT INTO
project_maintainer (
uname,
project_id,
name,
email
)
VALUES
(
:username,
:project_id,
:user_real_name,
:user_email
)
END;
$ownerQuery = "INSERT INTO projectmembers (projectid, name, uname, mail, role, lead, owner) VALUES (:id, :owner, :owneruname, :owneremail, 'Prosjektleder', 1, 1)";
$statement = $pdo->prepare($ownerQuery); $statement = $pdo->prepare($ownerQuery);
$statement->bindParam(':username', $uname, PDO::PARAM_STR); $statement->bindParam(':id', $new_id, PDO::PARAM_STR);
$statement->bindParam(':project_id', $new_project_id, PDO::PARAM_INT); $statement->bindParam(':owner', $name, PDO::PARAM_STR);
$statement->bindParam(':user_real_name', $name, PDO::PARAM_STR); $statement->bindParam(':owneruname', $uname, PDO::PARAM_STR);
$statement->bindParam(':user_email', $mail, PDO::PARAM_STR); $statement->bindParam(':owneremail', $mail, PDO::PARAM_STR);
$statement->execute(); $statement->execute();
} else { // Update existing project } else {
$projectManager = new pvv\side\ProjectManager($pdo); $projectManager = new pvv\side\ProjectManager($pdo);
$owner = $projectManager->getProjectOwner($id); $owner = $projectManager->getProjectOwner($id);
$members = $projectManager->getProjectMembers($id); $members = $projectManager->getProjectMembers($id);
@@ -139,7 +62,7 @@ if ($id == 0) { // Create new project
} }
} }
if ($is_member) {// leave if ($is_member) {// leave
$query = 'DELETE FROM projectmembers WHERE projectid=:id AND uname=:uname and lead=FALSE and owner=FALSE;'; $query = 'DELETE FROM projectmembers WHERE projectid=:id AND uname=:uname and lead=0 and owner=0;';
$statement = $pdo->prepare($query); $statement = $pdo->prepare($query);
$statement->bindParam(':id', $id, PDO::PARAM_STR); $statement->bindParam(':id', $id, PDO::PARAM_STR);
$statement->bindParam(':uname', $uname, PDO::PARAM_STR); $statement->bindParam(':uname', $uname, PDO::PARAM_STR);
@@ -147,7 +70,7 @@ if ($id == 0) { // Create new project
$statement->execute(); $statement->execute();
echo 'leave'; echo 'leave';
} else {// join } else {// join
$query = "INSERT INTO projectmembers (projectid, name, uname, mail, role, lead, owner) VALUES (:id, :name, :uname, :mail, 'Medlem', FALSE, FALSE)"; $query = "INSERT INTO projectmembers (projectid, name, uname, mail, role, lead, owner) VALUES (:id, :name, :uname, :mail, 'Medlem', 0, 0)";
$statement = $pdo->prepare($query); $statement = $pdo->prepare($query);
$statement->bindParam(':id', $id, PDO::PARAM_STR); $statement->bindParam(':id', $id, PDO::PARAM_STR);
$statement->bindParam(':name', $name, PDO::PARAM_STR); $statement->bindParam(':name', $name, PDO::PARAM_STR);