62 Commits

Author SHA1 Message Date
oysteikt bb8e1c0f6e fixup! WIP more stuff lol 2026-05-12 20:55:21 +09:00
oysteikt cf94020210 fixup! b18d378577 2026-05-12 20:55:21 +09:00
oysteikt 8fa7acd918 WIP more stuff lol 2026-05-12 20:54:50 +09:00
oysteikt b18d378577 fixup! WIP: revamp projects: redesign webpage 2026-05-12 20:53:25 +09:00
oysteikt 579e4f09f5 fixup! WIP: revamp projects: redesign webpage 2026-05-12 20:52:24 +09:00
oysteikt 35c7f56b80 WIP: revamp projects: redesign webpage 2026-05-12 20:52:23 +09:00
oysteikt 9d37fbead5 revamp projects: modify database schema 2026-05-12 20:51:14 +09:00
oysteikt 07d132575a The formattening, part 3 2026-05-11 22:41:14 +09:00
oysteikt 00d542ca59 flake.nix: bump php 8.4 -> php 8.5 2026-05-11 22:18:04 +09:00
oysteikt 11c1a1eb78 flake.lock: bump, composer.lock: bump 2026-05-11 22:15:50 +09:00
oysteikt 4158a776fd .mailmap: further dedup 2026-05-11 22:11:29 +09:00
oysteikt c4a86060b7 treewide: swap all uses of bindParam with bindValue
This causes complaints and warnings when used with functions generating
values instead of already bound variables. Nowhere do we rely on the
arguments being passed by reference.
2026-05-11 22:03:47 +09:00
vegardbm aa8adfc6a4 Merge pull request 'make y-margin for .serviceGrid fixed' (#117) from tjenester_consistency into main
Reviewed-on: #117
Reviewed-by: Oystein Kristoffer Tveit <oysteikt@pvv.ntnu.no>
2026-01-21 16:36:46 +01:00
vegardbm aa1bdd6996 make y-margin for .serviceGrid fixed 2026-01-21 15:24:58 +01:00
oysteikt 3a8f82b12a www/tjenester: add user databases 2026-01-17 16:53:20 +09:00
oysteikt 6c9284b8f6 www/tjenester: add mapcrafter 2026-01-17 16:48:09 +09:00
oysteikt 2a20a1b4eb www/tjenester: rename recreational category to 'other', add vaultwarden 2026-01-17 16:44:32 +09:00
oysteikt f3c13b1aee door: fix navbar (again) 2026-01-13 20:09:25 +09:00
oysteikt c997ad4b47 composer.lock: bump 2026-01-13 19:59:14 +09:00
oysteikt d5d8937365 .well-known: remove 2026-01-13 19:49:41 +09:00
oysteikt ceb9b51756 .mailmap: init 2026-01-13 19:49:11 +09:00
oysteikt f163ed3a2f www/door: fix navbar position 2026-01-13 19:29:58 +09:00
oysteikt bdea5cf182 www/door: remove gray color, add blue boxes 2026-01-13 19:24:10 +09:00
oysteikt 280833253c www/door: add navbar 2026-01-13 19:09:10 +09:00
oysteikt cefd8640b5 www/door: clean up graph options 2026-01-13 18:58:25 +09:00
oysteikt 5a25087d32 scripts/seed-test-data: fill some door data 2026-01-13 18:58:06 +09:00
oysteikt bde2e88273 www/css: remove leftover css and imagery from webmail page 2026-01-13 17:31:52 +09:00
oysteikt 597aaf413a tjenester: add physical services, replace github icon 2026-01-13 17:23:49 +09:00
oysteikt a5e3ac6308 tjenester: less yabai colors 2026-01-13 17:00:57 +09:00
oysteikt 83ce4792e2 tjenester: comment out miniflux 2026-01-13 16:55:43 +09:00
oysteikt 9c645fdb0e tjenester: add icon for gopher, find better svgs for discord and element 2026-01-13 16:49:55 +09:00
oysteikt 7f706ccd2a tjenester: grid and shid 2026-01-13 16:49:55 +09:00
oysteikt 2e7acb7a9a Wrap door status entries in dataclass 2026-01-13 13:39:12 +09:00
oysteikt 9e2fab0ea2 Wrap motd entries in dataclass 2026-01-13 13:28:10 +09:00
oysteikt 189324b87e www/tjenester: replace some pngs with svgs, remove unused images 2026-01-13 12:59:28 +09:00
oysteikt 9f81ae0b8a navbar: don't display webmail 2026-01-13 12:39:30 +09:00
oysteikt 167dd77107 www/tjenester/index: add and remove some sections 2026-01-13 12:37:37 +09:00
oysteikt cc8adf3f84 www/tjenester/index: pull content out of html 2026-01-13 12:17:56 +09:00
oysteikt 73aee17ae3 www/admin: format a few queries 2026-01-13 11:58:46 +09:00
oysteikt 2f0e4b2d83 www/admin/index: use php if blocks and literal html 2026-01-13 11:54:50 +09:00
oysteikt b67cbd6503 door: unvendor javascript blobs 2026-01-13 11:46:19 +09:00
oysteikt 32ba9c11f7 door: don't require default row in database 2026-01-13 11:35:34 +09:00
oysteikt b9992c7c57 dist/sql: format test data script 2026-01-13 11:22:51 +09:00
oysteikt 346cb433ac dist/sql: add yet more constraints 2026-01-13 11:22:03 +09:00
oysteikt 5279c588d5 pvv/side: format long queries 2026-01-13 11:15:37 +09:00
oysteikt e84236c84b dist/sql: add more constraints 2026-01-13 11:04:25 +09:00
oysteikt 8f4dfc992e dist/sql: move test data to test_data_sqlite.sql 2026-01-13 09:34:30 +09:00
oysteikt 1766cc23d6 door: return datetime objects for core functions 2026-01-13 09:27:28 +09:00
oysteikt 044444eaa8 door: fix data extraction 2026-01-13 08:34:59 +09:00
oysteikt 16c9b610ce door: fix data insertion 2026-01-12 21:47:46 +09:00
oysteikt 65118b6abe flake.lock: bump 2026-01-09 06:12:05 +09:00
oysteikt 75226f8314 flake.nix: system -> stdenv.hostPlatform.system 2026-01-09 06:11:43 +09:00
oysteikt 1a4676d85d docs/getting-started: fix warning block 2026-01-07 22:50:43 +09:00
oysteikt 08a216f447 flake.lock: bump 2025-12-30 16:36:28 +09:00
oysteikt 31b7026867 Fix nix package 2025-12-30 16:36:23 +09:00
oysteikt 961f021d27 docs: add directory, write getting started guide 2025-12-17 22:35:48 +09:00
oysteikt 158e816ed0 Add the dev auth users to the database 2025-12-17 22:11:06 +09:00
oysteikt 42cb584ef4 Fix user metadata for dev authsource 2025-12-17 22:11:06 +09:00
oysteikt 1eabf809f0 Use bool values in database 2025-12-17 22:11:06 +09:00
oysteikt bb5b013d31 Remove docker stuff 2025-12-17 21:17:24 +09:00
oysteikt a366769fb9 Add scripts for setting up and resetting the project 2025-12-17 21:17:24 +09:00
oysteikt 19c49b4a00 Host both service provider and identity provider for local dev 2025-12-17 19:58:06 +09:00
103 changed files with 4939 additions and 7731 deletions
+34
View File
@@ -0,0 +1,34 @@
Peder Bergebakken Sundt <pederbs@pvv.ntnu.no> Peder Bergebakken Sundt <pbsds@hotmail.com>
Peder Bergebakken Sundt <pederbs@pvv.ntnu.no> Peder B. Sundt <pbsds@hotmail.com>
Peder Bergebakken Sundt <pederbs@pvv.ntnu.no> Peder Bergebakken Sundt <pederbs@misantropy.pvv.ntnu.no>
Felix Albrigtsen <felixalb@pvv.ntnu.no> Felix Albrigtsen <felixalbrigtsen@gmail.com>
Felix Albrigtsen <felixalb@pvv.ntnu.no> Felix Albrigtsen <felix@albrigtsen.it>
Eirik Wittersø <eirikwit@pvv.ntnu.no> Eirik <eirikw@live.no>
Eirik Wittersø <eirikwit@pvv.ntnu.no> Eirik Wittersø <eirikw@LIVE.no>
Eirik Wittersø <eirikwit@pvv.ntnu.no> Eirik Wittersø <eirikw@live.no>
Jørn Åne <yorinad@pvv.ntnu.no> Jørn Åne <git@jornane.no>
Jørn Åne <yorinad@pvv.ntnu.no> Jørn Åne <yorinad@pvv.ntnu.no>
Markus Wang Halvorsen <markuswh@pvv.ntnu.no> Markus <markus@halvorsenfamilien.com>
Markus Wang Halvorsen <markuswh@pvv.ntnu.no> halworsen <mwh@halvorsenfamilien.com>
Markus Wang Halvorsen <markuswh@pvv.ntnu.no> Markus <markus@halvorsenfamilien.com>
Adrian Gunnar Lauterer <adriangl@pvv.ntnu.no> Adrian Gunnar Lauterer <adrian@lauterer.it>
Bjørnar Ørjansen Kaarevik <bjornoka@pvv.ntnu.no> Bjørnar Ørjansen Kaarevik <bjrnarkaarevik@gmail.com>
Bjørnar Ørjansen Kaarevik <bjornoka@pvv.ntnu.no> Bjornar Orjansen Kaarevik <bjornoka@eirin.pvv.ntnu.no>
Øystein Kristoffer Tveit <oysteikt@pvv.ntnu.no> h7x4 <h7x4@nani.wtf>
Øystein Kristoffer Tveit <oysteikt@pvv.ntnu.no> Oystein Kristoffer Tveit <oysteikt@pvv.ntnu.no>
Vegard Bieker Matthey <vegardbm@pvv.ntnu.no> Vegard Matthey <VegardMatthey@protonmail.com>
Vegard Bieker Matthey <vegardbm@pvv.ntnu.no> Vegard Bieker Matthey <VegardMatthey@protonmail.com>
Torstein Nordgård-Hansen <torsteno@pvv.ntnu.no> Torstein N-H <torsteinnh@gmail.com>
Torstein Nordgård-Hansen <torsteno@pvv.ntnu.no> Torstein N-H <38429890+torsteinnh@users.noreply.github.com>
Amalie Erdal Mansåker <amalieem@pvv.ntnu.no> Amalie Mansåker <amalie.mansaker@gmail.com>
Amalie Erdal Mansåker <amalieem@pvv.ntnu.no> amalieem <99425692+amalieem@users.noreply.github.com>
+24 -16
View File
@@ -20,24 +20,32 @@ $finder = (new PhpCsFixer\Finder())
return (new PhpCsFixer\Config())
->setRiskyAllowed(true)
->setRules([
'@PhpCsFixer' => true,
'@Symfony' => true,
'@PER-CS' => true,
'@PHP71Migration' => true,
'@PHP73Migration' => true,
'@PHP74Migration' => true,
'@PHP80Migration' => true,
'@PHP81Migration' => true,
'@PHP82Migration' => true,
'@PHP83Migration' => true,
'@PHP84Migration' => true,
'@PhpCsFixer:risky' => true,
'@Symfony:risky' => true,
'@PER-CS:risky' => true,
'@PHP74Migration:risky' => true,
'@PHP80Migration:risky' => true,
'@PHP82Migration:risky' => true,
'@PhpCsFixer' => true,
'@PhpCsFixer:risky' => true,
'@Symfony' => true,
'@Symfony:risky' => true,
'@PHP7x0Migration' => true,
'@PHP7x0Migration:risky' => true,
'@PHP7x1Migration' => true,
'@PHP7x1Migration:risky' => true,
'@PHP7x3Migration' => true,
'@PHP7x4Migration' => true,
'@PHP7x4Migration:risky' => true,
'@PHP8x0Migration' => true,
'@PHP8x0Migration:risky' => true,
'@PHP8x1Migration' => true,
'@PHP8x1Migration:risky' => true,
'@PHP8x2Migration' => true,
'@PHP8x2Migration:risky' => true,
'@PHP8x3Migration' => true,
'@PHP8x3Migration:risky' => true,
'@PHP8x4Migration' => true,
'@PHP8x4Migration:risky' => true,
'@PHP8x5Migration' => true,
'@PHP8x5Migration:risky' => true,
'strict_param' => true,
'array_syntax' => ['syntax' => 'short'],
-7
View File
@@ -1,7 +0,0 @@
# 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
+2 -30
View File
@@ -4,36 +4,8 @@
A website created with the latest and greatest web technologies.
May contain blackjack and other things one tends to include in awesome projects.
## 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);"
See [Getting Started](./docs/getting-started.md) for help to hack on the project.
## Hosting
![](./.gitea/hosting.jpg)
![](./docs/hosting.jpg)
Generated
+901 -662
View File
File diff suppressed because it is too large Load Diff
-45
View File
@@ -1,45 +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 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
View File
@@ -1,49 +0,0 @@
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
View File
@@ -1,54 +0,0 @@
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);
+36
View File
@@ -0,0 +1,36 @@
<?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',
],
];
+1416
View File
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
$metadata['http://localhost:1080/simplesaml/idp'] = [
'host' => '__DEFAULT__',
'privatekey' => 'localhost.pem',
'certificate' => 'localhost.crt',
'auth' => 'example-userpass',
];
+50
View File
@@ -0,0 +1,50 @@
<?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',
];
+16
View File
@@ -0,0 +1,16 @@
<?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',
],
],
];
+74
View File
@@ -0,0 +1,74 @@
CREATE TABLE events (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`name` TEXT NOT NULL,
`start` INTEGER,
`stop` INTEGER,
`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,
`programming_languages` TEXT,
`technologies` TEXT,
`keywords` TEXT,
`license` TEXT,
`logo_url` TEXT,
`is_hidden` BOOLEAN DEFAULT FALSE,
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_organizer` BOOLEAN DEFAULT FALSE,
PRIMARY KEY (uname, project_id),
FOREIGN KEY (project_id) REFERENCES project (id) ON DELETE CASCADE
);
CREATE TABLE users (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`uname` TEXT NOT NULL UNIQUE,
`groups` INT NOT NULL DEFAULT 0
);
CREATE TABLE motd (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`title` TEXT NOT NULL,
`content` TEXT NOT NULL
);
CREATE TABLE door (
`time` INTEGER PRIMARY KEY,
`open` BOOLEAN NOT NULL
);
+78
View File
@@ -0,0 +1,78 @@
CREATE TABLE "events" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"name" TEXT NOT NULL,
"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,
"programming_languages" TEXT,
"technologies" TEXT,
"keywords" TEXT,
"license" TEXT,
"logo_url" TEXT,
"is_hidden" BOOLEAN DEFAULT FALSE,
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_organizer" BOOLEAN DEFAULT FALSE,
PRIMARY KEY (uname, project_id),
FOREIGN KEY (project_id) REFERENCES project (id) ON DELETE CASCADE
);
--
CREATE TABLE "users" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"uname" TEXT NOT NULL UNIQUE,
"groups" INT NOT NULL DEFAULT 0
);
CREATE TABLE "motd" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"title" TEXT NOT NULL,
"content" TEXT NOT NULL
);
CREATE TABLE "door" (
"time" INTEGER PRIMARY KEY,
"open" BOOLEAN NOT NULL
);
+15
View File
@@ -0,0 +1,15 @@
-- See users in ../authsources.php
INSERT INTO
"users"("uname", "groups")
VALUES
('admin', 1 | 2 | 4),
('user', 0);
INSERT INTO
"motd"("title", "content")
VALUES
(
'MOTD ./dev.sh',
'du kan endre motd i admin panelet'
);
-21
View File
@@ -1,21 +0,0 @@
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}"

Before

Width:  |  Height:  |  Size: 477 KiB

After

Width:  |  Height:  |  Size: 477 KiB

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

