Passed
Push — master ( 6fb715...de9f22 )
by Gabriel
27:49
created

FunFunFactory::newCreditCardUrlConfig()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
crap 1
1
<?php
2
3
declare( strict_types = 1 );
4
5
namespace WMDE\Fundraising\Frontend\Factories;
6
7
use Doctrine\Common\Cache\Cache;
8
use Doctrine\Common\Cache\CacheProvider;
9
use Doctrine\Common\Cache\FilesystemCache;
10
use Doctrine\Common\Cache\VoidCache;
11
use Doctrine\DBAL\Connection;
12
use Doctrine\DBAL\DriverManager;
13
use Doctrine\ORM\EntityManager;
14
use FileFetcher\ErrorLoggingFileFetcher;
15
use FileFetcher\FileFetchingException;
16
use FileFetcher\SimpleFileFetcher;
17
use GuzzleHttp\Client;
18
use NumberFormatter;
19
use Pimple\Container;
20
use Pimple\ServiceProviderInterface;
21
use Psr\Log\LoggerInterface;
22
use Psr\Log\NullLogger;
23
use RemotelyLiving\Doorkeeper\Doorkeeper;
24
use RemotelyLiving\Doorkeeper\Features\Set;
25
use RemotelyLiving\Doorkeeper\Requestor;
26
use Swift_MailTransport;
27
use Swift_NullTransport;
28
use Symfony\Component\Console\Exception\RuntimeException;
29
use Symfony\Component\Filesystem\Filesystem;
30
use Symfony\Component\Stopwatch\Stopwatch;
31
use Symfony\Component\Translation\TranslatorInterface;
32
use Symfony\Component\Validator\Constraint as ValidatorConstraint;
33
use Symfony\Component\Validator\Constraints\Range as RangeConstraint;
34
use Symfony\Component\Validator\Constraints\Required as RequiredConstraint;
35
use Symfony\Component\Validator\Constraints\Type as TypeConstraint;
36
use TNvpServiceDispatcher;
37
use Twig_Environment;
38
use Twig_Extensions_Extension_Intl;
39
use Twig_SimpleFunction;
40
use WMDE\EmailAddress\EmailAddress;
41
use WMDE\Euro\Euro;
42
use WMDE\Fundraising\ContentProvider\ContentProvider;
43
use WMDE\Fundraising\DonationContext\Authorization\DonationAuthorizer;
44
use WMDE\Fundraising\DonationContext\Authorization\DonationTokenFetcher;
45
use WMDE\Fundraising\DonationContext\Authorization\RandomTokenGenerator;
46
use WMDE\Fundraising\DonationContext\Authorization\TokenGenerator;
47
use WMDE\Fundraising\DonationContext\DataAccess\DoctrineCommentFinder;
48
use WMDE\Fundraising\DonationContext\DataAccess\DoctrineDonationAuthorizer;
49
use WMDE\Fundraising\DonationContext\DataAccess\DoctrineDonationEventLogger;
50
use WMDE\Fundraising\DonationContext\DataAccess\DoctrineDonationPrePersistSubscriber;
51
use WMDE\Fundraising\DonationContext\DataAccess\DoctrineDonationRepository;
52
use WMDE\Fundraising\DonationContext\DataAccess\DoctrineDonationTokenFetcher;
53
use WMDE\Fundraising\DonationContext\DataAccess\UniqueTransferCodeGenerator;
54
use WMDE\Fundraising\DonationContext\Domain\Repositories\CommentFinder;
55
use WMDE\Fundraising\DonationContext\Domain\Repositories\DonationRepository;
56
use WMDE\Fundraising\DonationContext\Domain\Validation\DonorValidator;
57
use WMDE\Fundraising\DonationContext\DonationAcceptedEventHandler;
58
use WMDE\Fundraising\DonationContext\Infrastructure\BestEffortDonationEventLogger;
59
use WMDE\Fundraising\DonationContext\Infrastructure\DonationConfirmationMailer;
60
use WMDE\Fundraising\DonationContext\Infrastructure\DonationEventLogger;
61
use WMDE\Fundraising\DonationContext\Infrastructure\LoggingCommentFinder;
62
use WMDE\Fundraising\DonationContext\Infrastructure\LoggingDonationRepository;
63
use WMDE\Fundraising\DonationContext\Infrastructure\TemplateMailerInterface as DonationTemplateMailerInterface;
64
use WMDE\Fundraising\DonationContext\UseCases\AddComment\AddCommentUseCase;
65
use WMDE\Fundraising\DonationContext\UseCases\AddComment\AddCommentValidator;
66
use WMDE\Fundraising\DonationContext\UseCases\AddDonation\AddDonationPolicyValidator;
67
use WMDE\Fundraising\DonationContext\UseCases\AddDonation\AddDonationUseCase;
68
use WMDE\Fundraising\DonationContext\UseCases\AddDonation\AddDonationValidator;
69
use WMDE\Fundraising\DonationContext\UseCases\AddDonation\InitialDonationStatusPicker;
70
use WMDE\Fundraising\DonationContext\UseCases\AddDonation\ReferrerGeneralizer;
71
use WMDE\Fundraising\DonationContext\UseCases\CancelDonation\CancelDonationUseCase;
72
use WMDE\Fundraising\DonationContext\UseCases\CreditCardPaymentNotification\CreditCardNotificationUseCase;
73
use WMDE\Fundraising\DonationContext\UseCases\GetDonation\GetDonationUseCase;
74
use WMDE\Fundraising\DonationContext\UseCases\HandlePayPalPaymentNotification\HandlePayPalPaymentCompletionNotificationUseCase;
75
use WMDE\Fundraising\DonationContext\UseCases\ListComments\ListCommentsUseCase;
76
use WMDE\Fundraising\DonationContext\UseCases\SofortPaymentNotification\SofortPaymentNotificationUseCase;
77
use WMDE\Fundraising\DonationContext\UseCases\UpdateDonor\UpdateDonorUseCase;
78
use WMDE\Fundraising\DonationContext\UseCases\UpdateDonor\UpdateDonorValidator;
79
use WMDE\Fundraising\DonationContext\UseCases\ValidateDonor\ValidateDonorUseCase;
80
use WMDE\Fundraising\Frontend\BucketTesting\RandomBucketSelection;
81
use WMDE\Fundraising\Frontend\Infrastructure\Cache\AllOfTheCachePurger;
82
use WMDE\Fundraising\Frontend\Infrastructure\Cache\AuthorizedCachePurger;
83
use WMDE\Fundraising\Frontend\BucketTesting\Campaign;
84
use WMDE\Fundraising\Frontend\BucketTesting\CampaignBuilder;
85
use WMDE\Fundraising\Frontend\BucketTesting\CampaignCollection;
86
use WMDE\Fundraising\Frontend\BucketTesting\CampaignConfigurationLoader;
87
use WMDE\Fundraising\Frontend\BucketTesting\CampaignConfigurationLoaderInterface;
88
use WMDE\Fundraising\Frontend\BucketTesting\CampaignFeatureBuilder;
89
use WMDE\Fundraising\Frontend\Infrastructure\CookieBuilder;
90
use WMDE\Fundraising\Frontend\BucketTesting\DoorkeeperFeatureToggle;
91
use WMDE\Fundraising\Frontend\BucketTesting\FeatureToggle;
92
use WMDE\Fundraising\Frontend\BucketTesting\BucketSelector;
93
use WMDE\Fundraising\Frontend\Infrastructure\InternetDomainNameValidator;
94
use WMDE\Fundraising\Frontend\Infrastructure\LoggingMailer;
95
use WMDE\Fundraising\Frontend\Infrastructure\Payment\LoggingPaymentNotificationVerifier;
96
use WMDE\Fundraising\Frontend\Infrastructure\MailTemplateFilenameTraversable;
97
use WMDE\Fundraising\Frontend\Infrastructure\Messenger;
98
use WMDE\Fundraising\Frontend\Infrastructure\OperatorMailer;
99
use WMDE\Fundraising\Frontend\Infrastructure\PageViewTracker;
100
use WMDE\Fundraising\Frontend\Infrastructure\Payment\PaymentNotificationVerifier;
101
use WMDE\Fundraising\Frontend\Infrastructure\Payment\PayPalPaymentNotificationVerifier;
102
use WMDE\Fundraising\Frontend\Infrastructure\PiwikServerSideTracker;
103
use WMDE\Fundraising\Frontend\Infrastructure\ProfilerDataCollector;
104
use WMDE\Fundraising\Frontend\Infrastructure\ServerSideTracker;
105
use WMDE\Fundraising\Frontend\Infrastructure\TemplateBasedMailer;
106
use WMDE\Fundraising\Frontend\Infrastructure\UrlGenerator;
107
use WMDE\Fundraising\Frontend\Infrastructure\WordListFileReader;
108
use WMDE\Fundraising\Frontend\Infrastructure\Payment\KontoCheckBankDataGenerator;
109
use WMDE\Fundraising\Frontend\Infrastructure\Payment\KontoCheckIbanValidator;
110
use WMDE\Fundraising\Frontend\Infrastructure\Payment\McpCreditCardService;
111
use WMDE\Fundraising\Frontend\Presentation\AmountFormatter;
112
use WMDE\Fundraising\Frontend\Presentation\ContentPage\PageSelector;
113
use WMDE\Fundraising\Frontend\Presentation\FilePrefixer;
114
use WMDE\Fundraising\Frontend\Presentation\GreetingGenerator;
115
use WMDE\Fundraising\Frontend\Presentation\Honorifics;
116
use WMDE\Fundraising\Frontend\Presentation\PaymentTypesSettings;
117
use WMDE\Fundraising\Frontend\Presentation\Presenters\AddSubscriptionHtmlPresenter;
118
use WMDE\Fundraising\Frontend\Presentation\Presenters\AddSubscriptionJsonPresenter;
119
use WMDE\Fundraising\Frontend\Presentation\Presenters\CancelDonationHtmlPresenter;
120
use WMDE\Fundraising\Frontend\Presentation\Presenters\CancelMembershipApplicationHtmlPresenter;
121
use WMDE\Fundraising\Frontend\Presentation\Presenters\CommentListHtmlPresenter;
122
use WMDE\Fundraising\Frontend\Presentation\Presenters\CommentListJsonPresenter;
123
use WMDE\Fundraising\Frontend\Presentation\Presenters\CommentListRssPresenter;
124
use WMDE\Fundraising\Frontend\Presentation\Presenters\ConfirmSubscriptionHtmlPresenter;
125
use WMDE\Fundraising\Frontend\Presentation\Presenters\CreditCardNotificationPresenter;
126
use WMDE\Fundraising\Frontend\Presentation\Presenters\CreditCardPaymentUrlGenerator;
127
use WMDE\Fundraising\Frontend\Presentation\Presenters\DonationConfirmationHtmlPresenter;
128
use WMDE\Fundraising\Frontend\Presentation\Presenters\DonationFormPresenter;
129
use WMDE\Fundraising\Frontend\Presentation\Presenters\DonationFormViolationPresenter;
130
use WMDE\Fundraising\Frontend\Presentation\Presenters\GetInTouchHtmlPresenter;
131
use WMDE\Fundraising\Frontend\Presentation\Presenters\IbanPresenter;
132
use WMDE\Fundraising\Frontend\Presentation\Presenters\InternalErrorHtmlPresenter;
133
use WMDE\Fundraising\Frontend\Presentation\Presenters\MembershipApplicationConfirmationHtmlPresenter;
134
use WMDE\Fundraising\Frontend\Presentation\Presenters\MembershipFormViolationPresenter;
135
use WMDE\Fundraising\Frontend\Presentation\Presenters\PageNotFoundPresenter;
136
use WMDE\Fundraising\Frontend\Presentation\Presenters\DonorUpdateHtmlPresenter;
137
use WMDE\Fundraising\Frontend\Presentation\TwigTemplate;
138
use WMDE\Fundraising\SubscriptionContext\DataAccess\DoctrineSubscriptionRepository;
139
use WMDE\Fundraising\SubscriptionContext\Domain\Repositories\SubscriptionRepository;
140
use WMDE\Fundraising\SubscriptionContext\Infrastructure\LoggingSubscriptionRepository;
141
use WMDE\Fundraising\SubscriptionContext\UseCases\AddSubscription\AddSubscriptionUseCase;
142
use WMDE\Fundraising\SubscriptionContext\UseCases\ConfirmSubscription\ConfirmSubscriptionUseCase;
143
use WMDE\Fundraising\SubscriptionContext\Validation\SubscriptionDuplicateValidator;
144
use WMDE\Fundraising\SubscriptionContext\Validation\SubscriptionValidator;
145
use WMDE\Fundraising\SubscriptionContext\Infrastructure\TemplateMailerInterface as SubscriptionTemplateMailerInterface;
146
use WMDE\Fundraising\Frontend\UseCases\GetInTouch\GetInTouchUseCase;
147
use WMDE\Fundraising\Frontend\Validation\GetInTouchValidator;
148
use WMDE\Fundraising\Frontend\Validation\IsCustomAmountValidator;
149
use WMDE\Fundraising\Frontend\Validation\TemplateNameValidator;
150
use WMDE\Fundraising\MembershipContext\Authorization\ApplicationAuthorizer;
151
use WMDE\Fundraising\MembershipContext\Authorization\ApplicationTokenFetcher;
152
use WMDE\Fundraising\MembershipContext\Authorization\MembershipTokenGenerator;
153
use WMDE\Fundraising\MembershipContext\DataAccess\DoctrineApplicationPiwikTracker;
154
use WMDE\Fundraising\MembershipContext\DataAccess\DoctrineApplicationRepository;
155
use WMDE\Fundraising\MembershipContext\DataAccess\DoctrineApplicationTokenFetcher;
156
use WMDE\Fundraising\MembershipContext\DataAccess\DoctrineApplicationTracker;
157
use WMDE\Fundraising\MembershipContext\DataAccess\DoctrineMembershipApplicationPrePersistSubscriber;
158
use WMDE\Fundraising\MembershipContext\Domain\Repositories\ApplicationRepository;
159
use WMDE\Fundraising\MembershipContext\Infrastructure\LoggingApplicationRepository;
160
use WMDE\Fundraising\MembershipContext\Infrastructure\TemplateMailerInterface;
161
use WMDE\Fundraising\MembershipContext\MembershipContextFactory;
162
use WMDE\Fundraising\MembershipContext\Tracking\ApplicationPiwikTracker;
163
use WMDE\Fundraising\MembershipContext\Tracking\ApplicationTracker;
164
use WMDE\Fundraising\MembershipContext\UseCases\ApplyForMembership\ApplyForMembershipPolicyValidator;
165
use WMDE\Fundraising\MembershipContext\UseCases\ApplyForMembership\ApplyForMembershipUseCase;
166
use WMDE\Fundraising\MembershipContext\UseCases\ApplyForMembership\MembershipApplicationValidator;
167
use WMDE\Fundraising\MembershipContext\UseCases\ApplyForMembership\MembershipFeeValidator;
168
use WMDE\Fundraising\MembershipContext\UseCases\CancelMembershipApplication\CancelMembershipApplicationUseCase;
169
use WMDE\Fundraising\MembershipContext\UseCases\HandleSubscriptionPaymentNotification\HandleSubscriptionPaymentNotificationUseCase;
170
use WMDE\Fundraising\MembershipContext\UseCases\HandleSubscriptionSignupNotification\HandleSubscriptionSignupNotificationUseCase;
171
use WMDE\Fundraising\MembershipContext\UseCases\ShowApplicationConfirmation\ShowApplicationConfirmationPresenter;
172
use WMDE\Fundraising\MembershipContext\UseCases\ShowApplicationConfirmation\ShowApplicationConfirmationUseCase;
173
use WMDE\Fundraising\PaymentContext\DataAccess\Sofort\Transfer\Client as SofortClient;
174
use WMDE\Fundraising\PaymentContext\Domain\BankDataGenerator;
175
use WMDE\Fundraising\PaymentContext\Domain\BankDataValidator;
176
use WMDE\Fundraising\PaymentContext\Domain\DefaultPaymentDelayCalculator;
177
use WMDE\Fundraising\PaymentContext\Domain\IbanBlocklist;
178
use WMDE\Fundraising\PaymentContext\Domain\PaymentDataValidator;
179
use WMDE\Fundraising\PaymentContext\Domain\PaymentDelayCalculator;
180
use WMDE\Fundraising\PaymentContext\Domain\PaymentUrlGenerator\CreditCard as CreditCardUrlGenerator;
181
use WMDE\Fundraising\PaymentContext\Domain\PaymentUrlGenerator\CreditCardConfig;
182
use WMDE\Fundraising\PaymentContext\Domain\PaymentUrlGenerator\PayPal as PayPalUrlGenerator;
183
use WMDE\Fundraising\PaymentContext\Domain\PaymentUrlGenerator\PayPalConfig;
184
use WMDE\Fundraising\PaymentContext\Domain\PaymentUrlGenerator\Sofort as SofortUrlGenerator;
185
use WMDE\Fundraising\PaymentContext\Domain\PaymentUrlGenerator\SofortConfig;
186
use WMDE\Fundraising\PaymentContext\Domain\SimpleTransferCodeGenerator;
187
use WMDE\Fundraising\PaymentContext\Domain\TransferCodeGenerator;
188
use WMDE\Fundraising\PaymentContext\Infrastructure\CreditCardService;
189
use WMDE\Fundraising\PaymentContext\UseCases\CheckIban\CheckIbanUseCase;
190
use WMDE\Fundraising\PaymentContext\UseCases\GenerateIban\GenerateIbanUseCase;
191
use WMDE\Fundraising\Store\Factory as StoreFactory;
192
use WMDE\Fundraising\Store\Installer;
193
use WMDE\FunValidators\Validators\AllowedValuesValidator;
194
use WMDE\FunValidators\Validators\AmountPolicyValidator;
195
use WMDE\FunValidators\Validators\EmailValidator;
196
use WMDE\FunValidators\Validators\TextPolicyValidator;
197
198
/**
199
 * @licence GNU GPL v2+
200
 */
