Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
node_modules
storage/*
.idea/*
.DS_Store
_plugin_shop/
editor/src/templates
.buildpath
.dropbox
.project
.settings
Icon
*.esproj/
.vscode/settings.json
TODO
3 changes: 3 additions & 0 deletions .htaccess
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ RewriteCond %{REQUEST_URI} !^/engine/editor.*
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ /engine/dist/index.html [L]

# A site's private shop files: invoices and payment records
RewriteCond %{REQUEST_URI} ^/storage/(-sites/[^/]+/)?_shop_files/ [OR]
Comment thread
uldisrudzitis marked this conversation as resolved.
RewriteCond %{REQUEST_URI} ^/storage.*\.(xml|sqlite|db|log|php)$ [OR]
RewriteCond %{REQUEST_URI} ^/storage/$ [OR]
RewriteCond %{REQUEST_URI} ^/_templates/$
Expand All @@ -71,6 +73,7 @@ RewriteRule .* - [L]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !index.php$
RewriteCond %{REQUEST_URI} !/sitemap\.xml$
RewriteCond %{REQUEST_URI} !^(.*)/$
RewriteRule ^(.*)$ /$1/ [R=301,L]

Expand Down
9 changes: 7 additions & 2 deletions LocalValetDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,20 @@ private function resolve(string $sitePath, string $uri): array
return [self::STATIC, $sitePath . '/engine/dist/index.html'];
}

if (preg_match('#^/storage.*\.(xml|sqlite|db|log|php)$#', $uri) || $uri === '/storage/' || $uri === '/_templates/') {
if (
preg_match('#^/storage/(-sites/[^/]+/)?_shop_files/#', $uri)
|| preg_match('#^/storage.*\.(xml|sqlite|db|log|php)$#', $uri)
|| $uri === '/storage/'
|| $uri === '/_templates/'
) {
return [self::NOT_FOUND, null];
}

if (preg_match(self::PASSTHROUGH_PATTERN, $uri)) {
return $this->passthroughRoute($path, $uri);
}

if (! $this->isActualFile($path) && ! $this->endsWith($uri, 'index.php') && ! $this->endsWith($uri, '/')) {
if (! $this->isActualFile($path) && ! $this->endsWith($uri, 'index.php') && ! $this->endsWith($uri, '/sitemap.xml') && ! $this->endsWith($uri, '/')) {
return [self::REDIRECT, $uri . '/'];
}

Expand Down
5 changes: 5 additions & 0 deletions _api_app/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ AI_DEFAULT_PROVIDER=anthropic
ANTHROPIC_API_KEY=
AI_FEEDBACK_SCRIPT_URL=
AI_FEEDBACK_SCRIPT_SECRET=
SHOP_MAIL_HOST=
SHOP_MAIL_PORT=
SHOP_MAIL_USERNAME=
SHOP_MAIL_PASSWORD=
SHOP_MAIL_AUTO_TLS=
33 changes: 6 additions & 27 deletions _api_app/app/Configuration/SiteSettingsConfigService.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace App\Configuration;

use App\Shared\I18n;
use Illuminate\Support\Facades\Auth;
use App\User\UserModel;

class SiteSettingsConfigService
{
Expand All @@ -22,38 +22,17 @@ public function get($lang = 'en')
return $this->settings;
}

/* Setup for the @@@HACK: */
I18n::load_language($lang);
if (! isset($options)) {
$options = [];
}

$berta = (object) ['security' => (object) ['userLoggedIn' => Auth::check()]];
$ENGINE_ROOT_PATH = config('app.old_berta_root') . '/engine/';
$SITE_ROOT_PATH = config('app.old_berta_root') . '/';
$options['XML_ROOT'] = "{$SITE_ROOT_PATH}storage/";
$conf = file_get_contents(
realpath(config('app.old_berta_root') . '/engine/inc.settings.php')
// The page's settings: the shop's own settings come from the shop plugin
$this->settings = (require config('app.old_berta_root') . '/engine/inc.setting-definition.php')(
I18n::_(...),
false,
UserModel::hostingPlan(),
);

/**
* @todo Fix this HACK, also read settings definitions from shop plugin
*/
// @@@:HACK: read in settings config and set up namespace
// so that I18n would be visible there
$conf = str_replace('<?php', 'namespace App\Shared;', $conf);
eval($conf);

$this->settings = $settingsDefinition;
$this->settings['template']['template']['values'] = $this->getTemplates();
$this->settings['template']['template']['default'] = $this->defaultTemplate;

// Make sure `shop` settings don't mix with the page settings.
// @TODO remove this once we clean up the settings
if (isset($this->settings['shop'])) {
unset($this->settings['shop']);
}

return $this->settings;
}

