Skip to content

Commit 033aa54

Browse files
TatevikGrtatevikg1
andauthored
Blacklist, Subscribepage, Bounce processing, Translator, Message processor (#357)
* Blacklist (#352) * Blacklisted * user repository methods * fix configs * add test * fix: phpmd * fix: repo configs * return a created resource --------- Co-authored-by: Tatevik <[email protected]> * Subscribepage (#353) * subscriber page manager * owner entity * test * ci fix * getByPage data --------- Co-authored-by: Tatevik <[email protected]> * Bounceregex manager (#354) * BounceRegexManager * Fix manager directory * Prop name update admin -> adminId --------- Co-authored-by: Tatevik <[email protected]> * Bounce processing command (#355) * BounceManager * Add bounce email * Move to the processor dir * SendProcess lock service * ClientIp + SystemInfo * ProcessBouncesCommand * ProcessBouncesCommand all methods * BounceProcessingService * AdvancedBounceRulesProcessor * UnidentifiedBounceReprocessor * ConsecutiveBounceHandler * Refactor * BounceDataProcessor * ClientFactory + refactor * BounceProcessorPass * Register services + phpstan fix * PhpMd * PhpMd CyclomaticComplexity * PhpCodeSniffer * Tests * Refactor * Add tests * More tests * Fix tests --------- Co-authored-by: Tatevik <[email protected]> * EventLog + translator (#356) * EventLogManager * Log failed logins + translate messages * weblate * test fix * Use translations * Fix pipeline * Weblate * Deprecate DB translation table --------- Co-authored-by: Tatevik <[email protected]> * Access level check (#358) * OwnableInterface * PermissionChecker * Check related * Register service + test * Style fix --------- Co-authored-by: Tatevik <[email protected]> * Message processor (#359) * MessageStatusEnum * Status validate * Embargo check * IspRestrictions * IspRestrictions * SendRateLimiter * UserMessageStatus * Refactor * RateLimitedCampaignMailer * RateLimitedCampaignMailerTest * Rate limit initialized from history * Check maintenance mode * Max processing time limiter --------- Co-authored-by: Tatevik <[email protected]> * Exceptions + translations (#361) * MissingMessageIdException * MailboxConnectionException * BadMethodCallException (style fix) * Translate user facing messages * Translate * Translate PasswordResetMessageHandler texts * Translate SubscriberConfirmationMessageHandler texts * MessageBuilder exceptions * BlacklistEmailAndDeleteBounceHandler * BlacklistEmailHandler - AdvancedBounceRulesProcessor * AdvancedBounceRulesProcessor * BounceStatus * CampaignProcessor * UnidentifiedBounceReprocessor * Style fix * Test fix * ConsecutiveBounceHandler --------- Co-authored-by: Tatevik <[email protected]> * Fix autowiring * Reset subscriber bounce count * Install RssFeedBundle * Update import logic, add dynamic attribute repository (#362) * Skip password and modified fields while import, do not subscribe blacklisted users * DefaultConfigProvider * Use ConfigProvider in ProcessQueueCommand * Use ConfigProvider in SubscriberCsvImporter + send email * Rename paramProvider * Test fix * AttributeValueProvider for dynamic tables * Update: config provider * Translations in default configs * Email with messageHandler * Style fix * PhpCs fix * Fix configs * replace list names * Add tests + fix * Add more tests + fix handler --------- Co-authored-by: Tatevik <[email protected]> * Refactor import add subscriber history (#363) * Add blacklisted stat to import result * Add history * Add translations * addHistory for unconfirmed * Refactor * Add changeSetDto * Do not send email on creating without any list * Flush in controller * Add test --------- Co-authored-by: Tatevik <[email protected]> * Em flush (#364) * createSubscriberList * ProcessQueueCommand * Dispatch CampaignProcessorMessage for processing * Fix tests * Fix style * CleanUpOldSessionTokens * Move bounce processing into the Bounce folder * delete/remove * Remove hardcoded TatevikGrRssBundle mapping * Fix configs * Add sync * Fix: DQL in MessageRepository.php * PhpMD * SubscriberBlacklistService, SubscriberBlacklistManager * AdministratorManager * BounceManager * SendProcessManager * TemplateManager * SubscribePageManager * Fix: tests * BounceManager * rest of flushes * save * fix test * CouplingBetweenObjects 15 --------- Co-authored-by: Tatevik <[email protected]> * Add test * Update: docs * Migrations (mysql psql) (#366) * OnlyOrmTablesFilter * Current * Admin * Init migration * In progress * Fix mapping * Fix tests * Migrate * Separate MySql * Use psql * Rename indexes * PostgreSqlPlatform * MySqlSqlPlatform rename indexes * Fix: cs * Fix: test configs * Add migration template * PR agent (#365) * .coderabbit.yaml This reverts commit 2246e49. --------- Co-authored-by: Tatevik <[email protected]> * rename template * After review 0 * After review 1 * Fix MySql migrations * After review 2 --------- Co-authored-by: Tatevik <[email protected]> * Import by foreign key (#367) * Import with a foreign key --------- Co-authored-by: Tatevik <[email protected]> * Insert initial admin command (#368) * Update: codeRabbit configs * Update: Use command for initial admin insert --------- Co-authored-by: Tatevik <[email protected]> * Update pull request template --------- Co-authored-by: Tatevik <[email protected]>
1 parent 97a55d6 commit 033aa54

File tree

314 files changed

+18000
-1802
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

314 files changed

+18000
-1802
lines changed

.coderabbit.yaml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
2+
language: "en-US"
3+
tone_instructions: "chill"
4+
reviews:
5+
profile: "chill"
6+
high_level_summary: true
7+
collapse_walkthrough: true
8+
suggested_labels: false
9+
high_level_summary_in_walkthrough: false
10+
poem: false
11+
path_instructions:
12+
- path: "src/Domain/**"
13+
instructions: |
14+
You are reviewing PHP domain-layer code. Enforce strict domain purity:
15+
- ❌ Do not allow infrastructure persistence side effects here.
16+
- Flag ANY usage of Doctrine persistence APIs, especially:
17+
- $entityManager->flush(...), $this->entityManager->flush(...)
18+
- $em->persist(...), $em->remove(...)
19+
- direct transaction control ($em->beginTransaction(), commit(), rollback())
20+
- If found, request moving these calls to application-layer Command handlers or background Jobs.
21+
- Also flag repositories in Domain that invoke flush/transactional logic; Domain repositories should be abstractions without side effects.
22+
- Encourage domain events/outbox or return-values to signal write-intent, leaving orchestration to Commands/Jobs.
23+
24+
- path: "src/**/Command/**"
25+
instructions: |
26+
Application layer (Commands/Handlers) is the right place to coordinate persistence.
27+
- ✅ It is acceptable to call $entityManager->flush() here.
28+
- Check that flush is used atomically (once per unit of work) after all domain operations.
29+
- Ensure no domain entity or domain service is calling flush; only the handler orchestrates it.
30+
- Prefer $em->transactional(...) or explicit try/catch with rollback on failure.
31+
32+
- path: "src/**/MessageHandler/**"
33+
instructions: |
34+
Background jobs/workers may perform persistence.
35+
- ✅ Allow $entityManager->flush() here when the job is the orchestration boundary.
36+
- Verify idempotency and that flush frequency is appropriate (batching where practical).
37+
- Ensure no domain-layer code invoked by the job performs flush/transaction control.
38+
39+
auto_review:
40+
enabled: true
41+
base_branches:
42+
- ".*"
43+
drafts: false
44+
# ignore_title_keywords:
45+
# - ''
46+
47+
#knowledge_base:
48+
# code_guidelines:
49+
# filePatterns:
50+
# - ".github/AGENT.md"

.github/AGENT.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# AGENT.md — Code Review Knowledge Base for phpList/core
2+
3+
## 🧭 Repository Overview
4+
5+
This repository is the **core package** of **phpList 4**, a modular and extensible email campaign management system.
6+
7+
- **Purpose:** Provides the reusable foundation and framework for phpList applications and modules.
8+
- **Consumers:** `phpList/web-frontend`, `phpList/rest-api`, and `phpList/base-distribution`.
9+
- **Core responsibilities:**
10+
- Application bootstrapping and service configuration
11+
- Database ORM (Doctrine) integration
12+
- Command-line utilities (Symfony Console)
13+
- Async email sending via Symfony Messenger
14+
- Logging, configuration, routing, dependency injection
15+
- Schema definition and updates
16+
17+
> **Note:** This repository does *not* contain UI or REST endpoints. Those are part of other phpList packages.
18+
19+
---
20+
21+
## ⚙️ Tech Stack
22+
23+
| Category | Technology |
24+
|-----------|-------------|
25+
| Language | PHP ≥ 8.1 |
26+
| Framework | Symfony 6.x components |
27+
| ORM | Doctrine ORM 3.x |
28+
| Async / Queue | Symfony Messenger |
29+
| Tests | PHPUnit |
30+
| Static analysis | PHPStan |
31+
| Docs | PHPDocumentor |
32+
| License | AGPL-3.0 |
33+
34+
---
35+
36+
## 📁 Project Structure
37+
- .github/ CI/CD and PR templates
38+
- bin/ Symfony console entrypoints
39+
- config/ Application & environment configs
40+
- docs/ Developer documentation and generated API docs
41+
- public/ Public entrypoint for local web server
42+
- resources/Database/ Canonical SQL schema
43+
- src/ Core PHP source code
44+
- tests/ PHPUnit test suites
45+
- composer.json Package metadata and dependencies
46+
- phpunit.xml.dist Test configuration
47+
- phpstan.neon Static analysis configuration
48+
49+
---
50+
51+
## 💡 Code Design Principles
52+
53+
1. **Modularity:**
54+
The core remains framework-like — decoupled from frontend or API layers.
55+
56+
2. **Dependency Injection:**
57+
Use Symfony’s service container; avoid static/global dependencies.
58+
59+
3. **Strict Typing:**
60+
Always use `declare(strict_types=1);` and explicit type declarations.
61+
62+
4. **Doctrine Entities:**
63+
- Keep entities simple (no business logic).
64+
- Mirror schema changes in `resources/Database/Schema.sql`.
65+
- Maintain backward compatibility with phpList 3.
66+
67+
5. **Symfony Best Practices:**
68+
Follow Symfony structure and naming conventions. Use annotations or attributes for routing.
69+
70+
6. **Error Handling & Logging:**
71+
- Prefer structured logging via Graylog.
72+
- Catch and handle exceptions at service or command boundaries.
73+
74+
7. **Async Email:**
75+
- Uses Symfony Messenger.
76+
- Handlers must be idempotent and retry-safe.
77+
- Avoid blocking or synchronous email sending.
78+
79+
---
80+
81+
## 🧪 Testing Guidelines
82+
83+
- **Framework:** PHPUnit
84+
- **Database:** SQLite or mocks for unit tests; MySQL for integration tests.
85+
- **Coverage target:** ≥85% for core logic.
86+
- **Naming:** Mirror source structure (e.g., `Mailer.php``MailerTest.php`).
87+
88+
89+
## 🧱 Code Style
90+
91+
- Follow PSR-12 and Symfony coding conventions.
92+
- Match the current codebase’s formatting and spacing.
93+
- Use meaningful, consistent naming.
94+
- Apply a single responsibility per class.
95+
96+
97+
## 🔄 Pull Request Review Guidelines
98+
### 🔐 Security Review Notes
99+
100+
- Do not log sensitive data (passwords, tokens, SMTP credentials).
101+
- Sanitize all user and external inputs.
102+
- Always use parameterized Doctrine queries.
103+
- Async jobs must be retry-safe and idempotent.

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,3 @@
1-
### Summary
2-
3-
Provide a general description of the code changes in your pull request …
4-
were there any bugs you had fixed? If so, mention them. If these bugs have open
5-
GitHub issues, be sure to tag them here as well, to keep the conversation
6-
linked together.
7-
8-
9-
### Unit test
10-
11-
Are your changes covered with unit tests, and do they not break anything?
12-
13-
You can run the existing unit tests using this command:
14-
15-
vendor/bin/phpunit tests/
16-
17-
18-
### Code style
19-
20-
Have you checked that you code is well-documented and follows the PSR-2 coding
21-
style?
22-
23-
You can check for this using this command:
24-
25-
vendor/bin/phpcs --standard=PSR2 src/ tests/
26-
27-
28-
### Other Information
29-
30-
If there's anything else that's important and relevant to your pull
31-
request, mention that information here. This could include benchmarks,
32-
or other information.
33-
34-
If you are updating any of the CHANGELOG files or are asked to update the
35-
CHANGELOG files by reviewers, please add the CHANGELOG entry at the top of the file.
1+
"@coderabbitai summary"
362

373
Thanks for contributing to phpList!

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,4 @@ jobs:
7575
- name: Running PHPMD
7676
run: vendor/bin/phpmd src/ text config/PHPMD/rules.xml;
7777
- name: Running PHP_CodeSniffer
78-
run: vendor/bin/phpcs --standard=config/PhpCodeSniffer/ bin/ src/ tests/ public/;
78+
run: vendor/bin/phpcs --standard=config/PhpCodeSniffer/ --ignore=*/Migrations/* bin/ src/ tests/ public/;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: I18n Validate
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- 'resources/translations/**/*.xlf'
7+
- 'composer.lock'
8+
- 'composer.json'
9+
10+
jobs:
11+
validate-xliff:
12+
runs-on: ubuntu-22.04
13+
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
php: ['8.1']
18+
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
23+
- name: Setup PHP
24+
uses: shivammathur/setup-php@v2
25+
with:
26+
php-version: ${{ matrix.php }}
27+
extensions: imap, zip
28+
tools: composer:v2
29+
coverage: none
30+
31+
- name: Cache Composer packages
32+
uses: actions/cache@v4
33+
with:
34+
path: |
35+
~/.composer/cache/files
36+
key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}
37+
restore-keys: |
38+
${{ runner.os }}-composer-${{ matrix.php }}-
39+
40+
- name: Install dependencies (no dev autoloader scripts)
41+
run: |
42+
set -euo pipefail
43+
composer install --no-interaction --no-progress --prefer-dist
44+
45+
- name: Lint XLIFF with Symfony
46+
run: |
47+
set -euo pipefail
48+
# Adjust the directory to match your repo layout
49+
php bin/console lint:xliff resources/translations
50+
51+
- name: Validate XLIFF XML with xmllint
52+
run: |
53+
set -euo pipefail
54+
sudo apt-get update
55+
sudo apt-get install -y --no-install-recommends libxml2-utils
56+
# Adjust root dir; prune vendor; accept spaces/newlines safely
57+
find resources/translations -type f -name '*.xlf' -not -path '*/vendor/*' -print0 \
58+
| xargs -0 -n1 xmllint --noout
59+
60+
- name: Symfony translation sanity (extract dry-run)
61+
run: |
62+
set -euo pipefail
63+
# Show what would be created/updated without writing files
64+
php bin/console translation:extract en \
65+
--format=xlf \
66+
--domain=messages \
67+
--dump-messages \
68+
--no-interaction
69+
# Note: omit --force to keep this a dry-run

.weblate

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# .weblate
2+
---
3+
projects:
4+
- slug: phplist-core
5+
name: phpList core
6+
components:
7+
- slug: messages
8+
name: Messages
9+
files:
10+
# {language} is Weblate’s placeholder (e.g., fr, de, es)
11+
- src: resources/translations/messages.en.xlf
12+
template: true
13+
# Where localized files live (mirrors Symfony layout)
14+
target: resources/translations/messages.{language}.xlf
15+
file_format: xliff
16+
language_code_style: bcp
17+
# Ensure placeholders like %name% are preserved
18+
parse_file_headers: true
19+
check_flags:
20+
- xml-invalid
21+
- placeholders
22+
- urls
23+
- accelerated

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,11 @@ For detailed configuration instructions, see the [Mailer Transports documentatio
214214
## Copyright
215215

216216
phpList is copyright (C) 2000-2025 [phpList Ltd](https://www.phplist.com/).
217+
218+
219+
### Translations
220+
command to extract translation strings
221+
222+
```bash
223+
php bin/console translation:extract --force en --format=xlf
224+
```

composer.json

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@
3232
"role": "Maintainer"
3333
}
3434
],
35+
"repositories": [
36+
{
37+
"type": "vcs",
38+
"url": "https://github.com/TatevikGr/rss-bundle.git"
39+
}
40+
],
3541
"support": {
3642
"issues": "https://github.com/phpList/core/issues",
3743
"forum": "https://discuss.phplist.org/",
@@ -68,7 +74,10 @@
6874
"symfony/sendgrid-mailer": "^6.4",
6975
"symfony/twig-bundle": "^6.4",
7076
"symfony/messenger": "^6.4",
71-
"symfony/lock": "^6.4"
77+
"symfony/lock": "^6.4",
78+
"webklex/php-imap": "^6.2",
79+
"ext-imap": "*",
80+
"tatevikgr/rss-feed": "dev-main"
7281
},
7382
"require-dev": {
7483
"phpunit/phpunit": "^9.5",
@@ -140,7 +149,8 @@
140149
"Doctrine\\Bundle\\DoctrineBundle\\DoctrineBundle",
141150
"Doctrine\\Bundle\\MigrationsBundle\\DoctrineMigrationsBundle",
142151
"PhpList\\Core\\EmptyStartPageBundle\\EmptyStartPageBundle",
143-
"FOS\\RestBundle\\FOSRestBundle"
152+
"FOS\\RestBundle\\FOSRestBundle",
153+
"TatevikGr\\RssFeedBundle\\RssFeedBundle"
144154
],
145155
"routes": {
146156
"homepage": {
@@ -149,5 +159,10 @@
149159
}
150160
}
151161
}
162+
},
163+
"config": {
164+
"allow-plugins": {
165+
"php-http/discovery": true
166+
}
152167
}
153168
}

