forked from TildeClub/site
1
0
Fork 0

Compare commits

..

269 Commits

Author SHA1 Message Date
deepend-tildeclub 6dc5cd321f
Update wiki.tmpl 2025-08-03 10:03:15 -06:00
deepend-tildeclub a91c99303e
Update irc.md
mention of secondary channel.
2025-08-02 14:46:05 -06:00
deepend-tildeclub bbbaa2de7f
Update footer.php
remove status page and updated banner code.
2025-08-02 11:57:16 -06:00
ant a9172b250c Add a WIKI article about The Traditional Vi editor in ~club
About a year ago, The Traditional Vi was installed in Tilde.Club:
<https://ex-vi.sourceforge.net/> .  This patch adds a WIKI article about
this editor, and particularly about using it on the club's premises.
2025-07-29 16:24:08 +00:00
deepend-tildeclub c641e2fc9f
Update supporters.json
added new supporter +1
2025-07-23 17:44:26 -06:00
deepend-tildeclub c119bdb9f6
Update FUNDING.yml 2025-07-23 10:10:07 -06:00
deepend-tildeclub bb5886b477
Update header24h.php
new donate page
2025-07-18 13:29:18 -06:00
deepend-tildeclub 0ca98473ee
Update nav.html
new donate page
2025-07-18 13:28:38 -06:00
deepend-tildeclub c664cbfb3b
Update style.css 2025-07-14 12:18:36 -06:00
deepend-tildeclub 3900ee74a3
Update supporters.json
added gold star supporter 👍
2025-07-09 13:43:20 -06:00
deepend-tildeclub e4f3c051ef
Update index.php 2025-07-04 14:13:19 -06:00
keyboardan 7db1b1a6b9 Wiki patch to ssh section
Signed-off-by: keyboardan <keyboardan@tilde.club>
Signed-off-by: deepend <deepend@tilde.club>
2025-07-01 21:43:53 +00:00
keyboardan 371f9efb7f news-item tags for news feed
Signed-off-by: deepend <deepend@tilde.club>
2025-07-01 21:20:51 +00:00
keyboardan 1eb933f5f2 identify news element for a possible news feed
Signed-off-by: deepend <deepend@tilde.club>
2025-07-01 20:36:48 +00:00
deepend-tildeclub a98f9d0a52
Update donate.md 2025-06-09 16:13:01 -06:00
deepend-tildeclub 72b8203ad0
Update donate.md 2025-06-09 16:11:09 -06:00
deepend-tildeclub 636decf7a7
Update FUNDING.yml 2025-06-09 15:54:40 -06:00
deepend-tildeclub f5d5189b73
Update FUNDING.yml 2025-06-09 15:39:14 -06:00
deepend-tildeclub 230def16bb
Update donate.md 2025-06-09 15:18:48 -06:00
deepend-tildeclub 2db0e6c40a
Update donate.md 2025-06-09 15:18:25 -06:00
deepend-tildeclub f7aca73297
Update donate.md 2025-06-09 15:15:26 -06:00
deepend-tildeclub ffe71acf2f
Update donate.md 2025-06-09 15:13:56 -06:00
deepend-tildeclub 2205a20616
Update donate.md 2025-06-09 15:13:39 -06:00
deepend-tildeclub deea4eb334
Update donate.md
remove liberapay
2025-06-09 15:12:55 -06:00
deepend-tildeclub 49081bb620
Update nav.html 2025-05-28 18:51:24 -06:00
deepend-tildeclub 9c3b113086
Update header24h.php 2025-05-28 18:51:13 -06:00
deepend-tildeclub 33c7d9881b
Update header24h.php 2025-05-28 18:50:26 -06:00
deepend-tildeclub 076f0368ab
Update nav.html 2025-05-28 18:47:33 -06:00
deepend-tildeclub 9c999bf3c0
Update cgit.md 2025-05-28 18:41:31 -06:00
deepend-tildeclub 584a596c2d
Update cgit.md 2025-05-28 18:39:45 -06:00
deepend-tildeclub a1fba42dd3
Update cgit.md 2025-05-28 18:38:41 -06:00
deepend-tildeclub 16a842533a
Update cgit.md 2025-05-28 18:37:00 -06:00
deepend-tildeclub 5e2e1d71bb
Create cgit.md 2025-05-28 18:35:43 -06:00
deepend-tildeclub fafe495957
Update supporters.json 2025-05-07 20:45:18 -06:00
deepend-tildeclub 2644575d17
Update supporters.json 2025-05-04 22:07:00 -06:00
deepend-tildeclub 140320df02
Create pagecounter.md 2025-04-03 16:02:24 -06:00
deepend-tildeclub b6bad87eb8
Update signup-handler.php 2025-03-24 11:03:29 -06:00
deepend-tildeclub cfc138d4e6
Update donate.md 2025-03-06 15:58:19 -07:00
deepend-tildeclub e8a70697b7
Update donate.md 2025-03-06 14:17:30 -07:00
deepend-tildeclub 9eda625290
Update donate.md 2025-03-06 12:04:36 -07:00
deepend-tildeclub 4ff9cec585
Update donate.md 2025-03-06 11:48:30 -07:00
deepend-tildeclub f4bcd8f6bf
Update donate.md 2025-03-06 11:48:18 -07:00
deepend-tildeclub b0734298ec
Update donate.md 2025-03-06 11:45:45 -07:00
deepend-tildeclub d4491bcb28
Update donate.md 2025-03-06 11:28:14 -07:00
deepend-tildeclub 2f46cc83ab
Update donate.md 2025-03-06 11:27:00 -07:00
deepend-tildeclub 703f2ae260
Update supporters.json
new supporter
2025-03-03 13:29:02 -07:00
deepend-tildeclub f089b2e503
Update irc.md 2025-03-02 16:53:14 -07:00
deepend-tildeclub 6d81375778
Update irc.md
removed localhost reference.
2025-03-02 16:51:56 -07:00
deepend-tildeclub 4efa5ba4ab
Update index.php
remove extra ~
2025-02-11 20:33:12 -07:00
deepend-tildeclub 22551d20e0
Merge pull request #71 from litemotiv/dynamic_homepage_links
Colorize user home pages based on modified age
2025-02-07 12:54:40 -07:00
deepend-tildeclub 7f23984ab0
Update help_system.md 2025-02-07 12:51:16 -07:00
deepend-tildeclub 9dd8e8b510
Update cgi.md 2025-02-07 12:49:32 -07:00
deepend-tildeclub 920c032f36
Create help_system.md 2025-02-07 12:41:25 -07:00
litemotiv baf805493b Change hover to underline 2025-02-07 14:49:10 +01:00
litemotiv 1b3381eb99 Set opacity steps to 5% and emphasize hover 2025-02-07 14:00:51 +01:00
litemotiv a69e8a93bc Fix full user list layout 2025-02-05 12:02:26 +01:00
litemotiv c3d8a83eed fix userlist columns last row not honoring size 2025-02-05 11:56:31 +01:00
litemotiv cc381753a8 Colorize user home pages based on modified age 2025-02-05 11:47:34 +01:00
deepend-tildeclub 1184c77a1f
Merge pull request #68 from litemotiv/wiki_tweaks
Add order to wiki categories
2025-01-25 15:43:54 -07:00
deepend-tildeclub ae5b9fc4f5
Update admin.php 2025-01-24 19:01:35 -07:00
deepend-tildeclub 0f0dea9069
Update db.php 2025-01-24 18:57:05 -07:00
deepend-tildeclub 095dd7ea70
Update db.php 2025-01-24 18:40:13 -07:00
deepend-tildeclub 54e0502922
Update .gitignore 2025-01-24 18:34:31 -07:00
deepend-tildeclub f106cbecb9
Merge pull request #69 from tildeclub/deepend-tildeclub-patch-2
add poll system.
2025-01-24 18:33:44 -07:00
deepend-tildeclub 6a342bcc7a
Create db.php 2025-01-24 18:32:13 -07:00
deepend-tildeclub ff4e747196
Create setup.php 2025-01-24 18:31:20 -07:00
deepend-tildeclub 040c0bf64e
Create admin.php 2025-01-24 18:26:55 -07:00
deepend-tildeclub 002ea7dfe7
Create api.php 2025-01-24 18:25:42 -07:00
deepend-tildeclub 2f146ee57f
Create index.php 2025-01-24 18:24:46 -07:00
litemotiv 8abe14fb8c Clarify bashblog title 2025-01-24 14:24:42 +01:00
litemotiv dbba7245e9 Remove multiplexers capitals 2025-01-24 14:23:53 +01:00
litemotiv 852506d7c8 Clarify 404 title 2025-01-24 14:22:26 +01:00
litemotiv 97edfa0fc1 Clarify ssh article title 2025-01-24 14:20:38 +01:00
litemotiv f1111429d8 Decapitalize 2fa title 2025-01-24 14:18:57 +01:00
litemotiv 321c46c259 Clarify ttbp title 2025-01-24 14:18:08 +01:00
litemotiv 0fa06feed7 Clarify sshfs title 2025-01-24 14:14:35 +01:00
litemotiv 64c7616c08 Move and clarify ssh article 2025-01-24 14:14:07 +01:00
litemotiv 83ce0381f3 Clarify tin article title 2025-01-24 14:12:06 +01:00
litemotiv 5d9eec4133 Clarify terminal multiplexers title 2025-01-24 14:11:23 +01:00
litemotiv 2487d6c49a Clarify and move vimrc article 2025-01-24 14:09:11 +01:00
litemotiv be0cca5d2c Remove double quotes from CGI title for sorting 2025-01-24 14:07:37 +01:00
litemotiv 4916f15249 Move and clarify BBJ 2025-01-24 14:06:34 +01:00
litemotiv dffcd754c3 Capitalize article title for alphabetic sorting 2025-01-23 12:05:06 +01:00
litemotiv 33d0f6062c Add favicon to article page 2025-01-22 22:51:25 +01:00
litemotiv fd964baa96 Move VPN article to Tutorials category 2025-01-22 22:38:35 +01:00
litemotiv 6111c5185f Fix favicon display in subdirectories 2025-01-22 22:32:36 +01:00
litemotiv 0e2d568080 Add order to wiki categories 2025-01-22 22:29:18 +01:00
deepend-tildeclub 85305b357d
Merge pull request #67 from litemotiv/fix_wiki_size
Fix wiki column size
2025-01-20 18:14:47 -07:00
litemotiv e5ac1ee1aa Fix wiki column size 2025-01-21 01:52:44 +01:00
deepend-tildeclub 0450221613
Merge pull request #66 from litemotiv/fix_layout
Restore 2-column layout for 768px and higher resolution
2025-01-20 17:27:59 -07:00
litemotiv 8933f0a160 Restore 2-column layout for 768px and higher resolution 2025-01-20 23:50:27 +01:00
deepend-tildeclub 3d686c3fb3
Merge pull request #65 from litemotiv/home_font
Consolidate fonts to Liberation Mono
2025-01-20 15:27:16 -07:00
deepend-tildeclub b7696a75e6
Merge pull request #63 from alvanrahimli/patch-1
Update last release date of `tin`
2025-01-20 15:26:25 -07:00
alvanrahimli 248ff69854 Remove version information from tin wiki page 2025-01-20 21:21:36 +01:00
litemotiv fcf97d0279 Add monospace to base font list 2025-01-20 02:39:24 +01:00
litemotiv 96a40f437c Restore stylesheet path to root url 2025-01-20 02:38:01 +01:00
litemotiv 79859e0037 Consolidate fonts to Liberation Mono 2025-01-20 02:06:30 +01:00
Alvan Rahimli fee4fa5a6c
Update last release date of `tin`
The latest release of `tin` is TIN 2.6.4 - which has been released on 24th of December, 2024.
2025-01-19 21:35:46 +01:00
deepend-tildeclub 1f7214411d
Update .gitignore 2025-01-18 16:05:25 -07:00
deepend-tildeclub 25369a6f4d
Merge pull request #62 from litemotiv/updated_v2
Updated homepage indicator, reduced html output
2025-01-18 16:04:04 -07:00
litemotiv 81eeb53987 Updated homepage indicator, reduced html output 2025-01-18 23:11:42 +01:00
deepend-tildeclub dd62a18333
Merge pull request #60 from litemotiv/updated_homepages
Test only index pages for recent changes
2025-01-18 11:56:07 -07:00
litemotiv 3e3a512535 Cache updated user homepages list 2024-12-08 11:40:25 +01:00
litemotiv 3cdd5d6cb2 Test only index pages for recent changes 2024-12-08 10:44:40 +01:00
deepend-tildeclub b45ec60cc2
Merge pull request #57 from litemotiv/online_users
Simplify active users scroller and prevent loop from finishing early
2024-12-07 17:22:29 -07:00
deepend-tildeclub cca8aeeb1d
Merge pull request #58 from litemotiv/favicon
Totally optional tilde favicon
2024-12-07 17:20:57 -07:00
deepend-tildeclub d5f2563446
Update supporters.json 2024-11-22 19:09:46 -07:00
litemotiv cfb2128fd6 Totally optional tilde favicon 2024-11-02 13:00:00 +01:00
litemotiv c93fae31e1 Simplify active users scroller and prevent loop from finishing early 2024-11-02 12:45:35 +01:00
deepend-tildeclub b113fe11a3
Merge pull request #56 from litemotiv/tweaks
A few small tweaks to the header and stylesheet
2024-11-01 18:34:56 -06:00
deepend-tildeclub d130b781cb
Update index.php 2024-11-01 16:39:09 -06:00
deepend-tildeclub 4c7113dd74
Update index.php 2024-11-01 16:38:21 -06:00
litemotiv bd21c4fd1a Prevent RSS icon from resizing on the first load 2024-10-27 11:57:46 +01:00
litemotiv 8fe11a8dba Remove legacy IE tag 2024-10-27 11:54:51 +01:00
litemotiv e860ab0bcc Set HTML Doctype 2024-10-27 11:54:02 +01:00
litemotiv 258a8c1622 Prevent the H1 animation from pushing down the contents on the first run 2024-10-27 11:52:33 +01:00
deepend-tildeclub f1c971e910
Update 404.php 2024-10-21 11:49:14 -06:00
deepend-tildeclub 4e6fd1e7ea
Delete errors/499.php 2024-10-21 11:26:31 -06:00
deepend-tildeclub ad325ceeea
Update and rename error404.php to errors/404.php 2024-10-21 11:24:56 -06:00
deepend-tildeclub a470a27105
Create 502.php 2024-10-21 11:20:23 -06:00
deepend-tildeclub acdf650fe5
Create 500.php 2024-10-21 11:19:26 -06:00
deepend-tildeclub dfb3432664
Create 499.php 2024-10-21 10:19:18 -06:00
deepend-tildeclub c97eeb3671
Create 413.php 2024-10-21 10:17:53 -06:00
deepend-tildeclub 3a4823243e
Create 408.php 2024-10-21 10:16:36 -06:00
deepend-tildeclub 79e32499b5
Create 405.php 2024-10-21 10:15:47 -06:00
deepend-tildeclub 494811f60e
Create 403.php 2024-10-21 10:13:44 -06:00
deepend-tildeclub f0917cefc2
Create 400.php 2024-10-21 10:12:56 -06:00
deepend-tildeclub cc20fea6e0
Delete wiki/source/mailing_list.md
discontinuing mailing list.  Doesn't see enough use.
2024-10-20 11:04:15 -06:00
deepend-tildeclub 9c86f971db
Update 2fa.md
removed authy.
2024-10-20 11:02:50 -06:00
deepend-tildeclub 2302e282e8
Update irc.md 2024-10-20 10:59:38 -06:00
deepend-tildeclub 20afc77fd8
Update supporters.json 2024-10-14 19:49:30 -06:00
deepend-tildeclub 110fbd7365
Update index.php 2024-09-19 19:28:20 -06:00
deepend-tildeclub 9534c7e34c
Update style.css 2024-09-19 19:26:39 -06:00
deepend-tildeclub 825af838dd
Update index.php 2024-09-19 19:24:29 -06:00
keyboardan 420e0d7cf2 Wiki git patch to the Emacs section
Here is a git patch to the club's wiki. Apply this patch instead of the
patch of my last email.

From f018b8aad2a33caaee678ec007875f41dca5a736 Mon Sep 17 00:00:00 2001
From: keyboardan <keyboardan@tilde.club>
Date: Sun, 1 Sep 2024 13:58:23 +0100
Subject: [PATCH] wiki/source/emacs.md: add and update content
2024-09-01 15:43:48 +00:00
deepend-tildeclub cf5b0526d6
Update supporters.json
added crivic
2024-08-31 18:14:40 -06:00
deepend-tildeclub 11c0102a83
Update supporters.json 2024-08-23 19:23:03 -06:00
deepend-tildeclub cfeb303b97
Merge pull request #54 from alexlehm/ssh-backslash
fix commands with backslashes for windows, fix cmd format
2024-08-19 12:03:26 -06:00
Aleander Lehmann 69087be669 fix commands with backslashes for windows, fix cmd format 2024-08-19 16:23:21 +00:00
deepend-tildeclub f17390d706
Update footer.php 2024-08-17 11:57:11 -06:00
deepend-tildeclub 3ff750240d
Update index.php 2024-08-17 11:56:54 -06:00
deepend-tildeclub 23039ce7e2
Update header.php 2024-08-17 11:55:46 -06:00
deepend-tildeclub 90c4f3d500
Update footer.php 2024-08-17 11:54:02 -06:00
deepend-tildeclub 66087b1db3
Update footer.php 2024-08-17 11:53:40 -06:00
deepend-tildeclub f1543bcada
Update header.php 2024-08-17 11:52:25 -06:00
deepend-tildeclub 1ca4308a44
Update style.css 2024-08-17 11:46:27 -06:00
deepend-tildeclub ce11671717
Update index.php 2024-08-17 11:45:56 -06:00
deepend-tildeclub f92b12f8eb
Update index.php 2024-08-17 11:43:54 -06:00
deepend-tildeclub a997631cc6
Update style.css 2024-08-17 11:42:50 -06:00
deepend-tildeclub e40c9bea2d
Update style.css 2024-08-17 11:42:14 -06:00
deepend-tildeclub 046d37a355
Update footer.php
nntp stats
2024-08-17 11:37:49 -06:00
deepend-tildeclub 10bdfa43cb
Update footer.php 2024-08-17 11:36:40 -06:00
deepend-tildeclub cc540cd077
Update news.php 2024-08-17 11:34:34 -06:00
deepend-tildeclub 5b2c36a19e
Update footer.php 2024-08-17 11:33:55 -06:00
deepend 8b16a1ca02 added news.php 2024-08-17 17:31:39 +00:00
deepend 4694bedc07 updated index and added news.php. hid advisory. 2024-08-17 17:30:12 +00:00
deepend 55af3e13a5 changed updateonline-users 2024-08-17 17:20:14 +00:00
deepend-tildeclub 7a622eee6d
Create updateonline-users 2024-08-17 11:19:37 -06:00
deepend-tildeclub d73b120723
Update .gitignore 2024-08-17 11:19:05 -06:00
deepend-tildeclub 1f01b44d2a
Update .gitignore 2024-08-17 11:17:16 -06:00
deepend-tildeclub d923942f34
Update index.php 2024-08-17 10:56:24 -06:00
deepend-tildeclub 559820b906
Update style.css 2024-08-17 10:55:38 -06:00
deepend-tildeclub e9fdc3f345
Update style.css 2024-08-17 10:54:28 -06:00
deepend-tildeclub bee9ced345
Update index.php 2024-08-17 10:54:02 -06:00
deepend-tildeclub fb4c3033a7
Update index.php 2024-08-17 10:51:30 -06:00
deepend-tildeclub bb38fcaf12
Update style.css 2024-08-17 10:50:17 -06:00
deepend-tildeclub d6c8159496
Update style.css 2024-08-17 10:47:46 -06:00
deepend-tildeclub 84cb556779
Update index.php 2024-08-17 10:47:20 -06:00
deepend-tildeclub 0b3bd2a8f1
Update style.css 2024-08-17 10:45:59 -06:00
deepend-tildeclub 3537c30a48
Update style.css 2024-08-17 10:44:51 -06:00
deepend-tildeclub 5f2dee4000
Update style.css 2024-08-17 10:43:49 -06:00
deepend-tildeclub cc913bad6d
Update index.php 2024-08-17 10:41:52 -06:00
deepend-tildeclub cea587a65a
Update style.css 2024-08-17 10:41:25 -06:00
deepend-tildeclub b1598d1c75
Update style.css 2024-08-17 10:40:11 -06:00
deepend-tildeclub dc7e57c915
Update style.css 2024-08-17 10:38:49 -06:00
deepend-tildeclub 2c12d1a77a
Update style.css 2024-08-17 10:37:38 -06:00
deepend-tildeclub ca8a29713d
Update style.css 2024-08-17 10:36:39 -06:00
deepend-tildeclub 181e288b4e
Update index.php 2024-08-17 10:35:18 -06:00
deepend-tildeclub 0e6595dc42
Update index.php 2024-08-17 10:35:05 -06:00
deepend-tildeclub ce6b4937ae
Update header.php 2024-08-17 10:27:18 -06:00
deepend-tildeclub 8ba9569838
Update style.css 2024-08-17 10:26:17 -06:00
deepend-tildeclub 3c2fbbc0b3
Update style.css 2024-08-17 10:23:38 -06:00
deepend-tildeclub 23f73e6094
Rename rss.png to images/rss.png 2024-08-17 10:22:26 -06:00
deepend-tildeclub 170c35bc76
Add files via upload
rss icon
2024-08-17 10:22:10 -06:00
deepend-tildeclub abb7f6537b
Update header.php
add rss feed
2024-08-17 10:21:20 -06:00
deepend-tildeclub f436a4464d
Update index.php 2024-08-17 09:18:24 -06:00
deepend-tildeclub dc6e03bb09
Update style.css 2024-08-17 09:16:03 -06:00
deepend-tildeclub ee0ee935cd
Update style.css 2024-08-17 09:15:37 -06:00
deepend-tildeclub 3591280968
Update style.css 2024-08-17 09:14:57 -06:00
deepend-tildeclub 672b2e868f
Update style.css 2024-08-17 09:12:49 -06:00
deepend-tildeclub 904b5dd616
Update style.css 2024-08-17 09:11:45 -06:00
deepend-tildeclub 2e17a21be7
Update style.css 2024-08-17 09:10:51 -06:00
deepend-tildeclub 9503117dcb
Update style.css 2024-08-17 09:09:38 -06:00
deepend-tildeclub 3c67a32620
Update style.css 2024-08-17 09:07:42 -06:00
deepend-tildeclub ffd4b1278e
Update index.php 2024-08-17 09:07:12 -06:00
deepend-tildeclub f63ab8a9b5
Update news.json 2024-08-17 08:56:12 -06:00
deepend-tildeclub 217b00b6e6
Update index.php 2024-08-17 08:55:28 -06:00
deepend-tildeclub dcbd902aaa
Update news.json 2024-08-17 08:51:57 -06:00
deepend-tildeclub 4cc538c1be
Update index.php 2024-08-17 08:50:57 -06:00
deepend-tildeclub c115370510
Update index.php 2024-08-17 08:49:36 -06:00
deepend-tildeclub 8c0ccfb1fb
Update index.php 2024-08-17 08:46:25 -06:00
deepend-tildeclub 7cfb3df2fe
Update index.php 2024-08-17 08:44:34 -06:00
deepend-tildeclub d37b017d26
Update index.php 2024-08-17 08:41:53 -06:00
deepend-tildeclub 5ff71c8ee4
Update index.php 2024-08-17 08:39:06 -06:00
deepend-tildeclub 229282b8bc
Create news.json 2024-08-17 08:37:51 -06:00
deepend-tildeclub f68dbb8dad
Update index.php
moving news to json file.
2024-08-17 08:37:20 -06:00
deepend-tildeclub 436a87e234
Update index.php
change 10 most recent gold star supporters .
2024-08-17 08:31:04 -06:00
deepend-tildeclub bbfc51b8cb
Create supporters.json 2024-08-17 08:26:25 -06:00
deepend-tildeclub 7d056e8428
Update index.php
move goldstar supporters to json file
2024-08-17 08:25:01 -06:00
deepend 058d3662a7 added quota information to faq. 2024-08-16 12:46:45 +00:00
deepend 4c45bb9f27 quotas notice added 2024-08-16 12:36:56 +00:00
deepend-tildeclub 230e1dfcba
added tilde favicon 2024-07-16 10:36:55 -06:00
deepend-tildeclub ff9a2c6b54
Update index.php 2024-07-15 14:55:42 -06:00
deepend-tildeclub f458071c8a
Update irc.md
changed glowing bear link to locally hosted one.
2024-05-23 14:17:58 -06:00
deepend-tildeclub 350821e3e6
Update index.php 2024-05-10 07:54:38 -06:00
deepend-tildeclub cf5897d00a
Merge pull request #53 from alexlehm/cgi_perl_example
fix perl example, change chmod
2024-05-08 12:51:09 -06:00
Aleander Lehmann ac64e5cd44 fix perl example, change chmod 2024-05-08 17:54:40 +00:00
deepend-tildeclub 9a0236f48d
Update 404.html
edit title
2024-04-02 12:35:51 -06:00
deepend-tildeclub a8cddbf166
Update multiplexers.md 2024-03-25 12:19:17 -06:00
deepend-tildeclub f830ea1910
Delete wiki/source/screen.md 2024-03-25 12:16:32 -06:00
deepend-tildeclub cd5ff0bd10
Delete wiki/source/ttrv.md 2024-03-25 12:16:03 -06:00
deepend-tildeclub 0aa5647e11
Delete wiki/source/tmux.md 2024-03-25 12:15:47 -06:00
deepend-tildeclub b8959d78fb
Update faq.md
updating some links
2024-03-25 12:09:24 -06:00
deepend-tildeclub beeedf2707
Update irc.md 2024-03-25 11:19:04 -06:00
deepend-tildeclub 3c7057a017
Update index.php
added supporter.
2024-03-23 13:56:37 -06:00
deepend-tildeclub f9ae044792
Update index.php 2024-03-23 13:42:06 -06:00
deepend-tildeclub ccf2ce5d15
Update index.php 2024-03-23 13:38:35 -06:00
deepend-tildeclub e2d7f8c402
Update index.php 2024-03-23 13:37:57 -06:00
deepend-tildeclub 36e5cb435e
Rename indexold.php to indexold 2024-03-23 13:37:12 -06:00
deepend-tildeclub 5748695869
Create index.php 2024-03-23 13:36:40 -06:00
deepend-tildeclub f9874a3996
Rename index.php to indexold.php 2024-03-23 13:34:46 -06:00
deepend-tildeclub 528313c248
Rename autoconfig.xml to .well-known/autoconfig/mail/config-v1.1.xml 2024-03-19 17:17:11 -06:00
deepend-tildeclub 21cce97e87
Create autoconfig.xml 2024-03-19 17:10:49 -06:00
deepend-tildeclub f7e9ee87ae
Update index.php
typo
2024-03-10 08:15:17 -06:00
deepend-tildeclub c9627b88cc
Update index.php
added donors. :)
2024-03-10 08:12:13 -06:00
deepend-tildeclub f1e9999e64
Update index.php 2024-03-03 13:16:32 -07:00
deepend-tildeclub 052039715a
Update index.php
spelling error.
2024-01-20 23:10:38 -07:00
deepend-tildeclub 1f1943cc56
Update index.php
gmail email addresses blocked for signup.
2024-01-20 22:40:10 -07:00
deepend 365ebab2ef fixed signup page 2024-01-21 05:35:26 +00:00
deepend-tildeclub 24df7d9359
Update index.php 2024-01-20 22:01:29 -07:00
deepend-tildeclub 78f76ed7d9
Update index.php 2024-01-20 21:58:25 -07:00
deepend-tildeclub b99dcf55bb
Update index.php 2024-01-19 16:00:43 -07:00
deepend-tildeclub 57451374c9
Update index.php
removed un-needed period.
2024-01-10 21:45:38 -07:00
deepend-tildeclub c4b19b660d
Update index.php
gmail issue notice
2024-01-10 21:45:08 -07:00
deepend-tildeclub fcb7fa2baf
Update index.php 2024-01-07 13:26:16 -07:00
deepend-tildeclub 69278ec8f1
Update index.php 2024-01-07 13:25:28 -07:00
deepend-tildeclub c0ba435972
Update index.php 2024-01-07 13:25:13 -07:00
deepend-tildeclub 89b0bbbbcd
Update index.php 2024-01-07 13:24:18 -07:00
deepend-tildeclub ca7b44c5a7
Update index.php 2024-01-07 13:23:32 -07:00
deepend-tildeclub 1733ea82c4
Update index.php 2024-01-07 13:22:35 -07:00
deepend-tildeclub ffe3db4241
Merge pull request #49 from Cordelya/master
Update irc.md
2024-01-05 14:30:45 -07:00
deepend-tildeclub 07dd1e93f4
Update README.md 2024-01-05 13:06:36 -07:00
deepend-tildeclub 3edcc92bd4
Space based death ray now operational
Updated README.md
2024-01-05 12:29:39 -07:00
deepend-tildeclub ea9fcac4ce
Update signup-handler.php 2024-01-05 12:02:49 -07:00
deepend-tildeclub 5d2829d3ad
Update DNS2.php 2024-01-05 11:59:24 -07:00
deepend-tildeclub 830f26f26c
Update ipaddr.php 2024-01-05 11:56:38 -07:00
deepend-tildeclub a197ed6e54
Update smtp.php 2024-01-05 11:55:48 -07:00
deepend-tildeclub 76399a5d49
Update signup-handler.php 2024-01-05 11:34:08 -07:00
deepend-tildeclub 71b4b4ff29
Update index.php 2024-01-05 11:29:57 -07:00
deepend-tildeclub da8f2e95e1
Update signup-handler.php 2024-01-05 11:28:41 -07:00
Cordelya Sharpe ddb0688d51
Update tin.md
update date of last tin release
2023-12-24 22:53:14 -05:00
Cordelya Sharpe 531a955157
Update irc.md
add extra info about localhost IRC connections
2023-12-24 16:27:46 -05:00
deepend-tildeclub b47dbaec0c
Update index.php 2023-12-21 16:23:17 -07:00
deepend-tildeclub 1b79fa9c6e
Update gold star supporters. Sorry for delay Let me know if you were missed. 2023-12-19 15:35:37 -07:00
xwindows 232d64a5f5 Fix unquoted colon in YAML frontmatter of cgi.md
This patch is provided to Tilde.club under the terms of
GNU General Public License, any version
published by the Free Software Foundation.
2023-11-14 06:49:34 +00:00
xwindows 6e704f42ed Correct email link, heading misuse, and trailing newline in irc.md
Thanks user seifferth for bringing this to my attention.

