Compare commits

...

18 Commits

Author SHA1 Message Date
Anthony Stirling
8bfdb2abb5
Update home.html (#3560)
# Description of Changes

Please provide a summary of the changes, including:

- What was changed
- Why the change was made
- Any challenges encountered

Closes #(issue_number)

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-05-20 17:42:42 +01:00
Reece Browne
70349fb7e3
remove legacy homepage (#3518)
# Description of Changes

Please provide a summary of the changes, including:

- What was changed
- Why the change was made
- Any challenges encountered

Closes #(issue_number)

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
2025-05-20 12:08:20 +01:00
stirlingbot[bot]
bef86b44e4
Update 3rd Party Licenses (#3559)
Auto-generated by StirlingBot

Signed-off-by: stirlingbot[bot] <1113334+stirlingbot[bot]@users.noreply.github.com>
Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com>
2025-05-20 12:07:03 +01:00
Anthony Stirling
46cc2e05df
Add additional unit tests for utils and EE (#3557)
## Summary
- add tests for LicenseKeyChecker
- expand GeneralUtils coverage
- cover extra PdfUtils functionality
- merge PdfUtilsMoreTest into PdfUtilsTest

## Testing
- `./gradlew test --no-daemon`
- `./gradlew build spotlessApply --no-daemon`
2025-05-20 12:05:18 +01:00
Anthony Stirling
c8e25f4c5a
Fix TemplateResolver and LibreOfficeListener bugs (#3555)
## Summary
- log missing exceptions in FileFallbackTemplateResolver
- implement exists check for InputStreamTemplateResource
- use LISTENER_PORT constant when verifying LibreOffice listener

## Testing
- `./gradlew build --no-daemon`
- `./gradlew test --no-daemon`

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-20 12:02:26 +01:00
Anthony Stirling
218d21f07a
Update AGENTS guidelines (#3556)
## Summary
- clarify Codex contribution instructions
- remove `test.sh` reference and require `./gradlew build`
- add Developer Guide, AI note and translation policy

## Testing
- `./gradlew spotlessApply`
- `./gradlew build`
2025-05-20 12:02:10 +01:00
Anthony Stirling
9fe49c494d
Fix test compilation around pipeline processor (#3554)
## Summary
- allow tests to spy on PipelineProcessor web requests
- fix ResponseEntity usage in PipelineProcessorTest

## Testing
- `./gradlew test --offline` *(fails: No route to host while downloading
gradle-8.14-all.zip)*
2025-05-20 12:02:01 +01:00
dependabot[bot]
d59e39b4b6
Bump org.mockito:mockito-core from 5.11.0 to 5.17.0 (#3551)
Bumps [org.mockito:mockito-core](https://github.com/mockito/mockito)
from 5.11.0 to 5.17.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/mockito/mockito/releases">org.mockito:mockito-core's
releases</a>.</em></p>
<blockquote>
<h2>v5.17.0</h2>
<p><!-- raw HTML omitted --><!-- raw HTML omitted --><em>Changelog
generated by <a
href="https://github.com/shipkit/shipkit-changelog">Shipkit Changelog
Gradle Plugin</a></em><!-- raw HTML omitted --><!-- raw HTML omitted
--></p>
<h4>5.17.0</h4>
<ul>
<li>2025-04-04 - <a
href="https://github.com/mockito/mockito/compare/v5.16.1...v5.17.0">7
commit(s)</a> by Adrian Roos, Andre Kurait, Jan Ouwens, Rafael
Winterhalter, Taeik Lim, Thach Le, Tim van der Lippe</li>
<li>Fixes <a
href="https://redirect.github.com/mockito/mockito/issues/3631">#3631</a>:
Fix broken banner image link [(<a
href="https://redirect.github.com/mockito/mockito/issues/3632">#3632</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3632">mockito/mockito#3632</a>)</li>
<li>Banner image is broken [(<a
href="https://redirect.github.com/mockito/mockito/issues/3631">#3631</a>)](<a
href="https://redirect.github.com/mockito/mockito/issues/3631">mockito/mockito#3631</a>)</li>
<li>Update exception message with mockito-inline [(<a
href="https://redirect.github.com/mockito/mockito/issues/3628">#3628</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3628">mockito/mockito#3628</a>)</li>
<li>Clarify structure of commit messages [(<a
href="https://redirect.github.com/mockito/mockito/issues/3626">#3626</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3626">mockito/mockito#3626</a>)</li>
<li>Fixes <a
href="https://redirect.github.com/mockito/mockito/issues/3622">#3622</a>:
MockitoExtension fails cleanup when aborted before setup [(<a
href="https://redirect.github.com/mockito/mockito/issues/3623">#3623</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3623">mockito/mockito#3623</a>)</li>
<li>MockitoExtension fails cleanup when aborted before setup [(<a
href="https://redirect.github.com/mockito/mockito/issues/3622">#3622</a>)](<a
href="https://redirect.github.com/mockito/mockito/issues/3622">mockito/mockito#3622</a>)</li>
<li>Since mockito-inline has been removed, the exception messages with
<code>mockito-inline</code> should be modified. [(<a
href="https://redirect.github.com/mockito/mockito/issues/3621">#3621</a>)](<a
href="https://redirect.github.com/mockito/mockito/issues/3621">mockito/mockito#3621</a>)</li>
<li>Fixes <a
href="https://redirect.github.com/mockito/mockito/issues/3171">#3171</a>:
Fall back to Throwable Location strategy on Android [(<a
href="https://redirect.github.com/mockito/mockito/issues/3619">#3619</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3619">mockito/mockito#3619</a>)</li>
<li>Fixes <a
href="https://redirect.github.com/mockito/mockito/issues/3615">#3615</a>
: broken links to javadoc.io [(<a
href="https://redirect.github.com/mockito/mockito/issues/3616">#3616</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3616">mockito/mockito#3616</a>)</li>
<li>Broken links to javadoc.io [(<a
href="https://redirect.github.com/mockito/mockito/issues/3615">#3615</a>)](<a
href="https://redirect.github.com/mockito/mockito/issues/3615">mockito/mockito#3615</a>)</li>
<li>Mocks are not working on particular devices after update Android SDK
from 33 to 34 [(<a
href="https://redirect.github.com/mockito/mockito/issues/3171">#3171</a>)](<a
href="https://redirect.github.com/mockito/mockito/issues/3171">mockito/mockito#3171</a>)</li>
</ul>
<h2>v5.16.1</h2>
<p><!-- raw HTML omitted --><!-- raw HTML omitted --><em>Changelog
generated by <a
href="https://github.com/shipkit/shipkit-changelog">Shipkit Changelog
Gradle Plugin</a></em><!-- raw HTML omitted --><!-- raw HTML omitted
--></p>
<h4>5.16.1</h4>
<ul>
<li>2025-03-15 - <a
href="https://github.com/mockito/mockito/compare/v5.16.0...v5.16.1">3
commit(s)</a> by Adrian Roos, Jérôme Prinet, Rafael Winterhalter</li>
<li>Remove Arrays.asList from critical stubbing path in
GenericMetadataSu… [(<a
href="https://redirect.github.com/mockito/mockito/issues/3610">#3610</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3610">mockito/mockito#3610</a>)</li>
<li>Rework of injection strategy in the context of modules [(<a
href="https://redirect.github.com/mockito/mockito/issues/3608">#3608</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3608">mockito/mockito#3608</a>)</li>
<li>Adjust inline mocking snippet to allow task relocatability [(<a
href="https://redirect.github.com/mockito/mockito/issues/3606">#3606</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3606">mockito/mockito#3606</a>)</li>
<li>Inline mocking configuration snippet for Gradle should allow task
relocatability [(<a
href="https://redirect.github.com/mockito/mockito/issues/3605">#3605</a>)](<a
href="https://redirect.github.com/mockito/mockito/issues/3605">mockito/mockito#3605</a>)</li>
</ul>
<h2>v5.16.0</h2>
<p><!-- raw HTML omitted --><!-- raw HTML omitted --><em>Changelog
generated by <a
href="https://github.com/shipkit/shipkit-changelog">Shipkit Changelog
Gradle Plugin</a></em><!-- raw HTML omitted --><!-- raw HTML omitted
--></p>
<h4>5.16.0</h4>
<ul>
<li>2025-03-03 - <a
href="https://github.com/mockito/mockito/compare/v5.15.2...v5.16.0">10
commit(s)</a> by Brice Dutheil, Rafael Winterhalter, TDL,
dependabot[bot]</li>
<li>Add support for including module-info in Mockito. [(<a
href="https://redirect.github.com/mockito/mockito/issues/3597">#3597</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3597">mockito/mockito#3597</a>)</li>
<li>Bump com.gradle.develocity from 3.19 to 3.19.1 [(<a
href="https://redirect.github.com/mockito/mockito/issues/3579">#3579</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3579">mockito/mockito#3579</a>)</li>
<li>Bump org.assertj:assertj-core from 3.27.2 to 3.27.3 [(<a
href="https://redirect.github.com/mockito/mockito/issues/3577">#3577</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3577">mockito/mockito#3577</a>)</li>
<li>Bump com.diffplug.spotless:spotless-plugin-gradle from 7.0.1 to
7.0.2 [(<a
href="https://redirect.github.com/mockito/mockito/issues/3574">#3574</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3574">mockito/mockito#3574</a>)</li>
<li>Bump com.diffplug.spotless:spotless-plugin-gradle from 6.25.0 to
7.0.1 [(<a
href="https://redirect.github.com/mockito/mockito/issues/3571">#3571</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3571">mockito/mockito#3571</a>)</li>
<li>Bump org.assertj:assertj-core from 3.27.1 to 3.27.2 [(<a
href="https://redirect.github.com/mockito/mockito/issues/3569">#3569</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3569">mockito/mockito#3569</a>)</li>
<li>Tweaks documentation on mockito agent config for maven [(<a
href="https://redirect.github.com/mockito/mockito/issues/3568">#3568</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3568">mockito/mockito#3568</a>)</li>
<li>Adds <code>--info</code> to diagnose
closeAndReleaseStagingRepositories issues [(<a
href="https://redirect.github.com/mockito/mockito/issues/3567">#3567</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3567">mockito/mockito#3567</a>)</li>
<li>Refine reflection when calling management factory [(<a
href="https://redirect.github.com/mockito/mockito/issues/3566">#3566</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3566">mockito/mockito#3566</a>)</li>
<li>Avoid warning when dynamic attach is enabled [(<a
href="https://redirect.github.com/mockito/mockito/issues/3551">#3551</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3551">mockito/mockito#3551</a>)</li>
</ul>
<h2>v5.15.2</h2>
<p><!-- raw HTML omitted --><!-- raw HTML omitted --><em>Changelog
generated by <a
href="https://github.com/shipkit/shipkit-changelog">Shipkit Changelog
Gradle Plugin</a></em><!-- raw HTML omitted --><!-- raw HTML omitted
--></p>
<h4>5.15.2</h4>
<ul>
<li>2025-01-02 - <a
href="https://github.com/mockito/mockito/compare/v5.15.1...v5.15.2">2
commit(s)</a> by Brice Dutheil, dependabot[bot]</li>
<li>Fix javadoc publication [(<a
href="https://redirect.github.com/mockito/mockito/issues/3561">#3561</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3561">mockito/mockito#3561</a>)</li>
<li>Bump org.assertj:assertj-core from 3.27.0 to 3.27.1 [(<a
href="https://redirect.github.com/mockito/mockito/issues/3560">#3560</a>)](<a
href="https://redirect.github.com/mockito/mockito/pull/3560">mockito/mockito#3560</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7764992d12"><code>7764992</code></a>
Remove mention of <code>mockito-inline</code> from mockmaker exception
(<a
href="https://redirect.github.com/mockito/mockito/issues/3628">#3628</a>)</li>
<li><a
href="ee92ad4916"><code>ee92ad4</code></a>
Fix broken banner image link (<a
href="https://redirect.github.com/mockito/mockito/issues/3632">#3632</a>)</li>
<li><a
href="3edab52835"><code>3edab52</code></a>
Clarify structure of commit messages (<a
href="https://redirect.github.com/mockito/mockito/issues/3626">#3626</a>)</li>
<li><a
href="bfab74365e"><code>bfab743</code></a>
Fall back to Throwable Location strategy on Android (<a
href="https://redirect.github.com/mockito/mockito/issues/3619">#3619</a>)</li>
<li><a
href="4f469c830b"><code>4f469c8</code></a>
MockitoExtension fails cleanup when aborted before setup (<a
href="https://redirect.github.com/mockito/mockito/issues/3623">#3623</a>)</li>
<li><a
href="1764e62102"><code>1764e62</code></a>
Update links to javadoc.io (<a
href="https://redirect.github.com/mockito/mockito/issues/3616">#3616</a>)</li>
<li><a
href="1e029d767b"><code>1e029d7</code></a>
Add missing requirement to objenesis.</li>
<li><a
href="d000e63077"><code>d000e63</code></a>
Rework of injection strategy in the context of modules (<a
href="https://redirect.github.com/mockito/mockito/issues/3608">#3608</a>)</li>
<li><a
href="0215884a5e"><code>0215884</code></a>
Remove Arrays.asList from critical stubbing path in
GenericMetadataSupport (#...</li>
<li><a
href="d18503512b"><code>d185035</code></a>
Add reference to Gradle documentation on how to make task relocatable
(<a
href="https://redirect.github.com/mockito/mockito/issues/3606">#3606</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/mockito/mockito/compare/v5.11.0...v5.17.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.mockito:mockito-core&package-manager=gradle&previous-version=5.11.0&new-version=5.17.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 12:00:31 +01:00
dependabot[bot]
9514370cc3
Bump org.gradle.toolchains.foojay-resolver-convention from 0.10.0 to 1.0.0 (#3552)
Bumps org.gradle.toolchains.foojay-resolver-convention from 0.10.0 to
1.0.0.


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.gradle.toolchains.foojay-resolver-convention&package-manager=gradle&previous-version=0.10.0&new-version=1.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 11:58:23 +01:00
dependabot[bot]
b9dd78ced6
Bump io.micrometer:micrometer-core from 1.14.7 to 1.15.0 (#3550)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps
[io.micrometer:micrometer-core](https://github.com/micrometer-metrics/micrometer)
from 1.14.7 to 1.15.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/micrometer-metrics/micrometer/releases">io.micrometer:micrometer-core's
releases</a>.</em></p>
<blockquote>
<h2>1.15.0</h2>
<h2> New Features</h2>
<ul>
<li>Further enhancement to OtlpMetricsSender <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/6025">#6025</a></li>
<li>Make Prometheus Metric and Label naming conventions consistent <a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/5923">#5923</a></li>
<li>Metrics for Executors.newVirtualThreadPerTaskExecutor() <a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/5488">#5488</a></li>
<li>Metrics for live virtual threads <a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/5950">#5950</a></li>
<li>More flexible OTLP per meter configuration <a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/6099">#6099</a></li>
<li>Prometheus/OpenMetrics <code>_created</code> timestamp <a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/2625">#2625</a></li>
<li>Make jvm.classes.unloaded description generic <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5745">#5745</a></li>
<li>Use String.toLowerCase()/toUpperCase() with Locale.ROOT consistently
<a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5711">#5711</a></li>
<li>Use failWithActualExpectedAndMessage() where possible <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5696">#5696</a></li>
<li>Provide target host/port info in ObservationExecChainHandler when
HttpHostConnectException is thrown <a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/5615">#5615</a></li>
<li>Enable Gauge builders to take a subclass of Number <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5601">#5601</a></li>
<li>micrometer-observation-test support for assertions on events <a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/5576">#5576</a></li>
<li>Log delta count in addition to throughput in LoggingMeterRegistry <a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/5548">#5548</a></li>
<li>Add peer name and port to gRPC observation contexts <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/3512">#3512</a></li>
<li>Use direct equals call instead of Objects.equals wrapper <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5840">#5840</a></li>
<li>Remove special handling of 404/301 from JDK HTTP client
instrumentation <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5838">#5838</a></li>
<li>Make Timer and LongTaskTimer output similar in LoggingMeterRegistry
<a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5835">#5835</a></li>
<li>Remove special handling of 404 and redirection statuses from Jetty
client instrumentation <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5825">#5825</a></li>
<li>Log deprecation warning when creating SignalFxMeterRegistry <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5824">#5824</a></li>
<li>Log metrics recording failures in CountedAspect and TimedAspect <a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/5820">#5820</a></li>
<li>Remove special handling of 404/301 from OkHttp instrumentation <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5814">#5814</a></li>
<li>Support AutoShutdownDelegatedExecutorService in
ExecutorServiceMetrics <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5811">#5811</a></li>
<li>Deprecate micrometer-registry-signalfx in favor of
micrometer-registry-otlp <a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/5807">#5807</a></li>
<li>Rebind <code>Log4j2Metrics</code> when
<code>LoggerContext#reconfigure</code> is called <a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/5756">#5756</a></li>
<li>Send metrics via any protocol in the OTLP Registry <a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/5690">#5690</a></li>
<li>Improve average performance of DefaultLongTaskTimer for out-of-order
stopping <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5591">#5591</a></li>
<li>Improve OtlpMetricsSender API <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5994">#5994</a></li>
<li>Support configuring exponential histograms at the meter level <a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/5459">#5459</a></li>
<li>Allow TimedAspect/CountedAspect to create tags based on method
result <a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/3058">#3058</a></li>
</ul>
<h2>🐞 Bug Fixes</h2>
<ul>
<li>Do not leak OTLP types on public-facing API <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5699">#5699</a></li>
<li>micrometer-observation-test brings unnecessary JUnit dependencies,
leading to conflicts <a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/6012">#6012</a></li>
</ul>
<h2>🔨 Dependency Upgrades</h2>
<ul>
<li>Bump io.opentelemetry.proto:opentelemetry-proto from 1.4.0-alpha to
1.5.0-alpha <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5798">#5798</a></li>
<li>Bump com.google.cloud:libraries-bom from 26.55.0 to 26.56.0 <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5991">#5991</a></li>
<li>Bump com.google.cloud:google-cloud-monitoring from 3.59.0 to 3.60.0
<a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5986">#5986</a></li>
<li>Bump com.google.auth:google-auth-library-oauth2-http from 1.32.1 to
1.33.0 <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5963">#5963</a></li>
<li>Bump software.amazon.awssdk:cloudwatch from 2.29.46 to 2.30.11 <a
href="https://redirect.github.com/micrometer-metrics/micrometer/pull/5863">#5863</a></li>
</ul>
<h2>❤️ Contributors</h2>
<p>Thank you to all the contributors who worked on this release:</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e13042badc"><code>e13042b</code></a>
Bump software.amazon.awssdk:cloudwatch from 2.31.40 to 2.31.41 (<a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/6228">#6228</a>)</li>
<li><a
href="571793b84e"><code>571793b</code></a>
Merge branch '1.14.x'</li>
<li><a
href="315c1b1817"><code>315c1b1</code></a>
Merge branch '1.13.x' into 1.14.x</li>
<li><a
href="a3ae027d8c"><code>a3ae027</code></a>
Bump com.tngtech.archunit:archunit-junit5 from 1.3.1 to 1.3.2 (<a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/6225">#6225</a>)</li>
<li><a
href="ac6c26f7ba"><code>ac6c26f</code></a>
Merge branch '1.14.x'</li>
<li><a
href="163203f981"><code>163203f</code></a>
Add missing colons in &quot;Environment&quot; section in bug_report.md
(<a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/6223">#6223</a>)</li>
<li><a
href="1713feed26"><code>1713fee</code></a>
Bump maven-resolver from 1.9.22 to 1.9.23 (<a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/6222">#6222</a>)</li>
<li><a
href="e31548477a"><code>e315484</code></a>
Bump software.amazon.awssdk:cloudwatch from 2.31.39 to 2.31.40 (<a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/6221">#6221</a>)</li>
<li><a
href="d6b8d4e847"><code>d6b8d4e</code></a>
Bump com.google.cloud:libraries-bom from 26.59.0 to 26.60.0 (<a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/6220">#6220</a>)</li>
<li><a
href="121056e6d5"><code>121056e</code></a>
Bump software.amazon.awssdk:cloudwatch from 2.31.38 to 2.31.39 (<a
href="https://redirect.github.com/micrometer-metrics/micrometer/issues/6217">#6217</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/micrometer-metrics/micrometer/compare/v1.14.7...v1.15.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=io.micrometer:micrometer-core&package-manager=gradle&previous-version=1.14.7&new-version=1.15.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 11:53:02 +01:00
dependabot[bot]
f50f7230d0
Bump org.springframework.security:spring-security-saml2-service-provider from 6.4.5 to 6.5.0 (#3549)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps
[org.springframework.security:spring-security-saml2-service-provider](https://github.com/spring-projects/spring-security)
from 6.4.5 to 6.5.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/spring-projects/spring-security/releases">org.springframework.security:spring-security-saml2-service-provider's
releases</a>.</em></p>
<blockquote>
<h2>6.5.0</h2>
<h2> New Features</h2>
<ul>
<li>Add documentation for DPoP support <a
href="https://redirect.github.com/spring-projects/spring-security/issues/17072">#17072</a></li>
<li>Add logging to CsrfTokenRequestHandler implementations <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16994">#16994</a></li>
<li>Add mapping for DPoP in DefaultMapOAuth2AccessTokenResponseConverter
<a
href="https://redirect.github.com/spring-projects/spring-security/pull/16806">#16806</a></li>
<li>Bump Gradle Wrapper from 8.13 to 8.14 <a
href="https://redirect.github.com/spring-projects/spring-security/issues/17018">#17018</a></li>
<li>ClientRegistrations.fromIssuerLocation does not include failure
information <a
href="https://redirect.github.com/spring-projects/spring-security/issues/17015">#17015</a></li>
<li>Fix Typo In SubjectDnX509PrincipalExtractorTests <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16997">#16997</a></li>
<li>Implement internal cache in JtiClaimValidator <a
href="https://redirect.github.com/spring-projects/spring-security/issues/17107">#17107</a></li>
<li>Polish javadoc <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16924">#16924</a></li>
<li>Remove unused classes <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16935">#16935</a></li>
<li>Replace NimbusOpaqueTokenIntrospector with
SpringOpaqueTokenIntrospector in Documentation <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16962">#16962</a></li>
<li>RequestHeaderAuthenticationFilter creates a session even if not
configured to do so <a
href="https://redirect.github.com/spring-projects/spring-security/issues/17147">#17147</a></li>
</ul>
<h2>🪲 Bug Fixes</h2>
<ul>
<li>Add FunctionalInterface To X509PrincipalExtractor <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16952">#16952</a></li>
<li>Change NonNull import from reactor to spring <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16571">#16571</a></li>
<li>Fix DPoP jkt claim to be JWK SHA-256 thumbprint <a
href="https://redirect.github.com/spring-projects/spring-security/pull/17080">#17080</a></li>
<li>Minor error in the Handling Logouts documentation <a
href="https://redirect.github.com/spring-projects/spring-security/issues/17049">#17049</a></li>
<li>SecurityAnnotationScanner's method comparison should use .equals <a
href="https://redirect.github.com/spring-projects/spring-security/issues/17145">#17145</a></li>
<li>Use proper configuration key in Opaque Token documentation <a
href="https://redirect.github.com/spring-projects/spring-security/issues/17014">#17014</a></li>
</ul>
<h2>🔨 Dependency Upgrades</h2>
<ul>
<li>Bump com.fasterxml.jackson:jackson-bom from 2.18.3 to 2.18.4 <a
href="https://redirect.github.com/spring-projects/spring-security/issues/17069">#17069</a></li>
<li>Bump com.fasterxml.jackson:jackson-bom from 2.18.3 to 2.19.0 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16995">#16995</a></li>
<li>Bump com.google.code.gson:gson from 2.13.0 to 2.13.1 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16990">#16990</a></li>
<li>Bump com.webauthn4j:webauthn4j-core from 0.29.0.RELEASE to
0.29.1.RELEASE <a
href="https://redirect.github.com/spring-projects/spring-security/pull/17024">#17024</a></li>
<li>Bump com.webauthn4j:webauthn4j-core from 0.29.1.RELEASE to
0.29.2.RELEASE <a
href="https://redirect.github.com/spring-projects/spring-security/pull/17095">#17095</a></li>
<li>Bump io.micrometer:micrometer-observation from 1.14.6 to 1.14.7 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/17096">#17096</a></li>
<li>Bump io.mockk:mockk from 1.14.0 to 1.14.2 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/17019">#17019</a></li>
<li>Bump io.projectreactor:reactor-bom from 2023.0.17 to 2023.0.18 <a
href="https://redirect.github.com/spring-projects/spring-security/issues/17111">#17111</a></li>
<li>Bump io.spring.gradle:spring-security-release-plugin from 1.0.5 to
1.0.6 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/17040">#17040</a></li>
<li>Bump org-apache-maven-resolver from 1.9.22 to 1.9.23 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/17088">#17088</a></li>
<li>Bump org-eclipse-jetty from 11.0.24 to 11.0.25 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16761">#16761</a></li>
<li>Bump org.hibernate.orm:hibernate-core from 6.6.13.Final to
6.6.14.Final <a
href="https://redirect.github.com/spring-projects/spring-security/pull/17089">#17089</a></li>
<li>Bump org.hibernate.orm:hibernate-core from 6.6.14.Final to
6.6.15.Final <a
href="https://redirect.github.com/spring-projects/spring-security/pull/17105">#17105</a></li>
<li>Bump org.seleniumhq.selenium:selenium-java from 4.31.0 to 4.32.0 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/17037">#17037</a></li>
<li>Bump org.springframework.data:spring-data-bom from 2024.1.4 to
2024.1.5 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/16981">#16981</a></li>
<li>Bump org.springframework.data:spring-data-bom from 2024.1.5 to
2024.1.6 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/17137">#17137</a></li>
<li>Bump org.springframework:spring-framework-bom from 6.2.6 to 6.2.7 <a
href="https://redirect.github.com/spring-projects/spring-security/pull/17124">#17124</a></li>
</ul>
<h2>🔩 Build Updates</h2>
<ul>
<li>Release 6.5.0 <a
href="https://redirect.github.com/spring-projects/spring-security/issues/17138">#17138</a></li>
</ul>
<h2>❤️ Contributors</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="0fd0e9335a"><code>0fd0e93</code></a>
Release 6.5.0</li>
<li><a
href="78dd02a4c1"><code>78dd02a</code></a>
Merge branch '6.4.x' into 6.5.x</li>
<li><a
href="edc8735eb8"><code>edc8735</code></a>
Merge branch '6.3.x' into 6.4.x</li>
<li><a
href="cae3467a8d"><code>cae3467</code></a>
Improve AbstractPreAuthenticatedProcessingFilter docs</li>
<li><a
href="9a8f9a91bc"><code>9a8f9a9</code></a>
Merge branch '6.4.x' into 6.5.x</li>
<li><a
href="c972de5369"><code>c972de5</code></a>
Use .equals to Compare Methods</li>
<li><a
href="bf2aaa1b18"><code>bf2aaa1</code></a>
Use .equals to Compare Methods</li>
<li><a
href="6fb0591109"><code>6fb0591</code></a>
Merge branch
'gradle/6.5.x/org.springframework.data-spring-data-bom-2024.1.6'...</li>
<li><a
href="390972c4a0"><code>390972c</code></a>
Merge branch '6.4.x' into 6.5.x</li>
<li><a
href="3690517395"><code>3690517</code></a>
Merge branch
'gradle/6.4.x/org.springframework.data-spring-data-bom-2024.1.6'...</li>
<li>Additional commits viewable in <a
href="https://github.com/spring-projects/spring-security/compare/6.4.5...6.5.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.springframework.security:spring-security-saml2-service-provider&package-manager=gradle&previous-version=6.4.5&new-version=6.5.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 11:52:50 +01:00
dependabot[bot]
8ecd4e9c36
Bump org.springframework:spring-webmvc from 6.2.6 to 6.2.7 (#3547)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps
[org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework)
from 6.2.6 to 6.2.7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/spring-projects/spring-framework/releases">org.springframework:spring-webmvc's
releases</a>.</em></p>
<blockquote>
<h2>v6.2.7</h2>
<h2> New Features</h2>
<ul>
<li>Forward more methods to underlying InputStream in
NonClosingInputStream <a
href="https://redirect.github.com/spring-projects/spring-framework/pull/34893">#34893</a></li>
<li>Introduce Spring property for the default property placeholder
escape character <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34865">#34865</a></li>
<li>Close ApplicationContext once AOT processing has completed <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34841">#34841</a></li>
<li>Fix
<code>AbstractJackson2HttpMessageConverter#getObjectMappersForType</code>
nullness <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34811">#34811</a></li>
<li>Add option for case-insensitive match to PatternMatchUtils <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34801">#34801</a></li>
<li>RestClient <code>@RequestBody</code> parameters lose generic type
information when creating HTTP service beans <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34793">#34793</a></li>
<li>Adds option to set Principal in MockServerWebExchange <a
href="https://redirect.github.com/spring-projects/spring-framework/pull/34789">#34789</a></li>
</ul>
<h2>🐞 Bug Fixes</h2>
<ul>
<li>Beans created by FactoryBean are not considered as autowiring
candidates if another thread holds a singletonLock <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34902">#34902</a></li>
<li><code>PropertySourcesPlaceholderConfigurer</code> placeholder
resolution fails in several scenarios <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34861">#34861</a></li>
<li>HttpComponentsClientHttpRequestFactory setConnectionRequestTimeout
not working with httpclient 5.3.1 <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34851">#34851</a></li>
<li>Fragment.create() requires mutable map - which is unusable when used
with Kotlin <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34848">#34848</a></li>
<li>Duplicate <code>BeanOverrideHandler</code> discovered in
<code>@Nested</code> test case with superclass from different class or
in interface implemented multiple times <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34844">#34844</a></li>
<li>Accidental ClassLoader defineClass enforcement after <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34677">#34677</a>
<a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34824">#34824</a></li>
<li>HttpEntity.EMPTY headers should not be possible to mutate via
HttpHeaders constructor <a
href="https://redirect.github.com/spring-projects/spring-framework/pull/34812">#34812</a></li>
<li>AbstractFileResolvingResource.exists incorrectly reports result for
resources inside of spring-boot executable jar <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34796">#34796</a></li>
<li>Correctly expand query param with same name from URI variables array
<a
href="https://redirect.github.com/spring-projects/spring-framework/pull/34783">#34783</a></li>
<li>R2DBC <code>NamedParameterUtils</code> only expands reused
collection parameter once <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34768">#34768</a></li>
<li><code>PathMatchingResourcePatternResolver</code> wrongly assumes
that <code>target/classes</code> always exists <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34764">#34764</a></li>
</ul>
<h2>📔 Documentation</h2>
<ul>
<li>Clarify <code>CompositePropertySource</code> behavior for
<code>EnumerablePropertySource</code> contract <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34886">#34886</a></li>
<li>Javadoc and <code>@Nullable</code> annotation for
<code>servletContext</code> parameter of
<code>ConfigurableWebEnvironment.initPropertySources</code> are
contradictory <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34845">#34845</a></li>
<li>Spring MVC: <code>@EnableAsync</code> needs to be redeclared for
each ApplicationContext <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34843">#34843</a></li>
<li>Provide a working example instead of unclear placeholders <a
href="https://redirect.github.com/spring-projects/spring-framework/pull/34828">#34828</a></li>
</ul>
<h2>🔨 Dependency Upgrades</h2>
<ul>
<li>Upgrade to Micrometer 1.14.7 <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34889">#34889</a></li>
<li>Upgrade to Reactor 2024.0.6 <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34898">#34898</a></li>
</ul>
<h2>❤️ Contributors</h2>
<p>Thank you to all the contributors who worked on this release:</p>
<p><a href="https://github.com/Artur"><code>@​Artur</code></a>-, <a
href="https://github.com/blake-bauman"><code>@​blake-bauman</code></a>,
<a href="https://github.com/iifawzi"><code>@​iifawzi</code></a>, <a
href="https://github.com/kilink"><code>@​kilink</code></a>, <a
href="https://github.com/quaff"><code>@​quaff</code></a>, <a
href="https://github.com/whlit"><code>@​whlit</code></a>, and <a
href="https://github.com/zzoe2346"><code>@​zzoe2346</code></a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ba590ac9e4"><code>ba590ac</code></a>
Release v6.2.7</li>
<li><a
href="ee62701f56"><code>ee62701</code></a>
Make use of PatternMatchUtils ignoreCase option</li>
<li><a
href="fa168ca78a"><code>fa168ca</code></a>
Revise FactoryBean locking behavior for strict/lenient consistency</li>
<li><a
href="3c228a5c1d"><code>3c228a5</code></a>
Add missing <a href="https://github.com/since"><code>@​since</code></a>
tags in PatternMatchUtils</li>
<li><a
href="9bf6b8cddf"><code>9bf6b8c</code></a>
Upgrade to Reactor 2024.0.6</li>
<li><a
href="37ecdd1437"><code>37ecdd1</code></a>
Forward more methods to underlying InputStream in
NonClosingInputStream</li>
<li><a
href="73f1c5a189"><code>73f1c5a</code></a>
Polishing</li>
<li><a
href="4d296fb4ca"><code>4d296fb</code></a>
Upgrade to Micrometer 1.14.7</li>
<li><a
href="6a9444473f"><code>6a94444</code></a>
Clarify CompositePropertySource behavior for EnumerablePropertySource
contract</li>
<li><a
href="03ae97b2eb"><code>03ae97b</code></a>
Introduce Spring property for default escape character for
placeholders</li>
<li>Additional commits viewable in <a
href="https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.springframework:spring-webmvc&package-manager=gradle&previous-version=6.2.6&new-version=6.2.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 11:52:38 +01:00
dependabot[bot]
9aa692674f
Bump org.sonarqube from 6.1.0.5360 to 6.2.0.5505 (#3546)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps org.sonarqube from 6.1.0.5360 to 6.2.0.5505.


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.sonarqube&package-manager=gradle&previous-version=6.1.0.5360&new-version=6.2.0.5505)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 11:52:15 +01:00
dependabot[bot]
89992fe643
Bump org.springframework:spring-jdbc from 6.2.6 to 6.2.7 (#3545)
Bumps
[org.springframework:spring-jdbc](https://github.com/spring-projects/spring-framework)
from 6.2.6 to 6.2.7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/spring-projects/spring-framework/releases">org.springframework:spring-jdbc's
releases</a>.</em></p>
<blockquote>
<h2>v6.2.7</h2>
<h2> New Features</h2>
<ul>
<li>Forward more methods to underlying InputStream in
NonClosingInputStream <a
href="https://redirect.github.com/spring-projects/spring-framework/pull/34893">#34893</a></li>
<li>Introduce Spring property for the default property placeholder
escape character <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34865">#34865</a></li>
<li>Close ApplicationContext once AOT processing has completed <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34841">#34841</a></li>
<li>Fix
<code>AbstractJackson2HttpMessageConverter#getObjectMappersForType</code>
nullness <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34811">#34811</a></li>
<li>Add option for case-insensitive match to PatternMatchUtils <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34801">#34801</a></li>
<li>RestClient <code>@RequestBody</code> parameters lose generic type
information when creating HTTP service beans <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34793">#34793</a></li>
<li>Adds option to set Principal in MockServerWebExchange <a
href="https://redirect.github.com/spring-projects/spring-framework/pull/34789">#34789</a></li>
</ul>
<h2>🐞 Bug Fixes</h2>
<ul>
<li>Beans created by FactoryBean are not considered as autowiring
candidates if another thread holds a singletonLock <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34902">#34902</a></li>
<li><code>PropertySourcesPlaceholderConfigurer</code> placeholder
resolution fails in several scenarios <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34861">#34861</a></li>
<li>HttpComponentsClientHttpRequestFactory setConnectionRequestTimeout
not working with httpclient 5.3.1 <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34851">#34851</a></li>
<li>Fragment.create() requires mutable map - which is unusable when used
with Kotlin <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34848">#34848</a></li>
<li>Duplicate <code>BeanOverrideHandler</code> discovered in
<code>@Nested</code> test case with superclass from different class or
in interface implemented multiple times <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34844">#34844</a></li>
<li>Accidental ClassLoader defineClass enforcement after <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34677">#34677</a>
<a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34824">#34824</a></li>
<li>HttpEntity.EMPTY headers should not be possible to mutate via
HttpHeaders constructor <a
href="https://redirect.github.com/spring-projects/spring-framework/pull/34812">#34812</a></li>
<li>AbstractFileResolvingResource.exists incorrectly reports result for
resources inside of spring-boot executable jar <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34796">#34796</a></li>
<li>Correctly expand query param with same name from URI variables array
<a
href="https://redirect.github.com/spring-projects/spring-framework/pull/34783">#34783</a></li>
<li>R2DBC <code>NamedParameterUtils</code> only expands reused
collection parameter once <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34768">#34768</a></li>
<li><code>PathMatchingResourcePatternResolver</code> wrongly assumes
that <code>target/classes</code> always exists <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34764">#34764</a></li>
</ul>
<h2>📔 Documentation</h2>
<ul>
<li>Clarify <code>CompositePropertySource</code> behavior for
<code>EnumerablePropertySource</code> contract <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34886">#34886</a></li>
<li>Javadoc and <code>@Nullable</code> annotation for
<code>servletContext</code> parameter of
<code>ConfigurableWebEnvironment.initPropertySources</code> are
contradictory <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34845">#34845</a></li>
<li>Spring MVC: <code>@EnableAsync</code> needs to be redeclared for
each ApplicationContext <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34843">#34843</a></li>
<li>Provide a working example instead of unclear placeholders <a
href="https://redirect.github.com/spring-projects/spring-framework/pull/34828">#34828</a></li>
</ul>
<h2>🔨 Dependency Upgrades</h2>
<ul>
<li>Upgrade to Micrometer 1.14.7 <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34889">#34889</a></li>
<li>Upgrade to Reactor 2024.0.6 <a
href="https://redirect.github.com/spring-projects/spring-framework/issues/34898">#34898</a></li>
</ul>
<h2>❤️ Contributors</h2>
<p>Thank you to all the contributors who worked on this release:</p>
<p><a href="https://github.com/Artur"><code>@​Artur</code></a>-, <a
href="https://github.com/blake-bauman"><code>@​blake-bauman</code></a>,
<a href="https://github.com/iifawzi"><code>@​iifawzi</code></a>, <a
href="https://github.com/kilink"><code>@​kilink</code></a>, <a
href="https://github.com/quaff"><code>@​quaff</code></a>, <a
href="https://github.com/whlit"><code>@​whlit</code></a>, and <a
href="https://github.com/zzoe2346"><code>@​zzoe2346</code></a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ba590ac9e4"><code>ba590ac</code></a>
Release v6.2.7</li>
<li><a
href="ee62701f56"><code>ee62701</code></a>
Make use of PatternMatchUtils ignoreCase option</li>
<li><a
href="fa168ca78a"><code>fa168ca</code></a>
Revise FactoryBean locking behavior for strict/lenient consistency</li>
<li><a
href="3c228a5c1d"><code>3c228a5</code></a>
Add missing <a href="https://github.com/since"><code>@​since</code></a>
tags in PatternMatchUtils</li>
<li><a
href="9bf6b8cddf"><code>9bf6b8c</code></a>
Upgrade to Reactor 2024.0.6</li>
<li><a
href="37ecdd1437"><code>37ecdd1</code></a>
Forward more methods to underlying InputStream in
NonClosingInputStream</li>
<li><a
href="73f1c5a189"><code>73f1c5a</code></a>
Polishing</li>
<li><a
href="4d296fb4ca"><code>4d296fb</code></a>
Upgrade to Micrometer 1.14.7</li>
<li><a
href="6a9444473f"><code>6a94444</code></a>
Clarify CompositePropertySource behavior for EnumerablePropertySource
contract</li>
<li><a
href="03ae97b2eb"><code>03ae97b</code></a>
Introduce Spring property for default escape character for
placeholders</li>
<li>Additional commits viewable in <a
href="https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.springframework:spring-jdbc&package-manager=gradle&previous-version=6.2.6&new-version=6.2.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 11:52:04 +01:00
dependabot[bot]
1f56ccfc99
Bump gradle/actions from 4.3.1 to 4.4.0 (#3544)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps [gradle/actions](https://github.com/gradle/actions) from 4.3.1 to
4.4.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/gradle/actions/releases">gradle/actions's
releases</a>.</em></p>
<blockquote>
<h2>v4.4.0</h2>
<p>This release updates 2 downstream components:</p>
<ul>
<li>Develocity injection has been updated to <a
href="https://github.com/gradle/develocity-ci-injection/releases/tag/v2.0">v2.0</a>
<ul>
<li>Some environment variables related to Develocity injection have been
renamed. All vars now being with <code>DEVELOCITY_INJECTION_</code>.
Check <a
href="https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#configuring-develocity-injection">the
docs</a> for more details.</li>
</ul>
</li>
<li>Dependency-graph plugin has been updated to <a
href="https://github.com/gradle/github-dependency-graph-gradle-plugin/releases/tag/v1.4.0">v1.4.0</a>
<ul>
<li>The 'detector' values included in the generated graph can now be
configured via environment variables.</li>
</ul>
</li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>Update develocity-injection init script to v1.3 by <a
href="https://github.com/bot-githubaction"><code>@​bot-githubaction</code></a>
in <a
href="https://redirect.github.com/gradle/actions/pull/592">gradle/actions#592</a></li>
<li>Update develocity-injection init script to v2.0 by <a
href="https://github.com/bot-githubaction"><code>@​bot-githubaction</code></a>
in <a
href="https://redirect.github.com/gradle/actions/pull/593">gradle/actions#593</a></li>
<li>[StepSecurity] ci: Harden GitHub Actions by <a
href="https://github.com/step-security-bot"><code>@​step-security-bot</code></a>
in <a
href="https://redirect.github.com/gradle/actions/pull/597">gradle/actions#597</a></li>
<li>Use v1.4.0 of dependency graph plugin by <a
href="https://github.com/bigdaz"><code>@​bigdaz</code></a> in <a
href="https://redirect.github.com/gradle/actions/pull/638">gradle/actions#638</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/step-security-bot"><code>@​step-security-bot</code></a>
made their first contribution in <a
href="https://redirect.github.com/gradle/actions/pull/597">gradle/actions#597</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/gradle/actions/compare/v4.3.1...v4.4.0">https://github.com/gradle/actions/compare/v4.3.1...v4.4.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8379f6a132"><code>8379f6a</code></a>
Use v1.4.0 of dependency graph plugin (<a
href="https://redirect.github.com/gradle/actions/issues/638">#638</a>)</li>
<li><a
href="9f79b5fa2c"><code>9f79b5f</code></a>
[bot] Update dist directory</li>
<li><a
href="e093fac84c"><code>e093fac</code></a>
Bump the npm-dependencies group in /sources with 5 updates (<a
href="https://redirect.github.com/gradle/actions/issues/636">#636</a>)</li>
<li><a
href="768a17f348"><code>768a17f</code></a>
Bump the npm-dependencies group in /sources with 2 updates (<a
href="https://redirect.github.com/gradle/actions/issues/635">#635</a>)</li>
<li><a
href="3654113772"><code>3654113</code></a>
[bot] Update dist directory</li>
<li><a
href="2ad385cb2a"><code>2ad385c</code></a>
Replace use of typed-rest-client with <code>@​actions/http-client</code>
(<a
href="https://redirect.github.com/gradle/actions/issues/634">#634</a>)</li>
<li><a
href="95dcf96b0d"><code>95dcf96</code></a>
[bot] Update dist directory</li>
<li><a
href="2e3238a664"><code>2e3238a</code></a>
Bump actions/download-artifact from 4.2.1 to 4.3.0 in
/.github/actions/init-i...</li>
<li><a
href="39dddb8ae7"><code>39dddb8</code></a>
Remove direct use of octokit/request-error (<a
href="https://redirect.github.com/gradle/actions/issues/632">#632</a>)</li>
<li><a
href="755ed7db09"><code>755ed7d</code></a>
[bot] Update dist directory</li>
<li>Additional commits viewable in <a
href="06832c7b30...8379f6a132">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=gradle/actions&package-manager=github_actions&previous-version=4.3.1&new-version=4.4.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 11:51:52 +01:00
dependabot[bot]
f290f62e23
Bump actions/dependency-review-action from 4.7.0 to 4.7.1 (#3543)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps
[actions/dependency-review-action](https://github.com/actions/dependency-review-action)
from 4.7.0 to 4.7.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/dependency-review-action/releases">actions/dependency-review-action's
releases</a>.</em></p>
<blockquote>
<h2>v4.7.1</h2>
<ul>
<li>Packages added to <code>allow-dependencies-licenses</code> will be
allowed even if the package in question has no license information <a
href="https://redirect.github.com/actions/dependency-review-action/issues/889">#889</a></li>
<li>License expressions (e.g. <code>Ruby OR GPL-2.0</code>) in the allow
list are automatically discarded so that they don't invalidate the whole
allow list, which should just be license identifier (e.g.
<code>Ruby</code>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="da24556b54"><code>da24556</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/dependency-review-action/issues/933">#933</a>
from actions/dangoor/471-release</li>
<li><a
href="9af0caf0e5"><code>9af0caf</code></a>
Bump version number for 4.7.1</li>
<li><a
href="d8f2df20d5"><code>d8f2df2</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/dependency-review-action/issues/932">#932</a>
from actions/907-disallow-expression</li>
<li><a
href="6e9307a3d4"><code>6e9307a</code></a>
Discard allow list entries that are not SPDX IDs</li>
<li><a
href="8805179dc9"><code>8805179</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/dependency-review-action/issues/930">#930</a>
from actions/889-allow-no-license</li>
<li><a
href="014300b08c"><code>014300b</code></a>
Update build</li>
<li><a
href="34486f306e"><code>34486f3</code></a>
Check namespaces when excluding license checks</li>
<li><a
href="9b155d6432"><code>9b155d6</code></a>
Update build</li>
<li><a
href="f199659a6a"><code>f199659</code></a>
Allowing dependencies works with no licenses</li>
<li>See full diff in <a
href="38ecb5b593...da24556b54">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/dependency-review-action&package-manager=github_actions&previous-version=4.7.0&new-version=4.7.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 11:51:32 +01:00
dependabot[bot]
74fcf01d03
Bump github/codeql-action from 3.28.17 to 3.28.18 (#3542)
Bumps [github/codeql-action](https://github.com/github/codeql-action)
from 3.28.17 to 3.28.18.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/github/codeql-action/releases">github/codeql-action's
releases</a>.</em></p>
<blockquote>
<h2>v3.28.18</h2>
<h1>CodeQL Action Changelog</h1>
<p>See the <a
href="https://github.com/github/codeql-action/releases">releases
page</a> for the relevant changes to the CodeQL CLI and language
packs.</p>
<h2>3.28.18 - 16 May 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.21.3. <a
href="https://redirect.github.com/github/codeql-action/pull/2893">#2893</a></li>
<li>Skip validating SARIF produced by CodeQL for improved performance.
<a
href="https://redirect.github.com/github/codeql-action/pull/2894">#2894</a></li>
<li>The number of threads and amount of RAM used by CodeQL can now be
set via the <code>CODEQL_THREADS</code> and <code>CODEQL_RAM</code>
runner environment variables. If set, these environment variables
override the <code>threads</code> and <code>ram</code> inputs
respectively. <a
href="https://redirect.github.com/github/codeql-action/pull/2891">#2891</a></li>
</ul>
<p>See the full <a
href="https://github.com/github/codeql-action/blob/v3.28.18/CHANGELOG.md">CHANGELOG.md</a>
for more information.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/github/codeql-action/blob/main/CHANGELOG.md">github/codeql-action's
changelog</a>.</em></p>
<blockquote>
<h1>CodeQL Action Changelog</h1>
<p>See the <a
href="https://github.com/github/codeql-action/releases">releases
page</a> for the relevant changes to the CodeQL CLI and language
packs.</p>
<h2>[UNRELEASED]</h2>
<p>No user facing changes.</p>
<h2>3.28.18 - 16 May 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.21.3. <a
href="https://redirect.github.com/github/codeql-action/pull/2893">#2893</a></li>
<li>Skip validating SARIF produced by CodeQL for improved performance.
<a
href="https://redirect.github.com/github/codeql-action/pull/2894">#2894</a></li>
<li>The number of threads and amount of RAM used by CodeQL can now be
set via the <code>CODEQL_THREADS</code> and <code>CODEQL_RAM</code>
runner environment variables. If set, these environment variables
override the <code>threads</code> and <code>ram</code> inputs
respectively. <a
href="https://redirect.github.com/github/codeql-action/pull/2891">#2891</a></li>
</ul>
<h2>3.28.17 - 02 May 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.21.2. <a
href="https://redirect.github.com/github/codeql-action/pull/2872">#2872</a></li>
</ul>
<h2>3.28.16 - 23 Apr 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.21.1. <a
href="https://redirect.github.com/github/codeql-action/pull/2863">#2863</a></li>
</ul>
<h2>3.28.15 - 07 Apr 2025</h2>
<ul>
<li>Fix bug where the action would fail if it tried to produce a debug
artifact with more than 65535 files. <a
href="https://redirect.github.com/github/codeql-action/pull/2842">#2842</a></li>
</ul>
<h2>3.28.14 - 07 Apr 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.21.0. <a
href="https://redirect.github.com/github/codeql-action/pull/2838">#2838</a></li>
</ul>
<h2>3.28.13 - 24 Mar 2025</h2>
<p>No user facing changes.</p>
<h2>3.28.12 - 19 Mar 2025</h2>
<ul>
<li>Dependency caching should now cache more dependencies for Java
<code>build-mode: none</code> extractions. This should speed up
workflows and avoid inconsistent alerts in some cases.</li>
<li>Update default CodeQL bundle version to 2.20.7. <a
href="https://redirect.github.com/github/codeql-action/pull/2810">#2810</a></li>
</ul>
<h2>3.28.11 - 07 Mar 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.20.6. <a
href="https://redirect.github.com/github/codeql-action/pull/2793">#2793</a></li>
</ul>
<h2>3.28.10 - 21 Feb 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.20.5. <a
href="https://redirect.github.com/github/codeql-action/pull/2772">#2772</a></li>
<li>Address an issue where the CodeQL Bundle would occasionally fail to
decompress on macOS. <a
href="https://redirect.github.com/github/codeql-action/pull/2768">#2768</a></li>
</ul>
<h2>3.28.9 - 07 Feb 2025</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ff0a06e83c"><code>ff0a06e</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/2896">#2896</a>
from github/update-v3.28.18-b86edfc27</li>
<li><a
href="a41e0844be"><code>a41e084</code></a>
Update changelog for v3.28.18</li>
<li><a
href="b86edfc27a"><code>b86edfc</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/2893">#2893</a>
from github/update-bundle/codeql-bundle-v2.21.3</li>
<li><a
href="e93b90025f"><code>e93b900</code></a>
Merge branch 'main' into update-bundle/codeql-bundle-v2.21.3</li>
<li><a
href="510dfa3460"><code>510dfa3</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/2894">#2894</a>
from github/henrymercer/skip-validating-codeql-sarif</li>
<li><a
href="492d783245"><code>492d783</code></a>
Merge branch 'main' into henrymercer/skip-validating-codeql-sarif</li>
<li><a
href="83bdf3b7f9"><code>83bdf3b</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/2859">#2859</a>
from github/update-supported-enterprise-server-versions</li>
<li><a
href="cffc916774"><code>cffc916</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/2891">#2891</a>
from austinpray-mixpanel/patch-1</li>
<li><a
href="4420887272"><code>4420887</code></a>
Add deprecation warning for CodeQL 2.16.5 and earlier</li>
<li><a
href="4e178c5841"><code>4e178c5</code></a>
Update supported versions table in README</li>
<li>Additional commits viewable in <a
href="60168efe1c...ff0a06e83c">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github/codeql-action&package-manager=github_actions&previous-version=3.28.17&new-version=3.28.18)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 11:51:13 +01:00
dependabot[bot]
1346abf0e5
Bump docker/build-push-action from 6.16.0 to 6.17.0 (#3541)
Bumps
[docker/build-push-action](https://github.com/docker/build-push-action)
from 6.16.0 to 6.17.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/docker/build-push-action/releases">docker/build-push-action's
releases</a>.</em></p>
<blockquote>
<h2>v6.17.0</h2>
<ul>
<li>Bump <code>@​docker/actions-toolkit</code> from 0.59.0 to 0.61.0 by
<a href="https://github.com/crazy-max"><code>@​crazy-max</code></a> in
<a
href="https://redirect.github.com/docker/build-push-action/pull/1364">docker/build-push-action#1364</a></li>
</ul>
<blockquote>
<p>[!NOTE]
Build record is now exported using the <a
href="https://docs.docker.com/reference/cli/docker/buildx/history/export/"><code>buildx
history export</code></a> command instead of the legacy export-build
tool.</p>
</blockquote>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/docker/build-push-action/compare/v6.16.0...v6.17.0">https://github.com/docker/build-push-action/compare/v6.16.0...v6.17.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="1dc7386353"><code>1dc7386</code></a>
Merge pull request <a
href="https://redirect.github.com/docker/build-push-action/issues/1364">#1364</a>
from crazy-max/history-export-cmd</li>
<li><a
href="9c9803f364"><code>9c9803f</code></a>
chore: update generated content</li>
<li><a
href="db1f6c46e8"><code>db1f6c4</code></a>
DOCKER_BUILD_EXPORT_LEGACY env var to opt-in for legacy export</li>
<li><a
href="721e8c79de"><code>721e8c7</code></a>
Bump <code>@​docker/actions-toolkit</code> from 0.59.0 to 0.61.0</li>
<li>See full diff in <a
href="14487ce63c...1dc7386353">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=docker/build-push-action&package-manager=github_actions&previous-version=6.16.0&new-version=6.17.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 11:50:59 +01:00
35 changed files with 538 additions and 1260 deletions

View File

@ -180,7 +180,7 @@ jobs:
password: ${{ secrets.DOCKER_HUB_API }}
- name: Build and push PR-specific image
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
with:
context: .
file: ./Dockerfile

View File

@ -24,4 +24,4 @@ jobs:
- name: "Checkout Repository"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: "Dependency Review"
uses: actions/dependency-review-action@38ecb5b593bf0eb19e335c03f97670f792489a8b # v4.7.0
uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1

View File

@ -38,7 +38,7 @@ jobs:
java-version: "17"
distribution: "adopt"
- uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
- uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
- name: check the licenses for compatibility
run: ./gradlew clean checkLicense

View File

@ -68,7 +68,7 @@ jobs:
java-version: "21"
distribution: "temurin"
- uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
- uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
with:
gradle-version: 8.14
@ -156,7 +156,7 @@ jobs:
java-version: "21"
distribution: "temurin"
- uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
- uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
with:
gradle-version: 8.14

View File

@ -30,7 +30,7 @@ jobs:
java-version: "17"
distribution: "temurin"
- uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
- uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
with:
gradle-version: 8.14
@ -90,7 +90,7 @@ jobs:
- name: Build and push main Dockerfile
id: build-push-regular
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
@ -135,7 +135,7 @@ jobs:
- name: Build and push Dockerfile-ultra-lite
id: build-push-lite
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
if: github.ref != 'refs/heads/main'
with:
context: .
@ -166,7 +166,7 @@ jobs:
- name: Build and push main Dockerfile fat
id: build-push-fat
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
if: github.ref != 'refs/heads/main'
with:
builder: ${{ steps.buildx.outputs.name }}

View File

@ -35,7 +35,7 @@ jobs:
java-version: "17"
distribution: "temurin"
- uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
- uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
with:
gradle-version: 8.14

View File

@ -74,6 +74,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
with:
sarif_file: results.sarif

View File

@ -27,7 +27,7 @@ jobs:
fetch-depth: 0
- name: Setup Gradle
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
- name: Build and analyze with Gradle
env:

View File

@ -26,7 +26,7 @@ jobs:
java-version: "17"
distribution: "temurin"
- uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
- uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
- name: Generate Swagger documentation
run: ./gradlew generateOpenApiDocs

View File

@ -46,7 +46,7 @@ jobs:
password: ${{ secrets.DOCKER_HUB_API }}
- name: Build and push test image
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
with:
context: .
file: ./Dockerfile

24
AGENTS.md Normal file
View File

@ -0,0 +1,24 @@
# Codex Contribution Guidelines for Stirling-PDF
This file provides high-level instructions for Codex when modifying any files within this repository. Follow these rules to ensure changes remain consistent with the existing project structure.
## 1. Code Style and Formatting
- Respect the `.editorconfig` settings located in the repository root. Java files use 4 spaces; HTML, JS, and Python generally use 2 spaces. Lines should end with `LF`.
- Format Java code with `./gradlew spotlessApply` before committing.
- Review `DeveloperGuide.md` for project structure and design details before making significant changes.
## 2. Testing
- Run `./gradlew build` before committing changes to ensure the project compiles.
- If the build cannot complete due to environment restrictions, DO NOT COMMIT THE CHANGE
## 3. Commits
- Keep commits focused. Group related changes together and provide concise commit messages.
- Ensure the working tree is clean (`git status`) before concluding your work.
## 4. Pull Requests
- Summarize what was changed and why. Include build results from `./gradlew build` in the PR description.
- Note that the code was generated with the assistance of AI.
## 5. Translations
- Only modify `messages_en_GB.properties` when adding or updating translations.

View File

@ -10,7 +10,7 @@ plugins {
id "com.github.jk1.dependency-license-report" version "2.9"
//id "nebula.lint" version "19.0.3"
id("org.panteleyev.jpackageplugin") version "1.6.1"
id "org.sonarqube" version "6.1.0.5360"
id "org.sonarqube" version "6.2.0.5505"
}
import com.github.jk1.license.render.*
@ -24,7 +24,7 @@ ext {
imageioVersion = "3.12.0"
lombokVersion = "1.18.38"
bouncycastleVersion = "1.80"
springSecuritySamlVersion = "6.4.5"
springSecuritySamlVersion = "6.5.0"
openSamlVersion = "4.3.2"
tempJrePath = null
}
@ -434,7 +434,7 @@ dependencies {
}
//security updates
implementation "org.springframework:spring-webmvc:6.2.6"
implementation "org.springframework:spring-webmvc:6.2.7"
implementation("io.github.pixee:java-security-toolkit:1.2.1")
@ -459,7 +459,7 @@ dependencies {
implementation "org.springframework.boot:spring-boot-starter-mail:$springBootVersion"
implementation "org.springframework.session:spring-session-core:3.4.3"
implementation "org.springframework:spring-jdbc:6.2.6"
implementation "org.springframework:spring-jdbc:6.2.7"
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
// Don't upgrade h2database
@ -528,7 +528,7 @@ dependencies {
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
implementation "io.micrometer:micrometer-core:1.14.7"
implementation "io.micrometer:micrometer-core:1.15.0"
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
// https://mvnrepository.com/artifact/org.commonmark/commonmark
implementation "org.commonmark:commonmark:0.24.0"
@ -544,7 +544,7 @@ dependencies {
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
// Mockito (core)
testImplementation 'org.mockito:mockito-core:5.11.0'
testImplementation 'org.mockito:mockito-core:5.17.0'
testRuntimeOnly 'org.mockito:mockito-inline:5.2.0'

View File

@ -1,5 +1,5 @@
plugins {
// Apply the foojay-resolver plugin to allow automatic download of JDKs
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.10.0'
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
}
rootProject.name = 'Stirling-PDF'

View File

@ -49,10 +49,11 @@ public class KeygenLicenseVerifier {
private final ApplicationProperties applicationProperties;
// Shared HTTP client for connection pooling
private static final HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(java.time.Duration.ofSeconds(10))
.build();
private static final HttpClient httpClient =
HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(java.time.Duration.ofSeconds(10))
.build();
// License metadata context class to avoid shared mutable state
private static class LicenseContext {
@ -416,7 +417,9 @@ public class KeygenLicenseVerifier {
if (policyFloating) {
context.isFloatingLicense = true;
context.maxMachines = policyMaxMachines;
log.info("Policy defines floating license with max machines: {}", context.maxMachines);
log.info(
"Policy defines floating license with max machines: {}",
context.maxMachines);
}
// Extract max users and isEnterprise from policy or metadata
@ -474,7 +477,8 @@ public class KeygenLicenseVerifier {
activateMachine(licenseKey, licenseId, machineFingerprint, context);
if (activated) {
// Revalidate after activation
validationResponse = validateLicense(licenseKey, machineFingerprint, context);
validationResponse =
validateLicense(licenseKey, machineFingerprint, context);
isValid =
validationResponse != null
&& validationResponse
@ -494,8 +498,8 @@ public class KeygenLicenseVerifier {
}
}
private JsonNode validateLicense(String licenseKey, String machineFingerprint, LicenseContext context)
throws Exception {
private JsonNode validateLicense(
String licenseKey, String machineFingerprint, LicenseContext context) throws Exception {
String requestBody =
String.format(
"{\"meta\":{\"key\":\"%s\",\"scope\":{\"fingerprint\":\"%s\"}}}",
@ -514,7 +518,8 @@ public class KeygenLicenseVerifier {
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
log.info("ValidateLicenseResponse body: {}", response.body());
JsonNode jsonResponse = objectMapper.readTree(response.body());
if (response.statusCode() == 200) {
@ -534,8 +539,10 @@ public class KeygenLicenseVerifier {
context.isFloatingLicense = licenseAttrs.path("floating").asBoolean(false);
context.maxMachines = licenseAttrs.path("maxMachines").asInt(1);
log.info("License floating (from license): {}, maxMachines: {}",
context.isFloatingLicense, context.maxMachines);
log.info(
"License floating (from license): {}, maxMachines: {}",
context.isFloatingLicense,
context.maxMachines);
}
// Also check the policy for floating license support if included
@ -553,7 +560,8 @@ public class KeygenLicenseVerifier {
if (policyNode != null) {
// Check if this is a floating license from policy
boolean policyFloating = policyNode.path("attributes").path("floating").asBoolean(false);
boolean policyFloating =
policyNode.path("attributes").path("floating").asBoolean(false);
int policyMaxMachines = policyNode.path("attributes").path("maxMachines").asInt(1);
// Policy takes precedence over license attributes
@ -562,8 +570,10 @@ public class KeygenLicenseVerifier {
context.maxMachines = policyMaxMachines;
}
log.info("License floating (from policy): {}, maxMachines: {}",
context.isFloatingLicense, context.maxMachines);
log.info(
"License floating (from policy): {}, maxMachines: {}",
context.isFloatingLicense,
context.maxMachines);
}
// Extract user count, default to 1 if not specified
@ -593,11 +603,14 @@ public class KeygenLicenseVerifier {
return jsonResponse;
}
private boolean activateMachine(String licenseKey, String licenseId, String machineFingerprint,
LicenseContext context) throws Exception {
private boolean activateMachine(
String licenseKey, String licenseId, String machineFingerprint, LicenseContext context)
throws Exception {
// For floating licenses, we first need to check if we need to deregister any machines
if (context.isFloatingLicense) {
log.info("Processing floating license activation. Max machines allowed: {}", context.maxMachines);
log.info(
"Processing floating license activation. Max machines allowed: {}",
context.maxMachines);
// Get the current machines for this license
JsonNode machinesResponse = fetchMachinesForLicense(licenseKey, licenseId);
@ -605,17 +618,23 @@ public class KeygenLicenseVerifier {
JsonNode machines = machinesResponse.path("data");
int currentMachines = machines.size();
log.info("Current machine count: {}, Max allowed: {}", currentMachines, context.maxMachines);
log.info(
"Current machine count: {}, Max allowed: {}",
currentMachines,
context.maxMachines);
// Check if the current fingerprint is already activated
boolean isCurrentMachineActivated = false;
String currentMachineId = null;
for (JsonNode machine : machines) {
if (machineFingerprint.equals(machine.path("attributes").path("fingerprint").asText())) {
if (machineFingerprint.equals(
machine.path("attributes").path("fingerprint").asText())) {
isCurrentMachineActivated = true;
currentMachineId = machine.path("id").asText();
log.info("Current machine is already activated with ID: {}", currentMachineId);
log.info(
"Current machine is already activated with ID: {}",
currentMachineId);
break;
}
}
@ -628,7 +647,8 @@ public class KeygenLicenseVerifier {
// If we've reached the max machines limit, we need to deregister the oldest machine
if (currentMachines >= context.maxMachines) {
log.info("Max machines reached. Deregistering oldest machine to make room for the new machine.");
log.info(
"Max machines reached. Deregistering oldest machine to make room for the new machine.");
// Find the oldest machine based on creation timestamp
if (machines.size() > 0) {
@ -637,23 +657,28 @@ public class KeygenLicenseVerifier {
java.time.Instant oldestTime = null;
for (JsonNode machine : machines) {
String createdStr = machine.path("attributes").path("created").asText(null);
String createdStr =
machine.path("attributes").path("created").asText(null);
if (createdStr != null && !createdStr.isEmpty()) {
try {
java.time.Instant createdTime = java.time.Instant.parse(createdStr);
java.time.Instant createdTime =
java.time.Instant.parse(createdStr);
if (oldestTime == null || createdTime.isBefore(oldestTime)) {
oldestTime = createdTime;
oldestMachineId = machine.path("id").asText();
}
} catch (Exception e) {
log.warn("Could not parse creation time for machine: {}", e.getMessage());
log.warn(
"Could not parse creation time for machine: {}",
e.getMessage());
}
}
}
// If we couldn't determine the oldest by timestamp, use the first one
if (oldestMachineId == null) {
log.warn("Could not determine oldest machine by timestamp, using first machine in list");
log.warn(
"Could not determine oldest machine by timestamp, using first machine in list");
oldestMachineId = machines.path(0).path("id").asText();
}
@ -661,12 +686,15 @@ public class KeygenLicenseVerifier {
boolean deregistered = deregisterMachine(licenseKey, oldestMachineId);
if (!deregistered) {
log.error("Failed to deregister machine. Cannot proceed with activation.");
log.error(
"Failed to deregister machine. Cannot proceed with activation.");
return false;
}
log.info("Machine deregistered successfully. Proceeding with activation of new machine.");
log.info(
"Machine deregistered successfully. Proceeding with activation of new machine.");
} else {
log.error("License has reached machine limit but no machines were found to deregister. This is unexpected.");
log.error(
"License has reached machine limit but no machines were found to deregister. This is unexpected.");
// We'll still try to activate, but it might fail
}
}
@ -720,7 +748,8 @@ public class KeygenLicenseVerifier {
.POST(HttpRequest.BodyPublishers.ofString(body.toString()))
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
log.info("activateMachine Response body: " + response.body());
if (response.statusCode() == 201) {
log.info("Machine activated successfully");
@ -748,22 +777,33 @@ public class KeygenLicenseVerifier {
* @throws Exception if an error occurs during the HTTP request
*/
private JsonNode fetchMachinesForLicense(String licenseKey, String licenseId) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/" + ACCOUNT_ID + "/licenses/" + licenseId + "/machines"))
.header("Content-Type", "application/vnd.api+json")
.header("Accept", "application/vnd.api+json")
.header("Authorization", "License " + licenseKey)
.GET()
.build();
HttpRequest request =
HttpRequest.newBuilder()
.uri(
URI.create(
BASE_URL
+ "/"
+ ACCOUNT_ID
+ "/licenses/"
+ licenseId
+ "/machines"))
.header("Content-Type", "application/vnd.api+json")
.header("Accept", "application/vnd.api+json")
.header("Authorization", "License " + licenseKey)
.GET()
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
log.info("fetchMachinesForLicense Response body: {}", response.body());
if (response.statusCode() == 200) {
return objectMapper.readTree(response.body());
} else {
log.error("Error fetching machines for license. Status code: {}, error: {}",
response.statusCode(), response.body());
log.error(
"Error fetching machines for license. Status code: {}, error: {}",
response.statusCode(),
response.body());
return null;
}
}
@ -777,22 +817,26 @@ public class KeygenLicenseVerifier {
*/
private boolean deregisterMachine(String licenseKey, String machineId) {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/" + ACCOUNT_ID + "/machines/" + machineId))
.header("Content-Type", "application/vnd.api+json")
.header("Accept", "application/vnd.api+json")
.header("Authorization", "License " + licenseKey)
.DELETE()
.build();
HttpRequest request =
HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/" + ACCOUNT_ID + "/machines/" + machineId))
.header("Content-Type", "application/vnd.api+json")
.header("Accept", "application/vnd.api+json")
.header("Authorization", "License " + licenseKey)
.DELETE()
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 204) {
log.info("Machine {} successfully deregistered", machineId);
return true;
} else {
log.error("Error deregistering machine. Status code: {}, error: {}",
response.statusCode(), response.body());
log.error(
"Error deregistering machine. Status code: {}, error: {}",
response.statusCode(),
response.body());
return false;
}
} catch (Exception e) {

View File

@ -31,7 +31,8 @@ public class LibreOfficeListener {
log.info("waiting for listener to start");
try (Socket socket = new Socket()) {
socket.connect(
new InetSocketAddress("localhost", 2002), 1000); // Timeout after 1 second
new InetSocketAddress("localhost", LISTENER_PORT),
1000); // Timeout after 1 second
return true;
} catch (Exception e) {
return false;

View File

@ -11,8 +11,11 @@ import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver;
import org.thymeleaf.templateresource.FileTemplateResource;
import org.thymeleaf.templateresource.ITemplateResource;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.InputStreamTemplateResource;
@Slf4j
public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateResolver {
private final ResourceLoader resourceLoader;
@ -40,7 +43,8 @@ public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateRe
return new FileTemplateResource(resource.getFile().getPath(), characterEncoding);
}
} catch (IOException e) {
// Log the exception to help with debugging issues loading external templates
log.warn("Unable to read template '{}' from file system", resourceName, e);
}
InputStream inputStream =

View File

@ -93,6 +93,7 @@ public class PipelineProcessor {
ByteArrayOutputStream logStream = new ByteArrayOutputStream();
PrintStream logPrintStream = new PrintStream(logStream);
boolean hasErrors = false;
boolean filtersApplied = false;
for (PipelineOperation pipelineOperation : config.getOperations()) {
String operation = pipelineOperation.getOperation();
boolean isMultiInputOperation = apiDocService.isMultiInput(operation);
@ -134,7 +135,7 @@ public class PipelineProcessor {
if (operation.startsWith("filter-")
&& (response.getBody() == null
|| response.getBody().length == 0)) {
result.setFiltersApplied(true);
filtersApplied = true;
log.info("Skipping file due to filtering {}", operation);
continue;
}
@ -215,12 +216,12 @@ public class PipelineProcessor {
log.error("Errors occurred during processing. Log: {}", logStream.toString());
}
result.setHasErrors(hasErrors);
result.setFiltersApplied(hasErrors);
result.setFiltersApplied(filtersApplied);
result.setOutputFiles(outputFiles);
return result;
}
private ResponseEntity<byte[]> sendWebRequest(String url, MultiValueMap<String, Object> body) {
/* package */ ResponseEntity<byte[]> sendWebRequest(String url, MultiValueMap<String, Object> body) {
RestTemplate restTemplate = new RestTemplate();
// Set up headers, including API key
HttpHeaders headers = new HttpHeaders();

View File

@ -77,9 +77,8 @@ public class HomeWebController {
}
@GetMapping("/home-legacy")
public String homeLegacy(Model model) {
model.addAttribute("currentPage", "home-legacy");
return "home-legacy";
public String redirectHomeLegacy() {
return "redirect:/";
}
@GetMapping(value = "/robots.txt", produces = MediaType.TEXT_PLAIN_VALUE)

View File

@ -39,7 +39,6 @@ public class InputStreamTemplateResource implements ITemplateResource {
@Override
public boolean exists() {
// TODO Auto-generated method stub
return false;
return inputStream != null;
}
}

View File

@ -553,7 +553,7 @@
{
"moduleName": "io.micrometer:micrometer-core",
"moduleUrl": "https://github.com/micrometer-metrics/micrometer",
"moduleVersion": "1.14.7",
"moduleVersion": "1.15.0",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
@ -1637,7 +1637,7 @@
{
"moduleName": "org.springframework.security:spring-security-saml2-service-provider",
"moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.4.5",
"moduleVersion": "6.5.0",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
@ -1714,7 +1714,7 @@
{
"moduleName": "org.springframework:spring-jdbc",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.2.6",
"moduleVersion": "6.2.7",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
@ -1742,7 +1742,7 @@
{
"moduleName": "org.springframework:spring-webmvc",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.2.6",
"moduleVersion": "6.2.7",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},

View File

@ -1,229 +0,0 @@
#searchBar {
color: var(--md-sys-color-on-surface);
background-color: var(--md-sys-color-surface-container-low);
width: 100%;
font-size: 16px;
margin-bottom: 2rem;
padding: 0.75rem 3.5rem;
border: 1px solid var(--md-sys-color-outline-variant);
border-radius: 3rem;
outline-color: var(--md-sys-color-outline-variant);
}
#filtersContainer {
display: flex;
width: 100%;
align-items: center;
justify-content: center;
gap: 10px;
}
.filter-button {
color: var(--md-sys-color-secondary);
user-select: none;
cursor: pointer;
transition: transform 0.3s;
transform-origin: center center;
}
.filter-button:hover {
transform: scale(1.08);
}
.search-icon {
position: absolute;
margin: 0.75rem 1rem;
border: 0.1rem solid transparent;
}
.features-container {
display: flex;
flex-direction: column;
gap: 30px;
}
.feature-group-legacy {
display: flex;
flex-direction: column;
}
.feature-group-header {
display: flex;
align-items: center;
justify-content: flex-start;
color: var(--md-sys-color-on-surface);
margin-bottom: 15px;
user-select: none;
cursor: pointer;
gap: 10px;
}
.feature-group-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(15rem, 3fr));
gap: 30px 30px;
overflow: hidden;
margin: -20px;
padding: 20px;
box-sizing:content-box;
}
.feature-group-container.animated-group {
transition: 0.5s all;
}
.feature-group-legacy.collapsed>.feature-group-container {
max-height: 0 !important;
margin: 0;
padding: 0;
}
.header-expand-button {
transition: 0.5s all;
transform: rotate(90deg);
}
.header-expand-button.collapsed {
transform: rotate(0deg);
}
.feature-card {
border: 1px solid var(--md-sys-color-surface-5);
border-radius: 1.75rem;
padding: 1.25rem;
display: flex;
flex-direction: column;
align-items: flex-start;
background: var(--md-sys-color-surface-5);
transition:
transform 0.3s,
border 0.3s;
transform-origin: center center;
outline: 0px solid transparent;
position:relative;
}
.feature-card a {
text-decoration: none;
color: var(--md-sys-color-on-surface);
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
.feature-card .card-text {
font-size: .875rem;
}
.feature-card:hover {
cursor: pointer;
transform: scale(1.08);
box-shadow: var(--md-sys-elevation-2);
}
.card-title.text-primary {
color: #000;
}
.home-card-icon {
width: 3rem;
height: 3rem;
transform: translateY(-5px);
}
.favorite-icon {
display: none !important;
position: absolute;
top: 10px;
right: 10px;
color: var(--md-sys-color-secondary);
}
#tool-icon {
height: 100%;
}
#tool-text {
margin: 0.0rem 0 0 1.25rem;
}
.card-title {
margin-bottom: 1rem;
font-size: 1.1rem;
}
/* Only show the favorite icons when the parent card is being hovered over */
.feature-card:hover .favorite-icon {
display: block !important;
}
.favorite-icon img {
filter: brightness(0) invert(var(--md-theme-filter-color));
}
.favorite-icon:hover .material-symbols-rounded {
transform: scale(1.2);
}
.favorite-icon .material-symbols-rounded.fill{
color: #f5c000;
}
.jumbotron {
padding: 3rem 3rem;
/* Reduce vertical padding */
}
.lookatme {
opacity: 1;
position: relative;
display: inline-block;
}
.lookatme::after {
color: #e33100;
text-shadow: 0 0 5px #e33100;
/* in the html, the data-lookatme-text attribute must */
/* contain the same text as the .lookatme element */
content: attr(data-lookatme-text);
padding: inherit;
position: absolute;
inset: 0 0 0 0;
z-index: 1;
/* 20 steps / 2 seconds = 10fps */
-webkit-animation: 2s infinite Pulse steps(20);
animation: 2s infinite Pulse steps(20);
}
@keyframes Pulse {
from {
opacity: 0;
}
50% {
opacity: 1;
}
to {
opacity: 0;
}
}
.update-notice {
animation: scale 1s infinite alternate;
}
@keyframes scale {
0% {
transform: scale(0.96);
}
100% {
transform: scale(1);
}
}
.hidden {
visibility: hidden;
}

View File

@ -126,11 +126,7 @@ function addToFavorites(entryId) {
localStorage.setItem('favoritesList', JSON.stringify(favoritesList));
updateFavoritesDropdown();
updateFavoriteIcons();
const currentPath = window.location.pathname;
if (currentPath.includes('home-legacy')) {
syncFavoritesLegacy();
} else {
initializeCards();
}
}
}

View File

@ -1,266 +0,0 @@
function filterCardsLegacy() {
var input = document.getElementById('searchBar');
var filter = input.value.toUpperCase();
let featureGroups = document.querySelectorAll('.feature-group-legacy');
const collapsedGroups = getCollapsedGroups();
for (const featureGroup of featureGroups) {
var cards = featureGroup.querySelectorAll('.feature-card');
let groupMatchesFilter = false;
for (var i = 0; i < cards.length; i++) {
var card = cards[i];
var title = card.querySelector('h5.card-title').innerText;
var text = card.querySelector('p.card-text').innerText;
// Get the navbar tags associated with the card
var navbarItem = document.querySelector(`a.dropdown-item[href="${card.id}"]`);
var navbarTags = navbarItem ? navbarItem.getAttribute('data-bs-tags') : '';
var content = title + ' ' + text + ' ' + navbarTags;
if (content.toUpperCase().indexOf(filter) > -1) {
card.style.display = '';
groupMatchesFilter = true;
} else {
card.style.display = 'none';
}
}
if (!groupMatchesFilter) {
featureGroup.style.display = 'none';
} else {
featureGroup.style.display = '';
resetOrTemporarilyExpandGroup(featureGroup, filter, collapsedGroups);
}
}
}
function getCollapsedGroups() {
return localStorage.getItem('collapsedGroups') ? JSON.parse(localStorage.getItem('collapsedGroups')) : [];
}
function resetOrTemporarilyExpandGroup(featureGroup, filterKeywords = '', collapsedGroups = []) {
const shouldResetCollapse = filterKeywords.trim() === '';
if (shouldResetCollapse) {
// Resetting the group's expand/collapse to its original state (as in collapsed groups)
const isCollapsed = collapsedGroups.indexOf(featureGroup.id) != -1;
expandCollapseToggle(featureGroup, !isCollapsed);
} else {
// Temporarily expands feature group without affecting the actual/stored collapsed groups
featureGroup.classList.remove('collapsed');
featureGroup.querySelector('.header-expand-button').classList.remove('collapsed');
}
}
function updateFavoritesSectionLegacy() {
const favoritesContainer = document.getElementById('groupFavorites').querySelector('.feature-group-container');
favoritesContainer.innerHTML = '';
const cards = Array.from(document.querySelectorAll('.feature-card:not(.duplicate)'));
const addedCardIds = new Set();
let favoritesAmount = 0;
cards.forEach((card) => {
const favouritesList = JSON.parse(localStorage.getItem('favoritesList') || '[]');
if (favouritesList.includes(card.id) && !addedCardIds.has(card.id)) {
const duplicate = card.cloneNode(true);
duplicate.classList.add('duplicate');
favoritesContainer.appendChild(duplicate);
addedCardIds.add(card.id);
favoritesAmount++;
}
});
if (favoritesAmount === 0) {
document.getElementById('groupFavorites').style.display = 'none';
} else {
document.getElementById('groupFavorites').style.display = 'flex';
}
reorderCards(favoritesContainer);
}
function syncFavoritesLegacy() {
const cards = Array.from(document.querySelectorAll('.feature-card'));
cards.forEach((card) => {
const isFavorite = localStorage.getItem(card.id) === 'favorite';
const starIcon = card.querySelector('.favorite-icon span.material-symbols-rounded');
if (starIcon) {
if (isFavorite) {
starIcon.classList.remove('no-fill');
starIcon.classList.add('fill');
card.classList.add('favorite');
} else {
starIcon.classList.remove('fill');
starIcon.classList.add('no-fill');
card.classList.remove('favorite');
}
}
});
updateFavoritesSectionLegacy();
updateFavoritesDropdown();
filterCardsLegacy();
}
function reorderCards(container) {
var cards = Array.from(container.querySelectorAll('.feature-card'));
cards.forEach(function (card) {
container.removeChild(card);
});
cards.sort(function (a, b) {
var aIsFavorite = localStorage.getItem(a.id) === 'favorite';
var bIsFavorite = localStorage.getItem(b.id) === 'favorite';
if (a.id === 'update-link') {
return -1;
}
if (b.id === 'update-link') {
return 1;
}
if (aIsFavorite && !bIsFavorite) {
return -1;
} else if (!aIsFavorite && bIsFavorite) {
return 1;
} else {
return a.id > b.id;
}
});
cards.forEach(function (card) {
container.appendChild(card);
});
}
function reorderAllCards() {
const containers = Array.from(document.querySelectorAll('.feature-group-container'));
containers.forEach(function (container) {
reorderCards(container);
});
}
function initializeCardsLegacy() {
reorderAllCards();
updateFavoritesSectionLegacy();
updateFavoritesDropdown();
filterCardsLegacy();
}
function showFavoritesOnly() {
const groups = Array.from(document.querySelectorAll('.feature-group-legacy'));
if (localStorage.getItem('favoritesOnly') === 'true') {
groups.forEach((group) => {
if (group.id !== 'groupFavorites') {
group.style.display = 'none';
}
});
} else {
groups.forEach((group) => {
if (group.id !== 'groupFavorites') {
group.style.display = 'flex';
}
});
}
}
function toggleFavoritesOnly() {
if (localStorage.getItem('favoritesOnly') === 'true') {
localStorage.setItem('favoritesOnly', 'false');
} else {
localStorage.setItem('favoritesOnly', 'true');
}
showFavoritesOnly();
}
// Expands a feature group on true, collapses it on false and toggles state on null.
function expandCollapseToggle(group, expand = null) {
if (expand === null) {
group.classList.toggle('collapsed');
group.querySelector('.header-expand-button').classList.toggle('collapsed');
} else if (expand) {
group.classList.remove('collapsed');
group.querySelector('.header-expand-button').classList.remove('collapsed');
} else {
group.classList.add('collapsed');
group.querySelector('.header-expand-button').classList.add('collapsed');
}
const collapsed = localStorage.getItem('collapsedGroups') ? JSON.parse(localStorage.getItem('collapsedGroups')) : [];
const groupIndex = collapsed.indexOf(group.id);
if (group.classList.contains('collapsed')) {
if (groupIndex === -1) {
collapsed.push(group.id);
}
} else {
if (groupIndex !== -1) {
collapsed.splice(groupIndex, 1);
}
}
localStorage.setItem('collapsedGroups', JSON.stringify(collapsed));
}
function expandCollapseAll(expandAll) {
const groups = Array.from(document.querySelectorAll('.feature-group-legacy'));
groups.forEach((group) => {
expandCollapseToggle(group, expandAll);
});
}
window.onload = function () {
initializeCardsLegacy();
syncFavoritesLegacy(); // Ensure everything is in sync on page load
};
document.addEventListener('DOMContentLoaded', function () {
const materialIcons = new FontFaceObserver('Material Symbols Rounded');
materialIcons
.load()
.then(() => {
document.querySelectorAll('.feature-card.hidden').forEach((el) => {
el.classList.remove('hidden');
});
})
.catch(() => {
console.error('Material Symbols Rounded font failed to load.');
});
Array.from(document.querySelectorAll('.feature-group-header-legacy')).forEach((header) => {
const parent = header.parentNode;
const container = header.parentNode.querySelector('.feature-group-container');
if (parent.id !== 'groupFavorites') {
// container.style.maxHeight = container.scrollHeight + 'px';
}
header.onclick = () => {
expandCollapseToggle(parent);
};
});
const collapsed = localStorage.getItem('collapsedGroups') ? JSON.parse(localStorage.getItem('collapsedGroups')) : [];
const groupsArray = Array.from(document.querySelectorAll('.feature-group-legacy'));
groupsArray.forEach((group) => {
if (collapsed.indexOf(group.id) !== -1) {
expandCollapseToggle(group, false);
}
});
// Necessary in order to not fire the transition animation on page load, which looks wrong.
// The timeout isn't doing anything visible to the user, so it's not making the page load look slower.
setTimeout(() => {
groupsArray.forEach((group) => {
const container = group.querySelector('.feature-group-container');
container.classList.add('animated-group');
});
}, 500);
Array.from(document.querySelectorAll('.feature-group-header')).forEach((header) => {
const parent = header.parentNode;
header.onclick = () => {
expandCollapseToggle(parent);
};
});
showFavoritesOnly();
});

View File

@ -55,10 +55,6 @@ hideCookieBanner();
updateFavoriteIcons();
const contentPath = /*[[${@contextPath}]]*/ '';
const defaultView = localStorage.getItem('defaultView') || 'home'; // Default to "home"
if (defaultView === 'home-legacy') {
window.location.href = contentPath + 'home-legacy'; // Redirect to legacy view
}
document.addEventListener('DOMContentLoaded', function () {
const surveyVersion = '3.0';

View File

@ -1,6 +0,0 @@
<div th:fragment="featureGroupHeader" class="feature-group-header">
<h3 class="menu-title" th:text="${groupTitle}"></h3>
<span class="material-symbols-rounded header-expand-button">
chevron_right
</span>
</div>

View File

@ -1,528 +0,0 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title='')}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<!-- Jumbotron -->
<div class="p-5 rounded d-none d-md-block" id="jumbotron">
<div class="container">
<h1 class="display-4 fw-normal" th:text="${@appName}"></h1>
<p class="lead fs-4"
th:text="${@homeText != 'null' and @homeText != null and @homeText != ''} ? ${@homeText} : #{home.desc}">
</p>
</div>
</div>
<br class="d-md-none">
<!-- Features -->
<script th:src="@{'/js/homecard-legacy.js'}"></script>
<div class=" container">
<br>
<span class="material-symbols-rounded search-icon">
search
</span>
<input type="text" id="searchBar" onkeyup="filterCardsLegacy()" th:placeholder="#{home.searchBar}" autofocus>
<div style="display: flex; align-items: center;">
<a href="home" onclick="setAsDefault('home')"
style="text-decoration: none; color: inherit; cursor: pointer; display: flex; align-items: center;">
<span th:text="#{home.newHomePage}">
</span>
<span class="material-symbols-rounded toggle-favourites" style="font-size: 2rem; margin-left: 0.2rem;">
home
</span>
</a>
</div>
<div id="filtersContainer">
<span class="material-symbols-rounded filter-button" onclick="toggleFavoritesOnly()">
star
</span>
<span class="material-symbols-rounded filter-button" onclick="expandCollapseAll(true)">
expand_all
</span>
<span class="material-symbols-rounded filter-button" onclick="expandCollapseAll(false)">
collapse_all
</span>
<span class="material-symbols-rounded filter-button hidden" onclick="switchViewMode()">
dashboard
</span>
</div>
<div class="features-container">
<div th:if="${@shouldShow}" class="feature-card favorite update-notice visually-hidden" id="update-link-legacy">
<a href="https://github.com/Stirling-Tools/Stirling-PDF/releases" target="_blank" rel="noopener">
<div class="d-flex align-items-center">
<div id="tool-icon" class="advance" alt="icon">
<span class="material-symbols-rounded nav-icon">update</span>
</div>
<div id="tool-text">
<h5 class="card-title" th:text="#{settings.update}"></h5>
<p class="card-text" id="app-update"></p>
</div>
</div>
</a>
</div>
<div id="groupFavorites" class="feature-group-legacy">
<div
th:replace="~{fragments/featureGroupHeaderLegacy :: featureGroupHeader(groupTitle=#{navbar.favorite})}">
</div>
<div class="feature-group-container">
</div>
</div>
<div id="popularTools" class="feature-group-legacy">
<div
th:replace="~{fragments/featureGroupHeaderLegacy :: featureGroupHeader(groupTitle=#{navbar.sections.popular})}">
</div>
<div class="feature-group-container">
<div
th:replace="~{fragments/card :: card(id='view-pdf', cardTitle=#{home.viewPdf.title}, cardText=#{home.viewPdf.desc}, cardLink='view-pdf', toolIcon='menu_book', tags=#{viewPdf.tags}, toolGroup='other')}">
</div>
<div
th:replace="~{fragments/card :: card(id='multi-tool', cardTitle=#{home.multiTool.title}, cardText=#{home.multiTool.desc}, cardLink='multi-tool', toolIcon='construction', tags=#{multiTool.tags}, toolGroup='organize')}">
</div>
<div
th:replace="~{fragments/card :: card(id='pipeline', cardTitle=#{home.pipeline.title}, cardText=#{home.pipeline.desc}, cardLink='pipeline', toolIcon='family_history', tags=#{pipeline.tags}, toolGroup='advance')}">
</div>
<div
th:replace="~{fragments/card :: card(id='compress-pdf', cardTitle=#{home.compressPdfs.title}, cardText=#{home.compressPdfs.desc}, cardLink='compress-pdf', toolIcon='zoom_in_map', tags=#{compressPdfs.tags}, toolGroup='advance')}">
</div>
</div>
</div>
<div id="groupOrganize" class="feature-group-legacy">
<div
th:replace="~{fragments/featureGroupHeaderLegacy :: featureGroupHeader(groupTitle=#{navbar.sections.organize})}">
</div>
<div class="feature-group-container">
<div
th:replace="~{fragments/card :: card(id='multi-tool', cardTitle=#{home.multiTool.title}, cardText=#{home.multiTool.desc}, cardLink='multi-tool', toolIcon='construction', tags=#{multiTool.tags}, toolGroup='organize')}">
</div>
<div
th:replace="~{fragments/card :: card(id='merge-pdfs', cardTitle=#{home.merge.title}, cardText=#{home.merge.desc}, cardLink='merge-pdfs', toolIcon='add_to_photos', tags=#{merge.tags}, toolGroup='organize')}">
</div>
<div
th:replace="~{fragments/card :: card(id='split-pdfs', cardTitle=#{home.split.title}, cardText=#{home.split.desc}, cardLink='split-pdfs', toolIcon='cut', tags=#{split.tags}, toolGroup='organize')}">
</div>
<div
th:replace="~{fragments/card :: card(id='rotate-pdf', cardTitle=#{home.rotate.title}, cardText=#{home.rotate.desc}, cardLink='rotate-pdf', toolIcon='rotate_right', tags=#{rotate.tags}, toolGroup='organize')}">
</div>
<div
th:replace="~{fragments/card :: card(id='crop', cardTitle=#{home.crop.title}, cardText=#{home.crop.desc}, cardLink='crop', toolIcon='crop', tags=#{crop.tags}, toolGroup='organize')}">
</div>
<div
th:replace="~{fragments/card :: card(id='pdf-organizer', cardTitle=#{home.pdfOrganiser.title}, cardText=#{home.pdfOrganiser.desc}, cardLink='pdf-organizer', toolIcon='format_list_bulleted', tags=#{pdfOrganiser.tags}, toolGroup='organize')}">
</div>
<div
th:replace="~{fragments/card :: card(id='remove-pages', cardTitle=#{home.removePages.title}, cardText=#{home.removePages.desc}, cardLink='remove-pages', toolIcon='delete', tags=#{removePages.tags}, toolGroup='organize')}">
</div>
<div
th:replace="~{fragments/card :: card(id='multi-page-layout', cardTitle=#{home.pageLayout.title}, cardText=#{home.pageLayout.desc}, cardLink='multi-page-layout', toolIcon='dashboard', tags=#{pageLayout.tags}, toolGroup='organize')}">
</div>
<div
th:replace="~{fragments/card :: card(id='scale-pages', cardTitle=#{home.scalePages.title}, cardText=#{home.scalePages.desc}, cardLink='scale-pages', toolIcon='fullscreen', tags=#{scalePages.tags}, toolGroup='organize')}">
</div>
<div
th:replace="~{fragments/card :: card(id='extract-page', cardTitle=#{home.extractPage.title}, cardText=#{home.extractPage.desc}, cardLink='extract-page', toolIcon='upload', tags=#{extractPage.tags}, toolGroup='organize')}">
</div>
<div
th:replace="~{fragments/card :: card(id='pdf-to-single-page', cardTitle=#{home.PdfToSinglePage.title}, cardText=#{home.PdfToSinglePage.desc}, cardLink='pdf-to-single-page', toolIcon='looks_one', tags=#{PdfToSinglePage.tags}, toolGroup='organize')}">
</div>
</div>
</div>
<div id="groupConvertTo" class="feature-group-legacy">
<div
th:replace="~{fragments/featureGroupHeaderLegacy :: featureGroupHeader(groupTitle=#{navbar.sections.convertTo})}">
</div>
<div class="feature-group-container">
<div
th:replace="~{fragments/card :: card(id='img-to-pdf', cardTitle=#{home.imageToPdf.title}, cardText=#{home.imageToPdf.desc}, cardLink='img-to-pdf', toolIcon='picture_as_pdf', tags=#{imageToPdf.tags}, toolGroup='image')}">
</div>
<div
th:replace="~{fragments/card :: card(id='file-to-pdf', cardTitle=#{home.fileToPDF.title}, cardText=#{home.fileToPDF.desc}, cardLink='file-to-pdf', toolIcon='draft', tags=#{fileToPDF.tags}, toolGroup='convert')}">
</div>
<div
th:replace="~{fragments/card :: card(id='url-to-pdf', cardTitle=#{home.URLToPDF.title}, cardText=#{home.URLToPDF.desc}, cardLink='url-to-pdf', toolIcon='link', tags=#{URLToPDF.tags}, toolGroup='convert')}">
</div>
<div
th:replace="~{fragments/card :: card(id='html-to-pdf', cardTitle=#{home.HTMLToPDF.title}, cardText=#{home.HTMLToPDF.desc}, cardLink='html-to-pdf', toolIcon='html', tags=#{HTMLToPDF.tags}, toolGroup='convert')}">
</div>
<div
th:replace="~{fragments/card :: card(id='markdown-to-pdf', cardTitle=#{home.MarkdownToPDF.title}, cardText=#{home.MarkdownToPDF.desc}, cardLink='markdown-to-pdf', toolIcon='markdown', tags=#{MarkdownToPDF.tags}, toolGroup='convert')}">
</div>
</div>
</div>
<div id="groupConvertFrom" class="feature-group-legacy">
<div
th:replace="~{fragments/featureGroupHeaderLegacy :: featureGroupHeader(groupTitle=#{navbar.sections.convertFrom})}">
</div>
<div class="feature-group-container">
<div
th:replace="~{fragments/card :: card(id='pdf-to-img', cardTitle=#{home.pdfToImage.title}, cardText=#{home.pdfToImage.desc}, cardLink='pdf-to-img', toolIcon='photo_library', tags=#{pdfToImage.tags}, toolGroup='image')}">
</div>
<div
th:replace="~{fragments/card :: card(id='pdf-to-pdfa', cardTitle=#{home.pdfToPDFA.title}, cardText=#{home.pdfToPDFA.desc}, cardLink='pdf-to-pdfa', toolIcon='picture_as_pdf', tags=#{pdfToPDFA.tags}, toolGroup='convert')}">
</div>
<div
th:replace="~{fragments/card :: card(id='pdf-to-word', cardTitle=#{home.PDFToWord.title}, cardText=#{home.PDFToWord.desc}, cardLink='pdf-to-word', toolIcon='description', tags=#{PDFToWord.tags}, toolGroup='word')}">
</div>
<div
th:replace="~{fragments/card :: card(id='pdf-to-presentation', cardTitle=#{home.PDFToPresentation.title}, cardText=#{home.PDFToPresentation.desc}, cardLink='pdf-to-presentation', toolIcon='slideshow', tags=#{PDFToPresentation.tags}, toolGroup='ppt')}">
</div>
<div
th:replace="~{fragments/card :: card(id='pdf-to-text', cardTitle=#{home.PDFToText.title}, cardText=#{home.PDFToText.desc}, cardLink='pdf-to-text', toolIcon='text_fields', tags=#{PDFToText.tags}, toolGroup='convert')}">
</div>
<div
th:replace="~{fragments/card :: card(id='pdf-to-html', cardTitle=#{home.PDFToHTML.title}, cardText=#{home.PDFToHTML.desc}, cardLink='pdf-to-html', toolIcon='html', tags=#{PDFToHTML.tags}, toolGroup='convert')}">
</div>
<div
th:replace="~{fragments/card :: card(id='pdf-to-xml', cardTitle=#{home.PDFToXML.title}, cardText=#{home.PDFToXML.desc}, cardLink='pdf-to-xml', toolIcon='code', tags=#{PDFToXML.tags}, toolGroup='convert')}">
</div>
<div
th:replace="~{fragments/card :: card(id='pdf-to-csv', cardTitle=#{home.tableExtraxt.title}, cardText=#{home.tableExtraxt.desc}, cardLink='pdf-to-csv', toolIcon='csv', tags=#{tableExtraxt.tags}, toolGroup='convert')}">
</div>
</div>
</div>
<div id="groupSecurity" class="feature-group-legacy">
<div
th:replace="~{fragments/featureGroupHeaderLegacy :: featureGroupHeader(groupTitle=#{navbar.sections.security})}">
</div>
<div class="feature-group-container">
<div
th:replace="~{fragments/card :: card(id='add-password', cardTitle=#{home.addPassword.title}, cardText=#{home.addPassword.desc}, cardLink='add-password', toolIcon='lock', tags=#{addPassword.tags}, toolGroup='security')}">
</div>
<div
th:replace="~{fragments/card :: card(id='remove-password', cardTitle=#{home.removePassword.title}, cardText=#{home.removePassword.desc}, cardLink='remove-password', toolIcon='lock_open_right', tags=#{removePassword.tags}, toolGroup='security')}">
</div>
<div
th:replace="~{fragments/card :: card(id='change-permissions', cardTitle=#{home.permissions.title}, cardText=#{home.permissions.desc}, cardLink='change-permissions', toolIcon='encrypted', tags=#{permissions.tags}, toolGroup='security')}">
</div>
<div
th:replace="~{fragments/card :: card(id='sign', cardTitle=#{home.sign.title}, cardText=#{home.sign.desc}, cardLink='sign', toolIcon='signature', tags=#{sign.tags}, toolGroup='sign')}">
</div>
<div
th:replace="~{fragments/card :: card(id='cert-sign', cardTitle=#{home.certSign.title}, cardText=#{home.certSign.desc}, cardLink='cert-sign', toolIcon='workspace_premium', tags=#{certSign.tags}, toolGroup='security')}">
</div>
<div
th:replace="~{fragments/card :: card(id='validate-signature', cardTitle=#{home.validateSignature.title}, cardText=#{home.validateSignature.desc}, cardLink='validate-signature', toolIcon='verified', tags=#{validateSignature.tags}, toolGroup='security')}">
</div>
<div
th:replace="~{fragments/card :: card(id='remove-cert-sign', cardTitle=#{home.removeCertSign.title}, cardText=#{home.removeCertSign.desc}, cardLink='remove-cert-sign', toolIcon='remove_moderator', tags=#{removeCertSign.tags}, toolGroup='security')}">
</div>
<div
th:replace="~{fragments/card :: card(id='sanitize-pdf', cardTitle=#{home.sanitizePdf.title}, cardText=#{home.sanitizePdf.desc}, cardLink='sanitize-pdf', toolIcon='sanitizer', tags=#{sanitizePdf.tags}, toolGroup='security')}">
</div>
<div
th:replace="~{fragments/card :: card(id='auto-redact', cardTitle=#{home.autoRedact.title}, cardText=#{home.autoRedact.desc}, cardLink='auto-redact', toolIcon='ink_eraser', tags=#{autoRedact.tags}, toolGroup='security')}">
</div>
<div
th:replace="~{fragments/card :: card(id='redact', cardTitle=#{home.redact.title}, cardText=#{home.redact.desc}, cardLink='redact', toolIcon='playlist_remove', tags=#{redact.tags}, toolGroup='security')}">
</div>
<div
th:replace="~{fragments/card :: card(id='stamp', cardTitle=#{home.AddStampRequest.title}, cardText=#{home.AddStampRequest.desc}, cardLink='stamp', toolIcon='approval', tags=#{AddStampRequest.tags}, toolGroup='security')}">
</div>
<div
th:replace="~{fragments/card :: card(id='add-watermark', cardTitle=#{home.watermark.title}, cardText=#{home.watermark.desc}, cardLink='add-watermark', toolIcon='water_drop', tags=#{watermark.tags}, toolGroup='security')}">
</div>
</div>
</div>
<div id="groupView" class="feature-group-legacy">
<div
th:replace="~{fragments/featureGroupHeaderLegacy :: featureGroupHeader(groupTitle=#{navbar.sections.edit})}">
</div>
<div class="feature-group-container">
<div
th:replace="~{fragments/card :: card(id='view-pdf', cardTitle=#{home.viewPdf.title}, cardText=#{home.viewPdf.desc}, cardLink='view-pdf', toolIcon='menu_book', tags=#{viewPdf.tags}, toolGroup='other')}">
</div>
<div
th:replace="~{fragments/card :: card(id='add-page-numbers', cardTitle=#{home.add-page-numbers.title}, cardText=#{home.add-page-numbers.desc}, cardLink='add-page-numbers', toolIcon='123', tags=#{add-page-numbers.tags}, toolGroup='other')}">
</div>
<div
th:replace="~{fragments/card :: card(id='add-image', cardTitle=#{home.addImage.title}, cardText=#{home.addImage.desc}, cardLink='add-image', toolIcon='add_photo_alternate', tags=#{addImage.tags}, toolGroup='other')}">
</div>
<div
th:replace="~{fragments/card :: card(id='change-metadata', cardTitle=#{home.changeMetadata.title}, cardText=#{home.changeMetadata.desc}, cardLink='change-metadata', toolIcon='assignment', tags=#{changeMetadata.tags}, toolGroup='other')}">
</div>
<div
th:replace="~{fragments/card :: card(id='ocr-pdf', cardTitle=#{home.ocr.title}, cardText=#{home.ocr.desc}, cardLink='ocr-pdf', toolIcon='quick_reference_all', tags=#{ocr.tags}, toolGroup='other')}">
</div>
<div
th:replace="~{fragments/card :: card(id='extract-images', cardTitle=#{home.extractImages.title}, cardText=#{home.extractImages.desc}, cardLink='extract-images', toolIcon='wallpaper', tags=#{extractImages.tags}, toolGroup='other')}">
</div>
<div
th:replace="~{fragments/card :: card(id='flatten', cardTitle=#{home.flatten.title}, cardText=#{home.flatten.desc}, cardLink='flatten', toolIcon='layers_clear', tags=#{flatten.tags}, toolGroup='other')}">
</div>
<div
th:replace="~{fragments/card :: card(id='remove-blanks', cardTitle=#{home.removeBlanks.title}, cardText=#{home.removeBlanks.desc}, cardLink='remove-blanks', toolIcon='scan_delete', tags=#{removeBlanks.tags}, toolGroup='other')}">
</div>
<div
th:replace="~{fragments/card :: card(id='remove-annotations', cardTitle=#{home.removeAnnotations.title}, cardText=#{home.removeAnnotations.desc}, cardLink='remove-annotations', toolIcon='thread_unread', tags=#{removeAnnotations.tags}, toolGroup='other')}">
</div>
<div
th:replace="~{fragments/card :: card(id='compare', cardTitle=#{home.compare.title}, cardText=#{home.compare.desc}, cardLink='compare', toolIcon='compare', tags=#{compare.tags}, toolGroup='other')}">
</div>
<div
th:replace="~{fragments/card :: card(id='get-info-on-pdf', cardTitle=#{home.getPdfInfo.title}, cardText=#{home.getPdfInfo.desc}, cardLink='get-info-on-pdf', toolIcon='info', tags=#{getPdfInfo.tags}, toolGroup='other')}">
</div>
<div
th:replace="~{fragments/card :: card(id='remove-image-pdf', cardTitle=#{home.removeImagePdf.title}, cardText=#{home.removeImagePdf.desc}, cardLink='remove-image-pdf', toolIcon='remove_selection', tags=#{removeImagePdf.tags}, toolGroup='other')}">
</div>
<div
th:replace="~{fragments/card :: card(id='replace-and-invert-color-pdf', cardTitle=#{home.replaceColorPdf.title}, cardText=#{home.replaceColorPdf.desc}, cardLink='replace-and-invert-color-pdf', toolIcon='format_color_fill', tags=#{replaceColorPdf.tags}, toolGroup='other')}">
</div>
<div
th:replace="~{fragments/card :: card(id='unlock-pdf-forms', cardTitle=#{home.unlockPDFForms.title}, cardText=#{home.unlockPDFForms.desc}, cardLink='unlock-pdf-forms', toolIcon='preview_off', tags=#{unlockPDFForms.tags}, toolGroup='other')}">
</div>
</div>
</div>
<div id="groupAdvanced" class="feature-group-legacy">
<div
th:replace="~{fragments/featureGroupHeaderLegacy :: featureGroupHeader(groupTitle=#{navbar.sections.advance})}">
</div>
<div class="feature-group-container">
<div
th:replace="~{fragments/card :: card(id='pipeline', cardTitle=#{home.pipeline.title}, cardText=#{home.pipeline.desc}, cardLink='pipeline', toolIcon='family_history', tags=#{pipeline.tags}, toolGroup='advance')}">
</div>
<div
th:replace="~{fragments/card :: card(id='adjust-contrast', cardTitle=#{home.adjust-contrast.title}, cardText=#{home.adjust-contrast.desc}, cardLink='adjust-contrast', toolIcon='palette', tags=#{adjust-contrast.tags}, toolGroup='advance')}">
</div>
<div
th:replace="~{fragments/card :: card(id='compress-pdf', cardTitle=#{home.compressPdfs.title}, cardText=#{home.compressPdfs.desc}, cardLink='compress-pdf', toolIcon='zoom_in_map', tags=#{compressPdfs.tags}, toolGroup='advance')}">
</div>
<div
th:replace="~{fragments/card :: card(id='extract-image-scans', cardTitle=#{home.ScannerImageSplit.title}, cardText=#{home.ScannerImageSplit.desc}, cardLink='extract-image-scans', toolIcon='scanner', tags=#{ScannerImageSplit.tags}, toolGroup='advance')}">
</div>
<div
th:replace="~{fragments/card :: card(id='repair', cardTitle=#{home.repair.title}, cardText=#{home.repair.desc}, cardLink='repair', toolIcon='build', tags=#{repair.tags}, toolGroup='advance')}">
</div>
<div
th:replace="~{fragments/card :: card(id='auto-rename', cardTitle=#{home.auto-rename.title}, cardText=#{home.auto-rename.desc}, cardLink='auto-rename', toolIcon='text_fields_alt', tags=#{auto-rename.tags}, toolGroup='advance')}">
</div>
<div
th:replace="~{fragments/card :: card(id='auto-split-pdf', cardTitle=#{home.autoSplitPDF.title}, cardText=#{home.autoSplitPDF.desc}, cardLink='auto-split-pdf', toolIcon='cut', tags=#{autoSplitPDF.tags}, toolGroup='advance')}">
</div>
<div
th:replace="~{fragments/card :: card(id='show-javascript', cardTitle=#{home.showJS.title}, cardText=#{home.showJS.desc}, cardLink='show-javascript', toolIcon='javascript', tags=#{showJS.tags}, toolGroup='advance')}">
</div>
<div
th:replace="~{fragments/card :: card(id='split-by-size-or-count', cardTitle=#{home.autoSizeSplitPDF.title}, cardText=#{home.autoSizeSplitPDF.desc}, cardLink='split-by-size-or-count', toolIcon='vertical_split', tags=#{autoSizeSplitPDF.tags}, toolGroup='advance')}">
</div>
<div
th:replace="~{fragments/card :: card(id='overlay-pdf', cardTitle=#{home.overlay-pdfs.title}, cardText=#{home.overlay-pdfs.desc}, cardLink='overlay-pdf', toolIcon='layers', tags=#{overlay-pdfs.tags}, toolGroup='advance')}">
</div>
<div
th:replace="~{fragments/card :: card(id='split-pdf-by-sections', cardTitle=#{home.split-by-sections.title}, cardText=#{home.split-by-sections.desc}, cardLink='split-pdf-by-sections', toolIcon='grid_on', tags=#{split-by-sections.tags}, toolGroup='advance')}">
</div>
<div
th:replace="~{fragments/card :: card(id='split-pdf-by-chapters', cardTitle=#{home.splitPdfByChapters.title}, cardText=#{home.splitPdfByChapters.desc}, cardLink='split-pdf-by-chapters', toolIcon='book', tags=#{splitPdfByChapters.tags}, toolGroup='advance')}">
</div>
</div>
</div>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
<!-- Survey Modal -->
<div class="modal fade" id="surveyModal" tabindex="-1" role="dialog" aria-labelledby="surveyModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="surveyModalLabel" th:text="#{survey.title}">Stirling-PDF Survey</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p th:text="#{survey.meeting.1}">If you're using Stirling PDF at work, we'd love to speak to you. We're offering free technical support in exchange for a 15 minute user discovery session.</p>
<p th:text="#{survey.meeting.2}">This is a chance to:</p>
<p><span>🛠️</span><span th:text="#{survey.meeting.3}">Get help with deployment, integrations, or troubleshooting</span></p>
<p><span>📢</span><span th:text="#{survey.meeting.4}">Provide direct feedback on performance, edge cases, and feature gaps</span></p>
<p><span>🔍</span><span th:text="#{survey.meeting.5}">Help us refine Stirling PDF for real-world enterprise use</span></p>
<p th:text="#{survey.meeting.6}">If you're interested, you can book time with our team directly.</p>
<p th:text="#{survey.meeting.7}">Looking forward to digging into your use cases and making Stirling PDF even better!</p>
<a href="https://calendly.com/d/cm4p-zz5-yy8/stirling-pdf-15-minute-group-discussion" target="_blank" class="btn btn-primary" id="takeSurvey2" th:text="#{survey.meeting.button}">Book meeting</a>
</br>
</br>
<p th:text="#{survey.meeting.notInterested}">Not a business and/or interested in a meeting?</p>
<p th:text="#{survey.please}">Please consider taking our survey!</p>
<a href="https://stirlingpdf.info/s/cm28y3niq000o56dv7liv8wsu" target="_blank" class="btn btn-primary"
id="takeSurvey" th:text="#{survey.button}">Take Survey</a>
</div>
<div class="modal-footer">
<div class="form-check mb-3">
<input type="checkbox" id="dontShowAgain">
<label for="dontShowAgain" th:text="#{survey.dontShowAgain}">Don't show again</label>
</div>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" th:text="#{close}">Close</button>
</div>
</div>
</div>
</div>
<!-- Analytics Modal -->
<div class="modal fade" id="analyticsModal" tabindex="-1" role="dialog" aria-labelledby="analyticsModalLabel"
aria-hidden="true" th:if="${@analyticsPrompt}">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="analyticsModalLabel" th:text="#{analytics.title}">Do you want make Stirling PDF
better?</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p th:text="#{analytics.paragraph1}">Stirling PDF has opt in analytics to help us improve the product. We do
not track any personal information or file contents.</p>
<p th:text="#{analytics.paragraph2}">Please consider enabling analytics to help Stirling-PDF grow and to allow
us to understand our users better.</p>
<p th:text="#{analytics.settings}">You can change the settings for analytics in the config/settings.yml file
</p>
</div>
<div class="modal-footer justify-content-between">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" onclick="setAnalytics(false)"
th:text="#{analytics.disable}">Disable analytics</button>
<button type="button" class="btn btn-primary" th:text="#{analytics.enable}"
onclick="setAnalytics(true)">Enable analytics</button>
</div>
</div>
</div>
</div>
<script th:src="@{'/js/fetch-utils.js'}"></script>
<script th:inline="javascript">
/*<![CDATA[*/
const analyticsPromptBoolean = /*[[${@analyticsPrompt}]]*/ false;
document.addEventListener('DOMContentLoaded', function () {
if (analyticsPromptBoolean) {
const analyticsModal = new bootstrap.Modal(document.getElementById('analyticsModal'));
analyticsModal.show();
}
});
/*]]>*/
function setAnalytics(enabled) {
fetchWithCsrf('api/v1/settings/update-enable-analytics', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(enabled)
})
.then(response => {
if (response.status === 200) {
console.log('Analytics setting updated successfully');
bootstrap.Modal.getInstance(document.getElementById('analyticsModal')).hide();
} else if (response.status === 208) {
console.log('Analytics setting has already been set. Please edit /config/settings.yml to change it.', response);
alert('Analytics setting has already been set. Please edit /config/settings.yml to change it.');
} else {
throw new Error('Unexpected response status: ' + response.status);
}
})
.catch(error => {
console.error('Error updating analytics setting:', error);
alert('An error occurred while updating the analytics setting. Please try again.');
});
}
document.addEventListener("DOMContentLoaded", function () {
const surveyVersion = "3.0";
const modal = new bootstrap.Modal(document.getElementById('surveyModal'));
const dontShowAgain = document.getElementById('dontShowAgain');
const takeSurveyButton = document.getElementById('takeSurvey');
const viewThresholds = [5, 10, 15, 22, 30, 50, 75, 100, 150, 200];
// Check if survey version changed and reset page views if it did
const storedVersion = localStorage.getItem('surveyVersion');
if (storedVersion && storedVersion !== surveyVersion) {
localStorage.setItem('pageViews', '0');
localStorage.setItem('surveyVersion', surveyVersion);
}
let pageViews = parseInt(localStorage.getItem('pageViews') || '0');
pageViews++;
localStorage.setItem('pageViews', pageViews.toString());
function shouldShowSurvey() {
if (localStorage.getItem('dontShowSurvey') === 'true' ||
localStorage.getItem('surveyTaken') === 'true') {
return false;
}
// If survey version changed and we hit a threshold, show the survey
if (localStorage.getItem('surveyVersion') !== surveyVersion &&
viewThresholds.includes(pageViews)) {
return true;
}
return viewThresholds.includes(pageViews);
}
if (shouldShowSurvey()) {
modal.show();
}
dontShowAgain.addEventListener('change', function () {
if (this.checked) {
localStorage.setItem('dontShowSurvey', 'true');
localStorage.setItem('surveyVersion', surveyVersion);
} else {
localStorage.removeItem('dontShowSurvey');
localStorage.removeItem('surveyVersion');
}
});
takeSurveyButton.addEventListener('click', function () {
localStorage.setItem('surveyTaken', 'true');
localStorage.setItem('surveyVersion', surveyVersion);
modal.hide();
});
if (localStorage.getItem('dontShowSurvey')) {
modal.hide();
}
});
function setAsDefault(value) {
localStorage.setItem('defaultView', value);
console.log(`Default view set to: ${value}`);
}
</script>
</body>
</html>

View File

@ -82,13 +82,6 @@
visibility
</span>
</div>
<a href="home" onclick="setAsDefault('home-legacy')" th:title="#{home.legacyHomepage}"
style="text-decoration: none; color: inherit; cursor: pointer; display: flex; align-items: center;">
<span class="material-symbols-rounded toggle-favourites"
style="font-size: 2rem; margin-left: 0.2rem;">
home
</span>
</a>
<a th:if="${@shouldShow}" href="https://github.com/Stirling-Tools/Stirling-PDF/releases"
target="_blank" id="update-link" rel="noopener" th:title="#{settings.update}"
style="text-decoration: none; color: inherit; cursor: pointer; display: flex; align-items: center;">
@ -145,7 +138,7 @@
<p><span>🔍</span><span th:text="#{survey.meeting.5}">Help us refine Stirling PDF for real-world enterprise use</span></p>
<p th:text="#{survey.meeting.6}">If you're interested, you can book time with our team directly.</p>
<p th:text="#{survey.meeting.7}">Looking forward to digging into your use cases and making Stirling PDF even better!</p>
<a href="https://calendly.com/d/cm4p-zz5-yy8/stirling-pdf-15-minute-group-discussion" target="_blank" class="btn btn-primary" id="takeSurvey2" th:text="#{survey.meeting.button}">Book meeting</a>
<a href="https://calendly.com/d/crsr-tz6-487" target="_blank" class="btn btn-primary" id="takeSurvey2" th:text="#{survey.meeting.button}">Book meeting</a>
</br>
</br>
<p th:text="#{survey.meeting.notInterested}">Not a business and/or interested in a meeting?</p>

View File

@ -0,0 +1,77 @@
package stirling.software.SPDF.EE;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import stirling.software.SPDF.EE.KeygenLicenseVerifier.License;
import stirling.software.SPDF.model.ApplicationProperties;
@ExtendWith(MockitoExtension.class)
class LicenseKeyCheckerTest {
@Mock private KeygenLicenseVerifier verifier;
@Test
void premiumDisabled_skipsVerification() {
ApplicationProperties props = new ApplicationProperties();
props.getPremium().setEnabled(false);
props.getPremium().setKey("dummy");
LicenseKeyChecker checker = new LicenseKeyChecker(verifier, props);
assertEquals(License.NORMAL, checker.getPremiumLicenseEnabledResult());
verifyNoInteractions(verifier);
}
@Test
void directKey_verified() {
ApplicationProperties props = new ApplicationProperties();
props.getPremium().setEnabled(true);
props.getPremium().setKey("abc");
when(verifier.verifyLicense("abc")).thenReturn(License.PRO);
LicenseKeyChecker checker = new LicenseKeyChecker(verifier, props);
assertEquals(License.PRO, checker.getPremiumLicenseEnabledResult());
verify(verifier).verifyLicense("abc");
}
@Test
void fileKey_verified(@TempDir Path temp) throws IOException {
Path file = temp.resolve("license.txt");
Files.writeString(file, "filekey");
ApplicationProperties props = new ApplicationProperties();
props.getPremium().setEnabled(true);
props.getPremium().setKey("file:" + file.toString());
when(verifier.verifyLicense("filekey")).thenReturn(License.ENTERPRISE);
LicenseKeyChecker checker = new LicenseKeyChecker(verifier, props);
assertEquals(License.ENTERPRISE, checker.getPremiumLicenseEnabledResult());
verify(verifier).verifyLicense("filekey");
}
@Test
void missingFile_resultsNormal(@TempDir Path temp) {
Path file = temp.resolve("missing.txt");
ApplicationProperties props = new ApplicationProperties();
props.getPremium().setEnabled(true);
props.getPremium().setKey("file:" + file.toString());
LicenseKeyChecker checker = new LicenseKeyChecker(verifier, props);
assertEquals(License.NORMAL, checker.getPremiumLicenseEnabledResult());
verifyNoInteractions(verifier);
}
}

View File

@ -0,0 +1,76 @@
package stirling.software.SPDF.controller.api.pipeline;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import jakarta.servlet.ServletContext;
import stirling.software.SPDF.model.PipelineConfig;
import stirling.software.SPDF.model.PipelineOperation;
import stirling.software.SPDF.model.PipelineResult;
@ExtendWith(MockitoExtension.class)
class PipelineProcessorTest {
@Mock
ApiDocService apiDocService;
@Mock
UserServiceInterface userService;
@Mock
ServletContext servletContext;
PipelineProcessor pipelineProcessor;
@BeforeEach
void setUp() {
pipelineProcessor = spy(new PipelineProcessor(apiDocService, userService, servletContext));
}
@Test
void runPipelineWithFilterSetsFlag() throws Exception {
PipelineOperation op = new PipelineOperation();
op.setOperation("filter-page-count");
op.setParameters(Map.of());
PipelineConfig config = new PipelineConfig();
config.setOperations(List.of(op));
Resource file = new ByteArrayResource("data".getBytes()) {
@Override
public String getFilename() {
return "test.pdf";
}
};
List<Resource> files = List.of(file);
when(apiDocService.isMultiInput("filter-page-count")).thenReturn(false);
when(apiDocService.getExtensionTypes(false, "filter-page-count")).thenReturn(List.of("pdf"));
doReturn(new ResponseEntity<>(new byte[0], HttpStatus.OK))
.when(pipelineProcessor)
.sendWebRequest(anyString(), any());
PipelineResult result = pipelineProcessor.runPipelineAgainstFiles(files, config);
assertTrue(result.isFiltersApplied(), "Filter flag should be true when operation filters file");
assertFalse(result.isHasErrors(), "No errors should occur");
assertTrue(result.getOutputFiles().isEmpty(), "Filtered file list should be empty");
}
}

View File

@ -1,18 +1,17 @@
package stirling.software.SPDF.service;
import java.nio.file.Files;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.io.*;
import java.nio.file.*;
import java.nio.file.Files;
import java.util.Arrays;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.common.PDStream;
import org.aspectj.lang.annotation.Before;
import org.apache.pdfbox.cos.COSName;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
@ -43,12 +42,7 @@ class CustomPDFDocumentFactoryTest {
}
@ParameterizedTest
@CsvSource({
"5,MEMORY_ONLY",
"20,MIXED",
"60,TEMP_FILE"
})
@CsvSource({"5,MEMORY_ONLY", "20,MIXED", "60,TEMP_FILE"})
void testStrategy_FileInput(int sizeMB, StrategyType expected) throws IOException {
File file = writeTempFile(inflatePdf(basePdfBytes, sizeMB));
try (PDDocument doc = factory.load(file)) {
@ -57,12 +51,7 @@ class CustomPDFDocumentFactoryTest {
}
@ParameterizedTest
@CsvSource({
"5,MEMORY_ONLY",
"20,MIXED",
"60,TEMP_FILE"
})
@CsvSource({"5,MEMORY_ONLY", "20,MIXED", "60,TEMP_FILE"})
void testStrategy_ByteArray(int sizeMB, StrategyType expected) throws IOException {
byte[] inflated = inflatePdf(basePdfBytes, sizeMB);
try (PDDocument doc = factory.load(inflated)) {
@ -71,12 +60,7 @@ class CustomPDFDocumentFactoryTest {
}
@ParameterizedTest
@CsvSource({
"5,MEMORY_ONLY",
"20,MIXED",
"60,TEMP_FILE"
})
@CsvSource({"5,MEMORY_ONLY", "20,MIXED", "60,TEMP_FILE"})
void testStrategy_InputStream(int sizeMB, StrategyType expected) throws IOException {
byte[] inflated = inflatePdf(basePdfBytes, sizeMB);
try (PDDocument doc = factory.load(new ByteArrayInputStream(inflated))) {
@ -85,30 +69,22 @@ class CustomPDFDocumentFactoryTest {
}
@ParameterizedTest
@CsvSource({
"5,MEMORY_ONLY",
"20,MIXED",
"60,TEMP_FILE"
})
@CsvSource({"5,MEMORY_ONLY", "20,MIXED", "60,TEMP_FILE"})
void testStrategy_MultipartFile(int sizeMB, StrategyType expected) throws IOException {
byte[] inflated = inflatePdf(basePdfBytes, sizeMB);
MockMultipartFile multipart = new MockMultipartFile("file", "doc.pdf", "application/pdf", inflated);
MockMultipartFile multipart =
new MockMultipartFile("file", "doc.pdf", "application/pdf", inflated);
try (PDDocument doc = factory.load(multipart)) {
assertEquals(expected, factory.lastStrategyUsed);
}
}
@ParameterizedTest
@CsvSource({
"5,MEMORY_ONLY",
"20,MIXED",
"60,TEMP_FILE"
})
@CsvSource({"5,MEMORY_ONLY", "20,MIXED", "60,TEMP_FILE"})
void testStrategy_PDFFile(int sizeMB, StrategyType expected) throws IOException {
byte[] inflated = inflatePdf(basePdfBytes, sizeMB);
MockMultipartFile multipart = new MockMultipartFile("file", "doc.pdf", "application/pdf", inflated);
MockMultipartFile multipart =
new MockMultipartFile("file", "doc.pdf", "application/pdf", inflated);
PDFFile pdfFile = new PDFFile();
pdfFile.setFileInput(multipart);
try (PDDocument doc = factory.load(pdfFile)) {
@ -125,7 +101,9 @@ class CustomPDFDocumentFactoryTest {
stream.getCOSObject().setItem(COSName.TYPE, COSName.XOBJECT);
stream.getCOSObject().setItem(COSName.SUBTYPE, COSName.IMAGE);
doc.getDocumentCatalog().getCOSObject().setItem(COSName.getPDFName("DummyBigStream"), stream.getCOSObject());
doc.getDocumentCatalog()
.getCOSObject()
.setItem(COSName.getPDFName("DummyBigStream"), stream.getCOSObject());
ByteArrayOutputStream out = new ByteArrayOutputStream();
doc.save(out);
@ -151,28 +129,28 @@ class CustomPDFDocumentFactoryTest {
}
// neeed to add password pdf
// @Test
// void testLoadPasswordProtectedPdfFromInputStream() throws IOException {
// try (InputStream is = getClass().getResourceAsStream("/protected.pdf")) {
// assertNotNull(is, "protected.pdf must be present in src/test/resources");
// try (PDDocument doc = factory.load(is, "test123")) {
// assertNotNull(doc);
// }
// }
// }
//
// @Test
// void testLoadPasswordProtectedPdfFromMultipart() throws IOException {
// try (InputStream is = getClass().getResourceAsStream("/protected.pdf")) {
// assertNotNull(is, "protected.pdf must be present in src/test/resources");
// byte[] bytes = is.readAllBytes();
// MockMultipartFile file = new MockMultipartFile("file", "protected.pdf", "application/pdf", bytes);
// try (PDDocument doc = factory.load(file, "test123")) {
// assertNotNull(doc);
// }
// }
// }
// @Test
// void testLoadPasswordProtectedPdfFromInputStream() throws IOException {
// try (InputStream is = getClass().getResourceAsStream("/protected.pdf")) {
// assertNotNull(is, "protected.pdf must be present in src/test/resources");
// try (PDDocument doc = factory.load(is, "test123")) {
// assertNotNull(doc);
// }
// }
// }
//
// @Test
// void testLoadPasswordProtectedPdfFromMultipart() throws IOException {
// try (InputStream is = getClass().getResourceAsStream("/protected.pdf")) {
// assertNotNull(is, "protected.pdf must be present in src/test/resources");
// byte[] bytes = is.readAllBytes();
// MockMultipartFile file = new MockMultipartFile("file", "protected.pdf",
// "application/pdf", bytes);
// try (PDDocument doc = factory.load(file, "test123")) {
// assertNotNull(doc);
// }
// }
// }
@Test
void testLoadReadOnlySkipsPostProcessing() throws IOException {
@ -186,7 +164,6 @@ class CustomPDFDocumentFactoryTest {
}
}
@Test
void testCreateNewDocument() throws IOException {
try (PDDocument doc = factory.createNewDocument()) {
@ -198,7 +175,7 @@ class CustomPDFDocumentFactoryTest {
void testCreateNewDocumentBasedOnOldDocument() throws IOException {
byte[] inflated = inflatePdf(basePdfBytes, 5);
try (PDDocument oldDoc = Loader.loadPDF(inflated);
PDDocument newDoc = factory.createNewDocumentBasedOnOldDocument(oldDoc)) {
PDDocument newDoc = factory.createNewDocumentBasedOnOldDocument(oldDoc)) {
assertNotNull(newDoc);
}
}
@ -241,7 +218,6 @@ class CustomPDFDocumentFactoryTest {
@BeforeEach
void cleanup() {
System.gc();
System.gc();
}
}

View File

@ -1,13 +1,13 @@
package stirling.software.SPDF.service;
import org.apache.pdfbox.io.RandomAccessStreamCache.StreamCacheCreateFunction;
import stirling.software.SPDF.service.CustomPDFDocumentFactory;
import stirling.software.SPDF.service.PdfMetadataService;
class SpyPDFDocumentFactory extends CustomPDFDocumentFactory {
enum StrategyType {
MEMORY_ONLY, MIXED, TEMP_FILE
}
enum StrategyType {
MEMORY_ONLY,
MIXED,
TEMP_FILE
}
public StrategyType lastStrategyUsed;

View File

@ -27,25 +27,28 @@ class CustomHtmlSanitizerTest {
private static Stream<Arguments> provideHtmlTestCases() {
return Stream.of(
Arguments.of(
"<p>This is <strong>valid</strong> HTML with <em>formatting</em>.</p>",
new String[] {"<p>", "<strong>", "<em>"}
),
Arguments.of(
"<p>Text with <b>bold</b>, <i>italic</i>, <u>underline</u>, "
+ "<em>emphasis</em>, <strong>strong</strong>, <strike>strikethrough</strike>, "
+ "<s>strike</s>, <sub>subscript</sub>, <sup>superscript</sup>, "
+ "<tt>teletype</tt>, <code>code</code>, <big>big</big>, <small>small</small>.</p>",
new String[] {"<b>bold</b>", "<i>italic</i>", "<em>emphasis</em>", "<strong>strong</strong>"}
),
Arguments.of(
"<div>Division</div><h1>Heading 1</h1><h2>Heading 2</h2><h3>Heading 3</h3>"
+ "<h4>Heading 4</h4><h5>Heading 5</h5><h6>Heading 6</h6>"
+ "<blockquote>Blockquote</blockquote><ul><li>List item</li></ul>"
+ "<ol><li>Ordered item</li></ol>",
new String[] {"<div>", "<h1>", "<h6>", "<blockquote>", "<ul>", "<ol>", "<li>"}
)
);
Arguments.of(
"<p>This is <strong>valid</strong> HTML with <em>formatting</em>.</p>",
new String[] {"<p>", "<strong>", "<em>"}),
Arguments.of(
"<p>Text with <b>bold</b>, <i>italic</i>, <u>underline</u>, "
+ "<em>emphasis</em>, <strong>strong</strong>, <strike>strikethrough</strike>, "
+ "<s>strike</s>, <sub>subscript</sub>, <sup>superscript</sup>, "
+ "<tt>teletype</tt>, <code>code</code>, <big>big</big>, <small>small</small>.</p>",
new String[] {
"<b>bold</b>",
"<i>italic</i>",
"<em>emphasis</em>",
"<strong>strong</strong>"
}),
Arguments.of(
"<div>Division</div><h1>Heading 1</h1><h2>Heading 2</h2><h3>Heading 3</h3>"
+ "<h4>Heading 4</h4><h5>Heading 5</h5><h6>Heading 6</h6>"
+ "<blockquote>Blockquote</blockquote><ul><li>List item</li></ul>"
+ "<ol><li>Ordered item</li></ol>",
new String[] {
"<div>", "<h1>", "<h6>", "<blockquote>", "<ul>", "<ol>", "<li>"
}));
}
@Test

View File

@ -0,0 +1,41 @@
package stirling.software.SPDF.utils;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class GeneralUtilsAdditionalTest {
@Test
void testConvertSizeToBytes() {
assertEquals(1024L, GeneralUtils.convertSizeToBytes("1KB"));
assertEquals(1024L * 1024, GeneralUtils.convertSizeToBytes("1MB"));
assertEquals(1024L * 1024 * 1024, GeneralUtils.convertSizeToBytes("1GB"));
assertEquals(100L * 1024 * 1024, GeneralUtils.convertSizeToBytes("100"));
assertNull(GeneralUtils.convertSizeToBytes("invalid"));
assertNull(GeneralUtils.convertSizeToBytes(null));
}
@Test
void testFormatBytes() {
assertEquals("512 B", GeneralUtils.formatBytes(512));
assertEquals("1.00 KB", GeneralUtils.formatBytes(1024));
assertEquals("1.00 MB", GeneralUtils.formatBytes(1024L * 1024));
assertEquals("1.00 GB", GeneralUtils.formatBytes(1024L * 1024 * 1024));
}
@Test
void testURLHelpersAndUUID() {
assertTrue(GeneralUtils.isValidURL("https://example.com"));
assertFalse(GeneralUtils.isValidURL("htp:/bad"));
assertFalse(GeneralUtils.isURLReachable("http://localhost"));
assertFalse(GeneralUtils.isURLReachable("ftp://example.com"));
assertTrue(GeneralUtils.isValidUUID("123e4567-e89b-12d3-a456-426614174000"));
assertFalse(GeneralUtils.isValidUUID("not-a-uuid"));
assertFalse(GeneralUtils.isVersionHigher(null, "1.0"));
assertTrue(GeneralUtils.isVersionHigher("2.0", "1.9"));
assertFalse(GeneralUtils.isVersionHigher("1.0", "1.0.1"));
}
}

View File

@ -1,6 +1,5 @@
package stirling.software.SPDF.utils;
import io.github.pixee.security.ZipSecurity;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -29,6 +28,8 @@ import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
import io.github.pixee.security.ZipSecurity;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
/**
@ -214,7 +215,8 @@ class PDFToFileTest {
// Verify the content by unzipping it
try (ZipInputStream zipStream =
ZipSecurity.createHardenedInputStream(new java.io.ByteArrayInputStream(response.getBody()))) {
ZipSecurity.createHardenedInputStream(
new java.io.ByteArrayInputStream(response.getBody()))) {
ZipEntry entry;
boolean foundMdFiles = false;
boolean foundImage = false;
@ -286,7 +288,8 @@ class PDFToFileTest {
// Verify the content by unzipping it
try (ZipInputStream zipStream =
ZipSecurity.createHardenedInputStream(new java.io.ByteArrayInputStream(response.getBody()))) {
ZipSecurity.createHardenedInputStream(
new java.io.ByteArrayInputStream(response.getBody()))) {
ZipEntry entry;
boolean foundMainHtml = false;
boolean foundIndexHtml = false;
@ -437,7 +440,8 @@ class PDFToFileTest {
// Verify the content by unzipping it
try (ZipInputStream zipStream =
ZipSecurity.createHardenedInputStream(new java.io.ByteArrayInputStream(response.getBody()))) {
ZipSecurity.createHardenedInputStream(
new java.io.ByteArrayInputStream(response.getBody()))) {
ZipEntry entry;
boolean foundMainFile = false;
boolean foundMediaFiles = false;

View File

@ -5,12 +5,17 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
@ -18,6 +23,10 @@ import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.service.CustomPDFDocumentFactory;
import stirling.software.SPDF.service.PdfMetadataService;
public class PdfUtilsTest {
@Test
@ -49,4 +58,68 @@ public class PdfUtilsTest {
assertTrue(PdfUtils.hasImagesOnPage(page));
}
@Test
void testPageCountComparators() throws Exception {
PDDocument doc1 = new PDDocument();
doc1.addPage(new PDPage());
doc1.addPage(new PDPage());
doc1.addPage(new PDPage());
PdfUtils utils = new PdfUtils();
assertTrue(utils.pageCount(doc1, 2, "greater"));
PDDocument doc2 = new PDDocument();
doc2.addPage(new PDPage());
doc2.addPage(new PDPage());
doc2.addPage(new PDPage());
assertTrue(utils.pageCount(doc2, 3, "equal"));
PDDocument doc3 = new PDDocument();
doc3.addPage(new PDPage());
doc3.addPage(new PDPage());
assertTrue(utils.pageCount(doc3, 5, "less"));
PDDocument doc4 = new PDDocument();
doc4.addPage(new PDPage());
assertThrows(IllegalArgumentException.class, () -> utils.pageCount(doc4, 1, "bad"));
}
@Test
void testPageSize() throws Exception {
PDDocument doc = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
doc.addPage(page);
PDRectangle rect = page.getMediaBox();
String expected = rect.getWidth() + "x" + rect.getHeight();
PdfUtils utils = new PdfUtils();
assertTrue(utils.pageSize(doc, expected));
}
@Test
void testOverlayImage() throws Exception {
PDDocument doc = new PDDocument();
doc.addPage(new PDPage(PDRectangle.A4));
ByteArrayOutputStream pdfOut = new ByteArrayOutputStream();
doc.save(pdfOut);
doc.close();
BufferedImage image = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
g.setColor(Color.RED);
g.fillRect(0, 0, 10, 10);
g.dispose();
ByteArrayOutputStream imgOut = new ByteArrayOutputStream();
javax.imageio.ImageIO.write(image, "png", imgOut);
PdfMetadataService meta =
new PdfMetadataService(new ApplicationProperties(), "label", false, null);
CustomPDFDocumentFactory factory = new CustomPDFDocumentFactory(meta);
byte[] result =
PdfUtils.overlayImage(
factory, pdfOut.toByteArray(), imgOut.toByteArray(), 0, 0, false);
try (PDDocument resultDoc = factory.load(result)) {
assertEquals(1, resultDoc.getNumberOfPages());
}
}
}