Moving pages into sub-folders.

This commit is contained in:
2025-07-01 20:31:05 +10:00
parent 89a26f5362
commit 7607d807fe
17 changed files with 14 additions and 2 deletions

6
web/agenda/20250708.md Normal file
View File

@@ -0,0 +1,6 @@
- 7.30 pm - Welcome
- 7.35 pm - Agreement on Agenda Items
- 7.40 pm - Administrivia (if any)
- 7.45 pm - Presentation 1 - James: Git
- 8.15 pm - Presentation 2 - Terry?: Fossil?
- 8.45 pm - Loose (Discourse) Threads

6
web/agenda/20250724.md Normal file
View File

@@ -0,0 +1,6 @@
- 6.30 pm - Welcome
- 6.35 pm - Agreement on Agenda Items
- 6.40 pm - Administrivia (if any)
- 6.45 pm - Presentation 1: James: Git
- 7.15 pm - Presentation 2: Terry?: Fossil?
- 7.45 pm - Loose (Discourse) Threads

59
web/cal.php Executable file
View File

@@ -0,0 +1,59 @@
<?php
header('Access-Control-Allow-Origin: *'); // Allows all origins
header('Access-Control-Allow-Methods: GET'); // Allow GET requests
//header('Content-type: application/calendar'); // Specify content type
header('Content-Type: text/calendar; charset=utf-8');
header('Content-Disposition: inline; filename=calendar.ics');
// Use Brisbane timezone (no DST)
date_default_timezone_set('Australia/Brisbane');
// Read and parse the JSON file
$json = file_get_contents('events.json');
$events = json_decode($json, true);
// Start of the iCalendar content
echo "BEGIN:VCALENDAR\r\n";
echo "VERSION:2.0\r\n";
echo "PRODID:-//Homelab Brisbane//Event Calendar//EN\r\n";
echo "CALSCALE:GREGORIAN\r\n";
echo "METHOD:PUBLISH\r\n";
echo "X-WR-TIMEZONE:Australia/Brisbane\r\n";
// Add VTIMEZONE block for Australia/Brisbane
echo "BEGIN:VTIMEZONE\r\n";
echo "TZID:Australia/Brisbane\r\n";
echo "BEGIN:STANDARD\r\n";
echo "DTSTART:19700101T000000\r\n";
echo "TZOFFSETFROM:+1000\r\n";
echo "TZOFFSETTO:+1000\r\n";
echo "TZNAME:AEST\r\n";
echo "END:STANDARD\r\n";
echo "END:VTIMEZONE\r\n";
// Generate VEVENTS
foreach ($events as $event) {
$uid = uniqid() . "@homelabbrisbane.com.au";
$dtstart = date("Ymd\THis", strtotime($event["start"]));
$dtend = date("Ymd\THis", strtotime($event["end"]));
$summary = htmlspecialchars($event["title"]);
$location = htmlspecialchars($event["location"]);
$description = isset($event["description"]) ? htmlspecialchars($event["description"]) : '';
echo "BEGIN:VEVENT\r\n";
echo "UID:$uid\r\n";
echo "DTSTAMP:" . gmdate("Ymd\THis\Z") . "\r\n";
echo "DTSTART;TZID=Australia/Brisbane:$dtstart\r\n";
echo "DTEND;TZID=Australia/Brisbane:$dtend\r\n";
echo "SUMMARY:$summary\r\n";
echo "LOCATION:$location\r\n";
echo "DESCRIPTION:$description\r\n";
echo "END:VEVENT\r\n";
}
// End of calendar
echo "END:VCALENDAR\r\n";
?>

56
web/cal_testing.php Executable file
View File