201
class FunFunFactory implements ServiceProviderInterface {
202
203
	private $config;
204
205
	/**
206
	 * @var Container
207
	 */
208
	private $pimple;
209
210
	private $addDoctrineSubscribers = true;
211
212
	private $sharedObjects;
213
214
	/**
215
	 * @var Stopwatch|null
216
	 */
217
	private $profiler = null;
218
219
	public function __construct( array $config ) {
220
		$this->config = $config;
221
		$this->pimple = $this->newPimple();
222
		$this->sharedObjects = [];
223
	}
224
225
	private function newPimple(): Container {
226
		$container = new Container();
227
		$container->register(
228
			new MembershipContextFactory(
229
				[
230
					// Explicitly passing redundantly - repeated use could be a case for config parameters
231
					// http://symfony.com/doc/current/service_container/parameters.html#parameters-in-configuration-files
232
					'token-length' => $this->config['token-length'],
233
					'token-validity-timestamp' => $this->config['token-validity-timestamp']
234
				]
235
			)
236
		);
237
		$this->register( $container );
238
		return $container;
239
	}
240
241
	public function register( Container $container ): void {
242
		$container['logger'] = function() {
243
			return new NullLogger();
244
		};
245
246
		$container['paypal_logger'] = function() {
247
			return new NullLogger();
248
		};
249
250
		$container['sofort_logger'] = function() {
251
			return new NullLogger();
252
		};
253
254
		$container['profiler_data_collector'] = function() {
255
			return new ProfilerDataCollector();
256
		};
257
258
		$container['dbal_connection'] = function() {
259
			return DriverManager::getConnection( $this->config['db'] );
260
		};
261
262
		$container['entity_manager'] = function() {
263
			$entityManager = ( new StoreFactory( $this->getConnection(), $this->getVarPath() . '/doctrine_proxies' ) )
264
				->getEntityManager();
265
			if ( $this->addDoctrineSubscribers ) {
266
				$entityManager->getEventManager()->addEventSubscriber( $this->newDoctrineDonationPrePersistSubscriber() );
267
				$entityManager->getEventManager()->addEventSubscriber( $this->newDoctrineMembershipApplicationPrePersistSubscriber() );
268
			}
269
270
			return $entityManager;
271
		};
272
273
		$container['subscription_repository'] = function() {
274
			return new LoggingSubscriptionRepository(
275
				new DoctrineSubscriptionRepository( $this->getEntityManager() ),
276
				$this->getLogger()
277
			);
278
		};
279
280
		$container['donation_repository'] = function() {
281
			return new LoggingDonationRepository(
282
				new DoctrineDonationRepository( $this->getEntityManager() ),
283
				$this->getLogger()
284
			);
285
		};
286
287
		$container['membership_application_repository'] = function() {
288
			return new LoggingApplicationRepository(
289
				new DoctrineApplicationRepository( $this->getEntityManager() ),
290
				$this->getLogger()
291
			);
292
		};
293
294
		$container['comment_repository'] = function() {
295
			return new LoggingCommentFinder(
296
				new DoctrineCommentFinder( $this->getEntityManager() ),
297
				$this->getLogger()
298
			);
299
		};
300
301
		$container['mail_validator'] = function() {
302
			return new EmailValidator( new InternetDomainNameValidator() );
303
		};
304
305
		$container['subscription_validator'] = function() {
306
			return new SubscriptionValidator(
307
				$this->getEmailValidator(),
308
				$this->newTextPolicyValidator( 'fields' ),
309
				$this->newSubscriptionDuplicateValidator(),
310
				$this->newHonorificValidator()
311
			);
312
		};
313
314
		$container['template_name_validator'] = function() {
315
			return new TemplateNameValidator( $this->getSkinTwig() );
316
		};
317
318
		$container['contact_validator'] = function() {
319
			return new GetInTouchValidator( $this->getEmailValidator() );
320
		};
321
322
		$container['greeting_generator'] = function() {
323
			return new GreetingGenerator();
324
		};
325
326
		$container['translator'] = function() {
327
			$translationFactory = new TranslationFactory();
328
			$loaders = [
329
				'json' => $translationFactory->newJsonLoader()
330
			];
331
			$locale = $this->config['locale'];
332
			$messagesPath = $this->getI18nDirectory() . $this->config['translation']['message-dir'];
333
			$translator = $translationFactory->create( $loaders, $locale );
334
			foreach ($this->config['translation']['files'] as $domain => $file) {
335
				$translator->addResource( 'json', $messagesPath . '/' . $file, $locale, $domain );
336
			}
337
338
			return $translator;
339
		};
340
341
		// In the future, this could be locale-specific or filled from a DB table
342
		$container['honorifics'] = function() {
343
			return new Honorifics( [
344
				'' => 'Kein Titel',
345
				'Dr.' => 'Dr.',
346
				'Prof.' => 'Prof.',
347
				'Prof. Dr.' => 'Prof. Dr.'
348
			] );
349
		};
350
351
		$container['twig'] = function() {
352
			$config = $this->config['twig'];
353
			$config['loaders']['filesystem']['template-dir'] = $this->getSkinDirectory();
354
355
			$twigFactory = $this->newTwigFactory( $config );
356
			$configurator = $twigFactory->newTwigEnvironmentConfigurator();
357
358
			$loaders = array_filter( [
359
				$twigFactory->newFileSystemLoader(),
360
				$twigFactory->newArrayLoader(), // This is just a fallback for testing
361
			] );
362
			$extensions = [
363
				$twigFactory->newTranslationExtension( $this->getTranslator() ),
364
				new Twig_Extensions_Extension_Intl()
365
			];
366
			$filters = [
367
				$twigFactory->newFilePrefixFilter(
368
					$this->getFilePrefixer()
369
				)
370
			];
371
			$functions = [
372
				new Twig_SimpleFunction(
373
					'web_content',
374
					function( string $name, array $context = [] ): string {
375
						return $this->getContentProvider()->getWeb( $name, $context );
376
					},
377
					[ 'is_safe' => [ 'html' ] ]
378
				),
379
			];
380
381
			return $configurator->getEnvironment( $this->pimple['skin_twig_environment'], $loaders, $extensions, $filters, $functions );
382
		};
383
384
		$container['mailer_twig'] = function() {
385
			$twigFactory = $this->newTwigFactory( $this->config['mailer-twig'] );
386
			$configurator = $twigFactory->newTwigEnvironmentConfigurator();
387
388
			$loaders = array_filter( [
389
				$twigFactory->newFileSystemLoader(),
390
				$twigFactory->newArrayLoader(), // This is just a fallback for testing
391
			] );
392
			$extensions = [
393
				$twigFactory->newTranslationExtension( $this->getTranslator() ),
394
				new Twig_Extensions_Extension_Intl(),
395
			];
396
			$filters = [];
397
			$functions = [
398
				new Twig_SimpleFunction(
399
					'mail_content',
400
					function( string $name, array $context = [] ): string {
401
						return $this->getContentProvider()->getMail( $name, $context );
402
					},
403
					[ 'is_safe' => [ 'all' ] ]
404
				),
405
				new Twig_SimpleFunction(
406
					'url',
407
					function( string $name, array $parameters = [] ): string {
408
						return $this->getUrlGenerator()->generateAbsoluteUrl( $name, $parameters );
409
					}
410
				)
411
			];
412
413
			$twigEnvironment = new Twig_Environment();
414
415
			return $configurator->getEnvironment( $twigEnvironment, $loaders, $extensions, $filters, $functions );
416
		};
417
418
		$container['messenger_suborganization'] = function() {
419
			return new Messenger(
420
				new Swift_MailTransport(),
421
				$this->getSubOrganizationEmailAddress(),
422
				$this->config['contact-info']['suborganization']['name']
423
			);
424
		};
425
426
		$container['messenger_organization'] = function() {
427
			return new Messenger(
428
				new Swift_MailTransport(),
429
				$this->getOrganizationEmailAddress(),
430
				$this->config['contact-info']['organization']['name']
431
			);
432
		};
433
434
		$container['paypal-payment-notification-verifier'] = function() {
435
			return new LoggingPaymentNotificationVerifier(
436
				new PayPalPaymentNotificationVerifier(
437
					new Client(),
438
					$this->config['paypal-donation']['base-url'],
439
					$this->config['paypal-donation']['account-address']
440
				),
441
				$this->getLogger()
442
			);
443
		};
444
445
		$container['paypal-membership-fee-notification-verifier'] = function() {
446
			return new LoggingPaymentNotificationVerifier(
447
				new PayPalPaymentNotificationVerifier(
448
					new Client(),
449
					$this->config['paypal-membership']['base-url'],
450
					$this->config['paypal-membership']['account-address']
451
				),
452
				$this->getLogger()
453
			);
454
		};
455
456
		$container['credit-card-api-service'] = function() {
457
			return new McpCreditCardService(
458
				new TNvpServiceDispatcher(
459
					'IMcpCreditcardService_v1_5',
460
					'https://sipg.micropayment.de/public/creditcard/v1.5/nvp/'
461
				),
462
				$this->config['creditcard']['access-key'],
463
				$this->config['creditcard']['testmode']
464
			);
465
		};
466
467
		$container['donation_token_generator'] = function() {
468
			return new RandomTokenGenerator(
469
				$this->config['token-length'],
470
				new \DateInterval( $this->config['token-validity-timestamp'] )
471
			);
472
		};
473
474
		$container['page_cache'] = function() {
475
			return new VoidCache();
476
		};
477
478
		$container['rendered_page_cache'] = function() {
479
			return new VoidCache();
480
		};
481
482
		$container['campaign_cache'] = function() {
483
			return new VoidCache();
484
		};
485
486
		$container['page_view_tracker'] = function () {
487
			return new PageViewTracker( $this->newServerSideTracker(), $this->config['piwik']['siteUrlBase'] );
488
		};
489
490
		$container['cachebusting_fileprefixer'] = function () {
491
			return new FilePrefixer( $this->getFilePrefix() );
492
		};
493
494
		$container['content_page_selector'] = function () {
495
			$json = (new SimpleFileFetcher())->fetchFile( $this->getI18nDirectory() . '/data/pages.json' );
496
			$config = json_decode( $json, true ) ?? [];
497
498
			return new PageSelector( $config );
499
		};
500
501
		$container['content_provider'] = function () {
502
			return new ContentProvider( [
503
				'content_path' => $this->getI18nDirectory(),
504
				'cache' => $this->config['twig']['enable-cache'] ? $this->getCachePath() . '/content' : false,
505
				'globals' => [
506
					'basepath' => $this->config['web-basepath']
507
				]
508
			] );
509
		};
510
511
		$container['payment-delay-calculator'] = function() {
512
			return new DefaultPaymentDelayCalculator( $this->getPaymentDelayInDays() );
513
		};
514
515
		$container['sofort-client'] = function () {
516
			$config = $this->config['sofort'];
517
			return new SofortClient( $config['config-key'] );
518
		};
519
520
		$container['cookie-builder'] = function (): CookieBuilder {
521
			return new CookieBuilder(
522
				$this->config['cookie']['expiration'],
523
				$this->config['cookie']['path'],
524
				$this->config['cookie']['domain'],
525
				$this->config['cookie']['secure'],
526
				$this->config['cookie']['httpOnly'],
527
				$this->config['cookie']['raw'],
528
				$this->config['cookie']['sameSite']
529
			);
530
		};
531
532
		$container['payment-types-settings'] = function (): PaymentTypesSettings {
533
			return new PaymentTypesSettings( $this->config['payment-types'] );
534
		};
535
	}
536
537
	private function createSharedObject( string $id, callable $constructionFunction ) { // @codingStandardsIgnoreLine
538
		if ( !isset( $this->sharedObjects[$id] ) ) {
539
			$this->sharedObjects[$id] = $constructionFunction();
540
		}
541
		return $this->sharedObjects[$id];
542
	}
543
544
	public function getConnection(): Connection {
545
		return $this->pimple['dbal_connection'];
546
	}
547
548
	public function getEntityManager(): EntityManager {
549
		return $this->pimple['entity_manager'];
550
	}
551
552
	private function newDonationEventLogger(): DonationEventLogger {
553
		return new BestEffortDonationEventLogger(
554
			new DoctrineDonationEventLogger( $this->getEntityManager() ),
555
			$this->getLogger()
556
		);
557
	}
558
559
	public function newInstaller(): Installer {
560
		return ( new StoreFactory( $this->getConnection() ) )->newInstaller();
561
	}
562
563
	public function newListCommentsUseCase(): ListCommentsUseCase {
564
		return new ListCommentsUseCase( $this->getCommentFinder() );
565
	}
566
567
	public function newCommentListJsonPresenter(): CommentListJsonPresenter {
568
		return new CommentListJsonPresenter();
569
	}
570
571
	public function newCommentListRssPresenter(): CommentListRssPresenter {
572
		return new CommentListRssPresenter( new TwigTemplate(
573
			$this->getSkinTwig(),
574
			'Comment_List.rss.twig'
575
		) );
576
	}
577
578
	public function newCommentListHtmlPresenter(): CommentListHtmlPresenter {
579
		return new CommentListHtmlPresenter( $this->getLayoutTemplate( 'Comment_List.html.twig', [ 'piwikGoals' => [ 1 ] ] ) );
580
	}
581
582
	private function getCommentFinder(): CommentFinder {
583
		return $this->pimple['comment_repository'];
584
	}
585
586
	public function getSubscriptionRepository(): SubscriptionRepository {
587
		return $this->pimple['subscription_repository'];
588
	}
589
590
	public function setSubscriptionRepository( SubscriptionRepository $subscriptionRepository ): void {
591
		$this->pimple['subscription_repository'] = $subscriptionRepository;
592
	}
593
594
	private function getSubscriptionValidator(): SubscriptionValidator {
595
		return $this->pimple['subscription_validator'];
596
	}
597
598
	public function getEmailValidator(): EmailValidator {
599
		return $this->pimple['mail_validator'];
600
	}
601
602
	public function getTemplateNameValidator(): TemplateNameValidator {
603
		return $this->pimple['template_name_validator'];
604
	}
605
606
	public function newAddSubscriptionHtmlPresenter(): AddSubscriptionHtmlPresenter {
607
		return new AddSubscriptionHtmlPresenter( $this->getLayoutTemplate( 'Subscription_Form.html.twig' ), $this->getTranslator() );
608
	}
609
610
	public function newConfirmSubscriptionHtmlPresenter(): ConfirmSubscriptionHtmlPresenter {
611
		return new ConfirmSubscriptionHtmlPresenter(
612
			$this->getLayoutTemplate( 'Confirm_Subscription.twig' ),
613
			$this->getTranslator()
614
		);
615
	}
616
617
	public function newAddSubscriptionJsonPresenter(): AddSubscriptionJsonPresenter {
618
		return new AddSubscriptionJsonPresenter( $this->getTranslator() );
619
	}
620
621
	public function newGetInTouchHtmlPresenter(): GetInTouchHtmlPresenter {
622
		return new GetInTouchHtmlPresenter(
623
			$this->getLayoutTemplate( 'contact_form.html.twig' ),
624
			$this->getTranslator(),
625
			$this->getGetInTouchCategories()
626
		);
627
	}
628
629
	public function getGetInTouchCategories(): array {
630
		try {
631
			$json = ( new SimpleFileFetcher() )->fetchFile( $this->getI18nDirectory() . '/data/contact_categories.json' );
632
			if ( $json === '' ) {
633
				throw new RuntimeException( 'error_no_topics_defined' );
634
			}
635
			return json_decode( $json, true );
636
		}
637
		catch( FileFetchingException $e ) {
638
			throw new RuntimeException( 'error_no_topics_defined' );
639
		}
640
	}
641
642
	public function setSkinTwigEnvironment( Twig_Environment $twig ): void {
643
		$this->pimple['skin_twig_environment'] = $twig;
644
	}
645
646
	public function getSkinTwig(): Twig_Environment {
647
		return $this->pimple['twig'];
648
	}
649
650
	public function getMailerTwig(): Twig_Environment {
651
		return $this->pimple['mailer_twig'];
652
	}
653
654
	/**
655
	 * Get a template, with the content for the layout areas filled in.
656
	 *
657
	 * @param string $templateName
658
	 * @param array $context Additional variables for the template
659
	 * @return TwigTemplate
660
	 */
661
	public function getLayoutTemplate( string $templateName, array $context = [] ): TwigTemplate {
662
		 return new TwigTemplate(
663
			$this->getSkinTwig(),
664
			$templateName,
665
			array_merge( $this->getDefaultTwigVariables(), $context )
666
		);
667
	}
668
669
	public function getMailerTemplate( string $templateName, array $context = [] ): TwigTemplate {
670
		return new TwigTemplate(
671
			$this->getMailerTwig(),
672
			$templateName,
673
			array_merge( $this->getDefaultTwigVariables(), $context )
674
		);
675
	}
676
677
	private function getDefaultTwigVariables(): array {
678
		return [
679
			'honorifics' => $this->getHonorifics()->getList(),
680
			'piwik' => $this->config['piwik'],
681
			'locale' => $this->config['locale'],
682
		];
683
	}
684
685
	private function newReferrerGeneralizer(): ReferrerGeneralizer {
686
		return new ReferrerGeneralizer(
687
			$this->config['referrer-generalization']['default'],
688
			$this->config['referrer-generalization']['domain-map']
689
		);
690
	}
691
692
	public function getLogger(): LoggerInterface {
693
		return $this->pimple['logger'];
694
	}
695
696
	public function getPaypalLogger(): LoggerInterface {
697
		return $this->pimple['paypal_logger'];
698
	}
699
700
	public function getSofortLogger(): LoggerInterface {
701
		return $this->pimple['sofort_logger'];
702
	}
703
704
	private function getVarPath(): string {
705
		return __DIR__ . '/../../var';
706
	}
707
708
	public function getCachePath(): string {
709
		return $this->getVarPath() . '/cache';
710
	}
711
712
	public function getLoggingPath(): string {
713
		return $this->getVarPath() . '/log';
714
	}
715
716
	public function newAddSubscriptionUseCase(): AddSubscriptionUseCase {
717
		return new AddSubscriptionUseCase(
718
			$this->getSubscriptionRepository(),
719
			$this->getSubscriptionValidator(),
720
			$this->newAddSubscriptionMailer()
721
		);
722
	}
723
724
	public function newConfirmSubscriptionUseCase(): ConfirmSubscriptionUseCase {
725
		return new ConfirmSubscriptionUseCase(
726
			$this->getSubscriptionRepository(),
727
			$this->newConfirmSubscriptionMailer()
728
		);
729
	}
730
731
	private function newAddSubscriptionMailer(): SubscriptionTemplateMailerInterface {
732
		return $this->newTemplateMailer(
733
			$this->getSuborganizationMessenger(),
734
			new TwigTemplate(
735
				$this->getMailerTwig(),
736
				'Subscription_Request.txt.twig',
737
				[
738
					'greeting_generator' => $this->getGreetingGenerator()
739
				]
740
			),
741
			'mail_subject_subscription'
742
		);
743
	}
744
745
	private function newConfirmSubscriptionMailer(): SubscriptionTemplateMailerInterface {
746
		return $this->newTemplateMailer(
747
			$this->getSuborganizationMessenger(),
748
			new TwigTemplate(
749
					$this->getMailerTwig(),
750
					'Subscription_Confirmation.txt.twig',
751
					[ 'greeting_generator' => $this->getGreetingGenerator() ]
752
			),
753
			'mail_subject_subscription_confirmed'
754
		);
755
	}
756
757
	/**
758
	 * Create a new TemplateMailer instance
759
	 *
760
	 * There is decoration going on, so explicitly hinting what we return (Robustness principle) would be confusing
761
	 * (you'd expect a TemplateBasedMailer, not a LoggingMailer), so we hint the interface instead.
762
	 */
763
	private function newTemplateMailer( Messenger $messenger, TwigTemplate $template, string $messageKey ): LoggingMailer {
764
		$mailer = new TemplateBasedMailer(
765
			$messenger,
766
			$template,
767
			$this->getTranslator()->trans( $messageKey )
768
		);
769
770
		return new LoggingMailer( $mailer, $this->getLogger() );
771
	}
772
773
	public function getGreetingGenerator(): GreetingGenerator {
774
		return $this->pimple['greeting_generator'];
775
	}
776
777
	public function newCheckIbanUseCase(): CheckIbanUseCase {
778
		return new CheckIbanUseCase(
779
			$this->newBankDataConverter(),
780
			$this->newIbanValidator(),
781
			$this->newIbanBlockList()
782
		);
783
	}
784
785
	public function newGenerateIbanUseCase(): GenerateIbanUseCase {
786
		return new GenerateIbanUseCase( $this->newBankDataConverter(), $this->newIbanBlockList() );
787
	}
788
789
	public function newIbanPresenter(): IbanPresenter {
790
		return new IbanPresenter();
791
	}
792
793
	public function newBankDataConverter(): BankDataGenerator {
794
		return new KontoCheckBankDataGenerator( $this->newIbanValidator() );
795
	}
796
797
	public function setSubscriptionValidator( SubscriptionValidator $subscriptionValidator ): void {
798
		$this->pimple['subscription_validator'] = $subscriptionValidator;
799
	}
800
801
	public function newGetInTouchUseCase(): GetInTouchUseCase {
802
		return new GetInTouchUseCase(
803
			$this->getContactValidator(),
804
			$this->newContactOperatorMailer(),
805
			$this->newContactUserMailer()
806
		);
807
	}
808
809
	private function newContactUserMailer(): TemplateMailerInterface {
810
		return $this->newTemplateMailer(
811
			$this->getSuborganizationMessenger(),
812
			new TwigTemplate( $this->getMailerTwig(), 'Contact_Confirm_to_User.txt.twig' ),
813
			'mail_subject_getintouch'
814
		);
815
	}
816
817
	private function newContactOperatorMailer(): OperatorMailer {
818
		return new OperatorMailer(
819
			$this->getSuborganizationMessenger(),
820
			new TwigTemplate( $this->getMailerTwig(), 'Contact_Forward_to_Operator.txt.twig' )
821
		);
822
	}
823
824
	private function getContactValidator(): GetInTouchValidator {
825
		return $this->pimple['contact_validator'];
826
	}
827
828
	private function newSubscriptionDuplicateValidator(): SubscriptionDuplicateValidator {
829
		return new SubscriptionDuplicateValidator(
830
				$this->getSubscriptionRepository(),
831
				$this->newSubscriptionDuplicateCutoffDate()
832
		);
833
	}
834
835
	private function newSubscriptionDuplicateCutoffDate(): \DateTime {
836
		$cutoffDateTime = new \DateTime();
837
		$cutoffDateTime->sub( new \DateInterval( $this->config['subscription-interval'] ) );
838
		return $cutoffDateTime;
839
	}
840
841
	private function newHonorificValidator(): AllowedValuesValidator {
842
		return new AllowedValuesValidator( $this->getHonorifics()->getKeys() );
843
	}
844
845
	private function getHonorifics(): Honorifics {
846
		return $this->pimple['honorifics'];
847
	}
848
849
	public function newAuthorizedCachePurger(): AuthorizedCachePurger {
850
		return new AuthorizedCachePurger(
851
			new AllOfTheCachePurger(
852
				$this->getSkinTwig(),
853
				$this->getPageCache(),
854
				$this->getRenderedPageCache(),
855
				$this->getCampaignCache()
856
			),
857
			$this->config['purging-secret']
858
		);
859
	}
860
861
	private function newBankDataValidator(): BankDataValidator {
862
		return new BankDataValidator( $this->newIbanValidator() );
863
	}
864
865
	private function getSuborganizationMessenger(): Messenger {
866
		return $this->pimple['messenger_suborganization'];
867
	}
868
869
	public function setSuborganizationMessenger( Messenger $messenger ): void {
870
		$this->pimple['messenger_suborganization'] = $messenger;
871
	}
872
873
	private function getOrganizationMessenger(): Messenger {
874
		return $this->pimple['messenger_organization'];
875
	}
876
877
	public function setOrganizationMessenger( Messenger $messenger ): void {
878
		$this->pimple['messenger_organization'] = $messenger;
879
	}
880
881
	public function setNullMessenger(): void {
882
		$this->setSuborganizationMessenger( new Messenger(
883
			Swift_NullTransport::newInstance(),
884
			$this->getSubOrganizationEmailAddress()
885
		) );
886
		$this->setOrganizationMessenger( new Messenger(
887
			Swift_NullTransport::newInstance(),
888
			$this->getOrganizationEmailAddress()
889
		) );
890
	}
891
892
	public function getSubOrganizationEmailAddress(): EmailAddress {
893
		return new EmailAddress( $this->config['contact-info']['suborganization']['email'] );
894
	}
895
896
	public function getOrganizationEmailAddress(): EmailAddress {
897
		return new EmailAddress( $this->config['contact-info']['organization']['email'] );
898
	}
899
900
	public function newInternalErrorHtmlPresenter(): InternalErrorHtmlPresenter {
901
		return new InternalErrorHtmlPresenter( $this->getLayoutTemplate( 'Error_Page.html.twig' ) );
902
	}
903
904
	public function newAccessDeniedHtmlPresenter(): InternalErrorHtmlPresenter {
905
		return new InternalErrorHtmlPresenter( $this->getLayoutTemplate( 'Access_Denied.twig' ) );
906
	}
907
908
	public function getTranslator(): TranslatorInterface {
909
		return $this->pimple['translator'];
910
	}
911
912
	public function setTranslator( TranslatorInterface $translator ): void {
913
		$this->pimple['translator'] = $translator;
914
	}
915
916
	private function newTwigFactory( array $twigConfig ): TwigFactory {
917
		return new TwigFactory(
918
			array_merge_recursive(
919
				$twigConfig,
920
				[ 'web-basepath' => $this->config['web-basepath'] ]
921
			),
922
			$this->getCachePath() . '/twig',
923
			$this->config['locale']
924
		);
925
	}
926
927
	private function newTextPolicyValidator( string $policyName ): TextPolicyValidator {
928
		$fetcher = new ErrorLoggingFileFetcher(
929
			new SimpleFileFetcher(),
930
			$this->getLogger()
931
		);
932
		$textPolicyConfig = $this->config['text-policies'][$policyName];
933
		return new TextPolicyValidator(
934
			new WordListFileReader(
935
				$fetcher,
936
				$textPolicyConfig['badwords'] ? $this->getAbsolutePath( $textPolicyConfig['badwords'] ) : ''
937
			),
938
			new WordListFileReader(
939
				$fetcher,
940
				$textPolicyConfig['whitewords'] ? $this->getAbsolutePath( $textPolicyConfig['whitewords'] ) : ''
941
			)
942
		);
943
	}
944
945
	private function newCommentPolicyValidator(): TextPolicyValidator {
946
		return $this->newTextPolicyValidator( 'comment' );
947
	}
948
949
	public function newCancelDonationUseCase( string $updateToken ): CancelDonationUseCase {
950
		return new CancelDonationUseCase(
951
			$this->getDonationRepository(),
952
			$this->newCancelDonationMailer(),
953
			$this->newDonationAuthorizer( $updateToken ),
954
			$this->newDonationEventLogger()
955
		);
956
	}
957
958
	private function newCancelDonationMailer(): DonationTemplateMailerInterface {
959
		return $this->newTemplateMailer(
960
			$this->getSuborganizationMessenger(),
961
			new TwigTemplate(
962
				$this->getMailerTwig(),
963
				'Donation_Cancellation_Confirmation.txt.twig',
964
				[ 'greeting_generator' => $this->getGreetingGenerator() ]
965
			),
966
			'mail_subject_confirm_cancellation'
967
		);
968
	}
969
970
	public function newAddDonationUseCase(): AddDonationUseCase {
971
		return new AddDonationUseCase(
972
			$this->getDonationRepository(),
973
			$this->newDonationValidator(),
974
			$this->newDonationPolicyValidator(),
975
			$this->newReferrerGeneralizer(),
976
			$this->newDonationConfirmationMailer(),
977
			$this->newBankTransferCodeGenerator(),
978
			$this->newDonationTokenFetcher(),
979
			new InitialDonationStatusPicker()
980
		);
981
	}
982
983
	private function newBankTransferCodeGenerator(): TransferCodeGenerator {
984
		return new UniqueTransferCodeGenerator(
985
			new SimpleTransferCodeGenerator(),
986
			$this->getEntityManager()
987
		);
988
	}
989
990
	private function newDonationValidator(): AddDonationValidator {
991
		return new AddDonationValidator(
992
			$this->newPaymentDataValidator(),
993
			$this->newBankDataValidator(),
994
			$this->newIbanBlockList(),
995
			$this->getEmailValidator()
996
		);
997
	}
998
999
	public function newValidateDonorUseCase(): ValidateDonorUseCase {
1000
		return new ValidateDonorUseCase(
1001
			$this->getEmailValidator()
1002
		);
1003
	}
1004
1005
	public function newUpdateDonorUseCase( string $updateToken, string $accessToken ): UpdateDonorUseCase {
1006
		return new UpdateDonorUseCase(
1007
			$this->newDonationAuthorizer( $updateToken, $accessToken ),
1008
			$this->newUpdateDonorValidator(),
1009
			$this->getDonationRepository(),
1010
			$this->newDonationConfirmationMailer()
1011
		);
1012
	}
1013
1014
	private function newUpdateDonorValidator(): UpdateDonorValidator {
1015
		return new UpdateDonorValidator( new DonorValidator( $this->getEmailValidator() ) );
1016
	}
1017
1018
	private function newDonationConfirmationMailer(): DonationConfirmationMailer {
1019
		return new DonationConfirmationMailer(
1020
			$this->newTemplateMailer(
1021
				$this->getSuborganizationMessenger(),
1022
				new TwigTemplate(
1023
					$this->getMailerTwig(),
1024
					'Donation_Confirmation.txt.twig',
1025
					[
1026
						'greeting_generator' => $this->getGreetingGenerator()
1027
					]
1028
				),
1029
				'mail_subject_confirm_donation'
1030
			)
1031
		);
1032
	}
1033
1034
	public function newPayPalUrlGeneratorForDonations(): PayPalUrlGenerator {
1035
		return new PayPalUrlGenerator(
1036
			$this->getPayPalUrlConfigForDonations(),
1037
			$this->getTranslator()->trans( 'item_name_donation' )
1038
		);
1039
	}
1040
1041
	public function newPayPalUrlGeneratorForMembershipApplications(): PayPalUrlGenerator {
1042
		return new PayPalUrlGenerator(
1043
			$this->getPayPalUrlConfigForMembershipApplications(),
1044
			$this->getTranslator()->trans( 'item_name_membership' )
1045
		);
1046
	}
1047
1048
	private function getPayPalUrlConfigForDonations(): PayPalConfig {
1049
		return PayPalConfig::newFromConfig( $this->config['paypal-donation'] );
1050
	}
1051
1052
	private function getPayPalUrlConfigForMembershipApplications(): PayPalConfig {
1053
		return PayPalConfig::newFromConfig( $this->config['paypal-membership'] );
1054
	}
1055
1056
	public function newSofortUrlGeneratorForDonations(): SofortUrlGenerator {
1057
		$config = $this->config['sofort'];
1058
1059
		return new SofortUrlGenerator(
1060
			new SofortConfig(
1061
				$this->getTranslator()->trans( 'item_name_donation', [], 'messages' ),
1062
				$config['return-url'],
1063
				$config['cancel-url'],
1064
				$config['notification-url']
1065
			),
1066
			$this->getSofortClient()
1067
		);
1068
	}
1069
1070
	public function setSofortClient( SofortClient $client ): void {
1071
		$this->pimple['sofort-client'] = $client;
1072
	}
1073
1074
	private function getSofortClient(): SofortClient {
1075
		return $this->pimple['sofort-client'];
1076
	}
1077
1078
	private function newCreditCardUrlGenerator(): CreditCardUrlGenerator {
1079
		return new CreditCardUrlGenerator( $this->newCreditCardUrlConfig() );
1080
	}
1081
1082
	private function newCreditCardUrlConfig(): CreditCardConfig {
1083
		return CreditCardConfig::newFromConfig( $this->config['creditcard'] );
1084
	}
1085
1086
	public function getDonationRepository(): DonationRepository {
1087
		return $this->pimple['donation_repository'];
1088
	}
1089
1090
	public function newPaymentDataValidator(): PaymentDataValidator {
1091
		return new PaymentDataValidator(
1092
			$this->config['donation-minimum-amount'],
1093
			$this->config['donation-maximum-amount'],
1094
			$this->getPaymentTypesSettings()->getEnabledForDonation()
1095
		);
1096
	}
1097
1098
	private function newAmountFormatter(): AmountFormatter {
1099
		return new AmountFormatter( $this->config['locale'] );
1100
	}
1101
1102
	public function newDecimalNumberFormatter(): NumberFormatter {
1103
		return new NumberFormatter( $this->config['locale'], NumberFormatter::DECIMAL );
1104
	}
1105
1106
	public function newAddCommentUseCase( string $updateToken ): AddCommentUseCase {
1107
		return new AddCommentUseCase(
1108
			$this->getDonationRepository(),
1109
			$this->newDonationAuthorizer( $updateToken ),
1110
			$this->newCommentPolicyValidator(),
1111
			$this->newAddCommentValidator()
1112
		);
1113
	}
1114
1115
	private function newDonationAuthorizer( string $updateToken = null, string $accessToken = null ): DonationAuthorizer {
1116
		return new DoctrineDonationAuthorizer(
1117
			$this->getEntityManager(),
1118
			$updateToken,
1119
			$accessToken
1120
		);
1121
	}
1122
1123
	public function getDonationTokenGenerator(): TokenGenerator {
1124
		return $this->pimple['donation_token_generator'];
1125
	}
1126
1127
	public function getMembershipTokenGenerator(): MembershipTokenGenerator {
1128
		return $this->pimple['fundraising.membership.application.token_generator'];
1129
	}
1130
1131
	public function newDonationConfirmationPresenter(): DonationConfirmationHtmlPresenter {
1132
		return new DonationConfirmationHtmlPresenter(
1133
			new TwigTemplate(
1134
				$this->getSkinTwig(), 'Donation_Confirmation.html.twig',
1135
				array_merge(
1136
					$this->getDefaultTwigVariables(),
1137
					[
1138
						'paymentTypes' => $this->getPaymentTypesSettings()->getEnabledForMembershipApplication(),
1139
						'featureToggle' => ['donorUpdateEnabled' => $this->getChoiceFactory()->isDonationAddressOptional()]
1140
					]
1141
				)
1142
			),
1143
			$this->getUrlGenerator()
1144
		);
1145
	}
1146
1147
	public function newDonorUpdatePresenter(): DonorUpdateHtmlPresenter {
1148
		return new DonorUpdateHtmlPresenter(
1149
			new TwigTemplate(
1150
				$this->getSkinTwig(), 'Donation_Confirmation.html.twig',
1151
				$this->getDefaultTwigVariables()
1152
			),
1153
			$this->getUrlGenerator()
1154
		);
1155
	}
1156
1157
	public function newCreditCardPaymentUrlGenerator(): CreditCardPaymentUrlGenerator {
1158
		return new CreditCardPaymentUrlGenerator(
1159
			$this->getTranslator(),
1160
			$this->newCreditCardUrlGenerator()
1161
		);
1162
	}
1163
1164
	public function newCancelDonationHtmlPresenter(): CancelDonationHtmlPresenter {
1165
		return new CancelDonationHtmlPresenter(
1166
			$this->getLayoutTemplate( 'Donation_Cancellation_Confirmation.html.twig' )
1167
		);
1168
	}
1169
1170
	public function newApplyForMembershipUseCase(): ApplyForMembershipUseCase {
1171
		return new ApplyForMembershipUseCase(
1172
			$this->getMembershipApplicationRepository(),
1173
			$this->newMembershipApplicationTokenFetcher(),
1174
			$this->newApplyForMembershipMailer(),
1175
			$this->newMembershipApplicationValidator(),
1176
			$this->newApplyForMembershipPolicyValidator(),
1177
			$this->newMembershipApplicationTracker(),
1178
			$this->newMembershipApplicationPiwikTracker(),
1179
			$this->getPaymentDelayCalculator()
1180
		);
1181
	}
1182
1183
	private function newApplyForMembershipMailer(): TemplateMailerInterface {
1184
		return $this->newTemplateMailer(
1185
			$this->getOrganizationMessenger(),
1186
			new TwigTemplate(
1187
				$this->getMailerTwig(),
1188
				'Membership_Application_Confirmation.txt.twig',
1189
				[ 'greeting_generator' => $this->getGreetingGenerator() ]
1190
			),
1191
			'mail_subject_confirm_membership_application'
1192
		);
1193
	}
1194
1195
	private function newMembershipApplicationValidator(): MembershipApplicationValidator {
1196
		return new MembershipApplicationValidator(
1197
			new MembershipFeeValidator(),
1198
			$this->newBankDataValidator(),
1199
			$this->newIbanBlockList(),
1200
			$this->getEmailValidator()
1201
		);
1202
	}
1203
1204
	private function newMembershipApplicationTracker(): ApplicationTracker {
1205
		return new DoctrineApplicationTracker( $this->getEntityManager() );
1206
	}
1207
1208
	private function newMembershipApplicationPiwikTracker(): ApplicationPiwikTracker {
1209
		return new DoctrineApplicationPiwikTracker( $this->getEntityManager() );
1210
	}
1211
1212
	private function getPaymentDelayCalculator(): PaymentDelayCalculator {
1213
		return $this->pimple['payment-delay-calculator'];
1214
	}
1215
1216
	public function getPaymentDelayInDays(): int {
1217
		return $this->getPayPalUrlConfigForMembershipApplications()->getDelayInDays();
1218
	}
1219
1220
	public function setPaymentDelayCalculator( PaymentDelayCalculator $paymentDelayCalculator ): void {
1221
		$this->pimple['payment-delay-calculator'] = $paymentDelayCalculator;
1222
	}
1223
1224
	private function newApplyForMembershipPolicyValidator(): ApplyForMembershipPolicyValidator {
1225
		return new ApplyForMembershipPolicyValidator(
1226
			$this->newTextPolicyValidator( 'fields' ),
1227
			$this->config['email-address-blacklist']
1228
		);
1229
	}
1230
1231
	public function newCancelMembershipApplicationUseCase( string $updateToken ): CancelMembershipApplicationUseCase {
1232
		return new CancelMembershipApplicationUseCase(
1233
			$this->newMembershipApplicationAuthorizer( $updateToken ),
1234
			$this->getMembershipApplicationRepository(),
1235
			$this->newCancelMembershipApplicationMailer()
1236
		);
1237
	}
1238
1239
	private function newMembershipApplicationAuthorizer(
1240
		string $updateToken = null, string $accessToken = null ): ApplicationAuthorizer {
1241
1242
		$this->pimple['fundraising.membership.application.authorizer.update_token'] = $updateToken;
1243
		$this->pimple['fundraising.membership.application.authorizer.access_token'] = $accessToken;
1244
		return $this->pimple['fundraising.membership.application.authorizer'];
1245
	}
1246
1247
	public function setMembershipApplicationRepository( ApplicationRepository $applicationRepository ): void {
1248
		$this->pimple['membership_application_repository'] = $applicationRepository;
1249
	}
1250
1251
	public function getMembershipApplicationRepository(): ApplicationRepository {
1252
		return $this->pimple['membership_application_repository'];
1253
	}
1254
1255
	public function setMembershipApplicationAuthorizerClass( string $class ): void {
1256
		$this->pimple['fundraising.membership.application.authorizer.class'] = $class;
1257
	}
1258
1259
	private function newCancelMembershipApplicationMailer(): TemplateMailerInterface {
1260
		return $this->newTemplateMailer(
1261
			$this->getOrganizationMessenger(),
1262
			new TwigTemplate(
1263
				$this->getMailerTwig(),
1264
				'Membership_Application_Cancellation_Confirmation.txt.twig',
1265
				[ 'greeting_generator' => $this->getGreetingGenerator() ]
1266
			),
1267
			'mail_subject_confirm_membership_application_cancellation'
1268
		);
1269
	}
1270
1271
	public function newMembershipApplicationConfirmationUseCase( ShowApplicationConfirmationPresenter $presenter, string $accessToken ): ShowApplicationConfirmationUseCase {
1272
		return new ShowApplicationConfirmationUseCase(
1273
			$presenter,
1274
			$this->newMembershipApplicationAuthorizer( null, $accessToken ),
1275
			$this->getMembershipApplicationRepository(),
1276
			$this->newMembershipApplicationTokenFetcher()
1277
		);
1278
	}
1279
1280
	public function newGetDonationUseCase( string $accessToken ): GetDonationUseCase {
1281
		return new GetDonationUseCase(
1282
			$this->newDonationAuthorizer( null, $accessToken ),
1283
			$this->newDonationTokenFetcher(),
1284
			$this->getDonationRepository()
1285
		);
1286
	}
1287
1288
	public function newDonationFormViolationPresenter(): DonationFormViolationPresenter {
1289
		return new DonationFormViolationPresenter( $this->getDonationFormTemplate(), $this->newAmountFormatter() );
1290
	}
1291
1292
	public function newDonationFormPresenter(): DonationFormPresenter {
1293
		return new DonationFormPresenter(
1294
			$this->getDonationFormTemplate(),
1295
			$this->newAmountFormatter(),
1296
			$this->newIsCustomDonationAmountValidator()
1297
		);
1298
	}
1299
1300
	private function getDonationFormTemplate(): TwigTemplate {
1301
		// TODO make the template name dependent on the 'form' value from the HTTP POST request
1302
		// (we need different form pages for A/B testing)
1303
		return $this->getLayoutTemplate( 'Donation_Form.html.twig', [
1304
			'paymentTypes' => $this->getPaymentTypesSettings()->getEnabledForDonation(),
1305
			'presetAmounts' => $this->getPresetAmountsSettings( 'donations' )
1306
		] );
1307
	}
1308
1309
	public function getMembershipApplicationFormTemplate(): TwigTemplate {
1310
		return $this->getLayoutTemplate( 'Membership_Application.html.twig', [
1311
			'presetAmounts' => $this->getPresetAmountsSettings( 'membership' ),
1312
			'paymentTypes' => $this->getPaymentTypesSettings()->getEnabledForMembershipApplication()
1313
		] );
1314
	}
1315
1316
	public function newHandleSofortPaymentNotificationUseCase( string $updateToken ): SofortPaymentNotificationUseCase {
1317
		return new SofortPaymentNotificationUseCase(
1318
			$this->getDonationRepository(),
1319
			$this->newDonationAuthorizer( $updateToken ),
1320
			$this->newDonationConfirmationMailer()
1321
		);
1322
	}
1323
1324
	public function newHandlePayPalPaymentCompletionNotificationUseCase( string $updateToken ): HandlePayPalPaymentCompletionNotificationUseCase {
1325
		return new HandlePayPalPaymentCompletionNotificationUseCase(
1326
			$this->getDonationRepository(),
1327
			$this->newDonationAuthorizer( $updateToken ),
1328
			$this->newDonationConfirmationMailer(),
1329
			$this->newDonationEventLogger()
1330
		);
1331
	}
1332
1333
	public function newMembershipApplicationSubscriptionSignupNotificationUseCase( string $updateToken ): HandleSubscriptionSignupNotificationUseCase {
1334
		return new HandleSubscriptionSignupNotificationUseCase(
1335
			$this->getMembershipApplicationRepository(),
1336
			$this->newMembershipApplicationAuthorizer( $updateToken ),
1337
			$this->newApplyForMembershipMailer(),
1338
			$this->getLogger()
1339
		);
1340
	}
1341
1342
	public function newMembershipApplicationSubscriptionPaymentNotificationUseCase( string $updateToken ): HandleSubscriptionPaymentNotificationUseCase {
1343
		return new HandleSubscriptionPaymentNotificationUseCase(
1344
			$this->getMembershipApplicationRepository(),
1345
			$this->newMembershipApplicationAuthorizer( $updateToken ),
1346
			$this->newApplyForMembershipMailer(),
1347
			$this->getLogger()
1348
		);
1349
	}
1350
1351
	public function getPayPalPaymentNotificationVerifier(): PaymentNotificationVerifier {
1352
		return $this->pimple['paypal-payment-notification-verifier'];
1353
	}
1354
1355
	public function setPayPalPaymentNotificationVerifier( PaymentNotificationVerifier $verifier ): void {
1356
		$this->pimple['paypal-payment-notification-verifier'] = $verifier;
1357
	}
1358
1359
	public function getPayPalMembershipFeeNotificationVerifier(): PaymentNotificationVerifier {
1360
		return $this->pimple['paypal-membership-fee-notification-verifier'];
1361
	}
1362
1363
	public function setPayPalMembershipFeeNotificationVerifier( PaymentNotificationVerifier $verifier ): void {
1364
		$this->pimple['paypal-membership-fee-notification-verifier'] = $verifier;
1365
	}
1366
1367
	public function newCreditCardNotificationUseCase( string $updateToken ): CreditCardNotificationUseCase {
1368
		return new CreditCardNotificationUseCase(
1369
			$this->getDonationRepository(),
1370
			$this->newDonationAuthorizer( $updateToken ),
1371
			$this->getCreditCardService(),
1372
			$this->newDonationConfirmationMailer(),
1373
			$this->getLogger(),
1374
			$this->newDonationEventLogger()
1375
		);
1376
	}
1377
1378
	public function newCancelMembershipApplicationHtmlPresenter(): CancelMembershipApplicationHtmlPresenter {
1379
		return new CancelMembershipApplicationHtmlPresenter(
1380
			$this->getLayoutTemplate( 'Membership_Application_Cancellation_Confirmation.html.twig' )
1381
		);
1382
	}
1383
1384
	public function newMembershipApplicationConfirmationHtmlPresenter(): MembershipApplicationConfirmationHtmlPresenter {
1385
		return new MembershipApplicationConfirmationHtmlPresenter(
1386
			$this->getLayoutTemplate( 'Membership_Application_Confirmation.html.twig' ),
1387
			$this->newBankDataConverter()
1388
		);
1389
	}
1390
1391
	public function newMembershipFormViolationPresenter(): MembershipFormViolationPresenter {
1392
		return new MembershipFormViolationPresenter(
1393
			$this->getMembershipApplicationFormTemplate()
1394
		);
1395
	}
1396
1397
	public function setCreditCardService( CreditCardService $ccService ): void {
1398
		$this->pimple['credit-card-api-service'] = $ccService;
1399
	}
1400
1401
	public function getCreditCardService(): CreditCardService {
1402
		return $this->pimple['credit-card-api-service'];
1403
	}
1404
1405
	public function newCreditCardNotificationPresenter(): CreditCardNotificationPresenter {
1406
		return new CreditCardNotificationPresenter(
1407
			$this->config['creditcard']['return-url']
1408
		);
1409
	}
1410
1411
	private function newDoctrineDonationPrePersistSubscriber(): DoctrineDonationPrePersistSubscriber {
1412
		return new DoctrineDonationPrePersistSubscriber(
1413
			$this->getDonationTokenGenerator(),
1414
			$this->getDonationTokenGenerator()
1415
		);
1416
	}
1417
1418
	private function newDoctrineMembershipApplicationPrePersistSubscriber(): DoctrineMembershipApplicationPrePersistSubscriber {
1419
		return new DoctrineMembershipApplicationPrePersistSubscriber(
1420
			$this->getMembershipTokenGenerator(),
1421
			$this->getMembershipTokenGenerator()
1422
		);
1423
	}
1424
1425
	public function setDonationTokenGenerator( TokenGenerator $tokenGenerator ): void {
1426
		$this->pimple['donation_token_generator'] = $tokenGenerator;
1427
	}
1428
1429
	public function setMembershipTokenGenerator( MembershipTokenGenerator $tokenGenerator ): void {
1430
		$this->pimple['fundraising.membership.application.token_generator'] = $tokenGenerator;
1431
	}
1432
1433
	public function disableDoctrineSubscribers(): void {
1434
		$this->addDoctrineSubscribers = false;
1435
	}
1436
1437
	private function newDonationTokenFetcher(): DonationTokenFetcher {
1438
		return new DoctrineDonationTokenFetcher(
1439
			$this->getEntityManager()
1440
		);
1441
	}
1442
1443
	private function newMembershipApplicationTokenFetcher(): ApplicationTokenFetcher {
1444
		return new DoctrineApplicationTokenFetcher(
1445
			$this->getEntityManager()
1446
		);
1447
	}
1448
1449
	private function newDonationPolicyValidator(): AddDonationPolicyValidator {
1450
		return new AddDonationPolicyValidator(
1451
			$this->newDonationAmountPolicyValidator(),
1452
			$this->newTextPolicyValidator( 'fields' ),
1453
			$this->config['email-address-blacklist']
1454
		);
1455
	}
1456
1457
	private function newDonationAmountPolicyValidator(): AmountPolicyValidator {
1458
		// in the future, this might come from the configuration
1459
		return new AmountPolicyValidator( 1000, 1000 );
1460
	}
1461
1462
	public function getDonationTimeframeLimit(): string {
1463
		return $this->config['donation-timeframe-limit'];
1464
	}
1465
1466
	public function newSystemMessageResponse( string $message ): string {
1467
		return $this->getLayoutTemplate( 'System_Message.html.twig' )
1468
			->render( [ 'message' => $message ] );
1469
	}
1470
1471
	public function getMembershipApplicationTimeframeLimit(): string {
1472
		return $this->config['membership-application-timeframe-limit'];
1473
	}
1474
1475
	private function newAddCommentValidator(): AddCommentValidator {
1476
		return new AddCommentValidator();
1477
	}
1478
1479
	private function getPageCache(): Cache {
1480
		return $this->pimple['page_cache'];
1481
	}
1482
1483
	private function getRenderedPageCache(): Cache {
1484
		return $this->pimple['rendered_page_cache'];
1485
	}
1486
1487
	private function getCampaignCache(): CacheProvider {
1488
		return $this->pimple['campaign_cache'];
1489
	}
1490
1491
	public function enableCaching(): void {
1492
		$this->pimple['page_cache'] = function() {
1493
			return new FilesystemCache( $this->getCachePath() . '/pages/raw' );
1494
		};
1495
1496
		$this->pimple['rendered_page_cache'] = function() {
1497
			return new FilesystemCache( $this->getCachePath() . '/pages/rendered' );
1498
		};
1499
1500
		$this->pimple['campaign_cache'] = function() {
1501
			return new FilesystemCache( $this->getCachePath() . '/campaigns' );
1502
		};
1503
	}
1504
1505
	public function setProfiler( Stopwatch $profiler ): void {
1506
		$this->profiler = $profiler;
1507
	}
1508
1509
	public function setEmailValidator( EmailValidator $validator ): void {
1510
		$this->pimple['mail_validator'] = $validator;
1511
	}
1512
1513
	public function setLogger( LoggerInterface $logger ): void {
1514
		$this->pimple['logger'] = $logger;
1515
	}
1516
1517
	public function setPaypalLogger( LoggerInterface $logger ): void {
1518
		$this->pimple['paypal_logger'] = $logger;
1519
	}
1520
1521
	public function setSofortLogger( LoggerInterface $logger ): void {
1522
		$this->pimple['sofort_logger'] = $logger;
1523
	}
1524
1525
	public function getProfilerDataCollector(): ProfilerDataCollector {
1526
		return $this->pimple['profiler_data_collector'];
1527
	}
1528
1529
	private function newIbanValidator(): KontoCheckIbanValidator {
1530
		return new KontoCheckIbanValidator();
1531
	}
1532
1533
	private function newIbanBlockList(): IbanBlocklist {
1534
		return new IbanBlocklist( $this->config['banned-ibans'] );
1535
	}
1536
1537
	public function setFilePrefixer( FilePrefixer $prefixer ): void {
1538
		$this->pimple['cachebusting_fileprefixer'] = $prefixer;
1539
	}
1540
1541
	private function getFilePrefixer(): FilePrefixer {
1542
		return $this->pimple['cachebusting_fileprefixer'];
1543
	}
1544
1545
	private function getFilePrefix(): string {
1546
		$prefixContentFile = $this->getVarPath() . '/file_prefix.txt';
1547
		if ( !file_exists( $prefixContentFile ) ) {
1548
			return '';
1549
		}
1550
		return preg_replace( '/[^0-9a-f]/', '', file_get_contents( $prefixContentFile ) );
1551
	}
1552
1553
	public function newDonationAcceptedEventHandler( string $updateToken ): DonationAcceptedEventHandler {
1554
		return new DonationAcceptedEventHandler(
1555
			$this->newDonationAuthorizer( $updateToken ),
1556
			$this->getDonationRepository(),
1557
			$this->newDonationConfirmationMailer()
1558
		);
1559
	}
1560
1561
	public function newPageNotFoundHtmlPresenter(): PageNotFoundPresenter {
1562
		return new PageNotFoundPresenter( $this->getLayoutTemplate( 'Page_not_found.html.twig' ) );
1563
	}
1564
1565
	public function setPageViewTracker( PageViewTracker $tracker ): void {
1566
		$this->pimple['page_view_tracker'] = function () use ( $tracker )  {
1567
			return $tracker;
1568
		};
1569
	}
1570
1571
	public function getPageViewTracker(): PageViewTracker {
1572
		return $this->pimple['page_view_tracker'];
1573
	}
1574
1575
	public function newServerSideTracker(): ServerSideTracker {
1576
		// the "https:" prefix does NOT get any slashes because baseURL is stored in a protocol-agnostic way
1577
		// (e.g. "//tracking.wikimedia.de" )
1578
		return new PiwikServerSideTracker(
1579
			new \PiwikTracker( $this->config['piwik']['siteId'], 'https:' . $this->config['piwik']['baseUrl'] )
1580
		);
1581
	}
1582
1583
	public function getI18nDirectory(): string {
1584
		return $this->getAbsolutePath( $this->config['i18n-base-path'] ) . '/' . $this->config['locale'];
1585
	}
1586
1587
	/**
1588
	 * If the pathname does not start with a slash, make the path absolute to root dir of application
1589
	 */
1590
	private function getAbsolutePath( string $path ): string {
1591
		if ( $path[0] === '/' ) {
1592
			return $path;
1593
		}
1594
		return __DIR__ . '/../../' . $path;
1595
	}
1596
1597
	public function setContentPagePageSelector( PageSelector $pageSelector ): void {
1598
		$this->pimple['content_page_selector'] = $pageSelector;
1599
	}
1600
1601
	public function getContentPagePageSelector(): PageSelector {
1602
		return $this->pimple['content_page_selector'];
1603
	}
1604
1605
	public function setContentProvider( ContentProvider $contentProvider ): void {
1606
		$this->pimple['content_provider'] = $contentProvider;
1607
	}
1608
1609
	private function getContentProvider(): ContentProvider {
1610
		return $this->pimple['content_provider'];
1611
	}
1612
1613
	public function newMailTemplateFilenameTraversable(): MailTemplateFilenameTraversable {
1614
		return new MailTemplateFilenameTraversable(
1615
			$this->config['mailer-twig']['loaders']['filesystem']['template-dir']
1616
		);
1617
	}
1618
1619
	public function getUrlGenerator(): UrlGenerator {
1620
		return $this->pimple['url_generator'];
1621
	}
1622
1623
	public function setUrlGenerator( UrlGenerator $urlGenerator ): void {
1624
		$this->pimple['url_generator'] = $urlGenerator;
1625
	}
1626
1627
	public function getCookieBuilder(): CookieBuilder {
1628
		return $this->pimple['cookie-builder'];
1629
	}
1630
1631
	public function getPaymentTypesSettings(): PaymentTypesSettings {
1632
		return $this->pimple['payment-types-settings'];
1633
	}
1634
1635
	/**
1636
	 * @return Euro[]
1637
	 */
1638
	public function getPresetAmountsSettings( string $presetType ): array {
1639
		return array_map( function ( int $amount ) {
1640
			return Euro::newFromCents( $amount );
1641
		}, $this->config['preset-amounts'][$presetType] );
1642
	}
1643
1644
	public function newDonationAmountConstraint(): ValidatorConstraint {
1645
		return new RequiredConstraint( [
1646
			new TypeConstraint( [ 'type' => 'digit' ] ),
1647
			new RangeConstraint( [
1648
				'min' => Euro::newFromInt( $this->config['donation-minimum-amount'] )->getEuroCents(),
1649
				'max' => Euro::newFromInt( $this->config['donation-maximum-amount'] )->getEuroCents()
1650
			] )
1651
		] );
1652
	}
1653
1654
	public function newIsCustomDonationAmountValidator(): IsCustomAmountValidator {
1655
		return new IsCustomAmountValidator( $this->getPresetAmountsSettings( 'donations' ) );
1656
	}
1657
1658
	public function getSkinDirectory(): string {
1659
		return $this->getChoiceFactory()->getSkinTemplateDirectory();
1660
	}
1661
1662
	public function getAbsoluteSkinDirectory(): string {
1663
		return $this->getAbsolutePath( $this->getSkinDirectory() );
1664
	}
1665
1666
	/**
1667
	 * @return Campaign[]
1668
	 */
1669
	private function getCampaigns(): array {
1670
		$builder = new CampaignBuilder( new \DateTimeZone( $this->config['campaigns']['timezone'] ) );
1671
		$configFiles = array_map(
1672
			function ( string $campaignConfigFile ) {
1673
				return $this->getAbsolutePath( 'app/config/' . $campaignConfigFile );
1674
			},
1675
			$this->config['campaigns']['configurations']
1676
		);
1677
		$loader = $this->getCampaignConfigurationLoader();
1678
		return $builder->getCampaigns( $loader->loadCampaignConfiguration( ...$configFiles ) );
1679
	}
1680
1681
	public function getCampaignConfigurationLoader(): CampaignConfigurationLoaderInterface {
1682
		return $this->createSharedObject( CampaignConfigurationLoaderInterface::class, function (): CampaignConfigurationLoader {
1683
			return new CampaignConfigurationLoader( new Filesystem(), new SimpleFileFetcher(), $this->getCampaignCache() );
1684
		} );
1685
	}
1686
1687
	public function setCampaignConfigurationLoader( CampaignConfigurationLoaderInterface $loader ): void {
1688
		$this->sharedObjects[CampaignConfigurationLoaderInterface::class] = $loader;
1689
	}
1690
1691
	private function newCampaignFeatures(): Set {
1692
		// TODO Cache features so we don't have to parse the campaign config on every request
1693
		$factory = new CampaignFeatureBuilder( ...$this->getCampaigns() );
1694
		return $factory->getFeatures();
1695
	}
1696
1697
	private function getFeatureToggle(): FeatureToggle {
1698
		return $this->createSharedObject( FeatureToggle::class, function (): FeatureToggle {
1699
			$doorkeeper = new Doorkeeper( $this->newCampaignFeatures() );
1700
			$requestor = new Requestor();
1701
			foreach ( $this->getSelectedBuckets() as $bucket ) {
1702
				$requestor = $requestor->withStringHash( $bucket->getId() );
1703
			}
1704
			$doorkeeper->setRequestor( $requestor );
1705
			return new DoorkeeperFeatureToggle( $doorkeeper );
1706
		} );
1707
	}
1708
1709
	private function getChoiceFactory(): ChoiceFactory {
1710
		return $this->createSharedObject( ChoiceFactory::class, function (): ChoiceFactory {
1711
			return new ChoiceFactory( $this->getFeatureToggle() );
1712
		} );
1713
	}
1714
1715
	public function getBucketSelector(): BucketSelector {
1716
		return $this->createSharedObject( BucketSelector::class, function (): BucketSelector {
1717
			return new BucketSelector( $this->getCampaignCollection(), new RandomBucketSelection() );
1718
		} );
1719
	}
1720
1721
	public function getSelectedBuckets(): array {
1722
		// when in the web environment, selected buckets will be set by BucketSelectionServiceProvider during request processing
1723
		// other environments (testing/cli) may set this during setup
1724
		if ( !isset( $this->sharedObjects['selectedBuckets'] ) ) {
1725
			throw new \LogicException( 'Buckets were not selected yet, you must not initialize A/B tested classes before the app processes the request.' );
1726
		}
1727
		return $this->sharedObjects['selectedBuckets'];
1728
	}
1729
1730
	public function setSelectedBuckets( array $selectedBuckets ): void {
1731
		$this->sharedObjects['selectedBuckets'] = $selectedBuckets;
1732
	}
1733
1734
	public function getCampaignCollection(): CampaignCollection {
1735
		return new CampaignCollection( ...$this->getCampaigns() );
1736
	}
1737
}
1738