Init Void project
This commit is contained in:
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
github: [josephernest]
|
||||||
|
custom: https://afewthingz.com
|
||||||
7
.htaccess
Normal file
7
.htaccess
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
RewriteEngine On
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteRule ^(.*)$ index.php [QSA,L]
|
||||||
|
RewriteRule \.txt$ index.php [L]
|
||||||
|
</IfModule>
|
||||||
19
LICENSE
Normal file
19
LICENSE
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2014 Joseph Ernest
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
1528
Parsedown.php
Normal file
1528
Parsedown.php
Normal file
File diff suppressed because it is too large
Load Diff
59
README.md
Normal file
59
README.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# Low-Tech Lab - Grenoble
|
||||||
|
|
||||||
|
## Local Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start a PHP server
|
||||||
|
php -S localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
Void
|
||||||
|
====
|
||||||
|
|
||||||
|
**Void** is a website creation tool. Just static pages or blog articles? Both are possible with [Void](https://gget.it/void).
|
||||||
|
|
||||||
|
The core is done in a single PHP file of less than 100 lines of code. Huh, this is bad? See the discussion [here](https://gget.it/void/article/03).
|
||||||
|
What about performance? See [here](https://gget.it/void/article/05-perf).
|
||||||
|
|
||||||
|
Screenshot
|
||||||
|
----
|
||||||
|
|
||||||
|
[](https://gget.it/void/demo/)
|
||||||
|
|
||||||
|
About
|
||||||
|
----
|
||||||
|
|
||||||
|
Author: Joseph Ernest ([@JosephErnest](https:/twitter.com/JosephErnest))
|
||||||
|
|
||||||
|
Sponsors and consulting
|
||||||
|
----
|
||||||
|
|
||||||
|
I am available for Python, Data science, ML, Automation consulting. Please contact me on https://afewthingz.com for freelancing requests.
|
||||||
|
|
||||||
|
Do you want to support the development of my open-source projects? Please contact me!
|
||||||
|
|
||||||
|
I am currently sponsored by [CodeSigningStore.com](https://codesigningstore.com/). Thank you to them for providing a DigiCert Code Signing Certificate and supporting open source software.
|
||||||
|
|
||||||
|
Credit
|
||||||
|
----
|
||||||
|
|
||||||
|
**Void** uses the [Parsedown](https://github.com/erusev/parsedown) library, licensed under MIT license.
|
||||||
|
|
||||||
|
License
|
||||||
|
----
|
||||||
|
MIT license
|
||||||
|
|
||||||
|
FAQ
|
||||||
|
----
|
||||||
|
|
||||||
|
**Question: How to add automatic code highlighting in articles / pages?**
|
||||||
|
|
||||||
|
Use the library `highlight.js` by adding these three lines in the `<header>` part of `index.php`:
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.5/styles/default.min.css">
|
||||||
|
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.5/highlight.min.js"></script>
|
||||||
|
<script>hljs.initHighlightingOnLoad();</script>
|
||||||
|
|
||||||
|
**Question: How to count the number of unique visitors per day (analytics)?**
|
||||||
|
|
||||||
|
[See this blog article](https://gget.it/void/article/simpleanalytics).
|
||||||
7
article/01.txt
Normal file
7
article/01.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
TITLE:First article
|
||||||
|
DATE:2015 April 9th
|
||||||
|
AUTHOR:Jo
|
||||||
|
|
||||||
|
It's very simple to remove this useless article. Just delete the file `/article/01.txt` and that's it.
|
||||||
|
|
||||||
|

|
||||||
20
article/02-tech.txt
Normal file
20
article/02-tech.txt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
TITLE:A technical article
|
||||||
|
DATE:2015 April 9th
|
||||||
|
AUTHOR:Jo
|
||||||
|
|
||||||
|
This Python code is really good:
|
||||||
|
|
||||||
|
def good():
|
||||||
|
a = 1
|
||||||
|
b = 2
|
||||||
|
c = a + b
|
||||||
|
return None
|
||||||
|
|
||||||
|
This one is *even better:*
|
||||||
|
|
||||||
|
def better():
|
||||||
|
a = 128
|
||||||
|
b = 256
|
||||||
|
c = a * b
|
||||||
|
return None
|
||||||
|
|
||||||
12
article/03-coolarticle.txt
Normal file
12
article/03-coolarticle.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
TITLE:A new article
|
||||||
|
DATE:2015 April 10th
|
||||||
|
URL:coolarticle
|
||||||
|
AUTHOR:Jo
|
||||||
|
|
||||||
|
This is a sample blog article. What to do now?
|
||||||
|
* write a new article,
|
||||||
|
* listen to [some music](http://soundcloud.com/shura/sets/shura-just-once),
|
||||||
|
* or do something else?
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
BIN
assets/brand.jpg
Normal file
BIN
assets/brand.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 321 KiB |
86
index.php
Normal file
86
index.php
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
$sitename = "SomeWebsite";
|
||||||
|
$blogpagename = "blog";
|
||||||
|
|
||||||
|
error_reporting(0);
|
||||||
|
|
||||||
|
function getpage($page)
|
||||||
|
{
|
||||||
|
$pagestr = file_get_contents($page);
|
||||||
|
list($pageheader, $pagecontent) = preg_split('~(?:\r?\n){2}~', $pagestr, 2); // split into 2 parts : before/after the first blank line
|
||||||
|
preg_match("/^TITLE:(.*)$/m", $pageheader, $matches1); // for articles: title // for pages: title displayed in top-menu
|
||||||
|
preg_match("/^AUTHOR:(.*)$/m", $pageheader, $matches2); // for articles only
|
||||||
|
preg_match("/^DATE:(.*)$/m", $pageheader, $matches3); // for articles only
|
||||||
|
preg_match("/^(NOMENU:1)$/m", $pageheader, $matches4); // for pages only: if NOMENU:1, no link in top-menu
|
||||||
|
preg_match("/^URL:(.*)$/m", $pageheader, $matches5); // for articles: article's link ; for pages: top-menu's link
|
||||||
|
return array($pagecontent, $matches1[1], trim($matches2[1]), $matches3[1], $matches4[1], trim($matches5[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$siteroot = substr($_SERVER['PHP_SELF'], 0, - strlen(basename($_SERVER['PHP_SELF']))); // must have trailing slash, we don't use dirname because it can produce antislash on Windows
|
||||||
|
$requestedpage = basename(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
|
||||||
|
if (parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) === $siteroot) { $requestedpage = ""; } // check if homepage
|
||||||
|
$type = strpos($_SERVER['REQUEST_URI'], 'article/') ? 'article' : 'page';
|
||||||
|
$pages = glob("./" . $type ."/*$requestedpage.{txt,md}", GLOB_BRACE);
|
||||||
|
if ($pages) { $page = $pages[0]; } else { $page = "./page/HIDDEN-404.txt"; $type = 'page'; } // default 404 error page
|
||||||
|
list($pagecontent, $pagetitle, $pageauthor, $pagedate, $pagenomenu, $pageurl) = getpage($page);
|
||||||
|
if (!$pageurl) { $pageurl = pathinfo($page)['filename']; }
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><?php echo (trim($pagetitle) ? "$sitename - $pagetitle" : "$sitename")?></title>
|
||||||
|
<base href="<?php echo htmlspecialchars($siteroot, ENT_QUOTES, 'UTF-8'); ?>">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="header">
|
||||||
|
<div class="logo"><a href="."><?php echo $sitename;?></a></div>
|
||||||
|
<ul class="menu">
|
||||||
|
<?php
|
||||||
|
$pages = glob("./page/*.{txt,md}", GLOB_BRACE);
|
||||||
|
foreach($pages as $page)
|
||||||
|
{
|
||||||
|
list($menupagecontent, $menupagetitle, $menupageauthor, $menupagedate, $menupagenomenu, $menupageurl) = getpage($page);
|
||||||
|
if (!$menupagenomenu) { echo "<li><a href=\"" . ($menupageurl ? $menupageurl : strtolower($menupagetitle)) . "\">$menupagetitle</a></li>"; }
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="main">
|
||||||
|
|
||||||
|
<?php
|
||||||
|
require 'Parsedown.php';
|
||||||
|
|
||||||
|
if ($type === "article")
|
||||||
|
{
|
||||||
|
echo "<div class=\"article\"><a href=\"article/$pageurl\"><h2 class=\"articletitle\">$pagetitle</h2><div class=\"articleinfo\">by $pageauthor, on $pagedate</div></a>";
|
||||||
|
echo (new Parsedown())->text($pagecontent);
|
||||||
|
echo "</div>";
|
||||||
|
}
|
||||||
|
else if ($type === "page") { echo "<div class=\"page\">" . (new Parsedown())->text($pagecontent) . "</div>"; }
|
||||||
|
|
||||||
|
if ($requestedpage === $blogpagename)
|
||||||
|
{
|
||||||
|
$pages = array_slice(array_reverse(glob("./article/*.{txt,md}", GLOB_BRACE)), $_GET['start'], 10);
|
||||||
|
foreach($pages as $page)
|
||||||
|
{
|
||||||
|
list($pagecontent, $pagetitle, $pageauthor, $pagedate, $pagenomenu, $pageurl) = getpage($page);
|
||||||
|
if (!$pageurl) { $pageurl = pathinfo($page)['filename']; }
|
||||||
|
echo "<div class=\"article\"><a href=\"article/$pageurl\"><h2 class=\"articletitle\">$pagetitle</h2><div class=\"articleinfo\">by $pageauthor, on $pagedate</div></a>";
|
||||||
|
echo (new Parsedown())->text($pagecontent);
|
||||||
|
echo "</div>";
|
||||||
|
}
|
||||||
|
if ($_GET['start'] > 0) { echo "<a href=\"" . $blogpagename . (($_GET['start'] > 10) ? "?start=" . ($_GET['start'] - 10) : "") . "\">Newer articles</a> "; }
|
||||||
|
if (count(array_slice(array_reverse(glob("./article/*.{txt,md}", GLOB_BRACE)), $_GET['start'], 11)) > 10) { echo "<a href=\"" . $blogpagename . "?start=" . ($_GET['start'] + 10) . "\">Older articles</a>"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<div class="left"><a href="">© <?php echo date('Y') . " " . $sitename; ?></a></div>
|
||||||
|
<div class="right">Powered by <a href="https://github.com/josephernest/void">Void</a>.</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
6
page/01-home.txt
Normal file
6
page/01-home.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
TITLE:Home
|
||||||
|
NOMENU:1
|
||||||
|
|
||||||
|
This is a demo website powered by [Void](http://thisisvoid.org).
|
||||||
|
|
||||||
|

|
||||||
7
page/02-about.txt
Normal file
7
page/02-about.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
TITLE:About
|
||||||
|
|
||||||
|
#About
|
||||||
|
|
||||||
|
**SomeWebsite** is a great website.
|
||||||
|
|
||||||
|
Do you want to get the latest news? The [blog](blog) is here!
|
||||||
2
page/03-contact.txt
Normal file
2
page/03-contact.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
TITLE:Contact
|
||||||
|
URL:mailto:contact@somewebsi.te
|
||||||
3
page/04-blog.txt
Normal file
3
page/04-blog.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
TITLE:Blog
|
||||||
|
|
||||||
|
#Blog
|
||||||
7
page/HIDDEN-404.txt
Normal file
7
page/HIDDEN-404.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
NOMENU:1
|
||||||
|
|
||||||
|
#Page not found
|
||||||
|
|
||||||
|
Are you sure you entered the URL correctly?
|
||||||
|
|
||||||
|
Return to [home page](.).
|
||||||
41
style.css
Normal file
41
style.css
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
.header { margin-bottom: 1.5em; padding-bottom: 1em; min-height: 3.1em; border-bottom: 2px solid #2980b9; }
|
||||||
|
.header a:hover, .footer a:hover { border: none; }
|
||||||
|
.header:after { content: ''; clear: both; display: block; }
|
||||||
|
.logo { float: left; margin-top: 0.3em; }
|
||||||
|
.logo a { background-color: #2980b9; color: white; padding: 0.2em 0.2em; font-size: 1.4em; }
|
||||||
|
.logo a:hover { color: white; }
|
||||||
|
.menu { float: right; padding: 0; margin: 0; }
|
||||||
|
.menu li { display: inline-block; padding: 0.4em 0 0.4em 2em; }
|
||||||
|
.menu a { font-weight: 600; }
|
||||||
|
.main { min-height: 18em; padding-bottom: 3em; border-bottom: 2px solid #2980b9; }
|
||||||
|
.main a { border-bottom: 2px solid #D1D1D1; }
|
||||||
|
.main p, .main ul { margin-top: 0; margin-bottom: 0.5em; }
|
||||||
|
.page { font-size: 0.95em; }
|
||||||
|
.article { margin-bottom: 4em; font-size: 0.9em; line-height: 1.4em; }
|
||||||
|
.articleinfo { color: #AAA; margin-bottom: 1em; font-size: 0.9em; }
|
||||||
|
.articletitle { margin-bottom: 0; }
|
||||||
|
.footer { padding: 1em 0 2em; font-size: .75em; color: #333; }
|
||||||
|
.footer .left { float: left; }
|
||||||
|
.footer .right { float: right; }
|
||||||
|
|
||||||
|
* { margin: 0; }
|
||||||
|
html { height: 100%; font-family: sans-serif; font-size: 1.25em; }
|
||||||
|
body { margin: 0 auto; padding: 3em 3em 1em 3em; max-width: 44em; line-height: 1.5em; color: #333; }
|
||||||
|
a { color: black; text-decoration: none; transition: color .2s, background .2s, border .2s; }
|
||||||
|
a:hover { border-bottom: 2px solid black; color: #2980b9; }
|
||||||
|
img { max-width: 100%; }
|
||||||
|
h1 { font-size: 2em; font-weight: 300; margin-bottom: .5em; line-height: 1.25em; color: #2980b9; }
|
||||||
|
h2 { font-size: 1.95em; font-weight: 300; margin-bottom: .5em; line-height: 1.25em; }
|
||||||
|
h3 { font-size: 1.4em; font-weight: 400; margin-bottom: .5em; line-height: 1.25em; }
|
||||||
|
h4 { font-size: 1.2em; font-weight: 600; margin-bottom: .5em; line-height: 1.25em; }
|
||||||
|
blockquote { background: none; border-left: 3px solid #777; color: #777; padding: 0 0 0 10px; margin-left: 1.2em; }
|
||||||
|
hr { margin: 2em 0; height: 2px; background-color: #D1D1D1; border: none; }
|
||||||
|
pre { background-color: #F1F1F1; margin-bottom: 0.7em; line-height: 1.5em; }
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
body { padding: 1.25em; }
|
||||||
|
.header { padding-bottom: 0; }
|
||||||
|
.logo { float:none; }
|
||||||
|
.menu { float:none; margin-top: 2em; }
|
||||||
|
.menu li { display: block; padding: 0.2em 0; margin: 0.2em; border-top: 1px solid #D1D1D1; }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user