+72
View File
@@ -0,0 +1,72 @@
# 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!
> [!WARNING]
> 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.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Generated
+3 -3
View File
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1765803225,
"narHash": "sha256-xwaZV/UgJ04+ixbZZfoDE8IsOWjtvQZICh9aamzPnrg=",
"lastModified": 1778458615,
"narHash": "sha256-cY07EsdhBJ8tFXPzDYevgqxRev9ZLxFonuq9wmq5kwg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "ac9a217389ee622d4e1e727c4efcc9c4bc9089ba",
"rev": "c6e5ca3c836a5f4dd9af9f2c1fc1c38f0fac988a",
"type": "github"
},
"original": {
+2 -2
View File
@@ -18,11 +18,11 @@
in {
packages = forAllSystems (system: pkgs: {
default = self.packages.${system}.pvv-nettsiden;
pvv-nettsiden = pkgs.callPackage ./nix/package.nix { php = pkgs.php84; };
pvv-nettsiden = pkgs.callPackage ./nix/package.nix { php = pkgs.php85; };
});
overlays.default = final: prev: {
inherit (self.packages.${final.system}) pvv-nettsiden;
inherit (self.packages.${final.stdenv.hostPlatform.system}) pvv-nettsiden;
formats = prev.formats // {
php = import ./nix/php-generator.nix { pkgs = prev; lib = prev.lib; };
};
+1 -1
View File
@@ -10,7 +10,7 @@ function navbar($depth, $active = null) {
// 'Aktiviteter' => 'aktiviteter',
'Prosjekter' => 'prosjekt',
'Kontakt' => 'kontakt',
'Webmail' => 'https://webmail.pvv.ntnu.no/roundcube/',
// 'Webmail' => 'https://webmail.pvv.ntnu.no/roundcube/',
'Galleri' => 'galleri',
'Wiki' => 'https://wiki.pvv.ntnu.no/',
'Git' => 'https://git.pvv.ntnu.no/',
+22 -7
View File
@@ -4,23 +4,38 @@
}:
php.buildComposerProject rec {
src = ./..;
src = lib.fileset.toSource {
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";
version = "0.0.1";
vendorHash = "sha256-7I7Fdp5DvCwCdYY66Mv0hZ+a8xRzQt+WMUKG544k7Fc=";
vendorHash = "sha256-SxuKP7CCLXbTtKQ1seLpAqUje+SEAJ1R+gK09UW2T9k=";
passthru.simplesamlphpPath = "share/php/pvv-nettsiden/vendor/simplesamlphp/simplesamlphp";
postInstall = ''
install -Dm644 dist/simplesamlphp-config.php $out/${passthru.simplesamlphpPath}/config/config.php
install -Dm644 dist/simplesamlphp-authsources.php $out/${passthru.simplesamlphpPath}/config/authsources.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/simplesaml-prod/config.php "$out"/${passthru.simplesamlphpPath}/config/config.php
install -Dm644 dist/simplesaml-prod/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/config.source-env.php "$out"/share/php/pvv-nettsiden/config.php
${lib.pipe extra_files [
(lib.mapAttrsToList (target_path: source_path: ''
mkdir -p $(dirname "$out/${target_path}")
cp -r "${source_path}" "$out/${target_path}"
cp -r "${source_path}" "$out/${target_path}"
''))
(lib.concatStringsSep "\n")
]}
+2 -26
View File
@@ -1,4 +1,4 @@
{ pkgs, lib }:
{ pkgs }:
let
phpEnv = pkgs.php84.buildEnv {
extensions = { enabled, all }: enabled ++ (with all; [ iconv mbstring pdo_mysql pdo_sqlite ]);
@@ -12,30 +12,6 @@ pkgs.mkShellNoCC {
php84Packages.php-cs-fixer
sqlite-interactive
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
'';
}
+36
View File
@@ -0,0 +1,36 @@
#!/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
)
+21
View File
@@ -0,0 +1,21 @@
#!/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"
Executable
+37
View File
@@ -0,0 +1,37 @@
#!/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[@]}")
+48
View File
@@ -0,0 +1,48 @@
#!/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"
# Loop over the last 4 days' unix timestamps in 5-minute intervals and insert test data
END_TIME=$(date +%s)
START_TIME=$((END_TIME - 4 * 24 * 60 * 60))
for ((timestamp=START_TIME; timestamp<=END_TIME; timestamp+=60 * 5 * 10)); do
RANDOM_YES_NO=$((RANDOM % 2))
sqlite3 "$PROJECT_ROOT/pvv.sqlite" <<EOF
INSERT INTO
door(time, open)
VALUES
($timestamp + 60 * 5 * 0, $RANDOM_YES_NO),
($timestamp + 60 * 5 * 1, $RANDOM_YES_NO),
($timestamp + 60 * 5 * 2, $RANDOM_YES_NO),
($timestamp + 60 * 5 * 3, $RANDOM_YES_NO),
($timestamp + 60 * 5 * 4, $RANDOM_YES_NO),
($timestamp + 60 * 5 * 5, $RANDOM_YES_NO),
($timestamp + 60 * 5 * 6, $RANDOM_YES_NO),
($timestamp + 60 * 5 * 7, $RANDOM_YES_NO),
($timestamp + 60 * 5 * 8, $RANDOM_YES_NO),
($timestamp + 60 * 5 * 9, $RANDOM_YES_NO);
EOF
done
+57
View File
@@ -0,0 +1,57 @@
#!/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
+1 -1
View File
@@ -38,4 +38,4 @@ declare(strict_types=1);
*/
spl_autoload_extensions('.php');
spl_autoload_register('spl_autoload');
set_include_path(realpath(__DIR__) . \PATH_SEPARATOR . get_include_path());
set_include_path(realpath(__DIR__).\PATH_SEPARATOR.get_include_path());
+9 -9
View File
@@ -20,16 +20,16 @@ class UserManager {
public function setupUser(string $uname, int $groups = 0): void {
$query = 'INSERT INTO users (uname, groups) VALUES (:uname, :groups)';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':uname', $uname, \PDO::PARAM_STR);
$statement->bindParam(':groups', $groups, \PDO::PARAM_INT);
$statement->bindValue(':uname', $uname, \PDO::PARAM_STR);
$statement->bindValue(':groups', $groups, \PDO::PARAM_INT);
$statement->execute();
}
public function updateFlags(string $uname, int $flags): void {
$query = 'UPDATE users set groups=:groups WHERE uname=:uname';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':groups', $flags, \PDO::PARAM_INT);
$statement->bindParam(':uname', $uname, \PDO::PARAM_STR);
$statement->bindValue(':groups', $flags, \PDO::PARAM_INT);
$statement->bindValue(':uname', $uname, \PDO::PARAM_STR);
}
public function addGroup(string $uname, int $group): void {
@@ -45,7 +45,7 @@ class UserManager {
$userFlags = $this->getUsergroups($uname);
if ($userFlags) {
$newFlags = ($userFlags & (~ $group));
$newFlags = ($userFlags & (~$group));
$this->updateFlags($uname, $newFlags);
}
}
@@ -53,15 +53,15 @@ class UserManager {
public function setGroups(string $uname, int $groups): void {
$query = 'SELECT * FROM users WHERE uname=:uname LIMIT 1';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':uname', $uname, \PDO::PARAM_STR);
$statement->bindValue(':uname', $uname, \PDO::PARAM_STR);
$statement->execute();
$row = $statement->fetch();
if ($row) {
$query = 'UPDATE users set groups=:groups WHERE uname=:uname';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':groups', $groups, \PDO::PARAM_INT);
$statement->bindParam(':uname', $uname, \PDO::PARAM_STR);
$statement->bindValue(':groups', $groups, \PDO::PARAM_INT);
$statement->bindValue(':uname', $uname, \PDO::PARAM_STR);
$statement->execute();
} else {
$this->setupUser($uname, $groups);
@@ -94,7 +94,7 @@ class UserManager {
public function getUsergroups(string $uname): int {
$query = 'SELECT groups FROM users WHERE uname=:uname LIMIT 1';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':uname', $uname, \PDO::PARAM_STR);
$statement->bindValue(':uname', $uname, \PDO::PARAM_STR);
$statement->execute();
$row = $statement->fetch();
+4 -4
View File
@@ -58,7 +58,7 @@ class Agenda {
}
usort(
$result,
static fn($a, $b) => $a->getStart() < $b->getStart() ? -1 : 1,
static fn ($a, $b) => $a->getStart() < $b->getStart() ? -1 : 1,
);
return $result;
@@ -98,14 +98,14 @@ class Agenda {
public function getNextOfEach(\DateTimeImmutable $startDate): array {
$result = array_filter(
array_map(
static fn($a) => $a->getNextEventFrom($startDate),
static fn ($a) => $a->getNextEventFrom($startDate),
$this->activities,
),
static fn($a) => isset($a),
static fn ($a) => isset($a),
);
usort(
$result,
static fn($a, $b) => $a->getStart()->getTimeStamp()
static fn ($a, $b) => $a->getStart()->getTimeStamp()
< $b->getStart()->getTimeStamp()
? -1
: 1,
+31 -6
View File
@@ -39,7 +39,7 @@ class DBActivity implements Activity {
public function getEventByID(int $id): SimpleEvent {
$query = 'SELECT * FROM events WHERE id=:id LIMIT 1';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':id', $id, \PDO::PARAM_INT);
$statement->bindValue(':id', $id, \PDO::PARAM_INT);
$statement->execute();
$dbEvent = $statement->fetch();
@@ -56,15 +56,41 @@ class DBActivity implements Activity {
}
public function getNextEventFrom(\DateTimeImmutable $date): ?Event {
$query
= 'SELECT id,name,start,stop,organiser,location,description FROM events WHERE start > :date ORDER BY start ASC LIMIT 1';
$query = '
SELECT
id,
name,
start,
stop,
organiser,
location,
description
FROM events
WHERE
start > :date
ORDER BY start ASC
LIMIT 1
';
return $this->retrieve($date, $query);
}
public function getPreviousEventFrom(\DateTimeImmutable $date): ?Event {
$query
= 'SELECT id,name,start,stop,organiser,location,description FROM events WHERE start < :date ORDER BY start DESC LIMIT 1';
$query = '
SELECT
id,
name,
start,
stop,
organiser,
location,
description
FROM events
WHERE
start < :date
ORDER BY start DESC
LIMIT 1
';
return $this->retrieve($date, $query);
}
@@ -88,6 +114,5 @@ class DBActivity implements Activity {
}
return null;
}
}
+79 -35
View File
@@ -4,81 +4,125 @@ declare(strict_types=1);
namespace pvv\side;
class DoorStatus {
private \DateTimeImmutable $time;
private bool $open;
public function __construct(\DateTimeImmutable $time, bool $open) {
$this->time = $time;
$this->open = $open;
}
public function getTime(): \DateTimeImmutable {
return $this->time;
}
public function getTimeStamp(): int {
return $this->time->getTimestamp();
}
public function isOpen(): bool {
return $this->open;
}
}
class Door {
private $pdo;
public const DAYS_OF_DOOR_HISTORY = 7;
public function __construct(\PDO $pdo) {
$this->pdo = $pdo;
}
/**
* @return array{time: int, open: bool}[]
* @return DoorStatus[]
*/
public function getAll(): array {
$query = 'SELECT time, open FROM door ORDER BY time DESC';
$query = '
SELECT
time,
open
FROM door
ORDER BY time DESC
';
$statement = $this->pdo->prepare($query);
$statement->execute();
$doorEvents = [];
foreach ($statement->fetchAll() as $row) {
$doorEvents[] = [
'time' => (int) $row['time'],
'open' => (bool) $row['open'],
];
}
$result = array_map(
static fn ($row) => new DoorStatus(
new \DateTimeImmutable()->setTimestamp((int) $row['time']),
(bool) $row['open'],
),
$statement->fetchAll(),
);
return $doorEvents;
return $result;
}
/**
* @return array{time: int, open: bool}[]
* @return DoorStatus[]
*/
public function getEntriesAfter(\DateTimeImmutable $startTime): array {
$query
= 'SELECT time, open FROM door WHERE time > :startTime ORDER BY time DESC';
$query = '
SELECT
time,
open
FROM door
WHERE time > :startTime
ORDER BY time DESC
';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':startTime', $startTime, \PDO::PARAM_STR);
$statement->bindValue(':startTime', $startTime->getTimestamp(), \PDO::PARAM_INT);
$statement->execute();
$doorEvents = [];
foreach ($statement->fetchAll() as $row) {
$doorEvents[] = [
'time' => (int) $row['time'],
'open' => (bool) $row['open'],
];
}
$result = array_map(
static fn ($row) => new DoorStatus(
new \DateTimeImmutable()->setTimestamp((int) $row['time']),
(bool) $row['open'],
),
$statement->fetchAll(),
);
return $doorEvents;
return $result;
}
/**
* @return array{time: int, open: bool}
*/
public function getCurrent(): array {
$query = 'SELECT time, open FROM door ORDER BY time DESC LIMIT 1';
public function getCurrent(): ?DoorStatus {
$query = '
SELECT
time,
open
FROM door
ORDER BY time DESC
LIMIT 1
';
$statement = $this->pdo->prepare($query);
$statement->execute();
$row = $statement->fetch();
return [
'time' => (int) $row['time'],
'open' => (bool) $row['open'],
];
if (!$row) {
return null;
}
return new DoorStatus(
new \DateTimeImmutable()->setTimestamp((int) $row['time']),
(bool) $row['open'],
);
}
private function removeOld(): void {
$firstValidTime = time() - 60 * 60 * 24 * 7; // One week before now
$firstValidTime = time() - 60 * 60 * 24 * self::DAYS_OF_DOOR_HISTORY;
$query = 'DELETE FROM door WHERE time < :firstValid';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':firstValid', $firstValidTime, \PDO::PARAM_STR);
$statement->bindValue(':firstValid', $firstValidTime, \PDO::PARAM_INT);
$statement->execute();
}
public function createEvent(\DateTimeImmutable $time, bool $open): void {
$query = 'INSERT INTO door(time, open) VALUES (:time, :open)';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':time', $time, \PDO::PARAM_STR);
$statement->bindParam(':open', $open, \PDO::PARAM_STR);
$statement->bindValue(':time', $time->getTimestamp(), \PDO::PARAM_INT);
$statement->bindValue(':open', $open, \PDO::PARAM_BOOL);
$statement->execute();
$this->removeOld();
+63 -16
View File
@@ -4,6 +4,35 @@ declare(strict_types=1);
namespace pvv\side;
class MOTDItem {
private string $title;
/** @var string[] */
private array $content;
/**
* @param string[] $content
*/
public function __construct(string $title, array $content) {
$this->title = $title;
$this->content = $content;
}
public function getTitle(): string {
return $this->title;
}
/**
* @return string[]
*/
public function getContent(): array {
return $this->content;
}
public function getContentAsString(): string {
return implode("\n", $this->content);
}
}
class MOTD {
private $pdo;
@@ -15,41 +44,59 @@ class MOTD {
if (\is_array($content)) {
$content = implode('_', $content);
}
$query = 'INSERT INTO motd(title, content) VALUES (:title, :content);';
$query = 'INSERT INTO motd(title, content) VALUES (:title, :content)';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':title', $title, \PDO::PARAM_STR);
$statement->bindParam(':content', $content, \PDO::PARAM_STR);
$statement->bindValue(':title', $title, \PDO::PARAM_STR);
$statement->bindValue(':content', $content, \PDO::PARAM_STR);
$statement->execute();
}
/**
* @return array{title: string, content: string[]}
*/
public function getMOTD(): array {
$query
= 'SELECT motd.title, motd.content FROM motd ORDER BY motd.id DESC LIMIT 1';
public function getMOTD(): MOTDItem {
$query = '
SELECT
title,
content
FROM motd
ORDER BY id DESC
LIMIT 1
';
$statement = $this->pdo->prepare($query);
$statement->execute();
$data = $statement->fetch();
return ['title' => $data[0], 'content' => explode("\n", $data[1])];
return new MOTDItem(
$data['title'],
explode("\n", $data['content']),
);
}
/**
* @return array{title: string, content: string[]}
* @return MOTDItem[]
*/
public function getMOTD_history(int $limit = 5): array {
$query
= 'SELECT motd.title, motd.content FROM motd ORDER BY motd.id DESC LIMIT :limit';
$query = '
SELECT
title,
content
FROM motd
ORDER BY id DESC
LIMIT :limit
';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':limit', $limit, \PDO::PARAM_STR);
$statement->bindValue(':limit', $limit, \PDO::PARAM_STR);
$statement->execute();
$data = $statement->fetch();
$result = array_map(
static fn ($item) => new MOTDItem(
$item['title'],
explode("\n", $item['content']),
),
$statement->fetchAll(),
);
return ['title' => $data[0], 'content' => explode("\n", $data[1])];
return $result;
}
}
+139 -15
View File
@@ -6,35 +6,159 @@ namespace pvv\side;
class Project {
private int $id;
private string $name;
private array $descr;
private bool $active;
private string $title;
private array $description_en;
private array $description_no;
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;
private bool $is_hidden;
public function __construct(
int $id,
string $name,
string $descr,
bool $active,
string $title,
?string $description_en,
?string $description_no,
?string $gitea_link,
?string $issue_board_link,
?string $wiki_link,
?string $programming_languages,
?string $technologies,
?string $keywords,
?string $license,
?string $logo_url,
bool $is_hidden = false,
) {
$this->id = $id;
$this->name = $name;
$this->descr = explode("\n", $descr);
$this->active = $active;
$this->title = $title;
$this->description_en
= $description_en === null || $description_en === ''
? []
: 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;
$this->is_hidden = $is_hidden;
}
public function getID(): int {
return $this->id;
}
public function getTitle(): string {
return $this->title;
}
/**
* @return string[]
*/
public function getDescriptionEn(): array {
return $this->description_en;
}
/**
* @return string[]
*/
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;
}
public function isHidden(): bool {
return $this->is_hidden;
}
}
class ProjectMaintainer {
private string $uname;
private string $name;
private string $email;
private bool $is_organizer;
public function __construct(
string $uname,
string $name,
string $email,
bool $is_organizer,
) {
$this->uname = $uname;
$this->name = $name;
$this->email = $email;
$this->is_organizer = $is_organizer;
}
public function getUname(): string {
return $this->uname;
}
public function getName(): string {
return $this->name;
}
public function getDescription(): array {
return $this->descr;
public function getEmail(): string {
return $this->email;
}
public function getActive(): bool {
return $this->active;
public function isOrganizer(): bool {
return $this->is_organizer;
}
}
+158 -60
View File
@@ -11,11 +11,30 @@ class ProjectManager {
$this->pdo = $pdo;
}
// TODO: groupid
/**
* @return Project[]
*/
public function getAll(): array {
$query = 'SELECT * FROM projects ORDER BY id ASC';
$query = '
SELECT
id,
title,
description_en,
description_no,
gitea_link,
issue_board_link,
wiki_link,
programming_languages,
technologies,
keywords,
license,
logo_url,
is_hidden
FROM project
ORDER BY title ASC
';
$statement = $this->pdo->prepare($query);
$statement->execute();
@@ -23,9 +42,18 @@ class ProjectManager {
foreach ($statement->fetchAll() as $dbProj) {
$project = new Project(
$dbProj['id'],
$dbProj['name'],
$dbProj['description'],
$dbProj['active'],
$dbProj['title'],
$dbProj['description_en'],
$dbProj['description_no'],
$dbProj['gitea_link'],
$dbProj['issue_board_link'],
$dbProj['wiki_link'],
$dbProj['programming_languages'],
$dbProj['technologies'],
$dbProj['keywords'],
$dbProj['license'],
$dbProj['logo_url'],
(bool) $dbProj['is_hidden']
);
$projects[] = $project;
}
@@ -33,10 +61,28 @@ class ProjectManager {
return $projects;
}
// TODO: groupid
public function getByID(int $id): ?Project {
$query = 'SELECT * FROM projects WHERE id=:id LIMIT 1';
$query = '
SELECT
id,
title,
description_en,
description_no,
gitea_link,
issue_board_link,
wiki_link,
programming_languages,
technologies,
keywords,
license,
logo_url,
is_hidden
FROM project
WHERE id = :id
';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':id', $id, \PDO::PARAM_INT);
$statement->bindValue(':id', $id, \PDO::PARAM_INT);
$statement->execute();
$dbProj = $statement->fetch();
@@ -46,87 +92,139 @@ class ProjectManager {
return new Project(
$dbProj['id'],
$dbProj['name'],
$dbProj['description'],
$dbProj['active'],
$dbProj['title'],
$dbProj['description_en'],
$dbProj['description_no'],
$dbProj['gitea_link'],
$dbProj['issue_board_link'],
$dbProj['wiki_link'],
$dbProj['programming_languages'],
$dbProj['technologies'],
$dbProj['keywords'],
$dbProj['license'],
$dbProj['logo_url'],
(bool) $dbProj['is_hidden']
);
}
// TODO: groupid
/**
* @return Project[]
*/
public function getByOwner(string $uname): array {
$query = 'SELECT projectid FROM projectmembers WHERE uname=:uname';
public function getByMaintainer(string $uname): array {
$query = '
SELECT
project.id,
project.title
project.description_en,
project.description_no,
project.gitea_link,
project.issue_board_link,
project.wiki_link,
project.programming_languages,
project.technologies,
project.keywords,
project.license,
project.logo_url,
project.is_hidden
FROM project_maintainer
JOIN project ON project.id = project_maintainer.project_id
WHERE project_maintainer.uname = :uname
';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':uname', $uname, \PDO::PARAM_STR);
$statement->bindValue(':uname', $uname, \PDO::PARAM_STR);
$statement->execute();
$projectIDs = $statement->fetchAll();
$projects = [];
foreach ($projectIDs as $id) {
$id = $id['projectid'];
$query = 'SELECT * FROM projects WHERE id=:id';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':id', $id, \PDO::PARAM_INT);
$statement->execute();
foreach ($statement->fetchAll() as $dbProj) {
$project = new Project(
$result = array_map(
function ($dbProj) {
return new Project(
$dbProj['id'],
$dbProj['name'],
$dbProj['description'],
$dbProj['active'],
$dbProj['title'],
$dbProj['description_en'],
$dbProj['description_no'],
$dbProj['gitea_link'],
$dbProj['issue_board_link'],
$dbProj['wiki_link'],
$dbProj['programming_languages'],
$dbProj['technologies'],
$dbProj['keywords'],
$dbProj['license'],
$dbProj['logo_url'],
(bool) $dbProj['is_hidden']
);
$projects[] = $project;
}
}
},
$statement->fetchAll()
);
return $projects;
return $result;
}
/**
* @return array<int,array>
* @return ProjectMaintainer[]
*/
public function getProjectMembers(int $id): array {
$query = 'SELECT * FROM projectmembers WHERE projectid=:id';
public function getProjectMaintainers(int $project_id): array {
$query = '
SELECT
project_maintainer.uname,
project_maintainer.name,
project_maintainer.email,
project_maintainer.is_organizer
FROM project_maintainer
WHERE project_maintainer.project_id = :id
';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':id', $id, \PDO::PARAM_STR);
$statement->bindValue(':id', $project_id, \PDO::PARAM_STR);
$statement->execute();
$members = [];
foreach ($statement->fetchAll() as $dbUsr) {
$members[] = [
'name' => $dbUsr['name'],
'uname' => $dbUsr['uname'],
'mail' => $dbUsr['mail'],
'role' => $dbUsr['role'],
'lead' => $dbUsr['lead'],
'owner' => $dbUsr['owner'],
];
}
$result = array_map(
function ($dbUsr) {
return new ProjectMaintainer(
$dbUsr['uname'],
$dbUsr['name'],
$dbUsr['email'],
(bool)$dbUsr['is_organizer']
);
},
$statement->fetchAll()
);
return $members;
return $result;
}
/**
* @return array<string,mixed>
* @return ProjectMaintainer[]
*/
public function getProjectOwner(int $id): array {
$query = 'SELECT * FROM projectmembers WHERE (projectid=:id AND owner=1)';
public function getProjectOrganizers(int $project_id): array {
$query = '
SELECT
project_maintainer.uname,
project_maintainer.name,
project_maintainer.email,
project_maintainer.is_organizer
FROM project_maintainer
WHERE
project_maintainer.project_id = :id
AND project_maintainer.is_organizer = True
';
$statement = $this->pdo->prepare($query);
$statement->bindParam(':id', $id, \PDO::PARAM_STR);
$statement->bindValue(':id', $project_id, \PDO::PARAM_STR);
$statement->execute();
$dbOwner = $statement->fetch();
$result = array_map(
function ($dbUsr) {
return new ProjectMaintainer(
$dbUsr['uname'],
$dbUsr['name'],
$dbUsr['email'],
(bool)$dbUsr['is_organizer']
);
},
$statement->fetchAll()
);
return [
'name' => $dbOwner['name'],
'uname' => $dbOwner['uname'],
'mail' => $dbOwner['mail'],
'role' => $dbOwner['role'],
'lead' => $dbOwner['lead'],
'owner' => $dbOwner['owner'],
];
return $result;
}
}
+1 -1
View File
@@ -57,7 +57,7 @@ class SimpleEvent extends Event {
}
public function getURL(): string {
return '/hendelser/info.php?id=' . $this->id;
return '/hendelser/info.php?id='.$this->id;
}
public function getImageURL(): string {
+10 -10
View File
@@ -34,27 +34,27 @@ class BrettspillEvent extends Event {
public function getDescription(): array {
return [
'Er du en hardcore brettspillentusiast eller en nybegynner som har så vidt spilt ludo? '
. 'Da er vår brettspillkveld noe for deg! '
. 'Vi tar ut et par spill fra vårt samling of spiller så mye vi orker. Kom innom!',
.'Da er vår brettspillkveld noe for deg! '
.'Vi tar ut et par spill fra vårt samling of spiller så mye vi orker. Kom innom!',
'',
'## Vår samling',
'',
'* Dominion\\*',
'* Dominion\*',
'* Three cheers for master',
'* Avalon',
'* Hanabi',
'* Cards aginst humanity\\*',
'* Cards aginst humanity\*',
'* Citadels',
'* Munchkin\\*\\*',
'* Exploding kittens\\*\\*',
'* Munchkin\*\*',
'* Exploding kittens\*\*',
'* Aye dark overlord',
'* Settlers of catan\\*',
'* Risk\\*\\*',
'* Settlers of catan\*',
'* Risk\*\*',
'* 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',
];
}
@@ -1,31 +0,0 @@
<?xml version="1.0"?>
<clientConfig version="1.1">
<emailProvider id="pvv.ntnu.no">
<domain>pvv.ntnu.no</domain>
<domain>pvv.org</domain>
<displayName>Programvareverkstedet</displayName>
<incomingServer type="imap">
<hostname>imap.pvv.ntnu.no</hostname>
<port>993</port>
<socketType>SSL</socketType>
<username>%EMAILLOCALPART%</username>
<authentication>password-cleartext</authentication>
</incomingServer>
<outgoingServer type="smtp">
<hostname>smtp.pvv.ntnu.no</hostname>
<port>587</port>
<socketType>STARTTLS</socketType>
<username>%EMAILLOCALPART%</username>
<authentication>password-cleartext</authentication>
<useGlobalPreferredServer>true</useGlobalPreferredServer>
</outgoingServer>
<documentation url="https://www.pvv.ntnu.no/pvv/Drift/Mail/IMAP_POP3">
<descr lang="en">Setup programvareverkstedet email user with IMAP or POP3</descr>
<descr lang="nb">Sett opp programvareverkstedet email bruker med IMAP eller POP3</descr>
</documentation>
</emailProvider>
</clientConfig>
-3
View File
@@ -1,3 +0,0 @@
{
"m.server": "matrix.pvv.ntnu.no:443"
}
+1 -1
View File
@@ -25,4 +25,4 @@ $statement->execute();
header('Location: ' . $_SERVER['HTTP_REFERER']);
?>
<a href=".?page=1">Om du ikke ble omdirigert tilbake klikk her</a>
<a href=".?page=1">Om du ikke ble omdirigert tilbake klikk her</a>
+32 -15
View File
@@ -70,26 +70,43 @@ if ($start_date >= $stop_date) {
if ($id == 0) {
$query = 'INSERT INTO events (name, start, stop, organiser, location, description) VALUES (:title, :start, :stop, :organiser, :loc, :desc)';
$query = '
INSERT INTO
events(name, start, stop, organiser, location, description)
VALUES
(:title, :start, :stop, :organiser, :loc, :desc)
';
$statement = $pdo->prepare($query);
$statement->bindParam(':title', $title, PDO::PARAM_STR);
$statement->bindParam(':desc', $desc, PDO::PARAM_STR);
$statement->bindParam(':start', $start, PDO::PARAM_STR);
$statement->bindParam(':stop', $stop, PDO::PARAM_STR);
$statement->bindParam(':organiser', $organiser, PDO::PARAM_STR);
$statement->bindParam(':loc', $location, PDO::PARAM_STR);
$statement->bindValue(':title', $title, PDO::PARAM_STR);
$statement->bindValue(':desc', $desc, PDO::PARAM_STR);
$statement->bindValue(':start', $start, PDO::PARAM_STR);
$statement->bindValue(':stop', $stop, PDO::PARAM_STR);
$statement->bindValue(':organiser', $organiser, PDO::PARAM_STR);
$statement->bindValue(':loc', $location, PDO::PARAM_STR);
} else {
$query = 'UPDATE events SET name=:title, start=:start, stop=:stop, organiser=:organiser, location=:loc, description=:desc WHERE id=:id';
$query = '
UPDATE
events
SET
name = :title,
start = :start,
stop = :stop,
organiser = :organiser,
location = :loc,
description = :desc
WHERE
id = :id
';
$statement = $pdo->prepare($query);
$statement->bindParam(':title', $title, PDO::PARAM_STR);
$statement->bindParam(':desc', $desc, PDO::PARAM_STR);
$statement->bindParam(':start', $start, PDO::PARAM_STR);
$statement->bindParam(':stop', $stop, PDO::PARAM_STR);
$statement->bindParam(':organiser', $organiser, PDO::PARAM_STR);
$statement->bindParam(':loc', $location, PDO::PARAM_STR);
$statement->bindParam(':id', $id, PDO::PARAM_INT);
$statement->bindValue(':title', $title, PDO::PARAM_STR);
$statement->bindValue(':desc', $desc, PDO::PARAM_STR);
$statement->bindValue(':start', $start, PDO::PARAM_STR);
$statement->bindValue(':stop', $stop, PDO::PARAM_STR);
$statement->bindValue(':organiser', $organiser, PDO::PARAM_STR);
$statement->bindValue(':loc', $location, PDO::PARAM_STR);
$statement->bindValue(':id', $id, PDO::PARAM_INT);
}
$statement->execute();
+12 -14
View File
@@ -44,23 +44,21 @@ if (!($isAdmin | $projectGroup | $activityGroup)) {
<main>
<h2>Administrasjon</h2>
<ul class="tools">
<?php
if ($isAdmin | $activityGroup) {
echo '<li><a class="btn" href="aktiviteter/?page=1">Aktiviteter/Hendelser</a></li>';
}
<?php if ($isAdmin | $activityGroup) : ?>
<li><a class="btn" href="aktiviteter/?page=1">Aktiviteter/Hendelser</a></li>
<?php endif ?>
if ($isAdmin | $projectGroup) {
echo '<li><a class="btn" href="prosjekter/">Prosjekter</a></li>';
}
<?php if ($isAdmin | $projectGroup) : ?>
<li><a class="btn" href="prosjekter/?page=1">Prosjekter</a></li>
<?php endif ?>
if ($isAdmin) {
echo '<li><a class="btn" href="motd/">Dagens melding</a></li>';
}
<?php if ($isAdmin) : ?>
<li><a class="btn" href="motd/">Dagens melding</a></li>
<?php endif ?>
if ($isAdmin) {
echo '<li><a class="btn" href="brukere/">Brukerrettigheter</a></li>';
}
?>
<?php if ($isAdmin) : ?>
<li><a class="btn" href="brukere/">Brukerrettigheter</a></li>
<?php endif ?>
<ul>
</main>
</body>
+2 -2
View File
@@ -56,10 +56,10 @@ $motd = $motdfetcher->getMOTD();
<form action="update.php", method="post">
<p class="subtitle no-chin">Tittel</p>
<p class="subnote">Ikke nødvendig</p>
<input type="text" name="title" value="<?php echo $motd['title']; ?>" class="boxinput" style="width:66%;"><br>
<input type="text" name="title" value="<?php echo $motd->getTitle(); ?>" class="boxinput" style="width:66%;"><br>
<p class="subtitle no-chin">Innhold (<i>markdown</i>)</p>
<textarea name="content" style="width:100%" rows="8" class="boxinput"><?php echo implode("\n", $motd['content']); ?></textarea>
<textarea name="content" style="width:100%" rows="8" class="boxinput"><?php echo $motd->getContentAsString(); ?></textarea>
<div style="margin-top: 2em;">
<hr class="ruler">
+3 -2
View File
@@ -18,11 +18,12 @@ 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->bindValue(':projectID', $projectID, PDO::PARAM_INT);
$statement->execute();
header('Location: ' . $_SERVER['HTTP_REFERER']);
?>
<a href=".?page=1">Om du ikke ble omdirigert tilbake klikk her</a>
<a href=".?page=1">Om du ikke ble omdirigert tilbake klikk her</a>
+13 -8
View File
@@ -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;
}
}
+5 -5
View File
@@ -43,7 +43,7 @@ if(isset($_POST['organiser'])){
// filter
$projects = array_values(array_filter(
$projects,
static fn($project) => preg_match('/.*' . $filterTitle . '.*/i', $project->getName())
static fn($project) => preg_match('/.*' . $filterTitle . '.*/i', $project->getTitle())
));
?>
<!DOCTYPE html>
@@ -87,17 +87,17 @@ $projects = array_values(array_filter(
$project = $projects[$i];
$projectID = $project->getID();
$owner = $projectManager->getProjectOwner($projectID);
$organizers = $projectManager->getProjectOrganizers($projectID);
?>
<li>
<div class="event admin">
<div class="event-info">
<h3 class="no-chin"><?php echo $project->getName() . ' (ID: ' . $projectID . ')'; ?></h3>
<p class="subnote"><?php echo 'Organisert av: ' . $owner['name']; ?></p>
<h3 class="no-chin"><?php echo $project->getTitle() . ' (ID: ' . $projectID . ')'; ?></h3>
<p class="subnote"><?php echo 'Organisert av: ' . implode(',', array_map(function($org) { return $org->getName(); }, $organizers)); ?></p>
<?php
$Parsedown = new Parsedown();
echo $Parsedown->text(implode("\n", $project->getDescription()));
echo $Parsedown->text(implode("\n", $project->getDescriptionNo()));
?>
</div>
+43 -17
View File
@@ -37,39 +37,65 @@ $active = ($_POST['active'] ?? false);
if ($id == 0) {
$query = 'INSERT INTO projects (name, description, active) VALUES (:title, :desc, :active)';
$query = '
INSERT INTO
projects(name, description, active)
VALUES
(:title, :desc, :active)
';
$statement = $pdo->prepare($query);
$statement->bindParam(':title', $title, PDO::PARAM_STR);
$statement->bindParam(':desc', $desc, PDO::PARAM_STR);
$statement->bindParam(':active', $active, PDO::PARAM_BOOL);
$statement->bindValue(':title', $title, PDO::PARAM_STR);
$statement->bindValue(':desc', $desc, PDO::PARAM_STR);
$statement->bindValue(':active', $active, PDO::PARAM_BOOL);
$statement->execute();
$ownerQuery = 'INSERT INTO projectmembers (projectid, name, uname, mail, role, lead, owner) VALUES (last_insert_rowid(), :owner, :owneruname, :owneremail, \'Prosjektleder\', 1, 1)';
$ownerQuery = '
INSERT INTO
projectmembers(projectid, name, uname, mail, role, lead, owner)
VALUES
(last_insert_rowid(), :owner, :owneruname, :owneremail, \'Prosjektleder\', 1, 1)
';
$statement = $pdo->prepare($ownerQuery);
$statement->bindParam(':owner', $name, PDO::PARAM_STR);
$statement->bindParam(':owneruname', $uname, PDO::PARAM_STR);
$statement->bindParam(':owneremail', $mail, PDO::PARAM_STR);
$statement->bindValue(':owner', $name, PDO::PARAM_STR);
$statement->bindValue(':owneruname', $uname, PDO::PARAM_STR);
$statement->bindValue(':owneremail', $mail, PDO::PARAM_STR);
$statement->execute();
} else {
$query = 'UPDATE projects SET name=:title, description=:desc, active=:active WHERE id=:id';
$query = '
UPDATE
projects
SET
name = :title,
description = :desc,
active = :active
WHERE
id = :id
';
$statement = $pdo->prepare($query);
$statement->bindParam(':title', $title, PDO::PARAM_STR);
$statement->bindParam(':desc', $desc, PDO::PARAM_STR);
$statement->bindParam(':active', $active, PDO::PARAM_BOOL);
$statement->bindParam(':id', $id, PDO::PARAM_INT);
$statement->bindValue(':title', $title, PDO::PARAM_STR);
$statement->bindValue(':desc', $desc, PDO::PARAM_STR);
$statement->bindValue(':active', $active, PDO::PARAM_BOOL);
$statement->bindValue(':id', $id, PDO::PARAM_INT);
$statement->execute();
$query = 'UPDATE projectmembers SET name=:name, uname=:uname, mail=:mail';
$query = '
UPDATE
projectmembers
SET
name = :name,
uname = :uname,
mail = :mail
';
$statement = $pdo->prepare($query);
$statement->bindParam(':name', $name, PDO::PARAM_STR);
$statement->bindParam(':uname', $uname, PDO::PARAM_STR);
$statement->bindParam(':mail', $mail, PDO::PARAM_STR);
$statement->bindValue(':name', $name, PDO::PARAM_STR);
$statement->bindValue(':uname', $uname, PDO::PARAM_STR);
$statement->bindValue(':mail', $mail, PDO::PARAM_STR);
$statement->execute();
}
-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17.01 14.258" enable-background="new 0 0 17.01 14.258"><style type="text/css">.st0{fill:#004166;}</style><path class="st0" d="M14.009 8.551l.01-.019.005-.023.003-.014-.001-.006-.004-.022-.007-.021-.002-.006-4.629-8.382-.006-.006-.009-.013-.01-.01-.012-.009-.006-.005-.006-.002-.013-.005-.015-.004-.016-.002-.006-.002h-1.642l-.015.003-.021.004-.02.01-.017.012-.015.017-.009.011-4.233 7.425-.002.006-.007.021-.005.022-.001.006v2.86l.003.014.005.023.009.019.012.018.017.015.011.009.008.003.009.004.039.008h1.421l.038-.008.009-.004.031-.021.001-.001.019-.025 1.017-1.844h5.067l1.007 1.844.019.025.002.001.03.02.009.004.038.008h1.125l.04-.008.011-.005.023-.015.01-.009.015-.023.006-.011.002-.004.649-1.869.009-.014zm-.843 1.734h-.859l.582-1.678h.859l-.582 1.678zm-1.996-1.855l-.015-.018-.017-.012-.019-.01-.023-.005-.014-.003h-5.2l-.015.003-.023.005-.019.01-.018.012-.015.018-.01.011-1.017 1.844h-1.161l5.679-9.944 4.44 8.041h-.846l-3.426-6.236-.006-.007-.014-.017-.018-.015-.006-.006-.012-.003-.022-.007-.021-.002-.021.002-.021.007-.012.003-.007.006-.017.014-.014.018-.006.006-2.776 5.035-.002.005-.006.021-.005.023-.001.005.003.014.005.024.009.018.012.018.017.015.012.01.007.003.008.003.039.007h3.788l.039-.008.009-.004.007-.003.011-.009.018-.015.012-.017.01-.019.005-.023.003-.015-.001-.006-.004-.022-.007-.022-.002-.005-1.842-3.315-.037-.038.768-1.392 3.335 6.073-.572 1.649-.936-1.714-.01-.011zm-7.647-.863l4.186-7.342h1.383l-5.569 9.75v-2.408zm3.619-1.127h2.653l.378.682h-3.407l.376-.682zm.124-.225l1.198-2.173 1.206 2.173h-2.404zM1.197 14.258l-.13-.325h-.729l-.13.325h-.208l.588-1.464h.226l.591 1.464h-.208zm-.494-1.276l-.312.788h.621l-.309-.788zM2.123 14.258v-1.464h.959v.162h-.776v.472h.762v.162h-.762v.667h-.183zM4.22 14.258v-1.302h-.463v-.162h1.111v.162h-.465v1.302h-.183zM5.637 14.258v-1.464h.959v.162h-.777v.472h.762v.162h-.762v.505h.777v.162h-.959zM8.261 14.258l-.373-.582h-.292v.582h-.183v-1.464h.588c.268 0 .459.171.459.441 0 .263-.18.408-.38.426l.395.597h-.214zm.011-1.023c0-.165-.119-.279-.292-.279h-.384v.56h.384c.173 0 .292-.117.292-.281zM9.275 14.258v-1.464h.182v1.302h.681v.162h-.863zM10.613 13.527c0-.433.292-.757.727-.757.432 0 .727.325.727.757 0 .433-.294.758-.727.758-.435-.001-.727-.326-.727-.758zm1.264 0c0-.342-.211-.595-.538-.595-.329 0-.538.252-.538.595 0 .34.209.595.538.595.328 0 .538-.255.538-.595zM12.644 13.527c0-.454.336-.757.753-.757.259 0 .439.114.569.275l-.145.09c-.092-.119-.248-.202-.424-.202-.321 0-.564.246-.564.595 0 .347.244.597.564.597.176 0 .321-.086.393-.158v-.299h-.503v-.162h.685v.529c-.136.151-.336.252-.575.252-.417-.001-.753-.306-.753-.76zM14.84 14.258v-1.464h.182v1.464h-.182zM15.699 13.527c0-.45.332-.757.753-.757.259 0 .439.125.555.29l-.154.086c-.083-.123-.235-.213-.402-.213-.321 0-.564.246-.564.595 0 .347.244.595.564.595.167 0 .318-.088.402-.213l.156.086c-.123.167-.299.29-.558.29-.421-.002-.752-.309-.752-.759z"/></svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

-73
View File
@@ -1,73 +0,0 @@
ul#webmail {
margin-top: 0;
margin-left: auto;
margin-right: auto;
table-layout: fixed;
display: table;
width: 100%;
padding: 0;
}
ul#webmail li {
display: table-cell;
text-align: center;
}
ul#webmail li .mailname {
font-size: 1.2em;
}
@media all and (min-width: 980px) {
ul#webmail {
max-width: 1280px;
}
ul#webmail li {
display: table-cell;
text-align: center;
}
}
@media all and (max-width: 980px) {
ul#webmail {
max-width: 650px;
}
ul#webmail li {
display: table-row;
text-align: center;
}
}
ul#webmail li div {
position: relative;
background: white;
margin: 1em 1em;
box-shadow: rgba(0,0,0,.3) 0 .1em .17em;
border-radius: .5rem;
cursor: pointer;
}
ul#webmail li:hover div {
box-shadow: rgba(0,0,0,.5) 0 .15em .2em;
}
ul#webmail li div a {
padding-top: 10em;
display: block;
text-decoration: none;
color: black;
}
ul#webmail li#afterlogic div {
background: white url('afterlogic.png') no-repeat;
background: white url('afterlogic.svg') no-repeat;
background-size: auto 8em;
background-position: 50% 60%;
}
ul#webmail li#squirrelmail div {
background: white url('squirrelmail.png') no-repeat;
background-size: auto 10em;
background-position: 50% 0;
}
ul#webmail li#roundcube div {
background: white url('roundcube.png') no-repeat;
background-size: auto 10em;
background-position: 50% 0;
}
ul#webmail li#rainloop div {
background: white url('rainloop.png') no-repeat;
background-size: auto 10em;
background-position: 50% 0;
}
+80 -133
View File
@@ -1,173 +1,120 @@
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 */
form .wide {
min-width: 66%;
min-width: 100%;
}
form .tall {
min-height: calc(100vh - 28em);
}
@media screen and (max-width: 50rem) {
form .wide {
width: calc(100% - 2em);
margin: 0 1em;
}
}
form .input-group {
display: flex;
}
form .input-group-item {
flex: 1;
margin: 0 0.5em;
}
form .input-group-item input.boxinput,textarea {
width: 100%;
box-sizing: border-box;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

+64 -92
View File
@@ -4,116 +4,88 @@ main {
width: 100vw;
}
.serviceWrapper {
width: 80%;
.serviceGrid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 1em;
margin: auto auto;
margin-top: 4em;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1.5rem;
padding: 2rem;
margin: 50px 2%;
}
.categoryContainer {
border: 4px solid #002244;
border-radius: 5px;
box-shadow : 0 0 20px #002244;
margin-bottom: 20px;
}
.categoryLabel {
padding-top: 5px;
background-color: #002244;
color: white;
padding-left: 10px;
font-family: monospace;
font-size: 24px;
}
.service {
border: 2px solid #002244;
border-radius: 5px;
padding: 10px;
margin: 10px;
/* Base styles for all cards */
.baseServiceCard {
display: flex;
flex-direction: row;
gap: 0.6rem;
padding: 1rem;
border-radius: 14px;
box-shadow: 0 8px 8px rgba(0, 0, 0, 0.3);
}
/* Category Title Card Styling */
.categoryTitleCard {
align-items: center;
justify-content: center;
text-align: center;
box-shadow: none;
min-height: 140px;
}
.categoryTitle {
margin: 0;
font-weight: bold;
font-size: 1.5rem;
}
/* Service Card Styling */
.serviceCard {
display: flex;
gap: 0.6rem;
padding: 1rem;
border-radius: 14px;
height: fit-content;
width: fit-content;
}
/*.serviceCard:hover {
transform: translateY(-4px);
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.12);
}*/
.serviceContent {
flex-grow: 1;
margin-right: 4%;
flex: 1;
}
.serviceTitle {
margin: 0.2em !important;
margin: 0 0 0.5rem;
font-size: 1.2rem;
}
.serviceDescription {
margin-top: 0px !important;
margin: 0 0 1rem;
font-size: 0.95rem;
line-height: 1.5;
}
.serviceDescription::before {
content: " - ";
font-size: 18px;
display: inline;
.serviceLink a {
color: #0066cc;
text-decoration: none;
font-weight: 600;
}
.serviceLink {
width: 70%;
padding-bottom: 5px;
border-radius: 5px;
border: 2px solid #002244;
padding: 7px;
margin-top: 4px;
}
.serviceLink > a {
margin-bottom: 10px;
word-break: break-word;
.serviceLink a:hover {
text-decoration: underline;
}
.serviceImage {
flex-shrink: 1;
width: 100px;
height: 100px;
margin: auto auto;
}
@media (max-width: 800px) {
.serviceWrapper {
grid-template-columns: 1fr;
}
.categoryContainer {
width: 100%;
}
}
@media (max-width: 480px) {
.categoryContainer {
border-radius: unset;
border: unset;
box-shadow: unset;
margin-bottom: unset;
}
.serviceWrapper {
width: 100%;
}
.serviceImage {
width: 25%;
height: auto;
}
.serviceContent {
width: 50%;
}
}
@media (max-width: 360px) {
.serviceContent {
font-size: 14px;
}
width: 56px;
height: 56px;
object-fit: contain;
align-self: flex-start;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

-13
View File
File diff suppressed because one or more lines are too long
-8
View File
@@ -1,8 +0,0 @@
/*!
* chartjs-adapter-moment v1.0.0
* https://www.chartjs.org
* (c) 2021 chartjs-adapter-moment Contributors
* Released under the MIT license
*/
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("moment"),require("chart.js")):"function"==typeof define&&define.amd?define(["moment","chart.js"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).moment,e.Chart)}(this,(function(e,t){"use strict";function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var f=n(e);const a={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};t._adapters._date.override("function"==typeof f.default?{_id:"moment",formats:function(){return a},parse:function(e,t){return"string"==typeof e&&"string"==typeof t?e=f.default(e,t):e instanceof f.default||(e=f.default(e)),e.isValid()?e.valueOf():null},format:function(e,t){return f.default(e).format(t)},add:function(e,t,n){return f.default(e).add(t,n).valueOf()},diff:function(e,t,n){return f.default(e).diff(f.default(t),n)},startOf:function(e,t,n){return e=f.default(e),"isoWeek"===t?(n=Math.trunc(Math.min(Math.max(0,n),6)),e.isoWeekday(n).startOf("day").valueOf()):e.startOf(t).valueOf()},endOf:function(e,t){return f.default(e).endOf(t).valueOf()}}:{})}));
//# sourceMappingURL=chartjs-adapter-moment.min.js.map
-145
View File
@@ -1,145 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Inngangsverkstedet</title>
<style>
body {
text-align: center;
width: 80vw;
margin: auto auto;
}
#graphDiv {
display: flex;
flex-direction: column;
}
</style>
</head>
<body>
<h2>En kort analyse av nerders døgnrytme i deres naturlige habitat, PVV</h2>
<div id="graphDiv">
<h4>Siste 24 timer</h4>
<canvas id="doorGraphDay"></canvas>
<h4>Siste 7 dager</h4>
<canvas id="doorGraphWeek"></canvas>
</div>
<script src="chart.min.js"></script>
<script src="moment.js"></script>
<script src="chartjs-adapter-moment.js"></script>
<script>
const graphElDay = document.getElementById("doorGraphDay");
const graphElWeek = document.getElementById("doorGraphWeek");
const XHR = new XMLHttpRequest();
const url="/door/?period=week";
XHR.open("GET", url);
XHR.send();
XHR.onreadystatechange = ()=>{
if (XHR.readyState == 4 && XHR.status == 200) {
console.log("Response 200 from API")
response = JSON.parse(XHR.responseText); //Should be try-catched?
if (response.status != "OK") {
console.log("Error when connecting to API.");
return
} else {
const allDatapoints = response.entries;
console.log("Success, " + allDatapoints.length + " datapoints received.");
const dayDatapoints = getLastDay(allDatapoints);
displayLineDiagram(graphElDay, dayDatapoints, "hour");
displayLineDiagram(graphElWeek, allDatapoints, "day");
}
}
}
function getLastDay(data) {
let date = new Date();
let curTime = date.getTime();
let targetTime = parseInt(curTime/1e3) - (60*60*24);
let i;
for (i = 0; i < data.length; i++) {
if (data[i].time < targetTime) {
break;
}
}
return data.slice(0, i);
}
function displayLineDiagram(canv, data, timeunit) {
let ctx = canv.getContext("2d");
let dotColor = data.map(entry => entry.open ? "rgb(10, 150, 10)" : "rgb(200, 100, 100)");
let chart = new Chart(ctx, {
type: 'line',
data: {
labels: data.map(entry=> 1e3 * entry.time),
datasets: [{
data: data.map(entry => entry.open),
stepped: "before",
borderColor: dotColor,
backgroundColor: dotColor
}],
},
options: {
scales: {
xAxis: {
type: "time",
time: {
unit: timeunit
},
},
yAxis: {
suggestedMin: -0.1,
suggestedMax: 1.1,
grid: {display: false},
ticks: {
callback: function(label, index, labels) {
if (label == 0) {
return "Stengt";
} else if (label == 1) {
return "Åpent";
} else {
return "";
}
}
}
}
},
plugins: {
legend: {
display: false
},
tooltip: {
callbacks: {
label: function(tooltipItem) {
const value = tooltipItem.formattedValue;
if (value == 0) {
return "Stengt";
} else if (value == 1) {
return "Åpent";
} else {
return "";
}
}
}
}
}
}
});
}
</script>
</body>
</html>
+186
View File
@@ -0,0 +1,186 @@
<?php
require_once dirname(__DIR__, 2) . implode(\DIRECTORY_SEPARATOR, ['', 'inc', 'include.php']);
?>
<!doctype html>
<html lang="en">
<head>
<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/nav.css">
<link rel="stylesheet" href="../css/events.css">
<meta name="theme-color" content="#024" />
<title>Inngangsverkstedet</title>
<style>
body {
text-align: center;
width: 80vw;
margin: auto auto;
}
#graphDiv {
display: flex;
flex-direction: column;
}
.graphbox {
margin: 20px;
padding: 10px;
border: 5px solid #00407F;
border-radius: 10px;
}
</style>
</head>
<body>
<nav id="navbar" class="">
<?php echo navbar(1, ''); ?>
<?php echo loginbar(null, $pdo); ?>
</nav>
<main style="margin: 5em 0 2em 0;">
<h2>En kort analyse av nerders døgnrytme i deres naturlige habitat, PVV</h2>
<div id="graphDiv">
<h4>Siste 24 timer</h4>
<div class="graphbox">
<canvas id="doorGraphDay"></canvas>
</div>
<h4>Siste 7 dager</h4>
<div class="graphbox">
<canvas id="doorGraphWeek"></canvas>
</div>
</div>
</main>
<script
src="https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js"
integrity="sha384-jb8JQMbMoBUzgWatfe6COACi2ljcDdZQ2OxczGA3bGNeWe+6DChMTBJemed7ZnvJ"
crossorigin="anonymous"
></script>
<script
src="https://cdn.jsdelivr.net/npm/moment@2.30.1/moment.min.js"
integrity="sha384-+EEFFjsGn4BnW70Nv0OvoMe1VZuqS4xvx90V2MTeuYUUZSEabg7FSMWl6s2DJTAO"
crossorigin="anonymous"
></script>
<script
src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@1.0.1/dist/chartjs-adapter-moment.min.js"
integrity="sha384-s5cwu7c1MxOfC90RGRDWeB53/7VpDTxXi0YxKJF5y9oKA99+UYxMk0qvlqso188s"
crossorigin="anonymous"
></script>
<script>
const graphElDay = document.getElementById("doorGraphDay");
const graphElWeek = document.getElementById("doorGraphWeek");
const XHR = new XMLHttpRequest();
const url = "/door/?period=week";
XHR.open("GET", url);
XHR.send();
XHR.onreadystatechange = () => {
if (XHR.readyState == 4 && XHR.status == 200) {
console.log("Response 200 from API");
response = JSON.parse(XHR.responseText); //Should be try-catched?
if (response.status != "OK") {
console.log("Error when connecting to API.");
return;
} else {
const allDatapoints = response.entries;
console.log(
"Success, " + allDatapoints.length + " datapoints received.",
);
const dayDatapoints = getLastDay(allDatapoints);
displayLineDiagram(graphElDay, dayDatapoints, "hour");
displayLineDiagram(graphElWeek, allDatapoints, "day");
}
}
};
function getLastDay(data) {
let date = new Date();
let curTime = date.getTime();
let targetTime = parseInt(curTime / 1e3) - 60 * 60 * 24;
let i;
for (i = 0; i < data.length; i++) {
if (data[i].time < targetTime) {
break;
}
}
return data.slice(0, i);
}
function displayLineDiagram(canv, data, timeunit) {
let ctx = canv.getContext("2d");
let dotColor = data.map((entry) =>
entry.open ? "rgb(10, 150, 10)" : "rgb(200, 100, 100)",
);
let chart = new Chart(ctx, {
type: "line",
data: {
labels: data.map((entry) => 1e3 * entry.time),
datasets: [
{
data: data.map((entry) => entry.open),
stepped: "before",
segment: {
borderColor: (ctx) =>
ctx.p0.parsed.y === 1
? "rgb(10, 150, 10)"
: "rgb(200, 100, 100)",
},
borderColor: dotColor,
backgroundColor: dotColor,
},
],
},
options: {
scales: {
x: {
type: "time",
display: true,
time: {
unit: timeunit,
},
ticks: {
display: true,
source: "data",
},
grid: {
display: true,
},
},
y: {
suggestedMin: -0.1,
suggestedMax: 1.1,
grid: { display: false },
ticks: {
callback: (label, index, labels) =>
label === 1 ? "Åpent" : label === 0 ? "Stengt" : "",
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
label: (tooltipItem) =>
tooltipItem.formattedValue === "1" ? "Åpent" : "Stengt",
},
},
},
},
});
}
</script>
</body>
</html>
+29 -11
View File
@@ -31,9 +31,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$period = (string) htmlspecialchars($_GET['period']);
if ($period == 'day') {
$startTime = time() - (60 * 60 * 24);
$startTime = (new \DateTimeImmutable())
->setTimestamp(time())
->sub(new \DateInterval('P1D'));
} elseif ($period == 'week') {
$startTime = time() - (60 * 60 * 24 * 7);
$startTime = (new \DateTimeImmutable())
->setTimestamp(time())
->sub(new \DateInterval('P1W'));
} else {
echo '{"status": "error", "message": "Invalid period"}';
exit;
@@ -46,16 +50,28 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
echo json_encode([
'status' => 'OK',
'entries' => $lines,
'status' => 'OK',
'entries' => array_map(
function ($line) {
return [
'time' => $line->getTimestamp(),
'open' => $line->isOpen(),
];
},
$lines
),
]);
} else {
// Only last entry
$line = (object) $door->getCurrent();
$line = $door->getCurrent();
if (is_null($line)) {
echo '{"status": "error", "message": "No door data"}';
exit;
}
echo json_encode([
'status' => 'OK',
'time' => $line->time,
'open' => $line->open,
'status' => 'OK',
'time' => $line->getTimestamp(),
'open' => $line->isOpen(),
]);
}
}
@@ -76,7 +92,9 @@ function handleSetState(): void {
exit;
}
$door->createEvent((int) $event->time, $event->isDoorOpen ? 1 : 0);
$time = (new \DateTimeImmutable())->setTimestamp((int) $event->time);
$door->createEvent($time, $event->isDoorOpen);
echo '{"status": "OK"}';
}
@@ -85,9 +103,9 @@ function getChanges($items) {
$res = [];
foreach ($items as $item) {
if ($item['open'] !== $prevState) {
if ($item->isOpen() !== $prevState) {
$res[] = $item;
$prevState = $item['open'];
$prevState = $item->isOpen();
}
}
-5670
View File
File diff suppressed because it is too large Load Diff
+17 -7
View File
@@ -1,4 +1,7 @@
<?php
use pvv\side\DoorStatus;
require_once dirname(__DIR__) . implode(\DIRECTORY_SEPARATOR, ['', 'inc', 'include.php']);
$translation = ['I dag', 'I morgen', 'Denne uka', 'Neste uke', 'Denne måneden', 'Neste måned'];
@@ -9,17 +12,24 @@ $motdfetcher = new pvv\side\MOTD($pdo);
$motd = $motdfetcher->getMOTD();
$door = new pvv\side\Door($pdo);
$doorEntry = (object) $door->getCurrent();
if ($doorEntry->time < (time() - 60 * 30)) {
$doorEntry = $door->getCurrent();
if (is_null($doorEntry)) {
$doorEntry = new DoorStatus(
new DateTimeImmutable('@0'),
false,
);
}
if ($doorEntry->getTimestamp() < (time() - 60 * 30)) {
$doorStateText = 'Ingen data fra dørsensor';
} else {
if ($doorEntry->open) {
if ($doorEntry->isOpen()) {
$doorStateText = 'Døren er <b>åpen</b>';
} else {
$doorStateText = 'Døren er <b>ikke åpen</b>';
}
}
$doorTime = date('H:i', $doorEntry->time);
$doorTime = $doorEntry->getTime()->format('H:i');
?>
<!DOCTYPE html>
<html lang="no">
@@ -55,7 +65,7 @@ $doorTime = date('H:i', $doorEntry->time);
<a class="btn" href="om/"><li>Om PVV</li></a>
<a class="btn focus" href="paamelding/"><li>Bli medlem!</li></a>
<a class="btn" href="https://use.mazemap.com/#config=ntnu&v=1&zlevel=2&center=10.406281,63.417093&zoom=19.5&campuses=ntnu&campusid=1&sharepoitype=poi&sharepoi=38159&utm_medium=longurl">Veibeskrivelse</li></a>
<div id="doorIndicator" class="<?php echo $doorEntry->open ? 'doorIndicator_OPEN' : 'doorIndicator_CLOSED'; ?>" onclick="location.href='/door/graph.html'">
<div id="doorIndicator" class="<?php echo $doorEntry->isOpen() ? 'doorIndicator_OPEN' : 'doorIndicator_CLOSED'; ?>" onclick="location.href='/door/graph.php'">
<p class="doorStateText"><?php echo $doorStateText; ?></p>
<p class="doorStateTime">(Oppdatert <?php echo $doorTime; ?>)</p>
</div>
@@ -110,7 +120,7 @@ $doorTime = date('H:i', $doorEntry->time);
<div class="gridl">
<?php
$title = $motd['title'];
$title = $motd->getTitle();
echo '<h1>';
if ($title == '') {
@@ -121,7 +131,7 @@ $doorTime = date('H:i', $doorEntry->time);
echo '</h1>';
$Parsedown = new Parsedown();
echo $Parsedown->text(implode("\n", $motd['content']));
echo $Parsedown->text($motd->getContentAsString());
?>
</div>
</main>
+166 -35
View File
@@ -14,31 +14,37 @@ $pdo = new PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$projectManager = new pvv\side\ProjectManager($pdo);
$new = 0;
$project_is_new = false;
if (isset($_GET['new'])) {
$new = $_GET['new'];
$project_is_new = $_GET['new'];
}
$projectID = 0;
if (isset($_GET['id'])) {
$projectID = $_GET['id'];
} elseif ($new == 0) {
} elseif (!$project_is_new) {
echo "\nID not set";
exit;
}
$project = new pvv\side\Project(
0,
'Nytt Prosjekt',
'',
$attrs['cn'][0],
$attrs['uid'][0],
$attrs['mail'][0],
1
id: 0,
title: 'Nytt Prosjekt',
description_en: null,
description_no: null,
gitea_link: null,
issue_board_link: null,
wiki_link: null,
programming_languages: null,
technologies: null,
keywords: null,
license: null,
logo_url: null
);
if ($new == 0) {
if (!$project_is_new) {
$project = $projectManager->getByID($projectID);
$owner = $projectManager->getProjectOwner($projectID);
$maintainers = $projectManager->getProjectMaintainers($projectID);
if ($owner['uname'] != $attrs['uid'][0]) {
header('HTTP/1.0 403 Forbidden');
@@ -46,6 +52,7 @@ if ($new == 0) {
exit;
}
}
?>
<!DOCTYPE html>
<html lang="no">
@@ -63,32 +70,156 @@ if ($new == 0) {
<body>
<nav>
<?php echo navbar(1, 'prosjekt'); ?>
<?php echo loginbar(null, $pdo); ?>
</nav>
<nav>
<?php echo navbar(1, 'prosjekt'); ?>
<?php echo loginbar(null, $pdo); ?>
</nav>
<main>
<h2>Nytt prosjekt</h2>
<main>
<h2>Nytt prosjekt</h2>
<form action="update.php", method="post">
<p class="subtitle no-chin">Prosjektnavn</p>
<p class="subnote">Gi prosjektet ditt et passende navn</p>
<input class="wide" type="text" name="title" value="<?php echo $project->getName(); ?>" class="boxinput"><br>
<form action="update.php", method="post">
<p class="subtitle no-chin">Prosjektnavn</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>
<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">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" style="width:100%" rows="8" class="boxinput"><?php echo implode("\n", $project->getDescription()); ?></textarea>
<div class="input-group">
<div class="input-group-item">
<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">De første to linjene blir vist på prosjektkortet, prøv å gjøre de til et fint sammendrag eller intro!</p>
<textarea name="desc_no" rows="3" class="boxinput" required><?php echo implode("\n", $project->getDescriptionNo()); ?></textarea>
</div>
<?php echo '<input type="hidden" name="id" value="' . $project->getID() . '" />'; ?>
<input type="hidden" name="active" value="1"/>
<div class="input-group-item">
<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>
<br>
<textarea name="desc_en" rows="3" class="boxinput" required><?php echo implode("\n", $project->getDescriptionEn()); ?></textarea>
</div>
</div>
<div style="margin-top: 0.2em;">
<hr class="ruler">
<input type="submit" class="btn" value="<?php echo $new ? 'Opprett prosjekt' : 'Lagre endringer'; ?>"></input>
<?php if (!$new) {?><input type="submit" class="btn" name="delete" value="Slett"></input><?php } ?>
</div>
</form>
</main>
<div class="input-group">
<div class="input-group-item">
<p class="subtitle no-chin">Gitea-link</p>
<p class="subnote">Link til prosjektet på Gitea</p>
<input
type="text"
name="gitea"
placeholder="https://git.pvv.ntnu.no/Projects/mittprosjekt"
value="<?php echo $project->getGiteaLink(); ?>"
class="boxinput"
>
</div>
<div class="input-group-item">
<p class="subtitle no-chin">Issue board-link</p>
<p class="subnote">Link til issue board på Gitea</p>
<input
type="text"
name="issue"
placeholder="https://git.pvv.ntnu.no/Projects/mittprosjekt/issues"
value="<?php echo $project->getIssueBoardLink(); ?>"
class="boxinput"
>
</div>
</div>
<div class="input-group">
<div class="input-group-item">
<p class="subtitle no-chin">Logo-URL</p>
<p class="subnote">Link til logo for prosjektet</p>
<input
type="text"
name="logo"
placeholder="https://www.pvv.ntnu.no/~user/pictures/logos/prosjektlogo.png"
value="<?php echo $project->getLogoURL(); ?>"
class="boxinput"
>
</div>
<div class="input-group-item">
<p class="subtitle no-chin">Wiki-link</p>
<p class="subnote">Link til wiki-side</p>
<input
type="text"
name="wiki"
placeholder="https://wiki.pvv.ntnu.no/wiki/Prosjekter/Mitt_Prosjekt"
value="<?php echo $project->getWikiLink(); ?>"
class="boxinput"
>
</div>
</div>
<div class="input-group">
<div class="input-group-item">
<p class="subtitle no-chin">Programmeringsspråk</p>
<p class="subnote">Hvilke programmeringsspråk brukes i prosjektet?</p>
<input
type="text"
name="programming-languages"
placeholder="php,javascript,html,css"
value="<?php echo implode("\n", $project->getProgrammingLanguages()); ?>"
class="boxinput"
>
</div>
<div class="input-group-item">
<p class="subtitle no-chin">Teknologier</p>
<p class="subnote">Hvilke teknologier brukes i prosjektet?</p>
<input
type="text"
name="technologies"
placeholder="mysql,rest,ajax"
value="<?php echo implode("\n", $project->getTechnologies()); ?>"
class="boxinput"
>
</div>
<div class="input-group-item">
<p class="subtitle no-chin">Nøkkelord</p>
<p class="subnote">Nøkkelord som beskriver prosjektet</p>
<input
type="text"
name="keywords"
placeholder="web,very-cool"
value="<?php echo implode("\n", $project->getKeywords()); ?>"
class="boxinput"
>
</div>
</div>
<div class="input-group">
<div class="input-group-item">
<p class="subtitle no-chin">Lisens</p>
<p class="subnote">Hvilken lisens bruker prosjektet?</p>
<input
type="text"
name="license"
placeholder="GPL-3.0"
value="<?php echo $project->getLicense(); ?>"
class="boxinput"
>
</div>
<div class="input-group-item">
<p class="subtitle no-chin">Skult</p>
<p class="subnote">Skal prosjektet være skjult fra prosjektindeksen?</p>
<input
type="checkbox"
name="is_hidden"
value="<?php echo $project->isHidden(); ?>"
>
</div>
</div>
<?php echo '<input type="hidden" name="id" value="' . $project->getID() . '" />'; ?>
<input type="hidden" name="active" value="1"/>
<div style="margin-top: 0.2em;">
<hr class="ruler">
<input type="submit" class="btn" value="<?php echo $project_is_new ? 'Opprett prosjekt' : 'Lagre endringer'; ?>"></input>
<?php if (!$project_is_new) {?><input type="submit" class="btn" name="delete" value="Slett"></input><?php } ?>
</div>
</form>
</main>
</body>
+58 -13
View File
@@ -71,7 +71,6 @@ $projects = $projectManager->getAll();
<br>
<center>
<a class="btn" href="edit.php?new=1">Lag prosjekt</a>
<a class="btn" href="mine.php">Mine prosjekter</a>
</center>
<br>
<?php
@@ -84,25 +83,71 @@ $projects = $projectManager->getAll();
<div class="projects-container">
<?php
$randProjects = array_rand($projects, min(6, count($projects)));
$randProjects = array_rand($projects, min(8, count($projects)));
if (!is_array($randProjects)) {
$randProjects = [$randProjects];
}
foreach ($randProjects as $i) {
$project = $projects[$i];
$owner = $projectManager->getProjectOwner($project->getID());
$organizers = $projectManager->getProjectOrganizers($project->getID());
?>
<a class="nostyle" href="info.php?id=<?php echo $project->getID(); ?>"><div class="project-card">
<div class="card-content">
<h4 class="project-title"><?php echo $project->getName(); ?></h4>
<?php
$Parsedown = new Parsedown();
echo $Parsedown->text(implode("\n", array_slice($project->getDescription(), 0, 2)));
?>
</div>
<p class="project-organizer">Organisert av <?php echo $owner['name']; ?></p>
</div></a>
<a class="nostyle" href="info.php?id=<?php echo $project->getID(); ?>">
<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">
<p class="project-description">
<?php
$Parsedown = new Parsedown();
echo $Parsedown->text(
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>
<?php endif; ?>
</div>
<footer class="project-footer">
<span class="project-organizer">
Organisert av <?php echo htmlspecialchars(implode(', ', array_map(function ($org) {
return $org->getName();
}, $organizers))); ?>
</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 } ?>
</div>
<center>
+3 -3
View File
@@ -20,7 +20,7 @@ if (!$project) {
exit;
}
$members = $projectManager->getProjectMembers($projectID);
$members = $projectManager->getProjectMaintainers($projectID);
$normal_members = $members;
foreach ($normal_members as $i => $data) {
if ($data['lead']) {
@@ -65,10 +65,10 @@ if ($attrs) {
<main class="contentsplit">
<div class="gridr">
<h2><?php echo $project->getName(); ?></h2>
<h2><?php echo $project->getTitle(); ?></h2>
<?php
$Parsedown = new Parsedown();
echo $Parsedown->text(implode("\n", $project->getDescription()));
echo $Parsedown->text(implode("\n", $project->getDescriptionEn()));
?>
</div>
-120
View File
@@ -1,120 +0,0 @@
<?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>
+142 -39
View File
@@ -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',
'programming-languages',
'technologies',
'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,114 @@ $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'];
function clean_tags(string $raw): string {
$tags = array_map('trim', explode(',', $raw));
$tags = array_filter($tags, fn($tag) => !empty($tag));
return implode(',', $tags);
}
$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 = clean_tags($_POST['programming-languages']);
$techs = clean_tags($_POST['technologies']);
$keywords = clean_tags($_POST['keywords']);
$license = $_POST['license'];
$logo = $_POST['logo'];
$is_hidden = isset($_POST['is_hidden']) ? 1 : 0;
$name = $attrs['cn'][0];
$uname = $attrs['uid'][0];
$mail = $attrs['mail'][0];
if ($id == 0) {
$query = 'INSERT INTO projects (name, description, active) VALUES (:title, :desc, 1)';
if ($id == 0) { // Create new project
$query = <<<END
INSERT INTO
project(
title,
description_no,
description_en,
gitea_link,
issue_board_link,
wiki_link,
programming_languages,
technologies,
keywords,
license,
logo_url,
is_hidden
)
VALUES
(
:title,
:desc_no,
:desc_en,
:gitea,
:issue,
:wiki,
:programming_languages,
:technologies,
:keywords,
:license,
:logo,
:is_hidden
)
END;
$statement = $pdo->prepare($query);
$statement->bindParam(':title', $title, PDO::PARAM_STR);
$statement->bindParam(':desc', $desc, PDO::PARAM_STR);
$statement->bindValue(':title', $title, PDO::PARAM_STR);
$statement->bindValue(':desc_no', $desc_no, PDO::PARAM_STR);
$statement->bindValue(':desc_en', $desc_en, PDO::PARAM_STR);
$statement->bindValue(':gitea', $gitea, PDO::PARAM_STR);
$statement->bindValue(':issue', $issue, PDO::PARAM_STR);
$statement->bindValue(':wiki', $wiki, PDO::PARAM_STR);
$statement->bindValue(':programming_languages', $langs, PDO::PARAM_STR);
$statement->bindValue(':technologies', $techs, PDO::PARAM_STR);
$statement->bindValue(':keywords', $keywords, PDO::PARAM_STR);
$statement->bindValue(':license', $license, PDO::PARAM_STR);
$statement->bindValue(':logo', $logo, PDO::PARAM_STR);
$statement->bindValue(':is_hidden', $is_hidden, PDO::PARAM_BOOL);
$statement->execute();
$new_id = $pdo->lastInsertId();
$new_project_id = $pdo->lastInsertId();
$ownerQuery = "INSERT INTO projectmembers (projectid, name, uname, mail, role, lead, owner) VALUES (:id, :owner, :owneruname, :owneremail, 'Prosjektleder', 1, 1)";
$statement = $pdo->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);
$insertOrganizerQuery = <<<END
INSERT INTO
project_maintainer (
uname,
project_id,
name,
email,
is_organizer
)
VALUES
(
:username,
:project_id,
:user_real_name,
:user_email,
TRUE
)
END;
$statement = $pdo->prepare($insertOrganizerQuery);
$statement->bindValue(':username', $uname, PDO::PARAM_STR);
$statement->bindValue(':project_id', $new_project_id, PDO::PARAM_INT);
$statement->bindValue(':user_real_name', $name, PDO::PARAM_STR);
$statement->bindValue(':user_email', $mail, PDO::PARAM_STR);
$statement->execute();
} else {
} else { // Update existing project
$projectManager = new pvv\side\ProjectManager($pdo);
$owner = $projectManager->getProjectOwner($id);
$members = $projectManager->getProjectMembers($id);
// $owner = $projectManager->getProjectOwner($id);
$members = $projectManager->getProjectMaintainers($id);
// if ($do_join_or_leave and $owner['uname'] != $uname) {
if ($do_join_or_leave) {
@@ -62,20 +151,30 @@ if ($id == 0) {
}
}
if ($is_member) {// leave
$query = 'DELETE FROM projectmembers WHERE projectid=:id AND uname=:uname and lead=0 and owner=0;';
$query = '
DELETE FROM projectmembers
WHERE
projectid = :id
AND uname = :uname
';
$statement = $pdo->prepare($query);
$statement->bindParam(':id', $id, PDO::PARAM_STR);
$statement->bindParam(':uname', $uname, PDO::PARAM_STR);
$statement->bindValue(':id', $id, PDO::PARAM_STR);
$statement->bindValue(':uname', $uname, PDO::PARAM_STR);
$statement->execute();
echo 'leave';
} else {// join
$query = "INSERT INTO projectmembers (projectid, name, uname, mail, role, lead, owner) VALUES (:id, :name, :uname, :mail, 'Medlem', 0, 0)";
$query = '
INSERT INTO
projectmembers(projectid, name, uname, mail, role)
VALUES
(:id, :name, :uname, :mail, \'Medlem\')
';
$statement = $pdo->prepare($query);
$statement->bindParam(':id', $id, PDO::PARAM_STR);
$statement->bindParam(':name', $name, PDO::PARAM_STR);
$statement->bindParam(':uname', $uname, PDO::PARAM_STR);
$statement->bindParam(':mail', $mail, PDO::PARAM_STR);
$statement->bindValue(':id', $id, PDO::PARAM_STR);
$statement->bindValue(':name', $name, PDO::PARAM_STR);
$statement->bindValue(':uname', $uname, PDO::PARAM_STR);
$statement->bindValue(':mail', $mail, PDO::PARAM_STR);
$statement->execute();
echo 'join';
@@ -94,24 +193,28 @@ if ($id == 0) {
// this should be done as a transaction...
$pdo->beginTransaction();
// NOTE: project members are deleted via ON DELETE CASCADE
$query = 'DELETE FROM projects WHERE id=:id';
$statement = $pdo->prepare($query);
$statement->bindParam(':id', $id, PDO::PARAM_INT);
$statement->execute();
$query = 'DELETE FROM projectmembers WHERE projectid=:id';
$statement = $pdo->prepare($query);
$statement->bindParam(':id', $id, PDO::PARAM_INT);
$statement->bindValue(':id', $id, PDO::PARAM_INT);
$statement->execute();
$pdo->commit();
} else {
$query = 'UPDATE projects SET name=:title, description=:desc WHERE id=:id';
$query = '
UPDATE
projects
SET
name = :title,
description = :desc
WHERE
id = :id
';
$statement = $pdo->prepare($query);
$statement->bindParam(':title', $title, PDO::PARAM_STR);
$statement->bindParam(':desc', $desc, PDO::PARAM_STR);
$statement->bindParam(':id', $id, PDO::PARAM_INT);
$statement->bindValue(':title', $title, PDO::PARAM_STR);
$statement->bindValue(':desc', $desc, PDO::PARAM_STR);
$statement->bindValue(':id', $id, PDO::PARAM_INT);
$statement->execute();
}
+12 -4
View File
@@ -9,7 +9,15 @@ $pdo = new PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$door = new pvv\side\Door($pdo);
$doorEntry = (object) $door->getCurrent();
$doorEntry = $door->getCurrent();
// if (!is_null($doorEntry)) {
// $doorEntry = (object) $doorEntry;
// } else {
// $doorEntry = (object) [
// 'time' => new DateTimeImmutable('@0'),
// 'open' => false,
// ];
// }
?>
{
@@ -35,9 +43,9 @@ $doorEntry = (object) $door->getCurrent();
},
"issue_report_channels": ["email"],
"state": {
"open": <?php echo $doorEntry->open ? 'true' : 'false'; ?>,
"lastchange": <?php echo $doorEntry->time ? $doorEntry->time : 0; ?>,
"message": "<?php echo $doorEntry->open ? 'open for public, members are present' : 'closed'; ?>"
"open": <?php echo $doorEntry->isOpen() ? 'true' : 'false'; ?>,
"lastchange": <?php echo $doorEntry->getTimeStamp(); ?>,
"message": "<?php echo $doorEntry->isOpen() ? 'open for public, members are present' : 'closed'; ?>"
},
"feeds": {
"wiki": {
+1
View File
@@ -0,0 +1 @@
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200z"></path></svg>

After

Width:  |  Height:  |  Size: 239 B

+57
View File
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16" height="16" viewBox="0 0 4.2333332 4.2333335" version="1.1" id="svg1468" sodipodi:docname="codeberg-logo_icon_blue.svg" inkscape:version="1.2-alpha1 (b6a15bb, 2022-02-23)" inkscape:export-filename="/home/mray/Projects/Codeberg/logo/icon/png/codeberg-logo_icon_blue.png" inkscape:export-xdpi="384" inkscape:export-ydpi="384" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
<title id="title16">Codeberg logo</title>
<defs id="defs1462">
<linearGradient xlink:href="#linearGradient6924" id="linearGradient6918" x1="42519.285" y1="-7078.7891" x2="42575.336" y2="-6966.9307" gradientUnits="userSpaceOnUse"/>
<linearGradient id="linearGradient6924">
<stop style="stop-color:#2185d0;stop-opacity:0" offset="0" id="stop6920"/>
<stop id="stop6926" offset="0.49517274" style="stop-color:#2185d0;stop-opacity:0.48923996"/>
<stop style="stop-color:#2185d0;stop-opacity:0.63279623" offset="1" id="stop6922"/>
</linearGradient>
<linearGradient xlink:href="#linearGradient6924-6" id="linearGradient6918-3" x1="42519.285" y1="-7078.7891" x2="42575.336" y2="-6966.9307" gradientUnits="userSpaceOnUse"/>
<linearGradient id="linearGradient6924-6">
<stop style="stop-color:#2185d0;stop-opacity:0;" offset="0" id="stop6920-7"/>
<stop id="stop6926-5" offset="0.49517274" style="stop-color:#2185d0;stop-opacity:0.30000001;"/>
<stop style="stop-color:#2185d0;stop-opacity:0.30000001;" offset="1" id="stop6922-3"/>
</linearGradient>
</defs>
<sodipodi:namedview showborder="false" id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="22.627417" inkscape:cx="12.948893" inkscape:cy="12.661631" inkscape:document-units="px" inkscape:current-layer="svg1468" inkscape:document-rotation="0" showgrid="false" fit-margin-top="0" fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" units="px" inkscape:snap-global="false" inkscape:snap-page="true" showguides="false" inkscape:window-width="1531" inkscape:window-height="873" inkscape:window-x="69" inkscape:window-y="27" inkscape:window-maximized="1" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1">
<inkscape:grid type="xygrid" id="grid2067"/>
</sodipodi:namedview>
<metadata id="metadata1465">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title>Codeberg logo</dc:title>
<cc:license rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/"/>
<dc:creator>
<cc:Agent>
<dc:title>Robert Martinez</dc:title>
</cc:Agent>
</dc:creator>
<dc:rights>
<cc:Agent>
<dc:title>Codeberg and the Codeberg Logo are trademarks of Codeberg e.V.</dc:title>
</cc:Agent>
</dc:rights>
<dc:date>2020-04-09</dc:date>
<dc:publisher>
<cc:Agent>
<dc:title>Codeberg e.V.</dc:title>
</cc:Agent>
</dc:publisher>
<dc:source>codeberg.org</dc:source>
</cc:Work>
<cc:License rdf:about="http://creativecommons.org/publicdomain/zero/1.0/">
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
</cc:License>
</rdf:RDF>
</metadata>
<g id="g370484" inkscape:label="logo" transform="matrix(0.06551432,0,0,0.06551432,-2.232417,-1.431776)">
<path id="path6733-5" style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#linearGradient6918-3);fill-opacity:1;stroke:none;stroke-width:3.67846;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke markers fill;stop-color:#000000;stop-opacity:1" d="m 42519.285,-7078.7891 a 0.76086879,0.56791688 0 0 0 -0.738,0.6739 l 33.586,125.8886 a 87.182358,87.182358 0 0 0 39.381,-33.7636 l -71.565,-92.5196 a 0.76086879,0.56791688 0 0 0 -0.664,-0.2793 z" transform="matrix(0.37058478,0,0,0.37058478,-15690.065,2662.0533)" inkscape:label="berg"/>
<path id="path360787" style="opacity:1;fill:#2185d0;fill-opacity:1;stroke-width:17.0055;paint-order:markers fill stroke;stop-color:#000000" d="m 11249.461,-1883.6961 c -12.74,0 -23.067,10.3275 -23.067,23.0671 0,4.3335 1.22,8.5795 3.522,12.2514 l 19.232,-24.8636 c 0.138,-0.1796 0.486,-0.1796 0.624,0 l 19.233,24.8646 c 2.302,-3.6721 3.523,-7.9185 3.523,-12.2524 0,-12.7396 -10.327,-23.0671 -23.067,-23.0671 z" sodipodi:nodetypes="sccccccs" inkscape:label="sky" transform="matrix(1.4006354,0,0,1.4006354,-15690.065,2662.0533)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 61 81" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round"><style><![CDATA[.B{stroke:none}.C{fill:#000}.D{fill:#eceff1}.E{fill:url(#A)}.F{stroke-opacity:.34}.G{fill:#ffee58}.H{fill:#d9ff66}.I{fill:#4caf50}]]></style><use xlink:href="#C" x=".5" y=".5"/><defs><linearGradient id="A" x1="0%" y1="50%" x2="100%" y2="50%"><stop offset="0%" stop-color="#90a4ae"/><stop offset="100%" stop-color="#b0bec5"/></linearGradient><path id="B" d="M7.059 67.43c5.142 3.383 12.674 5.604 21.176 5.875"/></defs><symbol id="C" overflow="visible"><ellipse cx="30" cy="50" rx="30" ry="16.667" class="B D"/><g transform="translate(0 50)"><path d="M0 0v13.333C0 22.567 13.38 30 30 30s30-7.433 30-16.667V0c0 9.233-13.38 16.667-30 16.667S0 9.233 0 0z" class="B E"/></g><use xlink:href="#B" fill="none" class="F"/><g class="B"><ellipse cx="37.362" cy="73.34" rx="2.068" ry="1.993" class="G"/><ellipse cx="44.52" cy="71.326" rx="2.068" ry="1.993" class="H"/><ellipse cx="51.579" cy="68.66" rx="2.068" ry="1.993" class="I"/><ellipse cx="30" cy="33.334" rx="30" ry="16.667" class="D"/></g><g transform="translate(0 33.333)"><path d="M0 0v13.333C0 22.567 13.38 30 30 30s30-7.433 30-16.667V0c0 9.233-13.38 16.667-30 16.667S0 9.233 0 0z" class="B E"/></g><use xlink:href="#B" y="-16.667" fill="none" class="F"/><g class="B"><ellipse cx="37.362" cy="56.673" rx="2.068" ry="1.993" class="G"/><ellipse cx="44.52" cy="54.66" rx="2.068" ry="1.993" class="H"/><ellipse cx="51.579" cy="51.993" rx="2.068" ry="1.993" class="I"/><ellipse cx="30" cy="16.667" rx="30" ry="16.667" class="D"/></g><g transform="translate(0 16.667)"><path d="M0 0v13.333C0 22.567 13.38 30 30 30s30-7.433 30-16.667V0c0 9.233-13.38 16.667-30 16.667S0 9.233 0 0z" class="B E"/></g><use xlink:href="#B" y="-33.334" fill="none" class="F"/><g class="B"><ellipse cx="37.362" cy="40.007" rx="2.068" ry="1.993" class="G"/><ellipse cx="44.52" cy="37.993" rx="2.068" ry="1.993" class="H"/><ellipse cx="51.579" cy="35.326" rx="2.068" ry="1.993" class="I"/><ellipse cx="30" cy="16.666" rx="26.471" ry="13.333" fill="#cfd8dc"/></g></symbol></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Discord-Logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 126.644 96"><defs><style>.cls-1{fill:#5865f2;}</style></defs><path id="Discord-Symbol-Blurple" class="cls-1" d="M81.15,0c-1.2376,2.1973-2.3489,4.4704-3.3591,6.794-9.5975-1.4396-19.3718-1.4396-28.9945,0-.985-2.3236-2.1216-4.5967-3.3591-6.794-9.0166,1.5407-17.8059,4.2431-26.1405,8.0568C2.779,32.5304-1.6914,56.3725.5312,79.8863c9.6732,7.1476,20.5083,12.603,32.0505,16.0884,2.6014-3.4854,4.8998-7.1981,6.8698-11.0623-3.738-1.3891-7.3497-3.1318-10.8098-5.1523.9092-.6567,1.7932-1.3386,2.6519-1.9953,20.281,9.547,43.7696,9.547,64.0758,0,.8587.7072,1.7427,1.3891,2.6519,1.9953-3.4601,2.0457-7.0718,3.7632-10.835,5.1776,1.97,3.8642,4.2683,7.5769,6.8698,11.0623,11.5419-3.4854,22.3769-8.9156,32.0509-16.0631,2.626-27.2771-4.496-50.9172-18.817-71.8548C98.9811,4.2684,90.1918,1.5659,81.1752.0505l-.0252-.0505ZM42.2802,65.4144c-6.2383,0-11.4159-5.6575-11.4159-12.6535s4.9755-12.6788,11.3907-12.6788,11.5169,5.708,11.4159,12.6788c-.101,6.9708-5.026,12.6535-11.3907,12.6535ZM84.3576,65.4144c-6.2637,0-11.3907-5.6575-11.3907-12.6535s4.9755-12.6788,11.3907-12.6788,11.4917,5.708,11.3906,12.6788c-.101,6.9708-5.026,12.6535-11.3906,12.6535Z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

+6
View File
@@ -0,0 +1,6 @@
<svg width="54" height="54" viewBox="0 0 54 54" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.4414 3.24C19.4414 1.4506 20.892 0 22.6814 0C34.6108 0 44.2814 9.67065 44.2814 21.6C44.2814 23.3894 42.8308 24.84 41.0414 24.84C39.252 24.84 37.8014 23.3894 37.8014 21.6C37.8014 13.2494 31.032 6.48 22.6814 6.48C20.892 6.48 19.4414 5.0294 19.4414 3.24Z" fill="#0DBD8B"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M34.5586 50.76C34.5586 52.5494 33.108 54 31.3186 54C19.3893 54 9.71861 44.3294 9.71861 32.4C9.71861 30.6106 11.1692 29.16 12.9586 29.16C14.748 29.16 16.1986 30.6106 16.1986 32.4C16.1986 40.7505 22.9681 47.52 31.3186 47.52C33.108 47.52 34.5586 48.9706 34.5586 50.76Z" fill="#0DBD8B"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.24 34.5601C1.4506 34.5601 -6.34076e-08 33.1095 -1.41625e-07 31.3201C-6.63074e-07 19.3907 9.67065 9.72007 21.6 9.72007C23.3894 9.72007 24.84 11.1707 24.84 12.9601C24.84 14.7495 23.3894 16.2001 21.6 16.2001C13.2495 16.2001 6.48 22.9695 6.48 31.3201C6.48 33.1095 5.0294 34.5601 3.24 34.5601Z" fill="#0DBD8B"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M50.76 19.4399C52.5494 19.4399 54 20.8905 54 22.6799C54 34.6093 44.3294 44.2799 32.4 44.2799C30.6106 44.2799 29.16 42.8293 29.16 41.0399C29.16 39.2505 30.6106 37.7999 32.4 37.7999C40.7505 37.7999 47.52 31.0305 47.52 22.6799C47.52 20.8905 48.9706 19.4399 50.76 19.4399Z" fill="#0DBD8B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

+1
View File
@@ -0,0 +1 @@
<svg version="1.1" id="main_outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" style="enable-background:new 0 0 640 640;" xml:space="preserve" viewBox="5.67 143.05 628.65 387.55"> <g> <path id="teabag" style="fill:#FFFFFF" d="M395.9,484.2l-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5,21.2-17.9,33.8-11.8 c17.2,8.3,27.1,13,27.1,13l-0.1-109.2l16.7-0.1l0.1,117.1c0,0,57.4,24.2,83.1,40.1c3.7,2.3,10.2,6.8,12.9,14.4 c2.1,6.1,2,13.1-1,19.3l-61,126.9C423.6,484.9,408.4,490.3,395.9,484.2z"></path> <g> <g> <path style="fill:#609926" d="M622.7,149.8c-4.1-4.1-9.6-4-9.6-4s-117.2,6.6-177.9,8c-13.3,0.3-26.5,0.6-39.6,0.7c0,39.1,0,78.2,0,117.2 c-5.5-2.6-11.1-5.3-16.6-7.9c0-36.4-0.1-109.2-0.1-109.2c-29,0.4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5 c-9.8-0.6-22.5-2.1-39,1.5c-8.7,1.8-33.5,7.4-53.8,26.9C-4.9,212.4,6.6,276.2,8,285.8c1.7,11.7,6.9,44.2,31.7,72.5 c45.8,56.1,144.4,54.8,144.4,54.8s12.1,28.9,30.6,55.5c25,33.1,50.7,58.9,75.7,62c63,0,188.9-0.1,188.9-0.1s12,0.1,28.3-10.3 c14-8.5,26.5-23.4,26.5-23.4s12.9-13.8,30.9-45.3c5.5-9.7,10.1-19.1,14.1-28c0,0,55.2-117.1,55.2-231.1 C633.2,157.9,624.7,151.8,622.7,149.8z M125.6,353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6,321.8,60,295.4 c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5,38.5-30c13.8-3.7,31-3.1,31-3.1s7.1,59.4,15.7,94.2c7.2,29.2,24.8,77.7,24.8,77.7 S142.5,359.9,125.6,353.9z M425.9,461.5c0,0-6.1,14.5-19.6,15.4c-5.8,0.4-10.3-1.2-10.3-1.2s-0.3-0.1-5.3-2.1l-112.9-55 c0,0-10.9-5.7-12.8-15.6c-2.2-8.1,2.7-18.1,2.7-18.1L322,273c0,0,4.8-9.7,12.2-13c0.6-0.3,2.3-1,4.5-1.5c8.1-2.1,18,2.8,18,2.8 l110.7,53.7c0,0,12.6,5.7,15.3,16.2c1.9,7.4-0.5,14-1.8,17.2C474.6,363.8,425.9,461.5,425.9,461.5z"></path> <path style="fill:#609926" d="M326.8,380.1c-8.2,0.1-15.4,5.8-17.3,13.8c-1.9,8,2,16.3,9.1,20c7.7,4,17.5,1.8,22.7-5.4 c5.1-7.1,4.3-16.9-1.8-23.1l24-49.1c1.5,0.1,3.7,0.2,6.2-0.5c4.1-0.9,7.1-3.6,7.1-3.6c4.2,1.8,8.6,3.8,13.2,6.1 c4.8,2.4,9.3,4.9,13.4,7.3c0.9,0.5,1.8,1.1,2.8,1.9c1.6,1.3,3.4,3.1,4.7,5.5c1.9,5.5-1.9,14.9-1.9,14.9 c-2.3,7.6-18.4,40.6-18.4,40.6c-8.1-0.2-15.3,5-17.7,12.5c-2.6,8.1,1.1,17.3,8.9,21.3c7.8,4,17.4,1.7,22.5-5.3 c5-6.8,4.6-16.3-1.1-22.6c1.9-3.7,3.7-7.4,5.6-11.3c5-10.4,13.5-30.4,13.5-30.4c0.9-1.7,5.7-10.3,2.7-21.3 c-2.5-11.4-12.6-16.7-12.6-16.7c-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3c4.7-9.7,9.4-19.3,14.1-29 c-4.1-2-8.1-4-12.2-6.1c-4.8,9.8-9.7,19.7-14.5,29.5c-6.7-0.1-12.9,3.5-16.1,9.4c-3.4,6.3-2.7,14.1,1.9,19.8 C343.2,346.5,335,363.3,326.8,380.1z"></path> </g> </g> </g> </svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

+1
View File
@@ -0,0 +1 @@
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#24292f"/></svg>

After

Width:  |  Height:  |  Size: 963 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 KiB

+49
View File
@@ -0,0 +1,49 @@
<svg id="logo.svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
<defs>
<linearGradient id="gradient" gradientUnits="userSpaceOnUse" x1="0" x2="100" y1="100" y2="0">
<stop offset="0%" stop-color="#0a00b2"/>
<stop offset="50%" stop-color="#ff0000"/>
<stop offset="100%" stop-color="#fffc00"/>
</linearGradient>
<style>
.petal {
opacity: 0.65;
}
.petals {
fill: url(#gradient);
}
</style>
</defs>
<g id="petals" class="petals">
<path class="petal" d="M35.922,12.956a16.3,16.3,0,0,0,2.344,10.436,10.462,10.462,0,0,0,5.924,4.351,2.587,2.587,0,0,0,2.144-.521,11.022,11.022,0,0,0,3.635-9.771c-0.831-7.014-5.024-11.8-10.361-16.357A30.253,30.253,0,0,0,35.922,12.956Z"/>
<path class="petal" d="M28.485,16.676A16.327,16.327,0,0,0,32.959,26.4a10.491,10.491,0,0,0,6.7,3.031,2.581,2.581,0,0,0,1.988-.953,11.006,11.006,0,0,0,1.513-10.309c-2.279-6.689-7.38-10.5-13.554-13.857A30.215,30.215,0,0,0,28.485,16.676Z"/>
<path class="petal" d="M21.988,21.853A16.374,16.374,0,0,0,28.4,30.439a10.512,10.512,0,0,0,7.191,1.579,2.574,2.574,0,0,0,1.745-1.343,11,11,0,0,0-.675-10.4C33.031,14.206,27.245,11.533,20.5,9.526A30.218,30.218,0,0,0,21.988,21.853Z"/>
<path class="petal" d="M16.716,28.26a16.426,16.426,0,0,0,8.063,7.073,10.519,10.519,0,0,0,7.364.057,2.566,2.566,0,0,0,1.426-1.675,11.014,11.014,0,0,0-2.833-10.03C25.919,18.5,19.7,17.079,12.687,16.509A30.261,30.261,0,0,0,16.716,28.26Z"/>
<path class="petal" d="M12.9,35.618a16.476,16.476,0,0,0,9.366,5.251A10.512,10.512,0,0,0,29.478,39.4a2.559,2.559,0,0,0,1.045-1.933,11.038,11.038,0,0,0-4.868-9.225c-5.8-4.079-12.176-4.18-19.154-3.286A30.339,30.339,0,0,0,12.9,35.618Z"/>
<path class="petal" d="M10.7,43.6a16.515,16.515,0,0,0,10.258,3.2,10.493,10.493,0,0,0,6.751-2.927,2.553,2.553,0,0,0,.618-2.107,11.068,11.068,0,0,0-6.69-8.017c-6.522-2.791-12.783-1.571-19.422.746A30.434,30.434,0,0,0,10.7,43.6Z"/>
<path class="petal" d="M10.222,51.871a16.535,16.535,0,0,0,10.7,1.008,10.464,10.464,0,0,0,5.991-4.259,2.55,2.55,0,0,0,.164-2.189,11.1,11.1,0,0,0-8.219-6.458C11.9,38.591,6.028,41.08.019,44.719A30.534,30.534,0,0,0,10.222,51.871Z"/>
<path class="petal" d="M11.481,60.056a16.533,16.533,0,0,0,10.68-1.228,10.431,10.431,0,0,0,4.97-5.405,2.551,2.551,0,0,0-.3-2.175,11.133,11.133,0,0,0-9.39-4.617c-7.1.089-12.32,3.737-17.438,8.539A30.618,30.618,0,0,0,11.481,60.056Z"/>
<path class="petal" d="M14.423,67.8a16.51,16.51,0,0,0,10.19-3.41,10.4,10.4,0,0,0,3.732-6.315,2.555,2.555,0,0,0-.745-2.066,11.155,11.155,0,0,0-10.15-2.575c-6.926,1.555-11.27,6.2-15.272,11.959A30.673,30.673,0,0,0,14.423,67.8Z"/>
<path class="petal" d="M18.92,74.769a16.471,16.471,0,0,0,9.254-5.442,10.374,10.374,0,0,0,2.33-6.948,2.561,2.561,0,0,0-1.161-1.867,11.166,11.166,0,0,0-10.466-.419c-6.45,2.953-9.727,8.4-12.438,14.856A30.693,30.693,0,0,0,18.92,74.769Z"/>
<path class="petal" d="M24.775,80.654a16.42,16.42,0,0,0,7.914-7.237,10.36,10.36,0,0,0,.827-7.279,2.569,2.569,0,0,0-1.525-1.586,11.162,11.162,0,0,0-10.325,1.754c-5.691,4.222-7.759,10.226-9.061,17.1A30.668,30.668,0,0,0,24.775,80.654Z"/>
<path class="petal" d="M31.733,85.2a16.368,16.368,0,0,0,6.229-8.716,10.36,10.36,0,0,0-.713-7.29,2.576,2.576,0,0,0-1.824-1.236,11.144,11.144,0,0,0-9.733,3.851c-4.684,5.307-5.452,11.607-5.288,18.6A30.607,30.607,0,0,0,31.733,85.2Z"/>
<path class="petal" d="M39.488,88.208a16.323,16.323,0,0,0,4.271-9.814,10.373,10.373,0,0,0-2.221-6.984,2.583,2.583,0,0,0-2.042-.832,11.116,11.116,0,0,0-8.715,5.78C27.309,82.519,27.874,88.84,29.5,95.65A30.518,30.518,0,0,0,39.488,88.208Z"/>
<path class="petal" d="M47.7,89.546a16.293,16.293,0,0,0,2.126-10.482A10.4,10.4,0,0,0,46.2,72.692a2.588,2.588,0,0,0-2.171-.391,11.082,11.082,0,0,0-7.316,7.456c-2.109,6.743-.234,12.81,2.776,19.135A30.421,30.421,0,0,0,47.7,89.546Z"/>
<path class="petal" d="M56.018,89.156a16.283,16.283,0,0,0-.111-10.693,10.429,10.429,0,0,0-4.885-5.481,2.589,2.589,0,0,0-2.206.067,11.049,11.049,0,0,0-5.6,8.806C42.565,88.887,45.667,94.432,49.934,100A30.324,30.324,0,0,0,56.018,89.156Z"/>
<path class="petal" d="M64.07,87.055a16.3,16.3,0,0,0-2.344-10.436A10.462,10.462,0,0,0,55.8,72.268a2.587,2.587,0,0,0-2.144.521,11.022,11.022,0,0,0-3.635,9.771c0.831,7.014,5.024,11.8,10.361,16.357A30.254,30.254,0,0,0,64.07,87.055Z"/>
<path class="petal" d="M71.507,83.335a16.327,16.327,0,0,0-4.474-9.723,10.491,10.491,0,0,0-6.7-3.031,2.581,2.581,0,0,0-1.988.953,11.006,11.006,0,0,0-1.513,10.309c2.279,6.689,7.38,10.5,13.554,13.857A30.215,30.215,0,0,0,71.507,83.335Z"/>
<path class="petal" d="M78,78.158a16.374,16.374,0,0,0-6.409-8.586A10.512,10.512,0,0,0,64.4,67.994a2.574,2.574,0,0,0-1.745,1.343,11,11,0,0,0,.675,10.4c3.627,6.071,9.414,8.745,16.154,10.752A30.218,30.218,0,0,0,78,78.158Z"/>
<path class="petal" d="M83.276,71.751a16.426,16.426,0,0,0-8.064-7.073,10.519,10.519,0,0,0-7.364-.057A2.566,2.566,0,0,0,66.423,66.3a11.014,11.014,0,0,0,2.833,10.03C74.073,81.515,80.292,82.933,87.3,83.5A30.263,30.263,0,0,0,83.276,71.751Z"/>
<path class="petal" d="M87.094,64.393a16.476,16.476,0,0,0-9.366-5.251,10.512,10.512,0,0,0-7.215,1.467,2.559,2.559,0,0,0-1.045,1.933,11.037,11.037,0,0,0,4.868,9.225c5.8,4.079,12.176,4.18,19.154,3.287A30.338,30.338,0,0,0,87.094,64.393Z"/>
<path class="petal" d="M89.291,56.407a16.515,16.515,0,0,0-10.258-3.2,10.493,10.493,0,0,0-6.751,2.927,2.553,2.553,0,0,0-.618,2.107,11.068,11.068,0,0,0,6.69,8.017c6.522,2.791,12.783,1.571,19.422-.746A30.435,30.435,0,0,0,89.291,56.407Z"/>
<path class="petal" d="M89.77,48.14a16.534,16.534,0,0,0-10.7-1.008,10.465,10.465,0,0,0-5.991,4.259,2.551,2.551,0,0,0-.164,2.189,11.1,11.1,0,0,0,8.219,6.458c6.963,1.382,12.832-1.107,18.842-4.747A30.533,30.533,0,0,0,89.77,48.14Z"/>
<path class="petal" d="M88.511,39.956a16.533,16.533,0,0,0-10.68,1.228,10.431,10.431,0,0,0-4.97,5.405,2.551,2.551,0,0,0,.3,2.175,11.133,11.133,0,0,0,9.39,4.617c7.1-.089,12.32-3.737,17.438-8.539A30.618,30.618,0,0,0,88.511,39.956Z"/>
<path class="petal" d="M85.569,32.21a16.51,16.51,0,0,0-10.19,3.41,10.4,10.4,0,0,0-3.732,6.315A2.555,2.555,0,0,0,72.393,44a11.155,11.155,0,0,0,10.15,2.574c6.926-1.555,11.27-6.2,15.272-11.959A30.673,30.673,0,0,0,85.569,32.21Z"/>
<path class="petal" d="M81.072,25.242a16.471,16.471,0,0,0-9.254,5.442,10.374,10.374,0,0,0-2.33,6.948A2.561,2.561,0,0,0,70.648,39.5a11.166,11.166,0,0,0,10.466.419c6.45-2.953,9.727-8.4,12.438-14.856A30.7,30.7,0,0,0,81.072,25.242Z"/>
<path class="petal" d="M75.217,19.357A16.42,16.42,0,0,0,67.3,26.594a10.36,10.36,0,0,0-.827,7.279A2.568,2.568,0,0,0,68,35.459,11.162,11.162,0,0,0,78.326,33.7c5.691-4.223,7.759-10.226,9.061-17.1A30.668,30.668,0,0,0,75.217,19.357Z"/>
<path class="petal" d="M68.259,14.811a16.368,16.368,0,0,0-6.229,8.716,10.36,10.36,0,0,0,.713,7.29,2.576,2.576,0,0,0,1.824,1.236A11.144,11.144,0,0,0,74.3,28.2c4.684-5.307,5.452-11.607,5.288-18.6A30.608,30.608,0,0,0,68.259,14.811Z"/>
<path class="petal" d="M60.5,11.8a16.324,16.324,0,0,0-4.271,9.814A10.374,10.374,0,0,0,58.454,28.6a2.583,2.583,0,0,0,2.042.832,11.115,11.115,0,0,0,8.715-5.78c3.473-6.16,2.907-12.481,1.284-19.291A30.519,30.519,0,0,0,60.5,11.8Z"/>
<path class="petal" d="M52.288,10.465a16.294,16.294,0,0,0-2.126,10.482,10.4,10.4,0,0,0,3.632,6.372,2.588,2.588,0,0,0,2.171.391,11.082,11.082,0,0,0,7.316-7.456c2.109-6.744.234-12.809-2.776-19.135A30.42,30.42,0,0,0,52.288,10.465Z"/>
<path class="petal" d="M43.973,10.855a16.283,16.283,0,0,0,.111,10.693,10.429,10.429,0,0,0,4.885,5.482,2.589,2.589,0,0,0,2.206-.066,11.049,11.049,0,0,0,5.6-8.806C57.427,11.125,54.325,5.579,50.058.014A30.324,30.324,0,0,0,43.973,10.855Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="9.14 141.8 573.65 573.65">
<style type="text/css">
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#404F54;}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#E5E5E5;}
.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#CCCCCC;}
.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#37BEFF;}
</style>
<polygon class="st3" points="582.79,549.77 295.96,384.1 295.96,207.27 582.79,372.95 "/>
<polygon class="st0" points="9.14,549.77 295.96,384.1 295.96,207.27 9.14,372.95 "/>
<path class="st2" d="M295.96,141.8c109.56,0,198.41,88.85,198.41,198.41c0,109.56-88.85,198.41-198.41,198.41 c-109.56,0-198.41-88.85-198.41-198.41C97.55,230.65,186.4,141.8,295.96,141.8"/>
<path class="st1" d="M295.96,141.8c109.6,0,198.48,88.85,198.48,198.41c0,109.56-88.88,198.41-198.48,198.41 c-62.91-42.34-88.94-127.64-88.94-198.3S233.05,184.22,295.96,141.8"/>
<polygon class="st3" points="582.79,372.95 295.96,538.62 295.96,715.45 582.79,549.77 "/>
<polygon class="st0" points="9.14,372.95 295.96,538.62 295.96,715.45 9.14,549.77 "/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg"
aria-label="RSS" role="img"
viewBox="0 0 512 512"><path
d="m0 0H512V512H0"
fill="#f80"/><path fill="#fff" d="m109 271A132 133 0 01241 403h60A192 193 0 00109 211v-54A246 247 0 01355 403h60A306 307 0 00109 97m35 235a35 35 0 102 0"/></svg>

After

Width:  |  Height:  |  Size: 276 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="64 94 384.4 308"><style>path,rect{fill:#000}@media (prefers-color-scheme:dark){path,rect{fill:#000}rect{transform:scale(1,-1)}}</style><g transform="matrix(.1 0 0 -.1 0 512)"><path d="M933 3908c-150-150-276-281-279-291-5-16 51-77 272-298l279-279h177c98 0 178 3 178 7s-89 97-197 206c-157 157-195 200-183 207 9 6 257 10 613 10h597v134c0 113-2 135-16 140-9 3-283 6-610 6s-594 3-594 8c0 4 90 97 199 206 110 110 198 203 195 208-3 4-84 8-181 8h-176zm2627 265c0-4 88-96 196-204 115-115 194-201 190-208-5-8-178-11-611-11-540 0-603-2-609-16-3-9-6-67-6-129 0-103 2-115 19-125 13-6 224-10 615-10 328 0 596-3 596-8 0-4-90-97-200-207s-200-203-200-207c0-5 82-8 182-8h182l285 286 285 285-285 285-284 284h-177c-98 0-178-3-178-7"/><path d="M640 2317V1154l28-27 28-27h3730l27 28 27 28v1156c0 636-4 1159-8 1162-5 3-41-27-80-67l-72-72-2-1030-3-1030H805l-3 1030-2 1030-72 72c-40 40-76 73-80 73-5 0-8-523-8-1163"/><path d="M1290 2972c0-4 84-92 188-195l187-187h1790l188 188c103 103 187 191 187 195s-75 7-168 7h-168l-69-70-69-70H1764l-69 70-69 70h-168c-93 0-168-3-168-8"/><rect width="105.704" height="113.393" x="965.825" y="-1995.056" ry="0"/><rect width="105.704" height="113.393" x="968.171" y="-1775.339" ry="0"/><rect width="105.704" height="113.393" x="1544.743" y="-1770.834" ry="0"/><rect width="105.704" height="113.393" x="1201.284" y="-2214.654" ry="0"/><rect width="105.704" height="113.393" x="1529.886" y="-2211.156" ry="0"/><rect width="105.704" height="113.393" x="1766.14" y="-2213.186" ry="0"/><rect width="105.704" height="113.393" x="1767.395" y="-1976.642" ry="0"/><rect width="105.704" height="113.393" x="2112.529" y="-2219.311" ry="0"/><rect width="105.704" height="113.393" x="2677.813" y="-2214.965" ry="0"/><rect width="105.704" height="113.393" x="2672.628" y="-1988.655" ry="0"/><rect width="105.704" height="113.393" x="2673.453" y="-1762.207" ry="0"/><rect width="105.704" height="113.393" x="2898.873" y="-2216.276" ry="0"/><rect width="105.704" height="113.393" x="3249.094" y="-2212.257" ry="0"/><rect width="105.704" height="113.393" x="3251.061" y="-1984.478" ry="0"/><rect width="105.704" height="113.393" x="3251.56" y="-1774.923" ry="0"/><rect width="105.704" height="113.393" x="3470.185" y="-2214.272" ry="0"/><rect width="105.704" height="113.393" x="3829.944" y="-2215.038" ry="0"/><rect width="105.704" height="113.393" x="4044.099" y="-2214.53" ry="0"/><rect width="105.704" height="113.393" x="4041.513" y="-1999.062" ry="0"/><rect width="105.704" height="113.393" x="4043.404" y="-1785.199" ry="0"/><rect width="105.704" height="113.393" x="3830.52" y="-1781.447" ry="0"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Some files were not shown because too many files have changed in this diff Show More