Files
space_profiles/models/forms/SpaceProfileForm.php
2026-04-04 13:11:50 -04:00

292 lines
9.8 KiB
PHP

<?php
namespace humhub\modules\space_profiles\models\forms;
use humhub\modules\content\components\ContentContainerActiveRecord;
use humhub\modules\rescue_foundation\components\UploadStandards;
use humhub\modules\rescue_foundation\components\ValidationStandards;
use humhub\modules\space\models\Space;
use humhub\modules\space_profiles\components\TemplateRegistry;
use humhub\modules\space_profiles\helpers\ProfileHtmlSanitizer;
use humhub\modules\space_profiles\models\SpaceProfile;
use Yii;
use yii\base\Model;
use yii\helpers\FileHelper;
use yii\web\UploadedFile;
class SpaceProfileForm extends Model
{
public ContentContainerActiveRecord $contentContainer;
public string $rescue_name = '';
public string $address = '';
public string $city = '';
public string $state = '';
public string $zip = '';
public string $email = '';
public string $phone = '';
public string $animals_we_accept = '';
public string $description = '';
public string $mission_statement = '';
public string $template_key = TemplateRegistry::TEMPLATE_RESCUE_CENTER;
public ?string $header_html = null;
public ?string $body_html = null;
public ?string $footer_html = null;
public UploadedFile|string|null $iconFile = null;
public UploadedFile|string|null $backgroundImageFile = null;
public bool $removeIcon = false;
public bool $removeBackgroundImage = false;
private ?SpaceProfile $profile = null;
public function init()
{
parent::init();
$this->profile = SpaceProfile::findOne(['contentcontainer_id' => $this->contentContainer->contentcontainer_id]);
if ($this->profile) {
$this->address = (string)$this->profile->address;
$this->city = (string)$this->profile->city;
$this->state = (string)$this->profile->state;
$this->zip = (string)$this->profile->zip;
$this->email = (string)$this->profile->email;
$this->phone = (string)$this->profile->phone;
$this->animals_we_accept = (string)$this->profile->animals_we_accept;
$this->mission_statement = (string)$this->profile->mission_statement;
$this->template_key = !empty($this->profile->template_key)
? (string)$this->profile->template_key
: TemplateRegistry::TEMPLATE_RESCUE_CENTER;
$this->header_html = $this->profile->header_html;
$this->body_html = $this->profile->body_html;
$this->footer_html = $this->profile->footer_html;
}
$this->rescue_name = $this->getSpaceName();
$this->description = $this->getSpaceDescription();
}
public function rules()
{
$contactRules = ValidationStandards::contactRules([
'email' => 'email',
'phone' => 'phone',
'state' => 'state',
'zip' => 'zip',
]);
return array_merge([
[['address', 'city', 'state', 'zip', 'email', 'phone', 'animals_we_accept', 'mission_statement', 'template_key'], 'required'],
[['animals_we_accept', 'description', 'mission_statement', 'header_html', 'body_html', 'footer_html'], 'string'],
[['template_key'], 'in', 'range' => array_keys(TemplateRegistry::options())],
[['rescue_name', 'email'], 'string', 'max' => 190],
[['address'], 'string', 'max' => 255],
[['city'], 'string', 'max' => 120],
[['state'], 'string', 'max' => 2],
[['zip'], 'string', 'max' => 10],
[['phone'], 'string', 'max' => 32],
[['template_key'], 'string', 'max' => 64],
[['removeIcon', 'removeBackgroundImage'], 'boolean'],
[['iconFile', 'backgroundImageFile'], 'file',
'skipOnEmpty' => true,
'extensions' => UploadStandards::imageExtensions(),
'checkExtensionByMimeType' => true,
'mimeTypes' => UploadStandards::imageMimeTypes(),
'maxSize' => UploadStandards::IMAGE_MAX_BYTES,
],
], $contactRules);
}
public function save(): bool
{
if (!$this->validate()) {
return false;
}
$persistedDescription = $this->resolvePersistedDescription();
$profile = $this->profile ?: new SpaceProfile();
$profile->contentcontainer_id = $this->contentContainer->contentcontainer_id;
$profile->rescue_name = $this->getSpaceName();
$profile->address = trim($this->address);
$profile->city = trim($this->city);
$profile->state = strtoupper(trim($this->state));
$profile->zip = trim($this->zip);
$profile->email = trim($this->email);
$profile->phone = trim($this->phone);
$profile->animals_we_accept = trim($this->animals_we_accept);
$profile->description = $persistedDescription;
$profile->mission_statement = trim($this->mission_statement);
$profile->slug = $this->generateUniqueSlug();
$profile->template_key = $this->template_key;
$profile->header_html = ProfileHtmlSanitizer::sanitize($this->header_html);
$profile->body_html = ProfileHtmlSanitizer::sanitize($this->body_html);
$profile->footer_html = ProfileHtmlSanitizer::sanitize($this->footer_html);
if ($this->removeIcon) {
$this->deleteStoredFile($profile->icon_path);
$profile->icon_path = null;
}
if ($this->removeBackgroundImage) {
$this->deleteStoredFile($profile->background_image_path);
$profile->background_image_path = null;
}
if ($this->iconFile instanceof UploadedFile) {
$this->deleteStoredFile($profile->icon_path);
$profile->icon_path = $this->storeImage($this->iconFile, 'icon');
}
if ($this->backgroundImageFile instanceof UploadedFile) {
$this->deleteStoredFile($profile->background_image_path);
$profile->background_image_path = $this->storeImage($this->backgroundImageFile, 'background');
}
if (!$profile->save()) {
$this->addErrors($profile->getErrors());
return false;
}
$this->setDefaultHomePage();
$this->profile = $profile;
return true;
}
public function getProfile(): ?SpaceProfile
{
return $this->profile;
}
public function getTemplateOptions(): array
{
return TemplateRegistry::options();
}
private function storeImage(UploadedFile $file, string $prefix): ?string
{
$relativeDir = '/uploads/space-profiles/' . $this->contentContainer->contentcontainer_id;
$absoluteDir = Yii::getAlias('@webroot') . $relativeDir;
FileHelper::createDirectory($absoluteDir);
$random = Yii::$app->security->generateRandomString(8);
$fileName = $prefix . '-' . time() . '-' . $random . '.' . strtolower((string)$file->extension);
$absolutePath = $absoluteDir . '/' . $fileName;
if (!$file->saveAs($absolutePath)) {
return null;
}
return $relativeDir . '/' . $fileName;
}
private function deleteStoredFile(?string $relativePath): void
{
if (empty($relativePath)) {
return;
}
$absolutePath = Yii::getAlias('@webroot') . $relativePath;
if (is_file($absolutePath)) {
@unlink($absolutePath);
}
}
private function buildBaseSlug(): string
{
$source = $this->getSpaceName();
$normalized = trim((string)$source);
$ascii = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $normalized);
if ($ascii !== false) {
$normalized = $ascii;
}
$slug = strtolower($normalized);
$slug = preg_replace('/[^a-z0-9]+/', '-', $slug) ?? '';
$slug = trim($slug, '-');
return $slug !== '' ? $slug : 'rescue';
}
private function generateUniqueSlug(): string
{
$base = $this->buildBaseSlug();
$candidate = $base;
$suffix = 1;
while ($this->slugExists($candidate)) {
$candidate = $base . '-' . $suffix;
$suffix++;
}
return $candidate;
}
private function slugExists(string $slug): bool
{
$query = SpaceProfile::find()->where(['slug' => $slug]);
if ($this->profile && !$this->profile->isNewRecord) {
$query->andWhere(['!=', 'id', (int)$this->profile->id]);
}
return $query->exists();
}
private function setDefaultHomePage(): void
{
if (!$this->contentContainer instanceof Space) {
return;
}
$defaultUrl = $this->contentContainer->createUrl('/space_profiles/profile/view');
$settings = $this->contentContainer->getSettings();
$settings->set('indexUrl', $defaultUrl);
$settings->set('indexGuestUrl', $defaultUrl);
}
private function getSpaceName(): string
{
if ($this->contentContainer instanceof Space) {
return trim((string)$this->contentContainer->name);
}
return trim($this->rescue_name);
}
private function getSpaceDescription(): string
{
if ($this->contentContainer instanceof Space) {
$about = trim((string)$this->contentContainer->about);
if ($about !== '') {
return $about;
}
return trim((string)$this->contentContainer->description);
}
return trim($this->description);
}
private function resolvePersistedDescription(): string
{
$description = $this->getSpaceDescription();
if ($description !== '') {
return $description;
}
$mission = trim($this->mission_statement);
if ($mission !== '') {
return $mission;
}
$animals = trim($this->animals_we_accept);
if ($animals !== '') {
return $animals;
}
return $this->getSpaceName();
}
}