forked from TildeClub/site
Compare commits
487 Commits
gopher-wik
...
master
Author | SHA1 | Date |
---|---|---|
|
6dc5cd321f | |
|
a91c99303e | |
|
bbbaa2de7f | |
|
a9172b250c | |
|
c641e2fc9f | |
|
c119bdb9f6 | |
|
bb5886b477 | |
|
0ca98473ee | |
|
c664cbfb3b | |
|
3900ee74a3 | |
|
e4f3c051ef | |
|
7db1b1a6b9 | |
|
371f9efb7f | |
|
1eb933f5f2 | |
|
a98f9d0a52 | |
|
72b8203ad0 | |
|
636decf7a7 | |
|
f5d5189b73 | |
|
230def16bb | |
|
2db0e6c40a | |
|
f7aca73297 | |
|
ffe71acf2f | |
|
2205a20616 | |
|
deea4eb334 | |
|
49081bb620 | |
|
9c3b113086 | |
|
33c7d9881b | |
|
076f0368ab | |
|
9c999bf3c0 | |
|
584a596c2d | |
|
a1fba42dd3 | |
|
16a842533a | |
|
5e2e1d71bb | |
|
fafe495957 | |
|
2644575d17 | |
|
140320df02 | |
|
b6bad87eb8 | |
|
cfc138d4e6 | |
|
e8a70697b7 | |
|
9eda625290 | |
|
4ff9cec585 | |
|
f4bcd8f6bf | |
|
b0734298ec | |
|
d4491bcb28 | |
|
2f46cc83ab | |
|
703f2ae260 | |
|
f089b2e503 | |
|
6d81375778 | |
|
4efa5ba4ab | |
|
22551d20e0 | |
|
7f23984ab0 | |
|
9dd8e8b510 | |
|
920c032f36 | |
|
baf805493b | |
|
1b3381eb99 | |
|
a69e8a93bc | |
|
c3d8a83eed | |
|
cc381753a8 | |
|
1184c77a1f | |
|
ae5b9fc4f5 | |
|
0f0dea9069 | |
|
095dd7ea70 | |
|
54e0502922 | |
|
f106cbecb9 | |
|
6a342bcc7a | |
|
ff4e747196 | |
|
040c0bf64e | |
|
002ea7dfe7 | |
|
2f146ee57f | |
|
8abe14fb8c | |
|
dbba7245e9 | |
|
852506d7c8 | |
|
97edfa0fc1 | |
|
f1111429d8 | |
|
321c46c259 | |
|
0fa06feed7 | |
|
64c7616c08 | |
|
83ce0381f3 | |
|
5d9eec4133 | |
|
2487d6c49a | |
|
be0cca5d2c | |
|
4916f15249 | |
|
dffcd754c3 | |
|
33d0f6062c | |
|
fd964baa96 | |
|
6111c5185f | |
|
0e2d568080 | |
|
85305b357d | |
|
e5ac1ee1aa | |
|
0450221613 | |
|
8933f0a160 | |
|
3d686c3fb3 | |
|
b7696a75e6 | |
|
248ff69854 | |
|
fcf97d0279 | |
|
96a40f437c | |
|
79859e0037 | |
|
fee4fa5a6c | |
|
1f7214411d | |
|
25369a6f4d | |
|
81eeb53987 | |
|
dd62a18333 | |
|
3e3a512535 | |
|
3cdd5d6cb2 | |
|
b45ec60cc2 | |
|
cca8aeeb1d | |
|
d5f2563446 | |
|
cfb2128fd6 | |
|
c93fae31e1 | |
|
b113fe11a3 | |
|
d130b781cb | |
|
4c7113dd74 | |
|
bd21c4fd1a | |
|
8fe11a8dba | |
|
e860ab0bcc | |
|
258a8c1622 | |
|
f1c971e910 | |
|
4e6fd1e7ea | |
|
ad325ceeea | |
|
a470a27105 | |
|
acdf650fe5 | |
|
dfb3432664 | |
|
c97eeb3671 | |
|
3a4823243e | |
|
79e32499b5 | |
|
494811f60e | |
|
f0917cefc2 | |
|
cc20fea6e0 | |
|
9c86f971db | |
|
2302e282e8 | |
|
20afc77fd8 | |
|
110fbd7365 | |
|
9534c7e34c | |
|
825af838dd | |
|
420e0d7cf2 | |
|
cf5b0526d6 | |
|
11c0102a83 | |
|
cfeb303b97 | |
|
69087be669 | |
|
f17390d706 | |
|
3ff750240d | |
|
23039ce7e2 | |
|
90c4f3d500 | |
|
66087b1db3 | |
|
f1543bcada | |
|
1ca4308a44 | |
|
ce11671717 | |
|
f92b12f8eb | |
|
a997631cc6 | |
|
e40c9bea2d | |
|
046d37a355 | |
|
10bdfa43cb | |
|
cc540cd077 | |
|
5b2c36a19e | |
|
8b16a1ca02 | |
|
4694bedc07 | |
|
55af3e13a5 | |
|
7a622eee6d | |
|
d73b120723 | |
|
1f01b44d2a | |
|
d923942f34 | |
|
559820b906 | |
|
e9fdc3f345 | |
|
bee9ced345 | |
|
fb4c3033a7 | |
|
bb38fcaf12 | |
|
d6c8159496 | |
|
84cb556779 | |
|
0b3bd2a8f1 | |
|
3537c30a48 | |
|
5f2dee4000 | |
|
cc913bad6d | |
|
cea587a65a | |
|
b1598d1c75 | |
|
dc7e57c915 | |
|
2c12d1a77a | |
|
ca8a29713d | |
|
181e288b4e | |
|
0e6595dc42 | |
|
ce6b4937ae | |
|
8ba9569838 | |
|
3c2fbbc0b3 | |
|
23f73e6094 | |
|
170c35bc76 | |
|
abb7f6537b | |
|
f436a4464d | |
|
dc6e03bb09 | |
|
ee0ee935cd | |
|
3591280968 | |
|
672b2e868f | |
|
904b5dd616 | |
|
2e17a21be7 | |
|
9503117dcb | |
|
3c67a32620 | |
|
ffd4b1278e | |
|
f63ab8a9b5 | |
|
217b00b6e6 | |
|
dcbd902aaa | |
|
4cc538c1be | |
|
c115370510 | |
|
8c0ccfb1fb | |
|
7cfb3df2fe | |
|
d37b017d26 | |
|
5ff71c8ee4 | |
|
229282b8bc | |
|
f68dbb8dad | |
|
436a87e234 | |
|
bbfc51b8cb | |
|
7d056e8428 | |
|
058d3662a7 | |
|
4c45bb9f27 | |
|
230e1dfcba | |
|
ff9a2c6b54 | |
|
f458071c8a | |
|
350821e3e6 | |
|
cf5897d00a | |
|
ac64e5cd44 | |
|
9a0236f48d | |
|
a8cddbf166 | |
|
f830ea1910 | |
|
cd5ff0bd10 | |
|
0aa5647e11 | |
|
b8959d78fb | |
|
beeedf2707 | |
|
3c7057a017 | |
|
f9ae044792 | |
|
ccf2ce5d15 | |
|
e2d7f8c402 | |
|
36e5cb435e | |
|
5748695869 | |
|
f9874a3996 | |
|
528313c248 | |
|
21cce97e87 | |
|
f7e9ee87ae | |
|
c9627b88cc | |
|
f1e9999e64 | |
|
052039715a | |
|
1f1943cc56 | |
|
365ebab2ef | |
|
24df7d9359 | |
|
78f76ed7d9 | |
|
b99dcf55bb | |
|
57451374c9 | |
|
c4b19b660d | |
|
fcb7fa2baf | |
|
69278ec8f1 | |
|
c0ba435972 | |
|
89b0bbbbcd | |
|
ca7b44c5a7 | |
|
1733ea82c4 | |
|
ffe3db4241 | |
|
07dd1e93f4 | |
|
3edcc92bd4 | |
|
ea9fcac4ce | |
|
5d2829d3ad | |
|
830f26f26c | |
|
a197ed6e54 | |
|
76399a5d49 | |
|
71b4b4ff29 | |
|
da8f2e95e1 | |
|
ddb0688d51 | |
|
531a955157 | |
|
b47dbaec0c | |
|
1b79fa9c6e | |
|
232d64a5f5 | |
|
6e704f42ed | |
|
f97a2c53a7 | |
|
3948a9b8c4 | |
|
f818c4551a | |
|
88215c5320 | |
|
b694a95025 | |
|
811cbe823c | |
|
0e1fc20ec4 | |
|
a713a870d1 | |
|
cc8b805f25 | |
|
98145a7ee8 | |
|
991da8a094 | |
|
2abf088ecf | |
|
b1a555f1f8 | |
|
5cef45d2fc | |
|
5fd70335bf | |
|
6ef9126f91 | |
|
acc7627e49 | |
|
f002398b26 | |
|
f4eda52746 | |
|
555e3d65ad | |
|
fa398d6a7a | |
|
c968045fd7 | |
|
de0da1bcce | |
|
6cf3fecb9f | |
|
786b4389dd | |
|
a1460cc178 | |
|
d9d4fb013a | |
|
3ceb0b7b34 | |
|
7fe3e218bf | |
|
285512b896 | |
|
10b9f7aa9f | |
|
c1fbaf11c0 | |
|
20ecbcdf65 | |
|
c2c60392aa | |
|
5922eac9c3 | |
|
b366804d93 | |
|
b1b3474faf | |
|
d43a949c69 | |
|
c8ec235139 | |
|
131ef581fd | |
|
476c7bc8e6 | |
|
1cadcbbac1 | |
|
7d82a258c7 | |
|
d28b3c4041 | |
|
85451e5e84 | |
|
799448d087 | |
|
e3d3b950d1 | |
|
41263e0d0e | |
|
d2b3409a82 | |
|
9575d1738a | |
|
a9252d26c6 | |
|
ec8c5d8be1 | |
|
0f97a11748 | |
|
7147849f2e | |
|
c0fb945103 | |
|
1845217fe0 | |
|
a6bfe84dd8 | |
|
e87a132fcb | |
|
e9e6c75915 | |
|
6a826f3d6a | |
|
40b47b816d | |
|
045c7132c6 | |
|
7a5604ddb8 | |
|
0fb3e4db57 | |
|
e52ead3ab4 | |
|
2f373307f0 | |
|
554ca3fcf3 | |
|
910bd5807a | |
|
7a6a0f9374 | |
|
9b912b33dc | |
|
e3429a03d9 | |
|
8acb09b1f8 | |
|
030ea0aeef | |
|
0ffeb101ee | |
|
4507694dca | |
|
93d5f7ebc8 | |
|
2ac28802ee | |
|
abf69eacb1 | |
|
78e3493054 | |
|
ef146a7a27 | |
|
3468b12cde | |
|
baf4e1a692 | |
|
408db756c0 | |
|
84d5205059 | |
|
f365c3db0f | |
|
2eda52d876 | |
|
b094e93466 | |
|
9275d4b1bf | |
|
372c8df49d | |
|
26366dd1db | |
|
f77eefb083 | |
|
28e3c8a2f2 | |
|
3598808f09 | |
|
6d3ffd93f2 | |
|
eb376fcba4 | |
|
c28982bc41 | |
|
1a103abf98 | |
|
5bcd2868a0 | |
|
7651950983 | |
|
637d2d20b7 | |
|
f9d46b2da8 | |
|
27ae8998f8 | |
|
c96e8d8b85 | |
|
9a4c49e270 | |
|
f48fec3c15 | |
|
547eacf23c | |
|
49cc14ccc0 | |
|
d25a85278e | |
|
4bd19a0892 | |
|
e23a565b39 | |
|
cc7b5b5b10 | |
|
09459d976d | |
|
09f10320d6 | |
|
b357b914d4 | |
|
2229da26b1 | |
|
73573a1e0d | |
|
f8797657bf | |
|
caee5995f9 | |
|
d5e591a5f2 | |
|
f8c31d1bf7 | |
|
47c58ec3a6 | |
|
6211554085 | |
|
ad6f8591be | |
|
169611a891 | |
|
14b472423a | |
|
39b7d541e8 | |
|
12fb8c9768 | |
|
3db8d6f26a | |
|
4b45421b4a | |
|
2b79fa2539 | |
|
42fd9c3072 | |
|
74a172f516 | |
|
ed41642cd1 | |
|
bbd98ad620 | |
|
ea4fc79b0a | |
|
1a82b54315 | |
|
9491e5e137 | |
|
13c3805574 | |
|
d004a084e5 | |
|
0528cf5e4f | |
|
606e81d51d | |
|
187716ea40 | |
|
e94fff6678 | |
|
10e310d1db | |
|
a053206c14 | |
|
5497b4ef1e | |
|
534ad3c7ae | |
|
8c44115246 | |
|
515fd498c6 | |
|
6d658ce165 | |
|
c2ccf37fec | |
|
53bbf534ac | |
|
e19cbebd47 | |
|
021c076637 | |
|
e57947f0df | |
|
3f213acbc8 | |
|
3f63a2d0a1 | |
|
e8b4c9fd44 | |
|
e20df823fb | |
|
68c477359b | |
|
70ab8b4c66 | |
|
d24d6a78fc | |
|
b19a2d0ea7 | |
|
c8ebf3e576 | |
|
d0f19b67e8 | |
|
e3df53c327 | |
|
af792ca86d | |
|
e6f9e3f583 | |
|
1feb93cc5b | |
|
67d32bb755 | |
|
16fe603ab5 | |
|
67406ca983 | |
|
d190cd5542 | |
|
7342884706 | |
|
443011bfd2 | |
|
6f92582412 | |
|
cd09e67d8b | |
|
913784f9e4 | |
|
ada623f87c | |
|
e0a2d09d7d | |
|
ea697b92ff | |
|
d66ad4d2f3 | |
|
2a38e4ad1a | |
|
3096a6f69a | |
|
6711e6a3f9 | |
|
970cd6262c | |
|
d4443f6f56 | |
|
ac2ec8d199 | |
|
779ad50b58 | |
|
84eb156935 | |
|
13331aae80 | |
|
2b05b8592c | |
|
10516a8223 | |
|
02cbcd3cc8 | |
|
ba095bd4ce | |
|
c1cf0f1483 | |
|
bb85714176 | |
|
6674ec4b17 | |
|
d3c6f2eb8f | |
|
1c3a70e5fa | |
|
7caf0e8652 | |
|
e77db2b119 | |
|
1e5bc33e82 | |
|
73931e2f7f | |
|
c3f604cf97 | |
|
75b9effc30 | |
|
219abf12d9 | |
|
051ab77597 | |
|
bb0738cee3 | |
|
2344be758d | |
|
c8179d140c | |
|
809378f864 | |
|
0c463fdeda | |
|
e7f092ac05 | |
|
67574bdb60 | |
|
9f7c330162 | |
|
e380614a2c | |
|
c1b8ef873c | |
|
14350822e6 | |
|
73b524a46e | |
|
a9b2aed6b3 |
|
@ -21,10 +21,8 @@ trigger:
|
|||
steps:
|
||||
- name: deploy
|
||||
commands:
|
||||
- cd /usr/share/nginx/html
|
||||
- git pull origin master
|
||||
- git -C /usr/share/nginx/html pull --rebase origin master
|
||||
- name: build-wiki
|
||||
commands:
|
||||
- cd /usr/share/nginx/html
|
||||
- make -C wiki
|
||||
- make -C /usr/share/nginx/html/wiki
|
||||
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: tilde.club # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
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
|
||||
|
|
|
@ -1 +1,15 @@
|
|||
.gitignore
|
||||
header24h.php
|
||||
nginx-logo.png
|
||||
pkglist.txt
|
||||
poweredby.png
|
||||
social.html
|
||||
tilde.24h*
|
||||
tilde.json
|
||||
online-users.json
|
||||
changes.rss
|
||||
news/
|
||||
icons/
|
||||
stats/
|
||||
cache/
|
||||
polls/polls.db
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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>
|
|
@ -0,0 +1,4 @@
|
|||
This is a Brave Rewards publisher verification file.
|
||||
|
||||
Domain: tilde.club
|
||||
Token: 9c786faeb68dcbed2a1c7ce23ed1674e1bc910765da27327c53bbf58bcf812fc
|
7
404.php
7
404.php
|
@ -1,7 +0,0 @@
|
|||
<?php include "header.php"; ?>
|
||||
|
||||
<h1>$ ERROR 404</h1>
|
||||
|
||||
|
||||
|
||||
<?php include "footer.php"; ?>
|
|
@ -0,0 +1,19 @@
|
|||
<!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>
|
16
README.md
16
README.md
|
@ -1,2 +1,18 @@
|
|||
# site
|
||||
tilde.club site source
|
||||
|
||||
# Help Wanted
|
||||
Tilde.club is moving very quickly and is 100% volunteer led.
|
||||
|
||||
## 1. optimal: pull requests and bug reports
|
||||
- There is a lot of great work going on around config, FAQ, basic docs.
|
||||
- Jump in if you want. Literally everyone here said "I am ready to help, what can I do."
|
||||
- We need non-coders (writers, editors, communicators, artists, project managers) as much as we need tech folks.
|
||||
- Most of the coding work is done for us by the Unix operating system. We're celebrating that.
|
||||
- A lot of the basic docs are in the [wiki](https://github.com/tildeclub/tilde.club/wiki) which you (yes, you) can edit.
|
||||
|
||||
## 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)
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report (suspected) security vulnerabilities to security@tilde.club. You will receive a response from us within 48 hours.
|
||||
If the issue is confirmed, we will release a patch as soon as possible depending on complexity.
|
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
|
@ -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"; ?>
|
|
@ -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 don’t 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"; ?>
|
|
@ -0,0 +1,23 @@
|
|||
<?php include "../header.php"; ?>
|
||||
|
||||
<h1 id="fancyboi">ERROR 404 Not Found</h1>
|
||||
|
||||
<div class="grid">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
<p>A 404 error status implies that the file or page that you're looking for could not be found.</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"; ?>
|
||||
|
|
@ -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"; ?>
|
|
@ -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 client’s 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"; ?>
|
|
@ -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"; ?>
|
|
@ -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"; ?>
|
|
@ -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"; ?>
|
Binary file not shown.
After Width: | Height: | Size: 531 B |
26
footer.php
26
footer.php
|
@ -1,9 +1,19 @@
|
|||
|
||||
</div>
|
||||
<br>
|
||||
<!-- Tildeverse Banner code begin -->
|
||||
<div style="text-align: center;"><object data="https://banner.tildeverse.org/work.php?ID=deepend" type="text/html" width="500" height="80" style="overflow:hidden; width: 500px; height: 80px"></object></div>
|
||||
<!-- Tildeverse Banner code end -->
|
||||
</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>
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env python3
|
||||
import glob, subprocess, json, os.path, requests
|
||||
from bs4 import BeautifulSoup as bs
|
||||
|
||||
tdp = {}
|
||||
tdp["name"] = "tilde.club"
|
||||
tdp["url"] = "https://tilde.club"
|
||||
tdp["signup_url"] = tdp["url"] + "/signup/"
|
||||
users = sorted([u[len("/home/"):] for u in glob.glob("/home/*", recursive=False)])
|
||||
tdp["user_count"] = len(users)
|
||||
tdp["want_users"] = True
|
||||
tdp["admin_email"] = "root@tilde.club"
|
||||
tdp["description"] = "the original 'tilde': 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"
|
||||
|
||||
tdpusers = []
|
||||
for user in users:
|
||||
tdpuser = dict(username=user)
|
||||
|
||||
try:
|
||||
title = bs(
|
||||
requests.get(f"http://tilde.club/~{user}/", allow_redirects=False).text,
|
||||
"lxml", features='xml'
|
||||
).title.text
|
||||
except:
|
||||
title = "No title"
|
||||
tdpuser["title"] = title
|
||||
|
||||
if os.path.exists(f"/home/{user}/public_html/index.html"):
|
||||
tdpuser["mtime"] = os.path.getmtime(f"/home/{user}/public_html/index.html")
|
||||
elif os.path.exists(f"/home/{user}/public_html/index.php"):
|
||||
tdpuser["mtime"] = os.path.getmtime(f"/home/{user}/public_html/index.php")
|
||||
|
||||
tdpusers.append(tdpuser)
|
||||
|
||||
tdp["users"] = tdpusers
|
||||
|
||||
with open("tilde.json", "w") as f:
|
||||
json.dump(tdp, f)
|
41
header.php
41
header.php
|
@ -1,21 +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>
|
||||
<div id="navbar">
|
||||
<a href="/">~TILDE.CLUB~</a>
|
||||
<a href="/">HOME</a>
|
||||
<a href="/wiki/">WIKI</a>
|
||||
<a href="/wiki/donate.html">DONATE</a>
|
||||
<a href="https://web.tilde.chat/?join=club" target="_blank">CHAT</a>
|
||||
<a href="https://search.tildeverse.org/" target="_blank">SEARCH</a>
|
||||
<a href="/signup/">SIGN-UP</a>
|
||||
|
||||
</div>
|
||||
<div class="content">
|
||||
<?php include "nav.html"; ?>
|
||||
<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>
|
||||
|
|
|
@ -12,19 +12,24 @@
|
|||
<a href="/">~TILDE.CLUB~</a>
|
||||
<a href="/">HOME</a>
|
||||
<a href="/wiki/">WIKI</a>
|
||||
<a href="/wiki/donate.html">DONATE</a>
|
||||
<a href="https://web.tilde.chat/?join=club" target="_blank">CHAT</a>
|
||||
<a href="https://search.tildeverse.org/" target="_blank">SEARCH</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>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<h1 id="fancyboi">recently updated</h1>
|
||||
|
||||
<div class="grid">
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
|
||||
<h2>tilde.club home pages updated in last 24 hours</h2>
|
||||
<div>
|
||||
|
||||
There's also <a href="./tilde.24h.json">a JSON version of this data</a>; it's all updated once a minute, so hold yer damn horses, people. Also, times are in the server's time zone (GMT, it appears).<br>
|
||||
<br>
|
||||
This script is by <a href="http://tilde.club/~delfuego/" target="_blank">~delfuego</a>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../../../pixmaps/poweredby.png
|
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
367
index.php
367
index.php
|
@ -1,119 +1,272 @@
|
|||
<?php include "header.php"; ?>
|
||||
<?php
|
||||
include "header.php";
|
||||
|
||||
<h1>$ 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><a href="/wiki/faq.html">Questions? See the official FAQ.</a></p>
|
||||
|
||||
<div class="grid">
|
||||
<div class="row">
|
||||
<!-- <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);
|
||||
|
||||
<div class="col">
|
||||
<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>
|
||||
<hr>
|
||||
<h2>UPDATE: September 2019:</h2>
|
||||
<p>
|
||||
tilde.club is back!! as of september 20th, we're now accepting
|
||||
signups and operating as normal!
|
||||
</p>
|
||||
<p>
|
||||
your new admins are: <a href="/~deepend/">~deepend</a> and
|
||||
<a href="/~benharri/">~benharri</a>. if you need anything,
|
||||
reach out via email (root AT tilde DOT club) or on <a href="/wiki/chat.html#irc">irc</a>.
|
||||
</p>
|
||||
<p>so what's new?</p>
|
||||
<ul>
|
||||
<li>~club is joining the <a href="https://tildeverse.org">
|
||||
tildeverse</a>, a collective of like-minded tilde communities with a <a href="https://tilde.chat">shared irc network</a></li>
|
||||
<li>we now have a <a href="https://lists.tildeverse.org/postorius/lists/tildeclub.lists.tildeverse.org/">mailing list</a> and a full-fledged <a href="/wiki/email.html">mailserver</a>. new users are automatically subscribed to the list with their @tilde.club address.</li>
|
||||
<li>we're on a new vm with more ram and disk space to grow. check the expected ssh hostkey at the top of the page.</li>
|
||||
<li>to keep up with updates, stop by irc (via <a href="https://web.tilde.chat/?join=club">webchat</a> or at your shell by running "chat"), check the mailing list, or follow our projects on our <a href="https://github.com/tildeclub">github org</a></li>
|
||||
<li>most of the info from previous wiki iterations are now consolidated on our new <a href="/wiki/">wiki</a></li>
|
||||
<li>we have a new donation page set up on <a href="https://liberapay.com/tilde.club">liberapay</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
if (!empty($activeUsers))
|
||||
{
|
||||
echo '<marquee scrollamount="2">';
|
||||
|
||||
<h2>tilde.club gold star supporters</h2>
|
||||
foreach ($activeUsers as $user)
|
||||
{
|
||||
$username = htmlspecialchars($user);
|
||||
echo "<a href='/~$username'>$username</a> ";
|
||||
}
|
||||
|
||||
<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>
|
||||
echo '</marquee>';
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "<li>No active users at the moment.</li>";
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="row">
|
||||
|
||||
<ul>
|
||||
<li>4/21/2020 | <a href="/~cano">~cano</a></li>
|
||||
<li>11/9/2019 | <a href="/~sneak">~sneak</a></li>
|
||||
<li>10/5/2014 | <a href="/~beau">~beau</a></li>
|
||||
<li>10/5/2014 | <a href="/~skk">~skk</a></li>
|
||||
<li>10/5/2014 | <a href="/~joeld">~joeld</a></li>
|
||||
<li>10/5/2014 | <a href="/~john">~john</a></li>
|
||||
<li>10/5/2014 | <a href="/~brendn">~brendn</a></li>
|
||||
<li>10/5/2014 | <a href="/~droob">~droob</a></li>
|
||||
<li>10/5/2014 | <a href="/~delfuego">~delfuego</a></li>
|
||||
<li>10/5/2014 | <a href="/~jonathan">~jonathan</a></li>
|
||||
<li>10/5/2014 | <a href="/~coldmode">~coldmode</a></li>
|
||||
<li>10/5/2014 | <a href="/~jemal">~jemal</a></li>
|
||||
<li>10/5/2014 | <a href="/~jonbell">~jonbell</a></li>
|
||||
<li>10/5/2014 | <a href="/~_">~_</a></li>
|
||||
<li>10/5/2014 | <a href="/~dvd">~dvd</a></li>
|
||||
<li>10/5/2014 | <a href="/~whitneymcn">~whitneymcn</a></li>
|
||||
<li>10/5/2014 | <a href="/~jimray">~jimray</a></li>
|
||||
<li>10/5/2014 | <a href="/~schussat">~schussat</a></li>
|
||||
<li>10/5/2014 | <a href="/~macdiva">~macdiva</a></li>
|
||||
<li>10/3/2014 | <a href="/~extraface">~extraface</a></li>
|
||||
<li>10/3/2014 | <a href="/~joshuag">~joshuag</a></li>
|
||||
<li>10/3/2014 | <a href="/~zarate">~zarate</a></li>
|
||||
<li>10/3/2014 | <a href="/~englishm">~englishm</a></li>
|
||||
<li>10/3/2014 | <a href="/~danbri">~danbri</a></li>
|
||||
</ul>
|
||||
</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
|
||||
</p>
|
||||
<div id="news" class="col">
|
||||
<?php
|
||||
$news = json_decode(file_get_contents('news.json'), true);
|
||||
|
||||
<p>
|
||||
If you would like a list of <a href="http://tilde.club/tilde.24h.php">
|
||||
RECENTLY CHANGED PAGES</a> you can see that too
|
||||
</p>
|
||||
// Sort news items by date, most recent first
|
||||
usort($news, function ($a, $b) {
|
||||
return strtotime($b['date']) - strtotime($a['date']);
|
||||
});
|
||||
|
||||
<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>
|
||||
// Determine the year filter if present
|
||||
$selectedYear = isset($_GET['year']) ? $_GET['year'] : null;
|
||||
$filteredNews = [];
|
||||
$currentDate = new DateTime();
|
||||
|
||||
<ol>
|
||||
<?php foreach (glob("/home/*") as $user) {
|
||||
$index = "$user/public_html/index.html";
|
||||
if (!file_exists($index) ||
|
||||
in_array(sha1_file($index),
|
||||
// these are the hashes of previous and current default pages
|
||||
["0eb53dab435e2e6e401921146bed85a80e9ad3a1",
|
||||
"61eff8202777bae134ac4b11f1e16ec23dfc97d3",
|
||||
"e9d41eab6edb7cd375c63ecb4a23bca928992547",
|
||||
"cb2ce535ab34edebc225e88a321f972ba55763c3",
|
||||
"13af6898f536265af7dbbe2935b591f5e2ee0d7d"])) continue;
|
||||
$user = basename($user); ?>
|
||||
<li><a href="/~<?=$user?>/">~<?=$user?></a></li>
|
||||
<?php } ?>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
foreach ($news as $newsItem) {
|
||||
$newsDate = new DateTime($newsItem['date']);
|
||||
|
||||
<p>big kudos and thanks to the people who built the original tilde.club!</p>
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
<?php include "footer.php"; ?>
|
||||
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 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>
|
||||
If you would like a list of <a href="http://tilde.club/tilde.24h.php">
|
||||
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>
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
<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>
|
||||
<?php
|
||||
$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";
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<div id="navbar">
|
||||
<a href="/">~TILDE.CLUB~</a>
|
||||
<a href="/">HOME</a>
|
||||
<a href="/wiki/">WIKI</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>
|
|
@ -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 – You’ll 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 won’t be able to save more files until you clean up.",
|
||||
"Grace Period: 1 week – If you go over the soft limit, you’ll have a week to get back under before things get strict."
|
||||
],
|
||||
"additional_content": "You can check your usage and see how much space you’ve got left by running the <code>resources-used</code> script in your home directory. It’s 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."
|
||||
]
|
||||
}
|
||||
]
|
|
@ -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"; ?>
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
?>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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,
|
||||
|
@ -1283,7 +1280,7 @@
|
|||
{
|
||||
if (trim($line) !== "")
|
||||
{
|
||||
if ($line{0} !== "\xFF" && (($tagname === "/ul" && $line{0} !== "-") || ($tagname === "/ol" && !(int)$line{0}))) $prefix2 = "\xFF\xFF";
|
||||
if ($line[0] !== "\xFF" && (($tagname === "/ul" && $line[0] !== "-") || ($tagname === "/ol" && !(int)$line[0]))) $prefix2 = "\xFF\xFF";
|
||||
else $prefix2 = "";
|
||||
|
||||
$lines[$num] = $prefix . $prefix2 . trim($line);
|
||||
|
@ -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 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -1,37 +1,56 @@
|
|||
<?php
|
||||
$title = "sign up for the tilde.club!";
|
||||
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>sign up to join tilde.club</h1>
|
||||
|
||||
<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'; ?>
|
||||
<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>
|
||||
<form method="post">
|
||||
<div>
|
||||
<p>your desired username (numbers and lowercase letters only, no spaces)</p>
|
||||
<input class="form-control" name="username" value="<?=$_REQUEST["username"] ?? ""?>" type="text" required>
|
||||
<input class="form-control" name="username" value="<?=esc("username")?>" type="text" required>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>email to contact you with account info</p>
|
||||
<input class="form-control" name="email" value="<?=$_REQUEST["email"] ?? ""?>" type="text" required>
|
||||
<input class="form-control" name="email" value="<?=esc("email")?>" type="text" required>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>what interests you about tilde.club? we want to make sure you're a real human being :)</p>
|
||||
<textarea required class="form-control" name="interest" id="" cols="40" rows="7"><?=$_REQUEST["interest"] ?? ""?></textarea>
|
||||
<textarea required class="form-control" name="interest" id="" cols="40" rows="7"><?=esc("interest")?></textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>SSH public key</p>
|
||||
<textarea required class="form-control" name="sshkey" id="" cols="40" rows="10"><?=$_REQUEST["sshkey"] ?? ""?></textarea>
|
||||
<textarea required class="form-control" name="sshkey" id="" cols="40" rows="10"><?=esc("sshkey")?></textarea>
|
||||
<p>if you don't have a key, don't worry! <a href="https://tilde.club/wiki/ssh.html">check out our guide to ssh keys</a> and make sure that you only put your pubkey here</p>
|
||||
</div>
|
||||
|
||||
|
@ -40,8 +59,9 @@ include __DIR__."/../header.php";
|
|||
<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>
|
||||
|
||||
|
@ -49,4 +69,7 @@ include __DIR__."/../header.php";
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include __DIR__."/../footer.php";
|
||||
|
|
|
@ -1,8 +1,51 @@
|
|||
<?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',
|
||||
'admin',
|
||||
|
@ -55,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"]);
|
||||
|
@ -92,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"]}
|
||||
|
@ -109,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
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
|
|
399
style.css
399
style.css
|
@ -1,51 +1,102 @@
|
|||
/* {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: 30em) {
|
||||
.row { display: flex; flex-wrap: nowrap; flex-direction: row-reverse; }
|
||||
.col { flex-grow: 1; }
|
||||
@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;
|
||||
}
|
||||
|
||||
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 {
|
||||
border-collapse: collapse;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.col {
|
||||
border: 6px double #fb5;
|
||||
padding: 1em;
|
||||
border: 6px double #fb5;
|
||||
padding: 1em;
|
||||
flex-grow: 1;
|
||||
}
|
||||
ol {
|
||||
margin-left: 1em;
|
||||
|
||||
/* THEGOODS */
|
||||
#fancyboi::before {
|
||||
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;
|
||||
}
|
||||
.advisory {
|
||||
background: #fc4;
|
||||
color: #222;
|
||||
font-weight: bold;
|
||||
padding: 1em;
|
||||
border-radius: 0.25em;
|
||||
}
|
||||
h1 {
|
||||
text-transform: uppercase;
|
||||
color: #fb5;
|
||||
font-family: monospace;
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
@keyframes flash {
|
||||
50% { opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes reveal {
|
||||
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 */
|
||||
|
||||
.sourceCode {
|
||||
color:#008000;
|
||||
|
@ -57,52 +108,266 @@ code > span.fl {
|
|||
|
||||
/* Style the navbar */
|
||||
#navbar {
|
||||
overflow: hidden;
|
||||
background-color: #fb5;
|
||||
color: #222;
|
||||
z-index: 99;
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
border-radius: 0.25em;
|
||||
overflow: hidden;
|
||||
background-color: #fb5;
|
||||
color: #222;
|
||||
z-index: 99;
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
border-radius: 0.25em;
|
||||
}
|
||||
|
||||
#navbar a:hover {
|
||||
background-color: #f93;
|
||||
background-color: #f93;
|
||||
}
|
||||
|
||||
/* Navbar links */
|
||||
/* Navbar linkss */
|
||||
#navbar a {
|
||||
float: left;
|
||||
display: block;
|
||||
color: black;
|
||||
text-align: center;
|
||||
padding: 14px;
|
||||
text-decoration: none;
|
||||
float: left;
|
||||
display: block;
|
||||
color: black;
|
||||
text-align: center;
|
||||
padding: 14px;
|
||||
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"],
|
||||
textarea {
|
||||
background-color: #333;
|
||||
color: darkorange;
|
||||
background-color: #333;
|
||||
color: darkorange;
|
||||
}
|
||||
|
||||
div.alert-warning {
|
||||
background-color: darkred;
|
||||
background-color: darkred;
|
||||
}
|
||||
div.alert-success {
|
||||
background-color: darkgreen;
|
||||
background-color: darkgreen;
|
||||
}
|
||||
|
||||
/* Mobile */
|
||||
@media (max-width: 768px) {
|
||||
td {
|
||||
display: block;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"}
|
||||
]
|
|
@ -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);
|
||||
?>
|
|
@ -1,25 +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>full user list</h1>
|
||||
|
||||
<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>
|
||||
|
||||
<ol>
|
||||
<?php foreach (glob("/home/*") as $user) {
|
||||
$user = basename($user); ?>
|
||||
<li><a href="/~<?=$user?>/">~<?=$user?></a></li>
|
||||
<?php } ?>
|
||||
</ol>
|
||||
|
||||
<?php
|
||||
include __DIR__.'/../footer.php';
|
||||
|
||||
<h1 id="fancyboi">Tilde.club user list</h1>
|
||||
<div class="grid">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<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';
|
||||
|
|
|
@ -14,6 +14,7 @@ index: dep-php index.html
|
|||
$(info building $@)
|
||||
@$(PANDOC) \
|
||||
--from markdown+backtick_code_blocks \
|
||||
--include-before-body=../nav.html \
|
||||
--template wiki.tmpl \
|
||||
--lua-filter header-permalinks.lua \
|
||||
--highlight-style=custom.theme \
|
||||
|
@ -36,8 +37,7 @@ index: dep-php index.html
|
|||
|
||||
clean:
|
||||
$(info removing generated files)
|
||||
-rm $(DST_HTML_FILES)
|
||||
-rm $(DST_TXT_FILES)
|
||||
-rm *.html *.txt
|
||||
|
||||
dep-pandoc::
|
||||
ifndef PANDOC
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
!tilde.club wiki
|
||||
|
||||
welcome to the gopher portal of the tilde.club wiki
|
||||
|
||||
it's also available on the web at https://tilde.club/wiki/
|
||||
|
||||
here are the articles:
|
||||
|
||||
=./txtlist.sh
|
||||
|
||||
1back to tilde.club /
|
|
@ -2,43 +2,59 @@
|
|||
$title = "tilde.club wiki";
|
||||
include __DIR__."/../header.php";
|
||||
?>
|
||||
|
||||
<h1>the tilde.club wiki</h1>
|
||||
|
||||
<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>
|
||||
<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
|
||||
$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);
|
||||
<?php
|
||||
// category order
|
||||
$order = ['tilde.club', 'tutorials', 'software', 'links'];
|
||||
|
||||
$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) { ?>
|
||||
<li><a href="/wiki/<?=$name?>.html"><?=$title?></a></li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
|
||||
foreach ($name_to_title as $name => $title)
|
||||
echo '<li><a href="/wiki/'.$name.'.html">'.$title.'</a></li>';
|
||||
|
||||
echo '</ul><br>';
|
||||
|
||||
<?php } ?>
|
||||
</ul>
|
||||
|
||||
<?php include __DIR__."/../footer.php";
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<?php include "../footer.php";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: USING Two-Factor Authentication (2FA) ON TILDE.CLUB
|
||||
title: Using Two-Factor Authentication (2FA) on Tilde.club
|
||||
author: deepend
|
||||
category: tilde.club
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
# Using Two-Factor Authentication (2FA).
|
||||
|
@ -60,11 +60,26 @@ Now you're configured. You can now login without an SSH key. Make sure you
|
|||
know your account password because login using two-factor authentication will
|
||||
still require your password before letting you in.
|
||||
|
||||
|
||||
# Two-Factor Authentication (2FA) on Tilde.Club Webmail
|
||||
|
||||
You can now enable 2FA in our webmail that is offered to users.
|
||||
|
||||
1. Log into the RainLoop user interface at:
|
||||
[https://webmail.tilde.club](https://webmail.tilde.club)
|
||||
|
||||
2. After your logged in you can go directly to:
|
||||
[https://webmail.tilde.club/#/settings/security](https://webmail.tilde.club/#/settings/security)
|
||||
|
||||
3. There should be a link titled "Configurate 2-Step Authentication".
|
||||
Click it, and click the Activate button that appears.
|
||||
|
||||
|
||||
# 2FA Mobile Applications
|
||||
|
||||
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)
|
||||
|
||||
|
@ -72,6 +87,8 @@ still require your password before letting you in.
|
|||
|
||||
5. [Aegis Authenticator](https://getaegis.app/)
|
||||
|
||||
6. [Tofu for IOS](https://www.tofuauth.com/)
|
||||
6. [andOTP](https://github.com/andOTP/andOTP)
|
||||
|
||||
7. [Tofu for IOS](https://www.tofuauth.com/)
|
||||
|
||||
More suggestions welcome.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
title: bashblog
|
||||
title: bashblog (blog platform)
|
||||
author:
|
||||
- deepend
|
||||
- benharri
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: BBJ (Bulletin Butter & Jelly)
|
||||
author: audiodude
|
||||
category: software
|
||||
---
|
||||
bbj: bulletin butter and jelly, a cozy bbs in your terminal
|
|
@ -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
|
||||
—including how to program it _safely_ and _defensively_—
|
||||
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.
|
|
@ -0,0 +1,125 @@
|
|||
---
|
||||
title: using cgit on tilde.club
|
||||
author: deepend
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
`cgit` gives every tilde.club member a simple, read‑only 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 quick‑start plus a few tips.
|
||||
|
||||
---
|
||||
|
||||
## 1 Create 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.
|
||||
|
||||
---
|
||||
|
||||
## 2 Add a repository
|
||||
|
||||
Only **bare** repos are accepted (they have no working tree inside them).
|
||||
|
||||
```bash
|
||||
# create a brand‑new 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 mirror‑push 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`.
|
||||
|
||||
---
|
||||
|
||||
## 3 Browse 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.
|
||||
|
||||
---
|
||||
|
||||
## 4 Update 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)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5 Remove 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 non‑bare.
|
||||
`cgit` only scans `~/public_git/*.git`.
|
||||
|
||||
### Why bare repos only?
|
||||
|
||||
Internal `.git/` directories inside non‑bare repos confuse cgit’s 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 read‑only:
|
||||
|
||||
```
|
||||
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)**.
|
|
@ -1,49 +0,0 @@
|
|||
---
|
||||
title: Socializing and chat
|
||||
author:
|
||||
- emv
|
||||
- benharri
|
||||
category: tilde.club
|
||||
---
|
||||
|
||||
## irc
|
||||
|
||||
we're members of the [tildeverse](https://tildeverse.org) and host a server
|
||||
in the [tildeverse irc network](https://tilde.chat). the official channel for
|
||||
~club is `#club`. stop by and say hello!
|
||||
|
||||
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`.
|
||||
|
||||
feel free to use tilde.chat's [webchat](https://web.tilde.chat/?join=club) if
|
||||
you prefer.
|
||||
|
||||
|
||||
## mailing list
|
||||
|
||||
we now have an official mailing list!
|
||||
|
||||
if your account is old (pre sept 2019), you should be subscribed with the email
|
||||
you originally signed up with. if your account is newer (post sept 2019), then
|
||||
you should be subscribed with your @tilde.club address. if you don't fit either
|
||||
of those categories, you can subscribe by visiting the [web portal](
|
||||
https://lists.tildeverse.org/postorius/lists/tildeclub.lists.tildeverse.org/)
|
||||
or by sending a mail to tildeclub-subscribe@lists.tildeverse.org with "subscribe"
|
||||
in the subject line. in either case, you can change the email you're subscribed
|
||||
with on the web portal or by unsubscribing and re-subscribing from the other
|
||||
address.
|
||||
|
||||
list archives are available [on the web here](
|
||||
https://lists.tildeverse.org/hyperkitty/list/tildeclub@lists.tildeverse.org/).
|
||||
|
||||
as of september 17, 2019, we're still seeing quite a few pending mails to
|
||||
gmail, yahoo, and fastmail. help get our list delivered by making sure to
|
||||
mark list messages as not spam and adding the list address to your contacts.
|
||||
if you're feeling especially motivated, please reach out to the support on
|
||||
your mail provider and ask them to look into why you're not receiving the
|
||||
messages.
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: command line for absolute beginners
|
||||
author: cmccabe
|
||||
category: tutorial
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
So, you want to join a public-access shell community like tilde.club,
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: dcss (dungeon crawl stone soup)
|
||||
author: audiodude
|
||||
category: software
|
||||
---
|
||||
connect to [crawl.tildeverse.org](https://crawl.tildeverse.org) to play dungeon
|
||||
crawl stone soup, a roguelike game with leaderboards and the ability to
|
||||
spectate other players. Also available with full images on the web!
|
|
@ -4,18 +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.
|
||||
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) - You can donate as little as $0.02/week using this method
|
||||
[<img src="https://shields.io/badge/kofi-Support_Us-ff5f5f?logo=ko-fi&style=for-the-badgeKofi">](https://ko-fi.com/tildeclub)
|
||||
|
||||
2. Cryptocurrency:
|
||||
Bitcoin: 3QF2PANZs5vcveM41CK2M5sLbopfn3CUAK
|
||||
Etherium: 0xcaacfad5e63a5d7849b441097986fd722ed94553
|
||||
[](https://www.paypal.com/donate?hosted_button_id=DWHSADKJ26HZ8)
|
||||
|
||||
Other then financial contributions 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,
|
||||
[](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, 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.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: Editing your index.html file
|
||||
author: vielmetti
|
||||
category: tutorial
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
Type: `nano` index.html to open your index.html file and begin editing
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
---
|
||||
title: Editing with Emacs
|
||||
author:
|
||||
- ohnoitsnoah
|
||||
- xwindows
|
||||
- keyboardan
|
||||
category: tutorials
|
||||
---
|
||||
<!-- Make a section for Modes -->
|
||||
|
||||
Emacs in tilde.club
|
||||
===================
|
||||
|
||||
[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
|
||||
-------------
|
||||
|
||||
You can open Emacs by typing `emacs` in the shell. This then opens Emacs :D.
|
||||
|
||||
Basic Elements of the Screen/Terminology
|
||||
----------------------------------------
|
||||
|
||||

|
||||
|
||||
When first starting Emacs, there are a few elements of the screen that you should know:
|
||||
|
||||
### Buffer
|
||||
|
||||
A "Buffer" is basically a container that holds the contents of whatever file you're editing.
|
||||
|
||||
> "Buffers in Emacs editing are objects that have distinct names and hold text that can be edited."
|
||||
>
|
||||
> -[GNU Emacs 28.2 Manual](https://www.gnu.org/software/emacs/manual/html_node/emacs/index.html)
|
||||
|
||||
### Window
|
||||
|
||||
Unlike what Microsoft Windows and other common-place OS' consider "windows", a window in Emacs is more like a window "pane", as where a buffer contains content, a *window* contains a buffer.
|
||||
|
||||
> "In Emacs terminology, a "window" is a container in which a buffer is displayed. This may be confusing at first; if so, think "pane" whenever you see "window" in an Emacs context until you get used to it."
|
||||
>
|
||||
> -[WikEmacs](https://wikemacs.org/wiki/Emacs_Terminology)
|
||||
|
||||
### Modeline
|
||||
|
||||
The Modeline is the strip of details towards the bottom of an instance in Emacs. This shows the file/buffer name, file type, any active modes/extensions, line and column numbers, and more.
|
||||
|
||||
### Mini-Buffer
|
||||
|
||||
The Mini-Buffer is the small area under the Modeline. It acts as a prompt, telling you when you've hit the beginning/ending of a buffer, allowing/alerting you to type/confirm commands, and more.
|
||||
|
||||
### Menu Bar
|
||||
|
||||
The Menu Bar is the series of drop-down menus at the top of an Emacs instance. It gives all the available commands in the current buffer, and shows their corresponding key-bindings.
|
||||
|
||||
Emacs Command Key
|
||||
-----------------
|
||||
|
||||
<!-- Rename this section -->
|
||||
|
||||
Emacs has some weird, complex commands, that may not make sense to new users. While this tutorial will mostly be based around using the drop-down menus, it's still good to know how to read command's key-bindings, as using them is the more effective/productive route.
|
||||
|
||||
* `C-` means the Control/Ctrl key
|
||||
* `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 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
|
||||
-----------------------------------------
|
||||
|
||||
<!-- TODO (Maybe): Talk about using the corresponding key-bindings, maybe in another section(?) -->
|
||||
|
||||
The way you accsess the drop-downs are by pressing the `F10` key, then using the arrow keys to navigate through the menus. You can press the `F10` key again to exit the menus, or the global "quit" command, `C-g`. As easy as these menus are, I reccommend slowly learning the key-bindings, as they make it faster and easier to navigate through Emacs as a whole. On that note, *I will be putting each commands corresponding key-binding next to their respective section title.*
|
||||
|
||||
### Switching Buffers (C-x b)
|
||||
|
||||
When you start Emacs using `emacs` in the shell, you'll be greeted with a welcome screen, along with two other buffers titled `*scratch*` and `Messages`.
|
||||
You can switch between these buffers by going to the Buffers drop-down and selecting which buffer to go to.
|
||||
|
||||
### Visting a New File (C-x C-f)
|
||||
|
||||
To visit a new or existing file, simply go to the File menu and select "Visit New File". You may also select "Open file...", however this does the same thing as "Visit New File", just without the ability to create a new file.
|
||||
|
||||
### Save and Save As (C-x C-s, C-x C-w)
|
||||
|
||||
To save the current buffer, select "Save" from the File menu. If you need to save the buffer as something else, you can select "Save As", which is also in the File menu.
|
||||
|
||||
### Killing a Buffer (C-x k)
|
||||
|
||||
Whenever you are finished working on a file, and no longer need the buffer, you can kill the current buffer by selecting "Close" from the File menu.
|
||||
|
||||
### Cut, Copy, Paste (C-w, M-w, C-y)
|
||||
|
||||
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.
|
|
@ -1,10 +1,12 @@
|
|||
---
|
||||
title: email
|
||||
author: benharri
|
||||
category: tutorial
|
||||
author:
|
||||
- benharri
|
||||
- xwindows
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
Your shiny, new tilde.club account comes with an email account. `alpine` is a command-line email application to use it, as is `mutt`. Good old-fashioned `mail` works too, although it's a little cryptic.
|
||||
Your shiny, new tilde.club account comes with an email account. `alpine` is a command-line email application to use it, as is `mutt`. [Good old-fashioned `mail` works too](#using-traditional-unix-mail-program), once configured properly; although it's a little cryptic.
|
||||
|
||||
`alpine` is menu driven, and the menus are self-explanatory; it's surprisingly easy to learn, and surprisingly powerful when you want to customize it.
|
||||
|
||||
|
@ -23,6 +25,8 @@ connection settings:
|
|||
- pop3.tilde.club port 995 with ssl
|
||||
- smtp.tilde.club port 587 with starttls
|
||||
|
||||
please remember to use only your tilde username as the login name, excluding the `@tilde.club`; for example `invalid` instead of `invalid@tilde.club`
|
||||
|
||||
if you'd like your @tilde.club mail forwarded elsewhere, you can put an email
|
||||
address in a file called `~/.forward`
|
||||
|
||||
|
@ -36,8 +40,194 @@ symlink the active script to `~/.dovecot.sieve`, and make sure to compile it
|
|||
with `sievec ~/.dovecot.sieve`.
|
||||
|
||||
you can find some example sieve scripts [here](
|
||||
https://wiki.dovecot.org/Pigeonhole/Sieve/Examples).
|
||||
https://doc.dovecot.org/configuration_manual/sieve/examples/).
|
||||
|
||||
alternately, you can use webmail's [filter settings](
|
||||
https://webmail.tilde.club/#/settings/filters) to configure your filters.
|
||||
|
||||
## mailing list
|
||||
|
||||
we now have an official mailing list!
|
||||
|
||||
if your account is old (pre sept 2019), you should be subscribed with the email
|
||||
you originally signed up with. if your account is newer (post sept 2019), then
|
||||
you should be subscribed with your @tilde.club address. if you don't fit either
|
||||
of those categories, you can subscribe by visiting the [web portal](
|
||||
https://lists.tildeverse.org/postorius/lists/tildeclub.lists.tildeverse.org/)
|
||||
or by sending a mail to tildeclub-subscribe@lists.tildeverse.org with "subscribe"
|
||||
in the subject line. in either case, you can change the email you're subscribed
|
||||
with on the web portal or by unsubscribing and re-subscribing from the other
|
||||
address.
|
||||
|
||||
list archives are available [on the web here](
|
||||
https://lists.tildeverse.org/hyperkitty/list/tildeclub@lists.tildeverse.org/).
|
||||
|
||||
as of september 17, 2019, we're still seeing quite a few pending mails to
|
||||
gmail, yahoo, and fastmail. help get our list delivered by making sure to
|
||||
mark list messages as not spam and adding the list address to your contacts.
|
||||
if you're feeling especially motivated, please reach out to the support on
|
||||
your mail provider and ask them to look into why you're not receiving the
|
||||
messages.
|
||||
|
||||
## Login-Time New Mail Notification
|
||||
|
||||
If you use an on-server email client to handle your Tilde.club inbox
|
||||
and have ever received any email there,
|
||||
you probably noticed that there was no incoming mail notification
|
||||
(<q>You have new mail.</q> or similar message)
|
||||
appearing at login time.
|
||||
This is due to the [mailbox format](#mailbox-format) used in Tilde.club
|
||||
not being the traditional centralized-folder MBOX;
|
||||
but fret not:
|
||||
if you wish to bring back this old-timey function,
|
||||
it can still be done by a one-line script.
|
||||
|
||||
To add this notification,
|
||||
add the following one-line Bourne shell snippet
|
||||
to your _login script_:
|
||||
|
||||
ls -U ~/.mail/new | grep -F -q "" && echo "You got mail."
|
||||
|
||||
If you are using Bash (default) as your login shell,
|
||||
your _login script_ file would be `~/.bash_profile`;
|
||||
but if you are using Dash,
|
||||
your _login script_ would be the traditional `~/.profile`.
|
||||
(For other shells,
|
||||
check your manual)
|
||||
|
||||
However,
|
||||
if you arranged for a terminal multiplexer to start automatically
|
||||
at the login time,
|
||||
you would not see the notification added this way.
|
||||
So in this case,
|
||||
you would rather want this notification
|
||||
to be shown at each start of your shell:
|
||||
instead of adding the snippet to your login script,
|
||||
you would have to add it to your shell's startup script:
|
||||
in case of Bash (default shell),
|
||||
your startup script would be `~/.bashrc`.
|
||||
(For other shells,
|
||||
check your manual)
|
||||
|
||||
Note that this code snippet
|
||||
only checks your main inbox folder.
|
||||
So,
|
||||
if you have explicitly written some [Sieve](#sieve-filtering)
|
||||
or webmail filtering rules
|
||||
to deliver some of the incoming emails into specific folder
|
||||
other than the main inbox,
|
||||
those emails would not produce notification.
|
||||
(This can be a desirable outcome in most cases,
|
||||
where people write Sieve filter
|
||||
to redirect unsolicited emails into <q>Junk</q> folder)
|
||||
|
||||
## Using Traditional Unix Mail Program
|
||||
|
||||
A traditional Unix `mail` program provided on Tilde.club
|
||||
is [Heirloom Mailx](https://heirloom.sourceforge.net/mailx.html).
|
||||
In its default configuration,
|
||||
it works for sending emails,
|
||||
but not receiving;
|
||||
due to the incoming [mailbox format](#mailbox-format)
|
||||
used in Tilde.club
|
||||
not being the old style system-wide centralized inbox folders
|
||||
used in the olden days,
|
||||
which the program expects by default.
|
||||
|
||||
However,
|
||||
for anyone who are soughting for a traditional Unix mail experience,
|
||||
or is experimenting with Tilde.club using a real teleprinter;
|
||||
Heirloom Mailx could be configured
|
||||
to operate directly on your Tilde.club mailbox,
|
||||
with some quirks,
|
||||
by adding the following lines to your `~/.mailrc`
|
||||
or create it with the following lines if not already existing
|
||||
(substitute `USERNAME` part with your Tilde.club username):
|
||||
|
||||
set MAIL=/home/USERNAME/.mail
|
||||
set folder=.mail
|
||||
set emptybox
|
||||
set newfolders=maildir
|
||||
set record=+.sent-mail
|
||||
|
||||
Once you did so,
|
||||
running `mail` would now show the list your emails
|
||||
if you have any in your inbox.
|
||||
(Type `q` and press <kbd>Enter</kbd> to quit)
|
||||
|
||||
Tips:
|
||||
|
||||
- Set your `PAGER` and `EDITOR` environment variables properly;
|
||||
unless you really want to view your emails through `more`
|
||||
and/or compose your emails through `ed`.
|
||||
(Even if you are really using a teleprinter,
|
||||
it is still a good idea to set these explicitly however)
|
||||
- Type `?` and press <kbd>Enter</kbd> for in-program help.
|
||||
Also read a manual
|
||||
(`man mail` from the main system command line).
|
||||
- Heirloom Mailx does not understand the Maildir++ subfolder structure;
|
||||
so you will have to type the name of subdirectory
|
||||
as seen in the filesystem
|
||||
(including its dot prefix)
|
||||
when changing folder.
|
||||
For example,
|
||||
use command:
|
||||
|
||||
folder +.sent-mail
|
||||
|
||||
to view the emails you have sent.
|
||||
(See [mailbox format](#mailbox-format)
|
||||
below for the default folders available)
|
||||
- When changing to view mail folder other than inbox,
|
||||
you nearly always want to prefix it with `+`;
|
||||
which means the folder is a subdirectory of the main email folder.
|
||||
- When changing to view the main inbox folder,
|
||||
simply use `%` as folder name
|
||||
without any prefix.
|
||||
- Marking email as junk here
|
||||
does not do a thing you would normally expect
|
||||
from modern email clients
|
||||
or webmails;
|
||||
so don't do it.
|
||||
|
||||
## Mailbox Format
|
||||
|
||||
Tilde.club uses [Dovecot](https://www.dovecot.org/) as a local mail delivery agent
|
||||
as well as an IMAP server.
|
||||
It is configured to deliver your emails
|
||||
into a `.mail/` subdirectory
|
||||
within your home directory on the server,
|
||||
structured in [Courier MTA's Maildir++ format](https://www.courier-mta.org/imap/README.maildirquota.html).
|
||||
|
||||
Maildir++ format is essentially the same as Maildir mailbox format,
|
||||
but with a concept of subfolders added in:
|
||||
apart from the usual `cur/`,
|
||||
`new/`,
|
||||
and `tmp/` subdirectories for normal Maildir operations;
|
||||
there would now also be dot-subdirectories
|
||||
which are email subfolders.
|
||||
Each dot-subdirectory
|
||||
would contain usual Maildir subdirectories,
|
||||
but not any more dot-subdirectory inside it.
|
||||
|
||||
In Tilde.club,
|
||||
the default layout of your Maildir++ folder hierarchy
|
||||
would be as the following:
|
||||
|
||||
| Email Folder | Filesystem Directory |
|
||||
|:-------------|:----------------------|
|
||||
| _(Inbox)_ | `~/.mail/` |
|
||||
| _(Sent)_ | `~/.mail/.sent-mail/` |
|
||||
| Junk | `~/.mail/.Junk/` |
|
||||
| Drafts | `~/.mail/.Drafts/` |
|
||||
| Trash | `~/.mail/.Trash/` |
|
||||
|
||||
So,
|
||||
if you would like to use command line tools
|
||||
to tinker with your mailbox,
|
||||
then more power to you.
|
||||
Also,
|
||||
note that email access via IMAP and webmail
|
||||
actually read/write emails directly onto these directories;
|
||||
so now you know where to grab a copy of all your emails data
|
||||
if you ever need a backup as well.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: custom 404
|
||||
title: custom 404 error page
|
||||
author: deepend
|
||||
category: tutorial
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
All users can now setup their own custom 404 error page. Just add a file named `404.html` in `~/public_html`
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: faq
|
||||
author: ford
|
||||
author: ford (updated by deepend)
|
||||
category: tilde.club
|
||||
---
|
||||
|
||||
|
@ -17,7 +17,7 @@ category: tilde.club
|
|||
Looking to join? We can\'t wait to meet you!
|
||||
--------------------------------------------
|
||||
|
||||
Sign-ups have re-launched as of September 20th, 2019. Sign up on our main page [https://tilde.club/signup](https://tilde.club/signup)!
|
||||
Sign up on our main page [https://tilde.club/signup](https://tilde.club/signup)!
|
||||
|
||||
[Several alternate sites have sprouted
|
||||
up](http://tilde.club/%7Epfhawkins/othertildes.html)
|
||||
|
@ -48,11 +48,10 @@ Here are some good places to start
|
|||
|
||||
- [the unofficial official tilde
|
||||
primer](http://tilde.club/~anthonydpaul/primer.html)
|
||||
- [some quick tips](http://tilde.club/~procload/)
|
||||
- [some more quick
|
||||
tips](http://tilde.club/~pfhawkins/tipsntricks.html)
|
||||
- [how do I usenet in
|
||||
Alpine](http://tilde.club/~cortex/usenet_in_pine.txt)?
|
||||
tilde.club](http://tilde.club/wiki/usenet-news.html)?
|
||||
- [help me understand \'screen\'](http://tilde.club/~jonathan/screen/)
|
||||
|
||||
Who are all these folks and what are they up to?
|
||||
|
@ -62,15 +61,47 @@ Who are all these folks and what are they up to?
|
|||
- [who is online right
|
||||
now](http://tilde.club/~whitneymcn/whoville.shtml)?
|
||||
- [who updated their websites
|
||||
recently](http://tilde.club/~delfuego/tilde.24h.html)?
|
||||
- [who links to each other](http://tilde.club/~ford/social.html)?
|
||||
recently](http://tilde.club/tilde.24h.php)?
|
||||
- [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)
|
||||
------------------------------------------------------------------
|
||||
|
||||
**TL;DR**
|
||||
|
||||
Change your Terminal->Keyboard->"The Function keys and keypad" setting to "XTerm R6".
|
||||
|
||||
**Details**
|
||||
|
||||
I am using PuTTY 0.73 Windows 11, and could not get byobu hotkeys to work. Instead of creating or changing windows with F2, F3, and F4, the byobu command line would display a ~ every time I hit a a function key. The solution was to change my PuTTY settings for "XTerm R6" emulation. Now the Function keys work great, including F8! Note there are other articles out there that suggest using "VT100+" instead of "XTerm R6". Althought VT100+ helped out F2, F3, and F4, it *did not help F8* and some of the higher keys--that is where "XTerm R6" solved the rest.
|
||||
|
||||
**References**
|
||||
|
||||
- [Function Key Fix at codeyarns.com](https://codeyarns.com/tech/2013-01-21-byobu-function-keys-do-not-work-in-putty.html)
|
||||
- [Byobu Keybindings Cheat Sheet at cheatography.com](https://cheatography.com/mikemikk/cheat-sheets/byobu-keybindings/))
|
||||
|
||||
Other links
|
||||
-----------
|
||||
|
||||
- [Github](https://github.com/tildeclub/tilde.club)
|
||||
- [Webring](http://tilde.club/~harper/link.html?action=join)
|
||||
- [TildeClub Badge](http://tilde.club/~zarate/badge.html)
|
||||
- [TildeRadio](https://tilderadio.org/)
|
||||
|
||||
How do you pronounce \"tilde\"?
|
||||
-------------------------------
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: finding your index.html
|
||||
category: tutorial
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
There are some basic command line commands you'll want to Google and learn, but for this tutorial you only need a few:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: how to use git
|
||||
category: tutorial
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
`git` is a version control system. It's pretty confusing at first, but
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: gopher
|
||||
author: benharri
|
||||
category: tutorial
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
tilde.club now serves gopher! we're using
|
||||
|
@ -54,7 +54,7 @@ gophernicus also supports the following special types:
|
|||
## additional resources
|
||||
|
||||
see [gophernicus' gophermap
|
||||
documentation](https://github.com/gophernicus/gophernicus/blob/master/README.Gophermap)
|
||||
documentation](https://github.com/gophernicus/gophernicus/blob/master/README.gophermap)
|
||||
for more info on available item types and other special selectors.
|
||||
|
||||
if you're completely new to gopher, check out the [gopher zone](https://gopher.zone)!
|
||||
|
|
|
@ -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 you’ll 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. You’ll see a welcome message and a **main menu** with numbered options.
|
||||
|
||||
---
|
||||
|
||||
## Main Menu Overview
|
||||
|
||||
After logging in, you’ll 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, you’ll 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 it’s valid.
|
||||
3. If the email matches an existing account, you’ll 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. You’ll 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 account’s password immediately.
|
||||
|
||||
---
|
||||
|
||||
## 3. Exiting the Help Desk
|
||||
|
||||
Simply choose **“I’d 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 Didn’t Arrive?** Check your spam folder. If you still don’t see it, contact root@tilde.club.
|
||||
- **Invalid Email**: If you mistype an email or use an unrecognized domain, you’ll 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.
|
||||
|
||||
---
|
|
@ -0,0 +1,85 @@
|
|||
---
|
||||
title: Socializing and chat
|
||||
author:
|
||||
- emv
|
||||
- benharri
|
||||
- deepend
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
## irc
|
||||
|
||||
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 **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.
|
||||
|
||||
some channels might require you to register your nickname with NickServ to post in them (e.g. #meta). NickServ acts like a regular user, so you communicate with it through `/msg`. steps:
|
||||
|
||||
1. [optional] set nickname: `/nick YourNick` - not necessary with weechat, since it connects you under your tilde.club username
|
||||
2. register: `/msg NickServ REGISTER YourPassword youremail@example.com` - you can use your tilde.club e-mail address for this
|
||||
3. wait for registration email with confirmation code
|
||||
4. confirm: `/msg NickServ CONFIRM someCode`
|
||||
|
||||
after this, every time you reconnect to irc you will have to identify with nickserv again: `/msg NickServ IDENTIFY YourPassword`.
|
||||
|
||||
weechat tip: NickServ replies and error messages appear in the first buffer `tilde weechat` (use Alt + up/down to switch).
|
||||
|
||||
## Weechat relays
|
||||
|
||||
weechat introduced [unix socket relays](
|
||||
https://weechat.org/files/doc/stable/weechat_user.en.html#relay_unix_socket)
|
||||
in version 2.5 which is a much easier way to offer per-user relay access.
|
||||
|
||||
tilde.club/~username/weechat is configured to proxy to the default unix relay socket
|
||||
location (`~/.weechat/relay_socket`). to get started using it, follow these steps.
|
||||
|
||||
1. in weechat:
|
||||
* `/relay add unix.weechat %h/relay_socket`
|
||||
* `/set relay.network.password mysupersecretpassword` - don't use this password
|
||||
of course. note that you might already have this set.
|
||||
|
||||
2. at your shell:
|
||||
* `chmod o+rw ~/.weechat/relay_socket` - note that other members of the club group
|
||||
are not included in the granted permissions. this allows nginx to connect
|
||||
to your socket on your behalf. you will need to do this every time you start
|
||||
weechat as the socket doesn't exist until weechat starts up.
|
||||
|
||||
3. in your relay client:
|
||||
* [glowing-bear](https://glowingbear.tilde.club/):
|
||||
- relay hostname: tilde.club:443/~username/weechat
|
||||
- relay port: 443
|
||||
- your relay password
|
||||
|
||||
* [weechat-android](https://github.com/ubergeek42/weechat-android) and [lith](https://github.com/lithapp/lith):
|
||||
- connection type: websocket (ssl)
|
||||
- websocket path: ~username/weechat
|
||||
- relay host: tilde.club
|
||||
- relay port: 443
|
||||
- your relay password
|
||||
|
||||
- (if you get "Error: Could not connect using WebSocket", check to be sure
|
||||
~/ and ~/.weechat have at least o+rx permissions so nginx can reach
|
||||
~/.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.
|
|
@ -0,0 +1,74 @@
|
|||
---
|
||||
title: Byobu, TMUX & Screen (terminal multiplexers)
|
||||
author: rudi
|
||||
category: software
|
||||
---
|
||||
|
||||
# Terminal Muiltiplexers
|
||||
Terminal multiplexers are programs that will keep your session running, even if you disconnect from the server. They also allow you to 'multiplex' your terminals, spawning multiple shells in one local terminal. The three multiplexers on tilde.club are [byobu](https://linux.die.net/man/1/byobu), [tmux](https://linux.die.net/man/1/tmux), and [screen](https://linux.die.net/man/1/screen).
|
||||
|
||||
# Byobu
|
||||
byobu is the default mutliplexer for tilde.club. It was originally designed to provide elegant enhancements to the otherwise functional, plain, practical [GNU Screen](http://www.gnu.org/software/screen/), for the [Ubuntu](https://ubuntu.com/) server distribution. Byobu now includes an enhanced profiles, convenient keybindings, configuration utilities, and toggle-able system status notifications for both the GNU Screen window manager and the more modern [Tmux](https://github.com/tmux/tmux) terminal multiplexer, and works on most Linux, BSD, and Mac distributions.
|
||||
|
||||
In the spirit of the former tmux page, here is a super basic primer:
|
||||
The basic keys to know are f2, f3, f4, f6, f8, and f9
|
||||
f2 - spawn new tab
|
||||
f3 - move to previous (left) tab
|
||||
f4 - move to next (right) tab
|
||||
f6 - disconnect from byobu (and the server)
|
||||
f8 - rename a tab
|
||||
f9 - configuration menu (also has a basic help guide)
|
||||
|
||||
With those keys, you can do a lot. However tmux shortcuts will work here in byobu (but unless you configure it differently, the escape sequence isn't ctrl-b, it's ctrl-a)
|
||||
You can even configure byobu to run automatically when you connect, using `byobu-enable` (`byobu-disable` to undo).
|
||||
For a more in depth overview, you can go to [byobu's documentation](https://www.byobu.org/documentation) or `man byobu`.
|
||||
|
||||
# Tmux
|
||||
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'm 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 byobu.
|
||||
|
||||
Or, if you don't like it - try [screen](screen.html)
|
||||
|
||||
# screen
|
||||
|
||||
`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.
|
||||
|
|
@ -12,23 +12,6 @@ category: tilde.club
|
|||
|
||||
First things first, explore and have fun! But remember, this is a single linux server trying to support the weight of several hundred nerds. Don't host servers, don't run heavy processes, don't host giant files. In short, be gentle.
|
||||
|
||||
More about [scripting netiquette here](http://tilde.club/#WHEREISTHIS)
|
||||
More about [scripting netiquette here](http://tilde.club/wiki/safe-scripting-the-tilde-way.html)
|
||||
|
||||
Now let's talk about drama. There is a Mary J. Blige song called "No More Drama." If Mary J. Blige would think it was drama, it is drama. No flamewars about emacs/vi, no matter how historically accurate they may be. No guilting people, shaming them, or making them feel bad. More benefit of the doubt and less "are you kidding me?"
|
||||
|
||||
## ~ford's SACRED VOWS to the community
|
||||
|
||||
I will do my best to do the following things:
|
||||
|
||||
1) I will make a weekly backup of the `public_html` directories so when
|
||||
some teen in Estonia decides to hack in and ruin everything we can
|
||||
bring up a new server and limp back to life.
|
||||
|
||||
2) I won't shut things down without a month of warning and once it's
|
||||
shut down I'll make sure a file with all the `public_html` directories
|
||||
is uploaded to archive.org.
|
||||
|
||||
3) If any community forms at all (DUBIOUS BUT SURPRISE ME) I promise I
|
||||
won't blow up the community without, like, first pointing everyone to
|
||||
some free IRC channel or something.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
Here’s an example:
|
||||
|
||||
```html
|
||||
<p>Visitor count:
|
||||
<script src="https://counter.tilde.club/?page=homepage&user=alice&style=web1"></script>
|
||||
</p>
|
||||
```
|
||||
|
||||
Once you’ve 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 won’t increase the count unless unique mode is off.
|
||||
|
||||
If you need help or want a custom style added, reach out to the site admin.
|
||||
```
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
title: tilde quilt
|
||||
author: lab6
|
||||
category: links
|
||||
---
|
||||
|
||||
## 2-dimensional anonymous collaborative microblogging
|
||||
|
||||
[Visit the tilde quilt](https://tilde.club/~lab6/quilt.html) to read messages from other tilde users.
|
||||
|
||||
Add your own messages (up to 150 printable ASCII characters) by running a command like the following:
|
||||
|
||||
`echo "Hi!" > /home/lab6/quilt`
|
||||
|
||||
Each message has an id in the top left corner, which you can use to influence where your message is placed.
|
||||
You can prefix your message with <id><compassdirection>, where direction is one of N, E, W, or S, like this:
|
||||
|
||||
`echo "2EThis message goes to the east of the message with id 2" > /home/lab6/quilt`
|
||||
`echo "3NThis message goes to the north of message 3" > /home/lab6/quilt`
|
||||
`echo "4SThis message goes to the south of message 4" > /home/lab6/quilt`
|
||||
`echo "5WAnd this message goes to the west of message 5" > /home/lab6/quilt`
|
||||
|
||||
If you don't use a prefix like this, your message will be placed randomly.
|
||||
|
||||
Rate-limiting is in effect. If your write blocks, just wait. If your write fails, or your message fails to appear on the quilt,
|
||||
wait a minute or so and try again. The rate-limiting is intended to keep the quilt slow and human-scale. Please don't pipe in
|
||||
massive streams of data - it will clog the pipe for other users.
|
||||
|
||||
Have fun and be kind!
|
||||
|
||||
## What's going on?
|
||||
|
||||
We are building a patchwork quilt with the aid of POSIX named pipes. Any user able to execute a command on tilde.club
|
||||
can pipe their message to `/home/lab6/quilt`. Messages are anonymous - there is no way to detect which user sent data into the pipe.
|
||||
Users from outside the tilde.club server can [read the quilt](https://tilde.club/~lab6/quilt.html) but cannot add to it.
|
||||
|
||||
Backups will be taken regularly and the quilt will never be reset. The quilt is strictly
|
||||
append-only. Old messages are never edited or overwritten (spam and nastiness may be spray-painted over but I trust you folk to be better than this).
|
||||
The quilt grows without bound in all four directions.
|
||||
|
||||
Be creative. Use your 150 characters wisely. If you didn't use them wisely, try again. Hold a conversation. Vent a thought.
|
||||
Play correspondence chess. Golf some perl. Branch off into the void.
|
||||
|
||||
Early adopters who snag the low messages IDs will be venerated by the generations to come.
|
||||
|
||||
Contact ~lab6 if it's broken. Contact ~lab6 if it's not broken - he loves getting postcards.
|
||||
|
||||
## Roadmap
|
||||
|
||||
* Unicode
|
||||
* 2D blockchain
|
||||
* z-axis
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: safe scripting the tilde way
|
||||
author: michaelcoyote
|
||||
category: tutorial
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
Welcome to tilde.club. For many of you this may be your first multi-user host and for even more of you this may be your *first* host. Welcome to all of you.
|
||||
|
|
|
@ -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.
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: Editing Basic UNIX Security the Tilde way
|
||||
author: michaelcoyote
|
||||
category: tutorial
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
author: benharri
|
||||
title: ssh
|
||||
category: tutorial
|
||||
title: How to connect using SSH (secure shell)
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
|
||||
|
@ -10,7 +10,9 @@ _or, how to tell other computers to do cool things_
|
|||
---
|
||||
|
||||
> all users are required to use an ssh keypair for login, or will be required
|
||||
to proceed with manual account recovery
|
||||
> to proceed with manual account recovery.
|
||||
> you can also set up [two-factor authentication](2fa.html) to log in without your
|
||||
> keypair.
|
||||
|
||||
## tilde.club details
|
||||
|
||||
|
@ -28,6 +30,9 @@ ssh is also available on port 443 using the address `ssh.tilde.club`:
|
|||
this is useful if you're on a limited public network that blocks non-http
|
||||
ports.
|
||||
|
||||
if you are prompted for a verification code, it's for [2fa](2fa.html).
|
||||
it will not work if you haven't set it up. be sure you're using the right ssh keypair.
|
||||
|
||||
---
|
||||
|
||||
## intro
|
||||
|
@ -96,42 +101,33 @@ pick your fighter: [[mac](#mac)] | [[windows](#windows)] | [[linux](#linux)]
|
|||
|
||||
1. open terminal (it's in `/Applications/Utilities`)
|
||||
|
||||
1. create your .ssh directory:
|
||||
2. create your .ssh directory:
|
||||
|
||||
```bash
|
||||
mkdir -m 700 ~/.ssh
|
||||
```
|
||||
|
||||
1. create your keys:
|
||||
|
||||
for rsa keys:
|
||||
|
||||
```bash
|
||||
ssh-keygen -t rsa -b 4096
|
||||
```
|
||||
|
||||
for dd25519 keys:
|
||||
3. create your keypair:
|
||||
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -a 100
|
||||
```
|
||||
|
||||
1. if you press enter to accept the defaults, your public and private key will
|
||||
be located at `~/.ssh/id_rsa.pub` and `~/.ssh/id_rsa` respectively (or
|
||||
`~/.ssh/id_ed25519.pub` and `~/.ssh/id_ed25519` if you chose ed25519 type)
|
||||
4. if you press enter to accept the defaults, your public and private key will
|
||||
be located at `~/.ssh/id_ed25519.pub` and `~/.ssh/id_ed25519`
|
||||
|
||||
1. `cat ~/.ssh/id_rsa.pub` (or `cat ~/.ssh/id_ed25519.pub` for ed25519)
|
||||
`cat ~/.ssh/id_ed25519.pub`
|
||||
|
||||
1. copy the output of the last command and paste it in the sshkey field on the
|
||||
5. copy the output of the last command and paste it in the sshkey field on the
|
||||
signup form (or email it to [~root](mailto:root@tilde.club) if you already have an account)
|
||||
|
||||
#### using your keypair
|
||||
|
||||
once an admin approves your signup, you can join the tilde.club
|
||||
|
||||
1. open terminal (it's in `/Applications/Utilities`)
|
||||
6. open terminal (it's in `/Applications/Utilities`)
|
||||
|
||||
1. `ssh` to tilde.club:
|
||||
7. `ssh` to tilde.club:
|
||||
|
||||
```bash
|
||||
ssh username@tilde.club
|
||||
|
@ -139,69 +135,62 @@ ssh username@tilde.club
|
|||
|
||||
where username is your username (~benharri would use `ssh benharri@tilde.club`)
|
||||
|
||||
1. profit???
|
||||
8. profit???
|
||||
|
||||
---
|
||||
|
||||
### windows
|
||||
|
||||
there are a couple options for using ssh on windows these days.
|
||||
i like to use [git bash](https://git-scm.com).
|
||||
#### Ensure OpenSSH is installed
|
||||
|
||||
1. Open Settings, select Apps, then select Optional Features
|
||||
1. Scan the list to see if the OpenSSH is already installed. If not, at the top of the page, select Add a feature, then...
|
||||
1. Find OpenSSH Client, then select Install
|
||||
|
||||
#### generating your keypair
|
||||
|
||||
choose from any of the following options:
|
||||
1. open your command prompt (Start -> Search -> type "cmd")
|
||||
|
||||
- [windows subsystem for linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10)
|
||||
- [msys2](http://www.msys2.org/)
|
||||
- [git bash](https://git-scm.com)
|
||||
2. create your keypair:
|
||||
|
||||
1. open your new shell
|
||||
|
||||
1. create your .ssh directory
|
||||
|
||||
```bash
|
||||
mkdir .ssh
|
||||
```
|
||||
|
||||
1. create your keypair
|
||||
|
||||
for rsa keys:
|
||||
|
||||
```bash
|
||||
ssh-keygen -t rsa -b 4096
|
||||
```
|
||||
|
||||
for ed25519 keys:
|
||||
|
||||
```bash
|
||||
```cmd
|
||||
ssh-keygen -t ed25519 -a 100
|
||||
```
|
||||
|
||||
1. if you press enter to accept the defaults, your public and private key will
|
||||
be located at `~/.ssh/id_rsa.pub` and `~/.ssh/id_rsa` respectively (or
|
||||
`~/.ssh/id_ed25519.pub` and `~/.ssh/id_ed25519` if you chose ed25519 type)
|
||||
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.
|
||||
|
||||
1. `cat ~/.ssh/id_rsa.pub` (or `cat ~/.ssh/id_ed25519.pub` for ed25519)
|
||||
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.
|
||||
|
||||
1. copy the output of the last command and paste it in the sshkey field on the
|
||||
signup form (or email it to [~root](mailto:root@tilde.club) if you already have an account)
|
||||
Next we will open up the public key so we can copy its contents.
|
||||
|
||||
```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.
|
||||
|
||||
#### using your keypair
|
||||
|
||||
once an admin approves your signup, you can join the tilde.club
|
||||
|
||||
1. open terminal (it's in `/Applications/Utilities`)
|
||||
5. open command prompt (Start -> Search -> type "cmd")
|
||||
|
||||
1. `ssh` to 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`)
|
||||
|
||||
1. profit???
|
||||
|
||||
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???
|
||||
|
||||
---
|
||||
|
||||
|
@ -218,44 +207,37 @@ in almost all cases. if they're not, look up how to install ssh for your distro.
|
|||
mkdir -m 700 ~/.ssh
|
||||
```
|
||||
|
||||
1. create your keys
|
||||
|
||||
for rsa keys:
|
||||
|
||||
```bash
|
||||
ssh-keygen -t rsa -b 4096
|
||||
```
|
||||
|
||||
for ed25519 keys:
|
||||
2. create your keypair
|
||||
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -a 100
|
||||
```
|
||||
|
||||
1. if you press enter to accept the defaults, your public and private key will
|
||||
be located at `~/.ssh/id_rsa.pub` and `~/.ssh/id_rsa` respectively (or
|
||||
`~/.ssh/id_ed25519.pub` and `~/.ssh/id_ed25519` if you chose ed25519 type)
|
||||
3. if you press enter to accept the defaults, your public and private key will
|
||||
be located at `~/.ssh/id_ed25519.pub` and `~/.ssh/id_ed25519`
|
||||
|
||||
1. `cat ~/.ssh/id_rsa.pub` (or `cat ~/.ssh/id_ed25519.pub` for ed25519)
|
||||
```bash
|
||||
cat ~/.ssh/id_ed25519.pub
|
||||
```
|
||||
|
||||
1. 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
|
||||
|
||||
once an admin approves your signup, you can join the tilde.club
|
||||
|
||||
1. open a terminal (this depends on your distro)
|
||||
5. open a terminal (this depends on your distro)
|
||||
|
||||
1. `ssh` to tilde.club:
|
||||
6. `ssh` to tilde.club:
|
||||
|
||||
where username is your username (~benharri would use `ssh benharri@tilde.club`)
|
||||
|
||||
```bash
|
||||
ssh username@tilde.club
|
||||
```
|
||||
|
||||
where username is your username (~benharri would use `ssh benharri@tilde.club`)
|
||||
|
||||
1. profit???
|
||||
7. profit???
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
author: jeffbonhag
|
||||
title: SSHFS
|
||||
title: SSHFS (SSH Filesystem)
|
||||
category: software
|
||||
---
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
title: tildeverse
|
||||
author: audiodude
|
||||
category: links
|
||||
---
|
||||
From the website at [https://tildeverse.org/](https://tildeverse.org/):
|
||||
|
||||
> we're a loose association of like-minded tilde communities. if you're
|
||||
> interested in learning about *nix (linux, unix, bsd, etc) come check out our
|
||||
> [member tildes](https://tildeverse.org/members/) and sign up!
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: time zones
|
||||
category: tutorial
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
The timezone by default on the server is UTC.
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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)
|
|
@ -0,0 +1,200 @@
|
|||
---
|
||||
title: ttbp / feels (blog platform)
|
||||
author: benharri
|
||||
category: software
|
||||
---
|
||||
|
||||
# FEELS MANUAL #
|
||||
|
||||
`ttbp` stands for "tilde.town blogging platform", the original working name for
|
||||
this project. the complete codebase is available on
|
||||
[github](https://github.com/tildeclub/ttbp).
|
||||
|
||||
`ttbp` runs from the command line, providing a hub for writing personal blog
|
||||
posts and reading posts written by other users of tilde.club. it's a little bit
|
||||
like livejournal or dreamwidth or tumblr. you can opt to publish your posts to a
|
||||
public html file hosted on your tilde page, to tilde.club's gopher server, or
|
||||
keep all your entries private to the tilde.club server.
|
||||
|
||||
this is a project that runs on tilde.club, so all users of this program are
|
||||
expected to operate under the tilde.club [code of
|
||||
conduct](http://tilde.club/wiki/code-of-conduct.html). content/personal issues should be
|
||||
worked out according to the CoC, with support from the [administrative
|
||||
team](https://tilde.club/wiki/code-of-conduct.html#contact-info) if needed.
|
||||
|
||||
### support
|
||||
|
||||
if you're having trouble getting started, or run into program errors or strange
|
||||
behavior, please hop on irc and contact ben or deepend.
|
||||
|
||||
there's also a function from the main menu that lets you send feedback/inquiries
|
||||
to me directly; this uses email, which is what i'll respond to.
|
||||
|
||||
### writing entries
|
||||
|
||||
entries are recorded as plaintext files in your `~/.ttbp/entries` directory.
|
||||
`ttbp` will use your selected editor to open and write files; each day is its
|
||||
own entry, like a diary page. at midnight for whatever timezone you've set for
|
||||
your user account on tilde.club, you'll get a fresh entry. if you don't write
|
||||
any feels on a particular day, no entries will show up there.
|
||||
|
||||
when you save and quit the text editor, your entry will automatically propagate
|
||||
to the global feels list; if you've opted to publish your feels to html/gopher,
|
||||
those files will update immediately. you can always go back to the current day's
|
||||
entry and edit/add as you'd like, but older entries will not be available for
|
||||
editing from `ttbp`.
|
||||
|
||||
*(since files are just stored as plaintext in your directory, it's possible to
|
||||
edit and move old entries directly from the command line. however, changing old
|
||||
entries might cause strange things to happen with timestamps. the main program
|
||||
looks at the filename first for setting the date, then the last modified time to
|
||||
sort recent posts. it expects YYYMMDD.txt as the filename; anything else won't
|
||||
show up as a valid entry. yes, this means you can post things out of date order
|
||||
by creating files with any date you want.)*
|
||||
|
||||
#### general entry-writing notes
|
||||
|
||||
* you can use [markdown](https://daringfireball.net/projects/markdown/syntax)
|
||||
* you can use html
|
||||
* you can also put things between `<!-- comments -->` to have them show up
|
||||
in the feed but not render in a browser (but people can still read them with
|
||||
view-source)
|
||||
|
||||
### reading other feels
|
||||
|
||||
the `browse global feels` feature shows the ten most recent entries that anyone
|
||||
has written on ttbp. this list is only accessible from within tilde.club,
|
||||
although individual entries may be posted to html or gopher.
|
||||
|
||||
you can also pull up a list of a single user's feels through `check out your
|
||||
neighbors`, which displays all users who are writing on `ttbp` based on their
|
||||
most recently updated entry, and a link to their public html blog if they've
|
||||
opted to publish their posts.
|
||||
|
||||
**please note!** entries written on `ttbp` should be considered sensitive,
|
||||
private information, even if a particular user is publishing entries in a
|
||||
world-viewable way! please be respectful about having access to other people's
|
||||
feels, and do not copy/repeat any information without getting their explicit
|
||||
permission. tilde.club operates on a high level of mutual trust, and `ttbp` is
|
||||
designed to give individuals control over their content.
|
||||
|
||||
### privacy
|
||||
|
||||
when you start your ttbp, you have the option of publishing or not publishing
|
||||
your blog.
|
||||
|
||||
if you opt to not publish, your entires will never be accessible from outside of
|
||||
the tilde.club network; other tilde.club users will still be able to read your
|
||||
entries through the ttbp interface, or by directly accessing your
|
||||
`~/.ttbp/entries` directory.
|
||||
|
||||
if you want to further protect your entries, you can `chmod 700` your entries
|
||||
directory.
|
||||
|
||||
if you opt to publish, the program creates a directory `~/.ttbp/www` where it
|
||||
stores all html files it generates, and symlinks this from your `~/public_html`
|
||||
with your chosen blog directory. your blog will also be listed on the [main ttbp
|
||||
page](https://tilde.club/wiki/ttbp.html).
|
||||
|
||||
you can also opt to publish to gopher, and the program will automatically
|
||||
generate a gophermap of your feels.
|
||||
|
||||
you can set publishing status on individual entries, or bury individual feels;
|
||||
see "data management" below for details.
|
||||
|
||||
### data management
|
||||
|
||||
the `manage your feels` menu provides several tools for organizing your feels.
|
||||
these are all actions you can perform manually from the command line, but doing
|
||||
them from within the program can help keep your files properly linked up.
|
||||
|
||||
* **read over feels**--a list of all your entries, which you can open and
|
||||
read like any other feel
|
||||
* **modify feels publishing**--this lets you toggle privacy on individual
|
||||
posts. entries marked `(nopub)` will not get written to html or gopher,
|
||||
and toggling them from this menu will immediately publish or unpublish
|
||||
that entry (if you're not publishing your posts at all, these settings
|
||||
won't matter, since your feels will never show up outside of tilde.club)
|
||||
* **backup your feels**--makes a .tar.gz of all your entries, saving one
|
||||
copy to `~/.ttbp/backups/` with the current date, and a second copy to
|
||||
your home directory for safekeeping.
|
||||
* **import a feels backup**--unpacks a backup file into your current feels
|
||||
list. this tool checks the `~/.ttbp/backups` directory for archives, and
|
||||
expects a file created by the above backup utility. if it detects any file
|
||||
collisions, it will preserve your current live copy and leave the backup
|
||||
verison in a temp directory, and notify you that this happened. also, any
|
||||
entries that were previously marked as `(nopub)` will retain their nopub
|
||||
status.
|
||||
* **bury some feels**--hides individual feels from viewing; entries are
|
||||
moved to `~/.ttbp/buried` (and marked with a unique timestamp to prevent
|
||||
file collision) with permissions set to 600, meaning no one except you
|
||||
will be able to open that file. these entries are also hidden from your
|
||||
own view from `read over feels`, and you'll have to open the files from
|
||||
the command line if you want to see them. this is intended to be a
|
||||
permament action, so you'll be asked to type the entry date once to load
|
||||
the feel, then shown a preview of that feel, and then type the date again
|
||||
to confirm burying.
|
||||
* **delete feels by day**--*permanently removes individual entries*,
|
||||
including deleting published html/gopher files if needed. this action is
|
||||
not recoverable, unless you have a backup to restore; you'll be asked to
|
||||
type the entry date once to load the feel, then shown a preview of that
|
||||
feel, and then type the date again to confirm deletion.
|
||||
* **purge all feels**--*permanently removes all feels*, including deleting
|
||||
all published html/gopher files if needed. this action is not recoverable,
|
||||
unless you have a backup to restore. you'll be asked to type a
|
||||
one-time-use purge code to confirm this action.
|
||||
* **wipe feels account**--*permanently removes all data associated with
|
||||
feels*, including deleting any published hmtl/gopher files and removing
|
||||
your `~/.ttbp` directory. any backups that you have in `~/.ttbp/backups`
|
||||
will also be deleted with this action (which is why the backup function
|
||||
makes a second copy for safekeeping in your home directory). you will no
|
||||
longer show up in any lists as a user.
|
||||
|
||||
### settings
|
||||
|
||||
the settings menu lets you change specific options for handling your feels and
|
||||
using the interface.
|
||||
|
||||
* **editor**--set your text editor
|
||||
* **gopher**--opt in or out of automatically posting to gopher
|
||||
* **post as nopub**--set whether posts default to being published or not
|
||||
published (if you're not publishing your feels, this doesn't matter)
|
||||
* **publish dir**--set the directory under you `public_html` where feels will be
|
||||
published (if you're not publishing your feels, this defaults to `None`)
|
||||
* **publishing**--opt in or out of automatically publishing entries to a
|
||||
world-readable html page
|
||||
* **rainbows**--opt in or out of having multicolored menu text
|
||||
|
||||
### changing your page layout
|
||||
|
||||
you can modify how your blog looks by editing the stylesheet or header and
|
||||
footer files. the program sets you up with basic default. if you break your page
|
||||
somehow, you can force the program to regenerate your configuration by deleting
|
||||
your ~/.ttbp directory entirely. **you might want to back up your
|
||||
~/.ttbp/entries directory before you do this.**
|
||||
|
||||
* to modify your stylesheet, edit your ~/.ttbp/config/style.css
|
||||
* to modify the page header, edit your ~/.ttbp/config/header.txt
|
||||
* there's a place marked off in the default header where you can safely put
|
||||
custom HTML elements!
|
||||
* to modify the page footer, edit your ~/.ttbp/config/footer.txt
|
||||
|
||||
### general tips/troubleshooting
|
||||
|
||||
* if the date looks like it's ahead or behind, it's because you haven't set
|
||||
your local timezone yet. here are some
|
||||
[timezone setting instructions](http://www.cyberciti.biz/faq/linux-unix-set-tz-environment-variable/)
|
||||
* the feels burying tool will effectively clear your post for the day; you can
|
||||
use this feature to start a fresh entry on a particular day by burying the
|
||||
current day's feels and then editing a new file
|
||||
|
||||
### future features
|
||||
|
||||
these are a few ideas being kicked around, or under active development:
|
||||
|
||||
* stylesheet/theme selector
|
||||
* better entry display within ttbp (currently just offloads to `less`)
|
||||
* buried feels browser
|
||||
|
||||
other ideas are listed on github as
|
||||
[upcoming features](https://github.com/modgethanc/ttbp/issues?q=is%3Aissue+is%3Aopen+label%3A"upcoming+features") or [feature requests](https://github.com/modgethanc/ttbp/issues?q=is%3Aissue+is%3Aopen+label%3A"feature+request")!
|
|
@ -1,62 +1,97 @@
|
|||
---
|
||||
title: usenet news
|
||||
category: tutorial
|
||||
category: tutorials
|
||||
author:
|
||||
- deepend
|
||||
- audiodude
|
||||
- xwindows
|
||||
---
|
||||
|
||||
## As of 2020-04-14 news is back up!
|
||||
|
||||
You'll need a news (NNTP) client to read news. The address is
|
||||
`news.tilde.club port:119`, and it is open to the internet at large. Thanks to
|
||||
[~deepend](../~deepend) for getting it set back up!
|
||||
You'll need a news (NNTP) client to read news.
|
||||
The address to use inside of Tilde.club
|
||||
is `localhost` port 119.
|
||||
The address to use outside is `news.tilde.club` port 119 (plain),
|
||||
or port 563 (with TLS,
|
||||
available since 2023-02-17).
|
||||
If you connect from outside of Tilde servers,
|
||||
e.g. from home,
|
||||
you would have to use TLS
|
||||
and authenticate using your Tilde.club username and password,
|
||||
to be able to post.
|
||||
|
||||
This netnews service is brought to you by [~deepend](../~deepend) (administrator)
|
||||
and [~xwindows](/~xwindows/) (technical troubleshooter).
|
||||
|
||||
## News clients
|
||||
|
||||
### slrn
|
||||
[slrn](slrn.html) is a newsreader; see http://slrn.sourceforge.net/ for details.
|
||||
[slrn](slrn.html) is a newsreader; see [http://slrn.sourceforge.net/](http://slrn.sourceforge.net/) for details.
|
||||
|
||||
First, add `export NNTPSERVER="localhost"` to your shellrc (`.bashrc`, `.zshrc`)
|
||||
and source it (`source path/to/.shellrc`).
|
||||
|
||||
Then run `slrn --create` to create the slrn config file, and lastly `slrn -d` to
|
||||
populate group names.
|
||||
|
||||
You're now ready to run `slrn`! If the list is empty, press `L` (for list-groups) and enter `*` in the field for all groups. You might need to enter each group (pressing `space`) to get a proper count for how many (if any) unread messages there are.
|
||||
|
||||
### pine/alpine
|
||||
|
||||
[[pine]] can read news; this
|
||||
[http://www.chebucto.ns.ca/Help/News/PineNews.html](tutorial) might help.
|
||||
Pine can read news; this
|
||||
[tutorial](http://www.chebucto.ns.ca/Help/News/PineNews.html) might help.
|
||||
You can also read the [FAQ from U Washington](http://www.washington.edu/pine/faq/news.html).
|
||||
|
||||
In Pine do
|
||||
If you use Pine from inside Tilde.club,
|
||||
configure it by:
|
||||
|
||||
1. 'S' for setup, 'C' for config, then
|
||||
1. set 'NNTP Server (for news)' to news.tilde.club
|
||||
1. set 'NNTP Server (for news)' to `localhost`
|
||||
1. Then go back to the main menu, and pick Folder List,
|
||||
1. A for add, ^t for list
|
||||
|
||||
### Emacs
|
||||
|
||||
`M-x gnus` in [[emacs]] can read news, but you better know [[emacs]] first before you start.
|
||||
`M-x gnus` in emacs can read news, but you better know [emacs](emacs.html) first before you start.
|
||||
|
||||
### lynx
|
||||
|
||||
[[lynx]] reads news, a la `lynx news://news.tilde.club/tilde.general`. It can even post news, but you have to design your own headers.
|
||||
Lynx reads news, a la `lynx nntp://news.tilde.club/local.general`. It can even post news, but you have to design your own headers.
|
||||
|
||||
### tin
|
||||
There is also [tin](tin.html).
|
||||
|
||||
### Thunderbird
|
||||
If you're using Thunderbird for email, it can also be configured for news.
|
||||
If you are a Tilde.club member and already using Thunderbird for email;
|
||||
it can also be configured for news.
|
||||
|
||||
1. Go to the `Tools` menu, -> `Account Settings`
|
||||
1. Go to the `Tools` menu
|
||||
(`Edit` menu in case of Unix-like system) -> `Account Settings`.
|
||||
Or in versions without a menu bar:
|
||||
hamburger menu -> `Preferences` -> `Account Settings`.
|
||||
1. Under `Account Actions` click `Add Other Account...`
|
||||
1. Select "Newsgroup Account"
|
||||
1. Type in the name you and email address you want associated with your posts.
|
||||
This can be your real name and tilde.club email address, or any other name
|
||||
(like your tilde username) and any other email address.
|
||||
1. For the "Newsgroup Server" type `news.tilde.club`. Give it a name
|
||||
("news.tilde.club" works fine), confirm a couple of times and you're done!
|
||||
You should see a new entry for news.tilde.club in your accounts list.
|
||||
("news.tilde.club" works fine), confirm a couple of times,
|
||||
and you should see a new entry for news.tilde.club
|
||||
in the left panel of the Account Settings dialog.
|
||||
1. Select `Server Settings` under it,
|
||||
and change `Connection security` to "SSL/TLS";
|
||||
you would see that the `Port` setting above it changed to 563.
|
||||
(If it doesn't do that automatically,
|
||||
make it so)
|
||||
1. Press OK to close the Account Settings dialog.
|
||||
1. Right click on "news.tilde.club" in your accounts list and click
|
||||
`Subscribe...`
|
||||
1. You should see a dialog with a tree of news topics. Click one and click
|
||||
`Subscribe` to subscribe to the topic. When Thunderbird is running, it will
|
||||
periodically check for new messages to each of these topics. You will also
|
||||
see a list of topics in your accounts list with unread counts.
|
||||
1. To post to a topic, open the topic and click the "Write" button.
|
||||
1. To post to a topic, open the topic and click the "Write" button.
|
||||
1. When you click `Send` on your new post,
|
||||
Thunderbird would ask for your Tilde.club username and password
|
||||
(which is the same as the one you use on your [Tilde.club email](email.html)).
|
||||
|
|
|
@ -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
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: .vimrc file
|
||||
category: tutorial
|
||||
title: Vim (editor) .vimrc file
|
||||
category: software
|
||||
---
|
||||
|
||||
The file `.vimrc` in your home directory has instructions for [[vim]]
|
||||
|
@ -16,4 +16,4 @@ au BufRead,BufNewFile *.md set filetype=markdown
|
|||
``
|
||||
|
||||
For more suggestions in deep depth on how to set up your `.vimrc` please read
|
||||
[this tutorial from Doug Black](http://dougblack.io/words/a-good-vimrc.html).
|
||||
[this tutorial from Doug Black](https://web.archive.org/web/20161224112739/https://dougblack.io/words/a-good-vimrc.html). (The website's no longer up, it's on the 'net archive.
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
---
|
||||
title: Don't use VPN services
|
||||
author: joepie91 on github
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
# Don't use VPN services.
|
||||
|
||||
No, seriously, don't. You're probably reading this because you've asked what VPN service to use, and this is the answer.
|
||||
|
||||
*Note: The content in this post does not apply to using VPN for their intended purpose; that is, as a virtual private (internal) network. It only applies to using it as a glorified proxy, which is what every third-party "VPN provider" does.*
|
||||
|
||||
- A Russian translation of this article can be found [here](https://tdemin.github.io/posts/2017-08-13-dont-use-vpn-services_ru), contributed by Timur Demin.
|
||||
- A Turkish translation can be found [here](https://write.as/nwz9t04yfjwlv0yj.md), contributed by agyild.
|
||||
- There's also [this article](https://schub.io/blog/2019/04/08/very-precarious-narrative.html) about VPN services, which is honestly better written (and has more cat pictures!) than my article.
|
||||
|
||||
## Why not?
|
||||
|
||||
Because a VPN in this sense is just a glorified proxy. The VPN provider can see all your traffic, and do with it what they want - including logging.
|
||||
|
||||
## But my provider doesn't log!
|
||||
|
||||
There is no way for you to verify that, and of course this is what a malicious VPN provider would claim as well. In short: the only safe assumption is that *every* VPN provider logs.
|
||||
|
||||
And remember that it is in a VPN provider's best interest to log their users - it lets them deflect blame to the customer, if they ever were to get into legal trouble. The $10/month that you're paying for your VPN service doesn't even pay for the lawyer's *coffee*, so expect them to hand you over.
|
||||
|
||||
## But a provider would lose business if they did that!
|
||||
|
||||
I'll believe that when HideMyAss goes out of business. They gave up their users years ago, and [this was widely publicized](http://www.theregister.co.uk/2011/09/26/hidemyass_lulzsec_controversy/). The reality is that most of their customers will either not care or not even be aware of it.
|
||||
|
||||
## But I pay anonymously, using Bitcoin/PaysafeCard/Cash/drugs!
|
||||
|
||||
Doesn't matter. You're still connecting to their service from your own IP, and they can log that.
|
||||
|
||||
## But I want more security!
|
||||
|
||||
VPNs don't provide security. They are just a glorified proxy.
|
||||
|
||||
## But I want more privacy!
|
||||
|
||||
VPNs don't provide privacy, with a few exceptions (detailed below). They are just a proxy. If somebody wants to tap your connection, they can still do so - they just have to do so at a different point (ie. when your traffic leaves the VPN server).
|
||||
|
||||
## But I want more encryption!
|
||||
|
||||
Use SSL/TLS and HTTPS (for centralized services), or end-to-end encryption (for social or P2P applications). VPNs can't magically encrypt your traffic - it's simply not technically possible. If the endpoint expects plaintext, there is *nothing* you can do about that.
|
||||
|
||||
When using a VPN, the *only* encrypted part of the connection is from you to the VPN provider. From the VPN provider onwards, it is the same as it would have been without a VPN. And remember, __the VPN provider can see and mess with all your traffic.__
|
||||
|
||||
## But I want to confuse trackers by sharing an IP address!
|
||||
|
||||
Your IP address is a largely irrelevant metric in modern tracking systems. Marketers have gotten wise to these kind of tactics, and combined with increased adoption of [CGNAT](https://en.wikipedia.org/wiki/Carrier-grade_NAT) and an ever-increasing amount of devices per household, it just isn't a reliable data point anymore.
|
||||
|
||||
Marketers will almost always use some kind of other metric to identify and distinguish you. That can be anything from a useragent to a [fingerprinting profile](https://panopticlick.eff.org/). A VPN cannot prevent this.
|
||||
|
||||
## So when should I use a VPN?
|
||||
|
||||
There are roughly two usecases where you might want to use a VPN:
|
||||
|
||||
1. You are on a known-hostile network (eg. a public airport WiFi access point, or an ISP that is known to use MITM), and you want to work around that.
|
||||
2. You want to hide your IP from a very specific set of non-government-sanctioned adversaries - for example, circumventing a ban in a chatroom or preventing anti-piracy scareletters.
|
||||
|
||||
In the second case, you'd probably just want a regular proxy *specifically* for that traffic - sending *all* of your traffic over a VPN provider (like is the default with almost every VPN client) will still result in the provider being able to snoop on and mess with your traffic.
|
||||
|
||||
However, in practice, __just don't use a VPN provider at all, even for these cases.__
|
||||
|
||||
## So, then... what?
|
||||
|
||||
If you absolutely need a VPN, and you understand what its limitations are, purchase a VPS and set up your own (either using something like [Streisand](https://github.com/StreisandEffect/streisand) or manually - I recommend using Wireguard). I will not recommend any specific providers (diversity is good!), but there are plenty of cheap ones to be found on [LowEndTalk](https://www.lowendtalk.com/categories/offers).
|
||||
|
||||
## But how is that any better than a VPN service?
|
||||
|
||||
A VPN provider *specifically seeks out* those who are looking for privacy, and who may thus have interesting traffic. Statistically speaking, it is more likely that a VPN provider will be malicious or a honeypot, than that an arbitrary generic VPS provider will be.
|
||||
|
||||
## So why do VPN services exist? Surely they must serve some purpose?
|
||||
|
||||
Because it's easy money. You just set up OpenVPN on a few servers, and essentially start reselling bandwidth with a markup. You can make every promise in the world, because nobody can verify them. You don't even have to know what you're doing, because again, nobody can verify what you say. It is 100% snake-oil.
|
||||
|
||||
So yes, VPN services do serve a purpose - it's just one that benefits the provider, not you.
|
||||
|
||||
----
|
||||
|
||||
__This post is licensed under the [WTFPL](http://cryto.net/~joepie91/blog/LICENSE.txt) or [CC0](https://creativecommons.org/publicdomain/zero/1.0/), at your choice.__ You may distribute, use, modify, translate, and license it in any way.
|
||||
|
||||
----
|
|
@ -3,7 +3,8 @@ title: how to contribute to this wiki
|
|||
author:
|
||||
- benharri
|
||||
- audiodude
|
||||
category: tilde.club
|
||||
- xwindows
|
||||
category: tutorials
|
||||
---
|
||||
|
||||
# Want to contribute to this wiki?
|
||||
|
@ -12,14 +13,15 @@ There are at least 5 ways to contribute to this wiki:
|
|||
|
||||
## The super ultra easy way
|
||||
|
||||
Send an email to [~audiodude](../~audiodude), audiodude@tilde.club. Put the
|
||||
proposed contents in the email in markdown format. You don't have to worry
|
||||
To submit a new article,
|
||||
send an email to [~xwindows](/~xwindows/), `xwindows` _AT_ `tilde.club`.
|
||||
Put the proposed contents in the email in Pandoc Markdown format. You don't have to worry
|
||||
about "frontmatter", just indicate who the author is (your tilde username),
|
||||
what the title of the article should be, and what category, if any, it should
|
||||
fall under.
|
||||
|
||||
**~audiodude** will add the article to the git repo, add the YAML frontmatter,
|
||||
and merge the content into the wiki repo for you. He'll also email you when
|
||||
**~xwindows** will add the article to the git repo, add the YAML frontmatter,
|
||||
and submit the patch for you. He'll also email you back when
|
||||
the content is live on the server so you can show all your friends!
|
||||
|
||||
## The super ultra mega easy way
|
||||
|
@ -27,8 +29,8 @@ the content is live on the server so you can show all your friends!
|
|||
Same as the "super ultra easy way" above, except you don't even have to add
|
||||
Markdown formatting if you don't want. Just send an email with your wiki
|
||||
article, and all the "metadata" aka author, title, and category, and
|
||||
**~audiodude** will add the Markdown formatting for you (in addition to all the
|
||||
things from the super ultra easy way).
|
||||
**~xwindows** will add the Pandoc Markdown formatting for you
|
||||
(in addition to all the things from the super ultra easy way).
|
||||
|
||||
## The easy-ish way
|
||||
|
||||
|
@ -71,12 +73,6 @@ You can do this right from the Github GUI!
|
|||
1. Make a new article or update an existing one. Make sure that you create the
|
||||
title, author, and category keys in the yaml frontmatter (see an existing
|
||||
article for an example).
|
||||
1. grab a copy of the site's source code
|
||||
git clone https://github.com/tildeclub/site tilde.club && cd tilde.club/wiki
|
||||
|
||||
2. make a new article or update an existing one. make sure that you create the
|
||||
title and author keys in the yaml frontmatter (see an existing article for
|
||||
an example)
|
||||
|
||||
3. submit the code via a PR on github or with git-send-email(1) to root@tilde.club
|
||||
see [git-send-email.io](https://git-send-email.io) for more info on how to use
|
||||
|
@ -87,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.
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
title: Editing your tilde.club site with WinSCP
|
||||
author: vincenz00
|
||||
category: tutorials
|
||||
---
|
||||
|
||||

|
||||
|
||||
## Stuff you'll need:-
|
||||
|
||||
1. tilde.club sign up confirmation email
|
||||
1. WinSCP ([download](https://winscp.net/eng/index.php))
|
||||
1. Public and private keys you used for signing up to tilde.club
|
||||
1. Decent knowledge in HTML(why are you even here otherwise?)
|
||||
|
||||
## Steps to follow:-
|
||||
|
||||
1. Hopefully by now you've got your sign up confirmation by email and are
|
||||
looking forward to creating your own page, keep the password provided in
|
||||
the back of your head, it comes into play later.
|
||||
1. Download and install WinSCP
|
||||
1. Now when you open WinSCP, you'll meet with the login page.
|
||||
1. Enter the following here:-
|
||||
- Host name: tilde.club
|
||||
- Username: username you used to sign up to tilde.club
|
||||
- Password: the password given in the sign up confirmation email
|
||||
1. Now the final step before you go ahead and login. Go to
|
||||
advanced=>SSH=>Authentication and under authentication parameters, add your
|
||||
private key by locating it by using the file browser.
|
||||
1. Add your public key in the same tab by copying it from the file and
|
||||
adding it to the “Display public key” button.
|
||||
1. Close that tab and now save your preferences for making logging in
|
||||
easier.
|
||||
1. Login and after logging in you can find your index.html file present
|
||||
there.
|
||||
1. ???
|
||||
1. profit
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/sh
|
||||
|
||||
for txt in ./*.txt; do
|
||||
printf "0%s\t%s\ttilde.club\t70\n" "$(head -n1 $txt)" "/wiki/${txt#"./"}"
|
||||
done
|
||||
|
|
@ -14,9 +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{white-space: pre-wrap;}
|
||||
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; padding: 0;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
|
@ -46,42 +48,56 @@ $highlighting-css$
|
|||
$for(include-before)$
|
||||
$include-before$
|
||||
$endfor$
|
||||
$if(title)$
|
||||
<header>
|
||||
<h1 class="title">$title$</h1>
|
||||
<a href="/wiki/">< back to wiki home</a>
|
||||
$if(subtitle)$
|
||||
<p class="subtitle">$subtitle$</p>
|
||||
$endif$
|
||||
<div class="content">
|
||||
$if(title)$
|
||||
<header>
|
||||
<h1 id="fancyboi" class="title">$title$</h1>
|
||||
|
||||
$if(author)$
|
||||
<p>authors:</p>
|
||||
$for(author)$
|
||||
<p class="author"><a href="/~$author$/">~$author$</a></p>
|
||||
$endfor$
|
||||
<hr>
|
||||
$endif$
|
||||
$if(subtitle)$
|
||||
<p class="subtitle">$subtitle$</p>
|
||||
$endif$
|
||||
|
||||
$if(date)$
|
||||
<p class="date">$date$</p>
|
||||
$if(author)$
|
||||
<p>authors:</p>
|
||||
$for(author)$
|
||||
<p class="author"><a href="/~$author$/">~$author$</a></p>
|
||||
$endfor$
|
||||
$endif$
|
||||
|
||||
$if(date)$
|
||||
<p class="date">$date$</p>
|
||||
$endif$
|
||||
</header>
|
||||
$endif$
|
||||
</header>
|
||||
$endif$
|
||||
$if(toc)$
|
||||
$if(toc-title)$
|
||||
<h2>$toc-title$</h2>
|
||||
$endif$
|
||||
<nav id="$idprefix$TOC">
|
||||
$table-of-contents$
|
||||
</nav>
|
||||
<hr>
|
||||
<br>
|
||||
<div class="grid">
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
|
||||
$if(toc)$
|
||||
$if(toc-title)$
|
||||
<h2>$toc-title$</h2>
|
||||
$endif$
|
||||
<nav id="$idprefix$TOC">
|
||||
$table-of-contents$
|
||||
</nav>
|
||||
<hr>
|
||||
$endif$
|
||||
<!-- unindent body so that code blocks don't have extra indents. See
|
||||
https://stackoverflow.com/questions/42773587/code-block-formatting-issues-with-bootstrap-and-pandoc -->
|
||||
$body$
|
||||
$for(include-after)$
|
||||
$include-after$
|
||||
$endfor$
|
||||
$for(include-after)$
|
||||
$include-after$
|
||||
$endfor$
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<footer class="text-center">
|
||||
<p>ECDSA fingerprint: SHA256:duamOATgnGcfRFFkotCwrAWzZtRjwxm64WAhq5tQRwE</p>
|
||||
<a href="https://github.com/tildeclub/site">page source</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
Loading…
Reference in New Issue