Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
231 commits
Select commit Hold shift + click to select a range
968aec7
Improve grammar for unhide (#13835)
RCheesley Jun 18, 2024
20ddb34
fix: LeadBundle template errors (#13862)
Frettyl Jun 21, 2024
91ee433
[UI] fix CSS flexbox broken in campaign insert clone view (#13878)
andersonjeccel Jun 27, 2024
914ff02
DPMMA-1020 Fix search email with special characters in campaign actio…
patrykgruszka Jul 15, 2024
d272a0a
fix: focus item published (#13944)
andersonjeccel Jul 15, 2024
5d7c094
Update .gitpod.Dockerfile 5.1 branch (#13955)
RCheesley Jul 15, 2024
cc58e4f
fix [DPMMA-2661] mapped field form 5.1 (#13938)
tomekkowalczyk Jul 15, 2024
d80b664
FIX: Removes onConfigSave which invokes htmlspecialchars and escapes …
putzwasser Jul 17, 2024
7e0baa3
On forms, in combination with mapped input fields and custom properti…
Aug 16, 2024
64a084d
remove unrelated changes
Oct 10, 2024
475826c
Merge branch '5.x' into issue_13542
david-gap Oct 10, 2024
ec04bee
Merge branch '5.x' into issue_13542
david-gap Jan 9, 2025
808fd3c
Category - The "Type" field is missing after saving an unfilled form …
levente999 Jan 9, 2025
e3f1ee9
fix the testNewActionWithInForm
levente999 Jan 9, 2025
e89ba42
add test case for 14168
levente999 Jan 13, 2025
ed1212c
fix app callback when push_id is empty
thisismzm Jan 19, 2025
95f53c3
check if points available
npracht Mar 10, 2025
f2224ec
test: Add PointModelTest unit test file
kuzmany Mar 14, 2025
92a2e25
The test file looks good and covers the key scenarios for the early r…
kuzmany Mar 14, 2025
1cdc69d
refactor: Clean up test file and update method call signature
kuzmany Mar 14, 2025
e04a3bb
fix: Add PHPStan annotations to resolve deprecation and mock object w…
kuzmany Mar 14, 2025
2d6b59a
fix: use new route instead of edit on clone lead field
Hugo-Prossaird Mar 21, 2025
b741429
fixcs
Hugo-Prossaird Mar 21, 2025
2960015
test: add new test for redirect
Hugo-Prossaird Mar 21, 2025
4b67b0c
phpstan
Hugo-Prossaird Mar 21, 2025
eb8b4fb
test: add __clone test
Hugo-Prossaird Mar 21, 2025
bef096e
fixcs
Hugo-Prossaird Mar 21, 2025
b2168a4
phpstan
Hugo-Prossaird Mar 21, 2025
9cd247f
Fix #14379: Ensure Unique Object Names in Multiselect Fields
1792826181 Mar 18, 2025
af6f0b7
MTC-5559 Using path() instead of url()
JonasLudwig1998 Mar 25, 2025
2a15421
Merge pull request #14794 from 1792826181/issue_14379
escopecz Mar 25, 2025
663ee45
Make sure campaign actions of removed contacts are counted as well
JonasLudwig1998 Mar 26, 2025
bbed925
Make sure scheduled campaign actions are counted as pending
JonasLudwig1998 Mar 26, 2025
0e988c9
Better considering existing code logic
JonasLudwig1998 Mar 26, 2025
e4cd1d8
fix: [DPMMA-1850] boolean field default value
patrykgruszka Mar 26, 2025
09364fb
Refactoring tests
JonasLudwig1998 Mar 27, 2025
8fa05ee
Adding necessary tests and doing static code analysis
JonasLudwig1998 Mar 27, 2025
efb4bc6
Merge pull request #14797 from JonasLudwig1998/fix-company-links
escopecz Mar 27, 2025
c689863
Merge branch '5.2' into fix/custom-fields-clone-fix
Hugo-Prossaird Mar 28, 2025
c686124
fix: clone fixed field generate a non-fixed field
Hugo-Prossaird Mar 28, 2025
0c5842d
fix: [DPMMA-2876] email variant marketing duplicates
patrykgruszka Apr 1, 2025
c331483
fix: [DPMMA-2876] email variant marketing duplicates - tests
patrykgruszka Apr 2, 2025
e7ba7c0
fix: [DPMMA-2876] phpstan
patrykgruszka Apr 3, 2025
9860dcc
refactoring: switch to /clone route insteand of /new (based on segmen…
Hugo-Prossaird Apr 4, 2025
20f95c5
fix: [DPMMA-3090] SortableListTransformer - handle non sequential ind…
patrykgruszka Apr 9, 2025
9b995cd
fix: [DPMMA-3090] phpstan
patrykgruszka Apr 10, 2025
9dab0aa
Merge pull request #14805 from patrykgruszka/DPMMA-1850_fix-bool-fiel…
escopecz Apr 14, 2025
2067267
Merge pull request #14866 from patrykgruszka/DPMMA-3090_sortable-tran…
escopecz Apr 15, 2025
c83f457
##14882 Fixes issue when email is changed by user and it does not mat…
pelbox Apr 16, 2025
07903c5
##14882 Fixes issue when email is changed by user and it does not mat…
pelbox Apr 16, 2025
cfd39d0
Enable saving bool field value false in update contact action
JonasLudwig1998 Apr 16, 2025
a40cd38
Enable sRemoving unnecessary variable from custom_yesno_button_group_…
JonasLudwig1998 Apr 17, 2025
cee5a2a
Static code analysis
JonasLudwig1998 Apr 17, 2025
74e8842
Extending logic to update contacts primary company action
JonasLudwig1998 Apr 17, 2025
97a184a
Check permission for viewown not viewother in getTokens.
abhisekmazumdar Apr 17, 2025
ae45603
remove link to doc to avoid broken link in future + error in UI display
npracht Apr 17, 2025
71dbd1c
fix borken trans in UI because of double quotes in trans + remove doc…
npracht Apr 17, 2025
f492158
Implement QA findings
JonasLudwig1998 Apr 22, 2025
09e851d
Start with tests
JonasLudwig1998 Apr 22, 2025
3bb21ee
Merge pull request #14891 from npracht/fix/broken-trans
escopecz Apr 22, 2025
1c4b8c7
remove double quote
npracht Apr 23, 2025
57f7984
fix: [DPMMA-3098] fix report data bool filter
AlanWierzchonCA Apr 23, 2025
8ef3816
Update app/bundles/ReportBundle/Translations/en_US/messages.ini
npracht Apr 23, 2025
25cdf9b
Merge pull request #14908 from npracht/fix/remove-double-quote-in-tra…
matbcvo Apr 23, 2025
8c81c22
Making tests work and implement LeadFieldTestTrait.php
JonasLudwig1998 Apr 23, 2025
4b5241b
Make updatecontact and update contacts primary company more responsive
JonasLudwig1998 Apr 23, 2025
3791462
Merge branch '5.2' into fix/custom-fields-clone-fix
Hugo-Prossaird Apr 25, 2025
03aa93c
fixcs + rector
Hugo-Prossaird Apr 25, 2025
850c0fb
Merge remote-tracking branch 'origin/fix/custom-fields-clone-fix' int…
Hugo-Prossaird Apr 25, 2025
3f28f0d
refacto: rename test method
Hugo-Prossaird Apr 25, 2025
b48642e
fixcs + rector
Hugo-Prossaird Apr 25, 2025
4a9396a
test: add complete submission and redirect cloning test
Hugo-Prossaird Apr 25, 2025
229e7a2
fix: add model typing
Hugo-Prossaird Apr 25, 2025
b5c58f8
fix: use model instead of em
Hugo-Prossaird Apr 25, 2025
1b0614b
test: add redirection check
Hugo-Prossaird Apr 25, 2025
1d50b35
test: remove invalid check
Hugo-Prossaird Apr 25, 2025
8b0d605
test: fix check redirection
Hugo-Prossaird Apr 25, 2025
5a3e2ea
refactoring: remove useless form validation and save
Hugo-Prossaird Apr 25, 2025
360c090
Merge pull request #14809 from JonasLudwig1998/fix-change-campaigns-bug
matbcvo Apr 25, 2025
b5731e7
Merge branch '5.2' into fix-permission-get-token
RCheesley Apr 25, 2025
131fda8
Fix #12926.
biozshock Apr 24, 2025
5c3fcc6
Properly process repeating edit requests to the same data.
biozshock Apr 23, 2025
cc553cb
Merge pull request #14919 from biozshock/fix-12926
matbcvo Apr 26, 2025
c6f53ed
Fix report email sending UI.
biozshock Apr 26, 2025
cb8ea93
Merge pull request #14932 from biozshock/fix-14665
matbcvo Apr 28, 2025
acabe3c
add transifex workflow (#14935)
matbcvo Apr 28, 2025
7a914bf
Merge branch '5.2' into DPMMA-2876_fix-email-variant-duplicates
matbcvo Apr 28, 2025
bc8b7a9
Merge pull request #14838 from patrykgruszka/DPMMA-2876_fix-email-var…
matbcvo Apr 28, 2025
25e04a4
Merge branch '5.2' into fix-permission-get-token
matbcvo Apr 28, 2025
9189148
Merge branch '5.2' into 5.2
RCheesley Apr 28, 2025
e1a953a
Make "No change" string translatable
matbcvo Apr 28, 2025
f05a553
Merge branch '5.2' into edit-entity-twice-5.x
RCheesley Apr 28, 2025
47e8dee
Merge pull request #14884 from pelbox/5.2
matbcvo Apr 28, 2025
51cde3c
Merge branch '5.2' into fix-permission-get-token
RCheesley Apr 28, 2025
88f1c12
Merge branch '5.2' into m5-fix-bool-type
escopecz Apr 28, 2025
a2ba04b
bump version to 5.2.5
matbcvo Apr 28, 2025
26b614d
Merge pull request #14888 from JonasLudwig1998/m5-fix-bool-type
matbcvo Apr 28, 2025
4712139
Merge pull request #14943 from matbcvo/bump-version-to-5.2.5
matbcvo Apr 28, 2025
644aa4a
fix: [DPMMA-3098] fix filterValueYesNoTemplate saving issue
AlanWierzchonCA Apr 28, 2025
25ba78c
Fix Transifex workflow (#14947)
matbcvo Apr 29, 2025
b5124b7
fix: [DPMMA-3098] fix filterValueYesNoTemplate ID conflicts
AlanWierzchonCA Apr 29, 2025
c654fca
Add proper check.
abhisekmazumdar Apr 30, 2025
f387693
phpcs fixes.
abhisekmazumdar Apr 30, 2025
3ff27dc
Merge branch '5.2' into fix-permission-get-token
abhisekmazumdar Apr 30, 2025
ed0dbeb
Fix Transifex workflow
matbcvo Apr 30, 2025
5576c3b
Merge branch '5.2' into bugfix-14168
RCheesley May 1, 2025
421ca92
fix: Enable horizontal scrolling for dynamic content tabs
krishna-ramrakhyani May 1, 2025
d4e4634
fix comment
levente999 May 2, 2025
6a0e2df
Merge pull request #14963 from matbcvo/fix-transifex-workflow-5.2
matbcvo May 5, 2025
776e95c
bumping to version 6.0.1
nick-vanpraet May 6, 2025
923c975
[UserBundle] Use u.loginName instead of r.name to order by login name
Dominic-Mayers May 6, 2025
c67717d
Merge pull request #14985 from nick-vanpraet/6.0.1-release
nick-vanpraet May 6, 2025
8af880c
Merge branch '6.0' into fix/#14449
krishna-ramrakhyani May 8, 2025
45069fc
Merge pull request #14966 from Krishu0765/fix/#14449
escopecz May 15, 2025
f91bf7e
Fix encoding typo
matbcvo May 15, 2025
712e838
Fix unicode check
matbcvo May 15, 2025
2b540fa
Fix unicode check
matbcvo May 15, 2025
1de790e
Add more Unicode character in testHtmlFilter
matbcvo May 15, 2025
e7c8042
Merge branch '5.2' into fix/custom-fields-clone-fix
Hugo-Prossaird May 16, 2025
434c392
fix: remove useless method
Hugo-Prossaird May 16, 2025
da68ec5
Merge remote-tracking branch 'origin/fix/custom-fields-clone-fix' int…
Hugo-Prossaird May 16, 2025
ddb1405
refactor: autowire fieldModel and rename variable
Hugo-Prossaird May 16, 2025
0d76f85
revert: add back getIsCloned
Hugo-Prossaird May 16, 2025
099e197
Rebuild grapesjs using 5.2 branch of mautic preset
LordRembo Apr 25, 2025
8e87a2a
Merge pull request #14909 from AlanWierzchonCA/DPMMA-3098_fix_report_…
escopecz May 20, 2025
4ee672b
Merge branch '5.2' into fix-issue-14283
matbcvo May 20, 2025
1004771
Fixing date objects in a test
escopecz Sep 2, 2024
febedd4
Fixing test. The export will change numbers into strings
escopecz Sep 2, 2024
1d2d861
Upgrading the phpoffice/phpspreadsheet dependency to the latest version
escopecz May 20, 2025
0086ffa
Merge pull request #15016 from escopecz/phpspreadsheet-upgrade
escopecz May 21, 2025
49e7014
fix: improve token null handling in email content replacement
abhisekmazumdar May 21, 2025
d2e24ec
Merge branch '5.2' into fix/email-token-null-handling
abhisekmazumdar May 21, 2025
3a4066b
fix: [DPMMA-3118] segment dependecy tree
patrykgruszka May 21, 2025
85ce628
Merge branch '5.2' into fix-issue-14283
matbcvo May 22, 2025
b6c9709
Merge pull request #15028 from patrykgruszka/DPMMA-3118_fix-segment-d…
escopecz May 22, 2025
44ad72b
refactor SQL query
levente999 May 23, 2025
ce7b92b
Merge branch '5.2' into fix-issue-14283
matbcvo May 23, 2025
0f3de54
Merge pull request #14982 from Dominic-Mayers/fix_14910
nick-vanpraet May 26, 2025
9d603b2
Merge branch '5.2' into fix-issue-14283
escopecz May 26, 2025
08032d2
Merge pull request #14445 from levente999/bugfix-14168
nick-vanpraet May 26, 2025
ed83774
Merge pull request #15006 from matbcvo/fix-issue-14283
escopecz May 26, 2025
bcf114f
Merge pull request #15040 from levente999/refactor-SQL-query
nick-vanpraet May 26, 2025
67b3d57
Merge commit from fork
nick-vanpraet May 27, 2025
67b493e
Merge commit from fork
nick-vanpraet May 27, 2025
b5eade1
Merge commit from fork
nick-vanpraet May 27, 2025
e20bd9f
Merge commit from fork
nick-vanpraet May 27, 2025
3367f1e
Merge commit from fork
tomekkowalczyk May 27, 2025
e5770f4
Merge commit from fork
nick-vanpraet May 27, 2025
d0f5526
Merge commit from fork
nick-vanpraet May 27, 2025
616b274
Merge commit from fork
nick-vanpraet May 27, 2025
a425471
Merge commit from fork
nick-vanpraet May 27, 2025
9bc71ff
Merge commit from fork
nick-vanpraet May 27, 2025
52ef42a
Increment version to 5.2.6
nick-vanpraet May 27, 2025
3e00c36
Merge pull request #15047 from nick-vanpraet/metadata-increase-526
nick-vanpraet May 27, 2025
a6c7439
Merge branch '5.2' into backport-5.2-to-6.0
nick-vanpraet May 27, 2025
0d56a3c
phpstan and phpcs fix for CampaignDecistionTest after backport
nick-vanpraet May 27, 2025
f8c2bd8
phpstan fix for direct $container access
nick-vanpraet May 27, 2025
e956ace
Update app/bundles/LeadBundle/EventListener/CampaignSubscriber.php
nick-vanpraet May 28, 2025
593eff8
Remove methods from Test that were moved to a Trait
nick-vanpraet May 28, 2025
2c96c38
Remove unused use statements
nick-vanpraet May 28, 2025
5332550
Update app/bundles/CampaignBundle/Tests/Functional/Campaign/CampaignD…
nick-vanpraet May 28, 2025
16db2ba
Update app/bundles/CampaignBundle/Tests/Functional/Campaign/CampaignD…
nick-vanpraet May 28, 2025
f203152
Merge pull request #15048 from nick-vanpraet/backport-5.2-to-6.0
escopecz May 28, 2025
2d36a94
Bump version in preparation for release (#15050)
RCheesley May 28, 2025
ea455c8
Merge branch '5.2' into edit-entity-twice-5.x
escopecz May 29, 2025
a67d47e
Merge pull request #14928 from biozshock/edit-entity-twice-5.x
escopecz May 29, 2025
1c009bf
fix: improve campaign source widget text overflow handling
kuzmany May 30, 2025
5ac4f4c
Update app/bundles/CampaignBundle/Assets/css/campaign.css
kuzmany May 30, 2025
9698d3f
fix: add headerTitle block to timeline plugin templates
kuzmany May 26, 2025
c07e77f
Refactor empty to is_array for the tokens check.
abhisekmazumdar Jun 2, 2025
e19525e
Fixing problem when functional testing with most recent ddev version
JonasLudwig1998 Jun 2, 2025
aacf74a
Run fixcs
JonasLudwig1998 Jun 2, 2025
00a2925
Merge pull request #15026 from abhisekmazumdar/fix/email-token-null-h…
escopecz Jun 2, 2025
c936840
Use direct string interpolation instead of env variable syntax in DB …
JonasLudwig1998 Jun 5, 2025
565ce8e
Fixing phpstan and fixcs problem
JonasLudwig1998 Jun 5, 2025
93c4dce
Merge branch '6.0' into feature/pr-6225-campaign-source-widget-overflow
escopecz Jun 11, 2025
66fb5e0
Merge pull request #15055 from kuzmany/feature/pr-6225-campaign-sourc…
escopecz Jun 11, 2025
2e27e5d
Merge branch '5.2' into remove-mysql-pwd-variable
escopecz Jun 12, 2025
6a250f9
Merge pull request #15067 from JonasLudwig1998/remove-mysql-pwd-variable
escopecz Jun 12, 2025
1821efc
ref: use getter instead of property access
Hugo-Prossaird Jun 13, 2025
76d4b88
Merge branch '5.2' into fix/custom-fields-clone-fix
Hugo-Prossaird Jun 13, 2025
85f13de
Merge branch '5.2' into fix-permission-get-token
npracht Jun 13, 2025
02be5d1
Merge pull request #14048 from david-gap/issue_13542
npracht Jun 13, 2025
cc2ac5b
Report saved with proper options.
biozshock May 24, 2025
0611bce
Allow ReportModel to be tested.
biozshock Jun 10, 2025
d7722bf
Remove reports as they are sent.
biozshock Jun 10, 2025
2fc2e93
Unset global variables instead of null'ing them, to prevent getting t…
biozshock Jun 12, 2025
8b72c53
Swap key value pairs for locale Form field.
biozshock Jun 12, 2025
d933e4d
Allow external plugins to replace "{dwc" tokens with custom filter ev…
biozshock Feb 12, 2025
749e69b
Merge pull request #14599 from biozshock/dwc-token
escopecz Jun 16, 2025
6c2a360
Merge branch '5.2' into fix-12851
escopecz Jun 16, 2025
82e71b6
Merge pull request #15096 from biozshock/fix-12851
escopecz Jun 16, 2025
3daba7b
Merge branch '5.2' into fix/custom-fields-clone-fix
escopecz Jun 16, 2025
d9a8699
Merge pull request #14780 from Hugo-Prossaird/fix/custom-fields-clone…
escopecz Jun 16, 2025
f43c008
Adding the `write-test` prompt, fixing the `device_tracking_service' …
escopecz Jun 16, 2025
90ff7a0
Adding a data provider note
escopecz Jun 16, 2025
352c485
CI fixes
escopecz Jun 16, 2025
015445c
fix: Unable to Upload Assets with Non-Default File Extensions
andersonjeccel Jun 18, 2025
cc73de5
Merge pull request #15102 from escopecz/device_tracking_service-not-f…
kuzmany Jun 19, 2025
94fe02c
Refactor: Simplify complex validation condition for better readability
kuzmany Jun 19, 2025
30692bb
Apply PHP CS Fixer to AssetBundle files
kuzmany Jun 19, 2025
3a9010b
Merge pull request #15111 from andersonjeccel/fix-Unable-to-Upload-As…
kuzmany Jun 19, 2025
ea4d845
Merge branch '5.2' into fix-permission-get-token
kuzmany Jun 19, 2025
b0e67ba
Merge pull request #14890 from abhisekmazumdar/fix-permission-get-token
kuzmany Jun 19, 2025
a8de8ca
Merge branch '5.2' into patch-1
kuzmany Jun 19, 2025
9b20434
Merge remote-tracking branch 'origin/5.2' into fix/save-lead-entity-i…
kuzmany Jun 20, 2025
08da6ea
Remove PointModelTest.php file
kuzmany Jun 20, 2025
7fb721e
Add test for early return scenario when no points available
kuzmany Jun 20, 2025
9b82e89
Merge pull request #14925 from LordRembo/fix/14288-multiple-editors-5
kuzmany Jun 20, 2025
4eba0f5
Fix email sending memory leak by clearing message metadata
kuzmany Jun 23, 2025
0da0c28
Add test to verify message metadata is cleared after sending
kuzmany Jun 23, 2025
2173b1a
Fix PHPSTAN error in clearMetadata test by using assertCount instead …
kuzmany Jun 23, 2025
a421e8a
Merge branch '5.2' into fix-14976
RCheesley Jun 23, 2025
baad562
- send email preview with lead data
lenonleite Jun 23, 2025
cb03659
- fix test
lenonleite Jun 23, 2025
9bd4e21
- fix test
lenonleite Jun 24, 2025
575026d
- fix test
lenonleite Jun 24, 2025
986fc01
- fix ci
lenonleite Jun 24, 2025
8bcd215
Merge pull request #15121 from kuzmany/fix/email-sending-clear-metadata
kuzmany Jun 30, 2025
6c6c52c
Merge branch '5.2' into fix/save-lead-entity-if-points-log-exists
kuzmany Jun 30, 2025
a44aeda
Merge pull request #14480 from thisismzm/patch-1
kuzmany Jun 30, 2025
a50f3dd
Merge pull request #15052 from biozshock/fix-14976
kuzmany Jun 30, 2025
4e3596a
Merge pull request #15128 from lenonleite/send-email-preview-with-lea…
kuzmany Jun 30, 2025
4420f87
Merge pull request #14714 from npracht/fix/save-lead-entity-if-points…
kuzmany Jun 30, 2025
511dc75
Increment version to 5.2.7
kuzmany Jun 30, 2025
48bc6f4
Merge pull request #15148 from kuzmany/bump-version-to-5.2.7
kuzmany Jun 30, 2025
409f571
Merge branch '6.0' of https://github.com/kuzmany/mautic into 6.0
kuzmany Jun 30, 2025
e6f90ad
Merge remote-tracking branch 'upstream/5.2' into backport-5.2.7-to-6.0
kuzmany Jun 30, 2025
3074e9c
Fix Rector issues - add missing return type hints
kuzmany Jun 30, 2025
3e0f36e
Fix PHPStan errors in test files
kuzmany Jun 30, 2025
549c375
Fix ReportControllerFunctionalTest::testDelayedTransport
kuzmany Jun 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/prompts/write-test.prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
mode: 'agent'
tools: ['testFailure', 'usages']
description: 'Generate a functional test for highlighted code'
---
Generate a new or update an existing functional test for the highlighted code. The test should cover the following aspects:

A good test should be:
- **Isolated**: It should not depend on other tests or external systems.
- **Fast**: It should run quickly to allow for rapid feedback during development.
- **Deterministic**: It should produce the same result every time it runs, regardless of the environment.
- **Readable**: It should be easy to understand and maintain.
- **Descriptive**: It should clearly indicate what it is testing and why.
- **Comprehensive**: It should cover a wide range of scenarios, including edge cases.
- **Independent**: It should not rely on the state of the application or database, ensuring that it can run in any order without affecting other tests.
- **Behavior-focused**: It should test output for specific input rather than internal implementation, except when verifying performance-critical operations like expensive method calls.

Good practices:
- Never mock objects that have no PHP service dependencies like event or entity classes. Use the real object instead.
- The best way to write a functional test is to use the actual endpoint. Either call a route or a command. The next best thing is to call a subscriber via EventDispatcher. If that is still too broad call a service.
- Make all new classes final by default. Including tests.
- Provide property, param and return types. If not possible to use native types you can always specify the types in the docblock. Mautic uses PHPSTAN so be sure to add types so the PHPSTAN won't fail.
- Use `$this->assertResponseIsSuccessful();` to assert successful requests.
- Use PHPUNIT's data providers to test multiple scenarios in a single test method.

Suggestions for AI:
- Do not modify the production code unless requested. Always just modify the test code.
- Make the simplest test possible. The human will suggest improvements if needed.

You can take an inspiration of existing functional tests. They all extend the `MysqlFunctionalTestCase` class and are located in the `app/bundles/*Bundle/Tests` or `plugins/*Bundle/Tests` directory. The tests are written in PHPUNIT. You can read the version in the `composer.json` file.

To execute a test you have to run it in DDEV. Example: `ddev exec composer test app/bundles/ExampleBundle/tests/ExampleTest.php`.
10 changes: 6 additions & 4 deletions .github/workflows/transifex.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,22 @@ permissions:

jobs:
push-to-transifex:
if: github.repository == 'mautic/mautic'
# Run only in the 'mautic/mautic' repository and on the default branch (or manually triggered via UI)
if: github.repository == 'mautic/mautic' && (github.ref_name == github.event.repository.default_branch || github.event_name == 'workflow_dispatch')
name: Push translations to Transifex
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Checkout
uses: actions/checkout@v4

- name: Setup PHP, with composer and extensions
- name: Setup PHP with extensions
uses: shivammathur/setup-php@v2
with:
php-version: 8.3
extensions: mbstring, xml, ctype, iconv, intl, pdo_sqlite, mysql, pdo_mysql

- name: Install dependencies
- name: Install Composer dependencies
uses: "ramsey/composer-install@v3"

- name: Push translations to Transifex
Expand Down
11 changes: 11 additions & 0 deletions .htaccess
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@

# Apache 2.4+
<IfModule authz_core_module>
# Deny access via HTTP requests to all .env files.
<FilesMatch "^\.env.*$">
Require all denied
</FilesMatch>

# Deny access via HTTP requests to all PHP files.
<FilesMatch "\.php$">
Require all denied
Expand All @@ -117,6 +122,12 @@

# Fallback for Apache < 2.4
<IfModule !authz_core_module>
# Deny access via HTTP requests to all .env files.
<FilesMatch "^\.env.*$">
Order deny,allow
Deny from all
</FilesMatch>

# Deny access via HTTP requests to all PHP files.
<FilesMatch "\.php$">
Order deny,allow
Expand Down
11 changes: 11 additions & 0 deletions app/assets/scaffold/files/htaccess
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@

# Apache 2.4+
<IfModule authz_core_module>
# Deny access via HTTP requests to all .env files.
<FilesMatch "^\.env.*$">
Require all denied
</FilesMatch>

# Deny access via HTTP requests to all PHP files.
<FilesMatch "\.php$">
Require all denied
Expand All @@ -117,6 +122,12 @@

# Fallback for Apache < 2.4
<IfModule !authz_core_module>
# Deny access via HTTP requests to all .env files.
<FilesMatch "^\.env.*$">
Order deny,allow
Deny from all
</FilesMatch>

# Deny access via HTTP requests to all PHP files.
<FilesMatch "\.php$">
Order deny,allow
Expand Down
13 changes: 12 additions & 1 deletion app/bundles/ApiBundle/Controller/CommonApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,18 @@ protected function processBatchForm(Request $request, $key, $entity, $params, $m
$errors[$key] = $formResponse;
}

$this->doctrine->getManager()->detach($entity);
$lastEntityIndex = -1;
foreach ($entities as $index => $moreEntities) {
if ($moreEntities !== $entity) {
continue;
}

$lastEntityIndex = $index;
}

if (-1 === $lastEntityIndex || $lastEntityIndex === $key) {
$this->doctrine->getManager()->detach($entity);
}

$this->inBatchMode = false;
}
Expand Down
19 changes: 12 additions & 7 deletions app/bundles/AssetBundle/Entity/Asset.php
Original file line number Diff line number Diff line change
Expand Up @@ -1231,11 +1231,17 @@ public static function validateFile($object, ExecutionContextInterface $context)
->setTranslationDomain('validators')
->addViolation();
}
$loader = new ParameterLoader();
$parameters = $loader->getParameterBag();
$mimeTypesAllowed = $parameters->get('allowed_mimetypes');
$parameters = (new ParameterLoader())->getParameterBag();
$extensionsAllowed = $parameters->get('allowed_extensions');
$mimeTypesMap = $parameters->get('allowed_mimetypes');
$mimeTypesAllowed = array_intersect_key($mimeTypesMap, array_flip($extensionsAllowed));

if (!empty($object->getFileMimeType()) && !in_array($object->getFileMimeType(), $mimeTypesAllowed)) {
$fileMimeType = $object->getFileMimeType();
$fileExtension = strtolower($object->getExtension() ?? '');
$lowercaseMimeTypes = array_change_key_case($mimeTypesAllowed, CASE_LOWER);
$lowercaseMimeValues = array_map('strtolower', $mimeTypesAllowed);

if (!empty($fileMimeType) && array_key_exists($fileExtension, $lowercaseMimeTypes) && !in_array(strtolower($fileMimeType), $lowercaseMimeValues, true)) {
$context->buildViolation('mautic.asset.asset.error.invalid.mimetype', [
'%fileMimetype%'=> $object->getFileMimeType(),
'%mimetypes%' => implode(', ', $mimeTypesAllowed),
Expand All @@ -1244,9 +1250,8 @@ public static function validateFile($object, ExecutionContextInterface $context)
->addViolation();
}

$extensionsAllowed = array_keys($mimeTypesAllowed);
$fileType = $object->getExtension();
if (null !== $object->getExtension() && !in_array($fileType, $extensionsAllowed)) {
$fileType = $object->getExtension();
if (null !== $fileType && !in_array(strtolower($fileType), array_map('strtolower', $extensionsAllowed), true)) {
$context->buildViolation('mautic.asset.asset.error.file.extension', [
'%fileExtension%'=> $object->getExtension(),
'%extensions%' => implode(', ', $extensionsAllowed),
Expand Down
6 changes: 4 additions & 2 deletions app/bundles/AssetBundle/Entity/DownloadRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,13 @@ public function getLeadDownloads($leadId = null, array $options = [])
->leftJoin('d', MAUTIC_TABLE_PREFIX.'assets', 'a', 'd.asset_id = a.id');

if ($leadId) {
$query->where('d.lead_id = '.(int) $leadId);
$query->where('d.lead_id = :leadId')
->setParameter('leadId', $leadId);
}

if (isset($options['search']) && $options['search']) {
$query->andWhere($query->expr()->like('a.title', $query->expr()->literal('%'.$options['search'].'%')));
$query->andWhere('a.title LIKE :search')
->setParameter('search', '%'.$options['search'].'%');
}

return $this->getTimelineResults($query, $options, 'a.title', 'd.date_download', [], ['date_download'], null, 'd.id');
Expand Down
19 changes: 11 additions & 8 deletions app/bundles/AssetBundle/EventListener/UploadSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,11 @@ public function onPostUpload(PostUploadEvent $event): void
*/
public function onUploadValidation(ValidationEvent $event): void
{
$file = $event->getFile();
$mimetypes = $this->coreParametersHelper->get('allowed_mimetypes');
$extensions = array_keys($mimetypes);
$maxSize = $this->assetModel->getMaxUploadSize('B');
$file = $event->getFile();
$extensions = $this->coreParametersHelper->get('allowed_extensions');
$configuredMimeTypes = $this->coreParametersHelper->get('allowed_mimetypes');
$allowedMimeTypes = array_intersect_key($configuredMimeTypes, array_flip($extensions));
$maxSize = $this->assetModel->getMaxUploadSize('B');

if (null === $file) {
return;
Expand All @@ -81,10 +82,12 @@ public function onUploadValidation(ValidationEvent $event): void
throw new ValidationException($e->getMessage());
}

try {
$this->checkMimeType($file->getMimeType(), $mimetypes, 'mautic.asset.asset.error.file.mimetype');
} catch (FileInvalidException $e) {
throw new ValidationException($e->getMessage());
if (array_key_exists(strtolower($file->getExtension()), array_change_key_case($configuredMimeTypes, CASE_LOWER))) {
try {
$this->checkMimeType($file->getMimeType(), $allowedMimeTypes, 'mautic.asset.asset.error.file.mimetype');
} catch (FileInvalidException $e) {
throw new ValidationException($e->getMessage());
}
}
}

Expand Down
9 changes: 4 additions & 5 deletions app/bundles/AssetBundle/Form/Type/AssetType.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,14 @@ public function validateExtension($object, ExecutionContextInterface $context):
if (empty($object)) {
return;
}
$parameters = (new ParameterLoader())->getParameterBag();
$mimeTypesAllowed = $parameters->get('allowed_mimetypes');
$extensions = array_keys($mimeTypesAllowed);
$fileName = $object;
$parameters = (new ParameterLoader())->getParameterBag();
$extensions = $parameters->get('allowed_extensions');
$fileName = $object;
if (!is_string($object) && $object instanceof Asset) {
$fileName = $object->getOriginalFileName();
}
$fileExtension = pathinfo($fileName, PATHINFO_EXTENSION);
if (!in_array($fileExtension, $extensions, true)) {
if (!in_array(strtolower($fileExtension), array_map('strtolower', $extensions), true)) {
$context->buildViolation('mautic.asset.asset.error.file.extension', [
'%fileExtension%'=> $fileExtension,
'%extensions%' => implode(', ', $extensions),
Expand Down
11 changes: 11 additions & 0 deletions app/bundles/CampaignBundle/Assets/css/campaign.css
Original file line number Diff line number Diff line change
Expand Up @@ -420,3 +420,14 @@
.campaign-event-stat-divider {
padding: 0 4px;
}


/* Text overflow handling for campaign source widget */
.list-campaign-source .campaign-event-name {
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
vertical-align: middle;
white-space: nowrap;
}
30 changes: 14 additions & 16 deletions app/bundles/CampaignBundle/Entity/LeadEventLogRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ public function getLeadLogs($leadId = null, array $options = [])
->setParameter('eventType', 'decision');

if ($leadId) {
$query->where('ll.lead_id = '.(int) $leadId);
$query->where('ll.lead_id = :leadId')
->setParameter('leadId', $leadId);
}

if (isset($options['scheduledState'])) {
Expand All @@ -123,12 +124,12 @@ public function getLeadLogs($leadId = null, array $options = [])
if (isset($options['search']) && $options['search']) {
$query->andWhere(
$query->expr()->or(
$query->expr()->like('e.name', $query->expr()->literal('%'.$options['search'].'%')),
$query->expr()->like('e.description', $query->expr()->literal('%'.$options['search'].'%')),
$query->expr()->like('c.name', $query->expr()->literal('%'.$options['search'].'%')),
$query->expr()->like('c.description', $query->expr()->literal('%'.$options['search'].'%'))
$query->expr()->like('e.name', ':search'),
$query->expr()->like('e.description', ':search'),
$query->expr()->like('c.name', ':search'),
$query->expr()->like('c.description', ':search')
)
);
)->setParameter('search', '%'.$options['search'].'%');
}

return $this->getTimelineResults($query, $options, 'e.name', 'll.date_triggered', ['metadata'], ['dateTriggered', 'triggerDate'], null, 'll.id');
Expand Down Expand Up @@ -365,30 +366,27 @@ public function getChartQuery($options): array
->join('ll', MAUTIC_TABLE_PREFIX.'campaign_events', 'e', 'e.id = ll.event_id');

if (isset($options['channel'])) {
$query->andwhere("e.channel = '".$options['channel']."'");
$query->andWhere('e.channel = '.$query->expr()->literal($options['channel']));
}

if (isset($options['channelId'])) {
$query->andwhere('e.channel_id = '.(int) $options['channelId']);
$query->andWhere('e.channel_id = '.(int) $options['channelId']);
}

if (isset($options['type'])) {
$query->andwhere("e.type = '".$options['type']."'");
$query->andWhere('e.type = '.$query->expr()->literal($options['type']));
}

if (isset($options['logChannel'])) {
$query->andwhere("ll.channel = '".$options['logChannel']."'");
$query->andWhere('ll.channel = '.$query->expr()->literal($options['logChannel']));
}

if (isset($options['logChannelId'])) {
$query->andwhere('ll.channel_id = '.(int) $options['logChannelId']);
$query->andWhere('ll.channel_id = '.(int) $options['logChannelId']);
}

if (!isset($options['is_scheduled'])) {
$query->andWhere($query->expr()->eq('ll.is_scheduled', 0));
} else {
$query->andWhere($query->expr()->eq('ll.is_scheduled', 1));
}
$isScheduled = isset($options['is_scheduled']) ? 1 : 0;
$query->andWhere('ll.is_scheduled = '.$isScheduled);

return $chartQuery->fetchTimeData('('.$query.')', 'date_triggered');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{% endif %}
<div class="campaign-event-content">
<div class="h5 fw-b mb-5 campaign-source-title">{{'mautic.campaign.form.lead_source'|trans}}</div>
<div><span class="campaign-event-name ellipsis"><i class="mr-xs campaign-event-icon {% if 'lists' == sourceType %}ri-pie-chart-line{% else %}ri-survey-line{% endif %}"></i>{{ names|default(null) }}</span></div>
<div><span class="campaign-event-name ellipsis" title="{{ names|default(null) }}"><i class="mr-xs campaign-event-icon {% if 'lists' == sourceType %}ri-pie-chart-line{% else %}ri-survey-line{% endif %}"></i>{{ names|default(null) }}</span></div>
</div>

{% if update is not defined or update is empty %}
Expand Down
3 changes: 1 addition & 2 deletions app/bundles/CategoryBundle/Controller/CategoryController.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,6 @@ public function newAction(Request $request, $bundle)
$cancelled = $valid = false;
$method = $request->getMethod();
$inForm = $this->getInFormValue($request, $method);
$showSelect = $request->get('show_bundle_select', false);

// not found
if (!$this->security->isGranted($model->getPermissionBase($bundle).':create')) {
Expand All @@ -229,7 +228,7 @@ public function newAction(Request $request, $bundle)
'objectAction' => 'new',
'bundle' => $bundle,
]);
$form = $model->createForm($entity, $this->formFactory, $action, ['bundle' => $bundle, 'show_bundle_select' => $showSelect]);
$form = $model->createForm($entity, $this->formFactory, $action, ['bundle' => $bundle, 'show_bundle_select' => 'category' === $bundle]);
$form['inForm']->setData($inForm);
// /Check for a submitted form and process it
if (Request::METHOD_POST === $method) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public function testNewActionWithInForm(): void
$crawler->addHtmlContent($html);
$saveButton = $crawler->selectButton('category_form[buttons][save]');
$form = $saveButton->form();
$form['category_form[bundle]']->setValue('category');
$form['category_form[bundle]']->setValue('global');
$form['category_form[title]']->setValue('Test');
$form['category_form[isPublished]']->setValue('1');
$form['category_form[inForm]']->setValue('1');
Expand Down Expand Up @@ -118,6 +118,26 @@ public function testEditLockCategory(): void
$this->assertStringContainsString('is currently checked out by', $this->client->getResponse()->getContent());
}

public function testTypeFieldPersistsAfterValidationFailure(): void
{
$crawler = $this->client->request(Request::METHOD_GET, 's/categories/category/new');
$clientResponse = json_decode($this->client->getResponse()->getContent(), true);
$html = $clientResponse['newContent'];
$crawler->addHtmlContent($html);
$saveButton = $crawler->selectButton('category_form[buttons][save]');
$form = $saveButton->form();
$form['category_form[bundle]']->setValue('global');
$form['category_form[title]']->setValue('');
$form['category_form[isPublished]']->setValue('1');
$form['category_form[inForm]']->setValue('1');

$this->client->submit($form);
Assert::assertTrue($this->client->getResponse()->isOk());
$clientResponse = $this->client->getResponse();
$body = json_decode($clientResponse->getContent(), true);
$this->assertNotEmpty($crawler->filter('select#category_form_bundle')->count(), 'The "Type" (bundle) field should remain visible after validation failure.');
}

public function testDeleteUsedInStage(): void
{
$category = new Category();
Expand Down
9 changes: 5 additions & 4 deletions app/bundles/ChannelBundle/Entity/MessageQueueRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,14 @@ public function getLeadTimelineEvents($leadId = null, array $options = [])
mq.scheduled_date as scheduledDate, mq.last_attempt as lastAttempt, mq.date_sent as dateSent');

if ($leadId) {
$query->where('mq.lead_id = '.(int) $leadId);
$query->where('mq.lead_id = :leadId')
->setParameter('leadId', $leadId);
}

if (isset($options['search']) && $options['search']) {
$query->andWhere($query->expr()->or(
$query->expr()->like('mq.channel', $query->expr()->literal('%'.$options['search'].'%'))
));
$query->andWhere(
$query->expr()->like('mq.channel', ':search')
)->setParameter('search', '%'.$options['search'].'%');
}

return $this->getTimelineResults($query, $options, 'mq.channel', 'mq.date_published', [], ['dateAdded'], null, 'mq.id');
Expand Down
Loading