This patch is provided to Tilde.club under the terms of
GNU General Public License, any version
published by the Free Software Foundation.
2023-11-13 22:01:24 +00:00
xwindows f97a2c53a7 Add "Notes for the nerds" section to wiki.md
This patch is provided to Tilde.club under the terms of
GNU General Public License, any version
published by the Free Software Foundation.
2023-11-13 22:01:12 +00:00
xwindows 3948a9b8c4 Add robots.txt
This commit adds robots.txt entries to address web crawlers' accesses
to user xwindows' web space.

This patch is provided to Tilde.club under the terms of
GNU General Public License, any version
published by the Free Software Foundation.
2023-11-13 22:00:58 +00:00
xwindows f818c4551a Add cgi.md ("CGI: Making web applications like it's 90s")
This patch is provided to Tilde.club under the terms of
GNU General Public License, any version
published by the Free Software Foundation.
2023-11-13 21:59:44 +00:00
70 changed files with 9216 additions and 8405 deletions

5
.github/FUNDING.yml vendored
View File

@ -1,2 +1,5 @@
# These are supported funding model platforms
liberapay: tilde.club # Replace with a single Liberapay username
ko_fi: tildeclub # Replace with a single Liberapay username
github: tildeclub
custom: https://www.paypal.com/donate?hosted_button_id=DWHSADKJ26HZ8
custom: https://donate.tilde.club

4
.gitignore vendored
View File

@ -6,6 +6,10 @@ poweredby.png
social.html
tilde.24h*
tilde.json
online-users.json
changes.rss
news/
icons/
stats/
cache/
polls/polls.db

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<clientConfig version="1.1">
<emailProvider id="tilde.club">
<domain>tilde.club</domain>
<displayName>TildeClub EMail</displayName>
<displayShortName>club</displayShortName>
<incomingServer type="imap">
<hostname>imap.tilde.club</hostname>
<port>993</port>
<socketType>SSL</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILLOCALPART%</username>
</incomingServer>
<outgoingServer type="smtp">
<hostname>smtp.tilde.club</hostname>
<port>587</port>
<socketType>STARTTLS</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILLOCALPART%</username>
</outgoingServer>
</emailProvider>
</clientConfig>

119
404.html
View File

@ -1,119 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>The page is not found</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css">
/*<![CDATA[*/
body {
background-color: #fff;
color: #000;
font-size: 0.9em;
font-family: sans-serif,helvetica;
margin: 0;
padding: 0;
}
:link {
color: #c00;
}
:visited {
color: #c00;
}
a:hover {
color: #f50;
}
h1 {
text-align: center;
margin: 0;
padding: 0.6em 2em 0.4em;
background-color: #294172;
color: #fff;
font-weight: normal;
font-size: 1.75em;
border-bottom: 2px solid #000;
}
h1 strong {
font-weight: bold;
font-size: 1.5em;
}
h2 {
text-align: center;
background-color: #3C6EB4;
font-size: 1.1em;
font-weight: bold;
color: #fff;
margin: 0;
padding: 0.5em;
border-bottom: 2px solid #294172;
}
h3 {
text-align: center;
background-color: #ff0000;
padding: 0.5em;
color: #fff;
}
hr {
display: none;
}
.content {
padding: 1em 5em;
}
.alert {
border: 2px solid #000;
}
img {
border: 2px solid #fff;
padding: 2px;
margin: 2px;
}
a:hover img {
border: 2px solid #294172;
}
.logos {
margin: 1em;
text-align: center;
}
/*]]>*/
</style>
</head>
<body>
<h1><strong>nginx error!</strong></h1>
<div class="content">
<h3>The page you are looking for is not found.</h3>
<div class="alert">
<h2>Website Administrator</h2>
<div class="content">
<p>Something has triggered missing webpage on your
website. This is the default 404 error page for
<strong>nginx</strong> that is distributed with
Fedora. It is located
<tt>/usr/share/nginx/html/404.html</tt></p>
<p>You should customize this error page for your own
site or edit the <tt>error_page</tt> directive in
the <strong>nginx</strong> configuration file
<tt>/etc/nginx/nginx.conf</tt>.</p>
</div>
</div>
<div class="logos">
<a href="http://nginx.net/"><img
src="/nginx-logo.png"
alt="[ Powered by nginx ]"
width="121" height="32" /></a>
<a href="http://fedoraproject.org/"><img
src="/poweredby.png"
alt="[ Powered by Fedora ]"
width="88" height="31" /></a>
</div>
</div>
</body>
</html>

136
50x.html
View File

@ -1,119 +1,19 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>The page is temporarily unavailable</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css">
/*<![CDATA[*/
body {
background-color: #fff;
color: #000;
font-size: 0.9em;
font-family: sans-serif,helvetica;
margin: 0;
padding: 0;
}
:link {
color: #c00;
}
:visited {
color: #c00;
}
a:hover {
color: #f50;
}
h1 {
text-align: center;
margin: 0;
padding: 0.6em 2em 0.4em;
background-color: #294172;
color: #fff;
font-weight: normal;
font-size: 1.75em;
border-bottom: 2px solid #000;
}
h1 strong {
font-weight: bold;
font-size: 1.5em;
}
h2 {
text-align: center;
background-color: #3C6EB4;
font-size: 1.1em;
font-weight: bold;
color: #fff;
margin: 0;
padding: 0.5em;
border-bottom: 2px solid #294172;
}
h3 {
text-align: center;
background-color: #ff0000;
padding: 0.5em;
color: #fff;
}
hr {
display: none;
}
.content {
padding: 1em 5em;
}
.alert {
border: 2px solid #000;
}
img {
border: 2px solid #fff;
padding: 2px;
margin: 2px;
}
a:hover img {
border: 2px solid #294172;
}
.logos {
margin: 1em;
text-align: center;
}
/*]]>*/
</style>
</head>
<body>
<h1><strong>nginx error!</strong></h1>
<div class="content">
<h3>The page you are looking for is temporarily unavailable. Please try again later.</h3>
<div class="alert">
<h2>Website Administrator</h2>
<div class="content">
<p>Something has triggered an error on your
website. This is the default error page for
<strong>nginx</strong> that is distributed with
Fedora. It is located
<tt>/usr/share/nginx/html/50x.html</tt></p>
<p>You should customize this error page for your own
site or edit the <tt>error_page</tt> directive in
the <strong>nginx</strong> configuration file
<tt>/etc/nginx/nginx.conf</tt>.</p>
</div>
</div>
<div class="logos">
<a href="http://nginx.net/"><img
src="/nginx-logo.png"
alt="[ Powered by nginx ]"
width="121" height="32" /></a>
<a href="http://fedoraproject.org/"><img
src="/poweredby.png"
alt="[ Powered by Fedora ]"
width="88" height="31" /></a>
</div>
</div>
</body>
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>An error occurred.</h1>
<p>Sorry, the page you are looking for is currently unavailable.<br/>
Please try again later.</p>
<p>If you are the system administrator of this resource then you should check
the error log for details.</p>
<p><em>Faithfully yours, nginx.</em></p>
</body>
</html>

View File

