mirror of
https://github.com/getformwork/formwork.git
synced 2025-01-17 21:49:04 +01:00
Restore upload errors
This commit is contained in:
parent
484d8aab96
commit
de78f92029
@ -3,12 +3,12 @@
|
||||
namespace Formwork\Files;
|
||||
|
||||
use Formwork\Config\Config;
|
||||
use Formwork\Exceptions\TranslatedException;
|
||||
use Formwork\Http\Files\UploadedFile;
|
||||
use Formwork\Utils\Arr;
|
||||
use Formwork\Utils\FileSystem;
|
||||
use Formwork\Utils\MimeType;
|
||||
use Formwork\Utils\Str;
|
||||
use RuntimeException;
|
||||
|
||||
class FileUploader
|
||||
{
|
||||
@ -30,7 +30,7 @@ class FileUploader
|
||||
$mimeType = MimeType::fromFile($uploadedFile->tempPath());
|
||||
|
||||
if (!in_array($mimeType, $this->allowedMimeTypes(), true)) {
|
||||
throw new RuntimeException(sprintf('Invalid mime type %s for file uploads', $mimeType));
|
||||
throw new TranslatedException(sprintf('Invalid mime type %s for file uploads', $mimeType), 'upload.error.mimeType');
|
||||
}
|
||||
|
||||
$filename = Str::slug($name ?? pathinfo($uploadedFile->clientName(), PATHINFO_FILENAME)) . '.' . MimeType::toExtension($mimeType);
|
||||
|
@ -24,14 +24,14 @@ class UploadedFile
|
||||
/**
|
||||
* Uploader errors language strings
|
||||
*/
|
||||
protected const ERROR_LANGUAGE_STRINGS = [
|
||||
UPLOAD_ERR_INI_SIZE => 'panel.uploader.error.size',
|
||||
UPLOAD_ERR_FORM_SIZE => 'panel.uploader.error.size',
|
||||
UPLOAD_ERR_PARTIAL => 'panel.uploader.error.partial',
|
||||
UPLOAD_ERR_NO_FILE => 'panel.uploader.error.noFile',
|
||||
UPLOAD_ERR_NO_TMP_DIR => 'panel.uploader.error.noTemp',
|
||||
UPLOAD_ERR_CANT_WRITE => 'panel.uploader.error.cannotWrite',
|
||||
UPLOAD_ERR_EXTENSION => 'panel.uploader.error.phpExtension',
|
||||
protected const ERROR_TRANSLATION_STRINGS = [
|
||||
UPLOAD_ERR_INI_SIZE => 'upload.error.size',
|
||||
UPLOAD_ERR_FORM_SIZE => 'upload.error.size',
|
||||
UPLOAD_ERR_PARTIAL => 'upload.error.partial',
|
||||
UPLOAD_ERR_NO_FILE => 'upload.error.noFile',
|
||||
UPLOAD_ERR_NO_TMP_DIR => 'upload.error.noTemp',
|
||||
UPLOAD_ERR_CANT_WRITE => 'upload.error.cannotWrite',
|
||||
UPLOAD_ERR_EXTENSION => 'upload.error.phpExtension',
|
||||
];
|
||||
|
||||
protected string $clientName;
|
||||
@ -94,6 +94,11 @@ class UploadedFile
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return $this->error === UPLOAD_ERR_NO_FILE;
|
||||
}
|
||||
|
||||
public function isUploaded(): bool
|
||||
{
|
||||
return $this->error === UPLOAD_ERR_OK;
|
||||
@ -104,30 +109,31 @@ class UploadedFile
|
||||
return self::ERROR_MESSAGES[$this->error];
|
||||
}
|
||||
|
||||
public function move(string $destination, string $filename): bool
|
||||
public function getErrorTranslationString(): string
|
||||
{
|
||||
// if (!$this->isAllowedMimeType($mimeType)) {
|
||||
// throw new TranslatedException(sprintf('MIME type %s is not allowed', $mimeType), 'panel.uploader.error.mimeType');
|
||||
// }
|
||||
return self::ERROR_TRANSLATION_STRINGS[$this->error];
|
||||
}
|
||||
|
||||
public function move(string $destination, string $filename, bool $overwrite = false): bool
|
||||
{
|
||||
if (strlen($filename) > FileSystem::MAX_NAME_LENGTH) {
|
||||
throw new TranslatedException('File name too long', 'panel.uploader.error.fileNameTooLong');
|
||||
throw new TranslatedException('File name too long', 'upload.error.fileNameTooLong');
|
||||
}
|
||||
|
||||
$destinationPath = FileSystem::joinPaths($destination, $filename);
|
||||
|
||||
if (strlen($destinationPath) > FileSystem::MAX_PATH_LENGTH) {
|
||||
throw new TranslatedException('Destination path too long', 'panel.uploader.error.destinationTooLong');
|
||||
throw new TranslatedException('Destination path too long', 'upload.error.destinationTooLong');
|
||||
}
|
||||
|
||||
// if (!$this->options['overwrite'] && FileSystem::exists($destinationPath)) {
|
||||
// throw new TranslatedException(sprintf('File "%s" already exists', $filename), 'panel.uploader.error.alreadyExists');
|
||||
// }
|
||||
if (!$overwrite && FileSystem::exists($destinationPath)) {
|
||||
throw new TranslatedException(sprintf('File "%s" already exists', $filename), 'upload.error.alreadyExists');
|
||||
}
|
||||
|
||||
if (move_uploaded_file($this->tempPath, $destinationPath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new TranslatedException('Cannot move uploaded file to destination', 'panel.uploader.error.cannotMoveToDestination');
|
||||
throw new TranslatedException('Cannot move uploaded file to destination', 'upload.error.cannotMoveToDestination');
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ class FilesData extends RequestData
|
||||
if (parent::isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return !Arr::some($this->getAll(), fn ($file) => $file->isUploaded());
|
||||
return Arr::every($this->getAll(), fn (UploadedFile $uploadedFile) => $uploadedFile->isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -164,11 +164,13 @@ class PagesController extends AbstractController
|
||||
// Validate fields against data
|
||||
$fields->setValues($data, null)->validate();
|
||||
|
||||
$error = false;
|
||||
|
||||
// Update the page
|
||||
try {
|
||||
$page = $this->updatePage($page, $data, $fields);
|
||||
$this->panel()->notify($this->translate('panel.pages.page.edited'), 'success');
|
||||
} catch (TranslatedException $e) {
|
||||
$error = true;
|
||||
$this->panel()->notify($e->getTranslatedMessage(), 'error');
|
||||
}
|
||||
|
||||
@ -176,12 +178,16 @@ class PagesController extends AbstractController
|
||||
try {
|
||||
$this->processPageUploads($this->request->files()->get('uploadedFile', []), $page);
|
||||
$page->reload();
|
||||
|
||||
} catch (TranslatedException $e) {
|
||||
$this->panel()->notify($this->translate('panel.uploader.error', $e->getTranslatedMessage()), 'error');
|
||||
$error = true;
|
||||
$this->panel()->notify($this->translate('upload.error', $e->getTranslatedMessage()), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
$this->panel()->notify($this->translate('panel.pages.page.edited'), 'success');
|
||||
}
|
||||
|
||||
if ($page->route() === null) {
|
||||
throw new UnexpectedValueException('Unexpected missing page route');
|
||||
}
|
||||
@ -326,7 +332,7 @@ class PagesController extends AbstractController
|
||||
try {
|
||||
$this->processPageUploads($this->request->files()->getAll(), $page);
|
||||
} catch (TranslatedException $e) {
|
||||
$this->panel()->notify($this->translate('panel.uploader.error', $e->getTranslatedMessage()), 'error');
|
||||
$this->panel()->notify($this->translate('upload.error', $e->getTranslatedMessage()), 'error');
|
||||
return $this->redirect($this->generateRoute('panel.pages.edit', ['page' => $routeParams->get('page')]));
|
||||
|
||||
}
|
||||
@ -625,7 +631,7 @@ class PagesController extends AbstractController
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (!$file->isUploaded()) {
|
||||
throw new RuntimeException(sprintf('Cannot upload file "%s"', $file->fieldName()));
|
||||
throw new TranslatedException(sprintf('Cannot upload file "%s"', $file->fieldName()), $file->getErrorTranslationString());
|
||||
}
|
||||
if ($page->path() === null) {
|
||||
throw new UnexpectedValueException('Unexpected missing page path');
|
||||
|
@ -18,6 +18,20 @@ fields.array.remove: Remove
|
||||
fields.file.uploadLabel: <strong>Click</strong> to choose a file to upload or <strong>drag</strong> it here
|
||||
fields.select.empty: No matching options
|
||||
page.none: (None)
|
||||
upload.error: Cannot upload file. %s.
|
||||
upload.error.alreadyExists: A file with the same name already exists
|
||||
upload.error.cannotMoveToDestination: Failed to move uploaded file to destination
|
||||
upload.error.cannotWrite: Failed to write file to disk
|
||||
upload.error.destinationTooLong: Destination path too long
|
||||
upload.error.fileName: Invalid file name
|
||||
upload.error.fileNameTooLong: File name too long
|
||||
upload.error.hiddenFiles: Cannot upload hidden files beginning with a dot
|
||||
upload.error.mimeType: File type not allowed
|
||||
upload.error.noFile: No file was uploaded
|
||||
upload.error.noTemp: Missing a temporary folder
|
||||
upload.error.partial: The uploaded file was only partially uploaded
|
||||
upload.error.phpExtension: File upload stopped by extension
|
||||
upload.error.size: The uploaded file exceeds the maximum file size
|
||||
zip.error.alreadyExists: The archive already exists
|
||||
zip.error.cannotOpen: Cannot open the archive
|
||||
zip.error.cannotRead: Cannot read the archive
|
||||
|
@ -18,6 +18,20 @@ fields.array.remove: Supprimer
|
||||
fields.file.uploadLabel: <strong>Glisser-déposer</strong> dans la zone ou <strong>cliquer-ici</strong> pour sélectionner un fichier à envoyer.
|
||||
fields.select.empty: Aucune option correspondante
|
||||
page.none: (Aucun)
|
||||
upload.error: Impossible de télécharger le fichier. %s.
|
||||
upload.error.alreadyExists: Un fichier du même nom existe déjà
|
||||
upload.error.cannotMoveToDestination: Impossible de déplacer le fichier téléchargé vers la destination
|
||||
upload.error.cannotWrite: Échec de l’écriture du fichier sur le disque
|
||||
upload.error.destinationTooLong: Chemin de destination trop long
|
||||
upload.error.fileName: Nom de fichier non valide
|
||||
upload.error.fileNameTooLong: Nom de fichier trop long
|
||||
upload.error.hiddenFiles: Impossible de téléverser des fichiers cachés commençant par un point
|
||||
upload.error.mimeType: Type de fichier non autorisé
|
||||
upload.error.noFile: Aucun fichier n’a été téléchargé
|
||||
upload.error.noTemp: Absence du dossier temporaire
|
||||
upload.error.partial: Le fichier envoyé n’a été que partiellement téléversé
|
||||
upload.error.phpExtension: Téléversement du fichier arrêté par extension
|
||||
upload.error.size: Le fichier téléversé dépasse la taille de fichier maximale
|
||||
zip.error.alreadyExists: L’archive existe déjà
|
||||
zip.error.cannotOpen: Impossible d’ouvrir l’archive
|
||||
zip.error.cannotRead: Impossible de lire l’archive
|
||||
|
@ -18,6 +18,20 @@ fields.array.remove: Rimuovi
|
||||
fields.file.uploadLabel: <strong>Fare click</strong> per selezionare un file da caricare o <strong>trascinarlo</strong> qui
|
||||
fields.select.empty: Nessuna opzione corrispondente
|
||||
page.none: (Nessuna)
|
||||
upload.error: Impossibile caricare il file. %s.
|
||||
upload.error.alreadyExists: Un file con lo stesso nome esiste già
|
||||
upload.error.cannotMoveToDestination: Impossibile spostare il file caricato alla destinazione
|
||||
upload.error.cannotWrite: Impossibile salvare il file sul disco
|
||||
upload.error.destinationTooLong: Il percorso di destinazione è troppo lungo
|
||||
upload.error.fileName: Nome del file non valido
|
||||
upload.error.fileNameTooLong: Il nome del file è troppo lungo
|
||||
upload.error.hiddenFiles: Impossibile caricare i file che iniziano con ’.’
|
||||
upload.error.mimeType: Il tipo di file non è consentito
|
||||
upload.error.noFile: Nessun file caricato
|
||||
upload.error.noTemp: Cartella temporanea mancante
|
||||
upload.error.partial: Il file è stato caricato solo parzialmente
|
||||
upload.error.phpExtension: Il caricamento è stato interrotto da un’estensione
|
||||
upload.error.size: Il file caricato supera la dimensione massima consentita
|
||||
zip.error.alreadyExists: L’archivio esiste già
|
||||
zip.error.cannotOpen: Impossibile aprire l’archivio
|
||||
zip.error.cannotRead: Impossibile leggere l’archivio
|
||||
|
@ -18,6 +18,20 @@ fields.array.remove: Remover
|
||||
fields.file.uploadLabel: <strong>Clique</strong> aqui para escolher um ficheiro para enviar ou <strong>arrastar</strong> para aqui.
|
||||
fields.select.empty: Sem opções correspondentes
|
||||
page.none: (Nenhum)
|
||||
upload.error: Não é possível fazer upload do ficheiro. %s.
|
||||
upload.error.alreadyExists: Já existe um ficheiro com o mesmo nome
|
||||
upload.error.cannotMoveToDestination: Falha ao mover o ficheiro enviado para o destino
|
||||
upload.error.cannotWrite: Falha ao gravar ficheiro no disco
|
||||
upload.error.destinationTooLong: Caminho de destino demasiado longo
|
||||
upload.error.fileName: Nome do ficheiro inválido
|
||||
upload.error.fileNameTooLong: Nome do ficheiro demasiado longo
|
||||
upload.error.hiddenFiles: Não é possível efectuar envio de ficheros ocultos que começam com um ponto
|
||||
upload.error.mimeType: Tipo de ficheiro inválido
|
||||
upload.error.noFile: Nenhum ficheiro foi enviado
|
||||
upload.error.noTemp: Pasta temporária inexistente
|
||||
upload.error.partial: O ficheiro foi enviado apenas parcialmente
|
||||
upload.error.phpExtension: Envio de ficheiro interrompido por extensão
|
||||
upload.error.size: O ficheiro enviado excede o tamanho máximo permitido.
|
||||
zip.error.alreadyExists: O ficheiro já existe
|
||||
zip.error.cannotOpen: Erro de abertura do ficheiro
|
||||
zip.error.cannotRead: Erro de leitura do ficheiro
|
||||
|
@ -18,6 +18,20 @@ fields.array.remove: Удалить
|
||||
fields.file.uploadLabel: <strong>Нажмите</strong> чтобы выбрать файл для загрузки или <strong>тянуть</strong> это здесь
|
||||
fields.select.empty: Нет подходящих вариантов
|
||||
page.none: (Нет)
|
||||
upload.error: Невозможно загрузить файл. %s.
|
||||
upload.error.alreadyExists: Файл с таким именем уже существует
|
||||
upload.error.cannotMoveToDestination: Не удалось переместить загруженный файл в папку назначения
|
||||
upload.error.cannotWrite: Не удалось записать файл на диск
|
||||
upload.error.destinationTooLong: Слишком длинный путь назначения
|
||||
upload.error.fileName: Неверное имя файла
|
||||
upload.error.fileNameTooLong: Слишком длинное имя файла
|
||||
upload.error.hiddenFiles: Невозможно загрузить скрытые файлы, начинающиеся с точки
|
||||
upload.error.mimeType: Тип файла не допускается
|
||||
upload.error.noFile: Файл не был загружен
|
||||
upload.error.noTemp: Отсутствующие временную папку
|
||||
upload.error.partial: Загруженный файл был загружен только частично
|
||||
upload.error.phpExtension: Загрузка файла останавливали расширения
|
||||
upload.error.size: Загруженный файл превышает максимальный размер файла
|
||||
zip.error.alreadyExists: Архив уже существует
|
||||
zip.error.cannotOpen: Не удается открыть архив
|
||||
zip.error.cannotRead: Не удается прочитать архив
|
||||
|
@ -247,20 +247,6 @@ panel.updates.status.checking: Checking for updates...
|
||||
panel.updates.status.found: Updates found
|
||||
panel.updates.status.installing: Installing updates...
|
||||
panel.updates.status.upToDate: UpToDate!
|
||||
panel.uploader.error: Cannot upload file. %s.
|
||||
panel.uploader.error.alreadyExists: A file with the same name already exists
|
||||
panel.uploader.error.cannotMoveToDestination: Failed to move uploaded file to destination
|
||||
panel.uploader.error.cannotWrite: Failed to write file to disk
|
||||
panel.uploader.error.destinationTooLong: Destination path too long
|
||||
panel.uploader.error.fileName: Invalid file name
|
||||
panel.uploader.error.fileNameTooLong: File name too long
|
||||
panel.uploader.error.hiddenFiles: Cannot upload hidden files beginning with a dot
|
||||
panel.uploader.error.mimeType: File type not allowed
|
||||
panel.uploader.error.noFile: No file was uploaded
|
||||
panel.uploader.error.noTemp: Missing a temporary folder
|
||||
panel.uploader.error.partial: The uploaded file was only partially uploaded
|
||||
panel.uploader.error.phpExtension: File upload stopped by extension
|
||||
panel.uploader.error.size: The uploaded file exceeds the maximum file size
|
||||
panel.uploader.uploaded: File uploaded
|
||||
panel.user.actions: Actions
|
||||
panel.user.colorScheme: Color Scheme
|
||||
|
@ -247,20 +247,6 @@ panel.updates.status.checking: Vérification des mises à jour…
|
||||
panel.updates.status.found: Mises à jour trouvées
|
||||
panel.updates.status.installing: Installation de mises à jour…
|
||||
panel.updates.status.upToDate: À jour !
|
||||
panel.uploader.error: Impossible de télécharger le fichier. %s.
|
||||
panel.uploader.error.alreadyExists: Un fichier du même nom existe déjà
|
||||
panel.uploader.error.cannotMoveToDestination: Impossible de déplacer le fichier téléchargé vers la destination
|
||||
panel.uploader.error.cannotWrite: Échec de l’écriture du fichier sur le disque
|
||||
panel.uploader.error.destinationTooLong: Chemin de destination trop long
|
||||
panel.uploader.error.fileName: Nom de fichier non valide
|
||||
panel.uploader.error.fileNameTooLong: Nom de fichier trop long
|
||||
panel.uploader.error.hiddenFiles: Impossible de téléverser des fichiers cachés commençant par un point
|
||||
panel.uploader.error.mimeType: Type de fichier non autorisé
|
||||
panel.uploader.error.noFile: Aucun fichier n’a été téléchargé
|
||||
panel.uploader.error.noTemp: Absence du dossier temporaire
|
||||
panel.uploader.error.partial: Le fichier envoyé n’a été que partiellement téléversé
|
||||
panel.uploader.error.phpExtension: Téléversement du fichier arrêté par extension
|
||||
panel.uploader.error.size: Le fichier téléversé dépasse la taille de fichier maximale
|
||||
panel.uploader.uploaded: Fichier envoyé !
|
||||
panel.user.actions: Actions
|
||||
panel.user.colorScheme: Schéma de couleur
|
||||
|
@ -247,20 +247,6 @@ panel.updates.status.checking: Cerco aggiornamenti...
|
||||
panel.updates.status.found: Aggiornamento disponibile
|
||||
panel.updates.status.installing: Installo aggiornamento...
|
||||
panel.updates.status.upToDate: Aggiornato!
|
||||
panel.uploader.error: Impossibile caricare il file. %s.
|
||||
panel.uploader.error.alreadyExists: Un file con lo stesso nome esiste già
|
||||
panel.uploader.error.cannotMoveToDestination: Impossibile spostare il file caricato alla destinazione
|
||||
panel.uploader.error.cannotWrite: Impossibile salvare il file sul disco
|
||||
panel.uploader.error.destinationTooLong: Il percorso di destinazione è troppo lungo
|
||||
panel.uploader.error.fileName: Nome del file non valido
|
||||
panel.uploader.error.fileNameTooLong: Il nome del file è troppo lungo
|
||||
panel.uploader.error.hiddenFiles: Impossibile caricare i file che iniziano con ’.’
|
||||
panel.uploader.error.mimeType: Il tipo di file non è consentito
|
||||
panel.uploader.error.noFile: Nessun file caricato
|
||||
panel.uploader.error.noTemp: Cartella temporanea mancante
|
||||
panel.uploader.error.partial: Il file è stato caricato solo parzialmente
|
||||
panel.uploader.error.phpExtension: Il caricamento è stato interrotto da un’estensione
|
||||
panel.uploader.error.size: Il file caricato supera la dimensione massima consentita
|
||||
panel.uploader.uploaded: File caricato
|
||||
panel.user.actions: Azioni
|
||||
panel.user.colorScheme: Combinazione di colori
|
||||
|
@ -247,20 +247,6 @@ panel.updates.status.checking: Verificando actualizações...
|
||||
panel.updates.status.found: Actualizações encontradas
|
||||
panel.updates.status.installing: Instalando actualizações...
|
||||
panel.updates.status.upToDate: Actualizado!
|
||||
panel.uploader.error: Não é possível fazer upload do ficheiro. %s.
|
||||
panel.uploader.error.alreadyExists: Já existe um ficheiro com o mesmo nome
|
||||
panel.uploader.error.cannotMoveToDestination: Falha ao mover o ficheiro enviado para o destino
|
||||
panel.uploader.error.cannotWrite: Falha ao gravar ficheiro no disco
|
||||
panel.uploader.error.destinationTooLong: Caminho de destino demasiado longo
|
||||
panel.uploader.error.fileName: Nome do ficheiro inválido
|
||||
panel.uploader.error.fileNameTooLong: Nome do ficheiro demasiado longo
|
||||
panel.uploader.error.hiddenFiles: Não é possível efectuar envio de ficheros ocultos que começam com um ponto
|
||||
panel.uploader.error.mimeType: Tipo de ficheiro inválido
|
||||
panel.uploader.error.noFile: Nenhum ficheiro foi enviado
|
||||
panel.uploader.error.noTemp: Pasta temporária inexistente
|
||||
panel.uploader.error.partial: O ficheiro foi enviado apenas parcialmente
|
||||
panel.uploader.error.phpExtension: Envio de ficheiro interrompido por extensão
|
||||
panel.uploader.error.size: O ficheiro enviado excede o tamanho máximo permitido.
|
||||
panel.uploader.uploaded: Ficheiro enviado
|
||||
panel.user.actions: Acções
|
||||
panel.user.colorScheme: Esquema de cores
|
||||
|
@ -247,20 +247,6 @@ panel.updates.status.checking: Проверка обновлений...
|
||||
panel.updates.status.found: Обновления найдены
|
||||
panel.updates.status.installing: Установка обновлений ...
|
||||
panel.updates.status.upToDate: До настоящего времени!
|
||||
panel.uploader.error: Невозможно загрузить файл. %s.
|
||||
panel.uploader.error.alreadyExists: Файл с таким именем уже существует
|
||||
panel.uploader.error.cannotMoveToDestination: Не удалось переместить загруженный файл в папку назначения
|
||||
panel.uploader.error.cannotWrite: Не удалось записать файл на диск
|
||||
panel.uploader.error.destinationTooLong: Слишком длинный путь назначения
|
||||
panel.uploader.error.fileName: Неверное имя файла
|
||||
panel.uploader.error.fileNameTooLong: Слишком длинное имя файла
|
||||
panel.uploader.error.hiddenFiles: Невозможно загрузить скрытые файлы, начинающиеся с точки
|
||||
panel.uploader.error.mimeType: Тип файла не допускается
|
||||
panel.uploader.error.noFile: Файл не был загружен
|
||||
panel.uploader.error.noTemp: Отсутствующие временную папку
|
||||
panel.uploader.error.partial: Загруженный файл был загружен только частично
|
||||
panel.uploader.error.phpExtension: Загрузка файла останавливали расширения
|
||||
panel.uploader.error.size: Загруженный файл превышает максимальный размер файла
|
||||
panel.uploader.uploaded: Файл загружен
|
||||
panel.user.actions: Действия
|
||||
panel.user.colorScheme: Цветовая схема
|
||||
|
Loading…
x
Reference in New Issue
Block a user