Compare commits

..

1 Commits

Author SHA1 Message Date
stirlingbot[bot]
1065c95102
📝 Sync README.md 2025-05-21 14:42:53 +00:00
7 changed files with 121 additions and 312 deletions

View File

@ -143,7 +143,7 @@ Stirling-PDF currently supports 40 languages!
| Portuguese (Português) (pt_PT) | ![91%](https://geps.dev/progress/91) | | Portuguese (Português) (pt_PT) | ![91%](https://geps.dev/progress/91) |
| Portuguese Brazilian (Português) (pt_BR) | ![97%](https://geps.dev/progress/97) | | Portuguese Brazilian (Português) (pt_BR) | ![97%](https://geps.dev/progress/97) |
| Romanian (Română) (ro_RO) | ![75%](https://geps.dev/progress/75) | | Romanian (Română) (ro_RO) | ![75%](https://geps.dev/progress/75) |
| Russian (Русский) (ru_RU) | ![99%](https://geps.dev/progress/99) | | Russian (Русский) (ru_RU) | ![93%](https://geps.dev/progress/93) |
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![60%](https://geps.dev/progress/60) | | Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![60%](https://geps.dev/progress/60) |
| Simplified Chinese (简体中文) (zh_CN) | ![93%](https://geps.dev/progress/93) | | Simplified Chinese (简体中文) (zh_CN) | ![93%](https://geps.dev/progress/93) |
| Slovakian (Slovensky) (sk_SK) | ![69%](https://geps.dev/progress/69) | | Slovakian (Slovensky) (sk_SK) | ![69%](https://geps.dev/progress/69) |

View File

@ -37,21 +37,8 @@ public class EmailService {
*/ */
@Async @Async
public void sendEmailWithAttachment(Email email) throws MessagingException { public void sendEmailWithAttachment(Email email) throws MessagingException {
MultipartFile file = email.getFileInput();
// 1) Validate recipient email address
if (email.getTo() == null || email.getTo().trim().isEmpty()) {
throw new MessagingException("Invalid Addresses");
}
// 2) Validate attachment
if (file == null
|| file.isEmpty()
|| file.getOriginalFilename() == null
|| file.getOriginalFilename().isEmpty()) {
throw new MessagingException("An attachment is required to send the email.");
}
ApplicationProperties.Mail mailProperties = applicationProperties.getMail(); ApplicationProperties.Mail mailProperties = applicationProperties.getMail();
MultipartFile file = email.getFileInput();
// Creates a MimeMessage to represent the email // Creates a MimeMessage to represent the email
MimeMessage message = mailSender.createMimeMessage(); MimeMessage message = mailSender.createMimeMessage();

View File

@ -3,7 +3,6 @@ package stirling.software.SPDF.controller.api;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.mail.MailSendException;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@ -54,11 +53,6 @@ public class EmailController {
// Calls the service to send the email with attachment // Calls the service to send the email with attachment
emailService.sendEmailWithAttachment(email); emailService.sendEmailWithAttachment(email);
return ResponseEntity.ok("Email sent successfully"); return ResponseEntity.ok("Email sent successfully");
} catch (MailSendException ex) {
// handles your "Invalid Addresses" case
String errorMsg = ex.getMessage();
log.error("MailSendException: {}", errorMsg, ex);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMsg);
} catch (MessagingException e) { } catch (MessagingException e) {
// Catches any messaging exception (e.g., invalid email address, SMTP server issues) // Catches any messaging exception (e.g., invalid email address, SMTP server issues)
String errorMsg = "Failed to send email: " + e.getMessage(); String errorMsg = "Failed to send email: " + e.getMessage();

View File

@ -10,9 +10,9 @@ multiPdfPrompt=Выберите PDF-файлы (2+)
multiPdfDropPrompt=Выберите (или перетащите) все необходимые PDF-файлы multiPdfDropPrompt=Выберите (или перетащите) все необходимые PDF-файлы
imgPrompt=Выберите изображение(я) imgPrompt=Выберите изображение(я)
genericSubmit=Отправить genericSubmit=Отправить
uploadLimit=Максимальный размер файла: uploadLimit=Maximum file size:
uploadLimitExceededSingular=слишком велик. Максимально допустимый размер - uploadLimitExceededSingular=is too large. Maximum allowed size is
uploadLimitExceededPlural=слишком велики. Максимально допустимый размер - uploadLimitExceededPlural=are too large. Maximum allowed size is
processTimeWarning=Внимание: Данный процесс может занять до минуты в зависимости от размера файла processTimeWarning=Внимание: Данный процесс может занять до минуты в зависимости от размера файла
pageOrderPrompt=Пользовательский порядок страниц (Введите список номеров страниц через запятую или функции типа 2n+1): pageOrderPrompt=Пользовательский порядок страниц (Введите список номеров страниц через запятую или функции типа 2n+1):
pageSelectionPrompt=Выбор страниц (Введите список номеров страниц через запятую 1,5,6 или функции типа 2n+1): pageSelectionPrompt=Выбор страниц (Введите список номеров страниц через запятую 1,5,6 или функции типа 2n+1):
@ -86,14 +86,14 @@ loading=Загрузка...
addToDoc=Добавить в документ addToDoc=Добавить в документ
reset=Сбросить reset=Сбросить
apply=Применить apply=Применить
noFileSelected=Файл не выбран. Пожалуйста, загрузите его. noFileSelected=No file selected. Please upload one.
legal.privacy=Политика конфиденциальности legal.privacy=Политика конфиденциальности
legal.terms=Условия использования legal.terms=Условия использования
legal.accessibility=Доступность legal.accessibility=Доступность
legal.cookie=Политика использования файлов cookie legal.cookie=Политика использования файлов cookie
legal.impressum=Выходные данные legal.impressum=Выходные данные
legal.showCookieBanner=Настройки файлов cookie legal.showCookieBanner=Cookie Preferences
############### ###############
# Pipeline # # Pipeline #
@ -237,7 +237,7 @@ adminUserSettings.activeUsers=Активные пользователи:
adminUserSettings.disabledUsers=Отключенные пользователи: adminUserSettings.disabledUsers=Отключенные пользователи:
adminUserSettings.totalUsers=Всего пользователей: adminUserSettings.totalUsers=Всего пользователей:
adminUserSettings.lastRequest=Последний запрос adminUserSettings.lastRequest=Последний запрос
adminUserSettings.usage=Просмотр использования adminUserSettings.usage=View Usage
endpointStatistics.title=Статистика конечных точек endpointStatistics.title=Статистика конечных точек
endpointStatistics.header=Статистика конечных точек endpointStatistics.header=Статистика конечных точек
@ -292,18 +292,18 @@ home.desc=Ваше локальное решение для всех потре
home.searchBar=Поиск функций... home.searchBar=Поиск функций...
home.viewPdf.title=Просмотр/Редактирование PDF home.viewPdf.title=View/Edit PDF
home.viewPdf.desc=Просмотр, аннотирование, добавление текста или изображений home.viewPdf.desc=Просмотр, аннотирование, добавление текста или изображений
viewPdf.tags=просмотр,чтение,аннотации,текст,изображение viewPdf.tags=просмотр,чтение,аннотации,текст,изображение
home.setFavorites=Добавить в избранное home.setFavorites=Set Favourites
home.hideFavorites=Скрыть избранное home.hideFavorites=Hide Favourites
home.showFavorites=Показать избранное home.showFavorites=Show Favourites
home.legacyHomepage=Старая главная страница home.legacyHomepage=Old homepage
home.newHomePage=Попробуйте нашу новую главную страницу! home.newHomePage=Try our new homepage!
home.alphabetical=По алфавиту home.alphabetical=Alphabetical
home.globalPopularity=Глобальная популярность home.globalPopularity=Global Popularity
home.sortBy=Сортировать по: home.sortBy=Sort by:
home.multiTool.title=Мультиинструмент PDF home.multiTool.title=Мультиинструмент PDF
home.multiTool.desc=Объединение, поворот, переупорядочивание и удаление страниц home.multiTool.desc=Объединение, поворот, переупорядочивание и удаление страниц
@ -364,9 +364,9 @@ home.compressPdfs.title=Сжать
home.compressPdfs.desc=Сжимайте PDF-файлы для уменьшения их размера. home.compressPdfs.desc=Сжимайте PDF-файлы для уменьшения их размера.
compressPdfs.tags=сжатие,маленький,крошечный compressPdfs.tags=сжатие,маленький,крошечный
home.unlockPDFForms.title=Разблокировать формы PDF home.unlockPDFForms.title=Unlock PDF Forms
home.unlockPDFForms.desc=Удалить свойство только для чтения из полей формы в PDF-документе. home.unlockPDFForms.desc=Remove read-only property of form fields in a PDF document.
unlockPDFForms.tags=удалить,удаление,форма,поле,только для чтения unlockPDFForms.tags=remove,delete,form,field,readonly
home.changeMetadata.title=Изменить метаданные home.changeMetadata.title=Изменить метаданные
home.changeMetadata.desc=Изменить/удалить/добавить метаданные из PDF-документа home.changeMetadata.desc=Изменить/удалить/добавить метаданные из PDF-документа
@ -494,9 +494,9 @@ home.MarkdownToPDF.title=Markdown в PDF
home.MarkdownToPDF.desc=Преобразует любой файл Markdown в PDF home.MarkdownToPDF.desc=Преобразует любой файл Markdown в PDF
MarkdownToPDF.tags=разметка,веб-контент,преобразование,конвертация MarkdownToPDF.tags=разметка,веб-контент,преобразование,конвертация
home.PDFToMarkdown.title=PDF в Markdown home.PDFToMarkdown.title=PDF to Markdown
home.PDFToMarkdown.desc=Конвертирует любой PDF в Markdown home.PDFToMarkdown.desc=Converts any PDF to Markdown
PDFToMarkdown.tags=разметка,веб-контент,преобразование,конвертировать,md PDFToMarkdown.tags=markup,web-content,transformation,convert,md
home.getPdfInfo.title=Получить ВСЮ информацию о PDF home.getPdfInfo.title=Получить ВСЮ информацию о PDF
home.getPdfInfo.desc=Собирает всю возможную информацию о PDF home.getPdfInfo.desc=Собирает всю возможную информацию о PDF
@ -609,7 +609,7 @@ login.userIsDisabled=Пользователь деактивирован, вхо
login.alreadyLoggedIn=Вы уже вошли в login.alreadyLoggedIn=Вы уже вошли в
login.alreadyLoggedIn2=устройств(а). Пожалуйста, выйдите из этих устройств и попробуйте снова. login.alreadyLoggedIn2=устройств(а). Пожалуйста, выйдите из этих устройств и попробуйте снова.
login.toManySessions=У вас слишком много активных сессий login.toManySessions=У вас слишком много активных сессий
login.logoutMessage=Вы вышли из системы. login.logoutMessage=You have been logged out.
#auto-redact #auto-redact
autoRedact.title=Автоматическое редактирование autoRedact.title=Автоматическое редактирование
@ -648,7 +648,7 @@ redact.showAttatchments=Показать вложения
redact.showLayers=Показать слои (двойной щелчок для сброса всех слоев к состоянию по умолчанию) redact.showLayers=Показать слои (двойной щелчок для сброса всех слоев к состоянию по умолчанию)
redact.colourPicker=Выбор цвета redact.colourPicker=Выбор цвета
redact.findCurrentOutlineItem=Найти текущий элемент структуры redact.findCurrentOutlineItem=Найти текущий элемент структуры
redact.applyChanges=Применить изменения redact.applyChanges=Apply Changes
#showJS #showJS
showJS.title=Показать Javascript showJS.title=Показать Javascript
@ -686,9 +686,9 @@ MarkdownToPDF.credit=Использует WeasyPrint
#pdf-to-markdown #pdf-to-markdown
PDFToMarkdown.title=PDF в Markdown PDFToMarkdown.title=PDF To Markdown
PDFToMarkdown.header=PDF в Markdown PDFToMarkdown.header=PDF To Markdown
PDFToMarkdown.submit=Конвертировать PDFToMarkdown.submit=Convert
#url-to-pdf #url-to-pdf
@ -742,10 +742,10 @@ sanitizePDF.title=Очистить PDF
sanitizePDF.header=Очистить PDF-файл sanitizePDF.header=Очистить PDF-файл
sanitizePDF.selectText.1=Удалить JavaScript-действия sanitizePDF.selectText.1=Удалить JavaScript-действия
sanitizePDF.selectText.2=Удалить встроенные файлы sanitizePDF.selectText.2=Удалить встроенные файлы
sanitizePDF.selectText.3=Удалить метаданные XMP sanitizePDF.selectText.3=Remove XMP metadata
sanitizePDF.selectText.4=Удалить ссылки sanitizePDF.selectText.4=Удалить ссылки
sanitizePDF.selectText.5=Удалить шрифты sanitizePDF.selectText.5=Удалить шрифты
sanitizePDF.selectText.6=Удалить метаданные информации о документе sanitizePDF.selectText.6=Remove Document Info Metadata
sanitizePDF.submit=Очистить PDF sanitizePDF.submit=Очистить PDF
@ -894,8 +894,8 @@ sign.last=Последняя страница
sign.next=Следующая страница sign.next=Следующая страница
sign.previous=Предыдущая страница sign.previous=Предыдущая страница
sign.maintainRatio=Переключить сохранение пропорций sign.maintainRatio=Переключить сохранение пропорций
sign.undo=Отменить sign.undo=Undo
sign.redo=Повторить sign.redo=Redo
#repair #repair
repair.title=Восстановление repair.title=Восстановление
@ -966,8 +966,8 @@ compress.title=Сжать
compress.header=Сжать PDF compress.header=Сжать PDF
compress.credit=Этот сервис использует qpdf для сжатия/оптимизации PDF. compress.credit=Этот сервис использует qpdf для сжатия/оптимизации PDF.
compress.grayscale.label=Применить шкалу серого для сжатия compress.grayscale.label=Применить шкалу серого для сжатия
compress.selectText.1=Настройки сжатия compress.selectText.1=Compression Settings
compress.selectText.1.1=1-3 Сжатие PDF,</br> 4-6 легкое сжатие изображений,</br> 7-9 интенсивное сжатие изображений Существенно снижает качество изображений compress.selectText.1.1=1-3 PDF compression,</br> 4-6 lite image compression,</br> 7-9 intense image compression Will dramatically reduce image quality
compress.selectText.2=Уровень оптимизации: compress.selectText.2=Уровень оптимизации:
compress.selectText.4=Автоматический режим - автоматически настраивает качество для получения точного размера PDF compress.selectText.4=Автоматический режим - автоматически настраивает качество для получения точного размера PDF
compress.selectText.5=Ожидаемый размер PDF (например, 25MB, 10.8MB, 25KB) compress.selectText.5=Ожидаемый размер PDF (например, 25MB, 10.8MB, 25KB)
@ -1006,7 +1006,7 @@ pdfOrganiser.mode.7=Удалить первую
pdfOrganiser.mode.8=Удалить последнюю pdfOrganiser.mode.8=Удалить последнюю
pdfOrganiser.mode.9=Удалить первую и последнюю pdfOrganiser.mode.9=Удалить первую и последнюю
pdfOrganiser.mode.10=Объединение четных-нечетных pdfOrganiser.mode.10=Объединение четных-нечетных
pdfOrganiser.mode.11=Дублировать все страницы pdfOrganiser.mode.11=Duplicate all pages
pdfOrganiser.placeholder=(например, 1,3,2 или 4-8,2,10-12 или 2n-1) pdfOrganiser.placeholder=(например, 1,3,2 или 4-8,2,10-12 или 2n-1)
@ -1049,7 +1049,7 @@ decrypt.success=Файл успешно расшифрован.
multiTool-advert.message=Эта функция также доступна на нашей <a href="{0}">странице мультиинструмента</a>. Попробуйте её для улучшенного постраничного интерфейса и дополнительных возможностей! multiTool-advert.message=Эта функция также доступна на нашей <a href="{0}">странице мультиинструмента</a>. Попробуйте её для улучшенного постраничного интерфейса и дополнительных возможностей!
#view pdf #view pdf
viewPdf.title=Просмотр/Редактирование PDF viewPdf.title=View/Edit PDF
viewPdf.header=Просмотр PDF viewPdf.header=Просмотр PDF
#pageRemover #pageRemover
@ -1191,15 +1191,15 @@ changeMetadata.keywords=Ключевые слова:
changeMetadata.modDate=Дата изменения (yyyy/MM/dd HH:mm:ss): changeMetadata.modDate=Дата изменения (yyyy/MM/dd HH:mm:ss):
changeMetadata.producer=Производитель: changeMetadata.producer=Производитель:
changeMetadata.subject=Тема: changeMetadata.subject=Тема:
changeMetadata.trapped=Захвачено: changeMetadata.trapped=Trapped:
changeMetadata.selectText.4=Другие метаданные: changeMetadata.selectText.4=Другие метаданные:
changeMetadata.selectText.5=Добавить пользовательскую запись метаданных changeMetadata.selectText.5=Добавить пользовательскую запись метаданных
changeMetadata.submit=Изменить changeMetadata.submit=Изменить
#unlockPDFForms #unlockPDFForms
unlockPDFForms.title=Удалить только для чтения из полей формы unlockPDFForms.title=Remove Read-Only from Form Fields
unlockPDFForms.header=Разблокировать формы PDF unlockPDFForms.header=Unlock PDF Forms
unlockPDFForms.submit=Удалить unlockPDFForms.submit=Remove
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF в PDF/A pdfToPDFA.title=PDF в PDF/A
@ -1319,15 +1319,15 @@ survey.please=Пожалуйста, примите участие в нашем
survey.disabled=(Всплывающее окно опроса будет отключено в следующих обновлениях, но будет доступно в нижней части страницы) survey.disabled=(Всплывающее окно опроса будет отключено в следующих обновлениях, но будет доступно в нижней части страницы)
survey.button=Пройти опрос survey.button=Пройти опрос
survey.dontShowAgain=Больше не показывать survey.dontShowAgain=Больше не показывать
survey.meeting.1=Если вы используете Stirling PDF на работе, мы будем рады поговорить с вами. Мы предлагаем сеансы технической поддержки в обмен на 15-минутную сессию по изучению пользователей. survey.meeting.1=If you're using Stirling PDF at work, we'd love to speak to you. We're offering technical support sessions in exchange for a 15 minute user discovery session.
survey.meeting.2=Это возможность: survey.meeting.2=This is a chance to:
survey.meeting.3=Получить помощь с развертыванием, интеграцией или устранением неполадок survey.meeting.3=Get help with deployment, integrations, or troubleshooting
survey.meeting.4=Предоставить прямую обратную связь о производительности, крайних случаях и пробелах в функциях survey.meeting.4=Provide direct feedback on performance, edge cases, and feature gaps
survey.meeting.5=Помочь нам улучшить Stirling PDF для реального использования в корпоративной среде survey.meeting.5=Help us refine Stirling PDF for real-world enterprise use
survey.meeting.6=Если вы заинтересованы, вы можете записаться на встречу с нашей командой напрямую. (Только на английском языке) survey.meeting.6=If you're interested, you can book time with our team directly. (English speaking only)
survey.meeting.7=С нетерпением ждем возможности изучить ваши случаи использования и сделать Stirling PDF еще лучше! survey.meeting.7=Looking forward to digging into your use cases and making Stirling PDF even better!
survey.meeting.notInterested=Не являетесь бизнесом и/или не заинтересованы во встрече? survey.meeting.notInterested=Not a business and/or interested in a meeting?
survey.meeting.button=Записаться на встречу survey.meeting.button=Book meeting
#error #error
error.sorry=Извините за неполадки! error.sorry=Извините за неполадки!
@ -1415,25 +1415,25 @@ validateSignature.cert.bits=бит
#################### ####################
# Cookie banner # # Cookie banner #
#################### ####################
cookieBanner.popUp.title=Как мы используем файлы cookie cookieBanner.popUp.title=How we use Cookies
cookieBanner.popUp.description.1=Мы используем файлы cookie и другие технологии, чтобы Stirling PDF работал лучше для вас — помогая нам улучшать наши инструменты и добавлять функции, которые вам понравятся. cookieBanner.popUp.description.1=We use cookies and other technologies to make Stirling PDF work better for you—helping us improve our tools and keep building features you'll love.
cookieBanner.popUp.description.2=Если вы не хотите, нажав «Нет, спасибо», вы включите только основные файлы cookie, необходимые для бесперебойной работы. cookieBanner.popUp.description.2=If youd rather not, clicking 'No Thanks' will only enable the essential cookies needed to keep things running smoothly.
cookieBanner.popUp.acceptAllBtn=Хорошо cookieBanner.popUp.acceptAllBtn=Okay
cookieBanner.popUp.acceptNecessaryBtn=Нет, спасибо cookieBanner.popUp.acceptNecessaryBtn=No Thanks
cookieBanner.popUp.showPreferencesBtn=Управление предпочтениями cookieBanner.popUp.showPreferencesBtn=Manage preferences
cookieBanner.preferencesModal.title=Центр управления предпочтениями cookieBanner.preferencesModal.title=Consent Preferences Center
cookieBanner.preferencesModal.acceptAllBtn=Принять все cookieBanner.preferencesModal.acceptAllBtn=Accept all
cookieBanner.preferencesModal.acceptNecessaryBtn=Отклонить все cookieBanner.preferencesModal.acceptNecessaryBtn=Reject all
cookieBanner.preferencesModal.savePreferencesBtn=Сохранить предпочтения cookieBanner.preferencesModal.savePreferencesBtn=Save preferences
cookieBanner.preferencesModal.closeIconLabel=Закрыть окно cookieBanner.preferencesModal.closeIconLabel=Close modal
cookieBanner.preferencesModal.serviceCounterLabel=Сервис|Сервисы cookieBanner.preferencesModal.serviceCounterLabel=Service|Services
cookieBanner.preferencesModal.subtitle=Использование файлов cookie cookieBanner.preferencesModal.subtitle=Cookie Usage
cookieBanner.preferencesModal.description.1=Stirling PDF использует файлы cookie и аналогичные технологии, чтобы улучшить ваш опыт и понять, как используются наши инструменты. Это помогает нам улучшать производительность, разрабатывать функции, которые важны для нашего сообщества, и предоставлять постоянную поддержку нашим пользователям. cookieBanner.preferencesModal.description.1=Stirling PDF uses cookies and similar technologies to enhance your experience and understand how our tools are used. This helps us improve performance, develop the features you care about, and provide ongoing support to our users.
cookieBanner.preferencesModal.description.2=Stirling PDF не может — и никогда не будет — отслеживать или получать доступ к содержимому документов, которые вы используете. cookieBanner.preferencesModal.description.2=Stirling PDF cannot—and will never—track or access the content of the documents you use.
cookieBanner.preferencesModal.description.3=Ваша конфиденциальность и доверие — в основе того, что мы делаем. cookieBanner.preferencesModal.description.3=Your privacy and trust are at the core of what we do.
cookieBanner.preferencesModal.necessary.title.1=Строго необходимые файлы cookie cookieBanner.preferencesModal.necessary.title.1=Strictly Necessary Cookies
cookieBanner.preferencesModal.necessary.title.2=Всегда включены cookieBanner.preferencesModal.necessary.title.2=Always Enabled
cookieBanner.preferencesModal.necessary.description=Эти файлы cookie необходимы для правильной работы веб-сайта. Они включают основные функции, такие как установка ваших предпочтений конфиденциальности, вход в систему и заполнение форм — поэтому их нельзя отключить. cookieBanner.preferencesModal.necessary.description=These cookies are essential for the website to function properly. They enable core features like setting your privacy preferences, logging in, and filling out forms—which is why they cant be turned off.
cookieBanner.preferencesModal.analytics.title=Аналитика cookieBanner.preferencesModal.analytics.title=Analytics
cookieBanner.preferencesModal.analytics.description=Эти файлы cookie помогают нам понять, как используются наши инструменты, чтобы мы могли сосредоточиться на создании функций, которые ценит наше сообщество. Будьте уверены — Stirling PDF не может и никогда не будет отслеживать содержимое документов, с которыми вы работаете. cookieBanner.preferencesModal.analytics.description=These cookies help us understand how our tools are being used, so we can focus on building the features our community values most. Rest assured—Stirling PDF cannot and will never track the content of the documents you work with.

View File

@ -1,7 +1,5 @@
package stirling.software.SPDF.config.security.mail; package stirling.software.SPDF.config.security.mail;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -59,111 +57,4 @@ public class EmailServiceTest {
// Verify that the email was sent using mailSender // Verify that the email was sent using mailSender
verify(mailSender).send(mimeMessage); verify(mailSender).send(mimeMessage);
} }
@Test
void testSendEmailWithAttachmentThrowsExceptionForMissingFilename() throws MessagingException {
Email email = new Email();
email.setTo("test@example.com");
email.setSubject("Test Email");
email.setBody("This is a test email.");
email.setFileInput(fileInput);
when(fileInput.isEmpty()).thenReturn(false);
when(fileInput.getOriginalFilename()).thenReturn("");
try {
emailService.sendEmailWithAttachment(email);
fail("Expected MessagingException to be thrown");
} catch (MessagingException e) {
assertEquals("An attachment is required to send the email.", e.getMessage());
}
}
@Test
void testSendEmailWithAttachmentThrowsExceptionForMissingFilenameNull()
throws MessagingException {
Email email = new Email();
email.setTo("test@example.com");
email.setSubject("Test Email");
email.setBody("This is a test email.");
email.setFileInput(fileInput);
when(fileInput.isEmpty()).thenReturn(false);
when(fileInput.getOriginalFilename()).thenReturn(null);
try {
emailService.sendEmailWithAttachment(email);
fail("Expected MessagingException to be thrown");
} catch (MessagingException e) {
assertEquals("An attachment is required to send the email.", e.getMessage());
}
}
@Test
void testSendEmailWithAttachmentThrowsExceptionForMissingFile() throws MessagingException {
Email email = new Email();
email.setTo("test@example.com");
email.setSubject("Test Email");
email.setBody("This is a test email.");
email.setFileInput(fileInput);
when(fileInput.isEmpty()).thenReturn(true);
try {
emailService.sendEmailWithAttachment(email);
fail("Expected MessagingException to be thrown");
} catch (MessagingException e) {
assertEquals("An attachment is required to send the email.", e.getMessage());
}
}
@Test
void testSendEmailWithAttachmentThrowsExceptionForMissingFileNull() throws MessagingException {
Email email = new Email();
email.setTo("test@example.com");
email.setSubject("Test Email");
email.setBody("This is a test email.");
email.setFileInput(null); // Missing file
try {
emailService.sendEmailWithAttachment(email);
fail("Expected MessagingException to be thrown");
} catch (MessagingException e) {
assertEquals("An attachment is required to send the email.", e.getMessage());
}
}
@Test
void testSendEmailWithAttachmentThrowsExceptionForInvalidAddressNull()
throws MessagingException {
Email email = new Email();
email.setTo(null); // Invalid address
email.setSubject("Test Email");
email.setBody("This is a test email.");
email.setFileInput(fileInput);
try {
emailService.sendEmailWithAttachment(email);
fail("Expected MailSendException to be thrown");
} catch (MessagingException e) {
assertEquals("Invalid Addresses", e.getMessage());
}
}
@Test
void testSendEmailWithAttachmentThrowsExceptionForInvalidAddressEmpty()
throws MessagingException {
Email email = new Email();
email.setTo(""); // Invalid address
email.setSubject("Test Email");
email.setBody("This is a test email.");
email.setFileInput(fileInput);
try {
emailService.sendEmailWithAttachment(email);
fail("Expected MailSendException to be thrown");
} catch (MessagingException e) {
assertEquals("Invalid Addresses", e.getMessage());
}
}
} }

View File

@ -1,54 +0,0 @@
package stirling.software.SPDF.config.security.mail;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Properties;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import stirling.software.SPDF.model.ApplicationProperties;
class MailConfigTest {
private ApplicationProperties.Mail mailProps;
@BeforeEach
void initMailProperties() {
mailProps = mock(ApplicationProperties.Mail.class);
when(mailProps.getHost()).thenReturn("smtp.example.com");
when(mailProps.getPort()).thenReturn(587);
when(mailProps.getUsername()).thenReturn("user@example.com");
when(mailProps.getPassword()).thenReturn("password");
}
@Test
void shouldConfigureJavaMailSenderWithCorrectProperties() {
ApplicationProperties appProps = mock(ApplicationProperties.class);
when(appProps.getMail()).thenReturn(mailProps);
MailConfig config = new MailConfig(appProps);
JavaMailSender sender = config.javaMailSender();
assertInstanceOf(JavaMailSenderImpl.class, sender);
JavaMailSenderImpl impl = (JavaMailSenderImpl) sender;
Properties props = impl.getJavaMailProperties();
assertAll(
"SMTP configuration",
() -> assertEquals("smtp.example.com", impl.getHost()),
() -> assertEquals(587, impl.getPort()),
() -> assertEquals("user@example.com", impl.getUsername()),
() -> assertEquals("password", impl.getPassword()),
() -> assertEquals("UTF-8", impl.getDefaultEncoding()),
() -> assertEquals("true", props.getProperty("mail.smtp.auth")),
() -> assertEquals("true", props.getProperty("mail.smtp.starttls.enable")));
}
}

View File

@ -1,25 +1,18 @@
package stirling.software.SPDF.controller.api; package stirling.software.SPDF.controller.api;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*;
import static org.mockito.Mockito.doNothing; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.mockito.Mockito.doThrow; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.mail.MailSendException;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.multipart.MultipartFile;
import jakarta.mail.MessagingException; import jakarta.mail.MessagingException;
@ -27,7 +20,7 @@ import stirling.software.SPDF.config.security.mail.EmailService;
import stirling.software.SPDF.model.api.Email; import stirling.software.SPDF.model.api.Email;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class EmailControllerTest { public class EmailControllerTest {
private MockMvc mockMvc; private MockMvc mockMvc;
@ -35,61 +28,59 @@ class EmailControllerTest {
@InjectMocks private EmailController emailController; @InjectMocks private EmailController emailController;
@Mock private MultipartFile fileInput;
@BeforeEach @BeforeEach
void setUp() { void setUp() {
// Set up the MockMvc instance for testing
mockMvc = MockMvcBuilders.standaloneSetup(emailController).build(); mockMvc = MockMvcBuilders.standaloneSetup(emailController).build();
} }
@ParameterizedTest(name = "Case {index}: exception={0}, includeTo={1}") @Test
@MethodSource("emailParams") void testSendEmailWithAttachmentSuccess() throws Exception {
void shouldHandleEmailRequests( // Create a mock Email object
Exception serviceException, Email email = new Email();
boolean includeTo, email.setTo("test@example.com");
int expectedStatus, email.setSubject("Test Email");
String expectedContent) email.setBody("This is a test email.");
throws Exception { email.setFileInput(fileInput);
if (serviceException == null) {
doNothing().when(emailService).sendEmailWithAttachment(any(Email.class));
} else {
doThrow(serviceException).when(emailService).sendEmailWithAttachment(any(Email.class));
}
var request = // Mock the service to not throw any exception
doNothing().when(emailService).sendEmailWithAttachment(any(Email.class));
// Perform the request and verify the response
mockMvc.perform(
multipart("/api/v1/general/send-email") multipart("/api/v1/general/send-email")
.file("fileInput", "dummy-content".getBytes()) .file("fileInput", "dummy-content".getBytes())
.param("subject", "Test Email") .param("to", email.getTo())
.param("body", "This is a test email."); .param("subject", email.getSubject())
.param("body", email.getBody()))
if (includeTo) { .andExpect(status().isOk())
request = request.param("to", "test@example.com"); .andExpect(content().string("Email sent successfully"));
} }
mockMvc.perform(request) @Test
.andExpect(status().is(expectedStatus)) void testSendEmailWithAttachmentFailure() throws Exception {
.andExpect(content().string(expectedContent)); // Create a mock Email object
} Email email = new Email();
email.setTo("test@example.com");
email.setSubject("Test Email");
email.setBody("This is a test email.");
email.setFileInput(fileInput);
static Stream<Arguments> emailParams() { // Mock the service to throw a MessagingException
return Stream.of( doThrow(new MessagingException("Failed to send email"))
// success case .when(emailService)
Arguments.of(null, true, 200, "Email sent successfully"), .sendEmailWithAttachment(any(Email.class));
// generic messaging error
Arguments.of( // Perform the request and verify the response
new MessagingException("Failed to send email"), mockMvc.perform(
true, multipart("/api/v1/general/send-email")
500, .file("fileInput", "dummy-content".getBytes())
"Failed to send email: Failed to send email"), .param("to", email.getTo())
// missing 'to' results in MailSendException .param("subject", email.getSubject())
Arguments.of( .param("body", email.getBody()))
new MailSendException("Invalid Addresses"), .andExpect(status().isInternalServerError())
false, .andExpect(content().string("Failed to send email: Failed to send email"));
500,
"Invalid Addresses"),
// invalid email address formatting
Arguments.of(
new MessagingException("Invalid Addresses"),
true,
500,
"Failed to send email: Invalid Addresses"));
} }
} }