Expand Down
17 changes: 4 additions & 13 deletions _api_app/app/Configuration/SiteTemplatesConfigService.php
Original file line number Diff line number Diff line change
Expand Up @@ -228,23 +228,14 @@ public function get($lang = 'en')
$ret = [];
I18n::load_language($lang);

foreach ($this->getAllTemplates() as $tpl) {
$ret[$tpl] = [];
$conf = file_get_contents(
$this->TEMPLATE_ROOT . '/' . $tpl . '/template.conf.php'
);
$isShopEnabled = Helpers::isShopEnabled(request()->getHost());

/**
* @todo Fix this HACK
*/
// @@@:HACK: read in template config and set up namespace
// so that I18n would be visible there
$conf = str_replace('<?php', 'namespace App\Shared;', $conf);
foreach ($this->getAllTemplates() as $tpl) {
[
$ret[$tpl]['sectionTypes'],
$ret[$tpl]['templateConf'],
$ret[$tpl]['translations']
] = eval($conf);
$ret[$tpl]['translations'],
] = (require "{$this->TEMPLATE_ROOT}/{$tpl}/template.conf.php")(I18n::_(...), $isShopEnabled);
}

return $ret;
Expand Down
19 changes: 19 additions & 0 deletions _api_app/app/Shared/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,25 @@ public static function arrayToHtmlAttributes($attributes)
return $html;
}

/**
* A page's address on the site: its site, section and tag, ending in a slash. That's the form the
* web server answers, .htaccess redirects every other one to it.
*
* @param array<string> $urlParts The site, section and tag names the address has, none for the front page
*/
public static function sitePath(array $urlParts, bool $isPreviewMode = false): string
{
return '/' . ($urlParts ? implode('/', $urlParts) . '/' : '') . ($isPreviewMode ? '?preview=1' : '');
}

/**
* Whether the shop plugin is licensed for the requested domain
*/
public static function isShopEnabled(?string $host): bool
{
return self::isValidDomain($host, config('plugin-Shop.key'));
}

public static function isValidDomain($requestDomain, $domains)
{
if (empty($requestDomain) || empty($domains)) {
Expand Down
20 changes: 15 additions & 5 deletions _api_app/app/Shared/I18n.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@

namespace App\Shared;

/**
* Translations of the editor's texts, from the `engine/lang` files. A text is looked up by its key:
* either the English text itself, or a key whose English text is in `en.php`.
*/
class I18n
{
public static $translations;

/**
* The English texts of the keys, for a language that has no translation of a key
*
* @var array<string, string>
*/
public static $fallback = [];

public static $default_lang = 'en';

public static function load_language($lang = null)
Expand All @@ -17,6 +28,9 @@ public static function load_language($lang = null)
} elseif (file_exists($ENGINE_ROOT_PATH . '/lang/' . self::$default_lang . '.php')) {
self::$translations = include $ENGINE_ROOT_PATH . '/lang/' . self::$default_lang . '.php';
}

$englishPath = $ENGINE_ROOT_PATH . '/lang/en.php';
self::$fallback = file_exists($englishPath) ? include $englishPath : [];
}

/**
Expand All @@ -25,10 +39,6 @@ public static function load_language($lang = null)
*/
public static function _($key)
{
if (! empty(self::$translations) && isset(self::$translations[$key])) {
return self::$translations[$key];
}

return $key;
return self::$translations[$key] ?? self::$fallback[$key] ?? $key;
}
}
14 changes: 12 additions & 2 deletions _api_app/app/Shared/Storage.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@

class Storage
{
/**
* A site's private shop files, kept next to its shop database so they move, swap and back up with it.
* The web server denies this folder at the top of a site's storage only (.htaccess, LocalValetDriver.php),
* so a site or media folder that happens to share the name stays reachable.
*/
public const SHOP_FILES_FOLDER = '_shop_files';

/** @var array
* Associative array representing data structure handled by this service.
*/
Expand Down Expand Up @@ -297,7 +304,10 @@ protected function xmlFile2array($xml_file)
return [];
}

protected function copyFolder($src, $dst)
/**
* @param list<string> $skipFolders Folders at the top of $src left out of the copy
*/
protected function copyFolder($src, $dst, array $skipFolders = [])
{
if (! is_dir($src)) {
return;
Expand All @@ -309,7 +319,7 @@ protected function copyFolder($src, $dst)
while (false !== ($file = readdir($dir))) {
if (($file != '.') && ($file != '..')) {
if (is_dir($src . '/' . $file)) {
if (! in_array($file, ['-sites', $this->PREVIEW_FOLDER])) {
if (! in_array($file, ['-sites', $this->PREVIEW_FOLDER, ...$skipFolders])) {
self::copyFolder($src . '/' . $file, $dst . '/' . $file);
}
} else {
Expand Down
Loading
Loading