Skip to content

Commit

Permalink
feat: add experimental support for ajax endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
BernhardBaumrock committed Mar 11, 2024
1 parent 1833756 commit 7db7970
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 1 deletion.
123 changes: 122 additions & 1 deletion RockFrontend.module.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Sabberworm\CSS\Rule\Rule;
use Sabberworm\CSS\RuleSet\AtRuleSet;
use Sabberworm\CSS\RuleSet\RuleSet;
use Tracy\Dumper;
use Wa72\HtmlPageDom\HtmlPageCrawler;

function rockfrontend(): RockFrontend
Expand Down Expand Up @@ -47,6 +48,9 @@ class RockFrontend extends WireData implements Module, ConfigurableModule
const recompile = 'rockfrontend-recompile-less';
const defaultVspaceScale = 0.66;

const ajax_noaccess = "ajax-noaccess";
const ajax_rendererror = "ajax-render-error";

const webfont_agents = [
'woff2' => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0', // very modern browsers
'woff' => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0', // modern browsers
Expand Down Expand Up @@ -219,7 +223,8 @@ public function init()
$this->addHookBefore("Page::render", $this, "createCustomLess");
$this->addHookMethod("Page::otherLangUrl", $this, "otherLangUrl");

// health checks
// others
$this->ajaxAddEndpoints();
$this->checkHealth();

// development helpers by rockmigrations
Expand Down Expand Up @@ -362,6 +367,12 @@ private function addLiveReloadScript()
$this->scripts('rockfrontend')->add($file, "defer");
}

public function getLiveReloadFile($returnUrl = false): string
{
$file = $this->minifyFile($this->path . "livereload.js");
if ($returnUrl) return $this->url($this->wire->config->versionUrl($file));
}

/**
* Return link to add a new page under given parent
* @return string
Expand Down Expand Up @@ -441,6 +452,116 @@ private function adjustBrightness($hexCode, $adjustPercent)
return '#' . implode($hexCode);
}

protected function ajaxAddEndpoints(): void
{
// scan for these extensions
// earlier listed extensions have priority
$extensions = ['latte', 'php'];

// attach hook for every found endpoint
$added = [];
foreach ($extensions as $ext) {
$endpoints = $this->wire->files->find(
$this->wire->config->paths->templates . "ajax",
['extensions' => [$ext]]
);
foreach ($endpoints as $endpoint) {
$base = pathinfo($endpoint, PATHINFO_FILENAME);
if (in_array($base, $added)) continue;
$added[] = $base;
$this->wire->addHook("/ajax/$base", function (HookEvent $event) use ($endpoint) {
// make htmx endpoints only available via ajax
// superusers are allowed to access them directly (for debugging)
$sudo = $this->wire->user->isSuperuser();
$ajax = $this->wire->config->ajax;

if (!$ajax and $sudo) return $this->ajaxDebug($endpoint);
else return $this->ajaxPublic($endpoint);
});
}
}
}

private function ajaxDebug($endpoint): string
{
// get response
try {
$raw = $this->ajaxResponse($endpoint);
$response = Dumper::toHtml($raw);
} catch (\Throwable $th) {
$this->log($th->getMessage());
$response = var_dump($raw);
}

// render html
$markup = $this->render(__DIR__ . "/stubs/ajax-debug.latte", [
'endpoint' => $endpoint,
'response' => $response,
'formatted' => $this->ajaxFormatted($raw, $endpoint),
]);

// add live reload and return markup
$this->addLiveReloadSecret();
$this->injectJavascriptSettings($markup);
return $markup;
}

private function ajaxFormatted($raw, $endpoint): string
{
$extension = pathinfo($endpoint, PATHINFO_EXTENSION);
if ($extension === "latte") {
$response = $this->render($endpoint);
} else $response = $raw;

// is response already a string?
if (is_string($response)) {
$exceptions = [
self::ajax_noaccess => "No access",
self::ajax_rendererror => "Error rendering endpoint - see logs for details.",
];
if (array_key_exists($response, $exceptions)) {
throw new WireException($exceptions[$response]);
}

// no exception - return string
return $response;
}

// array --> json
if (is_array($response)) return json_encode($response, JSON_PRETTY_PRINT);

// still no string, try to cast it to string
try {
$response = (string)$response;
} catch (\Throwable $th) {
throw new WireException("Invalid return type");
}

return $response;
}

private function ajaxPublic($endpoint): string
{
try {
$raw = $this->ajaxResponse($endpoint);
$response = $this->ajaxFormatted($raw, $endpoint);
return $response;
} catch (\Throwable $th) {
$this->log($th->getMessage());
return "Error in AJAX endpoint";
}
}

private function ajaxResponse($endpoint)
{
try {
return $this->wire->files->render($endpoint);
} catch (\Throwable $th) {
$this->log($th->getMessage());
return self::ajax_rendererror;
}
}

/**
* ALFRED - A Lovely FRontend EDitor
*
Expand Down
31 changes: 31 additions & 0 deletions stubs/ajax-debug.latte
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RockFrontend AJAX Debugger</title>
<link rel="stylesheet" type="text/css" href="{$config->versionUrl('/wire/modules/AdminTheme/AdminThemeUikit/uikit/dist/css/uikit.min.css')}" />
<script type="text/javascript" src="{$config->versionUrl('/wire/modules/AdminTheme/AdminThemeUikit/uikit/dist/js/uikit.min.js')}"></script>
<script type="text/javascript" src="{$rockfrontend->getLiveReloadFile(true)}"></script>
</head>

<body>
<section class="uk-section-small uk-container">
<div class="uk-alert">
<p>Endpoint: <strong>{$endpoint}</strong></p>
</div>
<div class="uk-alert">
<div class="uk-text-bold">Raw response:</div>
<div class="uk-margin-small uk-background-default">{$response|noescape}</div>
</div>
<div class="uk-alert">
<div class="uk-text-bold">Formatted response:</div>
<div class="uk-margin-small uk-background-default">
<pre class="tracy-dump" style="max-height:500px;">{$formatted}</pre>
</div>
</div>
</section>
</body>

</html>

0 comments on commit 7db7970

Please sign in to comment.