84 Commits

Author SHA1 Message Date
ce99b309f8 Calendar: Remove recurring events for summer break 2024 2024-06-11 17:09:35 +02:00
13a6df92ce Merge pull request 'Gitea istedenfor gogs på tjenester-siden' () from gitea-i-tjenester into master
Reviewed-on: 
2024-05-11 22:55:36 +02:00
f87c6820d7 services: move gitea above github and remove trac 2024-05-07 21:31:42 +02:00
592c6d46c4 services: replaced gogs with gitea 2024-05-07 21:31:20 +02:00
9d9d99e6ba gallery: sort by timestamp. slideshow: fix transition bug on firefox 2024-04-14 01:59:12 +02:00
216e153f89 nix/module: correct some nginx locations directives 2024-04-11 13:19:59 +02:00
d528df5f99 Merge pull request 'Fix links after moving from spikkje to bekka' () from www2-www-fixes into master
Reviewed-on: 
2024-04-11 00:43:32 +02:00
faf93cc45a Fix links after moving from spikkje to bekka 2024-04-11 00:42:12 +02:00
1aa4f4fc8a Merge pull request 'Mega PR: Update deps, consolidate config, build with nix, gallery fixes, etc.' () from nixify-ng into master
Reviewed-on: 
2024-04-10 21:45:10 +02:00
f1958d9afc galleri: Support .thumbnails 2024-04-10 21:44:58 +02:00
7f269f05d6 gallery: Fix usernames/paths in the gallery 2024-04-10 21:44:58 +02:00
7e158c9fc3 Update dependencies 2024-04-10 21:44:58 +02:00
d79cd3ad37 dist/pvv.sql: fix failing parts 2024-04-10 21:44:58 +02:00
f5a6868301 Build new nix {module,package,flake}. Adapt to new config variables
Co-authored-by: h7x4 <h7x4@nani.wtf>
2024-04-10 21:44:58 +02:00
310246dd20 Update global config variables 2024-04-10 21:44:58 +02:00
151a0d44f6 move gallery/slideshow directories 2024-04-10 21:44:58 +02:00
10c70e89f6 Remove 'ticker', deprecated since 1da1785e46 2024-04-10 21:44:58 +02:00
318e9cfe6b Update dependencies, fix broken flake 2024-04-10 21:44:58 +02:00
1f60b80b98 move sql_config.php to config.php 2024-04-10 21:44:58 +02:00
517f39eee9 Update SimpleSamlPHP 2024-04-10 21:44:58 +02:00
2f6553225a remove deprecated options and dependencies for dataporten/oauth 2024-04-10 21:44:58 +02:00
1eb84eaec8 Merge pull request 'Fix navbar links' () from fix-navbar-links into master
Reviewed-on: 
2024-04-10 21:38:58 +02:00
3e1a21741f navbar: redirect webmail to modern roundcube 2024-04-08 23:46:28 +02:00
da6c1963f8 navbar: redirect wiki to wiki.pvv.ntnu.no 2024-04-08 23:43:06 +02:00
0da5d44ea1 Manually cast door state to int () 2023-09-12 20:31:14 +02:00
133c4e269d Re-enable recurring events for Fall 2023 () 2023-09-02 21:25:07 +02:00
850c8da95a Initial nix flake, automate devshell () 2023-09-02 19:55:24 +02:00
1ae6356aa5 Update contact information ()
* Update contact information

* Minor fixes to contact page

---------

Co-authored-by: Felix Albrigtsen <felix@albrigtsen.it>
2023-08-20 01:31:53 +02:00
fa4fa9ad7d Stop bots scraping mediawiki special pages/diffs/history () 2023-06-14 23:26:28 +02:00
d43e5faf3c Summer break; Disable recurring events () 2023-06-11 22:19:14 +02:00
57055c9c6f Fix previous merge ()
The previous merge contained a commit and a revert, but the original change remained on master.
2023-04-27 16:05:14 +02:00
5fbc1dbc00 Re-enable brettspillkveld ()
* Re-add brettspillkveld

Plz fix 
2023-04-27 16:01:40 +02:00
e364a5fee0 Stop including BrettspillActivity ()
Temporary until april 23
2023-04-17 01:21:15 +02:00
a4a6553329 Merge pull request from Programvareverkstedet/add-thunderbird-autoconfig
Add thunderbird autoconfig to .well-known
2023-02-25 23:28:10 +01:00
ae81d269fa Add thunderbird autoconfig to .well-known
This file will make it easier for people to set up thunderbird with a
pvv mail.

Relevant links:
- https://wiki.mozilla.org/Thunderbird:Autoconfiguration
- https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat
2023-02-25 23:10:34 +01:00
31d9a955df Merge pull request from Programvareverkstedet/patch
Various patches; Fix admin groups, update deps
2023-01-19 13:58:10 +01:00
6bd3ce7731 Add notice on empty image gallery 2023-01-19 13:46:39 +01:00
9f45271b18 Update deprecated template syntax 2023-01-19 11:28:29 +01:00
567962c947 Fix admins addGroup and removeGroup, missing parameter 2023-01-19 11:27:48 +01:00
7defd8b388 Update minor dependency changes 2023-01-19 11:26:56 +01:00
a7f2c4f2d3 Merge pull request from Programvareverkstedet/linkdash 2023-01-16 16:27:28 +01:00
847fe67534 Merge pull request from Programvareverkstedet/2023events 2023-01-16 16:26:01 +01:00
79854aee6f Improved linkdash-minecraft-map 2023-01-16 16:22:53 +01:00
699876405c Inverted week offset on recurring events 2023-01-16 16:20:54 +01:00
a734fe6bd3 Add brettspill, drift and hack activities 2023-01-16 14:18:30 +01:00
c33736a501 Fix minecraft map link 2023-01-16 14:09:09 +01:00
3a39ca0f73 Merge pull request from Programvareverkstedet/linkdash
Add wrapper to services page, fix css and favicon
2022-11-29 17:56:29 +01:00
d8f8aafdd9 Add wrapper to services page, fix css and favicon 2022-11-29 17:52:39 +01:00
79ffb1fff5 Merge pull request from Programvareverkstedet/linkdash
Fix top margin on services
2022-11-29 17:35:56 +01:00
29c4c67645 Fix top margin on services 2022-11-29 17:31:22 +01:00
98da6ada23 Merge pull request from Programvareverkstedet/linkdash
Added linkdash - list of our services
2022-11-29 17:26:10 +01:00
1c1656c3bc Added photo gallery to services list 2022-11-29 17:14:03 +01:00
c489f303ea Add services, fix column styling 2022-11-29 17:07:36 +01:00
fc0286c277 Merge pull request from Programvareverkstedet/calenderFix
Disabled brettspillkveld and hackekveld
2022-11-27 10:14:21 +01:00
f4407fde4a Disabled brettspillkveld and hackekveld 2022-11-19 21:23:41 +01:00
9f11d442fe Merge pull request from Programvareverkstedet/video-til-karusell
Replace promo video with slideshow
2022-11-05 20:51:12 +01:00
1bc41124c0 Replace promo video with slideshow 2022-11-05 19:06:22 +01:00
72fb9e9915 Merge pull request from Programvareverkstedet/brettspillkveld
Added brettspillkveld
2022-09-24 22:05:21 +02:00
b555f915a0 Added brettspillkveld 2022-09-24 21:58:28 +02:00
4b9957e9c5 Add more services, make two columns 2022-09-18 01:24:57 +02:00
b42214b404 Establish layout for /tjenester 2022-09-17 17:09:35 +02:00
fcef05d439 Merge pull request from Programvareverkstedet/calendar-changes
Add driftkveld and hackekveld
2022-09-04 00:56:41 +02:00
0f8b15715b Add driftkveld and hackekveld 2022-09-04 00:53:30 +02:00
8a79d9d016 Merge pull request from Programvareverkstedet/video-embed
Replace old promo-video in /om/
2022-08-09 21:17:18 +02:00
f507b0cddd Replace old promo-video in /om/ 2022-08-09 20:53:24 +02:00
0b25f8d54a Merge pull request from Programvareverkstedet/video-embed
Front page: Fix missing closing tag
2022-08-08 19:55:44 +02:00
4f504e9456 Fix missing closing tag 2022-08-08 19:53:37 +02:00
1f32c03d86 Merge pull request from Programvareverkstedet/video-embed
Replace slideshow with promo video
2022-08-08 17:24:53 +02:00
fc6d5d5f8e Update CSS to preserve video aspect ratio 2022-08-08 16:36:58 +02:00
1cde0a2a93 Replace slideshow with promo video 2022-08-08 15:43:23 +02:00
65c0bad9b0 Merge pull request from Programvareverkstedet/calendarChanges
Removed driftskveld
2022-05-26 16:16:52 +02:00
9fa6066724 Removed driftskveld temporarily 2022-05-26 14:08:41 +02:00
a506dc44cc Merge pull request from Programvareverkstedet/origin/calendarChanges
Removed hackekveld temporarily
2022-03-26 20:39:46 +01:00
a80114be7a Removed hackekveld temporarily 2022-03-26 20:34:05 +01:00
ab6edd92e1 Merge pull request from Programvareverkstedet/calendarChanges
Lagt til hackekveld fjerdehver uke
2022-03-15 13:11:13 +01:00
6070cf4bd3 Lagt til hackekveld fjerdehver uke 2022-02-17 16:04:42 +01:00
fdd1d12e9e allow uppercase file extensions 2022-01-31 12:32:33 +01:00
6cae4cb0e2 :) 2022-01-29 22:54:52 +01:00
15c9431c10 remove nerdepitsa
no-one whent there
2022-01-29 19:34:13 +01:00
500ea63c77 Merge pull request from Programvareverkstedet/AddDriftkveld
Added biweekly driftkveld
2022-01-29 19:33:54 +01:00
bbe51e702b Added biweekly driftkveld 2022-01-29 19:21:16 +01:00
ab1bbdd3df Forgot login 2022-01-25 16:23:14 +01:00
893f57c9b7 Forgot to remove line 2022-01-25 15:08:37 +01:00
184d40266b Clarify dependencies 2022-01-25 15:06:27 +01:00
82 changed files with 4086 additions and 2758 deletions

4
.gitignore vendored

@ -1,5 +1,5 @@
/sql_config.php
/dataporten_config.php
/result
/config.php
*.sqlite
/test.sql
/vendor/

@ -2,6 +2,6 @@
FROM php:7.4-cli
RUN apt-get update && \
apt-get install -y \
sqlite \
sqlite3 \
unzip \
git

@ -13,7 +13,9 @@ Put it in a folder your webserver can find.
Make sure you have `sqlite3`, `php` and `pdo-sqlite` installed.
These can be obtained from your package manager.
Then make sure PHP has the `curl`, `pdo-sqlite` and `sqlite3` extensions enabled, see `/etc/php/php.ini`.
Then make sure PHP has the `curl`, `pdo-sqlite`, `ext-dom` and `sqlite3` extensions enabled, see `/etc/php/php.ini`.
To install all mentioned requirements on debian: `apt install composer sqlite3 php8.1-sqlite3 simplesamlphp php-xml`, followed by `composer update`.
./dev.sh

