Initial import of space_profiles module

This commit is contained in:
Kelin Rescue Hub
2026-04-04 13:11:50 -04:00
commit 87a59e5a0a
35 changed files with 1627 additions and 0 deletions

37
helpers/ProfileAccess.php Normal file
View File

@@ -0,0 +1,37 @@
<?php
namespace humhub\modules\space_profiles\helpers;
use humhub\modules\space\models\Space;
use humhub\modules\user\models\User;
use Yii;
class ProfileAccess
{
public static function canView(Space $space, User $user = null): bool
{
if ($space->status === Space::STATUS_DISABLED) {
return false;
}
$user = $user ?: (!Yii::$app->user->isGuest ? Yii::$app->user->getIdentity() : null);
if ($user && method_exists($user, 'isSystemAdmin') && $user->isSystemAdmin()) {
return true;
}
if ($user && method_exists($space, 'isMember') && $space->isMember($user)) {
return true;
}
if ($space->visibility === Space::VISIBILITY_ALL) {
return true;
}
if ($space->visibility === Space::VISIBILITY_REGISTERED_ONLY) {
return $user !== null;
}
return false;
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace humhub\modules\space_profiles\helpers;
use yii\helpers\HtmlPurifier;
class ProfileHtmlSanitizer
{
public static function sanitize(?string $html): string
{
$value = (string)$html;
if ($value === '') {
return '';
}
$styleBlocks = [];
if (preg_match_all('#<style\\b[^>]*>(.*?)</style>#is', $value, $matches)) {
$styleBlocks = $matches[1] ?? [];
}
$value = preg_replace('#<style\\b[^>]*>.*?</style>#is', '', $value) ?? '';
$value = preg_replace('#<script\\b[^>]*>.*?</script>#is', '', $value) ?? '';
$value = preg_replace("/\\son[a-z]+\\s*=\\s*(\"[^\"]*\"|'[^']*'|[^\\s>]+)/i", '', $value) ?? '';
$value = preg_replace('/javascript\\s*:/i', '', $value) ?? '';
$value = preg_replace('/expression\\s*\\(/i', '', $value) ?? '';
$purified = HtmlPurifier::process($value, [
'HTML.Allowed' => 'div,p,br,span,strong,em,b,i,u,ul,ol,li,h1,h2,h3,h4,h5,h6,a[href|title|target],img[src|alt|title|width|height],blockquote,hr,table,thead,tbody,tr,th,td,code,pre',
'Attr.EnableID' => false,
'CSS.Trusted' => false,
'URI.AllowedSchemes' => [
'http' => true,
'https' => true,
'mailto' => true,
],
]);
$scopedCss = [];
foreach ($styleBlocks as $styleBlock) {
$css = self::scopeCss((string)$styleBlock);
if (trim($css) !== '') {
$scopedCss[] = $css;
}
}
if (empty($scopedCss)) {
return $purified;
}
return $purified . "\n<style>\n" . implode("\n", $scopedCss) . "\n</style>";
}
private static function scopeCss(string $css): string
{
$css = preg_replace('/@import\\s+[^;]+;/i', '', $css) ?? '';
$css = preg_replace('/javascript\\s*:/i', '', $css) ?? '';
$css = preg_replace('/expression\\s*\\(/i', '', $css) ?? '';
$chunks = explode('}', $css);
$scoped = [];
foreach ($chunks as $chunk) {
if (trim($chunk) === '' || strpos($chunk, '{') === false) {
continue;
}
[$selectors, $body] = explode('{', $chunk, 2);
$selectors = trim($selectors);
if ($selectors === '') {
continue;
}
if (strpos($selectors, '@') === 0) {
$scoped[] = $selectors . '{' . $body . '}';
continue;
}
$selectorParts = array_filter(array_map('trim', explode(',', $selectors)));
$scopedSelectors = [];
foreach ($selectorParts as $selector) {
$selector = str_replace(':root', '.rescue-profile-scope', $selector);
if (preg_match('/^(html|body)\\b/i', $selector)) {
$selector = preg_replace('/^(html|body)\\b/i', '.rescue-profile-scope', $selector) ?? '.rescue-profile-scope';
}
if (strpos($selector, '.rescue-profile-scope') !== 0) {
$selector = '.rescue-profile-scope ' . $selector;
}
$scopedSelectors[] = $selector;
}
if (!empty($scopedSelectors)) {
$scoped[] = implode(', ', $scopedSelectors) . '{' . $body . '}';
}
}
return implode("\\n", $scoped);
}
}