@@ -0,0 +1,56 @@
<?php
// Set header to tell clients it's a calendar feed
header('Content-Type: text/calendar; charset=utf-8');
header('Content-Disposition: inline; filename=calendar.ics');
// Use Brisbane timezone (no DST)
date_default_timezone_set('Australia/Brisbane');
// Read and parse the JSON file
$json = file_get_contents('events_testing.json');
$events = json_decode($json, true);
// Start of the iCalendar content
echo "BEGIN:VCALENDAR\r\n";
echo "VERSION:2.0\r\n";
echo "PRODID:-//Homelab Brisbane//Event Calendar//EN\r\n";
echo "CALSCALE:GREGORIAN\r\n";
echo "METHOD:PUBLISH\r\n";
echo "X-WR-TIMEZONE:Australia/Brisbane\r\n";
// Add VTIMEZONE block for Australia/Brisbane
echo "BEGIN:VTIMEZONE\r\n";
echo "TZID:Australia/Brisbane\r\n";
echo "BEGIN:STANDARD\r\n";
echo "DTSTART:19700101T000000\r\n";
echo "TZOFFSETFROM:+1000\r\n";
echo "TZOFFSETTO:+1000\r\n";
echo "TZNAME:AEST\r\n";
echo "END:STANDARD\r\n";
echo "END:VTIMEZONE\r\n";
// Generate VEVENTS
foreach ($events as $event) {
$uid = uniqid() . "@homelabbrisbane.com.au";
$dtstart = date("Ymd\THis", strtotime($event["start"]));
$dtend = date("Ymd\THis", strtotime($event["end"]));
$summary = htmlspecialchars($event["title"]);
$location = htmlspecialchars($event["location"]);
$description = isset($event["description"]) ? htmlspecialchars($event["description"]) : '';
echo "BEGIN:VEVENT\r\n";
echo "UID:$uid\r\n";
echo "DTSTAMP:" . gmdate("Ymd\THis\Z") . "\r\n";
echo "DTSTART;TZID=Australia/Brisbane:$dtstart\r\n";
echo "DTEND;TZID=Australia/Brisbane:$dtend\r\n";
echo "SUMMARY:$summary\r\n";
echo "LOCATION:$location\r\n";
echo "DESCRIPTION:$description\r\n";
echo "END:VEVENT\r\n";
}
// End of calendar
echo "END:VCALENDAR\r\n";
?>

33
web/events.json Executable file
View File

@@ -0,0 +1,33 @@
[
{
"start": "2025-06-10T19:00:00",
"end": "2025-06-10T21:00:00",
"title": "June 2025 Online Catch Up",
"description": "Getting started with meshtastic",
"location": "Jitsi"
},
{
"start": "2025-06-19T18:30:00",
"end": "2025-06-19T21:00:00",
"title": "June 2025 In Person Catch Up",
"description": "Social get-together. Discussion about large language models and meshtastic experiments. Clumsy attempt at adhering to an agenda and taking minutes.",
"location": "Chermside Library",
"coordinates": "-27.38621539644283,153.0351689206467"
},
{
"start": "2025-07-08T19:00:00",
"end": "2025-07-08T21:00:00",
"title": "July 2025 Online Catch Up",
"description": "Monthly Online Jitsi Get Together",
"location": "Jitsi"
},
{
"start": "2025-07-24T18:30:00",
"end": "2025-07-24T21:00:00",
"title": "July 2025 In Person Catch Up",
"description": "Monthly In Person Get Together",
"location": "Chermside Library",
"coordinates": "-27.38621539644283,153.0351689206467"
}
]

16
web/events_testing.json Executable file
View File

@@ -0,0 +1,16 @@
[
{
"start": "2025-06-10T19:00:00",
"end": "2025-06-10T21:00:00",
"title": "June 2025 Online Catch Up",
"description": "Getting started with meshtastic",
"location": "Jitsi"
},
{
"start": "2025-06-19T18:30:00",
"end": "2025-06-19T21:00:00",
"title": "June 2025 In Person Catch Up",
"location": "Chermside Library"
}
]

BIN
web/image/icon/calendar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
web/image/icon/chat.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
web/image/icon/email.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
web/image/icon/handshake.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