@ -1,9 +1,15 @@
{
"require": {
"simplesamlphp/simplesamlphp": "~1",
"erusev/parsedown": "^1.7"
},
"require-dev": {
"ext-pdo_sqlite": "*"
}
"name": "programvareverkstedet/nettsiden",
"description": "Programvareverkstedets nettside",
"license": "proprietary",
"config": {
"allow-plugins": {
"simplesamlphp/composer-module-installer": true
}
},
"require": {
"simplesamlphp/simplesamlphp": "^2.2",
"erusev/parsedown": "^1.7"
}
}

3880
composer.lock generated

File diff suppressed because it is too large Load Diff

11
dev.bat

@ -1,11 +0,0 @@
REM php og sqlite3 må være tilgjengelig i PATH
IF EXIST "sql_config.php" goto config_end
cp sql_config_example.php sql_config.php
:config_end
IF EXIST "pvv.sqlite" goto sqlite_end
sqlite3 pvv.sqlite < pvv.sql
:sqlite_end
php -S [::1]:1080 -t www/ -c php.ini

25
dev.sh

@ -1,25 +0,0 @@
#!/bin/sh
which sqlite3 > /dev/null 2>&1 || (echo ERROR: sqlite not found; false) || exit 1
test ! -e pvv.sqlite && sqlite3 pvv.sqlite < dist/pvv.sql
test ! -e sql_config.php && cp -v dist/sql_config_example.php sql_config.php
test ! -e dataporten_config.php && cp -v dist/dataporten_config.php dataporten_config.php
test -e composer.phar || curl -O https://getcomposer.org/composer.phar
if test ! -f lib/OAuth2-Client/OAuth2Client.php ; then
echo Missing git submodules. Installing...
(set -x; git submodule update --init --recursive) || exit $?
fi
if test ! -d vendor; then
php composer.phar install || exit $?
cp -v dist/authsources_example.php vendor/simplesamlphp/simplesamlphp/config/authsources.php
cp -v dist/saml20-idp-remote.php vendor/simplesamlphp/simplesamlphp/metadata/saml20-idp-remote.php
# cp -v vendor/simplesamlphp/simplesamlphp/config-templates/config.php vendor/simplesamlphp/simplesamlphp/config/config.php
sed -e "s/'trusted.url.domains' => array()/'trusted.url.domains' => array('localhost:1080')/g" < vendor/simplesamlphp/simplesamlphp/config-templates/config.php > vendor/simplesamlphp/simplesamlphp/config/config.php
ln -s ../vendor/simplesamlphp/simplesamlphp/www/ www/simplesaml
fi
php -S ${DOCKER_HOST:-[::1]}:${DOCKER_PORT:-1080} -d error_reporting=E_ALL -d display_errors=1 -t www/

24
dist/config.local.php vendored Normal file

@ -0,0 +1,24 @@
<?php
$DB_DSN = 'sqlite:'.__DIR__.DIRECTORY_SEPARATOR.'pvv.sqlite';
$DB_USER = null;
$DB_PASS = null;
$DOOR_SECRET = "changeme";
$GALLERY_DIR = __DIR__.DIRECTORY_SEPARATOR.'www'.DIRECTORY_SEPARATOR.'galleri'.DIRECTORY_SEPARATOR.'bilder'.DIRECTORY_SEPARATOR.'gallery';
$GALLERY_SERVER_PATH = '/images/gallery/';
$SLIDESHOW_DIR = __DIR__.DIRECTORY_SEPARATOR.'www'.DIRECTORY_SEPARATOR.'galleri'.DIRECTORY_SEPARATOR.'bilder'.DIRECTORY_SEPARATOR.'slideshow';
$SLIDESHOW_SERVER_PATH = '/images/slideshow/';
$SAML_COOKIE_SALT = 'changeme';
$SAML_COOKIE_SECURE = false;
$SAML_TRUSTED_DOMAINS = array("localhost:1080");
$SAML_ADMIN_PASSWORD = "changeme";
$SAML_ADMIN_NAME = 'PVV Drift';
$SAML_ADMIN_EMAIL = 'drift@pvv.ntnu.no';
$CACHE_DIRECTORY = __DIR__.DIRECTORY_SEPARATOR.'cache';
?>

6
dist/config.source-env.php vendored Normal file

@ -0,0 +1,6 @@
<?php
$configFile = getenv('PVV_CONFIG_FILE');
require_once($configFile);
?>

@ -1,21 +0,0 @@
<?php
# go to https://auth.dataporten.no/
$dataportenConfig = [
'client_id' => "",
'client_secret' => "",
//'redirect_uri' => "https://pvv.ntnu.no/paamelding/",
'redirect_uri' => "http://localhost:1080/paamelding/",
'auth' => "https://auth.dataporten.no/oauth/authorization",#Authorization endpoint
'token' => "https://auth.dataporten.no/oauth/token",#Token endpoint
/* OPTIONAL */
# 'authorization_type' => "Bearer",
# 'session' => false,
# 'verify' => 1,
# 'grant_type' => "authorization_code",
# 'response_type' => "code",
# 'scope' => "",
];

4
dist/pvv.sql vendored

@ -36,7 +36,7 @@ CREATE TABLE "motd" (
"content" TEXT
);
INSERT INTO motd (title, content)
VALUES ("MOTD ./dev.sh", "du kan endre motd i admin panelet");
VALUES ('MOTD ./dev.sh', 'du kan endre motd i admin panelet');
CREATE TABLE "door" (
"time" INTEGER PRIMARY KEY,
@ -47,4 +47,4 @@ VALUES (0, FALSE);
INSERT INTO users (uname, groups)
VALUES ("min_test_bruker", 1);
VALUES ('min_test_bruker', 1);

1301
dist/simplesamlphp-config.php vendored Normal file

File diff suppressed because it is too large Load Diff

@ -1,6 +0,0 @@
<?php
$dbDsn = 'sqlite:'.__DIR__.DIRECTORY_SEPARATOR.'pvv.sqlite';
$dbUser = null;
$dbPass = null;
$doorSensorSecret = "OGJiZTdjZDctMmFkNy00ZjZjLTk3OGItOTA3NzU3ZDM2Yjlm";

27
flake.lock generated Normal file

@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1707939175,
"narHash": "sha256-D1xan0lgxbmXDyzVqXTiSYHLmAMrMRdD+alKzEO/p3w=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f7e8132daca31b1e3859ac0fb49741754375ac3d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

41
flake.nix Normal file

@ -0,0 +1,41 @@
{
description = "Programvareverkstedet/nettsiden PHP environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
};
outputs = { self, nixpkgs }:
let
systems = [
"x86_64-linux"
"aarch64-linux"
"aarch64-darwin"
];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: let
pkgs = nixpkgs.legacyPackages.${system};
in f system pkgs);
in {
packages = forAllSystems (system: pkgs: {
default = self.packages.${system}.pvv-nettsiden;
pvv-nettsiden = pkgs.callPackage ./nix/package.nix { php = pkgs.php82; };
});
overlays.default = final: prev: {
inherit (self.packages.${final.system}) pvv-nettsiden;
formats = prev.formats // {
php = import ./nix/php-generator.nix { pkgs = prev; lib = prev.lib; };
};
};
nixosModules.default = nix/module.nix;
devShells = forAllSystems (system: pkgs: {
default = pkgs.callPackage ./nix/shell.nix { inherit pkgs; };
});
# TODO:
# - Relicense the project to GPL or something
# - Write a module for the project
};
}