config/PHPMD/rules.xml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
PHPMD rules for phpList
55
</description>
66

7+
<exclude-pattern>*/Migrations/*</exclude-pattern>
8+
79
<!-- rules from the "clean code" rule set -->
810
<rule ref="rulesets/cleancode.xml/BooleanArgumentFlag"/>
911
<rule ref="rulesets/cleancode.xml/StaticAccess">
@@ -45,13 +47,17 @@
4547
<rule ref="rulesets/design.xml/GotoStatement"/>
4648
<rule ref="rulesets/design.xml/NumberOfChildren"/>
4749
<rule ref="rulesets/design.xml/DepthOfInheritance"/>
48-
<rule ref="rulesets/design.xml/CouplingBetweenObjects"/>
50+
<rule ref="rulesets/design.xml/CouplingBetweenObjects">
51+
<properties>
52+
<property name="maximum" value="15"/>
53+
</properties>
54+
</rule>
4955
<rule ref="rulesets/design.xml/DevelopmentCodeFragment"/>
5056

5157
<!-- rules from the "naming" rule set -->
5258
<rule ref="rulesets/naming.xml/ShortVariable">
5359
<properties>
54-
<property name="exceptions" value="id,ip,cc"/>
60+
<property name="exceptions" value="id,ip,cc,io"/>
5561
</properties>
5662
</rule>
5763
<rule ref="rulesets/naming.xml/LongVariable">
@@ -67,5 +73,4 @@
6773
<rule ref="rulesets/unusedcode.xml/UnusedPrivateField"/>
6874
<rule ref="rulesets/unusedcode.xml/UnusedLocalVariable"/>
6975
<rule ref="rulesets/unusedcode.xml/UnusedPrivateMethod"/>
70-
<rule ref="rulesets/unusedcode.xml/UnusedFormalParameter"/>
7176
</ruleset>

0 commit comments

Comments
 (0)