🆙 Add cms i using 🆙

This commit is contained in:
Remco
2025-11-25 22:42:56 +01:00
parent 94704e0925
commit d44196149e
35591 changed files with 3601123 additions and 0 deletions
@@ -0,0 +1,193 @@
<?php
namespace Stevebauman\Purify\Cache;
use HTMLPurifier_Definition;
use HTMLPurifier_DefinitionCache;
use Illuminate\Support\Facades\Cache;
class CacheDefinitionCache extends HTMLPurifier_DefinitionCache
{
/**
* The cache repository.
*
* @var \Illuminate\Contracts\Cache\Repository
*/
protected $cache;
/**
* Constructor.
*
* @param string $type
*/
public function __construct($type)
{
parent::__construct($type);
$this->cache = Cache::driver(
config('purify.serializer.driver')
);
}
/**
* Adds a definition object to the cache.
*
* @param HTMLPurifier_Definition $def
* @param \HTMLPurifier_Config $config
*
* @return bool|void
*/
public function add($def, $config)
{
if (! $this->checkDefType($def)) {
return;
}
$key = $this->generateKey($config);
if ($this->cache->has($key)) {
return false;
}
return $this->cache->put($key, $this->encode($def));
}
/**
* Unconditionally saves a definition object to the cache.
*
* @param HTMLPurifier_Definition $def
* @param \HTMLPurifier_Config $config
*
* @return bool|void
*/
public function set($def, $config)
{
if (! $this->checkDefType($def)) {
return;
}
$key = $this->generateKey($config);
return $this->cache->put($key, $this->encode($def));
}
/**
* Replace an object in the cache.
*
* @param HTMLPurifier_Definition $def
* @param \HTMLPurifier_Config $config
*
* @return bool|void
*/
public function replace($def, $config)
{
if (! $this->checkDefType($def)) {
return;
}
$key = $this->generateKey($config);
if (! $this->cache->has($key)) {
return false;
}
return $this->cache->put($key, $this->encode($def));
}
/**
* Retrieves a definition object from the cache.
*
* @param \HTMLPurifier_Config $config
*
* @return bool|HTMLPurifier_Definition
*/
public function get($config)
{
$key = $this->generateKey($config);
if (! $this->cache->has($key)) {
return false;
}
return $this->decode($this->cache->get($key));
}
/**
* Removes a definition object to the cache.
*
* @param \HTMLPurifier_Config $config
*
* @return bool
*/
public function remove($config)
{
$key = $this->generateKey($config);
if (! $this->cache->has($key)) {
return false;
}
return $this->cache->delete($key);
}
/**
* Clears all objects from cache.
*
* @param \HTMLPurifier_Config $config
*
* @return bool
*/
public function flush($config)
{
return $this->cache->clear();
}
/**
* Clears all expired (older version or revision) objects from cache.
*
* @param \HTMLPurifier_Config $config
*
* @return bool
*/
public function cleanup($config)
{
$key = $this->generateKey($config);
if ($this->isOld($key, $config)) {
return $this->cache->delete($key);
}
return true;
}
/**
* Encode the definition for storage.
*
* @param HTMLPurifier_Definition $def
*
* @return string
*/
protected function encode($def)
{
return base64_encode(serialize($def));
}
/**
* Decode the definition from storage.
*
* @param string $def
*
* @return HTMLPurifier_Definition
*/
protected function decode($def)
{
// Backwards compatibility with old cache definitions.
$instance = @unserialize($def);
if ($instance instanceof HTMLPurifier_Definition) {
return $instance;
}
return unserialize(base64_decode($def));
}
}
@@ -0,0 +1,229 @@
<?php
namespace Stevebauman\Purify\Cache;
use HTMLPurifier_DefinitionCache;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class FilesystemDefinitionCache extends HTMLPurifier_DefinitionCache
{
/**
* The filesystem disk.
*
* @var \Illuminate\Contracts\Filesystem\Filesystem
*/
protected $disk;
/**
* Constructor.
*
* @param string $type
*/
public function __construct($type)
{
parent::__construct($type);
$this->disk = Storage::disk(
config('purify.serializer.disk')
);
}
/**
* Adds a definition object to the cache.
*
* @param \HTMLPurifier_Definition $def
* @param \HTMLPurifier_Config $config
*
* @return bool|void
*/
public function add($def, $config)
{
if (! $this->checkDefType($def)) {
return;
}
$file = $this->generateFilePath($config);
if ($this->disk->exists($file)) {
return false;
}
return $this->disk->put($file, serialize($def));
}
/**
* Unconditionally saves a definition object to the cache.
*
* @param \HTMLPurifier_Definition $def
* @param \HTMLPurifier_Config $config
*
* @return bool|void
*/
public function set($def, $config)
{
if (! $this->checkDefType($def)) {
return;
}
$file = $this->generateFilePath($config);
return $this->disk->put($file, serialize($def));
}
/**
* Replace an object in the cache.
*
* @param \HTMLPurifier_Definition $def
* @param \HTMLPurifier_Config $config
*
* @return bool|void
*/
public function replace($def, $config)
{
if (! $this->checkDefType($def)) {
return;
}
$file = $this->generateFilePath($config);
if (! $this->disk->exists($file)) {
return false;
}
return $this->disk->put($file, serialize($def));
}
/**
* Retrieves a definition object from the cache.
*
* @param \HTMLPurifier_Config $config
*
* @return bool|\HTMLPurifier_Config
*/
public function get($config)
{
$file = $this->generateFilePath($config);
if (! $this->disk->exists($file)) {
return false;
}
return unserialize($this->disk->get($file));
}
/**
* Removes a definition object to the cache.
*
* @param \HTMLPurifier_Config $config
*
* @return bool
*/
public function remove($config)
{
$file = $this->generateFilePath($config);
if (! $this->disk->exists($file)) {
return false;
}
return $this->disk->delete($file);
}
/**
* Clears all objects from cache.
*
* @param \HTMLPurifier_Config $config
*
* @return bool
*/
public function flush($config)
{
$dir = $this->generateDirectoryPath($config);
foreach ($this->disk->files($dir) as $filename) {
if (Str::startsWith($filename, '.')) {
continue;
}
$this->disk->delete(
implode(DIRECTORY_SEPARATOR, [$dir, $filename])
);
}
return true;
}
/**
* Clears all expired (older version or revision) objects from cache.
*
* @param \HTMLPurifier_Config $config
*
* @return bool
*/
public function cleanup($config)
{
$dir = $this->generateDirectoryPath($config);
foreach ($this->disk->files($dir) as $filename) {
if (Str::startsWith($filename, '.')) {
continue;
}
$key = substr($filename, 0, strlen($filename) - 4);
if ($this->isOld($key, $config)) {
$this->disk->delete(
implode(DIRECTORY_SEPARATOR, [$dir, $filename])
);
}
}
return true;
}
/**
* Generates the file path.
*
* @param \HTMLPurifier_Config $config
*
* @return string
*/
public function generateFilePath($config)
{
$key = $this->generateKey($config);
return $this->generateDirectoryPath($config).'DefinitionCache.php/'.$key.'.ser';
}
/**
* Generates the path to the directory contain this cache's serial files.
*
* @note No trailing slash
*
* @param \HTMLPurifier_Config $config
*
* @return string
*/
public function generateDirectoryPath($config)
{
$base = $this->generateBaseDirectoryPath($config);
return $base.'/'.$this->type;
}
/**
* Generates path to base directory that contains all definition type
* serials.
*
* @param \HTMLPurifier_Config $config
*
* @return string
*/
public function generateBaseDirectoryPath($config)
{
$base = $config->get('Cache.SerializerPath');
return is_null($base) ? '/' : $base;
}
}
@@ -0,0 +1,23 @@
<?php
namespace Stevebauman\Purify\Casts;
abstract class Caster
{
/**
* The name of the config to use for purification.
*
* @var string|null
*/
protected $config;
/**
* Constructor.
*
* @param string|null $config
*/
public function __construct($config = null)
{
$this->config = $config;
}
}
@@ -0,0 +1,43 @@
<?php
namespace Stevebauman\Purify\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Stevebauman\Purify\Facades\Purify;
class PurifyHtmlOnGet extends Caster implements CastsAttributes
{
/**
* Purify the given value.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
*
* @return string|array|null
*/
public function get($model, $key, $value, $attributes)
{
if (is_null($value)) {
return null;
}
return Purify::config($this->config)->clean($value);
}
/**
* Prepare the value for storage.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
*
* @return array|string|null
*/
public function set($model, $key, $value, $attributes)
{
return $value;
}
}
@@ -0,0 +1,43 @@
<?php
namespace Stevebauman\Purify\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Stevebauman\Purify\Facades\Purify;
class PurifyHtmlOnSet extends Caster implements CastsAttributes
{
/**
* Cast the given value.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
*
* @return string|array|null
*/
public function get($model, string $key, $value, array $attributes)
{
return $value;
}
/**
* Purify the value for storage.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
*
* @return array|string|null
*/
public function set($model, string $key, $value, array $attributes)
{
if (is_null($value)) {
return null;
}
return Purify::config($this->config)->clean($value);
}
}
@@ -0,0 +1,66 @@
<?php
namespace Stevebauman\Purify\Commands;
use Illuminate\Config\Repository;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
use Stevebauman\Purify\Cache\CacheDefinitionCache;
use Stevebauman\Purify\Cache\FilesystemDefinitionCache;
class ClearCommand extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'purify:clear';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Flush the HTML purifier serializer cache';
/**
* Execute the console command.
*
* @return void
*/
public function handle(Repository $config)
{
if (empty($serializer = $config->get('purify.serializer'))) {
return $this->error(
'Purifier serializer path is not defined. Did you set it to null or forget to publish the configuration?'
);
}
/** @var class-string $cache */
$cache = $serializer['cache'];
if (is_a($cache, FilesystemDefinitionCache::class, true)) {
$disk = Storage::disk($serializer['disk']);
$disk->deleteDirectory($serializer['path']);
$disk->makeDirectory($serializer['path']);
return $this->info('HTML Purifier serializer filesystem cache cleared successfully.');
}
if (is_a($cache, CacheDefinitionCache::class, true)) {
$cache = Cache::driver($serializer['driver']);
$cache->clear();
return $this->info('HTML Purifier serializer cache cleared successfully.');
}
return $this->error(
sprintf('Unable to determine a clear cache strategy with the given cache instance [%s].', $cache)
);
}
}
@@ -0,0 +1,17 @@
<?php
namespace Stevebauman\Purify\Definitions;
use HTMLPurifier_CSSDefinition;
interface CssDefinition
{
/**
* Apply rules to the CSS Purifier definition.
*
* @param HTMLPurifier_CSSDefinition $definition
*
* @return void
*/
public static function apply(HTMLPurifier_CSSDefinition $definition);
}
@@ -0,0 +1,17 @@
<?php
namespace Stevebauman\Purify\Definitions;
use HTMLPurifier_HTMLDefinition;
interface Definition
{
/**
* Apply rules to the HTML Purifier definition.
*
* @param HTMLPurifier_HTMLDefinition $definition
*
* @return void
*/
public static function apply(HTMLPurifier_HTMLDefinition $definition);
}
@@ -0,0 +1,73 @@
<?php
namespace Stevebauman\Purify\Definitions;
use HTMLPurifier_HTMLDefinition;
class Html5Definition implements Definition
{
/**
* Apply rules to the HTML Purifier definition.
*
* @param HTMLPurifier_HTMLDefinition $definition
*
* @return void
*/
public static function apply(HTMLPurifier_HTMLDefinition $definition)
{
// http://developers.whatwg.org/sections.html
$definition->addElement('section', 'Block', 'Flow', 'Common');
$definition->addElement('nav', 'Block', 'Flow', 'Common');
$definition->addElement('article', 'Block', 'Flow', 'Common');
$definition->addElement('aside', 'Block', 'Flow', 'Common');
$definition->addElement('header', 'Block', 'Flow', 'Common');
$definition->addElement('footer', 'Block', 'Flow', 'Common');
$definition->addElement('address', 'Block', 'Flow', 'Common');
$definition->addElement('hgroup', 'Block', 'Required: h1 | h2 | h3 | h4 | h5 | h6', 'Common');
// http://developers.whatwg.org/grouping-content.html
$definition->addElement('figure', 'Block', 'Optional: (figcaption, Flow) | (Flow, figcaption) | Flow', 'Common');
$definition->addElement('figcaption', 'Inline', 'Flow', 'Common');
// http://developers.whatwg.org/the-video-element.html#the-video-element
$definition->addElement('video', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', [
'src' => 'URI',
'type' => 'Text',
'width' => 'Length',
'height' => 'Length',
'poster' => 'URI',
'preload' => 'Enum#auto,metadata,none',
'controls' => 'Bool',
]);
$definition->addElement('source', 'Block', 'Flow', 'Common', [
'src' => 'URI',
'type' => 'Text',
]);
// http://developers.whatwg.org/interactive-elements.html
$definition->addElement('details', 'Block', 'Flow', 'Common');
$definition->addElement('summary', 'Inline', 'Flow', 'Common', [
'open' => 'Bool',
]);
// http://developers.whatwg.org/text-level-semantics.html
$definition->addElement('u', 'Inline', 'Inline', 'Common');
$definition->addElement('s', 'Inline', 'Inline', 'Common');
$definition->addElement('var', 'Inline', 'Inline', 'Common');
$definition->addElement('sub', 'Inline', 'Inline', 'Common');
$definition->addElement('sup', 'Inline', 'Inline', 'Common');
$definition->addElement('mark', 'Inline', 'Inline', 'Common');
$definition->addElement('wbr', 'Inline', 'Empty', 'Core');
// http://developers.whatwg.org/edits.html
$definition->addElement('ins', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']);
$definition->addElement('del', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']);
$definition->addAttribute('table', 'height', 'Text');
$definition->addAttribute('td', 'border', 'Text');
$definition->addAttribute('th', 'border', 'Text');
$definition->addAttribute('tr', 'width', 'Text');
$definition->addAttribute('tr', 'height', 'Text');
$definition->addAttribute('tr', 'border', 'Text');
}
}
@@ -0,0 +1,23 @@
<?php
namespace Stevebauman\Purify\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @method static \HTMLPurifier getPurifier()
* @method static array|string clean(array|string $input)
* @method static \Stevebauman\Purify\Purify config(string|array $driver = null)
*/
class Purify extends Facade
{
/**
* The facade accessor string.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'purify';
}
}
@@ -0,0 +1,50 @@
<?php
namespace Stevebauman\Purify;
use HTMLPurifier;
use HTMLPurifier_Config;
class Purify
{
/**
* The HTML Purifier instance.
*
* @var HTMLPurifier
*/
protected $purifier;
/**
* Constructor.
*
* @param HTMLPurifier_Config $config
*/
public function __construct(HTMLPurifier_Config $config)
{
$this->purifier = new HTMLPurifier($config);
}
/**
* Sanitize the given input.
*
* @param array|string $input
*
* @return array|string
*/
public function clean($input)
{
return is_array($input)
? $this->purifier->purifyArray($input)
: $this->purifier->purify($input);
}
/**
* Get the underlying HTML Purifier instance.
*
* @return HTMLPurifier
*/
public function getPurifier()
{
return $this->purifier;
}
}
@@ -0,0 +1,215 @@
<?php
namespace Stevebauman\Purify;
use HTMLPurifier_Config;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Manager;
use InvalidArgumentException;
use Stevebauman\Purify\Definitions\CssDefinition;
use Stevebauman\Purify\Definitions\Definition;
class PurifyManager extends Manager
{
/**
* The filesystem manager instance.
*
* @var \Illuminate\Filesystem\FilesystemManager
*/
protected $filesystem;
/**
* Constructor.
*
* @param Container $container
*/
public function __construct(Container $container)
{
parent::__construct($container);
$this->filesystem = $container->make('filesystem');
}
/**
* Convenience alias for driver().
*
* @param string|array|null $config
*
* @return Purify
*
* @throws \InvalidArgumentException
*/
public function config($config = null)
{
return $this->driver($config);
}
/**
* Get the default driver name.
*
* @return string
*/
public function getDefaultDriver()
{
return $this->config->get('purify.default');
}
/**
* Get a driver instance.
*
* @param string|array|null $driver
*
* @return Purify
*
* @throws \InvalidArgumentException
*/
public function driver($driver = null)
{
// First, we will check if the provided "driver" is an array. If so,
// we're dealing with an inline defined config. We'll serialize it
// into a string to dynamically define and set its configuration.
if (is_array($driver)) {
$config = $driver;
$driver = md5(serialize($driver));
$this->config->set("purify.configs.$driver", $config);
}
return parent::driver($driver);
}
/**
* Create a new driver instance.
*
* @param string|array $driver
*
* @return Purify
*
* @throws \InvalidArgumentException
*/
protected function createDriver($driver)
{
// First, we will determine if a custom driver creator exists for the given driver and
// if it does not we will check for a creator method for the driver. Custom creator
// callbacks allow developers to build their own "drivers" easily using Closures.
if (isset($this->customCreators[$driver])) {
return $this->callCustomCreator($driver);
}
if ($config = $this->resolveConfig($driver)) {
return $this->createInstance($driver, $config);
}
throw new InvalidArgumentException("Purify config [$driver] not defined.");
}
/**
* Resolve the configuration for the given config name.
*
* @param string $name
*
* @return array
*/
protected function resolveConfig($name)
{
return $this->config->get("purify.configs.$name");
}
/**
* Resolve the serializer filepath the given config name.
*
* @param string $name
*
* @return string|false
*/
protected function resolveSerializerPath($name)
{
$path = $this->config->get('purify.serializer.path');
if (empty($path)) {
return false;
}
return implode(DIRECTORY_SEPARATOR, [$path, $name]);
}
/**
* Create a new Purify instance with the given config.
*
* @param string $name
* @param array $config
*
* @return Purify
*/
protected function createInstance(string $name, array $config)
{
$serializerPath = $this->resolveSerializerPath($name);
if (! empty($serializerPath)) {
$this->prepareFilesystemStorage($serializerPath);
}
return new Purify(
$this->createHtmlConfig(array_merge(array_filter([
'Cache.SerializerPath' => $serializerPath,
]), $config))
);
}
/**
* Prepare the serializer path in the filesystem storage.
*
* @param string $serializerPath
*
* @return void
*/
protected function prepareFilesystemStorage(string $serializerPath)
{
$disk = $this->config->get('purify.serializer.disk');
if (empty($disk)) {
return;
}
$storage = $this->filesystem->disk($disk);
if (! $storage->exists($serializerPath)) {
$storage->makeDirectory($serializerPath);
}
}
/**
* Create an HTML purifier configuration instance.
*
* @param array $config
*
* @return HTMLPurifier_Config
*/
protected function createHtmlConfig($config)
{
$htmlConfig = HTMLPurifier_Config::create($config);
$htmlConfig->set('HTML.DefinitionID', 'HTML-purify');
$htmlConfig->set('HTML.DefinitionRev', 1);
$htmlConfig->set('Cache.DefinitionImpl', config('purify.serializer.cache'));
if ($definition = $htmlConfig->maybeGetRawHTMLDefinition()) {
$definitionsClass = $this->config->get('purify.definitions');
if ($definitionsClass && is_a($definitionsClass, Definition::class, true)) {
$definitionsClass::apply($definition);
}
}
if ($definition = $htmlConfig->getCSSDefinition()) {
$definitionsClass = $this->config->get('purify.css-definitions');
if ($definitionsClass && is_a($definitionsClass, CssDefinition::class, true)) {
$definitionsClass::apply($definition);
}
}
return $htmlConfig;
}
}
@@ -0,0 +1,54 @@
<?php
namespace Stevebauman\Purify;
use HTMLPurifier_DefinitionCacheFactory;
use Illuminate\Support\ServiceProvider;
use Stevebauman\Purify\Commands\ClearCommand;
class PurifyServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->mergeConfigFrom(__DIR__.'/../config/purify.php', 'purify');
$this->commands(ClearCommand::class);
$this->app->singleton('purify', function ($app) {
if ($cache = config('purify.serializer.cache')) {
HTMLPurifier_DefinitionCacheFactory::instance()->register($cache, $cache);
}
return new PurifyManager($app);
});
}
/**
* Register the publishable configuration.
*
* @return void
*/
public function boot()
{
if ($this->app->runningInConsole()) {
$this->publishes([
__DIR__.'/../config/purify.php' => config_path('purify.php'),
], 'config');
}
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return ['purify'];
}
}