Massive templating system and router overhaul.
This commit is contained in:
parent
9781827778
commit
09399a411b
24 changed files with 854 additions and 792 deletions
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
$quotes = isset($quotes) && is_array($quotes) ? $quotes : FM_FEET;
|
||||
$baseUrl = empty($external) ? '' : '//' . $_SERVER['HTTP_HOST'];
|
||||
?>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="footer-text">© flashwave 2010-<?=date('Y');?> - <?=($quotes[array_rand($quotes)]);?></div>
|
||||
</div>
|
||||
<?php if(isset($onload) && is_array($onload)): ?>
|
||||
<script type="text/javascript">
|
||||
window.fm = { onload: <?=json_encode($onload);?> };
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
<script src="<?=$baseUrl;?>/assets/2021.js" charset="utf-8" type="text/javascript"></script>
|
||||
<?php if(isset($scripts) && is_array($scripts)) foreach($scripts as $script): ?>
|
||||
<script src="<?=$script;?>" charset="utf-8" type="text/javascript"></script>
|
||||
<?php endforeach;?>
|
||||
</body>
|
||||
</html>
|
|
@ -18,6 +18,7 @@ define('MKI_DIR_LIB', MKI_ROOT . '/lib');
|
|||
define('MKI_DIR_PUB', MKI_ROOT . '/public');
|
||||
define('MKI_DIR_PAGES', MKI_ROOT . '/pages');
|
||||
define('MKI_DIR_CONFIG', MKI_ROOT . '/config');
|
||||
define('MKI_DIR_TEMPLATES', MKI_ROOT . '/tpl');
|
||||
|
||||
require_once MKI_DIR_LIB . '/index-new/index.php';
|
||||
|
||||
|
@ -47,3 +48,5 @@ if(!empty($dbConfig))
|
|||
|
||||
if(empty($db))
|
||||
$db = (new NullDbBackend)->createConnection(new NullDbConnectionInfo);
|
||||
|
||||
$ctx = new MakaiContext($db);
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
$router->get('/contact.php', mkiRedirect('/contact'));
|
||||
$router->get('/contact.html', mkiRedirect('/contact'));
|
||||
$router->get('/nintendo', mkiRedirect('/contact'));
|
||||
$router->get('/nintendo.php', mkiRedirect('/contact'));
|
||||
|
||||
$router->get('/contact', function() use ($db) {
|
||||
$contacts = (new Contacts($db))->getAll();
|
||||
|
||||
$body = fm_component('header', [
|
||||
'title' => 'flash.moe / contact',
|
||||
]);
|
||||
|
||||
$body .= <<<HTML
|
||||
<div class="section">
|
||||
<div class="section-content">
|
||||
<div class="section-background"></div>
|
||||
<h1>Contacts</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="socials">
|
||||
HTML;
|
||||
|
||||
foreach($contacts as $contact) {
|
||||
$body .= '<div class="social social-' . $contact->getName() . '" style="--social-colour: ' . $contact->getColourHex() . '">';
|
||||
|
||||
if($contact->hasLink()) {
|
||||
$body .= '<a href="' . $contact->getLink() . '" class="social-background" target="_blank" rel="noopener"></a>';
|
||||
} else {
|
||||
$body .= '<div class="social-background" onclick="fm.selectTextInElement(this.parentNode.querySelector(\'.social-handle\')); fm.copySelectedText();"></div>';
|
||||
}
|
||||
|
||||
$body .= <<<HTML
|
||||
<div class="social-icon {$contact->getIcon()}"></div>
|
||||
<div class="social-content">
|
||||
<div class="social-name">{$contact->getName()}</div>
|
||||
<div class="social-handle">{$contact->getDisplay()}</div>
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
|
||||
$body .= '</div>';
|
||||
|
||||
$body .= fm_component('footer');
|
||||
|
||||
return $body;
|
||||
});
|
270
pages/index.php
270
pages/index.php
|
@ -1,270 +0,0 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
$router->get('/about', mkiRedirect('/'));
|
||||
$router->get('/about.html', mkiRedirect('/'));
|
||||
$router->get('/about.php', mkiRedirect('/'));
|
||||
$router->get('/index.php', mkiRedirect('/'));
|
||||
$router->get('/index.html', mkiRedirect('/'));
|
||||
$router->get('/related.php', mkiRedirect('/'));
|
||||
$router->get('/related.html', mkiRedirect('/'));
|
||||
$router->get('/friends', mkiRedirect('/'));
|
||||
$router->get('/friends.php', mkiRedirect('/'));
|
||||
$router->get('/friends.html', mkiRedirect('/'));
|
||||
$router->get('/related', mkiRedirect('/'));
|
||||
$router->get('/etc.php', mkiRedirect('/'));
|
||||
$router->get('/etc.html', mkiRedirect('/'));
|
||||
$router->get('/etcetera', mkiRedirect('/'));
|
||||
$router->get('/etcetera.html', mkiRedirect('/'));
|
||||
$router->get('/etcetera.php', mkiRedirect('/'));
|
||||
$router->get('/misc', mkiRedirect('/'));
|
||||
$router->get('/misc.html', mkiRedirect('/'));
|
||||
$router->get('/misc.php', mkiRedirect('/'));
|
||||
$router->get('/etc', mkiRedirect('/'));
|
||||
$router->get('/365', mkiRedirect('/'));
|
||||
$router->get('/donate', mkiRedirect('/'));
|
||||
$router->get('/blog.php', mkiRedirect('/'));
|
||||
$router->get('/blog-post.php', mkiRedirect('/'));
|
||||
$router->get('/blog/:id', mkiRedirect('/'));
|
||||
$router->get('/old-blog', mkiRedirect('/'));
|
||||
$router->get('/old-blog/:id', mkiRedirect('/'));
|
||||
|
||||
$router->get('/header-bgs.json', function() {
|
||||
return json_encode(FM_BGS);
|
||||
});
|
||||
|
||||
$router->get('/now-listening', function() {
|
||||
$offset = (int)filter_input(INPUT_GET, 'offset', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
$body = fm_component('header', [
|
||||
'title' => 'flash.moe / now listening',
|
||||
'do_fullscreen_header' => true,
|
||||
'is_now_playing' => true,
|
||||
'offset' => $offset,
|
||||
]);
|
||||
|
||||
$body .= fm_component('footer', [
|
||||
'hide' => true,
|
||||
'onload' => [
|
||||
['fm.initIndex', 10],
|
||||
],
|
||||
]);
|
||||
|
||||
return $body;
|
||||
});
|
||||
|
||||
$router->get('/now-listening.json', function() {
|
||||
$lfmInfo = cache_output('lastfm', 10, function() {
|
||||
return json_decode(file_get_contents('https://now.flash.moe/get.php?u=flashwave_'));
|
||||
});
|
||||
|
||||
if(empty($lfmInfo[0]->name))
|
||||
return [];
|
||||
|
||||
$lfmInfo = $lfmInfo[0];
|
||||
|
||||
return [
|
||||
'name' => strval($lfmInfo->name),
|
||||
'now_playing' => !empty($lfmInfo->nowplaying),
|
||||
'url' => strval($lfmInfo->url),
|
||||
'cover' => !empty($lfmInfo->images->large) ? strval($lfmInfo->images->large) : '',
|
||||
'artist' => [
|
||||
'name' => !empty($lfmInfo->artist->name) ? strval($lfmInfo->artist->name) : '',
|
||||
'url' => explode('/_/', strval($lfmInfo->url))[0],
|
||||
],
|
||||
];
|
||||
});
|
||||
|
||||
$router->get('/np.php', function() {
|
||||
header('Content-Type: text/xml');
|
||||
return cache_output('lastfm-xml', 10, function() {
|
||||
return file_get_contents('https://now.flash.moe/get.php?u=flashwave_&f=xml');
|
||||
});
|
||||
});
|
||||
|
||||
$router->get('/home', function() {
|
||||
$body = fm_component('header', [
|
||||
'title' => 'flash.moe / homepage',
|
||||
'do_fullscreen_header' => true,
|
||||
]);
|
||||
|
||||
$body .= <<<HTML
|
||||
<div class="php">
|
||||
<div class="php-time">
|
||||
<div class="php-time-analog">
|
||||
<div class="clock">
|
||||
<div class="clock-background"></div>
|
||||
<div class="clock-center"></div>
|
||||
<div class="clock-hand clock-hand-hours"><div class="clock-hand-display"></div></div>
|
||||
<div class="clock-hand clock-hand-minutes"><div class="clock-hand-display"></div></div>
|
||||
<div class="clock-hand clock-hand-seconds"><div class="clock-hand-display"></div></div>
|
||||
<div class="clock-number clock-number-3"><div class="clock-number-display"></div></div>
|
||||
<div class="clock-number clock-number-6"><div class="clock-number-display"></div></div>
|
||||
<div class="clock-number clock-number-9"><div class="clock-number-display"></div></div>
|
||||
<div class="clock-number clock-number-12"><div class="clock-number-display"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="php-time-alter">
|
||||
<div class="php-time-digital">
|
||||
<div class="php-time-digital-hours">--</div>
|
||||
<div class="php-time-digital-separator">:</div>
|
||||
<div class="php-time-digital-minutes">--</div>
|
||||
</div>
|
||||
<div class="php-time-date">
|
||||
<span class="php-date-label">Week</span>
|
||||
<span class="php-date-week">--</span>
|
||||
<span class="php-date-label"> — </span>
|
||||
<span class="php-date-year">----</span><span class="php-date-label">-</span><span class="php-date-month">---</span><span class="php-date-label">-</span><span class="php-date-day">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form class="php-search" method="get" action="https://duckduckgo.com/">
|
||||
<div class="php-search-input">
|
||||
<input type="search" name="q" placeholder="Search using DuckDuckGo..."/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
$body .= fm_component('footer', [
|
||||
'hide' => true,
|
||||
'skip_analytics' => true,
|
||||
'onload' => [
|
||||
['fm.initClock'],
|
||||
['fm.initIndex', 10],
|
||||
],
|
||||
]);
|
||||
|
||||
return $body;
|
||||
});
|
||||
|
||||
$router->get('/', function() use ($db) {
|
||||
$legacyPage = (string)filter_input(INPUT_GET, 'p');
|
||||
if(!empty($legacyPage)) {
|
||||
$legacyPages = [
|
||||
'projects' => '/projects',
|
||||
'contact' => '/contact',
|
||||
'about' => '/',
|
||||
'etc' => '/etc',
|
||||
'hosted' => '/etc',
|
||||
'friends' => '/related',
|
||||
];
|
||||
|
||||
if(isset($legacyPages[$legacyPage])) {
|
||||
header('Location: ' . $legacyPages[$legacyPage]);
|
||||
return 302;
|
||||
}
|
||||
}
|
||||
|
||||
$projects = (new Projects($db))->getFeatured();
|
||||
$languages = new Languages($db);
|
||||
$contacts = (new Contacts($db))->getHomePage();
|
||||
|
||||
$body = fm_component('header', [
|
||||
'title' => 'flash.moe',
|
||||
'is_index' => true,
|
||||
]);
|
||||
|
||||
$body .= '<div class="index-menu">';
|
||||
|
||||
for($i = 1; $i < count(FM_NAV); ++$i) {
|
||||
$link = FM_NAV[$i];
|
||||
$body .= "<a href=\"{$link['link']}\"";
|
||||
|
||||
if($link['link'][0] === '/' && substr($link['link'], 0, 2) !== '//')
|
||||
$body .= '';
|
||||
|
||||
$body .= ">{$link['title']}</a>";
|
||||
}
|
||||
|
||||
$body .= <<<HTML
|
||||
</div>
|
||||
<div class="index-featured">
|
||||
HTML;
|
||||
|
||||
$body .= <<<HTML
|
||||
<div class="index-feature">
|
||||
<div class="index-feature-header">
|
||||
<a href="/projects" class="index-feature-header-link"></a>
|
||||
<div class="index-feature-header-title">Projects</div>
|
||||
<div class="index-feature-header-more">More</div>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
foreach($projects as $project) {
|
||||
$links = [];
|
||||
if($project->hasHomePageUrl())
|
||||
$links[] = ['class' => 'homepage', 'text' => 'Homepage', 'url' => $project->getHomePageUrl()];
|
||||
if($project->hasSourceUrl())
|
||||
$links[] = ['class' => 'repository', 'text' => 'Source', 'url' => $project->getSourceUrl()];
|
||||
if($project->hasDiscussionUrl())
|
||||
$links[] = ['class' => 'forum', 'text' => 'Discussion', 'url' => $project->getDiscussionUrl()];
|
||||
|
||||
$colour = $project->hasColour() ? $project->getColour() : $languages->getProjectColour($project);
|
||||
$colour = str_pad(dechex($colour), 6, '0', STR_PAD_LEFT);
|
||||
|
||||
$body .= <<<HTML
|
||||
<div class="index-project" style="background-color: #{$colour};">
|
||||
<a href="/projects#{$project->getCleanName()}" class="index-project-anchor"></a>
|
||||
<div class="index-project-content">
|
||||
<div class="index-project-name">{$project->getName()}</div>
|
||||
HTML;
|
||||
|
||||
if($project->hasSummary())
|
||||
$body .= "<div class=\"index-project-summary\">{$project->getSummary()}</div>";
|
||||
|
||||
$body .= '</div>';
|
||||
|
||||
if(!empty($links)) {
|
||||
$body .= '<div class="index-project-links">';
|
||||
|
||||
foreach($links as $link)
|
||||
$body .= "<a class=\"index-project-link index-project-link-{$link['class']}\" href=\"{$link['url']}\" rel=\"noopener\" target=\"_blank\">{$link['text']}</a>";
|
||||
|
||||
$body .= '</div>';
|
||||
}
|
||||
|
||||
$body .= '</div>';
|
||||
}
|
||||
|
||||
$body .= <<<HTML
|
||||
</div>
|
||||
<div class="index-feature">
|
||||
<div class="index-feature-header">
|
||||
<a href="/contact" class="index-feature-header-link"></a>
|
||||
<div class="index-feature-header-title">Contact</div>
|
||||
<div class="index-feature-header-more">More</div>
|
||||
</div>
|
||||
<div class="index-contact">
|
||||
HTML;
|
||||
|
||||
foreach($contacts as $contact) {
|
||||
$body .= "<div class=\"social social-{$contact->getName()}\" style=\"--social-colour: {$contact->getColourHex()}\">";
|
||||
|
||||
if($contact->hasLink()) {
|
||||
$body .= "<a href=\"{$contact->getLink()}\" class=\"social-background\" target=\"_blank\" rel=\"noopener\"></a>";
|
||||
} else {
|
||||
$body .= "<div class=\"social-background\" onclick=\"fm.selectTextInElement(this.parentNode.querySelector('.social-handle')); fm.copySelectedText();\"></div>";
|
||||
}
|
||||
|
||||
$body .= <<<HTML
|
||||
<div class="social-icon {$contact->getIcon()}"></div>
|
||||
<div class="social-content">
|
||||
<div class="social-name">{$contact->getTitle()}</div>
|
||||
<div class="social-handle">{$contact->getDisplay()}</div>
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
|
||||
$body .= '</div></div></div>';
|
||||
|
||||
$body .= fm_component('footer', [
|
||||
'is_index' => true,
|
||||
'onload' => [
|
||||
['fm.initIndex'],
|
||||
],
|
||||
]);
|
||||
|
||||
return $body;
|
||||
});
|
|
@ -1,126 +0,0 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
$router->get('/projects.php', mkiRedirect('/projects'));
|
||||
$router->get('/projects.html', mkiRedirect('/projects'));
|
||||
$router->get('/utilities', mkiRedirect('/projects'));
|
||||
$router->get('/utilities.php', mkiRedirect('/projects'));
|
||||
$router->get('/utilities.html', mkiRedirect('/projects'));
|
||||
|
||||
$router->get('/projects', function() use ($db) {
|
||||
$projects = (new Projects($db))->getAll();
|
||||
$languages = new Languages($db);
|
||||
|
||||
$sections = [
|
||||
'projects' => [
|
||||
'title' => 'Active Projects',
|
||||
'desc' => 'Projects that I work on on a fairly regular basis.',
|
||||
'items' => [],
|
||||
],
|
||||
'tools' => [
|
||||
'title' => 'Tools',
|
||||
'desc' => 'Personal quality of life tools that I update when I need something new from them.',
|
||||
'items' => [],
|
||||
],
|
||||
'archives' => [
|
||||
'title' => 'Archived Projects',
|
||||
'desc' => 'Past projects that I no longer work on.',
|
||||
'items' => [],
|
||||
],
|
||||
];
|
||||
|
||||
foreach($projects as $project) {
|
||||
if($project->isArchived())
|
||||
$sections['archives']['items'][] = $project;
|
||||
else {
|
||||
if($project->isTool())
|
||||
$sections['tools']['items'][] = $project;
|
||||
else
|
||||
$sections['projects']['items'][] = $project;
|
||||
}
|
||||
}
|
||||
|
||||
$body = fm_component('header', [
|
||||
'title' => 'flash.moe / projects',
|
||||
]);
|
||||
|
||||
foreach($sections as $sectionId => $section) {
|
||||
$body .= <<<HTML
|
||||
<div class="section" id="section-{$sectionId}">
|
||||
<div class="section-content">
|
||||
<div class="section-background"></div>
|
||||
<h1>{$section['title']}</h1>
|
||||
<p>{$section['desc']}</p>
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
foreach($section['items'] as $project) {
|
||||
$links = [];
|
||||
if($project->hasHomePageUrl())
|
||||
$links[] = ['class' => 'homepage', 'text' => 'Homepage', 'url' => $project->getHomePageUrl()];
|
||||
if($project->hasSourceUrl())
|
||||
$links[] = ['class' => 'repository', 'text' => 'Source', 'url' => $project->getSourceUrl()];
|
||||
if($project->hasDiscussionUrl())
|
||||
$links[] = ['class' => 'forum', 'text' => 'Discussion', 'url' => $project->getDiscussionUrl()];
|
||||
|
||||
$descLines = $project->hasDescription() ? $project->getDescription()->trim()->split("\n") : [];
|
||||
|
||||
$langs = $languages->getByProject($project);
|
||||
|
||||
if($project->hasColour())
|
||||
$colour = $project->getColour();
|
||||
else
|
||||
foreach($langs as $lang)
|
||||
if($lang->hasColour()) {
|
||||
$colour = $lang->getColour();
|
||||
break;
|
||||
}
|
||||
|
||||
$colour = str_pad(dechex($colour), 6, '0', STR_PAD_LEFT);
|
||||
|
||||
$body .= <<<HTML
|
||||
<div class="project project-type-{$sectionId}" id="{$project->getCleanName()}" style="--project-colour: #{$colour};">
|
||||
<div class="project-content">
|
||||
<div class="project-details">
|
||||
<h2>{$project->getName()}<div class="project-languages">
|
||||
HTML;
|
||||
|
||||
foreach($langs as $lang) {
|
||||
$langColour = str_pad(dechex($lang->getColour() ?? 0), 6, '0', STR_PAD_LEFT);
|
||||
$body .= "<div class=\"project-language\" style=\"--language-colour: #{$langColour};\">{$lang->getName()}</div>";
|
||||
}
|
||||
|
||||
$body .= '</div></h2>';
|
||||
|
||||
if($project->hasSummary())
|
||||
$body .= "<p class=\"project-details-summary\">{$project->getSummary()}</p>";
|
||||
|
||||
foreach($descLines as $line) {
|
||||
$line = $line->trim();
|
||||
|
||||
if($line->isEmpty())
|
||||
continue;
|
||||
|
||||
$body .= "<p>{$line}</p>";
|
||||
}
|
||||
|
||||
$body .= '</div>';
|
||||
|
||||
if(!empty($links)) {
|
||||
$body .= '<div class="project-links">';
|
||||
|
||||
foreach($links as $link)
|
||||
$body .= "<a class=\"project-link project-link-{$link['class']}\" href=\"{$link['url']}\" rel=\"noopener\" target=\"_blank\">{$link['text']}</a>";
|
||||
|
||||
$body .= '</div>';
|
||||
}
|
||||
|
||||
$body .= '</div></div>';
|
||||
}
|
||||
}
|
||||
|
||||
$body .= fm_component('footer');
|
||||
|
||||
return $body;
|
||||
});
|
|
@ -1,92 +0,0 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
$router->get('/ssh.php', function() {
|
||||
$query = '';
|
||||
|
||||
$minLevel = (int)filter_input(INPUT_GET, 'l', FILTER_SANITIZE_NUMBER_INT);
|
||||
if($minLevel > 0)
|
||||
$query .= sprintf('l=%d&', $minLevel);
|
||||
|
||||
if(!empty($_GET['c']))
|
||||
$query .= 'c=1&';
|
||||
|
||||
if(!empty($_GET['j']))
|
||||
$query .= 'j=1&';
|
||||
|
||||
if($query !== '')
|
||||
$query = '?' . substr($query, 0, -1);
|
||||
|
||||
header('Location: /ssh_keys' . $query);
|
||||
return 302;
|
||||
});
|
||||
|
||||
$router->get('/ssh_keys', function() use ($db) {
|
||||
$minLevel = (int)filter_input(INPUT_GET, 'l', FILTER_SANITIZE_NUMBER_INT);
|
||||
$includeComment = !empty($_GET['c']);
|
||||
$json = !empty($_GET['j']);
|
||||
|
||||
$keys = (new SSHKeys($db))->getKeys($minLevel);
|
||||
|
||||
if($json) {
|
||||
$items = [];
|
||||
|
||||
foreach($keys as $key) {
|
||||
$items[] = $item = new \stdClass;
|
||||
$item->algo = $key->getAlgorithm();
|
||||
$item->key = $key->getBody();
|
||||
if($includeComment) {
|
||||
$item->comment = (string)$key->getComment();
|
||||
$item->created = $key->getCreatedAt()->format(\DateTime::ATOM);
|
||||
$item->level = $key->getLevel();
|
||||
}
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
header('Content-Type: text/plain; charset=us-ascii');
|
||||
|
||||
$body = '';
|
||||
|
||||
foreach($keys as $key)
|
||||
$body .= $key->toString($includeComment) . "\n";
|
||||
|
||||
return $body;
|
||||
});
|
||||
|
||||
$router->get('/authorized_keys', function() use ($db) {
|
||||
$keys = (new SSHKeys($db))->getKeys(500);
|
||||
|
||||
header('Content-Type: text/plain; charset=us-ascii');
|
||||
|
||||
$body = '';
|
||||
foreach($keys as $key)
|
||||
$body .= $key->toString(true) . "\n";
|
||||
|
||||
return $body;
|
||||
});
|
||||
|
||||
$router->get('/git_keys_ro', function() use ($db) {
|
||||
$keys = (new SSHKeys($db))->getKeys(100);
|
||||
|
||||
header('Content-Type: text/plain; charset=us-ascii');
|
||||
|
||||
$body = '';
|
||||
foreach($keys as $key)
|
||||
$body .= $key->toString(false) . "\n";
|
||||
|
||||
return $body;
|
||||
});
|
||||
|
||||
$router->get('/git_keys_rw', function() use ($db) {
|
||||
$keys = (new SSHKeys($db))->getKeys(200);
|
||||
|
||||
header('Content-Type: text/plain; charset=us-ascii');
|
||||
|
||||
$body = '';
|
||||
foreach($keys as $key)
|
||||
$body .= $key->toString(false) . "\n";
|
||||
|
||||
return $body;
|
||||
});
|
432
public/index.php
432
public/index.php
|
@ -1,204 +1,268 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
use Index\DateTime;
|
||||
use Index\Routing\Router;
|
||||
use Index\Routing\RoutePathNotFoundException;
|
||||
use Index\Routing\RouteMethodNotSupportedException;
|
||||
use Index\Http\HttpFx;
|
||||
|
||||
require_once __DIR__ . '/../makai.php';
|
||||
|
||||
define('FM_NAV', [
|
||||
['title' => 'Home', 'link' => '/'],
|
||||
['title' => 'Projects', 'link' => '/projects'],
|
||||
['title' => 'Contact', 'link' => '/contact'],
|
||||
define('MKI_REDIRS', [
|
||||
'/about' => '/',
|
||||
'/about.html' => '/',
|
||||
'/about.php' => '/',
|
||||
'/index.php' => '/',
|
||||
'/index.html' => '/',
|
||||
'/related.php' => '/',
|
||||
'/related.html' => '/',
|
||||
'/friends' => '/',
|
||||
'/friends.php' => '/',
|
||||
'/friends.html' => '/',
|
||||
'/related' => '/',
|
||||
'/etc.php' => '/',
|
||||
'/etc.html' => '/',
|
||||
'/etcetera' => '/',
|
||||
'/etcetera.html' => '/',
|
||||
'/etcetera.php' => '/',
|
||||
'/misc' => '/',
|
||||
'/misc.html' => '/',
|
||||
'/misc.php' => '/',
|
||||
'/etc' => '/',
|
||||
'/365' => '/',
|
||||
'/donate' => '/',
|
||||
'/blog.php' => '/',
|
||||
'/blog-post.php' => '/',
|
||||
'/blog/:id' => '/',
|
||||
'/old-blog' => '/',
|
||||
'/old-blog/:id' => '/',
|
||||
'/projects.php' => '/projects',
|
||||
'/projects.html' => '/projects',
|
||||
'/utilities' => '/projects',
|
||||
'/utilities.php' => '/projects',
|
||||
'/utilities.html' => '/projects',
|
||||
'/contact.php' => '/contact',
|
||||
'/contact.html' => '/contact',
|
||||
'/nintendo' => '/contact',
|
||||
'/nintendo.php' => '/contact',
|
||||
]);
|
||||
|
||||
define('FM_BGS', [
|
||||
'/assets/headers/krk-000.jpg', '/assets/headers/krk-001.jpg', '/assets/headers/krk-002.jpg',
|
||||
'/assets/headers/krk-003.jpg', '/assets/headers/krk-004.jpg', '/assets/headers/krk-005.jpg',
|
||||
'/assets/headers/krk-006.jpg', '/assets/headers/krk-007.jpg', '/assets/headers/krk-008.jpg',
|
||||
$router = new HttpFx;
|
||||
|
||||
'/assets/headers/mkt-000.jpg', '/assets/headers/mkt-001.jpg', '/assets/headers/mkt-002.jpg',
|
||||
'/assets/headers/mkt-003.jpg', '/assets/headers/mkt-004.jpg', '/assets/headers/mkt-005.jpg',
|
||||
'/assets/headers/mkt-006.jpg', '/assets/headers/mkt-007.jpg', '/assets/headers/mkt-008.jpg',
|
||||
'/assets/headers/mkt-009.jpg', '/assets/headers/mkt-010.jpg', '/assets/headers/mkt-011.jpg',
|
||||
'/assets/headers/mkt-012.jpg', '/assets/headers/mkt-013.jpg', '/assets/headers/mkt-014.jpg',
|
||||
'/assets/headers/mkt-015.jpg', '/assets/headers/mkt-016.jpg', '/assets/headers/mkt-017.jpg',
|
||||
'/assets/headers/mkt-018.jpg', '/assets/headers/mkt-019.jpg', '/assets/headers/mkt-020.jpg',
|
||||
'/assets/headers/mkt-021.jpg', '/assets/headers/mkt-022.jpg', '/assets/headers/mkt-023.jpg',
|
||||
'/assets/headers/mkt-024.jpg', '/assets/headers/mkt-025.jpg', '/assets/headers/mkt-026.jpg',
|
||||
'/assets/headers/mkt-027.jpg', '/assets/headers/mkt-028.jpg', '/assets/headers/mkt-029.jpg',
|
||||
'/assets/headers/mkt-030.jpg', '/assets/headers/mkt-031.jpg', '/assets/headers/mkt-032.jpg',
|
||||
'/assets/headers/mkt-033.jpg', '/assets/headers/mkt-034.jpg', '/assets/headers/mkt-035.jpg',
|
||||
'/assets/headers/mkt-036.jpg', '/assets/headers/mkt-037.jpg', '/assets/headers/mkt-038.jpg',
|
||||
'/assets/headers/mkt-039.jpg', '/assets/headers/mkt-040.jpg', '/assets/headers/mkt-041.jpg',
|
||||
'/assets/headers/mkt-042.jpg', '/assets/headers/mkt-043.jpg', '/assets/headers/mkt-044.jpg',
|
||||
'/assets/headers/mkt-045.jpg', '/assets/headers/mkt-046.jpg', '/assets/headers/mkt-047.jpg',
|
||||
'/assets/headers/mkt-048.jpg', '/assets/headers/mkt-049.jpg', '/assets/headers/mkt-050.jpg',
|
||||
'/assets/headers/mkt-051.jpg', '/assets/headers/mkt-052.jpg', '/assets/headers/mkt-053.jpg',
|
||||
'/assets/headers/mkt-054.jpg', '/assets/headers/mkt-055.jpg', '/assets/headers/mkt-056.jpg',
|
||||
'/assets/headers/mkt-057.jpg', '/assets/headers/mkt-058.jpg', '/assets/headers/mkt-059.jpg',
|
||||
'/assets/headers/mkt-060.jpg', '/assets/headers/mkt-061.jpg', '/assets/headers/mkt-062.jpg',
|
||||
'/assets/headers/mkt-063.jpg',
|
||||
]);
|
||||
|
||||
define('FM_FEET', [
|
||||
'if it ain\'t broke, i\'ll break it',
|
||||
]);
|
||||
|
||||
define('FM_ERRS' , [
|
||||
403 => [
|
||||
'code' => 403,
|
||||
'title' => 'Access Denied',
|
||||
'image' => '/assets/errors/403.jpg',
|
||||
'desc' => 'You are not supposed to be here.',
|
||||
],
|
||||
404 => [
|
||||
'code' => 404,
|
||||
'title' => 'Not Found',
|
||||
'image' => '/assets/errors/404.jpg',
|
||||
'desc' => 'Whatever you\'re looking for is no longer here, or might not have been here in the first place.',
|
||||
],
|
||||
405 => [
|
||||
'code' => 405,
|
||||
'title' => 'Method Not Supported',
|
||||
'image' => '/assets/errors/405.jpg',
|
||||
'desc' => 'You\'re up to something, aren\'t you?',
|
||||
],
|
||||
]);
|
||||
|
||||
function fm_component(string $_component_name, array $vars = []): string {
|
||||
extract($vars);
|
||||
|
||||
ob_start();
|
||||
require __DIR__ . '/../components/' . $_component_name . '.php';
|
||||
$meow = ob_get_contents();
|
||||
ob_clean();
|
||||
|
||||
return $meow;
|
||||
}
|
||||
|
||||
function first_paragraph(string $text, string $delimiter = "\n"): string {
|
||||
$index = mb_strpos($text, $delimiter);
|
||||
return $index === false ? $text : mb_substr($text, 0, $index);
|
||||
}
|
||||
|
||||
function time_elapsed(int $since, bool $full = false): string {
|
||||
$now = new DateTime;
|
||||
$since = new DateTime(date('c', $since));
|
||||
$diff = $now->diff($since);
|
||||
|
||||
$diff->w = floor($diff->d / 7);
|
||||
$diff->d -= $diff->w * 7;
|
||||
|
||||
$string = [
|
||||
'y' => 'year',
|
||||
'm' => 'month',
|
||||
'w' => 'week',
|
||||
'd' => 'day',
|
||||
'h' => 'hour',
|
||||
'i' => 'minute',
|
||||
's' => 'second',
|
||||
];
|
||||
|
||||
foreach($string as $key => &$value) {
|
||||
if($diff->{$key})
|
||||
$value = $diff->{$key} . ' ' . $value . ($diff->{$key} > 1 ? 's' : '');
|
||||
else
|
||||
unset($string[$key]);
|
||||
}
|
||||
|
||||
if(!$full)
|
||||
$string = array_slice($string, 0, 1);
|
||||
return $string ? implode(', ', $string) . ' ago' : 'just now';
|
||||
}
|
||||
|
||||
function cache_output(string $name, int $lifetime, callable $callable) {
|
||||
$path = sys_get_temp_dir() . '/fm-' . $name . '.cache';
|
||||
if(!is_file($path) || (filemtime($path) + $lifetime) < time())
|
||||
file_put_contents($path, serialize($callable()));
|
||||
return unserialize(file_get_contents($path));
|
||||
}
|
||||
|
||||
$reqMethod = (string)filter_input(INPUT_SERVER, 'REQUEST_METHOD');
|
||||
$reqPath = '/' . trim(parse_url((string)filter_input(INPUT_SERVER, 'REQUEST_URI'), PHP_URL_PATH), '/');
|
||||
$reqHead = false;
|
||||
|
||||
if($reqMethod == 'HEAD') {
|
||||
$reqMethod = 'GET';
|
||||
$reqHead = true;
|
||||
}
|
||||
|
||||
$router = new Router;
|
||||
|
||||
$router->get('/error/:code', function(string $code) use ($reqHead) {
|
||||
$errorInfo = FM_ERRS[$code ?? 404] ?? FM_ERRS[404];
|
||||
http_response_code($errorInfo['code']);
|
||||
|
||||
if(!$reqHead) {
|
||||
$body = fm_component('header', ['title' => $errorInfo['title']]);
|
||||
|
||||
$body .= <<<HTML
|
||||
<div class="http-error">
|
||||
<h2 class="http-error-head">{$errorInfo['title']}</h2>
|
||||
<img src="{$errorInfo['image']}" alt="{$errorInfo['image']}" class="http-error-image"/>
|
||||
<div class="http-error-desc">{$errorInfo['desc']}</div>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
$body .= fm_component('footer');
|
||||
|
||||
return $body;
|
||||
}
|
||||
$router->setDefaultErrorHandler(function($response, $request, $code, $text) use ($ctx) {
|
||||
$path = 'errors/' . $code;
|
||||
$tpl = $ctx->getTemplating();
|
||||
$response->setContent($tpl->render($tpl->exists($path) ? $path : 'errors/master', [
|
||||
'http_error_code' => $code,
|
||||
'http_error_title' => $text,
|
||||
]));
|
||||
});
|
||||
|
||||
function mkiRedirect(string $to): callable {
|
||||
return function() use ($to) {
|
||||
header('Location: ' . $to);
|
||||
return 302;
|
||||
};
|
||||
}
|
||||
$router->get('/error/:code', function($response, $request, $code) {
|
||||
return intval($code);
|
||||
});
|
||||
|
||||
require_once MKI_DIR_PAGES . '/contact.php';
|
||||
require_once MKI_DIR_PAGES . '/index.php';
|
||||
require_once MKI_DIR_PAGES . '/projects.php';
|
||||
require_once MKI_DIR_PAGES . '/ssh.php';
|
||||
foreach(MKI_REDIRS as $source => $target)
|
||||
$router->get($source, function($response) use ($target) {
|
||||
$response->redirect($target);
|
||||
});
|
||||
|
||||
header('X-Powered-By: Makai');
|
||||
$router->use('/', function($response) {
|
||||
$response->setPoweredBy('Makai+Index');
|
||||
});
|
||||
|
||||
try {
|
||||
$handlers = $router->resolve($reqMethod, $reqPath);
|
||||
} catch(RoutePathNotFoundException $ex) {
|
||||
$handlers = $router->resolve('GET', '/error/404');
|
||||
} catch(RouteMethodNotSupportedException $ex) {
|
||||
$handlers = $router->resolve('GET', '/error/405');
|
||||
}
|
||||
$router->get('/header-bgs.json', function() {
|
||||
return json_encode(FM_BGS);
|
||||
});
|
||||
|
||||
$result = $handlers->run();
|
||||
$router->get('/now-listening', function() use ($ctx) {
|
||||
return $ctx->getTemplating()->render('np', [
|
||||
'header_offset' => (int)filter_input(INPUT_GET, 'offset', FILTER_SANITIZE_NUMBER_INT),
|
||||
]);
|
||||
});
|
||||
|
||||
if(is_int($result) && $result >= 400 && $result < 600)
|
||||
$result = $router->resolve('GET', "/error/{$result}")->run();
|
||||
$router->get('/now-listening.json', function() {
|
||||
$lfmInfo = json_decode(file_get_contents('https://now.flash.moe/get.php?u=flashwave_'));
|
||||
|
||||
if($result !== null && !is_bool($result)) {
|
||||
if(is_int($result)) {
|
||||
http_response_code($result);
|
||||
header('Content-Type: text/plain; charset=us-ascii');
|
||||
echo $result;
|
||||
} elseif(is_array($result)
|
||||
|| $result instanceof \stdClass
|
||||
|| $result instanceof \JsonSerializable) {
|
||||
http_response_code(200);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode($result);
|
||||
} else {
|
||||
$result = (string)$result;
|
||||
if(empty($lfmInfo[0]->name))
|
||||
return [];
|
||||
|
||||
if(strtolower(substr($result, 0, 14)) === '<!doctype html') {
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
echo $result;
|
||||
} else {
|
||||
echo $result;
|
||||
$lfmInfo = $lfmInfo[0];
|
||||
|
||||
return [
|
||||
'name' => strval($lfmInfo->name),
|
||||
'now_playing' => !empty($lfmInfo->nowplaying),
|
||||
'url' => strval($lfmInfo->url),
|
||||
'cover' => !empty($lfmInfo->images->large) ? strval($lfmInfo->images->large) : '',
|
||||
'artist' => [
|
||||
'name' => !empty($lfmInfo->artist->name) ? strval($lfmInfo->artist->name) : '',
|
||||
'url' => explode('/_/', strval($lfmInfo->url))[0],
|
||||
],
|
||||
];
|
||||
});
|
||||
|
||||
$router->get('/home', function() use ($ctx) {
|
||||
return $ctx->getTemplating()->render('home/index');
|
||||
});
|
||||
|
||||
$router->get('/', function() use ($ctx) {
|
||||
$legacyPage = (string)filter_input(INPUT_GET, 'p');
|
||||
if(!empty($legacyPage)) {
|
||||
$legacyPages = [
|
||||
'projects' => '/projects',
|
||||
'contact' => '/contact',
|
||||
'about' => '/',
|
||||
'etc' => '/etc',
|
||||
'hosted' => '/etc',
|
||||
'friends' => '/related',
|
||||
];
|
||||
|
||||
if(isset($legacyPages[$legacyPage])) {
|
||||
header('Location: ' . $legacyPages[$legacyPage]);
|
||||
return 302;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$db = $ctx->getDatabase();
|
||||
|
||||
return $ctx->getTemplating()->render('index', [
|
||||
'projects' => (new Projects($db))->getFeatured(),
|
||||
'languages' => new Languages($db),
|
||||
'contacts' => (new Contacts($db))->getHomePage(),
|
||||
]);
|
||||
});
|
||||
|
||||
$router->get('/contact', function() use ($ctx) {
|
||||
return $ctx->getTemplating()->render('contact', [
|
||||
'contacts' => (new Contacts($ctx->getDatabase()))->getAll(),
|
||||
]);
|
||||
});
|
||||
|
||||
$router->get('/projects', function() use ($ctx) {
|
||||
$db = $ctx->getDatabase();
|
||||
$projects = (new Projects($db))->getAll();
|
||||
|
||||
$sections = [
|
||||
'projects' => [
|
||||
'title' => 'Active Projects',
|
||||
'desc' => 'Projects that I work on on a fairly regular basis.',
|
||||
'items' => [],
|
||||
],
|
||||
'tools' => [
|
||||
'title' => 'Tools',
|
||||
'desc' => 'Personal quality of life tools that I update when I need something new from them.',
|
||||
'items' => [],
|
||||
],
|
||||
'archives' => [
|
||||
'title' => 'Archived Projects',
|
||||
'desc' => 'Past projects that I no longer work on.',
|
||||
'items' => [],
|
||||
],
|
||||
];
|
||||
|
||||
foreach($projects as $project) {
|
||||
if($project->isArchived())
|
||||
$sections['archives']['items'][] = $project;
|
||||
else {
|
||||
if($project->isTool())
|
||||
$sections['tools']['items'][] = $project;
|
||||
else
|
||||
$sections['projects']['items'][] = $project;
|
||||
}
|
||||
}
|
||||
|
||||
return $ctx->getTemplating()->render('projects', [
|
||||
'sections' => $sections,
|
||||
'languages' => new Languages($db),
|
||||
]);
|
||||
});
|
||||
|
||||
$router->get('/ssh.php', function() {
|
||||
$query = '';
|
||||
|
||||
$minLevel = (int)filter_input(INPUT_GET, 'l', FILTER_SANITIZE_NUMBER_INT);
|
||||
if($minLevel > 0)
|
||||
$query .= sprintf('l=%d&', $minLevel);
|
||||
|
||||
if(!empty($_GET['c']))
|
||||
$query .= 'c=1&';
|
||||
|
||||
if(!empty($_GET['j']))
|
||||
$query .= 'j=1&';
|
||||
|
||||
if($query !== '')
|
||||
$query = '?' . substr($query, 0, -1);
|
||||
|
||||
header('Location: /ssh_keys' . $query);
|
||||
return 302;
|
||||
});
|
||||
|
||||
$router->get('/ssh_keys', function() use ($db) {
|
||||
$minLevel = (int)filter_input(INPUT_GET, 'l', FILTER_SANITIZE_NUMBER_INT);
|
||||
$includeComment = !empty($_GET['c']);
|
||||
$json = !empty($_GET['j']);
|
||||
|
||||
$keys = (new SSHKeys($db))->getKeys($minLevel);
|
||||
|
||||
if($json) {
|
||||
$items = [];
|
||||
|
||||
foreach($keys as $key) {
|
||||
$items[] = $item = new \stdClass;
|
||||
$item->algo = $key->getAlgorithm();
|
||||
$item->key = $key->getBody();
|
||||
if($includeComment) {
|
||||
$item->comment = (string)$key->getComment();
|
||||
$item->created = $key->getCreatedAt()->format(\DateTime::ATOM);
|
||||
$item->level = $key->getLevel();
|
||||
}
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
header('Content-Type: text/plain; charset=us-ascii');
|
||||
|
||||
$body = '';
|
||||
|
||||
foreach($keys as $key)
|
||||
$body .= $key->toString($includeComment) . "\n";
|
||||
|
||||
return $body;
|
||||
});
|
||||
|
||||
$router->get('/authorized_keys', function() use ($db) {
|
||||
$keys = (new SSHKeys($db))->getKeys(500);
|
||||
|
||||
header('Content-Type: text/plain; charset=us-ascii');
|
||||
|
||||
$body = '';
|
||||
foreach($keys as $key)
|
||||
$body .= $key->toString(true) . "\n";
|
||||
|
||||
return $body;
|
||||
});
|
||||
|
||||
$router->get('/git_keys_ro', function() use ($db) {
|
||||
$keys = (new SSHKeys($db))->getKeys(100);
|
||||
|
||||
header('Content-Type: text/plain; charset=us-ascii');
|
||||
|
||||
$body = '';
|
||||
foreach($keys as $key)
|
||||
$body .= $key->toString(false) . "\n";
|
||||
|
||||
return $body;
|
||||
});
|
||||
|
||||
$router->get('/git_keys_rw', function() use ($db) {
|
||||
$keys = (new SSHKeys($db))->getKeys(200);
|
||||
|
||||
header('Content-Type: text/plain; charset=us-ascii');
|
||||
|
||||
$body = '';
|
||||
foreach($keys as $key)
|
||||
$body .= $key->toString(false) . "\n";
|
||||
|
||||
return $body;
|
||||
});
|
||||
|
||||
|
||||
$router->dispatch();
|
||||
|
|
74
src/MakaiContext.php
Normal file
74
src/MakaiContext.php
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
use Index\Data\IDbConnection;
|
||||
|
||||
final class MakaiContext {
|
||||
private IDbConnection $db;
|
||||
private ?TemplateContext $tpl = null;
|
||||
|
||||
public function __construct(IDbConnection $db) {
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function getDatabase(): IDbConnection {
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
public function getTemplating(): TemplateContext {
|
||||
if($this->tpl === null) {
|
||||
$this->tpl = new TemplateContext(MKI_DIR_TEMPLATES);
|
||||
$this->tpl->setGlobal('makai', $this);
|
||||
$this->tpl->setGlobal('header_nav', $this->getDefaultNavigation());
|
||||
$this->tpl->setGlobal('header_bgs', $this->getDefaultHeaders());
|
||||
$this->tpl->setGlobal('footer_quotes', $this->getFooterQuotes());
|
||||
}
|
||||
|
||||
return $this->tpl;
|
||||
}
|
||||
|
||||
public function getDefaultNavigation(): array {
|
||||
return [
|
||||
['title' => 'Home', 'link' => '/'],
|
||||
['title' => 'Projects', 'link' => '/projects'],
|
||||
['title' => 'Contact', 'link' => '/contact'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDefaultHeaders(): array {
|
||||
return [
|
||||
'/assets/headers/krk-000.jpg', '/assets/headers/krk-001.jpg', '/assets/headers/krk-002.jpg',
|
||||
'/assets/headers/krk-003.jpg', '/assets/headers/krk-004.jpg', '/assets/headers/krk-005.jpg',
|
||||
'/assets/headers/krk-006.jpg', '/assets/headers/krk-007.jpg', '/assets/headers/krk-008.jpg',
|
||||
|
||||
'/assets/headers/mkt-000.jpg', '/assets/headers/mkt-001.jpg', '/assets/headers/mkt-002.jpg',
|
||||
'/assets/headers/mkt-003.jpg', '/assets/headers/mkt-004.jpg', '/assets/headers/mkt-005.jpg',
|
||||
'/assets/headers/mkt-006.jpg', '/assets/headers/mkt-007.jpg', '/assets/headers/mkt-008.jpg',
|
||||
'/assets/headers/mkt-009.jpg', '/assets/headers/mkt-010.jpg', '/assets/headers/mkt-011.jpg',
|
||||
'/assets/headers/mkt-012.jpg', '/assets/headers/mkt-013.jpg', '/assets/headers/mkt-014.jpg',
|
||||
'/assets/headers/mkt-015.jpg', '/assets/headers/mkt-016.jpg', '/assets/headers/mkt-017.jpg',
|
||||
'/assets/headers/mkt-018.jpg', '/assets/headers/mkt-019.jpg', '/assets/headers/mkt-020.jpg',
|
||||
'/assets/headers/mkt-021.jpg', '/assets/headers/mkt-022.jpg', '/assets/headers/mkt-023.jpg',
|
||||
'/assets/headers/mkt-024.jpg', '/assets/headers/mkt-025.jpg', '/assets/headers/mkt-026.jpg',
|
||||
'/assets/headers/mkt-027.jpg', '/assets/headers/mkt-028.jpg', '/assets/headers/mkt-029.jpg',
|
||||
'/assets/headers/mkt-030.jpg', '/assets/headers/mkt-031.jpg', '/assets/headers/mkt-032.jpg',
|
||||
'/assets/headers/mkt-033.jpg', '/assets/headers/mkt-034.jpg', '/assets/headers/mkt-035.jpg',
|
||||
'/assets/headers/mkt-036.jpg', '/assets/headers/mkt-037.jpg', '/assets/headers/mkt-038.jpg',
|
||||
'/assets/headers/mkt-039.jpg', '/assets/headers/mkt-040.jpg', '/assets/headers/mkt-041.jpg',
|
||||
'/assets/headers/mkt-042.jpg', '/assets/headers/mkt-043.jpg', '/assets/headers/mkt-044.jpg',
|
||||
'/assets/headers/mkt-045.jpg', '/assets/headers/mkt-046.jpg', '/assets/headers/mkt-047.jpg',
|
||||
'/assets/headers/mkt-048.jpg', '/assets/headers/mkt-049.jpg', '/assets/headers/mkt-050.jpg',
|
||||
'/assets/headers/mkt-051.jpg', '/assets/headers/mkt-052.jpg', '/assets/headers/mkt-053.jpg',
|
||||
'/assets/headers/mkt-054.jpg', '/assets/headers/mkt-055.jpg', '/assets/headers/mkt-056.jpg',
|
||||
'/assets/headers/mkt-057.jpg', '/assets/headers/mkt-058.jpg', '/assets/headers/mkt-059.jpg',
|
||||
'/assets/headers/mkt-060.jpg', '/assets/headers/mkt-061.jpg', '/assets/headers/mkt-062.jpg',
|
||||
'/assets/headers/mkt-063.jpg',
|
||||
];
|
||||
}
|
||||
|
||||
public function getFooterQuotes(): array {
|
||||
return [
|
||||
'if it ain\'t broke, i\'ll break it',
|
||||
];
|
||||
}
|
||||
}
|
47
src/Template.php
Normal file
47
src/Template.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
class Template {
|
||||
public function __construct(
|
||||
private TemplateContext $context,
|
||||
private TemplateVars $vars,
|
||||
private string $path
|
||||
) {}
|
||||
|
||||
public function setVar(string $name, mixed $value): void {
|
||||
$this->vars->setVar($name, $value);
|
||||
}
|
||||
public function removeVar(string $name): void {
|
||||
$this->vars->removeVar($name);
|
||||
}
|
||||
|
||||
public function render(array $vars = [], ?TemplateSelf $self = null): string {
|
||||
if(!is_file($this->path))
|
||||
throw new \RuntimeException('Template file does not exist: ' . $this->path);
|
||||
|
||||
$self = new TemplateSelf($this->context->getFunctions() + $vars + $this->vars->getVars(), $self);
|
||||
$self->tplPath = $this->path;
|
||||
|
||||
$self->var = fn(string $name, mixed $default = null) => $this->vars->getVar($name, $default);
|
||||
$self->setVar = fn(string $name, mixed $value) => $this->vars->setVar($name, $value);
|
||||
$self->remVar = fn(string $name) => $this->vars->removeVar($name);
|
||||
|
||||
$self->block = fn(string $name, mixed $contents) => $this->context->setBlock($name, new TemplateBlock($self, $contents));
|
||||
|
||||
$self->ctx = $this->context;
|
||||
$self->extends = fn(string $name) => $self->extends = $name;
|
||||
|
||||
ob_start();
|
||||
(static function() use ($self) { include $self->tplPath; })();
|
||||
$buffer = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
if(is_string($self->extends)) {
|
||||
if(!empty($buffer))
|
||||
throw new \RuntimeException('You cannot output from templates that extend another.');
|
||||
$buffer = $this->context->render($self->extends, [], $self);
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
}
|
22
src/TemplateBlock.php
Normal file
22
src/TemplateBlock.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
use Closure;
|
||||
|
||||
class TemplateBlock {
|
||||
public function __construct(
|
||||
private object $self,
|
||||
private mixed $body
|
||||
) {}
|
||||
|
||||
public function render(): string {
|
||||
if(is_callable($this->body)) {
|
||||
ob_start();
|
||||
($this->body)($this->self);
|
||||
$body = ob_get_contents();
|
||||
ob_end_clean();
|
||||
} else $body = strval($this->body);
|
||||
|
||||
return $body;
|
||||
}
|
||||
}
|
54
src/TemplateContext.php
Normal file
54
src/TemplateContext.php
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
use Closure;
|
||||
|
||||
class TemplateContext {
|
||||
private const EXT = '.php';
|
||||
|
||||
private string $pathFormat = '%s' . self::EXT;
|
||||
private TemplateVars $vars;
|
||||
private array $blocks = [];
|
||||
|
||||
public function __construct($path = '') {
|
||||
if(!empty($path))
|
||||
$this->pathFormat = rtrim($path, '\\/') . DIRECTORY_SEPARATOR . '%s' . self::EXT;
|
||||
$this->vars = new TemplateVars;
|
||||
}
|
||||
|
||||
public function setGlobal(string $name, mixed $value): void {
|
||||
$this->vars->setVar($name, $value);
|
||||
}
|
||||
public function removeGlobal(string $name): void {
|
||||
$this->vars->removeVar($name);
|
||||
}
|
||||
|
||||
public function getFunctions(): array {
|
||||
return [
|
||||
'global' => $this->vars->getVar(...),
|
||||
'getBlock' => $this->getBlock(...),
|
||||
'x' => fn(string $string) => htmlspecialchars($string, ENT_QUOTES, 'utf-8', true),
|
||||
'e' => function() { return ''; }, // reserve for the HTML builder
|
||||
];
|
||||
}
|
||||
|
||||
public function create(string $path): Template {
|
||||
return new Template($this, new TemplateVars($this->vars), sprintf($this->pathFormat, $path));
|
||||
}
|
||||
|
||||
public function exists(string $path): bool {
|
||||
return is_file(sprintf($this->pathFormat, $path));
|
||||
}
|
||||
|
||||
public function render(string $path, array $vars = [], ?TemplateSelf $self = null): string {
|
||||
return $this->create($path)->render($vars, $self);
|
||||
}
|
||||
|
||||
public function getBlock(string $name, mixed $default = ''): string {
|
||||
return ($this->blocks[$name] ?? new TemplateBlock(new \stdClass, $default))->render();
|
||||
}
|
||||
|
||||
public function setBlock(string $name, TemplateBlock $block): void {
|
||||
$this->blocks[$name] = $block;
|
||||
}
|
||||
}
|
32
src/TemplateSelf.php
Normal file
32
src/TemplateSelf.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
class TemplateSelf {
|
||||
public function __construct(
|
||||
private array $members = [],
|
||||
?TemplateSelf $inherit = null
|
||||
) {
|
||||
if($inherit !== null)
|
||||
$this->members += $inherit->members;
|
||||
}
|
||||
|
||||
public function __get(string $name): mixed {
|
||||
return $this->members[$name];
|
||||
}
|
||||
|
||||
public function __set(string $name, mixed $value): void {
|
||||
$this->members[$name] = $value;
|
||||
}
|
||||
|
||||
public function __isset(string $name): bool {
|
||||
return isset($this->members[$name]);
|
||||
}
|
||||
|
||||
public function __unset(string $name): void {
|
||||
unset($this->members[$name]);
|
||||
}
|
||||
|
||||
public function __call(string $name, array $args): mixed {
|
||||
return ($this->members[$name])(...$args);
|
||||
}
|
||||
}
|
32
src/TemplateVars.php
Normal file
32
src/TemplateVars.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
class TemplateVars {
|
||||
public function __construct(
|
||||
private ?TemplateVars $parent = null,
|
||||
private array $vars = []
|
||||
) {}
|
||||
|
||||
public function getVars(): array {
|
||||
$vars = $this->vars;
|
||||
if($this->parent !== null)
|
||||
$vars += $this->parent->getVars();
|
||||
return $vars;
|
||||
}
|
||||
|
||||
public function getVar(string $name, mixed $default = null): mixed {
|
||||
if(isset($this->vars[$name]))
|
||||
return $this->vars[$name];
|
||||
if($this->parent !== null)
|
||||
return $this->parent->getVar($name, $default);
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function setVar(string $name, mixed $value): void {
|
||||
$this->vars[$name] = $value;
|
||||
}
|
||||
|
||||
public function removeVar(string $name): void {
|
||||
unset($this->vars[$name]);
|
||||
}
|
||||
}
|
33
tpl/contact.php
Normal file
33
tpl/contact.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
$self->extends('home/master');
|
||||
|
||||
$self->header_title = 'flash.moe / contact';
|
||||
|
||||
$self->block('container', function($self) {
|
||||
?>
|
||||
<div class="section">
|
||||
<div class="section-content">
|
||||
<div class="section-background"></div>
|
||||
<h1>Contacts</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="socials">
|
||||
<?php foreach($self->contacts as $contact): ?>
|
||||
<div class="social social-<?=$contact->getName();?>" style="--social-colour: <?=$contact->getColourHex();?>">
|
||||
|
||||
<?php if($contact->hasLink()): ?>
|
||||
<a href="<?=$contact->getLink();?>" class="social-background" target="_blank" rel="noopener"></a>
|
||||
<?php else: ?>
|
||||
<div class="social-background" onclick="fm.selectTextInElement(this.parentNode.querySelector('.social-handle')); fm.copySelectedText();"></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="social-icon <?=$contact->getIcon();?>"></div>
|
||||
<div class="social-content">
|
||||
<div class="social-name"><?=$contact->getName();?></div>
|
||||
<div class="social-handle"><?=$contact->getDisplay();?></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php
|
||||
});
|
5
tpl/errors/403.php
Normal file
5
tpl/errors/403.php
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
$self->extends('errors/master');
|
||||
|
||||
$self->http_error_image = '/assets/errors/403.jpg';
|
||||
$self->http_error_desc = 'You are not supposed to be here.';
|
5
tpl/errors/404.php
Normal file
5
tpl/errors/404.php
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
$self->extends('errors/master');
|
||||
|
||||
$self->http_error_image = '/assets/errors/404.jpg';
|
||||
$self->http_error_desc = 'Whatever you\'re looking for is no longer here, or might not have been here in the first place.';
|
5
tpl/errors/405.php
Normal file
5
tpl/errors/405.php
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
$self->extends('errors/master');
|
||||
|
||||
$self->http_error_image = '/assets/errors/405.jpg';
|
||||
$self->http_error_desc = 'You\'re up to something, aren\'t you?';
|
14
tpl/errors/master.php
Normal file
14
tpl/errors/master.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
$self->extends('master');
|
||||
|
||||
$self->block('container', function($self) {
|
||||
?>
|
||||
<div class="http-error">
|
||||
<h2 class="http-error-head"><?=($self->http_error_title ?? ('Unknown Error #' . $self->http_error_code));?></h2>
|
||||
<?php if(isset($self->http_error_image)): ?>
|
||||
<img src="<?=$self->http_error_image;?>" alt="<?=$self->http_error_image;?>" class="http-error-image"/>
|
||||
<?php endif; ?>
|
||||
<div class="http-error-desc"><?=($self->http_error_desc ?? 'No additional information is available.');?></div>
|
||||
</div>
|
||||
<?php
|
||||
});
|
46
tpl/home/index.php
Normal file
46
tpl/home/index.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
$self->extends('home/master');
|
||||
|
||||
$self->header_title = 'flash.moe / homepage';
|
||||
$self->header_full = true;
|
||||
$self->footer_onload = [['fm.initClock'], ['fm.initIndex', 10]];
|
||||
|
||||
$self->block('container', function($self) {
|
||||
?>
|
||||
<div class="php">
|
||||
<div class="php-time">
|
||||
<div class="php-time-analog">
|
||||
<div class="clock">
|
||||
<div class="clock-background"></div>
|
||||
<div class="clock-center"></div>
|
||||
<div class="clock-hand clock-hand-hours"><div class="clock-hand-display"></div></div>
|
||||
<div class="clock-hand clock-hand-minutes"><div class="clock-hand-display"></div></div>
|
||||
<div class="clock-hand clock-hand-seconds"><div class="clock-hand-display"></div></div>
|
||||
<div class="clock-number clock-number-3"><div class="clock-number-display"></div></div>
|
||||
<div class="clock-number clock-number-6"><div class="clock-number-display"></div></div>
|
||||
<div class="clock-number clock-number-9"><div class="clock-number-display"></div></div>
|
||||
<div class="clock-number clock-number-12"><div class="clock-number-display"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="php-time-alter">
|
||||
<div class="php-time-digital">
|
||||
<div class="php-time-digital-hours">--</div>
|
||||
<div class="php-time-digital-separator">:</div>
|
||||
<div class="php-time-digital-minutes">--</div>
|
||||
</div>
|
||||
<div class="php-time-date">
|
||||
<span class="php-date-label">Week</span>
|
||||
<span class="php-date-week">--</span>
|
||||
<span class="php-date-label"> — </span>
|
||||
<span class="php-date-year">----</span><span class="php-date-label">-</span><span class="php-date-month">---</span><span class="php-date-label">-</span><span class="php-date-day">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form class="php-search" method="get" action="https://duckduckgo.com/">
|
||||
<div class="php-search-input">
|
||||
<input type="search" name="q" placeholder="Search using DuckDuckGo..."/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
});
|
2
tpl/home/master.php
Normal file
2
tpl/home/master.php
Normal file
|
@ -0,0 +1,2 @@
|
|||
<?php
|
||||
$self->extends('master');
|
85
tpl/index.php
Normal file
85
tpl/index.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
$self->extends('master');
|
||||
|
||||
$self->header_title = 'flash.moe';
|
||||
$self->header_is_index = true;
|
||||
$self->footer_onload = [['fm.initIndex']];
|
||||
|
||||
$self->block('container', function($self) {
|
||||
?>
|
||||
<div class="index-menu">
|
||||
<?php for($i = 1; $i < count($self->header_nav); ++$i): $link = $self->header_nav[$i]; ?>
|
||||
<a href="<?=$link['link'];?>"><?=$link['title'];?></a>
|
||||
<?php endfor; ?>
|
||||
</div>
|
||||
|
||||
<div class="index-featured">
|
||||
|
||||
<div class="index-feature">
|
||||
<div class="index-feature-header">
|
||||
<a href="/projects" class="index-feature-header-link"></a>
|
||||
<div class="index-feature-header-title">Projects</div>
|
||||
<div class="index-feature-header-more">More</div>
|
||||
</div>
|
||||
<?php
|
||||
foreach($self->projects as $project):
|
||||
$links = [];
|
||||
if($project->hasHomePageUrl())
|
||||
$links[] = ['class' => 'homepage', 'text' => 'Homepage', 'url' => $project->getHomePageUrl()];
|
||||
if($project->hasSourceUrl())
|
||||
$links[] = ['class' => 'repository', 'text' => 'Source', 'url' => $project->getSourceUrl()];
|
||||
if($project->hasDiscussionUrl())
|
||||
$links[] = ['class' => 'forum', 'text' => 'Discussion', 'url' => $project->getDiscussionUrl()];
|
||||
|
||||
$colour = $project->hasColour() ? $project->getColour() : $self->languages->getProjectColour($project);
|
||||
$colour = str_pad(dechex($colour), 6, '0', STR_PAD_LEFT);
|
||||
?>
|
||||
<div class="index-project" style="background-color: #<?=$colour;?>;">
|
||||
<a href="/projects#<?=$project->getCleanName();?>" class="index-project-anchor"></a>
|
||||
<div class="index-project-content">
|
||||
<div class="index-project-name"><?=$project->getName();?></div>
|
||||
<?php if($project->hasSummary()): ?>
|
||||
<div class="index-project-summary"><?=$project->getSummary();?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if(!empty($links)): ?>
|
||||
<div class="index-project-links">
|
||||
<?php foreach($links as $link): ?>
|
||||
<a class="index-project-link index-project-link-<?=$link['class'];?>" href="<?=$link['url'];?>" rel="noopener" target="_blank"><?=$link['text'];?></a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="index-feature">
|
||||
<div class="index-feature-header">
|
||||
<a href="/contact" class="index-feature-header-link"></a>
|
||||
<div class="index-feature-header-title">Contact</div>
|
||||
<div class="index-feature-header-more">More</div>
|
||||
</div>
|
||||
<div class="index-contact">
|
||||
<?php foreach($self->contacts as $contact): ?>
|
||||
<div class="social social-<?=$contact->getName();?>" style="--social-colour: <?=$contact->getColourHex();?>">
|
||||
<?php if($contact->hasLink()): ?>
|
||||
<a href="<?=$contact->getLink();?>" class="social-background" target="_blank" rel="noopener"></a>
|
||||
<?php else: ?>
|
||||
<div class="social-background" onclick="fm.selectTextInElement(this.parentNode.querySelector('.social-handle')); fm.copySelectedText();"></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="social-icon <?=$contact->getIcon();?>"></div>
|
||||
<div class="social-content">
|
||||
<div class="social-name"><?=$contact->getTitle();?></div>
|
||||
<div class="social-handle"><?=$contact->getDisplay();?></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php
|
||||
});
|
|
@ -1,28 +1,19 @@
|
|||
<?php
|
||||
$menu = isset($menu) && is_array($menu) ? $menu : FM_NAV;
|
||||
$backgrounds = isset($backgrounds) && is_array($backgrounds) ? $backgrounds : FM_BGS;
|
||||
$baseUrl = empty($external) ? '' : '//' . $_SERVER['HTTP_HOST'];
|
||||
$showNowPlaying = !empty($is_index) || !empty($do_fullscreen_header);
|
||||
?>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title><?=($title ?? 'flash.moe');?></title>
|
||||
<title><?=($self->header_title ?? 'flash.moe');?></title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="<?=$baseUrl;?>/css/2021.css" type="text/css" rel="stylesheet"/>
|
||||
<link href="<?=$baseUrl;?>/assets/sprite.css" type="text/css" rel="stylesheet"/>
|
||||
<link href="<?=$baseUrl;?>/assets/fonts/electrolize/style.css" type="text/css" rel="stylesheet"/>
|
||||
<?php if(isset($styles) && is_array($styles)) foreach($styles as $style): ?>
|
||||
<link href="<?=$style;?>" type="text/css" rel="stylesheet"/>
|
||||
<?php endforeach;?>
|
||||
<link href="/css/2021.css" type="text/css" rel="stylesheet"/>
|
||||
<link href="/assets/sprite.css" type="text/css" rel="stylesheet"/>
|
||||
<link href="/assets/fonts/electrolize/style.css" type="text/css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body class="<?php if(isset($is_index)) { echo 'index ';} if(isset($do_fullscreen_header)) { echo 'fullscreen-header ';} if(isset($is_now_playing)) { echo 'now-playing ';} ?>">
|
||||
<body class="<?php if(isset($self->header_is_index)) { echo 'index ';} if(isset($self->header_full)) { echo 'fullscreen-header ';} if(isset($self->header_now_playing)) { echo 'now-playing ';} ?>">
|
||||
<div class="header">
|
||||
<div class="header-background">
|
||||
<img src="<?=($backgrounds[array_rand($backgrounds)]);?>" alt=""/>
|
||||
<img src="<?=($self->header_bgs[array_rand($self->header_bgs)]);?>" alt=""/>
|
||||
</div>
|
||||
<div class="header-foreground"<?php if(isset($offset) && $offset > 0) echo " style=\"padding-bottom: {$offset}px\""; ?>>
|
||||
<div class="header-foreground"<?php if(isset($self->header_offset) && $self->header_offset > 0) echo " style=\"padding-bottom: {$self->header_offset}px\""; ?>>
|
||||
<a class="header-logo" href="/">
|
||||
<div class="header-flash">flash</div>
|
||||
<div class="header-wave">wave</div>
|
||||
|
@ -41,7 +32,7 @@ $showNowPlaying = !empty($is_index) || !empty($do_fullscreen_header);
|
|||
</div>
|
||||
</div>
|
||||
<div class="header-menu">
|
||||
<?php foreach($menu as $link): ?>
|
||||
<?php foreach($self->header_nav as $link): ?>
|
||||
<a href="<?=$link['link'];?>"<?php if($link['link'][0] === '/' && substr($link['link'], 0, 2) !== '//') { echo ''; } ?>><?=$link['title'];?></a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
@ -49,3 +40,16 @@ $showNowPlaying = !empty($is_index) || !empty($do_fullscreen_header);
|
|||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<?=$self->getBlock('container');?>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="footer-text">© flashwave 2010-<?=date('Y');?> - <?=($self->footer_quotes[array_rand($self->footer_quotes)]);?></div>
|
||||
</div>
|
||||
<?php if(isset($self->footer_onload) && is_array($self->footer_onload)): ?>
|
||||
<script type="text/javascript">
|
||||
window.fm = { onload: <?=json_encode($self->footer_onload);?> };
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
<script src="/assets/2021.js" charset="utf-8" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
7
tpl/np.php
Normal file
7
tpl/np.php
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
$self->extends('master');
|
||||
|
||||
$self->header_title = 'flash.moe / now listening';
|
||||
$self->header_full = true;
|
||||
$self->header_now_playing = true;
|
||||
$self->footer_onload = [['fm.initIndex', 10]];
|
85
tpl/projects.php
Normal file
85
tpl/projects.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
$self->extends('home/master');
|
||||
|
||||
$self->header_title = 'flash.moe / projects';
|
||||
|
||||
$self->block('container', function($self) {
|
||||
?>
|
||||
<?php foreach($self->sections as $sectionId => $section): ?>
|
||||
<div class="section" id="section-<?=$sectionId;?>">
|
||||
<div class="section-content">
|
||||
<div class="section-background"></div>
|
||||
<h1><?=$section['title'];?></h1>
|
||||
<p><?=$section['desc'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
foreach($section['items'] as $project):
|
||||
$links = [];
|
||||
if($project->hasHomePageUrl())
|
||||
$links[] = ['class' => 'homepage', 'text' => 'Homepage', 'url' => $project->getHomePageUrl()];
|
||||
if($project->hasSourceUrl())
|
||||
$links[] = ['class' => 'repository', 'text' => 'Source', 'url' => $project->getSourceUrl()];
|
||||
if($project->hasDiscussionUrl())
|
||||
$links[] = ['class' => 'forum', 'text' => 'Discussion', 'url' => $project->getDiscussionUrl()];
|
||||
|
||||
$descLines = $project->hasDescription() ? $project->getDescription()->trim()->split("\n") : [];
|
||||
|
||||
$langs = $self->languages->getByProject($project);
|
||||
|
||||
if($project->hasColour())
|
||||
$colour = $project->getColour();
|
||||
else
|
||||
foreach($langs as $lang)
|
||||
if($lang->hasColour()) {
|
||||
$colour = $lang->getColour();
|
||||
break;
|
||||
}
|
||||
|
||||
$colour = str_pad(dechex($colour), 6, '0', STR_PAD_LEFT);
|
||||
?>
|
||||
|
||||
<div class="project project-type-<?=$sectionId;?>" id="<?=$project->getCleanName();?>" style="--project-colour: #<?=$colour;?>;">
|
||||
<div class="project-content">
|
||||
<div class="project-details">
|
||||
<h2><?=$project->getName();?><div class="project-languages">
|
||||
<?php
|
||||
foreach($langs as $lang):
|
||||
$langColour = str_pad(dechex($lang->getColour() ?? 0), 6, '0', STR_PAD_LEFT);
|
||||
?>
|
||||
<div class="project-language" style="--language-colour: #<?=$langColour;?>;"><?=$lang->getName();?></div>
|
||||
<?php endforeach; ?>
|
||||
</div></h2>
|
||||
|
||||
<?php if($project->hasSummary()): ?>
|
||||
<p class="project-details-summary"><?=$project->getSummary();?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
foreach($descLines as $line):
|
||||
$line = $line->trim();
|
||||
if($line->isEmpty())
|
||||
continue;
|
||||
?>
|
||||
<p><?=$line;?></p>
|
||||
<?php endforeach; ?>
|
||||
|
||||
</div>
|
||||
|
||||
<?php if(!empty($links)): ?>
|
||||
<div class="project-links">
|
||||
|
||||
<?php foreach($links as $link): ?>
|
||||
<a class="project-link project-link-<?=$link['class'];?>" href="<?=$link['url'];?>" rel="noopener" target="_blank"><?=$link['text'];?></a>
|
||||
<?php endforeach; ?>
|
||||
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endforeach; ?>
|
||||
<?php
|
||||
});
|
Loading…
Reference in a new issue