30 changed files with 2311 additions and 0 deletions
@ -0,0 +1,6 @@ |
|||
.idea/ |
|||
|
|||
vendor/ |
|||
composer.phar |
|||
|
|||
Configuration/Secrets.yaml |
@ -0,0 +1,44 @@ |
|||
<?php |
|||
|
|||
namespace App; |
|||
|
|||
use App\Common\UUID; |
|||
use App\Enumerators\Parameter; |
|||
use App\Enumerators\SessionElement; |
|||
use App\Wrappers\APIWrapper; |
|||
use SimpleXMLElement; |
|||
use Symfony\Component\Yaml\Yaml; |
|||
|
|||
class Configuration |
|||
{ |
|||
public static string $SecretDenominator = ";"; |
|||
|
|||
public static function GetConfig(string ...$nav) : string|array |
|||
{ |
|||
$config = Yaml::parseFile(__DIR__ . "/../Configuration/Configuration.yaml"); |
|||
|
|||
$temp = $config; |
|||
foreach ($nav as $n) $temp = $temp[$n]; |
|||
|
|||
if(is_array($temp)) |
|||
{ |
|||
return $temp; |
|||
} |
|||
else |
|||
{ |
|||
if(str_starts_with(haystack: $temp, needle: Configuration::$SecretDenominator)) |
|||
{ |
|||
$secrets = Yaml::parseFile(__DIR__ . "/../Configuration/Secrets.yaml"); |
|||
return $secrets[substr($temp, 1)]; |
|||
} |
|||
} |
|||
|
|||
|
|||
return $temp; |
|||
} |
|||
public static function GetDictionary() : array |
|||
{ |
|||
return Yaml::parseFile(__DIR__ . "/../Localisation/en-GB.yaml"); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,97 @@ |
|||
<?php |
|||
|
|||
namespace App\TwigExtensions; |
|||
|
|||
use App\Configuration; |
|||
use DateTime; |
|||
use DateTimeZone; |
|||
use Twig\Extension\AbstractExtension; |
|||
use Twig\TwigFilter; |
|||
|
|||
require_once dirname($_SERVER["DOCUMENT_ROOT"]) . "/vendor/autoload.php"; |
|||
|
|||
class FiltersExtension extends AbstractExtension |
|||
{ |
|||
public function getFilters() |
|||
{ |
|||
return [ |
|||
new TwigFilter('translate', [$this, 'Translate']), |
|||
new TwigFilter('base64_encode', [$this, 'Base64Encode']), |
|||
new TwigFilter('base64_decode', [$this, 'Base64Decode']), |
|||
new TwigFilter('get_type', [$this, 'GetType']), |
|||
new TwigFilter('pretty_date', [$this, 'PrettyDate']), |
|||
new TwigFilter('pretty_time', [$this, 'PrettyTime']), |
|||
new TwigFilter('pretty_date_time', [$this, 'PrettyDateTime']), |
|||
|
|||
new TwigFilter('json_encode', [$this, 'JSONEncode']), |
|||
new TwigFilter('json_decode', [$this, 'JSONDecode']), |
|||
]; |
|||
} |
|||
|
|||
public function JSONEncode(mixed $target): bool|string |
|||
{ |
|||
return json_encode(value: $target); |
|||
} |
|||
public function JSONDecode(string $target): ?array |
|||
{ |
|||
return json_decode($target, true); |
|||
} |
|||
|
|||
public function Translate(?string $target): string |
|||
{ |
|||
if($target == null) return "!!$target!!"; |
|||
|
|||
if(key_exists(key: $target, array: Configuration::GetDictionary())) |
|||
return Configuration::GetDictionary()[$target]; |
|||
/* |
|||
$temp = Configuration::GetDictionary()[$target]; |
|||
if($temp == null) return "!!$target!!"; |
|||
return $temp; |
|||
*/ |
|||
return "!!$target!!"; |
|||
} |
|||
|
|||
|
|||
public function Base64Encode($target) : string |
|||
{ |
|||
return base64_encode($target) . "=="; |
|||
} |
|||
|
|||
public function Base64Decode($target) |
|||
{ |
|||
return base64_decode($target); |
|||
} |
|||
|
|||
|
|||
public function GetType($target): string |
|||
{ |
|||
return gettype($target); |
|||
} |
|||
|
|||
|
|||
public function PrettyDate(?string $target): string |
|||
{ |
|||
if($target == "" || $target == null) return "meow :3"; |
|||
|
|||
$temp = new DateTime( |
|||
datetime: $target, |
|||
); |
|||
|
|||
return $temp->format("l d F Y"); |
|||
} |
|||
public function PrettyTime(?string $target): string |
|||
{ |
|||
if($target == [] || $target == "" || $target == null) return "meow :3"; |
|||
|
|||
$temp = new DateTime( |
|||
datetime: $target, |
|||
); |
|||
|
|||
return $temp->format("h:i:s A"); |
|||
} |
|||
|
|||
public function PrettyDateTime(?string $target): string |
|||
{ |
|||
return $this->PrettyDate(target: $target) . " at " . $this->PrettyTime(target: $target); |
|||
} |
|||
} |
@ -0,0 +1,130 @@ |
|||
<?php |
|||
|
|||
namespace App\TwigExtensions; |
|||
|
|||
use App\Configuration; |
|||
use App\Wrappers\APIWrapper; |
|||
use App\Wrappers\JWTWrapper; |
|||
use App\Wrappers\LFSWrapper; |
|||
use Ramsey\Uuid\Uuid; |
|||
use Twig\Environment; |
|||
use Twig\Error\LoaderError; |
|||
use Twig\Extension\AbstractExtension; |
|||
use Twig\TwigFunction; |
|||
|
|||
class FunctionExtensions extends AbstractExtension |
|||
{ |
|||
private $twig; |
|||
|
|||
public function __construct(Environment $twig) |
|||
{ |
|||
$this->twig = $twig; |
|||
} |
|||
|
|||
public function getFunctions() |
|||
{ |
|||
return [ |
|||
new TwigFunction('RandomUUID', [$this, 'RandomUUID'], ['is_safe' => ['html']]), |
|||
|
|||
new TwigFunction('BuildForms', [$this, 'BuildForms'], ['is_safe' => ['html']]), |
|||
new TwigFunction('BuildModifiableField', [$this, 'BuildModifiableField'], ['is_safe' => ['html']]), |
|||
new TwigFunction('BuildEnumField', [$this, 'BuildEnumField'], ['is_safe' => ['html']]), |
|||
|
|||
new TwigFunction('GetLFSFileURL', [$this, 'GetLFSFileURL'], ['is_safe' => ['html']]), |
|||
new TwigFunction('GetConfigurationItem', [$this, 'GetConfigurationItem'], ['is_safe' => ['html']]), |
|||
|
|||
new TwigFunction('ParseForUserToken', [$this, 'ParseForUserToken'], ['is_safe' => ['html']]), |
|||
]; |
|||
} |
|||
|
|||
public function RandomUUID(): string |
|||
{ |
|||
return Uuid::uuid4()->toString(); |
|||
} |
|||
|
|||
public function BuildForms(...$formDefinitions): string |
|||
{ |
|||
$output = '<div>'; |
|||
foreach ($formDefinitions as $formDefinition) |
|||
{ |
|||
$output .= twig_include($this->twig, [], '/Partials/Form.html.twig', ['FormDefinition' => $formDefinition]); |
|||
} |
|||
return "$output</div>"; |
|||
} |
|||
public function BuildModifiableField(string $apiBase, string $id, string $value): string |
|||
{ |
|||
$output = twig_include($this->twig, [], '/Partials/ModifiableField.html.twig', ['FieldData' => [ |
|||
"APIBase"=>$apiBase, |
|||
"ID"=>$id, |
|||
"Value"=>$value, |
|||
]]); |
|||
return "$output"; |
|||
} |
|||
public function BuildEnumField(string $apiBase, string $id, string $value, string $enumName): string |
|||
{ |
|||
$enumData = APIWrapper::Get("/Enumerators/$enumName")->Payload["Cases"]; |
|||
foreach($enumData as $case) |
|||
{ |
|||
if($case["Value"] == $value) |
|||
{ |
|||
$currentEnum = $case; |
|||
} |
|||
} |
|||
|
|||
$output = twig_include($this->twig, [], '/Partials/EnumField.html.twig', ['FieldData' => [ |
|||
"APIBase"=>$apiBase, |
|||
"ID"=>$id, |
|||
"Value"=>$value, |
|||
"EnumName"=>$enumName, |
|||
"CurrentEnum"=>$currentEnum, |
|||
"EnumData"=>$enumData, |
|||
]]); |
|||
return "$output"; |
|||
} |
|||
public function GetConfigurationItem(string...$nav): string |
|||
{ |
|||
return Configuration::GetConfig(...$nav); |
|||
} |
|||
|
|||
public function GetLFSFileURL(string $fileID): string |
|||
{ |
|||
return LFSWrapper::GetFileURLByID($fileID); |
|||
} |
|||
|
|||
|
|||
public function ParseForUserToken(string $input) : string |
|||
{ |
|||
$modes = implode(separator: "|", array:[ |
|||
"USER", |
|||
"VEHICLE", |
|||
]); |
|||
$uuidRegex = "[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}"; |
|||
$fullRegex = "/@({$modes}).{$uuidRegex}.{$uuidRegex}/"; |
|||
preg_match_all($fullRegex, $input, $matches); |
|||
|
|||
foreach($matches[0] as $match) |
|||
{ |
|||
$parts = explode(".", $match); |
|||
|
|||
$mode = substr(string: $parts[0], offset: 1); |
|||
$tenantID = $parts[1]; |
|||
$objectID = $parts[2]; |
|||
|
|||
switch ($mode) |
|||
{ |
|||
case "USER": |
|||
$temp = twig_include($this->twig, [], '/Partials/UserButton.html.twig', [ |
|||
'_tenantID' => $tenantID, |
|||
'_userID' => $objectID, |
|||
]); |
|||
break; |
|||
case "VEHICLE": |
|||
break; |
|||
} |
|||
|
|||
$input = str_replace($match, $temp, $input); |
|||
} |
|||
|
|||
return $input; |
|||
} |
|||
} |
@ -0,0 +1,45 @@ |
|||
<?php |
|||
|
|||
namespace App\TwigExtensions; |
|||
|
|||
use Twig\Extension\AbstractExtension; |
|||
use Twig\TwigFunction; |
|||
|
|||
require_once dirname($_SERVER["DOCUMENT_ROOT"]) . "/vendor/autoload.php"; |
|||
|
|||
class NavigationExtension extends AbstractExtension |
|||
{ |
|||
public function getFunctions() |
|||
{ |
|||
return [ |
|||
new TwigFunction('RenderServiceLinks', [$this, 'RenderServiceLinks']), |
|||
new TwigFunction('RenderBackButton', [$this, 'RenderBackButton']), |
|||
]; |
|||
} |
|||
|
|||
public function RenderServiceLinks(): string |
|||
{ |
|||
$serviceLinkHTML = ""; |
|||
|
|||
return "yrdy"; |
|||
} |
|||
|
|||
public function RenderBackButton(string $title) : string |
|||
{ |
|||
$html = ' |
|||
<div style="display: flex; align-items: center;"> |
|||
<div style="cursor: pointer; display: flex; align-items: center;" onclick="window.history.back();"> |
|||
<i class="material-icons">arrow_back</i> |
|||
<span>Back</span> |
|||
</div> |
|||
<h1 style="margin-left: 1em;">{{TITLE}}</h1> |
|||
<div style="display: table; clear: both"></div> |
|||
</div> |
|||
'; |
|||
return str_replace( |
|||
search: "{{TITLE}}", |
|||
replace: $title, |
|||
subject: $html |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,36 @@ |
|||
<?php |
|||
|
|||
namespace App\TwigExtensions; |
|||
|
|||
use Twig\Extension\AbstractExtension; |
|||
use Twig\TwigFunction; |
|||
|
|||
class StringManipulationExtension extends AbstractExtension |
|||
{ |
|||
public function getFunctions() |
|||
{ |
|||
return [ |
|||
new TwigFunction('turn_mot_code_to_url', [$this, 'TurnMOTCodeToURL']), |
|||
]; |
|||
} |
|||
|
|||
|
|||
public function TurnMOTCodeToURL(string $motCode) : string |
|||
{ |
|||
$groups = [ |
|||
7=>"7-other-equipment" |
|||
]; |
|||
|
|||
$motCodeElements = explode(separator: '.', string: $motCode); |
|||
|
|||
$builder = "https://www.gov.uk/guidance/mot-inspection-manual-for-private-passenger-and-light-commercial-vehicles/"; |
|||
$builder .= $groups[intval(value: $motCodeElements[0])]; |
|||
$builder .= "#section-"; |
|||
$builder .= "-"; |
|||
$builder .= $motCodeElements[1]; |
|||
$builder .= "-"; |
|||
$builder .= $motCodeElements[2]; |
|||
|
|||
return $builder; |
|||
} |
|||
} |
@ -0,0 +1,33 @@ |
|||
<?php |
|||
// src/Twig/AppExtension.php |
|||
namespace App\TwigTests; |
|||
|
|||
use Twig\Extension\AbstractExtension; |
|||
use Twig\TwigTest; |
|||
|
|||
class TwigTests extends AbstractExtension |
|||
{ |
|||
public function getTests() |
|||
{ |
|||
return array( |
|||
new TwigTest('object', [$this, 'isObject']), |
|||
new TwigTest('array', [$this, 'isArray']), |
|||
new TwigTest('numeric', [$this, 'isNumeric']), |
|||
); |
|||
} |
|||
|
|||
public function isObject($object) |
|||
{ |
|||
return is_object($object); |
|||
} |
|||
|
|||
public function isArray($value) |
|||
{ |
|||
return is_array($value); |
|||
} |
|||
|
|||
public function isNumeric($value) |
|||
{ |
|||
return is_numeric($value); |
|||
} |
|||
} |
@ -0,0 +1,18 @@ |
|||
<?php |
|||
|
|||
namespace App\Wrappers; |
|||
|
|||
use Algolia\AlgoliaSearch\Api\SearchClient; |
|||
use App\Configuration; |
|||
|
|||
class AlgoliaInteractions |
|||
{ |
|||
private SearchClient $SearchClient; |
|||
public function __construct() |
|||
{ |
|||
$this->SearchClient = SearchClient::create( |
|||
appId: Configuration::GetConfig("Algolia", "AppID"), |
|||
apiKey: Configuration::GetConfig("Algolia", "Keys", "SearchOnly"), |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,77 @@ |
|||
<?php |
|||
|
|||
namespace App\Wrappers; |
|||
|
|||
use App\API\Models\_DatabaseRecordPermissionSet; |
|||
use App\API\Models\EnumeratorDetails; |
|||
use App\API\Models\UserDetails; |
|||
use App\Configuration; |
|||
use Aura\SqlQuery\Common\DeleteInterface; |
|||
use Aura\SqlQuery\Common\InsertInterface; |
|||
use Aura\SqlQuery\Common\SelectInterface; |
|||
use Aura\SqlQuery\Common\UpdateInterface; |
|||
use Aura\SqlQuery\QueryFactory; |
|||
use PDO; |
|||
|
|||
class DatabaseInteractions |
|||
{ |
|||
private PDO $pdo; |
|||
|
|||
public function __construct() |
|||
{ |
|||
$servername = Configuration::GetConfig('MariaDB', 'Host'); |
|||
$database = Configuration::GetConfig('MariaDB', 'Database'); |
|||
$username = Configuration::GetConfig('MariaDB', 'Username'); |
|||
$password = Configuration::GetConfig('MariaDB', 'Password'); |
|||
|
|||
$this->pdo = new PDO( |
|||
"mysql:host=$servername;dbname=$database", $username, $password |
|||
); |
|||
} |
|||
|
|||
public function ScheduleRowForDeletion(string $tableName, string $id) |
|||
{ |
|||
|
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
public function RunSelect(SelectInterface $queryBuilder): array |
|||
{ |
|||
$sth = $this->pdo->prepare($queryBuilder->getStatement()); |
|||
$sth->execute($queryBuilder->getBindValues()); |
|||
$result = $sth->fetchAll(PDO::FETCH_ASSOC); |
|||
return $result; |
|||
} |
|||
|
|||
public function RunOneSelect(SelectInterface $queryBuilder): array |
|||
{ |
|||
$sth = $this->pdo->prepare($queryBuilder->getStatement()); |
|||
$sth->execute($queryBuilder->getBindValues()); |
|||
$result = $sth->fetchAll(PDO::FETCH_ASSOC); |
|||
if(sizeof($result) == 1) return $result[0]; |
|||
|
|||
echo "invalid single row db response"; |
|||
die(); |
|||
} |
|||
|
|||
public function UpdateSingleField(UpdateInterface $queryBuilder): void |
|||
{ |
|||
$sth = $this->pdo->prepare($queryBuilder->getStatement()); |
|||
$sth->execute($queryBuilder->getBindValues()); |
|||
$result = $sth->fetchAll(PDO::FETCH_ASSOC); |
|||
} |
|||
|
|||
public function RunInsert(InsertInterface $queryBuilder): bool |
|||
{ |
|||
$sth = $this->pdo->prepare($queryBuilder->getStatement()); |
|||
return $sth->execute($queryBuilder->getBindValues()); |
|||
} |
|||
|
|||
public function RunDelete(DeleteInterface $queryBuilder): bool |
|||
{ |
|||
$sth = $this->pdo->prepare($queryBuilder->getStatement()); |
|||
return $sth->execute($queryBuilder->getBindValues()); |
|||
} |
|||
} |
@ -0,0 +1,41 @@ |
|||
<?php |
|||
|
|||
namespace App\Wrappers; |
|||
|
|||
use App\Common\DateTimeHandling; |
|||
use App\Common\ErrorPayloadRendering; |
|||
use Aura\SqlQuery\Common\DeleteInterface; |
|||
use Aura\SqlQuery\QueryFactory; |
|||
use DateTime; |
|||
|
|||
class SQLQueryBuilderWrapper |
|||
{ |
|||
public static function SELECT(string $table) |
|||
{ |
|||
$query_factory = new QueryFactory(db: 'mysql'); |
|||
$query = $query_factory->newSelect() |
|||
->from("$table AS T") |
|||
; |
|||
return $query |
|||
->cols([ |
|||
"T.*", |
|||
]); |
|||
} |
|||
|
|||
public static function SELECT_ONE(string $table, string $id) |
|||
{ |
|||
return self::SELECT(table: $table) |
|||
->where(cond: "ID=:__id__") |
|||
->bindValue(name: "__id__", value: $id) |
|||
->limit(limit: 1) |
|||
; |
|||
} |
|||
|
|||
public static function SELECT_WITH_PARENT_ID(string $table, string $parentIDName, string $parentIDValue) |
|||
{ |
|||
return self::SELECT(table: $table) |
|||
->where(cond: "$parentIDName=:__parent_id__") |
|||
->bindValue(name: "__parent_id__", value: $parentIDValue) |
|||
; |
|||
} |
|||
} |
@ -0,0 +1,15 @@ |
|||
|
|||
Algolia: |
|||
AppID: ;ALGOLIA__APP_ID |
|||
IndexName: FolkTunes_LIVE |
|||
APIKeys: |
|||
SearchOnly: ;ALGOLIA__API_SEARCH_KEY |
|||
Write: ;ALGOLIA__API_WRITE_KEY |
|||
Admin: ;ALGOLIA__API_ADMIN_KEY |
|||
|
|||
MariaDB: |
|||
Host: vps-gen-eng-001.infra.scorpia.network |
|||
Port: 3306 |
|||
Username: ;MARIADB__USERNAME |
|||
Password: ;MARIADB__PASSWORD |
|||
Database: FolkTuneFinder_Live |
@ -0,0 +1,160 @@ |
|||
|
|||
################################################## |
|||
# A |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# B |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# C |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# D |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# E |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# F |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# G |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# H |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# I |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# J |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# K |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# L |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# M |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# N |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# O |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# P |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# Q |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# R |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# S |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# T |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# U |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# V |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# W |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# X |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# Y |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# Z |
|||
################################################## |
|||
|
|||
|
|||
|
|||
################################################## |
|||
# _ |
|||
################################################## |
@ -0,0 +1,8 @@ |
|||
<?php |
|||
|
|||
use App\Wrappers\TwigWrapper; |
|||
|
|||
require_once __DIR__ . "/../vendor/autoload.php"; |
|||
|
|||
TwigWrapper::AutoRenderTwig([ |
|||
]); |
@ -0,0 +1,22 @@ |
|||
<?php |
|||
|
|||
use App\Wrappers\DatabaseInteractions; |
|||
use App\Wrappers\SQLQueryBuilderWrapper; |
|||
use App\Wrappers\TwigWrapper; |
|||
|
|||
$db = new DatabaseInteractions(); |
|||
|
|||
$tuneDetails = $db->RunOneSelect( |
|||
queryBuilder: SQLQueryBuilderWrapper::SELECT_ONE( |
|||
table: 'Tunes', |
|||
id: $_GET['tune-id'] |
|||
), |
|||
); |
|||
|
|||
TwigWrapper::RenderTwig( |
|||
target: "Pages/tune/uuid.html.twig", |
|||
arguments: [ |
|||
"TuneDetails"=>$tuneDetails, |
|||
] |
|||
); |
|||
|
@ -0,0 +1,37 @@ |
|||
|
|||
|
|||
|
|||
.InnerContent { |
|||
margin: 0 auto; |
|||
text-align: center; |
|||
} |
|||
|
|||
input[type="text"] { |
|||
width: 80%; |
|||
padding: 10px; |
|||
margin-bottom: 10px; |
|||
border: 1px solid #ccc; |
|||
border-radius: 4px; |
|||
} |
|||
#AlgoliaResults { |
|||
margin-top: 20px; |
|||
text-align: left; |
|||
} |
|||
|
|||
.AlgoliaTuneHit { |
|||
padding: 10px; |
|||
border: 1px solid #ccc; |
|||
border-radius: 4px; |
|||
background-color: #fff; |
|||
margin-bottom: 10px; |
|||
} |
|||
|
|||
.AlgoliaTuneHit h2 { |
|||
|
|||
} |
|||
.AlgoliaTuneHit .TuneTimeSignature { |
|||
|
|||
} |
|||
.AlgoliaTuneHit .TuneKeySignature { |
|||
|
|||
} |
@ -0,0 +1,21 @@ |
|||
|
|||
* { |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
body{ |
|||
font-family: 'Atkinson Hyperlegible', sans-serif; |
|||
font-style: normal; |
|||
font-weight: 300; |
|||
font-smoothing: antialiased; |
|||
|
|||
-webkit-font-smoothing: antialiased; |
|||
-moz-osx-font-smoothing: grayscale; |
|||
|
|||
font-size: 1rem; |
|||
|
|||
} |
|||
main { |
|||
background: var(--colour--main--background); |
|||
} |
|||
|
@ -0,0 +1,11 @@ |
|||
|
|||
/* FONTS */ |
|||
@import url('https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible&display=swap'); |
|||
|
|||
/* ICONS */ |
|||
@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css'); |
|||
|
|||
/* ELEMENTS */ |
|||
@import "/Static/CSS/Elements/_General.css"; |
|||
@import "/Static/CSS/Elements/HomePage.css"; |
|||
|
@ -0,0 +1,2 @@ |
|||
:root { |
|||
} |
@ -0,0 +1,30 @@ |
|||
|
|||
|
|||
|
|||
function searchTunes(query) { |
|||
const resultsDiv = document.getElementById("AlgoliaResults"); |
|||
|
|||
resultsDiv.innerHTML = ""; |
|||
|
|||
index.search(query).then(({ hits }) => { |
|||
if (hits.length > 0) { |
|||
hits.forEach(hit => { |
|||
const tuneDiv = document.createElement("div"); |
|||
tuneDiv.className = "AlgoliaTuneHit"; |
|||
tuneDiv.innerHTML = ` |
|||
<h2><a href="/tune/${hit.objectID}">${hit.title}</a></h2> |
|||
<div class="TuneTimeSignature">time signature: ${hit.time_sig}</div> |
|||
<div class="TuneKeySignature">key signature: ${hit.key_sig}</div> |
|||
`;
|
|||
resultsDiv.appendChild(tuneDiv); |
|||
}); |
|||
} else { |
|||
resultsDiv.innerHTML = "<p>No tunes found. Try a different search!</p>"; |
|||
} |
|||
}).catch(err => { |
|||
console.error('Error searching Algolia:', err); |
|||
resultsDiv.innerHTML = "<p>An error occurred. Please try again later.</p>"; |
|||
}); |
|||
} |
|||
|
|||
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,29 @@ |
|||
<?php |
|||
|
|||
use App\Configuration; |
|||
use App\Enumerators\SessionElement; |
|||
use App\Wrappers\TwigWrapper; |
|||
|
|||
require_once __DIR__ . "/../vendor/autoload.php"; |
|||
|
|||
session_start(); |
|||
|
|||
// Get the request URI and break it into segments |
|||
$requestUri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH); |
|||
$requestElements = explode("/", trim($requestUri, "/")); |
|||
|
|||
switch($requestElements[0]) |
|||
{ |
|||
case "Static": |
|||
case "favicon.ico": |
|||
return false; |
|||
case "": |
|||
require_once __DIR__ . '/../Pages/index.php'; |
|||
return true; |
|||
case "tune": |
|||
$_GET['tune-id'] = $requestElements[1]; |
|||
require_once __DIR__ . '/../Pages/tune/uuid.php'; |
|||
return true; |
|||
} |
|||
|
|||
return false; |
@ -0,0 +1,26 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<link rel="shortcut icon" type="image/png" href="/favicon.png?type=png" /> |
|||
<title>{{ "Folk Tunes"|translate }} | {% block PageTitle %}{% endblock %}</title> |
|||
<link rel="stylesheet" href="/Static/CSS/Mapper.css"> |
|||
<link rel="stylesheet" href="/Static/CSS/Themes/Light.css"> |
|||
</head> |
|||
|
|||
<body> |
|||
|
|||
<main> |
|||
|
|||
<div class="ErrorPageContainer"> |
|||
<h1>{% block error_code %}{% endblock %}</h1> |
|||
<h2>{% block error_message %}{% endblock %}</h2> |
|||
|
|||
<a class="Button" href="/">Back to Home</a> |
|||
</div> |
|||
|
|||
</main> |
|||
|
|||
</body> |
|||
</html> |
@ -0,0 +1,33 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<link rel="shortcut icon" type="image/png" href="/favicon.png?type=png" /> |
|||
<title>{{ "Folk Tunes"|translate }} | {% block PageTitle %}{% endblock %}</title> |
|||
<link rel="stylesheet" href="/Static/CSS/Mapper.css"> |
|||
<link rel="stylesheet" href="/Static/CSS/Themes/Light.css"> |
|||
|
|||
|
|||
<script src="https://cdn.jsdelivr.net/npm/algoliasearch/dist/algoliasearch-lite.umd.js"></script> |
|||
<script> |
|||
const client = algoliasearch('{{ _ALGOLIA_APP_ID_ }}', '{{ _ALGOLIA_SEARCH_ONLY_API_KEY_ }}'); |
|||
const index = client.initIndex('{{ _ALGOLIA_INDEX_NAME_ }}'); |
|||
</script> |
|||
<script src="/Static/JS/AlgoliaInteractions.js"></script> |
|||
</head> |
|||
|
|||
<body> |
|||
|
|||
<div class="wrapper"> |
|||
<div class="main_container"> |
|||
<main> |
|||
{% block content %}{% endblock %} |
|||
</main> |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
</body> |
|||
|
|||
</html> |
@ -0,0 +1,4 @@ |
|||
{% extends "/Bases/ErrorPage.html.twig" %} |
|||
|
|||
{% block error_code %}404{% endblock %} |
|||
{% block error_message %}{{ "Page Not Found"|translate }}{% endblock %} |
@ -0,0 +1,21 @@ |
|||
{% extends "/Bases/StandardWebPage.html.twig" %} |
|||
|
|||
{% block content %} |
|||
<div class="InnerContent"> |
|||
<h1>Folk Tune Search</h1> |
|||
<input type="text" id="SearchInput" placeholder="Search for a tune..."> |
|||
<div id="AlgoliaResults"></div> |
|||
</div> |
|||
|
|||
<script> |
|||
// Event listener for real-time search |
|||
document.getElementById("SearchInput").addEventListener("input", (event) => { |
|||
const query = event.target.value; |
|||
if (query.trim().length > 0) { |
|||
searchTunes(query); |
|||
} else { |
|||
document.getElementById("AlgoliaResults").innerHTML = ""; |
|||
} |
|||
}); |
|||
</script> |
|||
{% endblock %} |
@ -0,0 +1,7 @@ |
|||
{% extends "/Bases/StandardWebPage.html.twig" %} |
|||
|
|||
{% block content %} |
|||
<div class="InnerContent"> |
|||
<h1>{{ TuneDetails.Title }}</h1> |
|||
</div> |
|||
{% endblock %} |
@ -0,0 +1,32 @@ |
|||
{ |
|||
"name": "darksparrow/folktunes", |
|||
"description": "Folk Tune Search Engine", |
|||
"type": "project", |
|||
"license": "proprietary", |
|||
"authors": [ |
|||
{ |
|||
"name": "Cerys Lewis", |
|||
"email": "cerys@darksparrow.uk", |
|||
"role": "Lead Developer" |
|||
} |
|||
], |
|||
"support": { |
|||
"email": "systems@darksparrow.uk" |
|||
}, |
|||
"autoload": { |
|||
"psr-4": { |
|||
"App\\": "App/" |
|||
} |
|||
}, |
|||
"require": { |
|||
"php": ">=8.2", |
|||
|
|||
"zircote/swagger-php": "~4.7.9", |
|||
|
|||
"twig/twig": "3.14.1.0", |
|||
|
|||
"algolia/algoliasearch-client-php": "*", |
|||
"ext-pdo": "*", |
|||
"aura/sqlquery": "2.8.1" |
|||
} |
|||
} |
File diff suppressed because it is too large
Loading…
Reference in new issue