Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3f5480dea | ||
|
|
77116ebae4 |
@@ -2211,4 +2211,4 @@ Older releases (v1.2.0–v4.0.0) are documented in **[CHANGELOG.md](CHANGELOG.md
|
||||
|
||||
Permitted free use covers internal/personal use and single-client freelance deployment within defined limits. Commercial distribution, bundling, high-volume use (>1M qualified events/month), software transfer to third parties, and competing products require a separate written Commercial License from the Copyright Holder.
|
||||
|
||||
Section 9 of the license excludes liability of the Copyright Holder toward licensees (B2B) and does not limit enforcement against license violators.
|
||||
Section 9 of the license excludes liability of the Copyright Holder toward licensees (B2B) and does not limit enforcement against license violators.
|
||||
|
||||
@@ -2877,4 +2877,92 @@ select.fi-select-input:disabled,
|
||||
.create-link-sidebar-select-wrapper .fi-fo-field-wrp-label,
|
||||
.create-link-sidebar-select-wrapper .fi-label {
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
.filamentshortUrl-panel-footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
margin-top: 1.75rem;
|
||||
padding: 0 1rem 1rem;
|
||||
}
|
||||
|
||||
.filamentshortUrl-panel-footer__card {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.3125rem 0.4375rem;
|
||||
max-width: 100%;
|
||||
padding: 0.3125rem 0.625rem;
|
||||
border-radius: 9999px;
|
||||
background: rgb(244 244 245 / 0.85);
|
||||
box-shadow: 0 0 0 1px rgb(0 0 0 / 0.04);
|
||||
font-size: 0.6875rem;
|
||||
line-height: 1.3;
|
||||
color: rgb(113 113 122);
|
||||
}
|
||||
|
||||
.dark .filamentshortUrl-panel-footer__card {
|
||||
background: rgb(39 39 42 / 0.85);
|
||||
box-shadow: 0 0 0 1px rgb(255 255 255 / 0.06);
|
||||
color: rgb(161 161 170);
|
||||
}
|
||||
|
||||
.filamentshortUrl-panel-footer__title {
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
color: rgb(63 63 70);
|
||||
}
|
||||
|
||||
.dark .filamentshortUrl-panel-footer__title {
|
||||
color: rgb(228 228 231);
|
||||
}
|
||||
|
||||
.filamentshortUrl-panel-footer__version {
|
||||
padding: 0.0625rem 0.3125rem;
|
||||
border-radius: 9999px;
|
||||
background: rgb(254 243 199);
|
||||
color: rgb(146 64 14);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 0.625rem;
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.dark .filamentshortUrl-panel-footer__version {
|
||||
background: rgb(69 26 3 / 0.55);
|
||||
color: rgb(253 230 138);
|
||||
}
|
||||
|
||||
.filamentshortUrl-panel-footer__meta {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.filamentshortUrl-panel-footer__sep {
|
||||
opacity: 0.35;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.filamentshortUrl-panel-footer__link {
|
||||
color: rgb(82 82 91);
|
||||
text-decoration: none;
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.filamentshortUrl-panel-footer__link:hover {
|
||||
color: rgb(180 83 9);
|
||||
}
|
||||
|
||||
.dark .filamentshortUrl-panel-footer__link {
|
||||
color: rgb(212 212 216);
|
||||
}
|
||||
|
||||
.dark .filamentshortUrl-panel-footer__link:hover {
|
||||
color: rgb(251 191 36);
|
||||
}
|
||||
|
||||
.filamentshortUrl-panel-footer__package {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 0.625rem;
|
||||
}
|
||||
2
resources/dist/filament-short-url.css
vendored
2
resources/dist/filament-short-url.css
vendored
File diff suppressed because one or more lines are too long
33
resources/views/panel/footer.blade.php
Normal file
33
resources/views/panel/footer.blade.php
Normal file
@@ -0,0 +1,33 @@
|
||||
@php
|
||||
use Bjanczak\FilamentShortUrl\FilamentShortUrlPlugin;
|
||||
|
||||
$version = FilamentShortUrlPlugin::version();
|
||||
@endphp
|
||||
|
||||
<footer class="filamentshortUrl-panel-footer" aria-label="Plugin footer">
|
||||
<div class="filamentshortUrl-panel-footer__card">
|
||||
<span class="filamentshortUrl-panel-footer__title">Filament Short URL</span>
|
||||
<span class="filamentshortUrl-panel-footer__version">v{{ $version }}</span>
|
||||
<span class="filamentshortUrl-panel-footer__sep" aria-hidden="true">·</span>
|
||||
<span class="filamentshortUrl-panel-footer__meta">
|
||||
{{ FilamentShortUrlPlugin::POWERED_BY_LABEL }}
|
||||
<a
|
||||
href="{{ FilamentShortUrlPlugin::AUTHOR_URL }}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="filamentshortUrl-panel-footer__link"
|
||||
>
|
||||
{{ FilamentShortUrlPlugin::AUTHOR_HANDLE }}
|
||||
</a>
|
||||
</span>
|
||||
<span class="filamentshortUrl-panel-footer__sep" aria-hidden="true">·</span>
|
||||
<a
|
||||
href="{{ FilamentShortUrlPlugin::PACKAGE_URL }}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="filamentshortUrl-panel-footer__link filamentshortUrl-panel-footer__package"
|
||||
>
|
||||
{{ FilamentShortUrlPlugin::PACKAGE_NAME }}
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -16,9 +16,23 @@ use Bjanczak\FilamentShortUrl\Filament\Resources\ShortUrlResource\Pages\ShortUrl
|
||||
use Bjanczak\FilamentShortUrl\Filament\Resources\ShortUrlTagResource;
|
||||
use Filament\Contracts\Plugin;
|
||||
use Filament\Panel;
|
||||
use Filament\Support\Facades\FilamentView;
|
||||
use Filament\View\PanelsRenderHook;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\View;
|
||||
|
||||
class FilamentShortUrlPlugin implements Plugin
|
||||
{
|
||||
public const PACKAGE_NAME = 'janczakb/filament-short-url';
|
||||
|
||||
public const PACKAGE_URL = 'https://github.com/janczakb/filament-short-url';
|
||||
|
||||
public const AUTHOR_HANDLE = 'bjanczak';
|
||||
|
||||
public const AUTHOR_URL = 'https://github.com/janczakb';
|
||||
|
||||
public const POWERED_BY_LABEL = 'Powered by';
|
||||
|
||||
protected ?string $navigationGroup = null;
|
||||
|
||||
protected ?int $navigationSort = null;
|
||||
@@ -67,7 +81,88 @@ class FilamentShortUrlPlugin implements Plugin
|
||||
|
||||
public function boot(Panel $panel): void
|
||||
{
|
||||
// Configuration is consumed by ShortUrlResource via FilamentShortUrlPlugin::get()
|
||||
FilamentView::registerRenderHook(
|
||||
PanelsRenderHook::PAGE_END,
|
||||
fn (): string => self::renderFooter(),
|
||||
);
|
||||
}
|
||||
|
||||
public static function renderFooter(): string
|
||||
{
|
||||
if (! self::shouldShowPluginFooter()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return View::make('filament-short-url::panel.footer')->render();
|
||||
}
|
||||
|
||||
public static function version(): string
|
||||
{
|
||||
static $version = null;
|
||||
|
||||
if ($version !== null) {
|
||||
return $version;
|
||||
}
|
||||
|
||||
$composerPath = dirname(__DIR__).'/composer.json';
|
||||
|
||||
if (! is_readable($composerPath)) {
|
||||
return $version = 'dev';
|
||||
}
|
||||
|
||||
$decoded = json_decode((string) file_get_contents($composerPath), true);
|
||||
|
||||
return $version = is_array($decoded) && isset($decoded['version'])
|
||||
? (string) $decoded['version']
|
||||
: 'dev';
|
||||
}
|
||||
|
||||
public static function shouldShowPluginFooter(?Request $request = null): bool
|
||||
{
|
||||
if (! config('filament-short-url.enabled', true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$request ??= request();
|
||||
|
||||
if ($request === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (self::pluginPathPrefixes() as $prefix) {
|
||||
if (self::pathMatchesPluginPrefix(trim($request->path(), '/'), $prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$routeName = $request->route()?->getName();
|
||||
|
||||
if (! is_string($routeName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return str_starts_with($routeName, 'filament.')
|
||||
&& str_contains($routeName, 'short-url');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
protected static function pluginPathPrefixes(): array
|
||||
{
|
||||
return [
|
||||
'short-urls',
|
||||
'short-url-pixels',
|
||||
'short-url-custom-domains',
|
||||
'short-url-folders',
|
||||
'short-url-tags',
|
||||
'short-url-settings',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function pathMatchesPluginPrefix(string $path, string $prefix): bool
|
||||
{
|
||||
return (bool) preg_match('#(?:^|/)'.preg_quote($prefix, '#').'(?:/|$)#', $path);
|
||||
}
|
||||
|
||||
// ─── Fluent configuration API ────────────────────────────────────────────
|
||||
|
||||
44
tests/Unit/PluginFooterTest.php
Normal file
44
tests/Unit/PluginFooterTest.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Bjanczak\FilamentShortUrl\FilamentShortUrlPlugin;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
it('shows footer only on plugin routes', function () {
|
||||
config(['filament-short-url.enabled' => true]);
|
||||
|
||||
expect(FilamentShortUrlPlugin::shouldShowPluginFooter(Request::create('/admin/short-urls')))
|
||||
->toBeTrue()
|
||||
->and(FilamentShortUrlPlugin::shouldShowPluginFooter(Request::create('/admin/short-url-settings')))
|
||||
->toBeTrue()
|
||||
->and(FilamentShortUrlPlugin::shouldShowPluginFooter(Request::create('/admin/short-url-pixels')))
|
||||
->toBeTrue()
|
||||
->and(FilamentShortUrlPlugin::shouldShowPluginFooter(Request::create('/admin/short-urls/create')))
|
||||
->toBeTrue()
|
||||
->and(FilamentShortUrlPlugin::shouldShowPluginFooter(Request::create('/admin/users')))
|
||||
->toBeFalse();
|
||||
});
|
||||
|
||||
it('hides footer when plugin is disabled', function () {
|
||||
config(['filament-short-url.enabled' => false]);
|
||||
|
||||
expect(FilamentShortUrlPlugin::shouldShowPluginFooter(Request::create('/admin/short-urls')))
|
||||
->toBeFalse();
|
||||
});
|
||||
|
||||
it('renders footer view with package name and author handle', function () {
|
||||
$this->app->instance('request', Request::create('/admin/short-urls'));
|
||||
|
||||
$html = FilamentShortUrlPlugin::renderFooter();
|
||||
|
||||
expect($html)
|
||||
->toContain('Filament Short URL')
|
||||
->toContain(FilamentShortUrlPlugin::POWERED_BY_LABEL)
|
||||
->toContain(FilamentShortUrlPlugin::AUTHOR_HANDLE)
|
||||
->toContain(FilamentShortUrlPlugin::PACKAGE_NAME);
|
||||
});
|
||||
|
||||
it('returns empty footer html outside plugin routes', function () {
|
||||
$this->app->instance('request', Request::create('/admin/users'));
|
||||
|
||||
expect(FilamentShortUrlPlugin::renderFooter())->toBe('');
|
||||
});
|
||||
Reference in New Issue
Block a user