2
web/image/icon/jitsi.svg Normal file
View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 192 192" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"><path d="M1815.77 349.658c-2.19 3.709-4.13 6.14-4.13 6.14 0 .012-.01.023-.02.035 0 0-11.92 14.167-28.62 14.167-11.66 0-18.42-3.309-22.16-6.338-1.94 1.716-3.48 4.067-4.8 6.407-3.6 6.393-5.19 13.281-5.19 13.281a6.005 6.005 0 0 1-11.51.648c-4.03-11.423-5.61-22.206-4.29-32.745 1.04-8.298 5.01-15.735 8.87-19.492 2.73-2.652 5.68-3.761 8.08-3.761 1.62 0 5.04.957 8.92 2.671 2.51 1.105 5.31 2.479 7.86 3.779-.39-.389-.78-.812-1.17-1.271-3.28-3.934-6.44-11.138-8.46-19.826-3.35-14.477 8.84-24.518 14.54-26.892 2.61-1.083 8.91-2.696 13.75-3.862-.25-1.422-.35-2.805-.43-4.171 0-.077 0-.154-.01-.231-.3-6.022-.32-13.626 4.16-19.743 2.07-2.82 4.39-4.542 7.35-6.007 3.06-1.516 7.04-2.694 12.63-5.722 2.94-1.593 4.03-4.493 4.54-6.891.59-2.813.36-5.157.36-5.157a5.998 5.998 0 0 1 11.38-3.249S1832 241.546 1832 247c0 2.484-.41 7.43-4.59 13.572 1.95 4.478 2.23 8.816 1.83 12.466.25-.026.51-.038.76-.038 4.23 0 15.94 3.037 18.88 17.83 2.26 11.321-1.97 29.169-3.02 32.455-3.48 10.873-9.35 19.984-19.3 28.547-1.84 1.578-4.57 1.558-6.65.313l-4.14-2.487Zm-49.54-3.025c-2.98-1.563-6.84-3.524-10.15-4.984-1.16-.511-2.66-1.056-3.58-1.379-.54.543-1.44 1.495-1.97 2.338-1.69 2.644-3.09 6.262-3.58 10.139a48.771 48.771 0 0 0-.23 9.521c3.3-5.232 7.73-9.821 13.15-11.876v-.002c.08-.029.16-.057.24-.083 2.6-.868 4.59-2.194 6.12-3.674Zm7 9.859c2.42.843 5.64 1.508 9.77 1.508 11.34 0 19.34-9.778 19.36-9.798.02-.024 6-7.06 7.37-14.382a23.581 23.581 0 0 1-4.1-2.817c-4.32 2.061-10.47 4.297-16.3 5.706a57.9 57.9 0 0 1-5.93 1.126c-.77 5.305-3.97 13.218-10.17 18.657Zm48.31-20.996c-.2 1.065-.47 2.127-.79 3.173.12.058.23.12.34.186l1.35.814c1.95-1.819 3.64-3.807 5.08-5.795-2.07.635-4.17 1.225-5.98 1.622Zm-28.51-41.91c-4.38.995-11.93 2.792-14.72 3.953-2.82 1.172-9.12 5.966-7.46 13.108 1.11 4.813 2.58 9.079 4.27 12.229.75 1.384 2.12 2.961 2.42 3.308 2.2.336 5.49-.296 8.97-1.139a81.934 81.934 0 0 0 8.18-2.455c-2.06-.943-4.33-1.59-6.69-1.59-2.81 0-5.23.732-5.23.732a5.995 5.995 0 0 1-6.87-8.89c2.09-3.382 5.81-6.237 9.75-8.118 3.84-1.833 7.83-2.724 10.35-2.724 4.24 0 12.4.824 21.27 4.446 3.28 1.339 7.22 3.71 10.84 6.118-1.98-7.087-4.17-13.999-5.57-17.081a20.32 20.32 0 0 1-1.49-4.895 37.522 37.522 0 0 1-3.69 3.311c-5.31 4.174-10.98 6.61-10.98 6.61-1.88.81-4.04.608-5.74-.536-3.32-2.245-5.78-4.376-7.61-6.387Zm6.43-25.343c5.89-3.138 10.12-6.119 13.15-8.832-2.93 1.359-5.35 2.271-7.42 3.165-1.79.778-3.21 1.421-4.35 2.97a9.806 9.806 0 0 0-1.38 2.697Zm17.97 2.636c-4.45 3.483-10.24 7.098-17.8 10.691.67 1.931 2.03 4.009 4.97 6.416 1.53-.852 3.46-2.045 5.34-3.521 2.23-1.749 4.49-3.865 5.4-6.447.12-.365.29-.716.48-1.049 0 0 1.48-2.556 1.61-6.09Zm20.06 31.168c.21-3.126.15-6.21-.37-8.877-.75-3.735-2.55-5.941-4.25-7.102-.05 1.448.03 3.222.59 4.449 1.05 2.306 2.52 6.58 4.03 11.53Zm-31.95 13.198c3.2 2.173 5.6 4.371 6.78 5.594 1.39 1.44 3.24 3.161 5.68 3.161 1.03 0 2.64-.359 4.43-.876-3.2-2.185-6.76-4.368-9.7-5.57a49.354 49.354 0 0 0-7.19-2.309Z" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2" transform="translate(-1696 -212)"/></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
web/image/icon/webcam.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

17
web/index.css Normal file
View File

@@ -0,0 +1,17 @@
body {
background-color: #d3e8ec;
}
.u {
text-decoration: underline;
}
.accordion-header .accordion-button {
font-size: 16pt;
}
.accordion-header .accordion-button[aria-expanded="true"] {
font-weight: bold;
background-color: #94b6bd;
color: #ffffff;
}
.accordion-button {
background-color: #f3f3f3;
}