@ -1,8 +1,8 @@
<?php
require __DIR__ . '/../src/_autoload.php';
require __DIR__ . '/../sql_config.php';
require __DIR__ . '/../config.php';
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$agenda = new \pvv\side\Agenda([
new \pvv\side\social\NerdepitsaActivity,

@ -5,10 +5,7 @@
require_once __DIR__ . DIRECTORY_SEPARATOR . 'agenda.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'navbar.php';
require_once dirname(__DIR__) . implode(DIRECTORY_SEPARATOR, ['', 'lib', 'OAuth2-Client', 'OAuth2Client.php']);
require_once dirname(__DIR__) . implode(DIRECTORY_SEPARATOR, ['', 'dataporten_config.php']);
require_once dirname(__DIR__) . implode(DIRECTORY_SEPARATOR, ['', 'sql_config.php']);
require_once dirname(__DIR__) . implode(DIRECTORY_SEPARATOR, ['', 'config.php']);
require_once dirname(__DIR__) . implode(DIRECTORY_SEPARATOR, ['', 'src', '_autoload.php']);
require_once dirname(__DIR__) . implode(DIRECTORY_SEPARATOR, ['', 'vendor', 'simplesamlphp', 'simplesamlphp', 'lib', '_autoload.php']);
@ -16,19 +13,21 @@ require_once dirname(__DIR__) . implode(DIRECTORY_SEPARATOR, ['', 'vendor', 'sim
date_default_timezone_set('Europe/Oslo');
setlocale(LC_ALL, 'nb_NO');
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$userManager = new \pvv\admin\UserManager($pdo);
$sp = 'default-sp';
$as = new SimpleSAML_Auth_Simple($sp);
$as = new \SimpleSAML\Auth\Simple($sp);
use \pvv\side\Agenda;
$agenda = new \pvv\side\Agenda([
new \pvv\side\social\NerdepitsaActivity,
// new \pvv\side\social\AnimekveldActivity,
// new \pvv\side\social\DriftkveldActivity,
new \pvv\side\DBActivity($pdo),
]);
// new \pvv\side\social\NerdepitsaActivity,
// new \pvv\side\social\AnimekveldActivity,
// new \pvv\side\social\HackekveldActivity,
// new \pvv\side\social\BrettspillActivity,
// new \pvv\side\social\DriftkveldActivity,
new \pvv\side\DBActivity($pdo),
]);
$months_translations = ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember'];

@ -8,11 +8,11 @@ function navbar($depth, $active = NULL) {
//'Aktiviteter' => 'aktiviteter',
'Prosjekter' => 'prosjekt',
'Kontakt' => 'kontakt',
'Webmail' => 'mail',
//'Webmail' => 'https://webmail.pvv.ntnu.no/',
'Webmail' => 'https://webmail.pvv.ntnu.no/roundcube/',
'Galleri' => 'galleri',
'Wiki' => 'pvv',
'Wiki' => 'https://wiki.pvv.ntnu.no/',
'Git' => 'https://git.pvv.ntnu.no/',
'Tjenester' => 'tjenester',
];
foreach($menuItems as $caption => $link) {
$isActive = $active === $link;
@ -35,7 +35,7 @@ function loginBar($sp = null, $pdo = null) {
if (is_null($sp)) $sp = 'default-sp';
$result = "\n";
require_once(__DIR__ . '/../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple($sp);
$as = new \SimpleSAML\Auth\Simple($sp);
$svg = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 64 64">
<circle cx="32" cy="27" r="14" stroke-width="0" />
@ -51,17 +51,17 @@ function loginBar($sp = null, $pdo = null) {
$userManager = new \pvv\admin\UserManager($pdo);
$isAdmin = $userManager->isAdmin($uname);
}
$result .= "\t<a id=\"login\" href=\"#usermenu\" aria-hidden=\"true\">${svg}" . htmlspecialchars($uname) . "</a>\n";
$result .= "\t<a id=\"login\" href=\"#usermenu\" aria-hidden=\"true\">{$svg}" . htmlspecialchars($uname) . "</a>\n";
$result .= "\n\t<ul id=\"usermenu\">\n";
$result .= "\n\t\t<li><a id=\"login\" href=\"#\">${svg}" . htmlspecialchars($uname) . "</a></li>\n";
$result .= "\n\t\t<li><a id=\"login\" href=\"#\">{$svg}" . htmlspecialchars($uname) . "</a></li>\n";
if ($isAdmin) {
$result .= "\n\t\t<li><a href=\"/admin/\">Admin</a></li>\n";
}
$result .= "\n\t\t<li><a href=\"" . htmlspecialchars($as->getLogoutURL()) . "\">Logg ut</a></li>\n";
$result .= "\n\t</ul>\n";
} else {
$result .= "\t<a id=\"login\" href=\"" . htmlspecialchars($as->getLoginURL()) . "\">${svg}Logg inn</a>\n";
$result .= "\t<a id=\"login\" href=\"" . htmlspecialchars($as->getLoginURL()) . "\">{$svg}Logg inn</a>\n";
}
$result .= "\n\t<a href=\"#menu\" id=\"menu_toggle\" aria-hidden=\"true\"><big>&#9776;&nbsp;</big>MENU</a>\n";

@ -1,35 +0,0 @@
<ul id="ticker">
<?php
{
require __DIR__ . '/../src/_autoload.php';
require __DIR__ . '/../sql_config.php';
$translation = ['i dag', 'i morgen', 'denne uken', 'neste uke', 'denne måneden', 'neste måned'];
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$agenda = new \pvv\side\Agenda([
new \pvv\side\social\NerdepitsaActivity,
new \pvv\side\social\AnimekveldActivity,
new \pvv\side\DBActivity($pdo),
]);
$test = true;
foreach($agenda->getNextDays() as $period => $events) {
if (!$events) continue;
$i = 0;
$n = count($events);
foreach($events as $event){
if ($i == 0){
echo '<li style="text-align: center;"><span style="text-transform: uppercase;">' . $translation[$period] . '</span>: ';
} else if ($i < $n-1) {
echo '<i style="opacity:0.7;">,&nbsp;</i>';
} else{
echo '<i style="opacity:0.7;">&nbsp;og&nbsp;</i>';
}
echo '<a href="' . $event->getURL() . '">' . $event->getName() . '</a>';
$i = $i + 1;
}
break;
}
}
?>
</ul>

254
nix/module.nix Normal file

@ -0,0 +1,254 @@
{ config, lib, pkgs, ... }:
let
cfg = config.services.pvv-nettsiden;
inherit (lib) mkDefault mkEnableOption mkPackageOption mkIf mkOption types mdDoc;
format = pkgs.formats.php { };
in
{
options.services.pvv-nettsiden = {
enable = mkEnableOption (lib.mdDoc "Enable pvv-nettsiden");
package = mkPackageOption pkgs "pvv-nettsiden" { };
user = mkOption {
type = types.str;
default = "pvv-nettsiden";
description = mdDoc "User to run php-fpm and own the image directories";
};
group = mkOption {
type = types.str;
default = "pvv-nettsiden";
description = mdDoc "Group to run php-fpm and own the image directories";
};
domainName = mkOption {
type = types.str;
default = "www.pvv.no";
description = mdDoc "Domain name for the website";
};
enableNginx = mkEnableOption "nginx" // { default = true; };
useSSL = mkEnableOption "secure cookies" // { default = true; };
settings = mkOption {
description = "Settings for the website";
default = { };
type = types.submodule {
freeformType = format.type;
options = lib.mapAttrsRecursiveCond
(attrs: !(attrs ? "type"))
(_: option: option // { type = types.either option.type format.lib.types.raw; })
{
DOOR_SECRET = mkOption {
type = types.str;
description = mdDoc "Secret for the door sensor API";
};
GALLERY = {
DIR = mkOption {
type = types.path;
default = "/var/lib/pvv-nettsiden/gallery";
description = mdDoc "Directory where the gallery is located. See documentation at TODO";
};
SERVER_PATH = mkOption {
type = types.str;
default = "/static/gallery";
description = mdDoc "Path to the gallery on the server";
};
};
SLIDESHOW = {
DIR = mkOption {
type = types.path;
default = "/var/lib/pvv-nettsiden/slideshow";
description = mdDoc "Directory where the slideshow is located. See documentation at TODO";
};
SERVER_PATH = mkOption {
type = types.str;
default = "/static/slideshow";
description = mdDoc "Path to the slideshow on the server";
};
};
DB = {
DSN = mkOption {
type = types.str;
default = "sqlite:/var/lib/pvv-nettsiden/pvv-nettsiden.db";
example = "pgsql:host=localhost;port=5432;dbname=testdb;user=bruce;password=mypass";
description = mdDoc "Database connection string, see https://www.php.net/manual/en/pdo.construct.php";
};
USER = mkOption {
type = with types; nullOr str;
default = null;
example = "pvv-nettsiden";
description = mdDoc "Database user";
};
PASS = mkOption {
type = with types; nullOr str;
default = null;
description = mdDoc "Database password. Recommends: null, set in extraConfig";
};
};
SAML = {
COOKIE_SALT = mkOption {
type = types.str;
description = mdDoc "Salt for the SAML cookies";
};
COOKIE_SECURE = mkOption {
type = types.bool;
default = true;
description = mdDoc "Whether to set the secure flag on the SAML cookies";
};
ADMIN_NAME = mkOption {
type = types.str;
description = mdDoc "Name for the admin user";
};
ADMIN_EMAIL = mkOption {
type = types.str;
description = mdDoc "Email for the admin user";
};
ADMIN_PASSWORD = mkOption {
type = types.str;
description = mdDoc "Password for the admin user";
};
TRUSTED_DOMAINS = mkOption {
type = types.listOf types.str;
default = [ cfg.domainName ];
description = mdDoc "List of trusted domains for the SAML service";
};
};
CACHE_DIRECTORY = mkOption {
type = types.path;
default = "/var/cache/pvv-nettsiden/simplesamlphp";
description = mdDoc "List of trusted domains for the SAML service";
};
};
};
};
};
config = mkIf cfg.enable (let
# NOTE: This should absolutely not be necessary, but for some reason this file refuses to import
# the toplevel configuration file.
# NOTE: Nvm, don't this this was the problem after all?
finalPackage = cfg.package.overrideAttrs (_: _: {
postInstall = let
f = x: lib.escapeShellArg (format.lib.valueToString x);
in cfg.package.postInstall + ''
substituteInPlace $out/${cfg.package.passthru.simplesamlphpPath}/config/config.php \
--replace '$SAML_COOKIE_SECURE' ${f cfg.settings.SAML.COOKIE_SECURE} \
--replace '$SAML_COOKIE_SALT' ${f cfg.settings.SAML.COOKIE_SALT} \
--replace '$SAML_ADMIN_PASSWORD' ${f cfg.settings.SAML.ADMIN_PASSWORD} \
--replace '$SAML_ADMIN_NAME' ${f cfg.settings.SAML.ADMIN_NAME} \
--replace '$SAML_ADMIN_EMAIL' ${f cfg.settings.SAML.ADMIN_EMAIL} \
--replace '$SAML_TRUSTED_DOMAINS' ${f cfg.settings.SAML.TRUSTED_DOMAINS} \
--replace '$CACHE_DIRECTORY' ${f cfg.settings.CACHE_DIRECTORY}
'';
});
in {
users.users = mkIf (cfg.user == "pvv-nettsiden") {
"pvv-nettsiden" = {
description = "PVV Website Service User";
group = cfg.group;
createHome = false;
isSystemUser = true;
};
};
users.groups = mkIf (cfg.group == "pvv-nettsiden") {
"pvv-nettsiden" = { };
};
systemd.tmpfiles.settings."10-pvv-nettsiden".${cfg.settings.CACHE_DIRECTORY}.d = {
inherit (cfg) user group;
mode = "0770";
};
services.nginx = mkIf cfg.enableNginx {
enable = true;
recommendedGzipSettings = mkDefault true;
recommendedProxySettings = mkDefault true;
virtualHosts."${cfg.domainName}" = {
forceSSL = mkDefault cfg.useSSL;
enableACME = mkDefault true;
locations = {
"/" = {
root = "${finalPackage}/share/php/pvv-nettsiden/www/";
index = "index.php";
};
"~ \\.php$".extraConfig = ''
include ${pkgs.nginx}/conf/fastcgi_params;
fastcgi_param SCRIPT_FILENAME ${finalPackage}/share/php/pvv-nettsiden/www$fastcgi_script_name;
fastcgi_pass unix:${config.services.phpfpm.pools."pvv-nettsiden".socket};
'';
# based on https://simplesamlphp.org/docs/stable/simplesamlphp-install.html#configuring-nginx
"^~ /simplesaml/" = {
alias = "${finalPackage}/${finalPackage.passthru.simplesamlphpPath}/public/";
index = "index.php";
extraConfig = ''
location ~ ^/simplesaml/(?<phpfile>.+?\.php)(?<pathinfo>/.*)?$ {
include ${pkgs.nginx}/conf/fastcgi_params;
fastcgi_pass unix:${config.services.phpfpm.pools."pvv-nettsiden".socket};
fastcgi_param SCRIPT_FILENAME ${finalPackage}/${finalPackage.passthru.simplesamlphpPath}/public/$phpfile;
# Must be prepended with the baseurlpath
fastcgi_param SCRIPT_NAME /simplesaml/$phpfile;
fastcgi_param PATH_INFO $pathinfo if_not_empty;
}
'';
};
"^~ ${cfg.settings.GALLERY.SERVER_PATH}" = {
root = cfg.settings.GALLERY.DIR;
extraConfig = ''
rewrite ^${cfg.settings.GALLERY.SERVER_PATH}/(.*)$ /$1 break;
'';
};
"^~ ${cfg.settings.SLIDESHOW.SERVER_PATH}" = {
root = cfg.settings.SLIDESHOW.DIR;
extraConfig = ''
rewrite ^${cfg.settings.SLIDESHOW.SERVER_PATH}/(.*)$ /$1 break;
'';
};
};
};
};
services.phpfpm.pools.pvv-nettsiden = {
user = cfg.user;
group = cfg.group;
phpEnv.PVV_CONFIG_FILE = toString (format.generate "pvv-nettsiden-conf.php" cfg.settings);
settings = {
"listen.owner" = config.services.nginx.user;
"listen.group" = config.services.nginx.group;
"pm" = mkDefault "ondemand";
"pm.max_children" = mkDefault 32;
"pm.process_idle_timeout" = mkDefault "10s";
"pm.max_requests" = mkDefault 500;
};
};
});
}

28
nix/package.nix Normal file

@ -0,0 +1,28 @@
{ lib
, php
, extra_files ? { }
}:
php.buildComposerProject rec {
src = ./..;
pname = "pvv-nettsiden";
version = "0.0.1";
vendorHash = "sha256-sWC5E60toa95re2NugvHVPT+vKYXc1I6Z3rSqXxTUu0=";
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
${lib.pipe extra_files [
(lib.mapAttrsToList (target_path: source_path: ''
mkdir -p $(dirname "$out/${target_path}")
cp -r "${source_path}" "$out/${target_path}"
''))
(lib.concatStringsSep "\n")
]}
'';
}

62
nix/php-generator.nix Normal file

@ -0,0 +1,62 @@
{ pkgs, lib }:
with lib;
{ }: let
valueToString = val:
if val == null then
"null"
else if isString val then
builtins.toJSON val
else if isBool val then
boolToString val
else if isInt val || isFloat val then
toString val
else if isList val then
"array(${concatMapStringsSep ", " valueToString val})"
else if isAttrs val && val ? value && (val._type or "") == "raw" then
val.value
else if isAttrs val then
throw "Found unexpected attrs, that were not created by mkRaw. Have you put attrs in an array?\n${val}"
else throw "unsupported :')";
in {
inherit (pkgs.formats.json { }) type;
generate = name: value: let
flattenStructuredSettings = attrs: let
partitionAttrs = pred: attrs: lib.pipe attrs [
attrsToList
(partition ({ name, value }: pred name value))
(mapAttrs (_: listToAttrs))
];
partitionedAttrs = partitionAttrs (_: v: isAttrs v && !(v ? value && (v._type or "") == "raw")) attrs;
flattenedAttrs = lib.pipe partitionedAttrs.right [
(mapAttrs (n1: mapAttrs' (n2: v2: nameValuePair "${n1}_${n2}" v2)))
attrValues
(map flattenStructuredSettings)
(foldl recursiveUpdate { })
];
in recursiveUpdate flattenedAttrs partitionedAttrs.wrong;
content = lib.pipe value [
flattenStructuredSettings
(mapAttrs (_: valueToString))
(mapAttrsToList (n: v: ''''$${n} = ${v};''))
(concatStringsSep "\n")
(content: "<?php\n${content}\n?>")
];
in pkgs.writeText name content;
lib = {
inherit valueToString;
mkRaw = value: {
inherit value;
_type = "raw";
};
types.raw = lib.types.attrs;
};
}

35
nix/shell.nix Normal file

@ -0,0 +1,35 @@
{ pkgs }:
let
phpEnv = pkgs.php82.buildEnv {
extensions = { enabled, all }: enabled ++ (with all; [ iconv mbstring pdo_mysql pdo_sqlite ]);
};
in
pkgs.mkShellNoCC {
buildInputs = with pkgs; [
phpEnv
php82Packages.composer
sqlite
git
];
shellHook = ''
alias runDev='php -S localhost:1080 -d error_reporting=E_ALL -d display_errors=1 -t www/'
# Prepare dev environment with sqlite and config files
test -e pvv.sqlite || sqlite3 pvv.sqlite < dist/pvv.sql
test -e config.php || cp -v dist/config.local.php config.php
if [ ! -d vendor ] ; then
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/www/ www/simplesaml
fi
'';
}

@ -32,7 +32,7 @@ class UserManager{
}
public function addGroup($uname, $group){
$userFlags = $this->getUsergroups();
$userFlags = $this->getUsergroups($uname);
if($userFlags){
$newFlags = ($group | $userFlags);
@ -41,7 +41,7 @@ class UserManager{
}
public function removeGroup($uname, $group){
$userFlags = $this->getUsergroups();
$userFlags = $this->getUsergroups($uname);
if($userFlags){
$newFlags = ($userFlags & (~ $group));
@ -131,4 +131,4 @@ class UserManager{
return $users;
}
}
}

@ -8,20 +8,25 @@ use \DateInterval;
class BrettspillActivity implements Activity {
public function nextDate(DateTimeImmutable $date) {
if ($date->format('H') > 20 || $date->format('H') == 19 && $date->format('i') > 30)
return $this->nextDate($date->add(new DateInterval('P1D'))->setTime(19, 30, 0));
$date = $date->setTime(19, 30, 0);
if ($date->format('N') != 4)
if ($date->format('H') > 17 || $date->format('H') == 16 && $date->format('i') > 15)
return $this->nextDate($date->add(new DateInterval('P1D'))->setTime(16, 15, 0));
$date = $date->setTime(16, 15, 0);
if ($date->format('N') != 7)
return $this->nextDate($date->add(new DateInterval('P1D')));
if ($date->format('W') % 2 - 1)
return $this->nextDate($date->add(new DateInterval('P7D')));
return $date;
}
public function prevDate(DateTimeImmutable $date) {
if ($date->format('H') < 19 || $date->format('H') == 20 && $date->format('i') < 30)
return $this->prevDate($date->sub(new DateInterval('P1D'))->setTime(19, 30, 0));
$date = $date->setTime(19, 30, 0);
if ($date->format('N') != 4)
if ($date->format('H') < 16 || $date->format('H') == 17 && $date->format('i') < 15)
return $this->prevDate($date->sub(new DateInterval('P1D'))->setTime(16, 15, 0));
$date = $date->setTime(16, 15, 0);
if ($date->format('N') != 7)
return $this->prevDate($date->sub(new DateInterval('P1D')));
if ($date->format('W') % 2 - 1)
return $this->prevDate($date->sub(new DateInterval('P7D')));
return $date;
}

@ -16,11 +16,11 @@ class BrettspillEvent extends Event {
}
public function getLocation() /* : Location */ {
return "Koserommet";
return "Programvareverkstedet";
}
public function getOrganiser() /* : User */ {
return "Robert Maikher";
return "Programvareverkstedet";
}
public function getURL() /* : string */ {
@ -33,8 +33,8 @@ class BrettspillEvent extends Event {
public function getDescription() {
return [
'Er du en hardcore brettspillentusiast eller en nybegynner som har så vidt spilt ludo?'.
'Da er vår brettspillkveld noe for deg!' .
'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!',
'',
'## Vår samling',

@ -11,8 +11,10 @@ class DriftkveldActivity implements Activity {
if ($date->format('H') > 18 || $date->format('H') == 17 && $date->format('i') > 30)
return $this->nextDate($date->add(new DateInterval('P1D'))->setTime(18, 15, 0));
$date = $date->setTime(18, 15, 0);
if ($date->format('N') != 2)
if ($date->format('N') != 6)
return $this->nextDate($date->add(new DateInterval('P1D')));
if ($date->format('W') % 2 - 1)
return $this->nextDate($date->add(new DateInterval('P7D')));
return $date;
}
@ -20,8 +22,10 @@ class DriftkveldActivity implements Activity {
if ($date->format('H') < 17 || $date->format('H') == 18 && $date->format('i') < 30)
return $this->prevDate($date->sub(new DateInterval('P1D'))->setTime(18, 15, 0));
$date = $date->setTime(18, 15, 0);
if ($date->format('N') != 2)
if ($date->format('N') != 6)
return $this->prevDate($date->sub(new DateInterval('P1D')));
if ($date->format('W') % 2 - 1)
return $this->prevDate($date->sub(new DateInterval('P7D')));
return $date;
}

@ -24,7 +24,7 @@ class DriftkveldEvent extends Event {
}
public function getURL() /* : string */ {
return '/drift/';
return '/driftkveld/';
}
public function getImageURL() {
@ -37,7 +37,7 @@ class DriftkveldEvent extends Event {
'Vil du være kul kis TM?',
'Kom på driftkveld!',
'',
'Vi møtes en gang i uka for å ta unna driftarbeid og drikke kaffe.',
'Vi møtes annenhver uke for å ta unna driftarbeid og drikke kaffe.',
'Alle PVVere er velkommene, enten de er erfarne driftere eller helt utenforstående!'
];
}

@ -0,0 +1,40 @@
<?php //declare(strict_types=1);
namespace pvv\side\social;
use \pvv\side\Activity;
use \DateTimeImmutable;
use \DateInterval;
class HackekveldActivity implements Activity {
public function nextDate(DateTimeImmutable $date) {
if ($date->format('H') > 18 || $date->format('H') == 17 && $date->format('i') > 30)
return $this->nextDate($date->add(new DateInterval('P1D'))->setTime(18, 15, 0));
$date = $date->setTime(16, 15, 0);
if ($date->format('N') != 6)
return $this->nextDate($date->add(new DateInterval('P1D')));
if ($date->format('W') % 2)
return $this->nextDate($date->add(new DateInterval('P7D')));
return $date;
}
public function prevDate(DateTimeImmutable $date) {
if ($date->format('H') < 17 || $date->format('H') == 18 && $date->format('i') < 30)
return $this->prevDate($date->sub(new DateInterval('P1D'))->setTime(18, 15, 0));
$date = $date->setTime(18, 15, 0);
if ($date->format('N') != 6)
return $this->prevDate($date->sub(new DateInterval('P1D')));
if ($date->format('W') % 2)
return $this->prevDate($date->sub(new DateInterval('P7D')));
return $date;
}
public function getNextEventFrom(DateTimeImmutable $date) /* : Event */ {
return new HackekveldEvent($this->nextDate($date));
}
public function getPreviousEventFrom(DateTimeImmutable $date) /* : Event */ {
return new HackekveldEvent($this->prevDate($date));
}
}

@ -0,0 +1,45 @@
<?php //declare(strict_types=1);
namespace pvv\side\social;
use \pvv\side\Event;
use \DateInterval;
class HackekveldEvent extends Event {
public function getStop() {
return $this->getStart()->add(new DateInterval('PT4H1800S'));
}
public function getName() /* : string */ {
return "Hackekveld";
}
public function getLocation() /* : Location */ {
return "Terminalrommet / Discord / IRC";
}
public function getOrganiser() /* : User */ {
return "PVV";
}
public function getURL() /* : string */ {
return '#';
}
public function getImageURL() {
return '/pvv-logo.png';
}
public function getDescription() {
return [
'Mange PVV-medlemmer liker å programmere.',
'Hvis du også liker å programmere, så bli med! Her kan du jobbe med dine egne prosjekter eller starte noe med andre nerder her på huset. Vi møtes for en hyggelig prat, mye god programmering og delsponset pizza.'
];
}
public function getColor() {
return "#35a";
}
}

@ -0,0 +1,31 @@
<?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>

@ -1,12 +1,12 @@
<?php
require __DIR__ . '/../../../src/_autoload.php';
require __DIR__ . '/../../../sql_config.php';
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
require __DIR__ . '/../../../config.php';
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$userManager = new \pvv\admin\UserManager($pdo);
require_once(__DIR__ . '/../../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$uname = $attrs['uid'][0];

@ -3,14 +3,14 @@ date_default_timezone_set('Europe/Oslo');
setlocale(LC_ALL, 'nb_NO');
require __DIR__ . '/../../../inc/navbar.php';
require __DIR__ . '/../../../src/_autoload.php';
require __DIR__ . '/../../../sql_config.php';
require __DIR__ . '/../../../config.php';
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$userManager = new \pvv\admin\UserManager($pdo);
require_once(__DIR__ . '/../../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$uname = $attrs['uid'][0];

@ -3,14 +3,14 @@ date_default_timezone_set('Europe/Oslo');
setlocale(LC_ALL, 'nb_NO');
require __DIR__ . '/../../../inc/navbar.php';
require __DIR__ . '/../../../src/_autoload.php';
require __DIR__ . '/../../../sql_config.php';
require __DIR__ . '/../../../config.php';
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$userManager = new \pvv\admin\UserManager($pdo);
require_once(__DIR__ . '/../../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$uname = $attrs['uid'][0];

@ -2,13 +2,13 @@
date_default_timezone_set('Europe/Oslo');
setlocale(LC_ALL, 'nb_NO');
require __DIR__ . '/../../../src/_autoload.php';
require __DIR__ . '/../../../sql_config.php';
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
require __DIR__ . '/../../../config.php';
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$userManager = new \pvv\admin\UserManager($pdo);
require_once(__DIR__ . '/../../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$uname = $attrs['uid'][0];

@ -5,17 +5,17 @@ setlocale(LC_ALL, 'nb_NO');
error_reporting(E_ALL);
require __DIR__ . '/../../../inc/navbar.php';
require __DIR__ . '/../../../src/_autoload.php';
require __DIR__ . '/../../../sql_config.php';
require __DIR__ . '/../../../config.php';
require_once(__DIR__ . '/../../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$attrs = $as->getAttributes();
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$userManager = new \pvv\admin\UserManager($pdo);
require_once(__DIR__ . '/../../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$uname = $attrs['uid'][0];

@ -4,13 +4,13 @@ date_default_timezone_set('Europe/Oslo');
setlocale(LC_ALL, 'nb_NO');
error_reporting(E_ALL);
require __DIR__ . '/../../../src/_autoload.php';
require __DIR__ . '/../../../sql_config.php';
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
require __DIR__ . '/../../../config.php';
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$userManager = new \pvv\admin\UserManager($pdo);
require_once(__DIR__ . '/../../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$uname = $attrs['uid'][0];

@ -1,11 +1,11 @@
<?php
require_once dirname(dirname(__DIR__)) . implode(DIRECTORY_SEPARATOR, ['', 'inc', 'include.php']);
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$userManager = new \pvv\admin\UserManager($pdo);
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$uname = $attrs['uid'][0];

@ -5,17 +5,17 @@ setlocale(LC_ALL, 'no_NO');
error_reporting(E_ALL);
require __DIR__ . '/../../../inc/navbar.php';
require __DIR__ . '/../../../src/_autoload.php';
require __DIR__ . '/../../../sql_config.php';
require __DIR__ . '/../../../config.php';
require_once(__DIR__ . '/../../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$attrs = $as->getAttributes();
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$userManager = new \pvv\admin\UserManager($pdo);
require_once(__DIR__ . '/../../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$uname = $attrs['uid'][0];

@ -4,13 +4,13 @@ date_default_timezone_set('Europe/Oslo');
setlocale(LC_ALL, 'no_NO');
error_reporting(E_ALL);
require __DIR__ . '/../../../src/_autoload.php';
require __DIR__ . '/../../../sql_config.php';
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
require __DIR__ . '/../../../config.php';
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$userManager = new \pvv\admin\UserManager($pdo);
require_once(__DIR__ . '/../../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$uname = $attrs['uid'][0];

@ -1,12 +1,12 @@
<?php
require __DIR__ . '/../../../src/_autoload.php';
require __DIR__ . '/../../../sql_config.php';
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
require __DIR__ . '/../../../config.php';
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$userManager = new \pvv\admin\UserManager($pdo);
require_once(__DIR__ . '/../../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$uname = $attrs['uid'][0];

@ -3,14 +3,14 @@ date_default_timezone_set('Europe/Oslo');
setlocale(LC_ALL, 'nb_NO');
require __DIR__ . '/../../../inc/navbar.php';
require __DIR__ . '/../../../src/_autoload.php';
require __DIR__ . '/../../../sql_config.php';
require __DIR__ . '/../../../config.php';
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$userManager = new \pvv\admin\UserManager($pdo);
require_once(__DIR__ . '/../../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$uname = $attrs['uid'][0];

@ -3,14 +3,14 @@ date_default_timezone_set('Europe/Oslo');
setlocale(LC_ALL, 'nb_NO');
require __DIR__ . '/../../../inc/navbar.php';
require __DIR__ . '/../../../src/_autoload.php';
require __DIR__ . '/../../../sql_config.php';
require __DIR__ . '/../../../config.php';
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$userManager = new \pvv\admin\UserManager($pdo);
require_once(__DIR__ . '/../../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$uname = $attrs['uid'][0];

@ -2,13 +2,13 @@
date_default_timezone_set('Europe/Oslo');
setlocale(LC_ALL, 'nb_NO');
require __DIR__ . '/../../../src/_autoload.php';
require __DIR__ . '/../../../sql_config.php';
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
require __DIR__ . '/../../../config.php';
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$userManager = new \pvv\admin\UserManager($pdo);
require_once(__DIR__ . '/../../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$uname = $attrs['uid'][0];

@ -51,11 +51,9 @@ main {
font-size: 1.5em;
}
/* #region modal */
.modal-target:hover {opacity: 0.7;}
/* The Modal (background) */
/* Modal Background */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
@ -70,14 +68,12 @@ main {
background-color: rgba(0,0,0,0.8); /* Black w/ opacity */
}
/* Modal Content (image) */
.modal-content {
margin: auto;
display: block;
object-fit: scale-down;
overflow: visible;
/* yolo, it all goes down from here */
min-width: 60vw;
max-width: 90vw !important;
min-height: 60vh;
@ -88,7 +84,6 @@ main {
opacity: 1 !important;
}
/* Caption of Modal Image */
.modal-caption {
margin: auto;
display: block;
@ -133,9 +128,7 @@ main {
text-decoration: none;
cursor: pointer;
}
/* #endregion modal */
/* #region screen-size media-rules */
@media only screen and (min-width:320px) {
.gallery-container { grid-template-columns: repeat(1, minmax(0, 1fr)) !important; }
main { padding: 1em; }
@ -150,4 +143,3 @@ main {
@media only screen and (min-width: 1281px) {
.gallery-container { grid-template-columns: repeat(4, minmax(0, 1fr)) !important; }
}
/* #endregion */

@ -1,154 +1,147 @@
header.landing {
display:block;
margin: 0;
margin-top: 3em;
padding: 3vh 15vw;
width: 70vw;
background-color: #002244;
color: #fff;
display: block;
margin: 0;
margin-top: 3em;
padding: 3vh 15vw;
width: 70vw;
background-color: #002244;
color: #fff;
}
header.landing .logo {
display: block;
width: 24vh;
height: 24vh;
margin: auto;
display: block;
width: 24vh;
height: 24vh;
margin: auto;
}
main {
margin-top: 0;
margin-top: 0;
}
.info {
text-align: center;
width: 100%;
padding: 0;
margin: 0;
text-align: center;
width: 100%;
padding: 0;
margin: 0;
overflow-y: hidden;
overflow-y: hidden;
}
.essentials {
width: 100%;
margin: 1em 0;
padding: 0;
list-style: none;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
margin: 1em 0;
padding: 0;
list-style: none;
display: flex;
align-items: center;
justify-content: center;
}
.essentials li {
display: inline-block;
display: inline-block;
}
.essentials a {
margin: 0 2em;
height: 100%;
margin: 0 2em;
height: 100%;
}
.essentials .focus {
padding: 0.8em;
font-size: 1.2em;
padding: 0.8em;
font-size: 1.2em;
}
img.float-right {
float: right;
max-width: 12em;
max-height: 12em;
vertical-align: baseline;
float: right;
max-width: 12em;
max-height: 12em;
vertical-align: baseline;
}
.calendar-events {
padding: 0;
margin-bottom: 1.5em;
list-style: none;
padding: 0;
margin-bottom: 1.5em;
list-style: none;
}
.calendar-events ul li :not(.date):not(.time) {
display: inline-block;
width: calc(100% - 7em);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
width: calc(100% - 7em);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.calendar-events ul .date {
color: rgba(0,0,0,.5);
font-size: 0.8em;
margin-top: .2em;
color: rgba(0, 0, 0, 0.5);
font-size: 0.8em;
margin-top: 0.2em;
}
.calendar-events > li > p {
border-bottom: .1em dotted rgba(0,0,0,.2);
border-bottom: 0.1em dotted rgba(0, 0, 0, 0.2);
}
.calendar-events ul {
list-style: none;
padding: 0;
list-style: none;
padding: 0;
}
.calendar-events a, #ticker a {
text-decoration: none;
font-weight: bold;
color: black;
}
.calendar-events a:hover, #ticker a:hover {
text-decoration: underline;
}
.calendar-events a,
.calendar-events ul .time {
color: rgba(0,0,0,.5);
float: right;
color: rgba(0, 0, 0, 0.5);
float: right;
}
.calendar-events ul .icon, .calendar-events ul .date {
float: right;
margin-right: .5em;
.calendar-events ul .icon,
.calendar-events ul .date {
float: right;
margin-right: 0.5em;
}
main.contentsplit {
padding-top: 1em;
padding-top: 1em;
}
.contentsplit {
display: grid;
grid-template-columns: 2.5fr 2fr;
display: grid;
grid-template-columns: 2.5fr 2fr;
}
.gridr {
border-left: 0;
border-left: 0;
}
@media screen and (max-width: 50rem) {
.essentials {
flex-direction: column;
}
.essentials {
flex-direction: column;
}
.essentials a {
margin: .2em 0;
}
.essentials a {
margin: 0.2em 0;
}
}
@media screen and (max-width: 65rem) {
.contentsplit {
display: block;
}
.contentsplit {
display: block;
}
.gridr {
height: auto;
padding: 0;
margin-left: 0;
margin-bottom: 3em;
}
.gridr {
height: auto;
padding: 0;
margin-left: 0;
margin-bottom: 3em;
}
.gridr br {
margin: 0;
height: 0px;
}
.gridr br {
margin: 0;
height: 0px;
}
.gridl {
margin-top: 0;
padding: 0;
}
.gridl {
margin-top: 0;
padding: 0;
}
}

93
www/css/services.css Normal file

@ -0,0 +1,93 @@
main {
margin: 0px !important;
padding: 0px !important;
width: 100vw;
}
.serviceWrapper {
width: 80%;
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 1em;
margin: auto auto;
margin-top: 4em;
}
.categoryContainer {
border: 4px solid #002244;
border-radius: 5px;
box-shadow : 0 0 20px #002244;
margin-bottom: 20px;
}
.categoryLabel {
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;
display: flex;
flex-direction: row;
}
.serviceContent {
flex-grow: 1;
}
.serviceTitle {
margin: 0.2em !important;
}
.serviceDescription {
margin-top: 0px !important;
}
.serviceDescription::before {
content: " - ";
font-size: 18px;
display: inline;
}
.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;
}
.serviceImage {
flex-shrink: 1;
width: 100px;
height: 100px;
margin: auto auto;
}
@media (max-width: 800px) {
.serviceWrapper {
grid-template-columns: 1fr;
}
.categoryContainer {
width: 100%;
}
}

@ -9,7 +9,7 @@ if($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_SERVER["HTTP_AUTHORIZATION"])) {
list($type, $data) = explode(" ", $_SERVER["HTTP_AUTHORIZATION"], 2);
if (strcasecmp($type, "Bearer") == 0) {
if (hash_equals($data, $doorSensorSecret)) {
if (hash_equals($data, $DOOR_SECRET)) {
handleSetState();
} else {
echo '{"status": "error", "message": "Invalid authentication key"}';
@ -74,7 +74,7 @@ function handleSetState() {
die();
}
$door->createEvent((int)($event->time), (bool)($event->isDoorOpen));
$door->createEvent((int)($event->time), $event->isDoorOpen ? 1 : 0);
echo '{"status": "OK"}';
}
@ -90,4 +90,4 @@ function getChanges($items) {
}
return $res;
}
}

@ -14,7 +14,7 @@ use \pvv\side\Agenda;
<meta name="theme-color" content="#024" />
<title>Drifterverkstedet</title>
<header>Sosial&shy;verk&shy;stedet</header>
<header>Drift&shy;verk&shy;stedet</header>
<main>

@ -1,14 +1,23 @@
#!/usr/bin/env bash
outfolder="bilder/pvv-photos"
folders=$(ssh pvv@microbel.pvv.ntnu.no -i /home/pvv/c/pvv/.ssh/photofetcher 'find /home/pvv -maxdepth 3 -name "pvv-photos" 2>/dev/null')
echo "Fetching user list"
# Note: `~pvv/.ssh/authorized_keys` forces the command as such
folders="$(ssh pvv@microbel.pvv.ntnu.no -i /home/pvv/c/pvv/.ssh/photofetcher 'cat /etc/passwd' | while read line; do test `echo "$line" | cut -d: -f 3` -gt 1000 && echo "$(echo $line | cut -d: -f 6)/pvv-photos" ; done)"
unamefile="usernames.txt"
> $unamefile # Empty the file
for imgfolder in $folders; do
user=$(echo $imgfolder | cut -d "/" -f5)
if ! test -d "$imgfolder"; then continue; fi
echo found $imgfolder
user="$(echo $imgfolder | cut -d "/" -f5)"
realname="$(getent passwd $user | cut -d ':' -f 5)"
echo "$user:$realname" >> $unamefile
echo "$user:$realname" >> "$unamefile"
destination="$outfolder/$user"
mkdir -p $destination
mkdir -p "$destination"
rsync -rtvz --delete "$imgfolder/" "$destination" # Copy and keep timestamps
done
echo
done

@ -2,59 +2,71 @@
error_reporting(0);
require_once dirname(dirname(__DIR__)) . implode(DIRECTORY_SEPARATOR, ['', 'inc', 'include.php']);
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
#$userManager = new \pvv\admin\UserManager($pdo);
#
#$as = new SimpleSAML_Auth_Simple('default-sp');
#$as->requireAuth();
#$attrs = $as->getAttributes();
#$loginname = $attrs['uid'][0];
#
#if(!$loginname) {
# header('Content-Type: text/plain', true, 403);
# echo "Du må være logget inn for å se bildegalleriet.\r\n";
# exit();
#}
#
#
$unamefile = __DIR__ . '/usernames.txt';
$relativePath = "/bilder/pvv-photos/";
$userManager = new \pvv\admin\UserManager($pdo);
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$loginname = $attrs['uid'][0];
if(!$loginname) {
header('Content-Type: text/plain', true, 403);
echo "Du må være logget inn for å se bildegalleriet.\r\n";
exit();
}
# Sourced from config.php through include.php
$galleryDir = $GALLERY_DIR;
$serverPath = $GALLERY_SERVER_PATH;
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp'];
$unamefile = $galleryDir . "/usernames.txt";
$unamepairs = file($unamefile);
$fullPath = getcwd() . $relativePath;
function getDirContents($dir, &$results = array()) {
$files = scandir($dir);
foreach ($files as $key => $value) {
$path = realpath($dir . DIRECTORY_SEPARATOR . $value);
if (!is_dir($path)) {
//Remove the full path on disk, keep username and relative path to image. ( $results[] = str_replace($GLOBALS["fullPath"], "", $path); is insecure.)
$pos = strpos($path, $GLOBALS["fullPath"]);
//Remove the full path on disk, keep username and relative path to image.
$pos = strpos($path, $GLOBALS["galleryDir"]);
if ($pos !== false) {
$cleanPath = substr_replace($path, "", $pos, strlen($GLOBALS["fullPath"]));
$cleanPath = substr_replace($path, "", $pos, strlen($GLOBALS["galleryDir"]));
}
//Check if the file is an image
$ext = pathinfo($path, PATHINFO_EXTENSION);
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if (in_array($ext, $GLOBALS["allowedExtensions"])) {
$results[] = $cleanPath;
}
} else if ($value != "." && $value != "..") {
} else if ($value != "." && $value != ".." && $value != ".thumbnails") {
//recursively scan directories
getDirContents($path, $results);
}
}
return $results;
}
$images = getDirContents($fullPath);
$images = getDirContents($galleryDir);
function cmpModifyTime($a, $b) {
global $galleryDir;
$mtime_a = filemtime($galleryDir . $a);
$mtime_b = filemtime($galleryDir . $b);
return ($mtime_a > $mtime_b) ? -1 : 1;
}
usort($images, "cmpModifyTime");
$imageTemplate = '
<div class="card">
<div class="card-image-div">
<img src="%path" alt="%name" class="card-image modal-target">
<img src="%thumbnail" data-fullsrc="%path" alt="%name" class="card-image modal-target">
</div>
<div class="card-body">
<p class="card-title">%realname</p>
@ -78,7 +90,6 @@ $imageTemplate = '
<link rel="stylesheet" href="../css/normalize.css">
<link rel="stylesheet" href="../css/style.css">
<link rel="stylesheet" href="../css/gallery.css">
<link rel="stylesheet" href="../css/bulma.min.css">
<meta name="theme-color" content="#024" />
<title>Fotoverkstedet</title>
</head>
@ -90,9 +101,9 @@ $imageTemplate = '
<main class="gallery-container">
<?php
foreach ($images as $key => $value) {
$modTime = date("d.m.Y H:i", filemtime($fullPath . $value));
$imguser = explode("/", $value)[0];
$displaypath = substr($value, strpos($value, "/")+1);
$modTime = date("d.m.Y H:i", filemtime($galleryDir . $value));
$imguser = explode("/", $value)[1];
$displaypath = implode("/", array_slice(explode("/", $value), 2));
$realname = "Ukjent";
foreach ($unamepairs as $unamepair) {
$unamepair = explode(":", $unamepair);
@ -105,13 +116,18 @@ $imageTemplate = '
$vars = [
"%user" => htmlspecialchars($imguser),
"%time" => $modTime,
"%timestamp" => filemtime($fullPath . $value),
"%name" => htmlspecialchars($displaypath),
"%path" => "/galleri/" . $relativePath .$value,
"%path" => $serverPath . $value,
"%thumbnail" => $serverPath . "/.thumbnails" . $value . ".png",
"%realname" => htmlspecialchars($realname)
];
echo strtr($imageTemplate, $vars);
}
if (count($images) == 0) {
echo "<h3 class='no-images'>Det er dessverre ingen bilder å vise.</h3>";
}
?>
</main>
<div id="modal" class="modal">

@ -1,30 +1,34 @@
<?php
//Short path to search folder, full to display in <img>
$relativePath = "/bilder/slideshow/";
$absolutePath = "/galleri" . $relativePath;
//Path to first image in slideshow and fallback image if no others are present
<?php
require __DIR__ . '/../../config.php';
$searchDir = $SLIDESHOW_DIR;
$serverPath = $SLIDESHOW_SERVER_PATH;
$splashImg = "/PNG/PVV-logo-big-bluebg.png";
$filenames = sCaNdIr(__DIR__ . $relativePath);
// Note: This does not support nested directories
$filenames = sCaNdIr($searchDir);
//Remove the expected non-images
foreach($filenames as $k => $value) {
if(in_array($value, [".gitkeep", ".", ".."])) {
if(in_array($value, [".", ".."])) {
unset($filenames[$k]);
}
}
function getFullPath($fname) { return ($GLOBALS["absolutePath"] . $fname ); }
function getFullPath($fname) { return ($GLOBALS["serverPath"] . "/" . $fname ); }
//Sort filenames alphabetically and prepend the path prefix to each item.
// Sort filenames alphabetically and prepend the path prefix to each item.
asort($filenames);
$slideshowimagefilenames = aRrAy_MaP("getFullPath", $filenames);
//Prepend the cover photo
// Prepend the cover photo
ArRaY_uNsHiFt($slideshowimagefilenames, $splashImg);
eChO('<img class="slideshowimg slideshowactive" id="slideshowImage1" src="' . $slideshowimagefilenames[0] . '">');
ecHo('<img class="slideshowimg" id="slideshowImage2" src="' . $slideshowimagefilenames[1] . '">');
//Store list of file names in a globel JS variable
EchO("<script> const slideshowFnames =" . jSoN_eNcOdE($slideshowimagefilenames) . "; </script>");
?>
// Store list of file names in a globel JS variable
EchO("<script> const slideshowFnames =" . jSoN_eNcOdE($slideshowimagefilenames) . "; </script>");
?>

@ -2,7 +2,7 @@
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'];
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$motdfetcher = new \pvv\side\MOTD($pdo);
@ -40,11 +40,10 @@ $doorTime = date("H:i", $doorEntry->time);
</nav>
<header class="landing">
<div id="imageSlideshow">
<div id="imageSlideshow">
<?php include("galleri/slideshow.php"); ?>
<script src="js/slideshow.js"></script>
</div>
<div class="info">
<h2>Velkommen til Programvare&shy;verkstedet</h2>
<p>Programvareverkstedet (PVV) er en studentorganisasjon ved NTNU som vil skape et miljø for datainteresserte personer tilknyttet universitetet.</p>

@ -1,22 +1,17 @@
// #region Modal
var modal = document.getElementById('modal');
const modal = document.getElementById('modal');
const modalImg = document.getElementById("modal-content");
const captionText = document.getElementById("modal-caption");
// global handler
document.addEventListener('click', function (e) {
if (e.target.className.indexOf('modal-target') !== -1) {
var img = e.target;
var modalImg = document.getElementById("modal-content");
var captionText = document.getElementById("modal-caption");
// Open modal
const img = e.target;
modal.style.display = "block";
modalImg.src = img.src;
modalImg.src = img.dataset.fullsrc;
captionText.innerHTML = img.alt;
} else if (modal.style.display != "none") {
// Close modal
modal.style.display = "none";
modalImg.src = "";
}
});
// #endregion
// #region sorting
// #endregion

@ -21,7 +21,7 @@ function stepSlideshow(imgs) {
//Change source to next picture after it is faded out
slideshowIndex = (slideshowIndex + 1) % imgs.length;
ssi2.src = slideshowFnames[slideshowIndex];
}, 800);
}, 1000);
}
//Initialize slideshow, start interval
@ -29,4 +29,4 @@ if (slideshowFnames.length > 1) {
slideshowInterval = setInterval(()=>{
stepSlideshow(slideshowFnames);
}, SLIDESHOWDELAYMS);
}
}

@ -9,7 +9,7 @@ require_once dirname(dirname(__DIR__)) . implode(DIRECTORY_SEPARATOR, ['', 'inc'
<link rel="stylesheet" href="../css/normalize.css">
<link rel="stylesheet" href="../css/style.css">
<link rel="stylesheet" href="../css/events.css">
<meta name="theme-color" content="#024" />
<meta name="theme-color" content="#024" >
<title>Kommunikasjonsverkstedet</title>
<header>Kommunikasjons&shy;verk&shy;stedet</header>
@ -22,22 +22,32 @@ require_once dirname(dirname(__DIR__)) . implode(DIRECTORY_SEPARATOR, ['', 'inc'
</nav>
<main>
<h2>Kommunikasjon<br><img style="width: 8em" src="kontakt.jpg"></img></h2>
<h2>
Kontaktinformasjon
<a href="https://en.wikipedia.org/wiki/IP_over_Avian_Carriers">
<img style="width: 8em" src="kontakt.jpg">
</a>
</h2>
<p> Det er flere ulike måter å kommunisere med PVV og deres medlemmer på.
<p>
Vi kan kontaktes på følgende e-postadresser:
<ul>
<li><a href="mailto:pvv@pvv.ntnu.no">pvv@pvv.ntnu.no</a> for hendvendelser til styret</li>
<li><a href="mailto:drift@pvv.ntnu.no">drift@pvv.ntnu.no</a> for hendvendelser anngående våre datasystemer</li>
</ul>
</p>
<p> Du kan registrere deg på våre <a href="http://list.pvv.org/mailman/listinfo/aktive">Aktive epostlister</a> for å få informasjon om de kommende aktivitetene våre.
<p>Det er også mulig å ta kontakt med oss ved å møte opp <a href="https://link.mazemap.com/JqgWGSnh">på våre lokaler i oppredning/gruvedrift</a>.</p>
<p> Kontaktinformasjonen til Styret og Drift funner du på vår <a href="../pvv/Kontaktinformasjon">Wiki</a>
<h2>Kommunikasjonskanaler</h2>
<p> Vi har en IRC-kanal på <a href="http://webchat.ircnet.net/">IRCnet</a> kalt #pvv.
<p>Vi har en <a href="http://list.pvv.org/mailman/listinfo/aktive">e-postliste for aktive medlemmer</a>. All offisiell informasjon blir sendt på denne listen, og alle arrengementer blir også annonsert her.</p>
<p> Vi har også en Discord-kanal. Denne kanalen er satt opp slik at man i Discord-kanalen ser hva som skrives i IRC-kanalen, og vice versa. For å bli med i Discord-kanalen, <a href="https://discord.gg/8VTBr6Q">klikk her</a>.
<!--
<i>(Denne broen for øyeblikket ute av drift)</i>
-->
<p>Vi har en <a target="_blank" href="https://matrix.to/#/#pvv:pvv.ntnu.no">Matrix-server</a> for chat, memes, og all annen kommunikasjon. Den er bridget med IRC-kanalen og Discord-guilden vår. Hvis du er medlem kan du bruke vår <a href="https://chat.pvv.ntnu.no">self-hosted web client</a>.</p>
<p>Vi har en IRC-kanal på <a href="http://webchat.ircnet.net/">IRCnet</a> kalt #pvv.</p>
<p> Det er også mulig å ta i bruk analog kontakt ved å møte opp <a href="https://bit.ly/31P6yr5">på våre lokaler i oppredning/gruvedrift</a>.
<p>Vi har en <a target="_blank" href="https://discord.gg/8VTBr6Q">Discord-guild</a> for de som foretrekker Discord over Matrix. </p>
</main>
</body>

@ -1,35 +0,0 @@
<?php
require_once dirname(dirname(__DIR__)) . implode(DIRECTORY_SEPARATOR, ['', 'inc', 'include.php']);
?>
<!DOCTYPE html>
<html lang="no">
<style>
p {hyphens: auto;}
</style>
<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/mail.css">
<meta name="theme-color" content="#024" />
<title>Mailverkstedet</title>
<header>Mail&shy;verk&shy;stedet</header>
<main>
<ul id="webmail">
<li id="afterlogic"><div><a href="https://webmail.pvv.ntnu.no/afterlogic_lite/"><span class="mailname" style="user-select: none">&nbsp;</span></a>
<li id="rainloop"><div><a href="https://webmail.pvv.ntnu.no/rainloop/"><span class="mailname">RainLoop</span></a>
<li id="squirrelmail"><div><a href="https://webmail.pvv.ntnu.no/squirrelmail/"><span class="mailname">SquirrelMail</span></a>
<li id="roundcube"><div><a href="https://webmail.pvv.ntnu.no/roundcube/"><span class="mailname">Roundcube</span></a>
</ul>
</main>
<nav>
<?= navbar(1, "mail"); ?>
<?= loginbar($sp, $pdo); ?>
</nav>

@ -47,7 +47,7 @@ p {hyphens: auto;}
}
</style>
<div class="iframe-container" style="max-width: 100em;">
<iframe class="responsive-iframe" src="https://www.youtube.com/embed/l-iEkaQNQdk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen ></iframe>
<iframe class="responsive-iframe" src="https://www.youtube.com/embed/Uy6HAvMdeuA" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen ></iframe>
</div>
</article>
@ -66,7 +66,7 @@ p {hyphens: auto;}
<p>PVV har også en del brettspill du kan prøve.
<p><a href="../pvv/Dokumentasjon">Her</a> er en oversikt over hva du kan gjøre når du har fått PVV bruker.
<p><a href="https://wiki.pvv.ntnu.no/wiki/Dokumentasjon">Her</a> er en oversikt over hva du kan gjøre når du har fått PVV bruker.
</article>

@ -5,25 +5,6 @@ session_start();
$attrs = $as->getAttributes();
$oauth2 = new Kasperrt\Oauth2($dataportenConfig);
if (isset($_GET['logout'])) {
session_destroy();
header('Location: ' . $dataportenConfig["redirect_uri"]);
die();
}
if (isset($_GET['login'])) {
$oauth2 -> redirect();
die();
}
if (isset($_GET['code'])) {
$token = $oauth2 -> get_access_token(htmlspecialchars($_GET['state']), htmlspecialchars($_GET['code']));
$_SESSION['userdata'] = $oauth2 -> get_identity($token, 'https://auth.dataporten.no/userinfo');
header('Location: ' . $dataportenConfig["redirect_uri"]);
die();
}
?>
<!DOCTYPE html>
<html lang="no">
@ -50,7 +31,7 @@ if (isset($_GET['code'])) {
<p>
Første steg for å bli medlem i PVV er å betale
<a href="../pvv/Medlemskontingent">medlemskontingent</a> på 50kr per år.
<a href="https://wiki.pvv.ntnu.no/wiki/Medlemskontingent">medlemskontingent</a> på 50kr per år.
Disse pengene brukes for å drifte PVV. Se lenken for kontonummeret.
</p>
<p>
@ -61,7 +42,7 @@ if (isset($_GET['code'])) {
å man møte opp på lokalene slik at man får satt passord.
</p>
<p>
Mer informasjon om medlemskap finner du <a href="../pvv/Medlem">her</a>.
Mer informasjon om medlemskap finner du <a href="https://wiki.pvv.ntnu.no/wiki/Medlem">her</a>.
</p>
<h2>Registrer deg som bruker</h2>
@ -70,7 +51,7 @@ if (isset($_GET['code'])) {
PVV har for øyeblikket et manuelt system for å legge til nye brukere.
Se lenkene over for mer informasjon.
Vi foretrekker at du kommer inn på besøk på <a href="https://link.mazemap.com/aKDz8eu8">våre lokaler i Oppredning/Gruvedrift, rom 247</a>
for å sette sette opp din PVV bruker. Hvis du ikke har mulighet til det, kan du <a href="../pvv/Kontaktinformasjon">finne oss her</a> og sende en epost.
for å sette sette opp din PVV bruker. Hvis du ikke har mulighet til det, kan du <a href="https://wiki.pvv.ntnu.no/wiki/Kontaktinformasjon">finne oss her</a> og sende en epost.
For å aktivere din brukerkonto på PVV, må du alikevell møte opp på
lokalene våre slik at vi kan få satt ditt passord.
</p>

@ -3,14 +3,14 @@ date_default_timezone_set('Europe/Oslo');
setlocale(LC_ALL, 'nb_NO');
require __DIR__ . '/../../inc/navbar.php';
require __DIR__ . '/../../src/_autoload.php';
require __DIR__ . '/../../sql_config.php';
require __DIR__ . '/../../config.php';
require_once __DIR__ . '/../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php';
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
$pdo = new \PDO($DB_DSN, $DB_USER, $DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$projectManager = new \pvv\side\ProjectManager($pdo);

@ -10,7 +10,7 @@ if(isset($_GET['id'])){
}
require_once(__DIR__ . '/../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$attrs = $as->getAttributes();
$projectManager = new \pvv\side\ProjectManager($pdo);

@ -3,14 +3,14 @@ date_default_timezone_set('Europe/Oslo');
setlocale(LC_ALL, 'nb_NO');
require __DIR__ . '/../../inc/navbar.php';
require __DIR__ . '/../../src/_autoload.php';
require __DIR__ . '/../../sql_config.php';
require __DIR__ . '/../../config.php';
require_once(__DIR__ . '/../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
$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]);

@ -2,8 +2,8 @@
date_default_timezone_set('Europe/Oslo');
setlocale(LC_ALL, 'nb_NO');
require __DIR__ . '/../../src/_autoload.php';
require __DIR__ . '/../../sql_config.php';
$pdo = new \PDO($dbDsn, $dbUser, $dbPass);
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']) or !isset($_POST['desc']) or !isset($_POST['active'])){
@ -12,7 +12,7 @@ if(!isset($_POST['title']) or !isset($_POST['desc']) or !isset($_POST['active'])
}
require_once(__DIR__ . '/../../vendor/simplesamlphp/simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('default-sp');
$as = new \SimpleSAML\Auth\Simple('default-sp');
$as->requireAuth();
$attrs = $as->getAttributes();

18
www/robots.txt Normal file

@ -0,0 +1,18 @@
# This file set up by fredj according to proposed Standard of Robot
# Exclusion at http://web.nexor.co.uk/mak/doc/robots/norobots.html
#
# Currently: allow all robots.
# (An empty 'Disallow' line, looking like this:)
# User-agent: * # Means: All robots.
# Disallow: # Means: Disallow nothing.
User-agent: *
Disallow: /usage
Disallow: /news_archive
Disallow: /cgi
Disallow: /finger
Disallow: /cgi-bin
Disallow: /~pallo/usage/
Disallow: /~pallo/historic/
Disallow: /~meldahl/
Disallow: /w/index.php?

Binary file not shown.

After

(image error) Size: 52 KiB

Binary file not shown.

After

(image error) Size: 4.6 KiB

BIN
www/tjenester/img/email.png Normal file

Binary file not shown.

After

(image error) Size: 42 KiB

BIN
www/tjenester/img/esxi.png Normal file

Binary file not shown.

After

(image error) Size: 2.3 KiB

Binary file not shown.

After

(image error) Size: 22 KiB

BIN
www/tjenester/img/gitea.png Normal file

Binary file not shown.

After

(image error) Size: 26 KiB

Binary file not shown.

After

(image error) Size: 6.9 KiB

BIN
www/tjenester/img/irc.png Normal file

Binary file not shown.

After

(image error) Size: 14 KiB

Binary file not shown.

After

(image error) Size: 58 KiB

BIN
www/tjenester/img/php.png Normal file

Binary file not shown.

After

(image error) Size: 31 KiB

Binary file not shown.

After

(image error) Size: 47 KiB

BIN
www/tjenester/img/rss.png Normal file

Binary file not shown.

After

(image error) Size: 117 KiB

BIN
www/tjenester/img/trac.png Normal file

Binary file not shown.

After

(image error) Size: 11 KiB

200
www/tjenester/index.php Normal file

@ -0,0 +1,200 @@
<?php namespace pvv\side;
require_once dirname(dirname(__DIR__)) . implode(DIRECTORY_SEPARATOR, ['', 'inc', 'include.php']);
?>
<!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/services.css">
<meta name="theme-color" content="#024" />
<title>Tjenesteverkstedet</title>
<header>Tjenesteverkstedet</header>
<body>
<nav>
<?= navbar(1, 'tjenester'); ?>
<?= loginbar($sp, $pdo); ?>
</nav>
<main>
<div class="serviceWrapper">
<div class="categoryContainer">
<div class="categoryLabel">Versjonskontroll og utvikling</div>
<div class="categoryContent">
<div class="service">
<div class="serviceContent">
<h2 class="serviceTitle">Gitea</h2>
<p class="serviceDescription">Vår interne git-tjener, åpen for alle medlemmer</p>
<div class="serviceLink"><a href="https://git.pvv.ntnu.no" target="_blank">Gå til git.pvv.ntnu.no</a></div>
</div>
<img class="serviceImage" src="img/gitea.png" alt="Gitea-logo">
</div>
<div class="service">
<div class="serviceContent">
<h2 class="serviceTitle">GitHub</h2>
<p class="serviceDescription">Våre offentlige kodebrønner, åpent for verden!</p>
<div class="serviceLink"><a href="https://github.com/Programvareverkstedet/" target="_blank">Gå til GitHub</a></div>
</div>
<img class="serviceImage" src="img/github.png" alt="GitHub-logo">
</div>
</div>
</div>
<div class="categoryContainer">
<div class="categoryLabel">Kommunikasjon</div>
<div class="categoryContent">
<div class="service">
<div class="serviceContent">
<h2 class="serviceTitle">Matrix via Element</h2>
<p class="serviceDescription">Åpen kommunikasjonsprotokoll som støtter ende-til-ende-kryptering og utallige kule funksjoner. Vårt space er bridget sammen med Discord, så du får alle de samme meldingene. <b>#pvv:pvv.ntnu.no</b></p>
<div class="serviceLink">
<a href="https://chat.pvv.ntnu.no" target="_blank">Gå til chat.pvv.ntnu.no(medlem)</a>
</div>
<div class="serviceLink">
<a href="https://matrix.to/#/#pvv:pvv.ntnu.no" target="_blank">Gå til #pvv:pvv.ntnu.no(offentlig)</a>
</div>
</div>
<img class="serviceImage" src="img/element.png" alt="Element-logo">
</div>
<div class="service">
<div class="serviceContent">
<h2 class="serviceTitle">Discord</h2>
<p class="serviceDescription">Vår hovedkanal, her finner du alt fra ofisielle announcements til memes og driftsdiskusjoner.</p>
<div class="serviceLink"><a href="https://discord.gg/WpaHGV8K" target="_blank">Gå til Discord</a></div>
</div>
<img class="serviceImage" src="img/discord.png" alt="Discord-logo">
</div>
<div class="service">
<div class="serviceContent">
<h2 class="serviceTitle">Epost</h2>
<p class="serviceDescription">Som PVV-medlem får du din egen @pvv.ntnu.no-adresse, som kan brukes med alle vanlige epostprotokoller.</p>
<div class="serviceLink">
<a href="https://www.pvv.ntnu.no/mail/" target="_blank">Gå til Webmail</a>
</div>
<div class="serviceLink">
<a href="https://wiki.pvv.ntnu.no/wiki/Drift/Mail/IMAP_POP3" target="_blank">IMAP/POP/SMTP-innstillinger</a>
</div>
</div>
<img class="serviceImage" src="img/email.png" alt="Epost-ikon">
</div>
<div class="service">
<div class="serviceContent">
<h2 class="serviceTitle">IRC</h2>
<p class="serviceDescription">Hvis Discord er for proprietært og Matrix er for hypermoderne er kanskje IRC for deg. Vi har en kanal på IRCNet, <b>#pvv</b>.</p>
<div class="serviceLink"><a href="irc://irc.pvv.ntnu.no/pvv" target="_blank">Koble til med IRC</a></div>
</div>
<img class="serviceImage" src="img/irc.png" alt="IRC-ikon">
</div>
</div>
</div>
<div class="categoryContainer">
<div class="categoryLabel">Hosting</div>
<div class="categoryContent">
<div class="service">
<div class="serviceContent">
<h2 class="serviceTitle">Brukernettsider</h2>
<p class="serviceDescription">Alle brukere får automatisk en egen side for html og php. Denne er offentlig på pvv.ntnu.no/~brukernavn.</p>
<div class="serviceLink"><a href="https://wiki.pvv.ntnu.no/wiki/Hjemmesider" target="_blank">Gå til dokumentasjon på wiki</a></div>
</div>
<img class="serviceImage" src="img/php.png" alt="En elephpant">
</div>
<div class="service">
<div class="serviceContent">
<h2 class="serviceTitle">PVV-siden</h2>
<p class="serviceDescription">Du befinner deg nå på PVV sin offisielle hjemmeside. Den er skrevet i PHP og kjører på en egen server.</p>
<div class="serviceLink"><a href="https://github.com/Programvareverkstedet/nettsiden" target="_blank">Se koden på github</a></div>
</div>
<img class="serviceImage" src="../pvv-logo.png" alt="PVV-logo">
</div>
<div class="service">
<div class="serviceContent">
<h2 class="serviceTitle">Proxmox @joshua</h2>
<p class="serviceDescription">Joshua er en av våre VM-tjenere, her kan du kjøre enten fulle VM-er eller konteinere. Bare Drift har tilgang på disse tjenerne.</p>
<div class="serviceLink"><a href="https://joshua.pvv.ntnu.no:8006" target="_blank">Gå til joshua.pvv.ntnu.no</a></div>
</div>
<img class="serviceImage" src="img/proxmox.png" alt="Proxmox-logo">
</div>
<div class="service">
<div class="serviceContent">
<h2 class="serviceTitle">Proxmox @andresbu</h2>
<p class="serviceDescription">Andresbu er en kraftigere VM-tjener, men har fortsatt en del rusk i maskineriet.</p>
<div class="serviceLink"><a href="https://andresbu.pvv.ntnu.no:8006" target="_blank">Gå til andresbu.pvv.ntnu.no</a></div>
</div>
<img class="serviceImage" src="img/proxmox.png" alt="Proxmox-logo">
</div>
<div class="service">
<div class="serviceContent">
<h2 class="serviceTitle">ESXI @asgore</h2>
<p class="serviceDescription">Asgore er vår eldste og største VM-tjener, og kjører ESXI.</p>
<div class="serviceLink"><a href="https://asgore.pvv.ntnu.no" target="_blank">Gå til asgore.pvv.ntnu.no</a></div>
</div>
<img class="serviceImage" src="img/esxi.png" alt="ESXI-logo">
</div>
</div>
</div>
<div class="categoryContainer">
<div class="categoryLabel">Underholdning</div>
<div class="categoryContent">
<div class="service">
<div class="serviceContent">
<h2 class="serviceTitle">Minecraft</h2>
<p class="serviceDescription">Vi har en egen Minecraft-server <b>for medlemmer</b>, som du kan koble til med IP-adressen <b>minecraft.pvv.ntnu.no</b>. Spør om whitelist på matrix/discord.</p>
<div class="serviceLink"><a href="https://isvegg.pvv.ntnu.no/kart/" target="_blank">Gå til verdenskartet vårt</a></div>
</div>
<img class="serviceImage" src="img/minecraft.png" alt="Minecraft-logo">
</div>
<div class="service">
<div class="serviceContent">
<h2 class="serviceTitle">MiniFlux RSS reader</h2>
<p class="serviceDescription">Trenger du en cross-platform RSS/Atom-leser for å følge med på omverdenen som det er 1990? </p>
<div class="serviceLink"><a href="https://feeds.pvv.ntnu.no" target="_blank">Gå til MiniFlux</a></div>
</div>
<img class="serviceImage" src="img/rss.png" alt="RSS-Ikon">
</div>
<div class="service">
<div class="serviceContent">
<h2 class="serviceTitle">Bildegalleri</h2>
<p class="serviceDescription">PVV har et felles bildegalleri, der alle kan legge relevante bilder, som automatisk blir inkludert på nettsiden.</p>
<div class="serviceLink">
<a href="https://www.pvv.ntnu.no/galleri/" target="_blank">Se galleriet</a>
</div>
<div class="serviceLink">
<a href="https://wiki.pvv.ntnu.no/wiki/Bildedeling" target="_blank">Opplasting</a>
</div>
</div>
<img class="serviceImage" src="img/gallery.png" alt="RSS-Ikon">
</div>
<!-- Bokhylle /brzeczyszczykiewicz ? -->
</div>
</div>
</div>
</main>
</body>
</html>