0], [['is_active'], 'boolean'], [['goal_type'], 'in', 'range' => array_keys(DonationGoal::goalTypeOptions())], [['title'], 'string', 'max' => 190], [['image_path'], 'string', 'max' => 255], [['imageGalleryPath'], 'string', 'max' => 500], [['imageGalleryPath'], 'validateGalleryImageSelection'], [['imageFile'], 'file', 'skipOnEmpty' => true, 'extensions' => UploadStandards::imageExtensions(), 'checkExtensionByMimeType' => true, 'mimeTypes' => UploadStandards::imageMimeTypes(), 'maxSize' => UploadStandards::IMAGE_MAX_BYTES, ], [['target_animal_id'], 'required', 'when' => function (self $model) { return $model->goal_type === DonationGoal::TYPE_ANIMAL; }, 'whenClient' => "function () { return $('#donationgoalform-goal_type').val() === 'animal'; }"], ]; } public function attributeLabels() { return [ 'target_animal_id' => Yii::t('DonationsModule.base', 'Target Animal'), 'target_amount' => Yii::t('DonationsModule.base', 'Target Amount'), 'current_amount' => Yii::t('DonationsModule.base', 'Current Amount'), 'imageGalleryPath' => Yii::t('DonationsModule.base', 'Image'), 'imageFile' => Yii::t('DonationsModule.base', 'Image Upload'), ]; } public function loadFromGoal(DonationGoal $goal): void { $this->id = (int)$goal->id; $this->goal_type = (string)$goal->goal_type; $this->target_animal_id = $goal->target_animal_id !== null ? (int)$goal->target_animal_id : null; $this->title = (string)$goal->title; $this->description = (string)$goal->description; $this->image_path = (string)$goal->image_path; $this->imageGalleryPath = (string)$goal->image_path; $this->target_amount = (float)$goal->target_amount; $this->current_amount = (float)$goal->current_amount; $this->is_active = (int)$goal->is_active === 1; } public function save(): ?DonationGoal { if (!$this->validate()) { return null; } $goal = null; if ($this->id) { $goal = DonationGoal::findOne([ 'id' => $this->id, 'contentcontainer_id' => $this->contentContainer->contentcontainer_id, ]); } if (!$goal instanceof DonationGoal) { $goal = new DonationGoal(); $goal->contentcontainer_id = $this->contentContainer->contentcontainer_id; } $goal->goal_type = $this->goal_type; $goal->target_animal_id = $this->goal_type === DonationGoal::TYPE_ANIMAL ? (int)$this->target_animal_id : null; $goal->title = trim($this->title); $goal->description = trim($this->description); $goal->image_path = trim((string)$goal->image_path); $goal->target_amount = $this->target_amount; $goal->current_amount = $this->current_amount; $goal->currency = $this->resolveDefaultCurrency(); $goal->is_active = $this->is_active ? 1 : 0; if (!$goal->save()) { $this->addErrors($goal->getErrors()); return null; } if ($this->imageFile instanceof UploadedFile) { $stored = $this->storeImage($this->imageFile, $goal); if ($stored === null) { $this->addError('imageFile', Yii::t('DonationsModule.base', 'Could not upload goal image.')); return null; } $goal->image_path = $stored; } else { $selectedFromGallery = trim((string)$this->imageGalleryPath); if ($selectedFromGallery !== '') { $goal->image_path = $selectedFromGallery; } } if (!$goal->save()) { $this->addErrors($goal->getErrors()); return null; } return $goal; } public function validateGalleryImageSelection(string $attribute): void { $value = trim((string)$this->$attribute); if ($value === '') { return; } if ($this->goal_type !== DonationGoal::TYPE_ANIMAL || (int)$this->target_animal_id <= 0) { if (preg_match('/^(https?:\/\/|\/)/i', $value)) { return; } $this->addError($attribute, Yii::t('DonationsModule.base', 'Please select a valid image source.')); return; } $options = $this->getGalleryImageOptionsForAnimal((int)$this->target_animal_id); if (!isset($options[$value])) { $this->addError($attribute, Yii::t('DonationsModule.base', 'Please select an image from this animal gallery or upload a new one.')); } } public function getAnimalOptions(): array { $animalClass = 'humhub\\modules\\animal_management\\models\\Animal'; if (!class_exists($animalClass)) { return []; } $tableName = $animalClass::tableName(); if (Yii::$app->db->schema->getTableSchema($tableName, true) === null) { return []; } $animals = $animalClass::find() ->where(['contentcontainer_id' => $this->contentContainer->contentcontainer_id]) ->orderBy(['name' => SORT_ASC, 'id' => SORT_ASC]) ->all(); $options = []; foreach ($animals as $animal) { $label = method_exists($animal, 'getDisplayName') ? (string)$animal->getDisplayName() : trim((string)($animal->name ?? '')); if ($label === '') { $label = 'Animal #' . (int)$animal->id; } $options[(int)$animal->id] = $label; } return $options; } public function getGalleryImageOptionsForAnimal(int $animalId): array { $animalGalleryClass = 'humhub\\modules\\animal_management\\models\\AnimalGalleryItem'; if (!class_exists($animalGalleryClass)) { return []; } if (Yii::$app->db->schema->getTableSchema($animalGalleryClass::tableName(), true) === null) { return []; } $items = $animalGalleryClass::find() ->where(['animal_id' => $animalId]) ->orderBy(['id' => SORT_DESC]) ->all(); $options = []; foreach ($items as $item) { $url = trim((string)$item->getImageUrl()); if ($url === '') { continue; } $options[$url] = Yii::t('DonationsModule.base', 'Gallery Image #{id}', [ 'id' => (int)$item->id, ]); } return $options; } private function resolveDefaultCurrency(): string { $config = DonationProviderConfig::findOne([ 'contentcontainer_id' => $this->contentContainer->contentcontainer_id, ]); if ($config instanceof DonationProviderConfig) { $currency = strtoupper(trim((string)$config->default_currency)); if ($currency !== '') { return $currency; } } return 'USD'; } private function storeImage(UploadedFile $file, DonationGoal $goal): ?string { $random = Yii::$app->security->generateRandomString(8); $extension = strtolower((string)$file->extension); $fileName = 'goal-' . (int)$goal->id . '-' . time() . '-' . $random . '.' . $extension; $candidateDirs = [ '/uploads/donations/goals/' . (int)$goal->id, '/uploads/donations-runtime/goals/' . (int)$goal->id, ]; foreach ($candidateDirs as $relativeDir) { $absoluteDir = Yii::getAlias('@webroot') . $relativeDir; try { FileHelper::createDirectory($absoluteDir, 0775, true); } catch (\Throwable $e) { Yii::warning($e->getMessage(), 'donations.goal_image_upload_dir'); continue; } if (!is_dir($absoluteDir) || !is_writable($absoluteDir)) { continue; } $absolutePath = $absoluteDir . '/' . $fileName; if ($file->saveAs($absolutePath)) { return $relativeDir . '/' . $fileName; } } return null; } }