22
web/index.js Executable file
View File

@@ -0,0 +1,22 @@
$(document).ready(function () {
var nextCtl = $('#vars').attr('nextCtl');
var c = $('button.accordion-button[data-bs-target="#ctl' + nextCtl + '"]');
$(c).click();
$('button[action]').click(function() {
var action = $(this).attr('action');
var url = null;
var target = action;
if ( action == 'jitsi' ) {
url = 'https://meet.homelabbrisbane.com.au/hlb';
}
if ( action == 'map' ) {
url = 'https://www.google.com/maps/place/' + $(this).attr('coordinates');
}
if ( action == 'copy' ) {
navigator.clipboard.writeText( $(this).attr('value') );
}
if ( url != null ) {
window.open( url, target );
}
});
});

197
web/index.php Executable file
View File

@@ -0,0 +1,197 @@
<!DOCTYPE html>
<?php
require 'vendor/autoload.php';
use Michelf\Markdown;
function getEvents() {
$content = file_get_contents("events.json");
$entries = json_decode($content, $associative = true);
for ( $i = 0; $i < count($entries); $i++ ) {
$entry = $entries[$i];
$dt = new DateTime( $entry["start"] );
$ctl = $dt -> format("YmdHi");
$entries[$i]["ctl"] = $ctl;
$datestamp = $dt -> format("Ymd");
$files = array( "agenda" => sprintf("agenda/%d.md", $datestamp)
, "minutes" => sprintf("minutes/%d.md", $datestamp)
);
foreach ( $files as $key => $file ) {
if ( file_exists( $file ) ) {
$entries[$i][$key] = file_get_contents($file);
}
}
}
usort($entries, function ($a, $b) {
$dateA = new DateTime($a['start']);
$dateB = new DateTime($b['start']);
return $dateA < $dateB ? 1 : -1;
});
return($entries);
}
?>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Homelab Brisbane</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
crossorigin="anonymous" />
<link href="index.css" rel="stylesheet" />
<style>
.card img {
max-height: 120px;
height: auto;
width: auto;
}
</style>
</head>
<body>
<div class="container">
<div class="p-5 mt-4 mb-4 bg-body-secondary rounded-3">
<div class="container-fluid py-4">
<h1 class="display-5 fw-bold">Homelab Brisbane</h1>
<p class="col-md-12 fs-4">Were a group of tech enthusiasts who enjoy exploring the latest trends in technology using our own hardware, in our own time. We founded Homelab Brisbane to connect with others who share this passion — to ask and answer questions, share insights, and make discoveries together.</p>
</div>
</div>
<div class="row">
<div class="col d-flex">
<div class="card h-100">
<div class="card-body">
<img src="image/icon/webcam.png" class="card-img-top" alt="...">
<h5 class="card-title">Meet Online</h5>
We meet online with our <a href="https://meet.homelabbrisbane.com.au/" target="jitsi">Jitsi</a> server every month. With these online meetings we can meet face to face, and even include people outside of Brisbane. They are announced on <a href="https://www.meetup.com/brisbane-homelabs/" target="meetup">meetup.com</a> and the <a href="#calendar">calendar below</a>.
</div>
</div>
</div>
<div class="col d-flex">
<div class="card h-100">
<div class="card-body">
<img src="image/icon/handshake.png" class="card-img-top" alt="...">
<h5 class="card-title">Meet In Person</h5>
Once a month we also meet in person in a library meeting room in Brisbane. We try to move these around to make them convenient to a broader range of people. These gatherings are also announced on <a href="https://www.meetup.com/brisbane-homelabs/" target="meetup">meetup.com</a> and on the <a href="#calendar">calendar below</a>.
</div>
</div>
</div>
<div class="col d-flex">
<div class="card h-100">
<div class="card-body">
<img src="image/icon/chat.png" class="card-img-top" alt="...">
<h5 class="card-title">Online Forum</h5>
When we're not meeting in person or online, we're discussing our various interests and projects on our <a href="https://discourse.homelabbrisbane.com.au/" target="discourse">Discourse</a> server.
</div>
</div>
</div>
<div class="col d-flex">
<div class="card h-100">
<div class="card-body">
<img src="image/icon/email.png" class="card-img-top" alt="...">
<h5 class="card-title">Contact Us</h5>
If you have any other inquiries you can always email us at <a href="mailto:listmaster@homelabbrisbane.com.au">listmaster@homelabbrisbane.com.au</a>.
</div>
</div>
</div>
</div>
<hr id="calendar" />
<div class="row mt-4">
<div class="col" style="max-width: 250px;">
<img src="image/icon/calendar.png" style="max-height: 200px; padding-left: 20px;" />
</div>
<div class="col">
<p>We publish our past and upcoming events on the following url. You can subscribe to them in the calendar of your choice.</p>
<div class="input-group input-group-lg mb-3">
<input type="text" class="form-control" disabled value="https://homelabbrisbane.com.au/cal.php">
<button class="btn btn-outline-secondary" type="button" action="copy" value="https://homelabbrisbane.com.au/cal.php">Copy</button>
</div>
<p>Subscribing to this calendar is typically an exercise in copying that address and pasting it into a "subscribe to url" dialog in your calendar platform of choice. For instructions on how to do this in Google Calendar, click <a href="https://support.google.com/calendar/answer/37100?hl=en&co=GENIE.Platform%3DDesktop" target="googleCalendarInstructions">here</a>, and for Microsoft Outlook 365, click <a href="https://support.microsoft.com/en-us/office/import-or-subscribe-to-a-calendar-in-outlook-com-or-outlook-on-the-web-cff1429c-5af6-41ec-a5b4-74f2c278e98c" target="microsoftCalendarInstructions">here</a>.</p>
</p>
</div>
</div>
<div class="row mt-2">
<div class="col">
<?php
$events = getEvents();
?>
<div class="accordion" id="accordionExample">
<?php
$nextCtl = null;
foreach ( $events as $event ) {
if ( ( new DateTime($event["start"]) ) > ( new DateTime() ) ) {
$nextCtl = $event["ctl"];
}
?>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#ctl<?= $event["ctl"] ?>" aria-expanded="false" aria-controls="ctl<?= $event["ctl"] ?>">
<?= ( new DateTime($event["start"]) ) -> format("j M Y") ?>
</button>
</h2>
<div id="ctl<?= $event["ctl"] ?>" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
<div class="accordion-body">
<div class="row">
<div class="col">
<div class="card-body">
<h3 class="card-title"><?= $event["title"] ?></h3>
<div class="input-group mt-3 mb-3">
<span class="input-group-text" id="basic-addon1">Start Time</span>
<input type="text" class="form-control" disabled value="<?= ( new DateTime($event["start"]) ) -> format("g:i a") ?>">
</div>
<div class="input-group mt-3 mb-3">
<span class="input-group-text" id="basic-addon1">Location</span>
<input type="text" class="form-control" disabled value="<?= $event["location"] ?>">
<?php
if ( $event["location"] == "Jitsi" ) {
?>
<button class="btn btn-outline-secondary" type="button" action="jitsi">
<img style="margin-top: -8px; max-height: 26px;" src="image/icon/jitsi.svg" />
</button>
<?php
} elseif ( in_array( "coordinates", array_keys($event) ) ) {
?>
<button class="btn btn-outline-secondary" type="button" action="map" coordinates="<?= $event["coordinates"] ?>">&#128205;</button>
<?php
}
?>
</div>
<?php if ( in_array( "description", array_keys($event) ) ) { ?>
<h5 class="card-title mb-2">Description</h5>
<p><?= $event["description"] ?></p>
<?php } ?>
</div>
</div>
<div class="col">
<?php
if ( in_array( "minutes", array_keys($event) ) ) {
?>
<h5 class="card-title mb-2">Minutes</h5>
<p style="white-space: pre;"><?= Markdown::defaultTransform($event["minutes"]) ?></p>
<?php
} elseif ( in_array( "agenda", array_keys($event) ) ) {
?>
<h5 class="card-title mb-2">Agenda</h5>
<p style="white-space: pre;"><?= Markdown::defaultTransform($event["agenda"]) ?></p>
<?php
}
?>
</div>
</div>
</div>
</div>
</div>
<?php
}
?>
</div>
</div>
</div>
</div>
<div id="vars" style="display: none;" nextCtl="<?= $nextCtl ?>"></div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="index.js"></script>
</body>
</html>

18
web/minutes/20250619.md Normal file
View File

@@ -0,0 +1,18 @@
- Admin
- ToS and Privacy Policy doesn't need to be hosted on the main page. It's already got a publicly accessible presence in Discourse.
- James to formalise agenda procedure
- James to render cal.php on main page
- James to publish "subscribe" link for calendar
- Recap from Last Month
- Hardware Donations for Education
- Substation 33, in Kingston or Logan?
- Dirk has a carload of stuff to offload
- Discourse Themes
- LLMs
- Hardware for LLMs
- Tiny11
- Agenda
- Motioneye
- Meshtastic
- Next Month
- Location Requests