@ -13,3 +13,6 @@ Tilde.club is moving very quickly and is 100% volunteer led.
## 2. suboptimal: open ended feature requests and broad discussion
- We love philosophy, just not in our issue tracker!
signup code from [https://tildegit.org/team/site/src/branch/master/signup](https://tildegit.org/team/site/src/branch/master/signup)

22
errors/400.php Normal file
View File

@ -0,0 +1,22 @@
<?php include "../header.php"; ?>
<h1 id="fancyboi">ERROR 400 Bad Request</h1>
<div class="grid">
<div class="row">
<div class="col">
<p>A 400 error status implies The request sent to the server was invalid or malformed. This could happen if there is a mistake in the request parameters or syntax.</p>
<br>
<h3>If you're a site visitor</h3>
<p>Please use your browser's back button and check that you're in the right place. If you need immediate assistance, please send us an email instead.</p>
<h3>If you're the site owner</h3>
<p>Please check that you're in the right place and get in touch with your website provider if you believe this to be an error.</p>
</div>
</div>
<?php include "../footer.php"; ?>

22
errors/403.php Normal file
View File

@ -0,0 +1,22 @@
<?php include "../header.php"; ?>
<h1 id="fancyboi">ERROR 403 Forbidden</h1>
<div class="grid">
<div class="row">
<div class="col">
<p>A 403 error status implies You dont have permission to access the requested resource. This usually occurs when access to the resource is restricted.</p>
<br>
<h3>If you're a site visitor</h3>
<p>Please use your browser's back button and check that you're in the right place. If you need immediate assistance, please send us an email instead.</p>
<h3>If you're the site owner</h3>
<p>Please check that you're in the right place and get in touch with your website provider if you believe this to be an error.</p>
</div>
</div>
<?php include "../footer.php"; ?>

View File

@ -1,6 +1,6 @@
<?php include "header.php"; ?>
<?php include "../header.php"; ?>
<h1 id="fancyboi">ERROR 404</h1>
<h1 id="fancyboi">ERROR 404 Not Found</h1>
<div class="grid">
@ -19,5 +19,5 @@
</div>
<?php include "footer.php"; ?>
<?php include "../footer.php"; ?>

22
errors/405.php Normal file
View File

@ -0,0 +1,22 @@
<?php include "../header.php"; ?>
<h1 id="fancyboi">ERROR 405 Method Not Allowed</h1>
<div class="grid">
<div class="row">
<div class="col">
<p>A 405 error implies the method used in the request (such as GET, POST, PUT, etc.) is not allowed for the requested resource.</p>
<br>
<h3>If you're a site visitor</h3>
<p>Please use your browser's back button and check that you're in the right place. If you need immediate assistance, please send us an email instead.</p>
<h3>If you're the site owner</h3>
<p>Please check that you're in the right place and get in touch with your website provider if you believe this to be an error.</p>
</div>
</div>
<?php include "../footer.php"; ?>

22
errors/408.php Normal file
View File

@ -0,0 +1,22 @@
<?php include "../header.php"; ?>
<h1 id="fancyboi">ERROR 408 Request Timeout</h1>
<div class="grid">
<div class="row">
<div class="col">
<p>A 408 error implies the server timed out while waiting for the clients request. This usually happens when the request takes too long to complete.</p>
<br>
<h3>If you're a site visitor</h3>
<p>Please use your browser's back button and check that you're in the right place. If you need immediate assistance, please send us an email instead.</p>
<h3>If you're the site owner</h3>
<p>Please check that you're in the right place and get in touch with your website provider if you believe this to be an error.</p>
</div>
</div>
<?php include "../footer.php"; ?>

22
errors/413.php Normal file
View File

@ -0,0 +1,22 @@
<?php include "../header.php"; ?>
<h1 id="fancyboi">ERROR 413 Payload Too Large</h1>
<div class="grid">
<div class="row">
<div class="col">
<p>A 413 error implies the server is refusing to process the request because the payload (body of the request) is too large.</p>
<br>
<h3>If you're a site visitor</h3>
<p>Please use your browser's back button and check that you're in the right place. If you need immediate assistance, please send us an email instead.</p>
<h3>If you're the site owner</h3>
<p>Please check that you're in the right place and get in touch with your website provider if you believe this to be an error.</p>
</div>
</div>
<?php include "../footer.php"; ?>

22
errors/500.php Normal file
View File

@ -0,0 +1,22 @@
<?php include "../header.php"; ?>
<h1 id="fancyboi">ERROR 500 Internal Server Error</h1>
<div class="grid">
<div class="row">
<div class="col">
<p>A 500 error implies the server encountered an internal error or misconfiguration and was unable to complete the request.</p>
<br>
<h3>If you're a site visitor</h3>
<p>Please use your browser's back button and check that you're in the right place. If you need immediate assistance, please send us an email instead.</p>
<h3>If you're the site owner</h3>
<p>Please check that you're in the right place and get in touch with your website provider if you believe this to be an error.</p>
</div>
</div>
<?php include "../footer.php"; ?>

22
errors/502.php Normal file
View File

@ -0,0 +1,22 @@
<?php include "../header.php"; ?>
<h1 id="fancyboi">ERROR 502 Bad Gateway</h1>
<div class="grid">
<div class="row">
<div class="col">
<p>A 502 error implies the server, while acting as a gateway or proxy, received an invalid response from the upstream server.</p>
<br>
<h3>If you're a site visitor</h3>
<p>Please use your browser's back button and check that you're in the right place. If you need immediate assistance, please send us an email instead.</p>
<h3>If you're the site owner</h3>
<p>Please check that you're in the right place and get in touch with your website provider if you believe this to be an error.</p>
</div>
</div>
<?php include "../footer.php"; ?>

BIN
favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

View File

@ -1,21 +1,19 @@
</div>
</div>
<div>
<br>
<footer class="text-center">
<br><br>
<!-- Tildeverse Banner Exchange code begin -->
<div style="text-align: center;">
<iframe src="https://banner.tildeverse.org/work.php?ID=deepend" width="468" height="60" marginwidth="0" marginheight="0" hspace="0" vspace="0" frameborder="0" scrolling="no"></iframe>
<p><a href="https://banner.tildeverse.org" target="_blank">Tildeverse Banner Exchange</a></p>
</div>
<!-- Tildeverse Banner Exchange code begin -->
<br><br>
<p>ECDSA fingerprint: SHA256:duamOATgnGcfRFFkotCwrAWzZtRjwxm64WAhq5tQRwE</p>
<a href="https://github.com/tildeclub/site">page source</a> | <a href="http://updown.tilde.club/">Uptime Status</a> | <a href="https://tilde.club/stats" target="_blank">Stats</a>
</footer>
</body>
</div> <!-- Close .content div opened in header.php -->
</div> <!-- Close the outermost div if there is any -->
<br>
<footer class="text-center">
<br><br>
<!-- Tildeverse Banner Exchange code begin -->
<div style="text-align: center;">
<iframe src="https://tildeexchange.org/work.php?ID=deepend" width="468" height="60" marginwidth="0" marginheight="0" hspace="0" vspace="0" frameborder="0" scrolling="no"></iframe>
<p><a href="https://tildeexchange.org" target="_blank">Tildeverse Banner Exchange</a></p>
</div>
<!-- Tildeverse Banner Exchange code end -->
<br><br>
<p>ECDSA fingerprint: SHA256:duamOATgnGcfRFFkotCwrAWzZtRjwxm64WAhq5tQRwE</p>
<a href="https://github.com/tildeclub/site">page source</a> |
<a href="https://tilde.club/stats" target="_blank">Stats</a> |
<a href="https://tilde.club/news.php">NNTP Stats</a>
</footer>
</body>
</html>

View File

@ -1,24 +0,0 @@
# Geocities-inspired Guestbook
A modern take on the classic Geocities guestbook, built with PHP and SQLite. This guestbook allows for multi-user functionality, user-specific themes, and adheres to current coding standards.
## Features
- **Multi-user Support**: Each user can have their own guestbook by simply accessing [https://tilde.club/guestbook/?user=username](https://tilde.club/guestbook/?user=username).
- **Custom Themes**: Users can specify their own CSS theme by placing a `.css` file in their directory and specifying it with the `theme` parameter in the URL.
- **Referrer Validation**: The guestbook checks the referrer to ensure that entries are being made from the correct user's page.
- **SQLite Backend**: Uses SQLite for a lightweight and serverless database solution.
## Customization
### Themes
Users can specify their own theme by placing a `.css` file in their directory. This theme can be applied by adding the `theme` parameter to the URL, e.g., [https://tilde.club/guestbook/?user=username&theme=cssname](https://tilde.club/guestbook/?user=username&theme=cssname).
A default `dark.css` theme is provided in the repository as an example.
### Adding Entries
User need to link to the guestbook from their tilde page to https://tilde.club/guestbook/?user=username or
https://tilde.club/guestbook/?user=username&theme=themecssname and then your viewers can
simply fill out the form on your guestbook page and submit.

View File

@ -1,30 +0,0 @@
/* Default styles */
body {
background-color: #FFF8DC;
font-family: "Comic Sans MS";
font-size: 16px;
padding: 20px;
}
.entry {
background-color: #f7f7f7;
padding: 15px;
margin-bottom: 20px;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.entry-name {
font-weight: bold;
font-size: 1.1em;
}
.entry-email {
font-style: italic;
color: #888;
}
.entry-message {
margin-top: 10px;
font-size: 1.1em;
}

View File

@ -1,83 +0,0 @@
/* Reset some default styles */
body, h1, p, form {
margin: 0;
padding: 0;
}
body {
font-family: 'Arial', sans-serif;
background-color: #2c3e50;
color: #ecf0f1;
padding: 20px;
}
h1, h2 {
font-size: 2em;
margin-bottom: 20px;
text-align: center;
}
p {
margin-bottom: 20px;
line-height: 1.5;
}
form {
background-color: #34495e;
padding: 20px;
border-radius: 5px;
margin-bottom: 20px;
}
input[type="text"], textarea {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #7f8c8d;
border-radius: 5px;
background-color: #ecf0f1;
color: #2c3e50;
font-size: 1em;
}
input[type="submit"] {
background-color: #e74c3c;
color: #ecf0f1;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s;
font-size: 1em;
}
input[type="submit"]:hover {
background-color: #c0392b;
}
/* Styles for the guestbook entries */
.entry {
background-color: #34495e;
padding: 15px;
margin-bottom: 20px;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.entry-name {
font-weight: bold;
font-size: 1.1em;
color: #e74c3c;
}
.entry-email {
font-style: italic;
color: #95a5a6;
margin-left: 10px;
}
.entry-message {
margin-top: 10px;
font-size: 1.1em;
color: #ecf0f1;
}

View File

@ -1,91 +0,0 @@
<?php
declare(strict_types=1);
// Get the username and theme from the URL
$username = $_GET['user'] ?? 'default';
$theme = $_GET['theme'] ?? 'default';
// Check if the referrer matches the expected pattern for the user
$referrer = $_SERVER['HTTP_REFERER'] ?? '';
$expectedPattern = "/https?:\/\/tilde\.club\/~" . preg_quote($username, '/') . "\//";
if (!preg_match($expectedPattern, $referrer)) {
die("Access denied: Invalid referrer.");
}
try {
$db = new PDO('sqlite:./guestbook.db');
// Set error mode to exceptions
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Check if the guestbook table exists
$tableCheck = $db->query("SELECT name FROM sqlite_master WHERE type='table' AND name='guestbook'")->fetch();
// If the table doesn't exist, create it
if (!$tableCheck) {
$query = "CREATE TABLE guestbook (id INTEGER PRIMARY KEY, username TEXT, name TEXT, email TEXT, message TEXT)";
$db->exec($query);
}
$username = filter_var($username, FILTER_SANITIZE_STRING);
if (isset($_POST['name'], $_POST['email'], $_POST['message'])) {
$name = filter_var($_POST['name'], FILTER_SANITIZE_STRING);
$email = filter_var($_POST['email'], FILTER_SANITIZE_STRING);
$message = filter_var($_POST['message'], FILTER_SANITIZE_STRING);
$stmt = $db->prepare("INSERT INTO guestbook (username, name, email, message) VALUES (:username, :name, :email, :message)");
$stmt->execute([':username' => $username, ':name' => $name, ':email' => $email, ':message' => $message]);
}
$stmt = $db->prepare("SELECT * FROM guestbook WHERE username = :username ORDER BY id DESC");
$stmt->execute([':username' => $username]);
$entries = $stmt->fetchAll();
} catch (PDOException $e) {
die("Error: " . $e->getMessage());
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome to <?= htmlspecialchars($username) ?>'s Guestbook!</title>
<?php
if ($theme !== 'default') {
echo '<link rel="stylesheet" href="' . ($_SERVER['HTTPS'] ? 'https' : 'http') . '://tilde.club/~' . htmlspecialchars($username) . '/' . htmlspecialchars($theme) . '.css">';
} else {
// Default theme
echo '<link rel="stylesheet" href="https://tilde.club/guestbook/default.css">';
}
?>
</head>
<body>
<h1 align="center">Welcome to <?= htmlspecialchars($username) ?>'s Guestbook!</h1>
<p>Please leave a message below to let us know what you think of our page.</p>
<form action="index.php?user=<?= htmlspecialchars($username) ?>&theme=<?= htmlspecialchars($theme) ?>" method="post">
<p>Name: <input type="text" name="name" size="30"></p>
<p>Email: <input type="text" name="email" size="30"></p>
<p>Message:</p>
<p><textarea name="message" rows="6" cols="50"></textarea></p>
<p><input type="submit" value="Submit"></p>
</form>
<h2>Guestbook Entries</h2>
<div class="entries">
<?php
if ($entries) {
foreach ($entries as $entry) {
echo '<div class="entry">';
echo '<h3>' . htmlspecialchars($entry['name']) . ' <span>(' . htmlspecialchars($entry['email']) . ')</span></h3>';
echo '<p>' . htmlspecialchars($entry['message']) . '</p>';
echo '</div>';
}
} else {
echo '<p>No guestbook entries were found for ' . htmlspecialchars($username) . '.</p>';
}
?>
</div>
</body>
</html>

View File

@ -1,12 +1,22 @@
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?=isset($title) ? $title : "Welcome to ~tilde.club~"?></title>
<link rel="stylesheet" href="/style.css">
</head>
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?=isset($title) ? $title : "Welcome to ~tilde.club~"?></title>
<link rel="icon" type="image/png" href="https://tilde.club/favicon.png">
<!-- Preload CSS and image resources -->
<link rel="preload" href="/style.css" as="style">
<link rel="preload" href="/images/rss.png" as="image">
<!-- Load the CSS file -->
<link rel="stylesheet" href="/style.css">
</head>
<body>
<?php include "nav.html"; ?>
<div class="content">
<div class="content">
<!-- RSS Icon -->
<a href="/changes.rss" class="rss-icon" title="RSS Feed">
<img src="/images/rss.png" width="24" alt="RSS icon">
</a>

View File

@ -12,7 +12,8 @@
<a href="/">~TILDE.CLUB~</a>
<a href="/">HOME</a>
<a href="/wiki/">WIKI</a>
<a href="https://tilde.club/wiki/donate.html">DONATE</a>
<a href="https://tildeforge.dev" target="_blank">TILDEFORGE</a>
<a href="https://donate.tilde.club">DONATE</a>
<a href="https://web.newnet.net/?join=club" target="_blank">CHAT</a>
<a href="/signup/">SIGN-UP</a>
</div>

BIN
images/rss.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

387
index.php
View File

@ -1,18 +1,136 @@
<?php include "header.php"; ?>
<?php
include "header.php";
<h1 id="fancyboi">welcome to tilde.club</h1>
// Display notice message based on query parameters
if (isset($_GET['notice'])) {
$notice = htmlspecialchars($_GET['notice']);
if ($notice == 'cancel') {
echo '<div class="notice-message">Your transaction was canceled.</div>';
} elseif ($notice == 'thanks') {
echo '<div class="notice-message">Thank you for helping this great service continue to exist!</div>';
}
}
?>
<h1 id="fancyboi">welcome to Tilde.club</h1>
<p><a href="/wiki/faq.html">Questions? See the official FAQ.</a></p>
<p class="advisory">SERVICE ADVISORY: Users using RSA SSH keys experiencing troubles connecting after Fedora 36 upgrade, you may need to update your client or switch to non-RSA keys.</p>
<!-- <p class="advisory">GMAIL USERS: We no longer accept gmail.com addresses for signups since you would not receive your account information email.</p> -->
<!-- Active Users Scrolling List -->
<div id="active-users">
<h2>Currently Active Users:</h2>
<?php
$activeUsers = json_decode(file_get_contents('/usr/share/nginx/html/online-users.json'), true);
if (!empty($activeUsers))
{
echo '<marquee scrollamount="2">';
foreach ($activeUsers as $user)
{
$username = htmlspecialchars($user);
echo "<a href='/~$username'>$username</a> &nbsp;";
}
echo '</marquee>';
}
else
{
echo "<li>No active users at the moment.</li>";
}
?>
</div>
<div class="grid">
<div class="row">
<div class="col">
<div id="news" class="col">
<?php
$news = json_decode(file_get_contents('news.json'), true);
// Sort news items by date, most recent first
usort($news, function ($a, $b) {
return strtotime($b['date']) - strtotime($a['date']);
});
// Determine the year filter if present
$selectedYear = isset($_GET['year']) ? $_GET['year'] : null;
$filteredNews = [];
$currentDate = new DateTime();
foreach ($news as $newsItem) {
$newsDate = new DateTime($newsItem['date']);
// Only add to filteredNews if the current date is on or after the news date
if ($newsDate <= $currentDate) {
if ($selectedYear) {
// If filtering by year, add items from the selected year only
if (date('Y', strtotime($newsItem['date'])) == $selectedYear) {
$filteredNews[] = $newsItem;
}
} else {
// Otherwise, show the most recent 2 items
$filteredNews[] = $newsItem;
}
}
if (!$selectedYear && count($filteredNews) >= 2) {
break;
}
}
// Display news items
foreach ($filteredNews as $newsItem) {
echo '<div class="news-item">';
echo '<div class="news-item-title">';
echo '<h2>' . htmlspecialchars($newsItem['title']) . ':</h2>';
echo '<h3>' . htmlspecialchars($newsItem['heading']) . '</h3>';
echo '</div>';
echo '<div class="news-item-body">';
echo '<p>' . htmlspecialchars($newsItem['content']) . '</p>';
if (isset($newsItem['details']) && is_array($newsItem['details'])) {
echo '<ul>';
foreach ($newsItem['details'] as $detail) {
echo '<li>' . htmlspecialchars($detail) . '</li>';
}
echo '</ul>';
}
if (isset($newsItem['note'])) {
echo '<p><strong>' . htmlspecialchars($newsItem['note']) . '</strong></p>';
}
if (isset($newsItem['additional_content'])) {
echo '<p>' . htmlspecialchars($newsItem['additional_content']) . '</p>';
}
echo '</div>';
echo '</div>';
echo '<hr>';
}
// Display year links for filtering
$years = array_unique(array_map(function ($newsItem) {
return date('Y', strtotime($newsItem['date']));
}, $news));
sort($years);
echo '<div><strong>View news by year:</strong> ';
foreach ($years as $year) {
echo '<a href="?year=' . $year . '">' . $year . '</a> ';
}
echo '</div>';
?>
</div>
<div class="col">
<p>
tilde.club is not a social network it is one tiny totally
standard unix computer that people respectfully use together
in their shared quest to build awesome web pages
Tilde.club gives you access to a shared Unix computer
where you can make web pages, learn, and share knowledge.
It's a place where working together happens naturally,
all within a friendly and supportive setting.
</p>
<p>
@ -20,152 +138,135 @@
RECENTLY CHANGED PAGES</a> you can see that too
</p>
<p>
Or Check out the <a href="https://tilde.club/~tweska/gallery" target="blank">tilde.club gallery</a> created by <a href="/~tweska" target="_blank">~tweska</a>
Or Check out the <a href="https://tilde.club/~tweska/gallery" target="blank">Tilde.club gallery</a> created by <a href="/~tweska" target="_blank">~tweska</a>
</p>
<hr>
<h2>tilde.club gold star supporters</h2>
<p>Tilde.Club is supported by a global community of
<h2>Tilde.club gold star supporters</h2>
<p>Tilde.club is supported by a global community of
good people. We don't rank people by the amount
they give, only by the fact that they gave.
Here's who has donated! When you're on the
server, THANK THEM.</p>
<ul>
<li>09-27-2022 | <a href="/~tubbo">~tubbo</a></li>
<li>09-08-2022 | <a href="/~cyrus">~cyrus</a></li>
<li>06-08-2022 | <a href="/~barnold">~barnold</a></li>
<li>05-12-2022 | <a href="/~hifikuno">~hifikuno</a></li>
<li>04-27-2022 | <a href="/~mhd">~mhd</a></li>
<li>02-29-2022 | <a href="/~neildaemond">~neildaemond</a></li>
<li>02-15-2022 | <a href="/~alex1138">~alex1138</a></li>
<li>02-15-2022 | <a href="/~amr">~amr</a></li>
<li>01/27/2022 | <a href="/~whitcomb">~whitcomb</a></li>
<li>12/15/2021 | <a href="/~dctrud">~dctrud</a></li>
<li>10/10/2021 | <a href="/~dmi3">~dmi3</a></li>
<li>03/08/2021 | <a href="/~webwiz">~webwiz</a></li>
<li>02/13/2021 | <a href="/~whitcomb">~whitcomb</a></li>
<li>11/30/2020 | <a href="/~dethelor">~dethelor</a></li>
<li>11/26/2020 | <a href="/~waffles">~waffles</a></li>
<li>11/20/2020 | <a href="/~buttstuf">~buttstuf</a></li>
<li>09/03/2020 | <a href="/~northernlights">~northernlights</a></li>
<li>08/16/2020 | <a href="/~dctrud">~dctrud</a></li>
<li>07/15/2020 | <a href="/~necrotechno">~necrotechno</a></li>
<li>07/10/2020 | <a href="/~snowdusk">~snowdusk</a></li>
<li>05/31/2020 | <a href="/~melyanna">~melyanna</a></li>
<li>05/15/2020 | <a href="/~wgreenhouse">~wgreenhouse</a></li>
<li>04/21/2020 | <a href="/~cano">~cano</a></li>
<li>11/09/2019 | <a href="/~sneak">~sneak</a></li>
<li>10/05/2014 | <a href="/~beau">~beau</a></li>
<li>10/05/2014 | <a href="/~skk">~skk</a></li>
<li>10/05/2014 | <a href="/~joeld">~joeld</a></li>
<li>10/05/2014 | <a href="/~john">~john</a></li>
<li>10/05/2014 | <a href="/~brendn">~brendn</a></li>
<li>10/05/2014 | <a href="/~droob">~droob</a></li>
<li>10/05/2014 | <a href="/~delfuego">~delfuego</a></li>
<li>10/05/2014 | <a href="/~jonathan">~jonathan</a></li>
<li>10/05/2014 | <a href="/~coldmode">~coldmode</a></li>
<li>10/05/2014 | <a href="/~jemal">~jemal</a></li>
<li>10/05/2014 | <a href="/~jonbell">~jonbell</a></li>
<li>10/05/2014 | <a href="/~_">~_</a></li>
<li>10/05/2014 | <a href="/~dvd">~dvd</a></li>
<li>10/05/2014 | <a href="/~whitneymcn">~whitneymcn</a></li>
<li>10/05/2014 | <a href="/~jimray">~jimray</a></li>
<li>10/05/2014 | <a href="/~schussat">~schussat</a></li>
<li>10/05/2014 | <a href="/~macdiva">~macdiva</a></li>
<li>10/03/2014 | <a href="/~extraface">~extraface</a></li>
<li>10/03/2014 | <a href="/~joshuag">~joshuag</a></li>
<li>10/03/2014 | <a href="/~zarate">~zarate</a></li>
<li>10/03/2014 | <a href="/~englishm">~englishm</a></li>
<li>10/03/2014 | <a href="/~danbri">~danbri</a></li>
</ul>
</div>
<div class="col">
<h2>UPDATE: September 2022:</h2>
<h3>OS Upgrade to Fedora 36</h3>
<p>
Fedora 36 has been installed and things should be back to normal.
<strong>**NOTE** SSH client requires SHA2 support since SHA1 support is now disabled.</strong>
</p>
<hr>
<h2>UPDATE: November 2021:</h2>
<h3>OS Upgrade to Fedora 35</h3>
<p>
We have upgraded our OS to Fedora 35. All updates installed without error or any issues.
If you encounter any issues please let ben or deepend know.
One notable update that may affect your programs is php is now version 8.
Please check your php scripts to ensure they still work.
</p>
<p>
Webmail has also been upgraded as well and we have enabled the ability for our users to use 2-Factor Authentication
with it. You can find 2-Factor Authentication inside webmail/settings/security
</p>
<hr>
<h2>UPDATE: March 2020:</h2>
<p>
Things at tilde.club are going well, Thank you to all our new and existing users.
Lets make 2020 a great one for ~club and the wider tildeverse!
</p>
<p>so what's new?</p>
<ul>
<li>We have reached 1985 users! and many more signing up daily. Welcome everyone.</li>
<li>Users can now utilize more to make their pages unique, such as PHP.</li>
<li>~club now has a Mastodon page you can follow us at <a href="https://tilde.zone/@tildeclub" target="_blank">https://tilde.zone/@tildeclub</a></li>
<li>Users can now setup Two-Factor Authentication (2FA) to use for SSH logins instead of only public key auth <a href="https://tilde.club/wiki/2fa.html">More Info</a></li>
</ul>
</div>
</div>
<div class="col">
<h3>here are the home pages of our users</h3>
<p>this list does not include people who haven't changed their page yet</p>
<p>if you're not seeing yourself listed here, change your page from the default.</p>
<p><a href="/users/">list all users</a></p>
<ul class="user-list">
<?php
// these are the hashes of previous and current default pages
$page_shas = [
"0eb53dab435e2e6e401921146bed85a80e9ad3a1",
"61eff8202777bae134ac4b11f1e16ec23dfc97d3",
"e9d41eab6edb7cd375c63ecb4a23bca928992547",
"cb2ce535ab34edebc225e88a321f972ba55763c3",
"13af6898f536265af7dbbe2935b591f5e2ee0d7d",
"b0eb2bf442e52b98714456b2f8a6662ba4c1f443",
"0b4f272852e3391e97f0ebb7b5d734a765958eeb",
"59e857ec585d6d34ed21e027164b3c3c36a95f0f",
"9da422e8759799cce29327024fc77b6aa2ace484",
"e5da596bd5f72aa583839bcefd28e988c9d4fcbe",
"adc83b19e793491b1c6ea0fd8b46cd9f32e592fc",
"3f51b25206c137593124a16d8b881079352cd1c4",
"051b45fb2da9b15c523dfafd2a1dd33cc8b54e87",
"5a1cf2f88acb15da43f5d18a13fe638d175a44cc",
"0066d512c24f4ada7ad925fd0856b6b68334a93c",
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
"f032fa07b3f4d9c1264a5a9369bff19f231bff45",
"259969faeba97eb16a675bb66427efd168406c92",
"75b7ce715379312bcc0b24696b9406e10b97cedc",
"e28aa091c26528a4084b8fad2a2ddb72bee3db75",
"12608ff7f6d222d97d2e02c1a13496e1d79713bb",
"22d29b83dc93b925bee08d090b0ea0fffdeb57f2",
"4706d8acdd74efd5c6d8f9e69ec215928d9ff165",
"385212d5ea557a21b77af992ea1b3c1ea71d22c9",
"b51a889545b5f065fd1ac2b8760cab0088a9dc22"
];
foreach (glob("/home/*") as $user) {
$index = "$user/public_html/index.html";
if (!file_exists($index) || in_array(sha1_file($index), $page_shas)) continue;
$user = basename($user); ?>
<li><a href="/~<?=$user?>/">~<?=$user?></a></li>
<?php } ?>
</ul>
$supporters = json_decode(file_get_contents('supporters.json'), true);
usort($supporters, function($a, $b) {
return strtotime($b['date']) - strtotime($a['date']);
});
$showAll = isset($_GET['show_all']) && $_GET['show_all'] == 'true';
$supportersToShow = $showAll ? $supporters : array_slice($supporters, 0, 10);
?>
<ul>
<?php foreach ($supportersToShow as $supporter): ?>
<li><?= htmlspecialchars($supporter['date']) ?> | <a href="<?= htmlspecialchars($supporter['url']) ?>"><?= htmlspecialchars($supporter['name']) ?></a></li>
<?php endforeach; ?>
</ul>
<?php if (!$showAll): ?>
<p><a href="?show_all=true">Show all supporters</a></p>
<?php else: ?>
<p><a href="?show_all=false">Show only the 10 most recent supporters</a></p>
<?php endif; ?>
</div>
</div>
<div class="row">
<div class="col">
<h3>Here are the home pages of our users</h3>
<p>This list does not include people who haven't changed their page yet.</p>
<p>If you're not seeing yourself listed here, change your page from the default.</p>
<p>Users with recently updated pages are highlighted in a lighter color.</p>
<p><a href="/users/">List all users</a></p>
<?php
// these are the hashes of previous and current default pages
$page_shas = [
"0eb53dab435e2e6e401921146bed85a80e9ad3a1",
"61eff8202777bae134ac4b11f1e16ec23dfc97d3",
"e9d41eab6edb7cd375c63ecb4a23bca928992547",
"cb2ce535ab34edebc225e88a321f972ba55763c3",
"13af6898f536265af7dbbe2935b591f5e2ee0d7d",
"b0eb2bf442e52b98714456b2f8a6662ba4c1f443",
"0b4f272852e3391e97f0ebb7b5d734a765958eeb",
"59e857ec585d6d34ed21e027164b3c3c36a95f0f",
"9da422e8759799cce29327024fc77b6aa2ace484",
"e5da596bd5f72aa583839bcefd28e988c9d4fcbe",
"adc83b19e793491b1c6ea0fd8b46cd9f32e592fc",
"3f51b25206c137593124a16d8b881079352cd1c4",
"051b45fb2da9b15c523dfafd2a1dd33cc8b54e87",
"5a1cf2f88acb15da43f5d18a13fe638d175a44cc",
"0066d512c24f4ada7ad925fd0856b6b68334a93c",
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
"f032fa07b3f4d9c1264a5a9369bff19f231bff45",
"259969faeba97eb16a675bb66427efd168406c92",
"75b7ce715379312bcc0b24696b9406e10b97cedc",
"e28aa091c26528a4084b8fad2a2ddb72bee3db75",
"12608ff7f6d222d97d2e02c1a13496e1d79713bb",
"22d29b83dc93b925bee08d090b0ea0fffdeb57f2",
"4706d8acdd74efd5c6d8f9e69ec215928d9ff165",
"385212d5ea557a21b77af992ea1b3c1ea71d22c9",
"b51a889545b5f065fd1ac2b8760cab0088a9dc22"
];
// Retrieve from cache file if available
$cache_file = 'cache/homepages_list.html';
if (file_exists($cache_file) and time() - filemtime($cache_file) < 86400)
{
$homepagesOutput = file_get_contents($cache_file);
}
// Cache not available or expired - create list
else
{
$homepagesOutput = '<div class="user-list">';
$now = time();
foreach (glob("/home/*") as $user)
{
// Look for index files with common extensions
$indexFiles = glob("$user/public_html/index.{html,htm,php}", GLOB_BRACE);
$index = count($indexFiles) > 0 ? $indexFiles[0] : null;
if (!$index || in_array(sha1_file($index), $page_shas)) continue;
// determine the most recently updated file
$age = 0;
foreach ($indexFiles as $file)
{
$access = filemtime($file);
if ($access > $age)
$age = $access;
}
$user = basename($user);
// For simplicity, we use a maximum of 50 months old
$monthsOld = floor(($now - $age) / 2592000);
if ($monthsOld > 50) $monthsOld = 50;
// Set opacity in steps of 5
$opacity = 100 - 2 * $monthsOld;
$opacity = ceil($opacity / 5) * 5;
// Minimum is 15% opacity
if ($opacity < 15) $opacity = 15;
$homepagesOutput .= '<a data-op="'.$opacity.'" href="/~'.$user.'/">'.$user.'</a>';
}
$homepagesOutput .= '</div>';
// Save cache file
$save_cache = file_put_contents($cache_file, $homepagesOutput);
}
echo $homepagesOutput;
?>
</div>
</div>
</div>
<?php include "footer.php"; ?>
<?php include "footer.php";

View File

@ -2,7 +2,8 @@
<a href="/">~TILDE.CLUB~</a>
<a href="/">HOME</a>
<a href="/wiki/">WIKI</a>
<a href="https://tilde.club/wiki/donate.html">DONATE</a>
<a href="https://tildeforge.dev" target="_blank">TILDEFORGE</a>
<a href="https://donate.tilde.club">DONATE</a>
<a href="https://web.newnet.net/?join=club" target="_blank">CHAT</a>
<a href="/signup/">SIGN-UP</a>
</div>

56
news.json Normal file
View File

@ -0,0 +1,56 @@
[
{
"date": "2024-09-30",
"title": "UPDATE: September 2024",
"heading": "Happy 10th Birthday, Tilde.Club!",
"content": "Tilde.Club turned 10! This cozy corner of the internet has become a haven for creativity and community. Members have crafted quirky personal pages, shared knowledge through the wiki, and supported each other in countless projects. It's a space where everyone's unique contributions shine, making it truly special. Here's to a decade of fun and friendship, and many more to come!"
},
{
"date": "2024-08-01",
"title": "UPDATE: August 2024",
"heading": "Hey Everyone, Disk Quotas are Here!",
"content": "Just a heads up: we've rolled out disk quotas to keep things running smoothly for everyone. This will help us share space fairly and make sure the system stays in good shape. Here's the scoop:",
"details": [
"Soft Limit: 1 GB Youll get a nudge if you go over this, but no worries, you can still go up to the hard limit.",
"Hard Limit: 3 GB This is the max. Once you hit this, you wont be able to save more files until you clean up.",
"Grace Period: 1 week If you go over the soft limit, youll have a week to get back under before things get strict."
],
"additional_content": "You can check your usage and see how much space youve got left by running the <code>resources-used</code> script in your home directory. Its easy!"
},
{
"date": "2024-03-01",
"title": "UPDATE: March 2024",
"heading": "Hey everyone, we've leveled up to Fedora 39!",
"content": "Big shoutout to all of you who've been part of this journey with tilde.club. Your contributions, big and small, have really made a difference. We couldn't keep this going without all of you. Fedora 39 is here, and it's packed with cool updates and features. Just a heads-up for those of you working with PHP, there's been an update, so you might want to check your scripts to make sure everything's still running smoothly.",
"details": [
"Looking forward, 2024 is shaping up to be an exciting year, and we're just getting started. We're all about fostering a community that's innovative, supportive, and fun. Together, we're not just keeping tilde.club alive; we're making it thrive.",
"Thanks again to every single one of you. Your creativity, support, and collaboration are what make this community special. Here's to more adventures and achievements together in 2024 and beyond!"
]
},
{
"date": "2022-09-01",
"title": "UPDATE: September 2022",
"heading": "OS Upgrade to Fedora 36",
"content": "Fedora 36 has been installed and things should be back to normal.",
"note": "**NOTE** SSH client requires SHA2 support since SHA1 support is now disabled."
},
{
"date": "2021-11-01",
"title": "UPDATE: November 2021",
"heading": "OS Upgrade to Fedora 35",
"content": "We have upgraded our OS to Fedora 35. All updates installed without error or any issues. If you encounter any issues please let ben or deepend know. One notable update that may affect your programs is php is now version 8. Please check your php scripts to ensure they still work.",
"additional_content": "Webmail has also been upgraded as well and we have enabled the ability for our users to use 2-Factor Authentication with it. You can find 2-Factor Authentication inside webmail/settings/security."
},
{
"date": "2020-03-01",
"title": "UPDATE: March 2020",
"heading": "",
"content": "Things at tilde.club are going well, Thank you to all our new and existing users. Let's make 2020 a great one for ~club and the wider tildeverse!",
"details": [
"We have reached 1985 users! and many more signing up daily. Welcome everyone.",
"Users can now utilize more to make their pages unique, such as PHP.",
"~club now has a Mastodon page you can follow us at https://tilde.zone/@tildeclub.",
"Users can now setup Two-Factor Authentication (2FA) to use for SSH logins instead of only public key auth."
]
}
]

69
news.php Normal file
View File

@ -0,0 +1,69 @@
<?php include "header.php"; ?>
<h1 id="fancyboi">welcome to tilde.club</h1>
<p><a href="/wiki/faq.html">Questions? See the official FAQ.</a></p>
<!-- <p class="advisory">GMAIL USERS: We no longer accept gmail.com addresses for signups since you would not receive your account> -->
<!-- Active Users Scrolling List -->
<div class="active-users-container">
<h2 style="display: inline;">Currently Active Users:</h2>
<div class="active-users-list">
<ul>
<?php
$activeUsers = json_decode(file_get_contents('online-users.json'), true);
if (!empty($activeUsers)) {
foreach ($activeUsers as $user) {
$username = htmlspecialchars($user);
echo "<li><a href='/~$username'>$username</a></li>";
}
// Repeat the list for seamless scrolling
foreach ($activeUsers as $user) {
$username = htmlspecialchars($user);
echo "<li><a href='/~$username'>$username</a></li>";
}
} else {
echo "<li>No active users at the moment.</li>";
}
?>
</ul>
</div>
</div>
<?php
// Load the HTML from inn_status.html
$html = file_get_contents('./news/inn_status.html');
// Use DOMDocument to parse the HTML
$dom = new DOMDocument();
libxml_use_internal_errors(true); // Suppress warnings for malformed HTML
$dom->loadHTML($html);
libxml_clear_errors();
// Extract the content inside the <pre> tag
$preTags = $dom->getElementsByTagName('pre');
$innStatus = '';
if ($preTags->length > 0) {
$innStatus = $preTags->item(0)->nodeValue;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="600">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>INN Status</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="content">
<h1>INN Status</h1>
<pre><?php echo htmlspecialchars($innStatus); ?></pre>
</div>
</body>
</html>
<?php include "footer.php"; ?>

347
polls/admin.php Normal file
View File

@ -0,0 +1,347 @@
<?php
session_start();
require_once 'db.php'; // Ensure the database and $db PDO instance are available
// ------------------------------
// Utility Function: Check if admin is logged in
// ------------------------------
function isAdminLoggedIn()
{
return isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in'] === true;
}
// ------------------------------
// Handle Admin Login
// ------------------------------
if (isset($_POST['login'])) {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
// Prepare a query to fetch the user
$stmt = $db->prepare("SELECT * FROM users WHERE username = :username LIMIT 1");
$stmt->bindValue(':username', $username, PDO::PARAM_STR);
$stmt->execute();
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password'])) {
// Successful login
$_SESSION['admin_logged_in'] = true;
header('Location: admin.php');
exit;
} else {
$error = "Invalid credentials. Please try again.";
}
}
// ------------------------------
// Handle Admin Logout
// ------------------------------
if (isset($_GET['action']) && $_GET['action'] === 'logout') {
session_destroy();
header('Location: admin.php');
exit;
}
// ------------------------------
// Handle Creating a New Poll
// ------------------------------
if (isset($_POST['create_poll']) && isAdminLoggedIn()) {
$questionText = trim($_POST['question_text'] ?? '');
if (!empty($questionText)) {
$stmt = $db->prepare("INSERT INTO poll_questions (question_text) VALUES (:question_text)");
$stmt->bindValue(':question_text', $questionText, PDO::PARAM_STR);
$stmt->execute();
$successMsg = "Poll question created successfully!";
} else {
$errorMsg = "Please enter a question text.";
}
}
// ------------------------------
// Handle Adding Options to an Existing Poll
// ------------------------------
if (isset($_POST['add_option']) && isAdminLoggedIn()) {
$questionId = (int)($_POST['poll_id'] ?? 0);
$optionText = trim($_POST['option_text'] ?? '');
if ($questionId > 0 && !empty($optionText)) {
// Check if poll question exists
$stmt = $db->prepare("SELECT id FROM poll_questions WHERE id = :id");
$stmt->bindValue(':id', $questionId, PDO::PARAM_INT);
$stmt->execute();
if ($stmt->fetchColumn()) {
// Insert the new option
$insertOption = $db->prepare("
INSERT INTO poll_options (question_id, option_text)
VALUES (:question_id, :option_text)
");
$insertOption->bindValue(':question_id', $questionId, PDO::PARAM_INT);
$insertOption->bindValue(':option_text', $optionText, PDO::PARAM_STR);
$insertOption->execute();
// Also initialize poll_results with a 0 vote count for the new option
$optionId = $db->lastInsertId();
$insertResult = $db->prepare("
INSERT INTO poll_results (question_id, option_id, vote_count)
VALUES (:question_id, :option_id, 0)
");
$insertResult->bindValue(':question_id', $questionId, PDO::PARAM_INT);
$insertResult->bindValue(':option_id', $optionId, PDO::PARAM_INT);
$insertResult->execute();
$successMsg = "Option added successfully!";
} else {
$errorMsg = "Poll question does not exist.";
}
} else {
$errorMsg = "Please select a poll and enter an option text.";
}
}
// ------------------------------
// Handle Editing an Existing Poll
// ------------------------------
if (isset($_POST['edit_poll']) && isAdminLoggedIn()) {
$pollId = (int)($_POST['poll_id'] ?? 0);
$newQuestionText = trim($_POST['edit_question_text'] ?? '');
if ($pollId > 0 && !empty($newQuestionText)) {
// Check if poll question exists
$checkStmt = $db->prepare("SELECT id FROM poll_questions WHERE id = :id");
$checkStmt->bindValue(':id', $pollId, PDO::PARAM_INT);
$checkStmt->execute();
if ($checkStmt->fetchColumn()) {
// Update the poll question
$updateStmt = $db->prepare("
UPDATE poll_questions
SET question_text = :question_text
WHERE id = :id
");
$updateStmt->bindValue(':question_text', $newQuestionText, PDO::PARAM_STR);
$updateStmt->bindValue(':id', $pollId, PDO::PARAM_INT);
$updateStmt->execute();
$successMsg = "Poll question updated successfully!";
} else {
$errorMsg = "Poll question does not exist.";
}
} else {
$errorMsg = "Invalid poll ID or question text.";
}
}
// ------------------------------
// Handle Deleting an Existing Poll
// ------------------------------
if (isset($_POST['delete_poll']) && isAdminLoggedIn()) {
$pollId = (int)($_POST['poll_id'] ?? 0);
if ($pollId > 0) {
// Check if poll question exists
$checkStmt = $db->prepare("SELECT id FROM poll_questions WHERE id = :id");
$checkStmt->bindValue(':id', $pollId, PDO::PARAM_INT);
$checkStmt->execute();
if ($checkStmt->fetchColumn()) {
// Delete poll_results
$deleteResults = $db->prepare("DELETE FROM poll_results WHERE question_id = :id");
$deleteResults->bindValue(':id', $pollId, PDO::PARAM_INT);
$deleteResults->execute();
// Delete poll_options
$deleteOptions = $db->prepare("DELETE FROM poll_options WHERE question_id = :id");
$deleteOptions->bindValue(':id', $pollId, PDO::PARAM_INT);
$deleteOptions->execute();
// Finally, delete the poll question
$deletePoll = $db->prepare("DELETE FROM poll_questions WHERE id = :id");
$deletePoll->bindValue(':id', $pollId, PDO::PARAM_INT);
$deletePoll->execute();
$successMsg = "Poll deleted successfully!";
} else {
$errorMsg = "Poll question does not exist.";
}
} else {
$errorMsg = "Invalid poll ID.";
}
}
// ------------------------------
// Fetch All Polls for Display
// ------------------------------
$polls = [];
if (isAdminLoggedIn()) {
$pollsQuery = $db->query("SELECT id, question_text, created_at FROM poll_questions ORDER BY id DESC");
$polls = $pollsQuery->fetchAll(PDO::FETCH_ASSOC);
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Poll Admin</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.login-box, .admin-content { max-width: 600px; margin: 0 auto; }
.error { color: red; }
.success { color: green; }
h2 { border-bottom: 1px solid #ccc; }
form { margin-bottom: 20px; }
label { display: inline-block; width: 100px; }
input[type=text], input[type=password] { width: 200px; }
.poll-item { border: 1px solid #ccc; padding: 10px; margin-bottom: 20px; }
.poll-options { margin-top: 10px; }
.option-result { margin-left: 20px; }
.inline-form { display: inline-block; margin-right: 10px; }
</style>
</head>
<body>
<?php if (!isAdminLoggedIn()): ?>
<div class="login-box">
<h2>Admin Login</h2>
<?php if (!empty($error)): ?>
<div class="error"><?php echo htmlspecialchars($error); ?></div>
<?php endif; ?>
<form method="post" action="admin.php">
<div>
<label for="username">Username:</label>
<input type="text" name="username" id="username" required />
</div>
<div>
<label for="password">Password:</label>
<input type="password" name="password" id="password" required />
</div>
<div>
<button type="submit" name="login">Login</button>
</div>
</form>
</div>
<?php else: ?>
<div class="admin-content">
<h2>Poll Administration</h2>
<p>
<a href="admin.php?action=logout">Logout</a>
</p>
<!-- Display success or error messages -->
<?php if (!empty($successMsg)): ?>
<div class="success"><?php echo htmlspecialchars($successMsg); ?></div>
<?php endif; ?>
<?php if (!empty($errorMsg)): ?>
<div class="error"><?php echo htmlspecialchars($errorMsg); ?></div>
<?php endif; ?>
<!-- Section: Create a New Poll -->
<h3>Create a New Poll</h3>
<form method="post" action="admin.php">
<div>
<label for="question_text">Question:</label>
<input type="text" name="question_text" id="question_text" required>
</div>
<div>
<button type="submit" name="create_poll">Create Poll</button>
</div>
</form>
<!-- Section: Add Options to Existing Poll -->
<h3>Add Options to a Poll</h3>
<?php if (count($polls) > 0): ?>
<form method="post" action="admin.php">
<div>
<label for="poll_id">Select Poll:</label>
<select name="poll_id" id="poll_id">
<?php foreach ($polls as $poll): ?>
<option value="<?php echo $poll['id']; ?>">
<?php echo htmlspecialchars($poll['question_text']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label for="option_text">Option:</label>
<input type="text" name="option_text" id="option_text" required>
</div>
<div>
<button type="submit" name="add_option">Add Option</button>
</div>
</form>
<?php else: ?>
<p>No polls available. Create a new poll first.</p>
<?php endif; ?>
<!-- Section: Existing Polls & Results -->
<h3>Existing Polls & Results</h3>
<?php if (count($polls) > 0): ?>
<?php foreach ($polls as $poll): ?>
<div class="poll-item">
<strong>Question:</strong>
<?php echo htmlspecialchars($poll['question_text']); ?><br>
<em>Created at: <?php echo $poll['created_at']; ?></em>
<!-- Edit and Delete forms for the poll -->
<div style="margin-top: 10px;">
<!-- Edit Form (inline) -->
<form method="post" class="inline-form">
<input type="hidden" name="poll_id" value="<?php echo $poll['id']; ?>">
<input type="text" name="edit_question_text" value="<?php echo htmlspecialchars($poll['question_text']); ?>" style="width:250px;">
<button type="submit" name="edit_poll">Save</button>
</form>
<!-- Delete Form (inline) -->
<form method="post" class="inline-form" onsubmit="return confirm('Are you sure you want to delete this poll?');">
<input type="hidden" name="poll_id" value="<?php echo $poll['id']; ?>">
<button type="submit" name="delete_poll">Delete</button>
</form>
</div>
<!-- Display poll options and vote counts -->
<?php
// Fetch options
$optionsStmt = $db->prepare("
SELECT po.id as option_id, po.option_text,
pr.vote_count
FROM poll_options po
LEFT JOIN poll_results pr
ON po.id = pr.option_id
WHERE po.question_id = :question_id
ORDER BY po.id ASC
");
$optionsStmt->bindValue(':question_id', $poll['id'], PDO::PARAM_INT);
$optionsStmt->execute();
$options = $optionsStmt->fetchAll(PDO::FETCH_ASSOC);
?>
<div class="poll-options">
<?php if (!empty($options)): ?>
<ul>
<?php foreach ($options as $opt): ?>
<li>
<?php echo htmlspecialchars($opt['option_text']); ?>
<span class="option-result">
(Votes: <?php echo $opt['vote_count']; ?>)
</span>
</li>
<?php endforeach; ?>
</ul>
<?php else: ?>
<p>No options for this poll yet.</p>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
<?php else: ?>
<p>No polls to display.</p>
<?php endif; ?>
</div>
<?php endif; ?>
</body>
</html>

161
polls/api.php Normal file
View File

@ -0,0 +1,161 @@
<?php
header('Content-Type: application/json');
require_once 'db.php';
// Quick helper to send JSON responses and exit
function sendJson($data, $statusCode = 200) {
http_response_code($statusCode);
echo json_encode($data);
exit;
}
$action = $_GET['action'] ?? ($_POST['action'] ?? null);
switch ($action) {
// --------------------------------------------------
// 1) List all polls (IDs + questions)
// --------------------------------------------------
case 'list_polls':
try {
$stmt = $db->query("SELECT id, question_text FROM poll_questions ORDER BY id DESC");
$polls = $stmt->fetchAll(PDO::FETCH_ASSOC);
sendJson(['success' => true, 'polls' => $polls]);
} catch (Exception $e) {
sendJson(['success' => false, 'error' => $e->getMessage()], 500);
}
break;
// --------------------------------------------------
// 2) Get a single poll (question + options)
// --------------------------------------------------
case 'get_poll':
$pollId = (int)($_GET['poll_id'] ?? 0);
if ($pollId <= 0) {
sendJson(['success' => false, 'error' => 'Invalid poll_id'], 400);
}
try {
// Fetch poll question
$stmt = $db->prepare("SELECT id, question_text FROM poll_questions WHERE id = :id");
$stmt->execute([':id' => $pollId]);
$poll = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$poll) {
sendJson(['success' => false, 'error' => 'Poll not found'], 404);
}
// Fetch options
$optionsStmt = $db->prepare("
SELECT po.id AS option_id, po.option_text,
IFNULL(pr.vote_count, 0) AS vote_count
FROM poll_options po
LEFT JOIN poll_results pr ON po.id = pr.option_id
WHERE po.question_id = :question_id
ORDER BY po.id ASC
");
$optionsStmt->execute([':question_id' => $pollId]);
$options = $optionsStmt->fetchAll(PDO::FETCH_ASSOC);
sendJson([
'success' => true,
'poll' => [
'id' => $poll['id'],
'question_text' => $poll['question_text'],
'options' => $options
]
]);
} catch (Exception $e) {
sendJson(['success' => false, 'error' => $e->getMessage()], 500);
}
break;
// --------------------------------------------------
// 3) Cast a vote
// Expects: poll_id, option_id, username
// --------------------------------------------------
case 'vote':
// This can come from POST or GET. We'll assume POST for clarity.
$pollId = (int)($_POST['poll_id'] ?? 0);
$optionId = (int)($_POST['option_id'] ?? 0);
$username = trim($_POST['username'] ?? '');
if ($pollId <= 0 || $optionId <= 0 || empty($username)) {
sendJson(['success' => false, 'error' => 'Missing or invalid parameters'], 400);
}
// Check if user already voted on this poll
try {
// 1) Ensure poll & option exist
$checkOption = $db->prepare("
SELECT COUNT(*)
FROM poll_options
WHERE id = :option_id
AND question_id = :poll_id
");
$checkOption->execute([
':option_id' => $optionId,
':poll_id' => $pollId
]);
if (!$checkOption->fetchColumn()) {
sendJson(['success' => false, 'error' => 'Option does not belong to poll or does not exist'], 400);
}
// 2) Check if user already voted
$checkVote = $db->prepare("
SELECT COUNT(*)
FROM user_votes
WHERE question_id = :poll_id
AND user_name = :username
");
$checkVote->execute([
':poll_id' => $pollId,
':username' => $username
]);
if ($checkVote->fetchColumn() > 0) {
// Already voted
sendJson(['success' => false, 'error' => 'Already voted'], 403);
}
// 3) Cast the vote (increment poll_results)
$updateStmt = $db->prepare("
UPDATE poll_results
SET vote_count = vote_count + 1
WHERE question_id = :poll_id
AND option_id = :option_id
");
$updateStmt->execute([
':poll_id' => $pollId,
':option_id' => $optionId
]);
// 4) Record the user vote
// Ensure user_votes table is created:
// CREATE TABLE IF NOT EXISTS user_votes (
// id INTEGER PRIMARY KEY AUTOINCREMENT,
// question_id INTEGER NOT NULL,
// option_id INTEGER NOT NULL,
// user_name TEXT NOT NULL,
// voted_at DATETIME DEFAULT CURRENT_TIMESTAMP
// );
$insertVote = $db->prepare("
INSERT INTO user_votes (question_id, option_id, user_name)
VALUES (:poll_id, :option_id, :username)
");
$insertVote->execute([
':poll_id' => $pollId,
':option_id' => $optionId,
':username' => $username
]);
sendJson(['success' => true, 'message' => 'Vote cast successfully']);
} catch (Exception $e) {
sendJson(['success' => false, 'error' => $e->getMessage()], 500);
}
break;
// --------------------------------------------------
// 4) Unknown / default
// --------------------------------------------------
default:
sendJson(['success' => false, 'error' => 'Unknown action'], 400);
break;
}

90
polls/db.php Normal file
View File

@ -0,0 +1,90 @@
<?php
/**
* db.php
*
* This file checks if the SQLite database file 'poll.db' exists.
* If not, it creates one and sets up the required tables for:
* - Users (including an admin user/password)
* - Poll questions
* - Poll options
* - Poll results
*/
$databaseFile = __DIR__ . '/poll.db';
try {
// If the database file does not exist, create it and set it up
$dbExists = file_exists($databaseFile);
// Initialize the PDO connection
$db = new PDO('sqlite:' . $databaseFile);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// If the DB didn't exist before, create the required tables
if (!$dbExists) {
// Create 'users' table
$db->exec("
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
");
// Create 'poll_questions' table
$db->exec("
CREATE TABLE IF NOT EXISTS poll_questions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
question_text TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
");
// Create 'poll_options' table
$db->exec("
CREATE TABLE IF NOT EXISTS poll_options (
id INTEGER PRIMARY KEY AUTOINCREMENT,
question_id INTEGER NOT NULL,
option_text TEXT NOT NULL,
FOREIGN KEY (question_id) REFERENCES poll_questions(id)
);
");
// Create 'poll_results' table
$db->exec("
CREATE TABLE IF NOT EXISTS poll_results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
question_id INTEGER NOT NULL,
option_id INTEGER NOT NULL,
vote_count INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY (question_id) REFERENCES poll_questions(id),
FOREIGN KEY (option_id) REFERENCES poll_options(id)
);
");
// Create a default admin user with a hashed password
// NOTE: In production, you should not hardcode these credentials.
// Instead, store them outside of your code or set them up once.
$adminUsername = 'admin';
$adminPlainPassword = 'password'; // Change this in production
$adminHashedPassword = password_hash($adminPlainPassword, PASSWORD_DEFAULT);
$insertUser = $db->prepare("
INSERT INTO users (username, password)
VALUES (:username, :password)
");
$insertUser->bindValue(':username', $adminUsername, PDO::PARAM_STR);
$insertUser->bindValue(':password', $adminHashedPassword, PDO::PARAM_STR);
$insertUser->execute();
}
// Optionally, you can return $db or leave it globally accessible
// for other parts of your application.
// Example:
// return $db;
} catch (PDOException $e) {
echo "Database error: " . $e->getMessage();
exit;
}
?>

201
polls/index.php Normal file
View File

@ -0,0 +1,201 @@
<?php
session_start();
require_once 'db.php'; // Ensures $db is available and the database is set up
// Array to store poll IDs that the user has voted on in this session
if (!isset($_SESSION['voted_polls'])) {
$_SESSION['voted_polls'] = [];
}
// Helper function: Fetch poll details by ID
function getPollById($db, $pollId) {
$stmt = $db->prepare("SELECT * FROM poll_questions WHERE id = :id LIMIT 1");
$stmt->bindValue(':id', $pollId, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetch(PDO::FETCH_ASSOC);
}
// Helper function: Fetch poll options (and their results) by poll ID
function getPollOptions($db, $pollId) {
$stmt = $db->prepare("
SELECT
po.id AS option_id,
po.option_text,
IFNULL(pr.vote_count, 0) AS vote_count
FROM poll_options po
LEFT JOIN poll_results pr ON po.id = pr.option_id
WHERE po.question_id = :question_id
ORDER BY po.id ASC
");
$stmt->bindValue(':question_id', $pollId, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// If a vote is being cast
if (isset($_POST['vote']) && isset($_POST['option_id']) && isset($_POST['poll_id'])) {
$pollId = (int)$_POST['poll_id'];
$optionId = (int)$_POST['option_id'];
// Ensure this user hasn't already voted on this poll in this session
if (!in_array($pollId, $_SESSION['voted_polls'], true)) {
// Update the vote count
$updateStmt = $db->prepare("
UPDATE poll_results
SET vote_count = vote_count + 1
WHERE question_id = :question_id
AND option_id = :option_id
");
$updateStmt->bindValue(':question_id', $pollId, PDO::PARAM_INT);
$updateStmt->bindValue(':option_id', $optionId, PDO::PARAM_INT);
$updateStmt->execute();
// Mark the user as having voted
$_SESSION['voted_polls'][] = $pollId;
}
// Redirect back to the same poll to show results
header("Location: index.php?poll_id=" . $pollId);
exit;
}
// Check if a specific poll is requested
$pollId = isset($_GET['poll_id']) ? (int)$_GET['poll_id'] : null;
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Poll Application</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.poll-list, .poll-detail {
max-width: 600px;
margin: 0 auto;
}
h2, h3 { margin-bottom: 10px; }
.poll-item {
margin-bottom: 15px;
padding: 10px;
border: 1px solid #ccc;
}
.poll-options ul { list-style-type: none; padding: 0; }
.poll-options li { margin-bottom: 8px; }
.vote-button { margin-top: 10px; }
.results { margin-top: 10px; }
.results li { margin-bottom: 5px; }
.back-link { margin-top: 20px; display: inline-block; }
</style>
</head>
<body>
<?php
// If no poll_id given, show all polls
if ($pollId === null) {
?>
<div class="poll-list">
<h2>Available Polls</h2>
<?php
// Fetch all polls
$allPollsStmt = $db->query("SELECT id, question_text, created_at FROM poll_questions ORDER BY id DESC");
$allPolls = $allPollsStmt->fetchAll(PDO::FETCH_ASSOC);
if ($allPolls) {
foreach ($allPolls as $poll) {
?>
<div class="poll-item">
<strong>Question:</strong> <?php echo htmlspecialchars($poll['question_text']); ?><br>
<em>Created at: <?php echo htmlspecialchars($poll['created_at']); ?></em><br>
<a href="index.php?poll_id=<?php echo $poll['id']; ?>">View Poll</a>
</div>
<?php
}
} else {
?>
<p>No polls available.</p>
<?php
}
?>
</div>
<?php
} else {
// Display a single poll
$poll = getPollById($db, $pollId);
?>
<div class="poll-detail">
<?php
if (!$poll) {
echo "<p>Poll not found.</p>";
} else {
$options = getPollOptions($db, $pollId);
$hasVoted = in_array($pollId, $_SESSION['voted_polls'], true);
?>
<h2><?php echo htmlspecialchars($poll['question_text']); ?></h2>
<?php
// If not voted yet, show the vote form
if (!$hasVoted) {
if (!empty($options)) {
?>
<form method="post" action="index.php">
<div class="poll-options">
<ul>
<?php foreach ($options as $option) { ?>
<li>
<label>
<input type="radio" name="option_id"
value="<?php echo $option['option_id']; ?>"
required>
<?php echo htmlspecialchars($option['option_text']); ?>
</label>
</li>
<?php } ?>
</ul>
</div>
<input type="hidden" name="poll_id" value="<?php echo $poll['id']; ?>">
<button type="submit" name="vote" class="vote-button">Vote</button>
</form>
<?php
} else {
echo "<p>No options available for this poll.</p>";
}
} else {
// Show the results if user has already voted
?>
<h3>Results</h3>
<div class="results">
<ul>
<?php
$totalVotes = 0;
foreach ($options as $opt) {
$totalVotes += $opt['vote_count'];
}
foreach ($options as $opt) {
$voteCount = (int) $opt['vote_count'];
$percentage = ($totalVotes > 0) ? ($voteCount / $totalVotes) * 100 : 0;
?>
<li>
<?php echo htmlspecialchars($opt['option_text']); ?>:
<?php echo $voteCount; ?> votes
(<?php echo round($percentage, 1); ?>%)
</li>
<?php
}
?>
</ul>
<p>Total votes: <?php echo $totalVotes; ?></p>
</div>
<?php
}
}
?>
<a href="index.php" class="back-link">Back to Poll List</a>
</div>
<?php
}
?>
</body>
</html>

166
polls/setup.php Normal file
View File

@ -0,0 +1,166 @@
<?php
session_start();
// Include the database setup/connection
require_once 'db.php';
// Initialize variables
$error = '';
$success = '';
// Count how many users already exist in the database
$checkTotal = $db->query("SELECT COUNT(*) FROM users")->fetchColumn();
// If at least one user exists, show a message and no form
if ($checkTotal > 0) {
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Setup Admin User</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.container {
max-width: 500px;
margin: 0 auto;
}
.info {
color: #333;
}
a {
color: blue;
text-decoration: underline;
}
</style>
</head>
<body>
<div class="container">
<h2>Admin User Already Exists</h2>
<p class="info">
An admin user has already been created. No additional admins can be set up here.
</p>
<p>
Go back to the <a href="index.php">Polls site</a>.
</p>
</div>
</body>
</html>
<?php
exit;
}
// If we are here, no user exists yet, so show the form
if (isset($_POST['setup'])) {
$username = trim($_POST['username'] ?? '');
$password = trim($_POST['password'] ?? '');
$confirmPassword = trim($_POST['confirm_password'] ?? '');
// Basic validation
if ($username === '' || $password === '' || $confirmPassword === '') {
$error = 'All fields are required.';
} elseif ($password !== $confirmPassword) {
$error = 'Passwords do not match.';
} else {
// Create the first (and only) admin user
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
$insertStmt = $db->prepare("
INSERT INTO users (username, password)
VALUES (:username, :password)
");
$insertStmt->bindValue(':username', $username, PDO::PARAM_STR);
$insertStmt->bindValue(':password', $hashedPassword, PDO::PARAM_STR);
$insertStmt->execute();
$success = "Admin user '$username' created successfully.";
}
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Setup Admin User</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.container {
max-width: 500px;
margin: 0 auto;
}
.error {
color: red;
}
.success {
color: green;
}
label {
display: inline-block;
width: 120px;
}
input[type=text],
input[type=password] {
width: 250px;
margin-bottom: 10px;
}
button {
margin-top: 5px;
}
</style>
</head>
<body>
<div class="container">
<h2>Setup Admin User</h2>
<?php if ($error): ?>
<div class="error"><?php echo htmlspecialchars($error); ?></div>
<?php endif; ?>
<?php if ($success): ?>
<div class="success"><?php echo htmlspecialchars($success); ?></div>
<p>You can now <a href="admin.php">go to the Admin page</a> to log in.</p>
<?php else: ?>
<form action="setup.php" method="post">
<div>
<label for="username">Admin Username:</label>
<input
type="text"
name="username"
id="username"
value="<?php echo isset($_POST['username']) ? htmlspecialchars($_POST['username']) : ''; ?>"
required
/>
</div>
<div>
<label for="password">Password:</label>
<input
type="password"
name="password"
id="password"
required
/>
</div>
<div>
<label for="confirm_password">Confirm Password:</label>
<input
type="password"
name="confirm_password"
id="confirm_password"
required
/>
</div>
<div>
<button type="submit" name="setup">Save Admin User</button>
</div>
</form>
<?php endif; ?>
</div>
</body>
</html>

91
robots.txt Normal file
View File

@ -0,0 +1,91 @@
User-Agent: MojeekBot
Allow: /~xwindows/
User-Agent: Qwantify
Allow: /~xwindows/
User-Agent: Wibybot
Allow: /~xwindows/
User-Agent: search.marginalia.nu
Allow: /~xwindows/
User-Agent: SearchMySiteBot
Allow: /~xwindows/
User-Agent: Duckduckbot
Allow: /~xwindows/
User-Agent: ia_archiver
Allow: /~xwindows/
User-Agent: Googlebot
Allow: /~xwindows/$
Allow: /~xwindows/index.html$
Disallow: /~xwindows/
User-Agent: bingbot
Allow: /~xwindows/$
Allow: /~xwindows/index.html$
Disallow: /~xwindows/
User-Agent: YandexBot
Allow: /~xwindows/$
Allow: /~xwindows/index.html$
Disallow: /~xwindows/
User-Agent: YandexFavicons
Disallow: /~xwindows/
User-Agent: MegaIndex.ru
Disallow: /~xwindows/
User-Agent: Amazonbot
Disallow: /~xwindows/
User-Agent: Linespider
Disallow: /~xwindows/
User-Agent: Bytespider
Disallow: /~xwindows/
User-Agent: CCBot
Disallow: /~xwindows/
User-Agent: Neevabot
Disallow: /~xwindows/
User-Agent: PetalBot
Disallow: /~xwindows/
User-Agent: SemrushBot
Disallow: /~xwindows/
User-Agent: AhrefsBot
Disallow: /~xwindows/
User-Agent: DataForSeoBot
Disallow: /~xwindows/
User-Agent: dotbot
Disallow: /~xwindows/
User-Agent: Barkrowler
Disallow: /~xwindows/
User-Agent: MJ12bot
Disallow: /~xwindows/
User-Agent: BuiltWith
Disallow: /~xwindows/
User-Agent: webprosbot
Disallow: /~xwindows/
User-Agent: Dataprovider
Disallow: /~xwindows/
User-Agent: *
Allow: /~xwindows/$
Allow: /~xwindows/index.html$
Disallow: /~xwindows/

File diff suppressed because it is too large Load Diff

View File

@ -7,11 +7,10 @@
static function NormalizeIP($ipaddr)
{
$ipv4addr = "";
$ipv6addr = "";
// Generate IPv6 address.
$ipaddr = strtolower(trim($ipaddr));
if (strpos($ipaddr, ":") === false) $ipaddr = "::ffff:" . $ipaddr;
if (!str_contains($ipaddr, ":")) $ipaddr = "::ffff:" . $ipaddr;
$ipaddr = explode(":", $ipaddr);
if (count($ipaddr) < 3) $ipaddr = array("", "", "0");
$ipaddr2 = array();
@ -27,7 +26,7 @@
}
}
// Convert ::ffff:123.123.123.123 format.
if (strpos($ipaddr2[count($ipaddr2) - 1], ".") !== false)
if (str_contains($ipaddr2[count($ipaddr2) - 1], "."))
{
$x = count($ipaddr2) - 1;
if ($ipaddr2[count($ipaddr2) - 2] != "ffff") $ipaddr2[$x] = "0";
@ -51,7 +50,7 @@
$ipv6addr = implode(":", $ipaddr);
// Extract IPv4 address.
if (substr($ipv6addr, 0, 30) == "0000:0000:0000:0000:0000:ffff:") $ipv4addr = hexdec(substr($ipv6addr, 30, 2)) . "." . hexdec(substr($ipv6addr, 32, 2)) . "." . hexdec(substr($ipv6addr, 35, 2)) . "." . hexdec(substr($ipv6addr, 37, 2));
if (str_starts_with($ipv6addr, "0000:0000:0000:0000:0000:ffff:")) $ipv4addr = hexdec(substr($ipv6addr, 30, 2)) . "." . hexdec(substr($ipv6addr, 32, 2)) . "." . hexdec(substr($ipv6addr, 35, 2)) . "." . hexdec(substr($ipv6addr, 37, 2));
// Make a short IPv6 address.
$shortipv6 = $ipv6addr;
@ -73,7 +72,7 @@
static function GetRemoteIP($proxies = array())
{
$ipaddr = self::NormalizeIP(isset($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"] : "127.0.0.1");
$ipaddr = self::NormalizeIP($_SERVER["REMOTE_ADDR"] ?? "127.0.0.1");
// Check for trusted proxies. Stop at first untrusted IP in the chain.
if (isset($proxies[$ipaddr["ipv6"]]) || ($ipaddr["ipv4"] != "" && isset($proxies[$ipaddr["ipv4"]])))
@ -85,10 +84,9 @@
{
$found = false;
if (isset($proxies[$ipaddr["ipv6"]])) $header = $proxies[$ipaddr["ipv6"]];
else $header = $proxies[$ipaddr["ipv4"]];
$header = $proxies[$ipaddr["ipv6"]] ?? $proxies[$ipaddr["ipv4"]];
$header = strtolower($header);
$header = strtolower($header);
if ($header == "xforward" && count($xforward) > 0)
{
$ipaddr = self::NormalizeIP(array_pop($xforward));
@ -109,7 +107,7 @@
{
if (is_string($ipaddr)) $ipaddr = self::NormalizeIP($ipaddr);
if (strpos($pattern, ":") !== false)
if (str_contains($pattern, ":"))
{
// Pattern is IPv6.
$pattern = explode(":", strtolower($pattern));
@ -128,7 +126,7 @@
$piece = $piece[0];
if ($piece == "*") $found = true;
else if (strpos($piece, "-") !== false)
else if (str_contains($piece, "-"))
{
$range = explode("-", $piece);
$range[0] = hexdec($range[0]);
@ -148,7 +146,7 @@
$val2 = hexdec(substr($ipaddr[$num], 2, 2));
if ($piece[0] == "*") $found2 = true;
else if (strpos($piece[0], "-") !== false)
else if (str_contains($piece[0], "-"))
{
$range = explode("-", $piece[0]);
if ($range[0] > $range[1]) $range[0] = $range[1];
@ -157,7 +155,7 @@
else if ($piece[0] == $val) $found2 = true;
if ($piece[1] == "*") $found3 = true;
else if (strpos($piece[1], "-") !== false)
else if (str_contains($piece[1], "-"))
{
$range = explode("-", $piece[1]);
if ($range[0] > $range[1]) $range[0] = $range[1];
@ -189,7 +187,7 @@
$piece = trim($piece);
if ($piece == "*") $found = true;
else if (strpos($piece, "-") !== false)
else if (str_contains($piece, "-"))
{
$range = explode("-", $piece);
if ($range[0] > $range[1]) $range[0] = $range[1];
@ -207,4 +205,3 @@
return true;
}
}
?>

View File

@ -14,16 +14,14 @@
// Reduce dependencies. Duplicates code though.
private static function FilenameSafe($filename)
{
return preg_replace('/[_]+/', "_", preg_replace('/[^A-Za-z0-9_.\-]/', "_", $filename));
return preg_replace('/_+/', "_", preg_replace('/[^A-Za-z0-9_.\-]/', "_", $filename));
}
private static function ReplaceNewlines($replacewith, $data)
{
$data = str_replace("\r\n", "\n", $data);
$data = str_replace("\r", "\n", $data);
$data = str_replace("\n", $replacewith, $data);
return $data;
return str_replace("\n", $replacewith, $data);
}
// RFC1341 is a hacky workaround to allow 8-bit over 7-bit transport.
@ -184,7 +182,7 @@
// Reverse parse out the initial domain/IP address part of the e-mail address.
$domain = "";
$state = "domend";
$cfwsdepth = 0;
while ($email != "" && $state != "")
{
$prevchr = substr($email, -2, 1);
@ -224,7 +222,7 @@
{
$email = trim(substr($email, 0, -1));
$depth--;
if (!$depth && substr($email, -1) != ")") $state = $laststate;
if (!$depth && !str_ends_with($email, ")")) $state = $laststate;
}
else $email = trim(substr($email, 0, -1));
@ -286,7 +284,7 @@
// Forward parse out the local part of the e-mail address.
// Remove CFWS (comments, folding whitespace).
while (substr($email, 0, 1) == "(")
while (str_starts_with($email, "("))
{
while ($email != "")
{
@ -301,14 +299,14 @@
{
$email = trim(substr($email, 1));
$depth--;
if (!$depth && substr($email, 0, 1) != "(") break;
if (!$depth && !str_starts_with($email, "(")) break;
}
}
}
// Process quoted/unquoted string.
$local = "";
if (substr($email, 0, 1) == "\"")
if (str_starts_with($email, "\""))
{
$email = substr($email, 1);
while ($email != "")
@ -338,7 +336,7 @@
else $email = substr($email, 1);
}
if (substr($local, -1) != "\"") $local .= "\"";
if (!str_ends_with($local, "\"")) $local .= "\"";
}
else
{
@ -355,17 +353,17 @@
}
$local = preg_replace('/[.]+/', ".", $local);
if (substr($local, 0, 1) == ".") $local = substr($local, 1);
if (substr($local, -1) == ".") $local = substr($local, 0, -1);
if (str_starts_with($local, ".")) $local = substr($local, 1);
if (str_ends_with($local, ".")) $local = substr($local, 0, -1);
}
while (substr($local, -2) == "\\\"") $local = substr($local, 0, -2) . "\"";
while (str_ends_with($local, "\\\"")) $local = substr($local, 0, -2) . "\"";
if ($local == "\"" || $local == "\"\"") $local = "";
// Analyze the domain/IP part and fix any issues.
$domain = preg_replace('/[.]+/', ".", $domain);
if (substr($domain, -1) == "]")
if (str_ends_with($domain, "]"))
{
if (substr($domain, 0, 1) != "[") $domain = "[" . $domain;
if (!str_starts_with($domain, "[")) $domain = "[" . $domain;
// Process the IP address.
if (strtolower(substr($domain, 0, 6)) == "[ipv6:") $ipaddr = IPAddr::NormalizeIP(substr($domain, 6, -1));
@ -377,13 +375,13 @@
else
{
// Process the domain.
if (substr($domain, 0, 1) == ".") $domain = substr($domain, 1);
if (substr($domain, -1) == ".") $domain = substr($domain, 0, -1);
if (str_starts_with($domain, ".")) $domain = substr($domain, 1);
if (str_ends_with($domain, ".")) $domain = substr($domain, 0, -1);
$domain = explode(".", $domain);
foreach ($domain as $num => $part)
{
if (substr($part, 0, 1) == "-") $part = substr($part, 1);
if (substr($part, -1) == "-") $part = substr($part, 0, -1);
if (str_starts_with($part, "-")) $part = substr($part, 1);
if (str_ends_with($part, "-")) $part = substr($part, 0, -1);
if (strlen($part) > 63) $part = substr($part, 0, 63);
$domain[$num] = $part;
@ -401,13 +399,13 @@
if ($y > 64 || $y2 > 253 || $y + $y2 + 1 > 253) return array("success" => false, "error" => self::SMTP_Translate("E-mail address is too long."), "errorcode" => "email_too_long", "info" => $email);
// Process results.
if (substr($domain, 0, 1) == "[" && substr($domain, -1) == "]") $result = array("success" => true, "email" => $email, "lookup" => false, "type" => "IP");
if (str_starts_with($domain, "[") && str_ends_with($domain, "]")) $result = array("success" => true, "email" => $email, "lookup" => false, "type" => "IP");
else if (isset($options["usedns"]) && $options["usedns"] === false) $result = array("success" => true, "email" => $email, "lookup" => false, "type" => "Domain");
else if ((!isset($options["usednsttlcache"]) || $options["usednsttlcache"] === true) && isset(self::$dnsttlcache[$domain]) && self::$dnsttlcache[$domain] >= time()) $result = array("success" => true, "email" => $email, "lookup" => false, "type" => "CachedDNS");
else
{
// Check for a mail server based on a DNS lookup.
$result = self::GetDNSRecord($domain, array("MX", "A"), (isset($options["nameservers"]) ? $options["nameservers"] : array("8.8.8.8", "8.8.4.4")), (!isset($options["usednsttlcache"]) || $options["usednsttlcache"] === true));
$result = self::GetDNSRecord($domain, array("MX", "A"), ($options["nameservers"] ?? array("8.8.8.8", "8.8.4.4")), (!isset($options["usednsttlcache"]) || $options["usednsttlcache"] === true));
if ($result["success"]) $result = array("success" => true, "email" => $email, "lookup" => true, "type" => $result["type"], "records" => $result["records"]);
}
@ -480,7 +478,6 @@
$name = "";
$email = "";
$state = "addrend";
$cfwsdepth = 0;
$inbracket = false;
while ($data != "" && $state != "")
@ -538,7 +535,7 @@
{
$data = trim(substr($data, 0, -1));
$depth--;
if (!$depth && substr($data, -1) != ")") $state = $laststate;
if (!$depth && !str_ends_with($data, ")")) $state = $laststate;
}
else $data = trim(substr($data, 0, -1));
@ -616,18 +613,18 @@
{
if ($prevchr == "\\")
{
$email .= $lastchar . $prevchr;
$email .= $lastchr . $prevchr;
$data = substr($data, 0, -2);
}
else if ($lastchr == "\"")
{
$email .= $lastchar;
$email .= $lastchr;
$data = trim(substr($data, 0, -1));
$state = "localstart";
}
else
{
$email .= $lastchar;
$email .= $lastchr;
$data = substr($data, 0, -1);
}
@ -683,7 +680,7 @@
{
if ($prevchr == "\\")
{
$name .= $lastchar . $prevchr;
$name .= $lastchr . $prevchr;
$data = substr($data, 0, -2);
}
else if ($lastchr == "\"")
@ -718,7 +715,7 @@
{
if ($removenames) $name = "";
$name = trim(strrev($name));
if (substr($name, 0, 1) == "\"") $name = trim(substr($name, 1));
if (str_starts_with($name, "\"")) $name = trim(substr($name, 1));
$name = str_replace("\\\\", "\\", $name);
$name = str_replace("\\\"", "\"", $name);
@ -816,11 +813,11 @@
// Reads one or more lines in.
private static function ProcessState__ReadLine(&$state)
{
while (strpos($state["data"], "\n") === false)
while (!str_contains($state["data"], "\n"))
{
$data2 = @fgets($state["fp"], 116000);
if ($data2 === false) return array("success" => false, "error" => self::SMTP_Translate("Underlying stream encountered a read error."), "errorcode" => "stream_read_error");
if (strpos($data2, "\n") === false)
if (!str_contains($data2, "\n"))
{
if (feof($state["fp"])) return array("success" => false, "error" => self::SMTP_Translate("Remote peer disconnected."), "errorcode" => "peer_disconnected");
if (self::StreamTimedOut($state["fp"])) return array("success" => false, "error" => self::SMTP_Translate("Underlying stream timed out."), "errorcode" => "stream_timeout_exceeded");
@ -851,7 +848,7 @@
if ($state["timeout"] !== false && self::GetTimeLeft($state["startts"], $state["timeout"]) == 0) return array("success" => false, "error" => self::SMTP_Translate("HTTP timeout exceeded."), "errorcode" => "timeout_exceeded");
$data2 = substr($state["data"], 0, $result);
$state["data"] = (string)substr($state["data"], $result);
$state["data"] = substr($state["data"], $result);
$state["result"]["rawsendsize"] += $result;
@ -993,9 +990,9 @@
case "helo_ehlo":
{
// Send EHLO or HELO depending on server support.
$hostname = (isset($state["options"]["hostname"]) ? $state["options"]["hostname"] : "[" . trim(isset($_SERVER["SERVER_ADDR"]) && $_SERVER["SERVER_ADDR"] != "127.0.0.1" ? $_SERVER["SERVER_ADDR"] : "192.168.0.101") . "]");
$hostname = ($state["options"]["hostname"] ?? "[" . trim(isset($_SERVER["SERVER_ADDR"]) && $_SERVER["SERVER_ADDR"] != "127.0.0.1" ? $_SERVER["SERVER_ADDR"] : "192.168.0.101") . "]");
$state["size_supported"] = 0;
if (strpos($state["response"], " ESMTP") !== false)
if (str_contains($state["response"], " ESMTP"))
{
self::InitSMTPRequest($state, "EHLO " . $hostname, 250, "esmtp_extensions", self::SMTP_Translate("Expected a 250 response from the SMTP server upon EHLO."));
}
@ -1021,7 +1018,7 @@
$state["state"] = "mail_from";
// Process login (if any and supported).
if (strpos($auth, "LOGIN") !== false)
if (str_contains($auth, "LOGIN"))
{
$state["username"] = (isset($state["options"]["username"]) ? (string)$state["options"]["username"] : "");
$state["password"] = (isset($state["options"]["password"]) ? (string)$state["options"]["password"] : "");
@ -1132,7 +1129,7 @@
public static function SendSMTPEmail($toaddr, $fromaddr, $message, $options = array())
{
$startts = microtime(true);
$timeout = (isset($options["timeout"]) ? $options["timeout"] : false);
$timeout = ($options["timeout"] ?? false);
if (!function_exists("stream_socket_client") && !function_exists("fsockopen")) return array("success" => false, "error" => self::SMTP_Translate("The functions 'stream_socket_client' and 'fsockopen' do not exist."), "errorcode" => "function_check");
@ -1143,11 +1140,11 @@
if (!self::EmailAddressesToNamesAndEmail($temptonames, $temptoaddrs, $toaddr, true, $options)) return array("success" => false, "error" => self::SMTP_Translate("Invalid 'To' e-mail address(es)."), "errorcode" => "invalid_to_address", "info" => $toaddr);
if (!self::EmailAddressesToNamesAndEmail($tempfromnames, $tempfromaddrs, $fromaddr, true, $options)) return array("success" => false, "error" => self::SMTP_Translate("Invalid 'From' e-mail address."), "errorcode" => "invalid_from_address", "info" => $fromaddr);
$server = (isset($options["server"]) ? $options["server"] : "localhost");
$secure = (isset($options["secure"]) ? $options["secure"] : false);
$server = ($options["server"] ?? "localhost");
$secure = ($options["secure"] ?? false);
$port = (isset($options["port"]) ? (int)$options["port"] : -1);
if ($port < 0 || $port > 65535) $port = ($secure ? 465 : 25);
$debug = (isset($options["debug"]) ? $options["debug"] : false);
$debug = ($options["debug"] ?? false);
$headers = "Message-ID: <" . self::SMTP_RandomHexString(8) . "." . self::SMTP_RandomHexString(7) . "@" . substr($tempfromaddrs[0], strrpos($tempfromaddrs[0], "@") + 1) . ">\r\n";
$headers .= "Date: " . date("D, d M Y H:i:s O") . "\r\n";
@ -1168,7 +1165,7 @@
if ($timeout !== false && self::GetTimeLeft($startts, $timeout) == 0) return array("success" => false, "error" => self::SMTP_Translate("HTTP timeout exceeded."), "errorcode" => "timeout_exceeded");
// Connect to the target server.
$hostname = (isset($options["hostname"]) ? $options["hostname"] : "[" . trim(isset($_SERVER["SERVER_ADDR"]) && $_SERVER["SERVER_ADDR"] != "127.0.0.1" ? $_SERVER["SERVER_ADDR"] : "192.168.0.101") . "]");
$hostname = ($options["hostname"] ?? "[" . trim(isset($_SERVER["SERVER_ADDR"]) && $_SERVER["SERVER_ADDR"] != "127.0.0.1" ? $_SERVER["SERVER_ADDR"] : "192.168.0.101") . "]");
$errornum = 0;
$errorstr = "";
if (isset($options["fp"]) && is_resource($options["fp"]))
@ -1202,7 +1199,7 @@
// Initialize the connection request state array.
$state = array(
"fp" => $fp,
"async" => (isset($options["async"]) ? $options["async"] : false),
"async" => ($options["async"] ?? false),
"debug" => $debug,
"startts" => $startts,
"timeout" => $timeout,
@ -1305,7 +1302,7 @@
if (TagFilter::GetParentPos($stack, "pre") === false)
{
$content = preg_replace('/\s{2,}/', " ", str_replace(array("\r\n", "\n", "\r", "\t"), " ", $content));
if ($result !== "" && substr($result, -1) === "\n") $content = trim($content);
if ($result !== "" && str_ends_with($result, "\n")) $content = trim($content);
}
}
@ -1352,13 +1349,13 @@
$subject = str_replace("\n", " ", $subject);
if (!UTF8::IsASCII($subject)) $subject = self::ConvertToRFC1342($subject);
$replytoaddr = (isset($options["replytoaddr"]) ? $options["replytoaddr"] : "");
$ccaddr = (isset($options["ccaddr"]) ? $options["ccaddr"] : "");
$bccaddr = (isset($options["bccaddr"]) ? $options["bccaddr"] : "");
$headers = (isset($options["headers"]) ? $options["headers"] : "");
$textmessage = (isset($options["textmessage"]) ? $options["textmessage"] : "");
$htmlmessage = (isset($options["htmlmessage"]) ? $options["htmlmessage"] : "");
$attachments = (isset($options["attachments"]) ? $options["attachments"] : array());
$replytoaddr = ($options["replytoaddr"] ?? "");
$ccaddr = ($options["ccaddr"] ?? "");
$bccaddr = ($options["bccaddr"] ?? "");
$headers = ($options["headers"] ?? "");
$textmessage = ($options["textmessage"] ?? "");
$htmlmessage = ($options["htmlmessage"] ?? "");
$attachments = ($options["attachments"] ?? array());
$messagetoaddr = self::EmailAddressesToEmailHeaders($toaddr, "To", true, false, $options);
$replytoaddr = self::EmailAddressesToEmailHeaders($replytoaddr, "Reply-To", false, false, $options);
@ -1373,8 +1370,7 @@
if ($htmlmessage == "" && !count($attachments))
{
// Plain-text e-mail.
$destheaders = "";
$destheaders .= $messagefromaddr;
$destheaders = $messagefromaddr;
if ($headers != "") $destheaders .= $headers;
$destheaders .= "MIME-Version: 1.0\r\n";
if (!isset($options["usemail"]) || !$options["usemail"]) $destheaders .= $messagetoaddr;
@ -1391,8 +1387,7 @@
{
// MIME e-mail (HTML, text, attachments).
$mimeboundary = "--------" . self::MIME_RandomString(25);
$destheaders = "";
$destheaders .= $messagefromaddr;
$destheaders = $messagefromaddr;
if ($headers != "") $destheaders .= $headers;
$destheaders .= "MIME-Version: 1.0\r\n";
if (!isset($options["usemail"]) || !$options["usemail"]) $destheaders .= $messagetoaddr;
@ -1516,4 +1511,3 @@
}
}
}
?>

View File

@ -5,25 +5,34 @@ include __DIR__."/../header.php";
function esc($v) {
return isset($_REQUEST[$v]) ? htmlspecialchars($_REQUEST[$v]) : "";
}
function isGmail($email) {
return substr(strrchr($email, "@"), 1) === 'gmail.com';
}
$gmailError = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isGmail($_REQUEST['email'])) {
$gmailError = true;
} else {
// Process the form here only if the email is not a Gmail address
include 'signup-handler.php';
}
}
?>
<?php if ($gmailError): ?>
<div class="alert alert-warning">
<strong>We don't accept Gmail addresses due to Google not allowing emails to go through due to a recent spam issue.</> </div>
<?php endif; ?>
<h1 id="fancyboi">sign up to join tilde.club</h1>
<div class="grid">
<div class="row">
<div class="col">
<p>we're excited you're here! let's get you signed up!</p>
<p>fill out this form and we'll get back to you with account info</p>
<table>
<tr>
<td>
<form method="post">
<?php include 'signup-handler.php'; ?>
<div class="grid">
<div class="row">
<div class="col">
<p>We're excited you're here! Let's get you signed up!</p>
<p>Fill out this form and we'll get back to you with account info</p>
<form method="post">
<div>
<p>your desired username (numbers and lowercase letters only, no spaces)</p>
<input class="form-control" name="username" value="<?=esc("username")?>" type="text" required>
@ -50,8 +59,9 @@ function esc($v) {
<br>
no drama. be respectful. have fun. we're all trying, and we're all in this together :)
</p>
<p>you must be at least 13 years or older to sign up and use tilde.club.</p>
<button class="btn btn-primary" type="submit">submit</button>
<button class="btn btn-primary" type="submit">Submit</button>
</form>

View File

@ -1,20 +1,59 @@
<?php
$filepath = __FILE__;
# require __DIR__.'/../vendor/autoload.php';
require_once "email/smtp.php";
function forbidden_name($name) {
return in_array($name, [
function getUserIpAddr() {
if(!empty($_SERVER['HTTP_CLIENT_IP'])) {
//ip from share internet
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
//ip pass from proxy
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
return $ip;
}
function add_ban_info($name, $email): void
{
$user_ip = getUserIpAddr();
$user_info = "$name - $email - $user_ip";
file_put_contents("/var/signups_banned", $user_info.PHP_EOL, FILE_APPEND);
}
function is_ssh_pubkey($string): bool
{
// list from sshd(8)
$valid_pubkeys = [
'sk-ecdsa-sha2-nistp256@openssh.com',
'ecdsa-sha2-nistp256',
'ecdsa-sha2-nistp384',
'ecdsa-sha2-nistp521',
'sk-ssh-ed25519@openssh.com',
'ssh-ed25519',
'ssh-dss',
'ssh-rsa',
];
foreach ($valid_pubkeys as $pub)
if (str_starts_with($string, $pub)) return true;
return false;
}
function forbidden_name($name): bool
{
$badnames = [
'0x0',
'abuse',
'adam',
'admin',
'administrator',
'amcclure',
'anton',
'auth',
'autoconfig',
'bbj',
'broadcasthost',
'chickfilla',
'cloud',
'forum',
'ftp',
@ -30,7 +69,6 @@ function forbidden_name($name) {
'localdomain',
'localhost',
'lounge',
'lukewarmcat',
'mail',
'mailer-daemon',
'marketing',
@ -60,32 +98,58 @@ function forbidden_name($name) {
'wpad',
'www',
'znc',
]);
];
return in_array(
$name,
array_merge(
$badnames,
file("/var/signups_current", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES),
file("/var/banned_names.txt", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)
)
);
}
function forbidden_email($email): bool
{
$femail = file("/var/banned_emails.txt", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
return in_array($email, $femail);
}
function forbidden_sshkey($sshkey): bool
{
$fsshkey = file("/var/banned_sshkeys.txt", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$fsk = [];
foreach ($fsshkey as $line) {
$fsk_line = explode(' ',trim($line));
$fsk[] = $fsk_line[1];
}
$sk = explode(' ',trim($sshkey));
return in_array($sk[1], $fsk);
}
$message = "";
if (isset($_REQUEST["username"]) && isset($_REQUEST["email"])) {
// Check the name.
$name = trim($_REQUEST["username"]);
if ($name == "")
$message .= "<li>please fill in your desired username</li>";
$message .= "<li>fill in your desired username</li>\n";
else {
if (strlen($name) < 2)
$message .= "<li>username is too short (2 character min)</li>\n";
if (strlen($name) > 32)
$message .= "<li>username too long (32 character max)</li>";
if (strlen($name) > 32)
$message .= "<li>username too long (32 character max)</li>\n";
if (!preg_match('/^[a-z][a-z0-9]{1,31}$/', $name))
$message .= "<li>username contains invalid characters (lowercase ascii only, must start with a letter)</li>";
if (strlen($name) > 1 && !preg_match('/^[a-z][a-z0-9]{1,31}$/', $name))
$message .= "<li>username contains invalid characters (lowercase only, must start with a letter).</li>\n";
if ($_REQUEST["sshkey"] == "" || mb_substr($_REQUEST["sshkey"], 0, 4) !== "ssh-")
$message .= '<li>ssh key required: please create one and submit the public key. '
. 'see our <a href="https://tilde.club/wiki/ssh.html">ssh wiki</a> or '
. 'hop on <a href="https://web.tilde.chat/?join=club">irc</a> and ask for help</li>';
if ($_REQUEST["interest"] == "")
$message .= "<li>please explain why you're interested so we can make sure you're a real human being</li>";
if (posix_getpwnam($name) || forbidden_name($name))
$message .= "<li>sorry, the username $name is unavailable</li>";
if (posix_getpwnam($name) || forbidden_name($name))
$message .= "<li>sorry, the username $name is unavailable</li>\n";
}
// Check the e-mail address.
$email = trim($_REQUEST["email"]);
@ -97,13 +161,35 @@ if (isset($_REQUEST["username"]) && isset($_REQUEST["email"])) {
$message .= "<li>invalid email address: " . htmlspecialchars($result["error"]) . "</li>";
elseif ($result["email"] != $email)
$message .= "<li>invalid email address. did you mean: " . htmlspecialchars($result["email"]) . "</li>";
elseif ($name != "" && forbidden_email($email)) {
$message .= "<li>your email is banned!</li><br />";
add_ban_info($name, $email);
}
}
if ($_REQUEST["interest"] == "")
$message .= "<li>please explain why you're interested so we can make sure you're a real human being</li>";
$sshkey = trim($_REQUEST["sshkey"]);
if ($sshkey == "" || !is_ssh_pubkey($sshkey))
$message .= '<li>ssh key required: please create one and submit the public key. '
. 'see our <a href="https://tilde.club/wiki/ssh">ssh wiki</a> or '
. 'hop on <a href="https://web.newnet.net/?join=club">irc</a> and ask for help</li>';
else {
if ($name != "" && $email != "") {
if (forbidden_sshkey($sshkey)) {
$message .= "<li>your sshkey is banned!</li>\n";
add_ban_info($name, $email);
}
}
}
// no validation errors
if ($message == "") {
$sshkey = trim($_REQUEST["sshkey"]);
$makeuser = "makeuser {$_REQUEST["username"]} {$_REQUEST["email"]} \"{$sshkey}\"";
if ($message == "") {
$makeuser = "makeuser {$_REQUEST["username"]} {$_REQUEST["email"]} \"$sshkey\"";
$msgbody = "
username: {$_REQUEST["username"]}
email: {$_REQUEST["email"]}
@ -114,23 +200,26 @@ $makeuser
if (mail('root', 'new tilde.club signup', $msgbody)) {
echo '<div class="alert alert-success" role="alert">
email sent! we\'ll get back to you soon (usually within a day) with login instructions! be sure to check your spam folder as we\'ve had several reports of being marked as spam! <a href="/">back to tilde.club home</a>
</div>';
email sent! we\'ll get back to you soon with login instructions! (timeframe for processing signups varies greatly) <a href="/">back to tilde.club home</a>
</div>';
// temp. add to forbidden to prevent double signups (cleanup after user creation)
file_put_contents("/var/signups_current", $name.PHP_EOL, FILE_APPEND);
file_put_contents("/var/signups", $makeuser.PHP_EOL, FILE_APPEND);
// clear form fields
$_REQUEST["email"] = $_REQUEST["username"] = $_REQUEST["sshkey"] = $_REQUEST["interest"] = "";
} else {
echo '<div class="alert alert-danger" role="alert">
something went wrong... please send an email to <a href="mailto:root@tilde.club">root@tilde.club</a> with details of what happened
</div>';
something went wrong... please send an email to <a href="mailto:root@tilde.club">root@tilde.club</a> with details of what happened
</div>';
}
} else {
?>
?>
<div class="alert alert-warning" role="alert">
<strong>please correct the following errors: </strong>
<strong>notice: </strong>
<?=$message?>
</div>
<?php
<?php
}
}
?>

342
style.css
View File

@ -1,12 +1,60 @@
/* {font-size:13pt;font-weight:normal;} */
@font-face {
font-family: 'Liberation Monospace';
font-style: normal;
font-weight: normal;
src: local('Liberation Monospace'), url('https://fonts.cdnfonts.com/s/276/LiberationMono-Regular.woff') format('woff');
}
@media (min-width: 64em) {
.row {
display: flex;
flex-wrap: nowrap;
flex-direction: row-reverse;
@font-face {
font-family: 'Liberation Monospace';
font-style: normal;
font-weight: bold;
src: local('Liberation Monospace'), url('https://fonts.cdnfonts.com/s/276/LiberationMono-Bold.woff') format('woff');
}
body {
margin: auto;
padding: 1em;
max-width: 1024px;
font-family: 'Liberation Monospace', 'Courier New', monospace, sans-serif;
color: #fb5;
background: #111;
word-wrap: break-word;
}
a {
text-decoration: none;
color: #f70;
font-weight: bold;
padding-right: 0.25em;
}
a:hover {
color: #f20;
}
.col { flex-grow: 1; }
hr {
border-color: #fb5;
}
.text-center {
text-align: center
}
.advisory {
background: #fc4;
color: #222;
font-weight: bold;
padding: 1em;
border-radius: 0.25em;
}
h1 {
font-size:1.6rem;
text-transform: uppercase;
letter-spacing:-.75px;
color: #fb5;
padding-top:.5rem;
}
.grid {
@ -15,81 +63,37 @@
.col {
border: 6px double #fb5;
padding: 1em;
}
.user-list {
display: flex;
flex-flow: row wrap;
justify-content: space-evenly;
list-style-type: none;
padding: 0;
}
.user-list li {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 10em;
}
.user-list::after {
content: "";
}
body {
margin: auto;
padding: 1em;
max-width: 64em;
font-family: "courier new", monospace;
color: #fb5;
background: #111;
word-wrap: break-word;
}
a {
text-decoration: none;
color: #f70;
font-weight: bold;
padding-right: 0.25em;
}
a:hover {color: #f20;}
hr {
border-color: #fb5;
}
.text-center {
text-align: center
}
.advisory {
background: #fc4;
color: #222;
font-weight: bold;
padding: 1em;
border-radius: 0.25em;
}
h1 {
text-transform: uppercase;
color: #fb5;
font-family: monospace;
flex-grow: 1;
}
/* THEGOODS */
#fancyboi::before {
content: "$ ";
}
@media (prefers-reduced-motion: no-preference) {
@keyframes flash {
50% { opacity: 0; }
}
@keyframes reveal {
from { width: 2em; } /* Width of ::before */
to { width: 100%; }
from { max-width: 2em; } /* Width of ::before */
to { max-width: 100%; }
}
#fancyboi {
overflow: hidden;
white-space: nowrap;
animation: reveal 4s linear;
text-overflow: "█";
}
#fancyboi::after {
content: "█";
animation: flash 0.5s step-end infinite;
position: relative;
top: -2px;
margin-left: 3px;
}
}
/* SDOOGEHT */
@ -119,7 +123,7 @@ code > span.fl {
background-color: #f93;
}
/* Navbar links */
/* Navbar linkss */
#navbar a {
float: left;
display: block;
@ -129,9 +133,58 @@ code > span.fl {
text-decoration: none;
}
/* RSS Icon Styles */
.rss-icon {
float: right;
margin-top:5px;
}
/* Page content */
.content {
padding-top: 5px;
.user-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(195px, 1fr));
}
.user-list a {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 10em;
}
.user-list a:before {
content:"~";
}
[data-op="5"] { opacity:5%; }
[data-op="10"] { opacity:10%; }
[data-op="15"] { opacity:15%; }
[data-op="20"] { opacity:20%; }
[data-op="25"] { opacity:25%; }
[data-op="30"] { opacity:30%; }
[data-op="35"] { opacity:35%; }
[data-op="40"] { opacity:40%; }
[data-op="45"] { opacity:45%; }
[data-op="50"] { opacity:50%; }
[data-op="55"] { opacity:55%; }
[data-op="60"] { opacity:60%; }
[data-op="65"] { opacity:65%; }
[data-op="70"] { opacity:70%; }
[data-op="75"] { opacity:75%; }
[data-op="80"] { opacity:80%; }
[data-op="85"] { opacity:85%; }
[data-op="90"] { opacity:90%; }
[data-op="95"] { opacity:95%; }
[data-op="100"] { opacity:100%; }
.user-list a:hover {
text-decoration:underline;
color:#f70;
}
.user-list b {
background-color: #fb5;
color:#000;
}
input[type="text"],
@ -147,15 +200,174 @@ div.alert-success {
background-color: darkgreen;
}
.notice-message {
background-color: #f93;
color: #222;
font-weight: bold;
padding: 0.75em;
border-radius: 0.25em;
margin: 1em 0;
text-align: center;
border: 2px solid #fb5;
box-shadow: 0 0 10px rgba(255, 187, 85, 0.5);
}
blockquote {
border-left: 2px solid #fb5;
background-color: rgba(255, 187, 85, 5%);
padding: 0.5em;
}
/* Mobile */
@media (min-width: 768px) {
.row {
display: flex;
flex-wrap: nowrap;
flex-direction: row-reverse;
}
}
@media (max-width: 768px) {
td {
display: block;
}
}
/* Ensure the user-list displays items in a single column */
.single-column {
display: block;
}
.single-column li {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Style for the article content */
.article-headers {
background-color: #333;
color: #fb5;
padding: 1em;
border: 1px solid #fb5;
border-radius: 0.25em;
margin-bottom: 1em;
}
.article-headers p {
margin: 0.5em 0;
}
pre {
background-color: #333;
color: #fb5;
padding: 1em;
border: 1px solid #fb5;
border-radius: 0.25em;
}
/* Style for error messages */
.alert-warning {
background-color: darkred;
color: #fb5;
padding: 1em;
border-radius: 0.25em;
font-weight: bold;
}
/* Ensure the user-list displays items in a single column */
.single-column {
display: block;
}
.single-column li {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Tree structure styles */
.tree, .tree ul {
padding: 0;
list-style: none;
position: relative;
}
.tree ul {
margin-left: 1em; /* indentation for nested lists */
}
.tree ul:before {
content: "";
display: block;
width: 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
border-left: 1px solid #fb5;
}
.tree li {
margin: 0;
padding: 0.5em 0 0.5em 1em; /* space between items */
position: relative;
}
.tree li:before {
content: "";
display: inline-block;
width: 1em; /* horizontal space for the branch line */
height: 0;
border-top: 1px solid #fb5;
position: absolute;
top: 1.2em; /* adjust as needed */
left: 0;
}
.tree li:after {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: -0.5em; /* adjust to align with the branch */
border-left: 1px solid #fb5;
}
.tree li:last-child:after {
display: none; /* hide the line for the last child */
}
/* Style for timestamps */
.timestamp {
color: #999;
font-size: 0.9em;
margin-left: 0.5em;
}
/* Highlighted item */
.highlighted {
background-color: #333; /* Dark background to fit the page colorscheme */
border: 1px solid #fb5; /* Matching border color */
border-radius: 0.25em; /* Slight border radius for better appearance */
padding: 0.5em; /* Padding for better readability */
}
#active-users {
margin-top: 20px;
display: flex;
align-items: center;
gap:10px;
}
#active-users h2 {
flex-shrink:0;
font-size: 1.25rem;
}
#active-users a {
color: #fb5;
text-decoration: none;
}
#active-users a:hover {
color: #f70;
}

72
supporters.json Normal file
View File

@ -0,0 +1,72 @@
[
{"date": "07/20/2025", "name": "mlot", "url": "/~mlot"},
{"date": "07/03/2025", "name": "whitcomb", "url": "/~whitcomb"},
{"date": "05/06/2025", "name": "mlot", "url": "/~mlot"},
{"date": "05/03/2025", "name": "ve3zsh", "url": "/~ve3zsh"},
{"date": "02/26/2025", "name": "djhsu", "url": "/~djhsu"},
{"date": "11/11/2024", "name": "litemotiv", "url": "/~litemotiv"},
{"date": "10/12/2024", "name": "subarctic", "url": "/~subarctic"},
{"date": "09/25/2024", "name": "thumos", "url": "/~thumos"},
{"date": "09/21/2024", "name": "jpurnell", "url": "/~jpurnell"},
{"date": "09/09/2024", "name": "barnold", "url": "/~barnold"},
{"date": "08/29/2024", "name": "crivic", "url": "/~crivic"},
{"date": "08/20/2024", "name": "speedygo55", "url": "/~speedygo55"},
{"date": "05/08/2024", "name": "whitcomb", "url": "/~whitcomb"},
{"date": "03/14/2024", "name": "alcor", "url": "/~alcor"},
{"date": "02/17/2024", "name": "dddaaannn", "url": "/~dddaaannn"},
{"date": "12/19/2023", "name": "passthejoe", "url": "/~passthejoe"},
{"date": "12/16/2023", "name": "lake", "url": "/~lake"},
{"date": "11/18/2023", "name": "serge", "url": "/~serge"},
{"date": "08/17/2023", "name": "amr", "url": "/~amr"},
{"date": "04/08/2023", "name": "corwin", "url": "/~corwin"},
{"date": "05/15/2022", "name": "hifikuno", "url": "/~hifikuno"},
{"date": "12/20/2022", "name": "rajiv", "url": "/~rajiv"},
{"date": "09/27/2022", "name": "tubbo", "url": "/~tubbo"},
{"date": "09/08/2022", "name": "cyrus", "url": "/~cyrus"},
{"date": "08/17/2023", "name": "ranguli", "url": "/~ranguli"},
{"date": "06/08/2022", "name": "barnold", "url": "/~barnold"},
{"date": "05/12/2022", "name": "hifikuno", "url": "/~hifikuno"},
{"date": "04/27/2022", "name": "mhd", "url": "/~mhd"},
{"date": "02/29/2022", "name": "neildaemond", "url": "/~neildaemond"},
{"date": "02/15/2022", "name": "alex1138", "url": "/~alex1138"},
{"date": "02/15/2022", "name": "amr", "url": "/~amr"},
{"date": "01/27/2022", "name": "whitcomb", "url": "/~whitcomb"},
{"date": "12/15/2021", "name": "dctrud", "url": "/~dctrud"},
{"date": "10/10/2021", "name": "dmi3", "url": "/~dmi3"},
{"date": "03/08/2021", "name": "webwiz", "url": "/~webwiz"},
{"date": "02/13/2021", "name": "whitcomb", "url": "/~whitcomb"},
{"date": "11/30/2020", "name": "dethelor", "url": "/~dethelor"},
{"date": "11/26/2020", "name": "waffles", "url": "/~waffles"},
{"date": "11/20/2020", "name": "buttstuf", "url": "/~buttstuf"},
{"date": "09/03/2020", "name": "northernlights", "url": "/~northernlights"},
{"date": "08/16/2020", "name": "dctrud", "url": "/~dctrud"},
{"date": "07/21/2020", "name": "offdutypirate", "url": "/~offdutypirate"},
{"date": "07/15/2020", "name": "necrotechno", "url": "/~necrotechno"},
{"date": "07/10/2020", "name": "snowdusk", "url": "/~snowdusk"},
{"date": "05/31/2020", "name": "melyanna", "url": "/~melyanna"},
{"date": "05/15/2020", "name": "wgreenhouse", "url": "/~wgreenhouse"},
{"date": "04/21/2020", "name": "cano", "url": "/~cano"},
{"date": "11/09/2019", "name": "sneak", "url": "/~sneak"},
{"date": "10/05/2014", "name": "beau", "url": "/~beau"},
{"date": "10/05/2014", "name": "skk", "url": "/~skk"},
{"date": "10/05/2014", "name": "joeld", "url": "/~joeld"},
{"date": "10/05/2014", "name": "john", "url": "/~john"},
{"date": "10/05/2014", "name": "brendn", "url": "/~brendn"},
{"date": "10/05/2014", "name": "droob", "url": "/~droob"},
{"date": "10/05/2014", "name": "delfuego", "url": "/~delfuego"},
{"date": "10/05/2014", "name": "jonathan", "url": "/~jonathan"},
{"date": "10/05/2014", "name": "coldmode", "url": "/~coldmode"},
{"date": "10/05/2014", "name": "jemal", "url": "/~jemal"},
{"date": "10/05/2014", "name": "jonbell", "url": "/~jonbell"},
{"date": "10/05/2014", "name": "_", "url": "/~_"},
{"date": "10/05/2014", "name": "dvd", "url": "/~dvd"},
{"date": "10/05/2014", "name": "whitneymcn", "url": "/~whitneymcn"},
{"date": "10/05/2014", "name": "jimray", "url": "/~jimray"},
{"date": "10/05/2014", "name": "schussat", "url": "/~schussat"},
{"date": "10/05/2014", "name": "macdiva", "url": "/~macdiva"},
{"date": "10/03/2014", "name": "extraface", "url": "/~extraface"},
{"date": "10/03/2014", "name": "joshuag", "url": "/~joshuag"},
{"date": "10/03/2014", "name": "zarate", "url": "/~zarate"},
{"date": "10/03/2014", "name": "englishm", "url": "/~englishm"},
{"date": "10/03/2014", "name": "danbri", "url": "/~danbri"}
]

BIN
tilde.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

17
updateonline-users Executable file
View File

@ -0,0 +1,17 @@
#!/usr/bin/php
<?php
// Define the path to the JSON file
$jsonFile = '/usr/share/nginx/html/online-users.json';
// Run the `who` command to get the list of active users
$activeUsers = shell_exec("who | awk '{print $1}' | sort | uniq");
// Convert the output into an array, filtering out any empty lines
$activeUsersArray = array_filter(explode("\n", $activeUsers));
// Convert the array to JSON format
$activeUsersJson = json_encode($activeUsersArray, JSON_PRETTY_PRINT);
// Write the JSON data to the file
file_put_contents($jsonFile, $activeUsersJson);
?>

View File

@ -1,36 +1,24 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$title = 'tilde.club users';
include __DIR__.'/../header.php';
?>
<h1 id="fancyboi">full user list</h1>
<h1 id="fancyboi">Tilde.club user list</h1>
<div class="grid">
<div class="row">
<div class="col">
<p>here's a full list of users (including those who haven't updated their page from the default)</p>
<p>see <a href="http://tilde.club/tilde.24h.php">users who have updated their page in the last 24 hours</a></p>
<br>
<ul class="user-list">
<?php foreach (glob("/home/*") as $user) {
$user = basename($user); ?>
<li><a href="/~<?=$user?>/">~<?=$user?></a></li>
<?php } ?>
</ul>
<p>Here is a full list of users (including those who haven't updated their page from the default)</p>
<p>Also see users who have updated their page in the <a href="http://tilde.club/tilde.24h.php">last 24 hours</a></p>
<br>
<div class="user-list">
<?php
foreach (glob("/home/*") as $user)
{
$user = basename($user);
echo '<a href="/~'.$user.'/">'.$user.'</a>';
}
?>
</div>
</div>
</div>
</div>
<?php
include __DIR__.'/../footer.php';
<?php include __DIR__.'/../footer.php';

View File

@ -2,50 +2,59 @@
$title = "tilde.club wiki";
include __DIR__."/../header.php";
?>
<h1 id="fancyboi">the tilde.club wiki</h1>
<div class="grid">
<div class="row">
<div class="col">
<p>here's the articles on our wiki:</p>
<ul>
<?php
$category_to_articles = [];
foreach (glob("source/*.md") as $file) {
$article = basename($file, ".md");
$title = preg_match("/title: (.*)/i", file_get_contents($file), $matches) ? $matches[1] : $article;
$category = preg_match("/category: (.*)/i", file_get_contents($file), $matches) ? $matches[1] : 'default';
if (array_key_exists($category, $category_to_articles)) {
array_push($category_to_articles[$category], [$article, $title]);
} else {
$category_to_articles[$category] = [[$article, $title]];
}
ksort($category_to_articles);
}
?>
<?php foreach ($category_to_articles as $category => $articles) { ?>
<li><?=$category?></li>
<p>Here's the articles on our wiki:</p>
<ul>
<?php
$article_titles = [];
$article_names = [];
foreach ($articles as $article) {
array_push($article_names, $article[0]);
array_push($article_titles, $article[1]);
}
$name_to_title = array_combine($article_names, $article_titles);
asort($name_to_title);
foreach ($name_to_title as $name => $title) { ?>
<li><a href="/wiki/<?=$name?>.html"><?=$title?></a></li>
<?php } ?>
</ul>
<?php
// category order
$order = ['tilde.club', 'tutorials', 'software', 'links'];
<?php } ?>
</ul>
$category_to_articles = [];
// get articles
foreach (glob("source/*.md") as $file)
{
$article = basename($file, ".md");
$title = preg_match("/title: (.*)/i", file_get_contents($file), $matches) ? $matches[1] : $article;
$title = ucfirst($title);
$category = preg_match("/category: (.*)/i", file_get_contents($file), $matches) ? $matches[1] : 'default';
if (array_key_exists($category, $category_to_articles))
array_push($category_to_articles[$category], [$article, $title]);
else
$category_to_articles[$category] = [[$article, $title]];
ksort($category_to_articles);
}
foreach ($order as $category)
{
echo '<li>'.ucwords($category).'</li>';
echo '<ul>';
$article_titles = [];
$article_names = [];
foreach ($category_to_articles[$category] as $article)
{
array_push($article_names, $article[0]);
array_push($article_titles, $article[1]);
}
$name_to_title = array_combine($article_names, $article_titles);
asort($name_to_title);
foreach ($name_to_title as $name => $title)
echo '<li><a href="/wiki/'.$name.'.html">'.$title.'</a></li>';
echo '</ul><br>';
}
?>
</ul>
</div>
</div>
<?php include "../footer.php"; ?>
<?php include "../footer.php";

View File

@ -1,5 +1,5 @@
---
title: USING Two-Factor Authentication (2FA) ON TILDE.CLUB
title: Using Two-Factor Authentication (2FA) on Tilde.club
author: deepend
category: tutorials
---
@ -79,7 +79,7 @@ You can now enable 2FA in our webmail that is offered to users.
1. [Google Authenticator](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en_CA)
2. [Authy](https://authy.com/)
2. [Ente Auth](https://ente.io/auth/)
3. [Microsoft Authenticator](https://www.microsoft.com/en-us/account/authenticator)

View File

@ -1,5 +1,5 @@
---
title: bashblog
title: bashblog (blog platform)
author:
- deepend
- benharri

View File

@ -1,6 +1,6 @@
---
title: bbj
title: BBJ (Bulletin Butter & Jelly)
author: audiodude
category: tutorials
category: software
---
bbj: bulletin butter and jelly, a cozy bbs in your terminal

432
wiki/source/cgi.md Normal file
View File

@ -0,0 +1,432 @@
---
title: CGI - Making web applications like it's 90s
category: tutorials
author: xwindows
---
Web programming today is a mess:
gazillion of frameworks and libraries
thrown on top of each other,
runaway complexity so rampant,
while the whole setup teetering closer
to the state of house of cards than ever.
You know it had become so bad
because people have now started shipping you their computers
(a la Docker)
just for you to be able to run their web applications...
Don't you ever hate your middleware for pulling in millions of dependencies?
Feeling so done with juggling between multiple web programming libraries?
Getting tired of seeing your PHP script break
on every single PHP update that arrived?
Looking for alternatives that could shine
even in lowly environments like routers and single board computers?
Would like something more retro and longer-standing for a change?
If so,
**welcome to the olden world of CGI programming!**
Introduction
------------
At the most basic level of web-serving,
when your browser sent a request to the web server,
the server would check for a file residing on that URI path requested;
if exists,
it would give that file's content to your browser,
the end.
The point of CGI is to extend that
with the following idea:
if that path is not pointing to a file,
but rather to an executable program;
instead of serving the program binary to the client,
we run that program,
with request body piped to its standard input,
and pipe its standard output back to the client as a response.
(Lawyers will say this is a sly oversimplification,
but you get an idea)
> By the way,
> CGI stands for <q>Common Gateway Interface</q>.
> Of course,
> a very common question that follows would be
> why is it being called <em>gateway</em>:
> it was because in early 1990s,
> the main use of this kind of server-executed program
> was not web application,
> but for writing glue logic
> to access institution's already-existing in-house infosystem,
> which previously only accessible as command line programs
> run via on-premise terminal
> or over telnet/dial-in shell session.
>
> Such glue logic programs would accept the request,
> invoke the on-server infosystem programs with correct parameters,
> dress its output a bit
> before sending that result to web browser;
> making them <em>gateways</em>
> to let users from the web
> access those in-house information systems.
> These were in fact,
> the main uses that pushed for the effort
> to standardize web servers
> into using the same _common interface_ for running such _gateway_ programs;
> and that's where the name came from.
Anyway,
by using just standard input/output and some environment variables,
it means you can use **virtually any** compiled **programming language**,
and any shebang-compatible interpreted programming language
for server-side web development.
There would be no complicated protocol you need to grok;
and when you chose your language wisely,
there would be no dependency hell to watch out for,
no API/ABI breakage to rewrite around,
and no upgrade treadmill forced on you.
Life was definitely simpler back in the days;
and by using CGI,
your life could be simple _today_ too.
For these reasons,
while having limited amount of bling and bang to offer,
CGI has been standing through time,
as the lowest common denominator,
programming language-agnostic,
platform-independent scheme for running web applications;
from its first standardization at the dawn of World Wide Web era
nearly 3 decades ago,
to today.
And... did you know that the development of PHP was originally
become possible because of CGI too?
Tilde.club have been supporting CGI programming
on user web space since 17 May 2020.
As CGI was originally conceived
in shared institutional Unix server environment;
on a tilde,
it means we are experiencing it
in its natural habitat.
Hello World!
------------
As simple as it is,
everybody has to start somewhere;
so the following are example <q>Hello World</q> CGI programs
in many programming languages that Tilde.club supports.
All of them produce HTTP response with status code 200,
<q>`text/plain`</q> MIME type,
and simple <q>Hello World</q> text as a response body.
Note that **every example scripts here
all work under <q>`.cgi`</q> file extension**;
other language-specific file extension that work
would be noted in each example.
- Perl
(also works with <q><samp>.pl</samp></q> file extension):
#!/usr/bin/perl
print "Status: 200\n";
print "Content-Type: text/plain\n";
print "\n";
print "Hello World!\n";
Note that Perl was the main language of choice
back in the heyday of CGI programming.
- Bourne shell script
(also works with <q><samp>.sh</samp></q> file extension):
#!/bin/sh
echo "Status: 200"
echo "Content-Type: text/plain"
echo
echo "Hello World!"
- Python
(usable under both 3.x and 2.x,
also works with <q><samp>.py</samp></q> file extension):
#!/usr/bin/python
print("Status: 200")
print("Content-Type: text/plain")
print("")
print("Hello World!")
- AWK:
#!/usr/bin/awk -E
BEGIN {
print "Status: 200"
print "Content-Type: text/plain"
print
print "Hello World!"
}
- Lua
(also works with <q><samp>.lua</samp></q> extension):
#!/usr/bin/lua
print("Status: 200")
print("Content-Type: text/plain")
print("")
print("Hello World!")
- Tcl:
#!/usr/bin/tclsh
puts "Status: 200"
puts "Content-Type: text/plain"
puts ""
puts "Hello World!"
- Common Lisp:
#!/usr/bin/sbcl --script
(progn
(princ "Status: 200") (terpri)
(princ "Content-Type: text/plain") (terpri)
(terpri)
(princ "Hello World!") (terpri)
)
Pick the language you like,
put the script (or executable) in a file anywhere inside your <q>`public_html`</q> subdirectory
of your Tilde.club home directory,
with appropriate file extension;
and also make sure that the thing is **world-readable** _and_ **world-executable**
(something like <q>`chmod +rx YOURFILE.EXT`</q> would do).
If you use other language that compiles to a binary executable,
just world-executable permission will suffice.
The URL for accessing a CGI program from a web browser
is no different from accessing
regular file hosted on your Tilde.club web space.
Note that there are no assembly,
C,
and C++ example here,
and that is intentional:
you are supposed to already know such languages well already
&mdash;including how to program it _safely_ and _defensively_&mdash;
before even thinking about trying them in this task.
Program Output
--------------
Output of your CGI programs is expected to have two parts:
1. Lines you printed before the first blank line
will be treated as HTTP response headers fields:
- The only exception is the <q>`Status:`</q> pseudo-header,
which will not be output as a real response header,
but its value will be rather used as HTTP status code of the response.
- When <q>`Status:`</q> pseudo-header is omitted,
the HTTP status code of your response would be 200.
- Your program ought **NOT** to output this as a real HTTP response line
(<q>`HTTP/1.0 200 OK`</q> and suchlike).
Doing so is off-spec;
and while some servers handle this okay,
Tilde.club doesn't.
- You **MUST** output <q>`Content-Type:`</q> header;
or else the server would reject your program's output
and give HTTP 502 error to the client instead.
- A blank line ends the headers section.
- You should output headers
(including the blank line terminating the headers)
in platform's native line ending,
which is LF in case of Tilde.club and other GNU/Linux hosts;
but in practice,
CR/LF is accepted as well.
1. And what you output after the first blank line
is your response body (i.e. content).
This part can use any line ending in case of text,
or it could even be binary;
as long as it fits
with the <q>`Content-Type:`</q> header value you had just printed.
Empty response body is allowed as well;
by not outputting anything after that first blank line.
Program Input
-------------
Information from HTTP request
arrive at your CGI program in two different channels:
1. Request line,
request headers,
misc request information,
and server information:
these arrive as environment variables.
1. Request body:
this arrives verbatim as standard input data.
Unless you are processing HTTP POST or PUT request
(which are quite advanced stuff),
you don't really need to look at request body at all.
So the information of interest are mostly contained
in the environment variables:
- The HTTP request method used
would be passed to your program as a value of environment variable `REQUEST_METHOD`.
- The part after <q>`?`</q> of request URI
would be passed to your CGI program
as the value of environment variable `QUERY_STRING`.
- This variable will always be present.
If the request URI had no <q>`?`</q>,
or there was nothing after <q>`?`</q>;
the value would be empty.
- Each request headers field's name would be converted to uppercase,
prepended with <q>`HTTP_`</q>,
and set as environment variable
with value equals to the header value received from the client.
For example,
<q>`Host: tilde.club`</q> header line
would be converted to an environment variable `HTTP_HOST`
with value <q>`tilde.club`</q>.
The following are environment variables from the CGI 1.1 specification
which are set for CGI programs in Tilde.club,
in alphabetical order:
> `CONTENT_LENGTH`,
> `CONTENT_TYPE`,
> `GATEWAY_INTERFACE`,
> `QUERY_STRING`,
> `REMOTE_ADDR`,
> `REMOTE_PORT`,
> `REQUEST_METHOD`,
> `SCRIPT_NAME`,
> `SERVER_NAME`,
> `SERVER_PORT`,
> `SERVER_PROTOCOL`,
> `SERVER_SOFTWARE`
- You can find out more what each of these variables mean
in the original CGI 1.1 specification,
linked in the [Further Reading](#further-reading) section below.
And the following are other environment variables
that which are not in CGI 1.1 specification,
but are set for CGI programs in Tilde.club:
> `DOCUMENT_ROOT`,
> `DOCUMENT_URI`,
> `HTTPS`,
> `REDIRECT_STATUS`,
> `REQUEST_SCHEME`,
> `REQUEST_URI`,
> `SCRIPT_FILENAME`,
> `SERVER_ADDR`
**Notes:**
- `REMOTE_HOST` variable is always absent;
including when the remote host does have a valid reverse-DNS address.
- `REMOTE_IDENT` variable is always absent;
including when the remote client does connect
from a host with Identd service.
- While both `HTTPS` and `REQUEST_SCHEME` variables
could be used for discerning HTTPS from plain old HTTP request;
checking for `HTTPS` value <q>`on`</q> is to be used
if you expect your CGI program to be portable to Apache HTTP Server.
- This can be used for ensuring a correct version of Atom or RSS feed
got served on a right protocol.
Program Execution
-----------------
These are conditions that your CGI programs would be running in:
- One instance of CGI program would be executed to service one request;
and that instance would terminate at the end of response.
- Multiple instances of a CGI program could be run at the same time.
- Your CGI program would start after the server
had read the entire request header from the client
(but not the request body, if any);
and only when the request URI matched your CGI program of course.
- Your CGI program would be run inside its directory
(and not other location like server binary's directory).
- Once your CGI program runs,
its standard input would be fed with the request body
(if any).
- If you are going to process request body,
you ought to do so before producing any output.
(HTTP is a request-response protocol,
remember?)
- Everything your program output on standard error stream
would go into the server's error log.
- CGI program that did not finish running for too long
will cause the server to return HTTP 504 <q>Gateway Time-out</q> error
to the client instead of its response.
Tips
----
- Avoid making your CGI program a time hog;
good CGI programs start quickly and finish quickly.
- Avoid making your CGI program a resource hog;
just like everything else you do on Tilde.club shell.
- Avoid making your CGI program a security hole.
For this reason,
using C or C++ for a non-trivial CGI program are **not** recommended
unless you actually know your craft.
- Remember:
it costs Tilde.club 1 program execution
to service one HTTP request to a CGI program;
use it responsibly
and for things that matter.
Setup-Specific Notes
--------------------
Following are tidbits specific
to the CGI setup used in Tilde.club:
- If you would like to make CGI program a directory index,
name it <q><samp>index.cgi</samp></q>.
(<q><samp>index.sh</samp></q> works too in case of shell script)
- There is no database daemon of any kind.
(If you would like to use SQLite,
see below for a caveat about credential and files)
And some caveats:
- There is no support for <var>PATH_INFO</var> environment variable;
you can blame Nginx for this one.
This mean you cannot simulate files and directory-like URIs
(like <q>`/~SOMEONE/category.cgi/automobile/ev`</q>)
under your CGI program;
the server will simply return HTTP 404 error for such URIs
even when <q>`category.cgi`</q> exist and being executable.
- Avoid leaving files with following extensions in your web space
when you don't intend for them to be run as CGI:
- `.cgi`
- `.pl`
- `.sh`
- `.py`
- `.lua`
This is because in current setup,
requests to these files will be forwarded to a CGI handler anyway,
even when their corresponding executable bit is not set;
while it would not really run such script,
it would result in HTTP 502 error being sent to client.
If you would like to distribute these verbatim as source files,
you might want to workaround by renaming such files
to add <q><samp>.txt</samp></q> at the end.
- CGI programs here run under web server's credentials:
user <q><samp>nginx</samp></q>
and group <q><samp>nginx</samp></q>
(user ID <samp>994</samp>,
group ID <samp>990</samp>);
tread carefully if you need to make your program read/write
private files.
Further Reading
---------------
- [Common Gateway Interface version 1.1 specification (RFC 3875)](https://www.rfc-editor.org/rfc/rfc3875.html),
which is [also available in text version](https://www.rfc-editor.org/rfc/rfc3875.txt).
- [Ten Million Users and Ten Years Later](https://dl.acm.org/doi/10.1145/3472749.3474819),
a case study of CGI being a secret ingredient
for developing web application that could stay in-service
more than a decade.

125
wiki/source/cgit.md Normal file
View File

@ -0,0 +1,125 @@
---
title: using cgit on tilde.club
author: deepend
category: tutorials
---
`cgit` gives every tilde.club member a simple, readonly web view of their public Git repositories.
Any repo you put in `~/public_git/` and end with `.git` is automatically shown at
```
https://tilde.club/~<username>/git/
```
Below is the quickstart plus a few tips.
---
## 1Create the **public\_git** directory (once)
```bash
$ mkdir -p ~/public_git
```
The web server is already allowed to traverse `~/public_git`, so you do **not** have to chmod anything manually.
---
## 2Add a repository
Only **bare** repos are accepted (they have no working tree inside them).
```bash
# create a brandnew bare repository
$ cd ~/public_git
$ git init --bare hello.git
```
or push an existing project:
```bash
$ cd ~/my/project
# add tilde as a remote and mirrorpush everything
$ git remote add tilde ssh://<username>@tilde.club/~/public_git/project.git
$ git push --mirror tilde
```
You can repeat for as many repos as you like; just keep each one directly in
`~/public_git/` and make sure the name ends with `.git`.
---
## 3Browse your repos
```
Index page : https://tilde.club/~<username>/git/
Single repo : https://tilde.club/~<username>/git/<repo>.git/
```
Example for user **deepend**:
```
https://tilde.club/~deepend/git/ # lists everything
https://tilde.club/~deepend/git/hello.git/ # specific repo
```
The header will say `~deepend Git Repositories`, commits are clickable, diffs
are highlighted, and the cloning URL is shown near the top right.
---
## 4Update a repository
Because the repo is bare you **push** into it; cgit shows the new state
immediately.
```bash
$ git push tilde main # normal
$ git push --mirror tilde # full mirror (branches + tags)
```
---
## 5Remove a repository
Simply delete or rename the directory:
```bash
$ rm -rf ~/public_git/oldstuff.git
```
The entry disappears from the index on the next page load.
---
## FAQ
### Can I hide a repo from the list?
Move it somewhere else in your home directory or make it nonbare.
`cgit` only scans `~/public_git/*.git`.
### Why bare repos only?
Internal `.git/` directories inside nonbare repos confuse cgits scanner and
produce broken links like `/repo.git/.git/`.
Bare repos avoid that and are the normal way to publish Git over HTTP.
### Clone / pull URL?
Use SSH for write access:
```
ssh://<username>@tilde.club/~/public_git/<repo>.git
```
or plain HTTPS for readonly:
```
https://tilde.club/~<username>/git/<repo>.git
```
---
Happy hacking, and show off your code!
Questions? `#club` on irc.newnet.net or mail **[root@tilde.club](mailto:root@tilde.club)**.

View File

@ -4,26 +4,34 @@ author: deepend
category: tilde.club
---
***NOTICE: Tilde Club is not a registered legal entity or charity, so any donations made cannot be claimed as tax-deductible contributions. ***
If you like what tilde.club does and would like to contribute to the costs of running such a service, feel free to make a donation. If you can, we appreciate it, if you can't, we also appreciate you being here.
## Methods you can donate to tilde.club.
There are currently two (2) methods you can donate financially to tilde.club. (All money is used towards paying hosting costs (domains, servers)
1. [Liberapay](https://liberapay.com/tilde.club/donate)
<img src="https://img.shields.io/liberapay/receives/tilde.club.svg?logo=liberapay"> <img src="https://img.shields.io/liberapay/patrons/tilde.club.svg?logo=liberapay"> <img src="https://img.shields.io/liberapay/goal/tilde.club.svg?logo=liberapay">
[<img src="https://shields.io/badge/kofi-Support_Us-ff5f5f?logo=ko-fi&style=for-the-badgeKofi">](https://ko-fi.com/tildeclub)
2. [Paypal](https://www.paypal.com/donate?hosted_button_id=DWHSADKJ26HZ8)
[![PayPal](https://img.shields.io/badge/PayPal-Support_Us-003087?logo=paypal&logoColor=fff)](https://www.paypal.com/donate?hosted_button_id=DWHSADKJ26HZ8)
Our current server costs are $180.00 USD /Month. And will be going up in the coming months due to costs increasing at OVH.
(is the reason this page exists again)
[![Github Sponsors](https://img.shields.io/badge/GitHub%20Sponsors-30363D?&logo=GitHub-Sponsors&logoColor=EA4AAA)](https://github.com/sponsors/tildeclub)
[Vultr](https://www.vultr.com/?ref=9732299-9J) Signing up to Vultr using this link gives you $300 credit and it benefits tilde.club ($300 Credit is Limited Time).
[DigitalOcean](https://www.digitalocean.com/?refcode=be3f8510bfe9&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge) Signing up gets your $200 Credit and helps tilde.club.
[Web Hosting Canada](https://clients.whc.ca/aff.php?aff=7560) Buying any service/domain helps tilde.club.
[Voip.ms](https://voip.ms/en/invite/MTEyMDM5) Buying Voip Services gives me credit so I will be able to offer more tilde.tel features in the future.
**NOTE: Please E-Mail root@tilde.club to notify that you have donated so we can add you to the gold star supporters list.
## Be Involved!
Best thing to do is be involved in the community.
Eg. Create a PR on tilde.club's github or tildegit, Chat on IRC, create awesome webpages,
Eg. Create a PR on tilde.club's github, Chat on IRC, create awesome webpages,
develop software using the many tools available to you on tilde.club.
Tilde.club does cost money to run, however it all makes it worth it if the community is active and enjoying what we offer.

View File

@ -3,6 +3,7 @@ title: Editing with Emacs
author:
- ohnoitsnoah
- xwindows
- keyboardan
category: tutorials
---
<!-- Make a section for Modes -->
@ -10,7 +11,7 @@ category: tutorials
Emacs in tilde.club
===================
[Emacs](https://www.gnu.org/software/emacs/) is a text-editor that is very capable, but also can be very confusing to new users, so here's a basic guide to working with Emacs.
[Emacs](https://www.gnu.org/software/emacs/) is a text-editor that is very capable, but also can be confusing to new users, so here's a basic guide to working with Emacs.
Opening Emacs
-------------
@ -63,7 +64,14 @@ Emacs has some weird, complex commands, that may not make sense to new users. Wh
* `M-` means the Meta key
* While not common on modern hardware, the Meta key can be typed by using either the Escape/ESC key, or the Alt key in some cases
Key-bindings in Emacs are typically longer than normal key-bindings used outside of Emacs, usually being a combination of what would be two "normal" key-bindings. For example; to exit Emacs, you can type `C-x C-c`. That means you press both of these key-bindings in series to complete the Emacs key-binding of exiting Emacs. It's very weird and complex, but is easy to adjust to after using it for a while.
Key-bindings in Emacs are typically longer than normal key-bindings used outside of Emacs, usually being a combination of what would be two "normal" key-bindings. For example; to exit Emacs, you can type `C-x C-c`. That means you press both of these key-bindings in series to complete the Emacs key-binding of exiting Emacs. It's somewhat weird, but is easy to adjust to after using it for a while.
Interactive Learning
--------------------
The best way to begin learning, is the interactive way. GNU Emacs has a built-in command that is specifically to learning the basic of GNU Emacs. To access it, you can press `C-h t`, or execute the GNU Emacs command named `help-with-tutorial` (pressing `M-x help-with-tutorial`).
To close the tutorial, use `C-x k` to kill the buffer.
Navigating Emacs with the Drop-Down Menus
-----------------------------------------
@ -91,4 +99,45 @@ Whenever you are finished working on a file, and no longer need the buffer, you
### Cut, Copy, Paste (C-w, M-w, C-y)
Cut, Copy, and Past are all available in the Edit menu.
Cut, Copy, and Paste are all available in the Edit menu.
Emacs is Self-Documented
------------------------
GNU Emacs is a very big program, and to master it, it takes a lot of time. GNU Emacs is an LISP environment, where you can *grow* a lot. Which makes learning GNU Emacs, a great investment of time; because you can do basically everything, related to text, and, with time, you will master GNU Emacs flexibility. Which means you will be able to do anything with GNU Emacs.
But you don't need to know everything that GNU Emacs has to offer (is there anyone that does know?).... The most important thing to know, is how to seek for help. And GNU Emacs is there to help you.
To access the main help menu of GNU Emacs, you press `C-h C-h`. This will open a new buffer, where you can interact. In this buffer, there is, for example, " b Show all keybindings" entry. This means that if you now press "b" it will show all the keybindings of the buffer.
And, if you are in any other buffer, and want to know all the keybindings quickly, you may, instead, press `C-h b` (mind that this "b", after the `C-h`, has the same effect as pressing "b" in the main menu help). `C-h` in GNU Emacs, means exactly, I want help. Then follow it with a keystroke that say the type of help you want.
### Common Help Keybindings
#### C-h t (help-with-tutorial)
Select the Emacs learn-by-doing tutorial.
#### C-h k (describe-key)
Display documentation of the function invoked by a key sequence.
#### C-h f (describe-function)
Display the full documentation of function.
#### C-h v (describe-variable)
Display the full documentation of variable.
#### C-h a (apropos-command)
Show commands that match a certain pattern.
#### C-h m (describe-mode)
Display documentation of current major mode and minor modes.
#### C-h i (info)
Enter Info, the documentation browser. It shows a complete documentation of your installed operating system's commands.

View File

@ -1,5 +1,5 @@
---
title: custom 404
title: custom 404 error page
author: deepend
category: tutorials
---

View File

@ -62,9 +62,23 @@ Who are all these folks and what are they up to?
now](http://tilde.club/~whitneymcn/whoville.shtml)?
- [who updated their websites
recently](http://tilde.club/tilde.24h.php)?
- [who links to each other](http://tilde.club/~deepend/social.html)?
- [can you give me a blogroll](http://tilde.club/~_/)?
Disk Quotas Now Enforced
------------------------
To help keep tilde.club running smoothly, we've introduced disk quotas. This helps make sure everyone gets their fair share of space and keeps the system in good shape.
**Here's what you need to know:**
- **Soft Limit:** 1 GB You'll get a heads-up if you go over this, but you can still use more space up to the hard limit.
- **Hard Limit:** 3 GB This is the max. If you hit this limit, you'll need to clear some space before adding more files.
- **Grace Period:** 1 week If you exceed the soft limit, you have 7 days to get back under it before the hard limit kicks in.
You can check your usage and see how much space you have left by running the `resources-used` script in your home directory.
If you have any questions or run into any issues, just reach out. We're all in this together!
My function keys don't work in byobu! (using Windows PuTTY)
------------------------------------------------------------------
@ -79,7 +93,7 @@ I am using PuTTY 0.73 Windows 11, and could not get byobu hotkeys to work. Inste
**References**
- [Function Key Fix at codeyarns.com](https://codeyarns.com/tech/2013-01-21-byobu-function-keys-do-not-work-in-putty.html)
- [byobu hotkey list at linuxsecrets.com](https://www.linuxsecrets.com/3326-byobu-commands)
- [Byobu Keybindings Cheat Sheet at cheatography.com](https://cheatography.com/mikemikk/cheat-sheets/byobu-keybindings/))
Other links
-----------

104
wiki/source/help_system.md Normal file
View File

@ -0,0 +1,104 @@
---
title: Tilde.club Help Desk Guide
category: tilde.club
author: deepend
---
# Tilde.club Help Desk Guide
Welcome! This Help Desk system provides a quick, self-serve way to:
1. **Request or redeem a new SSH key** for your tilde account.
2. **Reset your account password** (if you've forgotten it).
Below youll find step-by-step instructions on how to use the help desk system when you **SSH** into the `help` user.
---
## Accessing the Help Desk
1. Open a terminal on your local machine.
2. **SSH** into the **help** account (adjust the hostname to your actual tilde server name):
```bash
ssh help@tilde.club
```
3. Youll see a welcome message and a **main menu** with numbered options.
---
## Main Menu Overview
After logging in, youll see three main options:
1. **SSH Key Help**
2. **Password Help**
3. **Exit**
Select the option that applies by typing its corresponding number and pressing **Enter**.
---
## 1. SSH Key Help
When you choose **SSH Key Help** at the main menu, youll see another menu:
1. **Request a new SSH key**
2. **Redeem a code** for a new SSH key
3. **Return** to the previous menu
### 1.1 Request a New SSH Key
1. Pick **“I want to request a new SSH key”** (option 1).
2. **Enter the email** you registered with your tilde account. The system does a simple check to ensure its valid.
3. If the email matches an existing account, youll receive a **“request code”** at that address.
4. After receiving that code, **log out** or press **Enter** to return to the main menu.
### 1.2 Redeem a New SSH Key
1. Back in the **SSH Key Help** menu, choose **“I have a code from my email and need to redeem it.”**
2. Paste in the **request code** you received.
3. The system confirms your username.
4. When prompted, **paste your new public SSH key** (the part that starts with `ssh-ed25519` or `ssh-rsa` or similar).
5. The system appends your key to your `~/.ssh/authorized_keys`.
6. Youll see a success message, and you can then **log in** to your tilde with that new key.
---
## 2. Password Help
At the main menu, choosing **Password Help** displays:
1. **Request a password reset code**
2. **Redeem a password reset code**
3. **Return** to the previous menu
### 2.1 Request a Password Reset Code
1. Choose **“Request a password reset code.”**
2. **Enter your email** address.
3. The system sends a **reset code** to your email if the account matches.
4. Exit or return to the menu once you have the code.
### 2.2 Redeem a Password Reset Code
1. Choose **“Redeem a password reset code.”**
2. **Paste in** the code from your email.
3. Enter a **new password** for your tilde account, then **confirm** it.
4. Upon success, the system updates your accounts password immediately.
---
## 3. Exiting the Help Desk
Simply choose **“Id like to leave this help desk”** (option 3 in the main menu) or press <kbd>Ctrl</kbd>+<kbd>D</kbd> (end of file) to disconnect.
---
## Tips & Troubleshooting
- **Time Limit**: Each prompt has a 2-minute inactivity timer. If you wait too long, the help desk **exits** automatically. Just log in again to resume.
- **Email Didnt Arrive?** Check your spam folder. If you still dont see it, contact root@tilde.club.
- **Invalid Email**: If you mistype an email or use an unrecognized domain, youll see an error. Double-check your address.
- **Incorrect Code**: If the code was typed incorrectly or expired, the system will refuse it. Request a new one if needed.
---

View File

@ -3,21 +3,26 @@ title: Socializing and chat
author:
- emv
- benharri
- deepend
category: tutorials
---
## irc
we're members of the [tildeverse](https://tildeverse.org) which we do have a channel on.
We have changed our main channel to the [Newnet IRC Network](https://newnet.net). the official
channel for ~club is `#club`. on both networks. Stop by and say hello!
Our main channel is on the [Newnet IRC Network](https://newnet.net).
The official channel for ~club is `#club`. Stop by and say hello!
> **New!** An *official secondary* channel is now open on the Zoite IRC
> Network in case Newnet ever has an outage (or if you just feel like
> hanging out elsewhere). Connect to **irc.zoite.net** on port **6670 SSL**
> and `/join #club` same welcoming vibe, different network.
run `chat` to open [weechat](https://weechat.org) auto-connected to our irc
server. try launching [tmux](tmux.html), [byobu](https://superuser.com/a/423397)
or [screen](screen.html) to keep your chat session running.
other clients like irssi are available as well! just connect to localhost on
port 6667 and `/join #club`.
other clients like irssi are available as well! just connect to **irc.newnet.net**
on port **6697 TLS** and `/join #club`.
feel free to use Newnet's [webchat](https://web.newnet.net/?join=club) if
you prefer.
@ -54,7 +59,7 @@ location (`~/.weechat/relay_socket`). to get started using it, follow these step
weechat as the socket doesn't exist until weechat starts up.
3. in your relay client:
* [glowing-bear](https://glowing-bear.org):
* [glowing-bear](https://glowingbear.tilde.club/):
- relay hostname: tilde.club:443/~username/weechat
- relay port: 443
- your relay password
@ -71,12 +76,10 @@ location (`~/.weechat/relay_socket`). to get started using it, follow these step
~/.weechat/relay_socket)
## IRC Bouncer (ZNC)
NOTE: Email deepend or message him on IRC if you require ZNC access.
You can find a ZNC IRC Bouncer by going to: [https://services.tilde.club/znc](https://services.tilde.club/znc).
Use your tilde.club username and password for login.
To connect to your ZNC its at services.tilde.club Port: 6699(SSL)
NOTE: long passwords fail to authenticate with the ZNC server.
### Users created before December 28th, 2021 will need to email [mailto:root@tilde.club](root@tilde.club) to get your user added.

View File

@ -1,31 +0,0 @@
---
title: mailing list and guidelines
author: audiodude
category: tilde.club
---
There are tilde.club mailing list available at:
* [https://lists.tildeverse.org/postorius/lists/tildeclub.lists.tildeverse.org/](https://lists.tildeverse.org/postorius/lists/tildeclub.lists.tildeverse.org/)
* new users are automatically subscribed using their $user@tilde.club address.
* old users with a contact email are subscribed with that email.
* create an account with the email you're subscribed with to adjust your subscription.
# General Mailing List Guidelines
1. Be polite, Respect others, Assume good faith.
2. Debate the issue, do not engage in personal attacks / Flamewars.
3. Before posting a question, first research your problem by reading past messages on this list and other sources on the internet.
4. Send really descriptive questions or informational postings.
5. Follow up with a brief note on the solution.
6. Keep your messages closely related to the topic of the thread your replying to.
7. Write a good subject line.
8. Keep your email short and to the point, Don't send meaningless messages, like "me too" or "thank you" posts that do not contribute to the conversation.
9. Provide URLs to articles wherever possible.
10. Don't be critical of people's questions posted to the email discussion group.
11. If in doubt, send a message to the list moderator and ask.
12. Don't send somebody else's personally identifying information.
13. Don't send copyrighted material not owned by you and not licensed to you for redistribution.
14. Encourage newcomers to lurk for a while and absorb the "style" of your group, before posting any messages.
15. No self-promotion, job listings, or advertising unless relevant to the topic.
16. Commercial posts are not acceptable, although you may provide your commercial contact information in your signature.
17. Avoid using the mailing list for political or ideological battle.
18. No chain letters.

View File

@ -1,7 +1,7 @@
---
title: Terminal Multiplexers
title: Byobu, TMUX & Screen (terminal multiplexers)
author: rudi
category: tutorials
category: software
---
# Terminal Muiltiplexers

View File

@ -0,0 +1,57 @@
---
title: Adding a web counter to your page
author: deepend
category: tutorials
---
To display a visit counter on your webpage, you'll need to edit your `index.html` file and insert a `<script>` tag that loads the counter.
Type: `nano index.html` to open your file for editing.
Add the following line *exactly where you want the counter to appear*:
```html
<script src="https://counter.tilde.club/?page=homepage&user=yourname&style=57chevy"></script>
```
- Replace `homepage` with any identifier for the page you're tracking.
- Replace `yourname` with your username or something unique to your site.
- Replace `57chevy` with a different style if you'd like.
Heres an example:
```html
<p>Visitor count:
<script src="https://counter.tilde.club/?page=homepage&user=alice&style=web1"></script>
</p>
```
Once youve added the line, save and close the file using `CTRL+X`, then press `y` and `[Enter]`.
Refresh your site in your browser to see the counter.
### Available Styles
You can use any of these style names in the `style=` parameter:
- `57chevy`
- `7seg`
- `bbldotg`
- `bellbtm`
- `blgrv`
- `cntdwn`
- `computer`
- `ds9`
- `fdb`
- `led`
- `links`
- `marsil`
- `sbgs`
- `web1`
Try different styles to see which one fits your site best.
Note: The counter uses sessions and cookies to count *unique* visits per user every 24 hours (or whatever time is configured). Page refreshes wont increase the count unless unique mode is off.
If you need help or want a custom style added, reach out to the site admin.
```

View File

@ -1,11 +0,0 @@
---
title: terminal multiplexers - screen
category: software
---
`screen` is a unix utility that lets you manage multiple shells from within a single window. You switch between them with a few keystrokes. When you disconnect it keeps the processes alive, and you can reconnect from another login.
It's pretty handy. [tmux](tmux.html) does a similar set of things.
a nice [screen tutorial](http://tilde.club/~jonathan/screen/) from ~jonathan will walk you through it.

View File

@ -1,6 +1,6 @@
---
author: benharri
title: ssh
title: How to connect using SSH (secure shell)
category: tutorials
---
@ -153,21 +153,21 @@ where username is your username (~benharri would use `ssh benharri@tilde.club`)
2. create your keypair:
```bash
```cmd
ssh-keygen -t ed25519 -a 100
```
3. If you press enter to accept the defaults, your public and private key will be located at %USERPROFILE%\.ssh\id_ed25519.pub and %USERPROFILE%\.ssh\id_ed25519 respectively.
3. If you press enter to accept the defaults, your public and private key will be located at %USERPROFILE%\\.ssh\\id_ed25519.pub and %USERPROFILE%\\.ssh\\id_ed25519 respectively.
Note: %USERPROFILE% is a short code that the computer expands to mean c:\users\your_name\, or whatever the relevant path is to your user's main folder.
Note: %USERPROFILE% is a short code that the computer expands to mean C:\\Users\\your_name\\, or whatever the relevant path is to your user's main folder.
Next we will open up the public key so we can copy its contents.
Next we will open up the public key so we can copy its contents.
```bash
```cmd
notepad %USERPROFILE%\.ssh\id_ed25519.pub
```
4. Copy the text of the pubkey that opens in Notepad and paste it in the sshkey field on the signup form or email it to the relevant sign-up address for the tilde you are joining.
4. Copy the text of the pubkey that opens in Notepad and paste it in the sshkey field on the signup form or email it to the relevant sign-up address for the tilde you are joining.
#### using your keypair
@ -177,12 +177,19 @@ once an admin approves your signup, you can join the tilde.club
6. `ssh` to tilde.club:
```bash
```cmd
ssh username@tilde.club
```
where username is your username (~benharri would use `ssh benharri@tilde.club`)
Note: If you generated your key to the location above (%USERPROFILE%\\.ssh\\...) then you will be able to SSH to your tilde server without having to specify the location of the key. That folder is the default used by SSH and it will be found automatically. If you generated your keys in a different location or moved them, you will need to specify the full path to the private key.
```cmd
ssh -i c:\path\to\my\private\key username@your.tilde
```
7. profit???
---
@ -213,7 +220,7 @@ be located at `~/.ssh/id_ed25519.pub` and `~/.ssh/id_ed25519`
cat ~/.ssh/id_ed25519.pub
```
4. copy the output of the last command and paste it in the sshkey field on the
4. copy the output of the last command and paste it in the sshkey field on the
signup form (or email it to [root@tilde.club](mailto:root@tilde.club) if you already have an account)
#### using your keypair
@ -230,12 +237,6 @@ where username is your username (~benharri would use `ssh benharri@tilde.club`)
ssh username@tilde.club
```
Note: If you generated your key to the location above (%USERPROFILE%\ssh\...) then you will be able to SSH to your tilde server without having to specify the location of the key. That folder is the default used by SSH and it will be found automatically. If you generated your keys in a different location or moved them, you will need to specify the full path to the private key.
```bash
ssh -i c:\path\to\my\private\key username@your.tilde
```
7. profit???
---

View File

@ -1,6 +1,6 @@
---
author: jeffbonhag
title: SSHFS
title: SSHFS (SSH Filesystem)
category: software
---

View File

@ -1,5 +1,5 @@
---
title: tin
title: tin (UseNet reader)
category: software
---
@ -7,6 +7,6 @@ tin is a threaded NNTP and spool based [UseNet](usenet-news.html) newsreader for
[http://www.tin.org/](http://www.tin.org/)
it's under active development, with a last release on October 3, 2014. It is installed on ``tilde.club``, and should be invoked as `tin -r` to read from the remote news spool. Use <tab> to go to the next unread article and `w` to write a new article.
it is an actively maintained project, and is installed on ``tilde.club``. `tin` should be invoked as `tin -r` to read from the remote news spool. Use <tab> to go to the next unread article and `w` to write a new article.
if you have `tin` on your local box and can do [[ssh port forwarding]], you can read news using it. (details forthcoming).

View File

@ -1,44 +0,0 @@
---
title: terminal multiplexers - tmux
category: software
---
TMUX IS THE BEST. Here's a super basic primer.
to start a new session, type `tmux new -s tildemux`.
A yellow bar will appear at the bottom of your terminal. You're now in TMUX!
TMUX has sessions, windows, and panes. Each of these things will have a terminal in it. If you actually typed what I told you to earlier, you'll be in a session named `tildemux`. That session has one window, `0`. That window has one pane, also named `0`. (Computers start counting at 0, not 1.)
## windows
Your tmux bar should look like:
`[tildemux] 0:bash*`
…which means that you're in a session named `tildemux`, which has a window `0`, running the command `bash`. `*` means that window 0 is active, and the pane running bash is currently active.
To create a new window within this session, type `PREFIX c`. PREFIX?!? By default, it's `control-b`. Now you should see:
`[tildemux] 0:bash- 1:bash*`
`1:bash*` means you're in a pane running `bash` inside window 1. To change back to pane 0, type `PREFIX 0`. The `*` should be back on `0:bash`.
Run a cool interactive command, such as `htop` (to see how many of system resources we're eating up) or `vim` (to write some awesome webpages). Your tmux status bar should update to `0:<name of the current process>`. So now instead of saying `bash` it will say `htop` or `vim`.
## panes
Panes are great. TMUX panes let you run more than one terminal inside your one, actual terminal. To "split" a new pane, `PREFIX "`. That makes a horizontal split. You'll notice there are now two panes open one on top of the other. `PREFIX %` makes a vertical split, for side-by-side panes. Did I mention that panes are great?
To move between panes in the current window, use `PREFIX <up,down,left,right>`. That's right, the arrow keys.
## more
I not the best writer or teacher. Just google anything that doesn't make sense.
[Or take a look at this tmux guide](http://robots.thoughtbot.com/a-tmux-crash-course)
But definitely use tmux.
Or, if you don't like it - try [screen](screen.html)

View File

@ -1,7 +1,6 @@
---
title: ttbp (feels)
author:
- benharri
title: ttbp / feels (blog platform)
author: benharri
category: software
---

View File

@ -1,6 +0,0 @@
---
title: ttrv
author: audiodude
category: software
---
*ttrv* (used to be RTV) is a terminal based Reddit viewer. Contribute to ttrv: [https://github.com/tildeclub/ttrv](https://github.com/tildeclub/ttrv)

77
wiki/source/vi.md Normal file
View File

@ -0,0 +1,77 @@
---
title: The Vi Editor
category: software
author:
- ant
---
*Tilde.Club* has
[The Traditional Vi](https://ex-vi.sourceforge.net/)
installed on its premises.
It is not just another
[Vi clone](https://texteditors.org/cgi-bin/wiki.pl?ViFamily),
but the direct continuation
of Bill Joy's legendary work at Berkeley.
The binaries are in `/usr/archaic/bin/`
and the man pages
(separately for `ex` and `vi`)
in `/usr/archaic/share/man/man/`.
You can invoke *The Traditional Vi* in several ways
(in the order of increased engagement):
1. by the full path to the executable:
/usr/archaic/bin/vi
2. by adding it as an alias to your shell's `rc` file
(`~/.bashrc` for *Bash*), e.g:
alias tvi=/usr/archaic/bin/vi
and then invokng *Vi* by typing `tvi`,
3. by adding the locations *Vi* and its documentation
in front of the `PATH` and `MANPATH` environment variables
in your shell's profile script
(for *Bash*, `~/.bash_profile` or `~/.profile`):
export PATH="/usr/archaic/bin/:$PATH"
export MANPATH="/usr/archaic/share/man/man:$MANPATH"
The latter method has the advantage
of affecting subshells,
so that if you specify `vi` as the default editor
in your e-mail or news client, or another CLI program,
it will invoke *The Traditional Vi*,
ditto for your shell scripts
and the `EDITOR` environment variale.
## Resources
1. Bill Joy's
[An Introduction to Display Editing with Vi ](https://ex-vi.sourceforge.net/viin/paper.html),
2. [`vi(1)`](https://ex-vi.sourceforge.net/vi.html) man page,
3. [`ex(1)`](https://ex-vi.sourceforge.net/ex.html) man page,
4. [A concise `vi` reference](http://www.ungerhu.com/jxh/vi.html),
5. [The Ultimate guide to the VI and EX text editors](https://archive.org/details/ultimateguidetov0000unse_i5e4) (a paper book),
6. The `#vi` channel on the
[Libera.Chat](https://libera.chat/)
IRC network,
dedicated to the original *Vi*
and all its variants except *Vim* & co,
7. [VI experience in the shell](https://deut-erium.github.io/2024/01/28/inputrc.html).
## Building
*The Traditional Vi* is surprisingly easy to build from
[its source](https://sourceforge.net/p/ex-vi/code/).
You only need to locate the following line in `Makefile`:
TERMLIB = termlib
and replace the value with `curses` or `ncurses`,
depending on your preferred terminal library.
Now you can build and install the project with:
make && make install

View File

@ -1,6 +1,6 @@
---
title: .vimrc file
category: tutorials
title: Vim (editor) .vimrc file
category: software
---
The file `.vimrc` in your home directory has instructions for [[vim]]

View File

@ -1,7 +1,7 @@
---
title: Don't use VPN services
author: joepie91 on github
category: tilde.club
category: tutorials
---
# Don't use VPN services.

View File

@ -83,3 +83,83 @@ You can do this right from the Github GUI!
Ask on irc if you have questions!
## Notes for the nerds
Following is technical information about Tilde.club website code repository
(which contains Tilde.club wiki),
for any of you who won't settle for an easy way out,
but would rather embark on a do-it-yourself journey
of technological liberation...
* The code of Tilde.club website (which includes the wiki)
is maintained using [Git version control system](https://git-scm.com/).
* The master Git repository is available inside Tilde.club at <q>`/usr/share/nginx/html`</q> directory,
with read-only access to Tilde.club members.
In case you prefer to work on your edits directly inside Tilde.club,
run a command like this to clone the repository:
git clone /usr/share/nginx/html tildeclub-site
If you prefer to clone to your local machine via SSH instead,
use **one** of the following commands:
git clone ssh://USERNAME@tilde.club/usr/share/nginx/html tildeclub-site
git clone USERNAME@tilde.club:/usr/share/nginx/html tildeclub-site
* Don't forget to substitute <var>USERNAME</var> with your Tilde.club username
(else SSH would fail logging you in for the access).
* The first command works even with a very old Git version.
Note that you cannot issue <q>`git push`</q> from a repository cloned these ways
(but <q>`git pull`</q> will work like usual),
so it would be suitable mainly for email-based patch submission,
or your private experiment/exploration.
* This Git repository is also officially available online at two places:
* [GitHub](https://github.com/tildeclub/site/)
(requires JavaScript and latest browser to view)
* [Tildegit](https://tildegit.org/club/site/)
(read-only mirror,
replicates from GitHub;
do not need JavaScript to view)
The <q>`git clone`</q> command also work directly with these URLs;
although note that pull requests are accepted only on GitHub.
If you prefer not to use GitHub,
email-based patch submission is also accepted.
* Traditional email-based patch submission
is accepted on email address `root` at `tilde.club`.
If your patches require a use of <q>`--scissors`</q> option on <q>`git am`</q> command,
please explicitly mention that on each patch email.
* The in-repository subdirectory that contains the actual wiki text
is <q>`wiki/source/`</q>.
* Wiki articles are stored in that directory as individual files:
one file per article,
in [Pandoc Markdown format](https://pandoc.org/MANUAL.html#pandocs-markdown)
with [YAML metadata](https://pandoc.org/MANUAL.html#extension-yaml_metadata_block) frontmatter at the top,
bearing file extension <q>`.md`</q>.
* The actual mechanism used
for rendering each Markdown files into actual HTML pages
on Tilde.club's website
(and text files on Tilde.club's Gopher site)
is specified in <q>`wiki/Makefile`</q> in the repository.
* The Gophermap of the wiki
is generated dynamically on Gopher request however;
see <q>`wiki/gophermap`</q> and <q>`wiki/txtlist.sh`</q> in the repository
for this part.
* The copyright license of this repository is
[GNU General Public License](https://www.gnu.org/licenses/gpl.html),
_any version_
published by the [Free Software Foundation](https://www.fsf.org/);
as stated in <q>`LICENSE`</q> file on the repository,
while the _any version_ part comes from the fallback condition
specified in the section 14 of that license.
If you submit your work to be a part of this wiki,
this is the copyright license that your work would get published under.
**Do not trust what GitHub says about the specifics
of licensing** on this repository
(or any other repository hosted there for that matter),
always check the <q>`README`</q>,
<q>`LICENSE`</q>,
and <q>`COPYING`</q> files for the fine prints.

View File

@ -14,10 +14,11 @@
<meta name="keywords" content="$for(keywords)$$keywords$$sep$, $endfor$" />
$endif$
<title>$if(title-prefix)$$title-prefix$ $endif$$pagetitle$</title>
<link rel="stylesheet" href="../style.css">
<link rel="stylesheet" href="https://tilde.club/style.css">
<link rel="icon" type="image/png" href="https://tilde.club/favicon.png">
<style type="text/css">
code{background: rgba(255, 187, 85, 0.15);padding: 0.1em 0.2em;border-radius: 0.3em;white-space: pre-wrap;}
pre code{background: none;}
pre code{background: none; padding: 0;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}