Passed
Push — develop-3.3.x ( c85e2c...2b4c5a )
by Mario
02:35
created

transactions_controller::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 44
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 23
nc 1
nop 18
dl 0
loc 44
rs 9.552
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 *
4
 * PayPal Donation extension for the phpBB Forum Software package.
5
 *
6
 * @copyright (c) 2015-2024 Skouat
7
 * @license GNU General Public License, version 2 (GPL-2.0)
8
 *
9
 */
10
11
namespace skouat\ppde\controller\admin;
12
13
use phpbb\auth\auth;
14
use phpbb\config\config;
15
use phpbb\language\language;
16
use phpbb\log\log;
17
use phpbb\request\request;
18
use phpbb\template\template;
19
use phpbb\user;
20
use phpbb\user_loader;
21
use skouat\ppde\actions\core;
22
use skouat\ppde\actions\currency;
23
use skouat\ppde\exception\transaction_exception;
24
use skouat\ppde\includes\transaction_template_helper;
25
use skouat\ppde\includes\transaction_validator;
26
use skouat\ppde\operators\transactions;
27
use Symfony\Component\DependencyInjection\ContainerInterface;
28
29
/**
30
 * @property array              args               Array of args for hidden fields
31
 * @property config             config             Config object
32
 * @property ContainerInterface container          Service container interface
33
 * @property string             id_prefix_name     Prefix name for identifier in the URL
34
 * @property string             lang_key_prefix    Prefix for the messages thrown by exceptions
35
 * @property language           language           Language user object
36
 * @property log                log                The phpBB log system
37
 * @property request            request            Request object
38
 * @property template           template           Template object
39
 * @property string             u_action           Action URL
40
 * @property user               user               User object
41
 */
42
class transactions_controller extends admin_main
43
{
44
	public $ppde_operator;
45
	protected $adm_relative_path;
46
	protected $auth;
47
	protected $user_loader;
48
	protected $entry_count;
49
	protected $last_page_offset;
50
	protected $php_ext;
51
	protected $phpbb_admin_path;
52
	protected $phpbb_root_path;
53
	protected $ppde_actions;
54
	protected $ppde_actions_currency;
55
	protected $ppde_entity;
56
	protected $template_helper;
57
	protected $transaction_validator;
58
59
	/**
60
	 * Constructor
61
	 *
62
	 * @param auth                             $auth                       Authentication object
63
	 * @param config                           $config                     Config object
64
	 * @param ContainerInterface               $container                  Service container interface
65
	 * @param language                         $language                   Language user object
66
	 * @param log                              $log                        The phpBB log system
67
	 * @param core                             $ppde_actions               PPDE actions object
68
	 * @param currency                         $ppde_actions_currency      PPDE currency actions object
69
	 * @param \skouat\ppde\entity\transactions $ppde_entity_transactions   Entity object
70
	 * @param transactions                     $ppde_operator_transactions Operator object
71
	 * @param request                          $request                    Request object
72
	 * @param template                         $template                   Template object
73
	 * @param user                             $user                       User object
74
	 * @param user_loader                      $user_loader                User loader object
75
	 * @param string                           $adm_relative_path          phpBB admin relative path
76
	 * @param string                           $phpbb_root_path            phpBB root path
77
	 * @param string                           $php_ext                    phpEx
78
	 */
79
	public function __construct(
80
		auth $auth,
81
		config $config,
82
		ContainerInterface $container,
83
		language $language,
84
		log $log,
85
		core $ppde_actions,
86
		currency $ppde_actions_currency,
87
		\skouat\ppde\entity\transactions $ppde_entity_transactions,
88
		transactions $ppde_operator_transactions,
89
		request $request,
90
		template $template,
91
		user $user,
92
		user_loader $user_loader,
93
		transaction_template_helper $template_helper,
94
		transaction_validator $transaction_validator,
95
		string $adm_relative_path,
96
		string $phpbb_root_path,
97
		string $php_ext
98
	)
99
	{
100
		$this->auth = $auth;
101
		$this->config = $config;
102
		$this->container = $container;
103
		$this->language = $language;
104
		$this->log = $log;
105
		$this->ppde_actions = $ppde_actions;
106
		$this->ppde_actions_currency = $ppde_actions_currency;
107
		$this->ppde_entity = $ppde_entity_transactions;
108
		$this->ppde_operator = $ppde_operator_transactions;
109
		$this->request = $request;
110
		$this->template = $template;
111
		$this->user = $user;
112
		$this->user_loader = $user_loader;
113
		$this->template_helper = $template_helper;
114
		$this->transaction_validator = $transaction_validator;
115
		$this->adm_relative_path = $adm_relative_path;
116
		$this->phpbb_admin_path = $phpbb_root_path . $adm_relative_path;
117
		$this->phpbb_root_path = $phpbb_root_path;
118
		$this->php_ext = $php_ext;
119
		parent::__construct(
120
			'transactions',
121
			'PPDE_DT',
122
			'transaction'
123
		);
124
	}
125
126
	/**
127
	 * {@inheritdoc}
128
	 */
129
	public function display(): void
130
	{
131
		// Sorting and pagination setup
132
		$sort_by_text = $this->get_sort_by_text_options();
133
		$sort_by_sql = $this->get_sort_options();
134
		$sort_key = $this->request->variable('sk', 't');
135
		$sort_dir = $this->request->variable('sd', 'd');
136
		$start = $this->request->variable('start', 0);
137
		$limit = (int) $this->config['topics_per_page'];
138
139
		// Filtering setup
140
		$limit_days = $this->get_limit_day_options();
141
		$selected_days = $this->request->variable('st', 0);
142
		$keywords = $this->request->variable('keywords', '', true);
143
144
		// Generate sorting and filtering selects
145
		$s_limit_days = $s_sort_key = $s_sort_dir = $u_sort_param = '';
146
		gen_sort_selects($limit_days, $sort_by_text, $selected_days, $sort_key, $sort_dir, $s_limit_days, $s_sort_key, $s_sort_dir, $u_sort_param);
147
148
		// Prepare SQL conditions
149
		$sql_sort = $sort_by_sql[$sort_key] . ' ' . (($sort_dir === 'd') ? 'DESC' : 'ASC');
150
151
		// Fetch log data
152
		$log_data = [];
153
		$log_count = 0;
154
		$log_time = $this->calculate_timestamp($selected_days);
155
		$this->view_txn_log($log_data, $log_count, $limit, $start, $log_time, $sql_sort, $keywords);
156
157
		// Generate pagination
158
		$this->generate_pagination($log_count, $limit, $start, $u_sort_param, $keywords);
159
160
		// Assign template variables
161
		$this->assign_template_vars($s_limit_days, $s_sort_key, $s_sort_dir, $u_sort_param, $keywords, $start);
162
163
		// Assign log entries to template
164
		$this->assign_log_entries_to_template($log_data);
165
	}
166
167
	/**
168
	 * Get sort by text options for transactions
169
	 *
170
	 * @return array An associative array of sort options and their corresponding language strings
171
	 */
172
	private function get_sort_by_text_options(): array
173
	{
174
		return [
175
			'txn'      => $this->language->lang('PPDE_DT_SORT_TXN_ID'),
176
			'u'        => $this->language->lang('PPDE_DT_SORT_DONORS'),
177
			'ipn'      => $this->language->lang('PPDE_DT_SORT_IPN_STATUS'),
178
			'ipn_test' => $this->language->lang('PPDE_DT_SORT_IPN_TYPE'),
179
			'ps'       => $this->language->lang('PPDE_DT_SORT_PAYMENT_STATUS'),
180
			't'        => $this->language->lang('SORT_DATE'),
181
		];
182
	}
183
184
	/**
185
	 * Get sort options for transactions
186
	 *
187
	 * @return array An associative array of sort keys and their corresponding SQL column names
188
	 */
189
	private function get_sort_options(): array
190
	{
191
		return [
192
			'txn'      => 'txn.txn_id',
193
			'u'        => 'u.username_clean',
194
			'ipn'      => 'txn.confirmed',
195
			'ipn_test' => 'txn.test_ipn',
196
			'ps'       => 'txn.payment_status',
197
			't'        => 'txn.payment_date',
198
		];
199
	}
200
201
	/**
202
	 * Get limit day options for filtering
203
	 *
204
	 * @return array An associative array of day limits and their corresponding language strings
205
	 */
206
	private function get_limit_day_options(): array
207
	{
208
		return [
209
			0   => $this->language->lang('ALL_ENTRIES'),
210
			1   => $this->language->lang('1_DAY'),
211
			7   => $this->language->lang('7_DAYS'),
212
			14  => $this->language->lang('2_WEEKS'),
213
			30  => $this->language->lang('1_MONTH'),
214
			90  => $this->language->lang('3_MONTHS'),
215
			180 => $this->language->lang('6_MONTHS'),
216
			365 => $this->language->lang('1_YEAR'),
217
		];
218
	}
219
220
	/**
221
	 * Calculate the timestamp for filtering transactions based on the selected number of days
222
	 *
223
	 * @param int $selected_days Number of days to look back for transactions
224
	 * @return int The calculated timestamp, or 0 if no day limit is set
225
	 */
226
	private function calculate_timestamp(int $selected_days): int
227
	{
228
		return $selected_days > 0 ? time() - ($selected_days * self::SECONDS_IN_A_DAY) : 0;
229
	}
230
231
	/**
232
	 * View transaction log
233
	 *
234
	 * @param array  &$log       The result array with the logs
235
	 * @param int    &$log_count If $log_count is set to false, we will skip counting all entries in the database
236
	 *                           Otherwise an integer with the number of total matching entries is returned
237
	 * @param int     $limit     Limit the number of entries that are returned
238
	 * @param int     $offset    Offset when fetching the log entries, e.g. when paginating
239
	 * @param int     $log_time  Timestamp to filter logs
240
	 * @param string  $sort_by   SQL order option, e.g. 'l.log_time DESC'
241
	 * @param string  $keywords  Will only return log entries that have the keywords in log_operation or log_data
242
	 * @return void
243
	 */
244
	private function view_txn_log(array &$log, &$log_count, int $limit = 0, int $offset = 0, int $log_time = 0, string $sort_by = 'txn.payment_date DESC', string $keywords = ''): void
245
	{
246
		$count_logs = ($log_count !== false);
247
248
		$log = $this->get_logs($count_logs, $limit, $offset, $log_time, $sort_by, $keywords);
249
		$log_count = $this->get_log_count();
250
	}
251
252
	/**
253
	 * Get logs based on specified parameters
254
	 *
255
	 * @param bool   $count_logs Whether to count the total number of logs
256
	 * @param int    $limit      Maximum number of logs to retrieve
257
	 * @param int    $offset     Starting point for retrieving logs
258
	 * @param int    $log_time   Timestamp to filter logs
259
	 * @param string $sort_by    SQL ORDER BY clause
260
	 * @param string $keywords   Keywords to filter logs
261
	 * @return array Array of log entries
262
	 */
263
	private function get_logs(bool $count_logs = true, int $limit = 0, int $offset = 0, int $log_time = 0, string $sort_by = 'txn.payment_date DESC', string $keywords = ''): array
264
	{
265
		$this->entry_count = 0;
266
		$this->last_page_offset = $offset;
267
		$url_ary = [];
268
269
		if ($this->phpbb_admin_path && $this->ppde_actions->is_in_admin())
270
		{
271
			$url_ary['profile_url'] = append_sid($this->phpbb_admin_path . 'index.' . $this->php_ext, 'i=users&amp;mode=overview');
272
			$url_ary['txn_url'] = append_sid($this->phpbb_admin_path . 'index.' . $this->php_ext, 'i=-skouat-ppde-acp-ppde_module&amp;mode=transactions');
273
		}
274
		else
275
		{
276
			$url_ary['profile_url'] = append_sid($this->phpbb_root_path . 'memberlist.' . $this->php_ext, 'mode=viewprofile');
277
			$url_ary['txn_url'] = '';
278
		}
279
280
		$get_logs_sql_ary = $this->ppde_operator->get_logs_sql_ary($keywords, $sort_by, $log_time);
281
282
		if ($count_logs)
283
		{
284
			$this->entry_count = $this->ppde_operator->query_sql_count($get_logs_sql_ary, 'txn.transaction_id');
285
286
			if ($this->entry_count === 0)
287
			{
288
				// Save the queries, because there are no logs to display
289
				$this->last_page_offset = 0;
290
291
				return [];
292
			}
293
294
			// Return the user to the last page that is valid
295
			while ($this->last_page_offset >= $this->entry_count)
296
			{
297
				$this->last_page_offset = max(0, $this->last_page_offset - $limit);
298
			}
299
		}
300
301
		return $this->ppde_operator->build_log_entries($get_logs_sql_ary, $url_ary, $limit, $this->last_page_offset);
302
	}
303
304
	/**
305
	 * Get the total count of log entries
306
	 *
307
	 * @return int The total number of log entries
308
	 */
309
	public function get_log_count(): int
310
	{
311
		return (int) $this->entry_count ?: 0;
312
	}
313
314
	/**
315
	 * Generate pagination for transaction list
316
	 *
317
	 * @param int    $log_count    Total number of log entries
318
	 * @param int    $limit        Number of entries per page
319
	 * @param int    $start        Starting offset for the current page
320
	 * @param string $u_sort_param URL parameters for sorting
321
	 * @param string $keywords     Search keywords
322
	 */
323
	private function generate_pagination(int $log_count, int $limit, int $start, string $u_sort_param, string $keywords): void
324
	{
325
		$pagination = $this->container->get('pagination');
326
		$base_url = $this->u_action . '&amp;' . $u_sort_param . $this->get_keywords_param($keywords);
327
		$pagination->generate_template_pagination($base_url, 'pagination', 'start', $log_count, $limit, $start);
328
	}
329
330
	/**
331
	 * Get keywords parameter for URL
332
	 *
333
	 * @param string $keywords Search keywords
334
	 * @return string URL-encoded keywords parameter
335
	 */
336
	private function get_keywords_param(string $keywords): string
337
	{
338
		return !empty($keywords) ? '&amp;keywords=' . urlencode(htmlspecialchars_decode($keywords)) : '';
339
	}
340
341
	/**
342
	 * Assign common template variables
343
	 *
344
	 * @param string $s_limit_days
345
	 * @param string $s_sort_key
346
	 * @param string $s_sort_dir
347
	 * @param string $u_sort_param
348
	 * @param string $keywords
349
	 * @param int    $start
350
	 */
351
	private function assign_template_vars(string $s_limit_days, string $s_sort_key, string $s_sort_dir, string $u_sort_param, string $keywords, int $start): void
352
	{
353
		$this->template->assign_vars([
354
			'S_CLEARLOGS'  => $this->auth->acl_get('a_ppde_manage'),
355
			'S_KEYWORDS'   => $keywords,
356
			'S_LIMIT_DAYS' => $s_limit_days,
357
			'S_SORT_KEY'   => $s_sort_key,
358
			'S_SORT_DIR'   => $s_sort_dir,
359
			'U_ACTION'     => $this->u_action . '&amp;' . $u_sort_param . $this->get_keywords_param($keywords) . '&amp;start=' . $start,
360
		]);
361
	}
362
363
	/**
364
	 * Assign log entries to template
365
	 *
366
	 * @param array $log_data Array of log entries
367
	 */
368
	private function assign_log_entries_to_template(array $log_data): void
369
	{
370
		foreach ($log_data as $row)
371
		{
372
			$this->template->assign_block_vars('log', [
373
				'CONFIRMED'        => ($row['confirmed']) ? $this->language->lang('PPDE_DT_VERIFIED') : $this->language->lang('PPDE_DT_UNVERIFIED'),
374
				'DATE'             => $this->user->format_date($row['payment_date']),
375
				'ID'               => $row['transaction_id'],
376
				'PAYMENT_STATUS'   => $this->language->lang(['PPDE_DT_PAYMENT_STATUS_VALUES', strtolower($row['payment_status'])]),
377
				'TXN_ID'           => $row['txn_id'],
378
				'USERNAME'         => $row['username_full'],
379
				'S_CONFIRMED'      => (bool) $row['confirmed'],
380
				'S_PAYMENT_STATUS' => strtolower($row['payment_status']) === 'completed',
381
				'S_TXN_ERRORS'     => !empty($row['txn_errors']),
382
				'S_TEST_IPN'       => (bool) $row['test_ipn'],
383
			]);
384
		}
385
	}
386
387
	/**
388
	 * Set hidden fields for the transaction form
389
	 *
390
	 * @param string $id     Module id
391
	 * @param string $mode   Module category
392
	 * @param string $action Action name
393
	 */
394
	public function set_hidden_fields($id, $mode, $action): void
395
	{
396
		$this->args['action'] = $action;
397
		$this->args['hidden_fields'] = [
398
			'start'     => $this->request->variable('start', 0),
399
			'delall'    => $this->request->variable('delall', false, false, \phpbb\request\request_interface::POST),
400
			'delmarked' => $this->request->variable('delmarked', false, false, \phpbb\request\request_interface::POST),
401
			'i'         => $id,
402
			'mark'      => $this->request->variable('mark', [0]),
403
			'mode'      => $mode,
404
			'st'        => $this->request->variable('st', 0),
405
			'sk'        => $this->request->variable('sk', 't'),
406
			'sd'        => $this->request->variable('sd', 'd'),
407
		];
408
409
		// Prepare args depending on actions
410
		if (($this->args['hidden_fields']['delall'] || ($this->args['hidden_fields']['delmarked'] && count($this->args['hidden_fields']['mark']))) && $this->auth->acl_get('a_ppde_manage'))
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($this->args['hidden_fie...cl_get('a_ppde_manage'), Probably Intended Meaning: $this->args['hidden_fiel...l_get('a_ppde_manage'))
Loading history...
411
		{
412
			$this->args['action'] = 'delete';
413
		}
414
		else if ($this->request->is_set('approve'))
415
		{
416
			$this->args['action'] = 'approve';
417
			$this->args['hidden_fields'] = array_merge($this->args['hidden_fields'], [
418
				'approve'             => true,
419
				'id'                  => $this->request->variable('id', 0),
420
				'txn_errors_approved' => $this->request->variable('txn_errors_approved', 0),
421
			]);
422
		}
423
		else if ($this->request->is_set('add'))
424
		{
425
			$this->args['action'] = 'add';
426
		}
427
		else if ($this->request->is_set_post('change'))
428
		{
429
			$this->args['action'] = 'change';
430
		}
431
	}
432
433
	/**
434
	 * Get hidden fields for the transaction form
435
	 *
436
	 * @return array
437
	 */
438
	public function get_hidden_fields(): array
439
	{
440
		return array_merge(
441
			['i'                           => $this->args['hidden_fields']['i'],
442
			 'mode'                        => $this->args['hidden_fields']['mode'],
443
			 'action'                      => $this->args['action'],
444
			 $this->id_prefix_name . '_id' => $this->args[$this->id_prefix_name . '_id']],
445
			$this->args['hidden_fields']);
446
	}
447
448
	/**
449
	 * {@inheritdoc}
450
	 */
451
	public function change(): void
452
	{
453
		$username = $this->request->variable('username', '', true);
454
		$donor_id = $this->request->variable('donor_id', 0);
455
		$transaction_id = $this->request->variable('id', 0);
456
457
		try
458
		{
459
			$user_id = $this->transaction_validator->validate_user_id($username, $donor_id);
460
			$this->update_transaction($transaction_id, $user_id);
461
			$this->log_action('DT_UPDATED');
462
		}
463
		catch (transaction_exception $e)
464
		{
465
			$this->output_errors($e->get_errors());
466
		}
467
	}
468
469
	/**
470
	 * Validate and return the user ID
471
	 *
472
	 * @param string $username
473
	 * @param int    $donor_id
474
	 * @return int
475
	 * @throws transaction_exception
476
	 */
477
	private function validate_user_id(string $username, int $donor_id = 0): int
478
	{
479
		if (empty($username) && ($donor_id === ANONYMOUS || $this->request->is_set('u')))
480
		{
481
			return ANONYMOUS;
482
		}
483
484
		$user_id = ($username !== '') ? $this->user_loader->load_user_by_username($username) : $donor_id;
485
486
		if ($user_id <= ANONYMOUS)
487
		{
488
			throw new transaction_exception([$this->language->lang('PPDE_MT_DONOR_NOT_FOUND')]);
489
		}
490
491
		return $user_id;
492
	}
493
494
	/**
495
	 * Update transaction with new user ID
496
	 *
497
	 * @param int $transaction_id
498
	 * @param int $user_id
499
	 * @throws transaction_exception
500
	 */
501
	private function update_transaction($transaction_id, $user_id): void
502
	{
503
		$this->ppde_entity->load($transaction_id);
504
505
		if (!$this->ppde_entity->data_exists($this->ppde_entity->build_sql_data_exists()))
506
		{
507
			throw new transaction_exception([$this->language->lang('PPDE_DT_NO_TRANSACTION')]);
508
		}
509
510
		$this->ppde_entity->set_user_id($user_id)->add_edit_data();
511
	}
512
513
	/**
514
	 * Log the action in the admin log
515
	 *
516
	 * @param string $action_type
517
	 */
518
	private function log_action(string $action_type): void
519
	{
520
		$this->log->add('admin', $this->user->data['user_id'], $this->user->ip, 'LOG_PPDE_' . $action_type);
521
		trigger_error($this->language->lang('PPDE_' . $action_type) . adm_back_link($this->u_action));
522
	}
523
524
	/**
525
	 * {@inheritdoc}
526
	 */
527
	public function add(): void
528
	{
529
		$transaction_data = $this->request_transaction_vars();
530
531
		if ($this->is_form_submitted())
532
		{
533
			try
534
			{
535
				$this->process_transaction($transaction_data);
536
				$this->log_action('MT_ADDED');
537
			}
538
			catch (transaction_exception $e)
539
			{
540
				$this->prepare_add_template($e->get_errors(), $transaction_data);
541
				return;
542
			}
543
		}
544
545
		$this->prepare_add_template([], $transaction_data);
546
	}
547
548
	/**
549
	 * Request transaction variables from the form
550
	 *
551
	 * @return array
552
	 */
553
	private function request_transaction_vars(): array
554
	{
555
		return [
556
			'MT_ANONYMOUS'          => $this->request->is_set('u'),
557
			'MT_USERNAME'           => $this->request->variable('username', '', true),
558
			'MT_FIRST_NAME'         => $this->request->variable('first_name', '', true),
559
			'MT_LAST_NAME'          => $this->request->variable('last_name', '', true),
560
			'MT_PAYER_EMAIL'        => $this->request->variable('payer_email', '', true),
561
			'MT_RESIDENCE_COUNTRY'  => $this->request->variable('residence_country', ''),
562
			'MT_MC_GROSS'           => $this->request->variable('mc_gross', 0.0),
563
			'MT_MC_CURRENCY'        => $this->request->variable('mc_currency', ''),
564
			'MT_MC_FEE'             => $this->request->variable('mc_fee', 0.0),
565
			'MT_PAYMENT_DATE_YEAR'  => $this->request->variable('payment_date_year', (int) $this->user->format_date(time(), 'Y')),
566
			'MT_PAYMENT_DATE_MONTH' => $this->request->variable('payment_date_month', (int) $this->user->format_date(time(), 'n')),
567
			'MT_PAYMENT_DATE_DAY'   => $this->request->variable('payment_date_day', (int) $this->user->format_date(time(), 'j')),
568
			'MT_PAYMENT_TIME'       => $this->request->variable('payment_time', $this->user->format_date(time(), 'H:i:s')),
569
			'MT_MEMO'               => $this->request->variable('memo', '', true),
570
		];
571
	}
572
573
	/**
574
	 * Process a transaction with the given transaction data and handle any errors that occur
575
	 *
576
	 * @param array $transaction_data The data for the transaction
577
	 * @throws transaction_exception
578
	 */
579
	private function process_transaction(array $transaction_data): void
580
	{
581
		$data_ary = $this->build_data_ary($transaction_data);
582
		$this->ppde_actions->log_to_db($data_ary);
583
584
		$this->ppde_actions->set_transaction_data($transaction_data);
585
		$this->ppde_actions->is_donor_is_member();
586
587
		$this->ppde_actions->do_transactions_actions(
588
			$this->ppde_actions->get_donor_is_member() && !$transaction_data['MT_ANONYMOUS']
589
		);
590
	}
591
592
	/**
593
	 * Prepare data array before sending it to $this->entity
594
	 *
595
	 * @param array $transaction_data
596
	 * @return array
597
	 * @throws transaction_exception
598
	 */
599
	private function build_data_ary(array $transaction_data): array
600
	{
601
		$errors = [];
602
603
		try
604
		{
605
			$user_id = $this->validate_user_id($transaction_data['MT_USERNAME']);
606
		}
607
		catch (transaction_exception $e)
608
		{
609
			$errors = array_merge($errors, $e->get_errors());
610
		}
611
612
		try
613
		{
614
			$payment_date_time = $this->transaction_validator->validate_payment_date_time($transaction_data);
615
		}
616
		catch (transaction_exception $e)
617
		{
618
			$errors = array_merge($errors, $e->get_errors());
619
		}
620
621
		try
622
		{
623
			$this->transaction_validator->validate_transaction_amounts($transaction_data);
624
		}
625
		catch (transaction_exception $e)
626
		{
627
			$errors = array_merge($errors, $e->get_errors());
628
		}
629
630
		if (!empty($errors))
631
		{
632
			throw new transaction_exception($errors);
633
		}
634
635
		return [
636
			'business'          => $this->config['ppde_account_id'],
637
			'confirmed'         => true,
638
			'custom'            => implode('_', ['uid', $user_id, time()]),
639
			'exchange_rate'     => '',
640
			'first_name'        => $transaction_data['MT_FIRST_NAME'],
641
			'item_name'         => '',
642
			'item_number'       => implode('_', ['uid', $user_id, time()]),
643
			'last_name'         => $transaction_data['MT_LAST_NAME'],
644
			'mc_currency'       => $transaction_data['MT_MC_CURRENCY'],
645
			'mc_gross'          => $transaction_data['MT_MC_GROSS'],
646
			'mc_fee'            => $transaction_data['MT_MC_FEE'],
647
			'net_amount'        => 0.0, // This value is calculated in core_actions:log_to_db()
648
			'parent_txn_id'     => '',
649
			'payer_email'       => $transaction_data['MT_PAYER_EMAIL'],
650
			'payer_id'          => '',
651
			'payer_status'      => '',
652
			'payment_date'      => $payment_date_time,
653
			'payment_status'    => 'Completed',
654
			'payment_type'      => '',
655
			'memo'              => $transaction_data['MT_MEMO'],
656
			'receiver_id'       => '',
657
			'receiver_email'    => '',
658
			'residence_country' => strtoupper($transaction_data['MT_RESIDENCE_COUNTRY']),
659
			'settle_amount'     => 0.0,
660
			'settle_currency'   => '',
661
			'test_ipn'          => false,
662
			'txn_errors'        => '',
663
			'txn_id'            => 'PPDE' . gen_rand_string(13),
664
			'txn_type'          => 'ppde_manual_donation',
665
			'user_id'           => $user_id,
666
		];
667
	}
668
669
	/**
670
	 * Validate the payment date and time
671
	 *
672
	 * @param array $transaction_data
673
	 * @return int
674
	 * @throws transaction_exception
675
	 */
676
	private function validate_payment_date_time(array $transaction_data)
677
	{
678
		$payment_date = implode('-', [
679
			$transaction_data['MT_PAYMENT_DATE_YEAR'],
680
			$transaction_data['MT_PAYMENT_DATE_MONTH'],
681
			$transaction_data['MT_PAYMENT_DATE_DAY'],
682
		]);
683
684
		$payment_time = $transaction_data['MT_PAYMENT_TIME'];
685
		$date_time_string = $payment_date . ' ' . $payment_time;
686
687
		$payment_date_time = $this->parse_date_time($date_time_string);
688
689
		if ($payment_date_time === false)
690
		{
691
			throw new transaction_exception([$this->language->lang('PPDE_MT_PAYMENT_DATE_ERROR', $date_time_string)]);
692
		}
693
694
		if ($payment_date_time > time())
695
		{
696
			throw new transaction_exception([$this->language->lang('PPDE_MT_PAYMENT_DATE_FUTURE', $this->user->format_date($payment_date_time))]);
697
		}
698
699
		// Validate time
700
		$time_parts = explode(':', $payment_time);
701
		if (count($time_parts) < 2 || count($time_parts) > 3)
702
		{
703
			throw new transaction_exception([$this->language->lang('PPDE_MT_PAYMENT_TIME_ERROR', $payment_time)]);
704
		}
705
706
		$hours = (int) $time_parts[0];
707
		$minutes = (int) $time_parts[1];
708
		$seconds = isset($time_parts[2]) ? (int) $time_parts[2] : 0;
709
710
		if ($hours >= 24 || $minutes >= 60 || $seconds >= 60)
711
		{
712
			throw new transaction_exception([$this->language->lang('PPDE_MT_PAYMENT_TIME_ERROR', $payment_time)]);
713
		}
714
715
		return $payment_date_time;
716
	}
717
718
	/**
719
	 * Parse date and time string
720
	 *
721
	 * @param string $date_time_string
722
	 * @return int|false
723
	 */
724
	private function parse_date_time($date_time_string)
725
	{
726
		$formats = ['Y-m-d H:i:s', 'Y-m-d G:i', 'Y-m-d h:i:s a', 'Y-m-d g:i A'];
727
728
		foreach ($formats as $format)
729
		{
730
			$parsed = \DateTime::createFromFormat($format, $date_time_string);
731
			if ($parsed !== false)
732
			{
733
				return $parsed->getTimestamp();
734
			}
735
		}
736
737
		return false;
738
	}
739
740
	/**
741
	 * Validate transaction amounts
742
	 *
743
	 * @param array $transaction_data
744
	 * @throws transaction_exception
745
	 */
746
	private function validate_transaction_amounts(array $transaction_data)
747
	{
748
		$errors = [];
749
750
		if ($transaction_data['MT_MC_GROSS'] <= 0)
751
		{
752
			$errors[] = $this->language->lang('PPDE_MT_MC_GROSS_TOO_LOW');
753
		}
754
755
		if ($transaction_data['MT_MC_FEE'] < 0)
756
		{
757
			$errors[] = $this->language->lang('PPDE_MT_MC_FEE_NEGATIVE');
758
		}
759
760
		if ($transaction_data['MT_MC_FEE'] >= $transaction_data['MT_MC_GROSS'])
761
		{
762
			$errors[] = $this->language->lang('PPDE_MT_MC_FEE_TOO_HIGH');
763
		}
764
765
		if (!empty($errors))
766
		{
767
			throw new transaction_exception($errors);
768
		}
769
	}
770
771
	/**
772
	 * Prepare and assign template variables for adding a new transaction
773
	 *
774
	 * @param array $errors           Array of error messages
775
	 * @param array $transaction_data Transaction data to be displayed in the form
776
	 */
777
	private function prepare_add_template(array $errors, array $transaction_data): void
778
	{
779
		$this->ppde_actions_currency->build_currency_select_menu((int) $this->config['ppde_default_currency']);
780
		$this->s_error_assign_template_vars($errors);
781
		$this->template->assign_vars($transaction_data);
782
		$this->template->assign_vars([
783
			'U_ACTION'             => $this->u_action,
784
			'U_BACK'               => $this->u_action,
785
			'S_ADD'                => true,
786
			'ANONYMOUS_USER_ID'    => ANONYMOUS,
787
			'U_FIND_USERNAME'      => append_sid($this->phpbb_root_path . 'memberlist.' . $this->php_ext, 'mode=searchuser&amp;form=manual_transaction&amp;field=username&amp;select_single=true'),
788
			'PAYMENT_TIME_FORMATS' => $this->get_payment_time_examples(),
789
		]);
790
	}
791
792
	/**
793
	 * Returns a list of valid times that the user can provide in the manual transaction form
794
	 *
795
	 * @return array Array of strings representing the current time, each in a different format
796
	 */
797
	private function get_payment_time_examples(): array
798
	{
799
		$formats = [
800
			'H:i:s',
801
			'G:i',
802
			'h:i:s a',
803
			'g:i A',
804
		];
805
806
		$examples = [];
807
808
		foreach ($formats as $format)
809
		{
810
			$examples[] = $this->user->format_date(time(), $format);
811
		}
812
813
		return $examples;
814
	}
815
816
	/**
817
	 * Output errors
818
	 *
819
	 * @param array $errors
820
	 */
821
	private function output_errors(array $errors)
822
	{
823
		trigger_error(implode('<br>', $errors) . adm_back_link($this->u_action), E_USER_WARNING);
824
	}
825
826
	/**
827
	 * Approve a transaction
828
	 */
829
	public function approve(): void
830
	{
831
		$transaction_id = (int) $this->args['hidden_fields']['id'];
832
		$txn_approved = empty($this->args['hidden_fields']['txn_errors_approved']);
833
834
		// Update DB record
835
		$this->ppde_entity->load($transaction_id);
836
		$this->ppde_entity->set_txn_errors_approved($txn_approved);
837
		$this->ppde_entity->save(false);
838
839
		// Prepare transaction settings before doing actions
840
		$transaction_data = $this->ppde_entity->get_data($this->ppde_operator->build_sql_data($transaction_id));
841
		$this->ppde_actions->set_transaction_data($transaction_data[0]);
842
		$this->ppde_actions->set_ipn_test_properties($this->ppde_entity->get_test_ipn());
843
		$this->ppde_actions->is_donor_is_member();
844
845
		if ($txn_approved)
846
		{
847
			$this->do_transactions_actions(!$this->ppde_actions->get_ipn_test() && $this->ppde_actions->get_donor_is_member());
0 ignored issues
show
Bug introduced by
The method do_transactions_actions() does not exist on skouat\ppde\controller\a...transactions_controller. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

847
			$this->/** @scrutinizer ignore-call */ 
848
          do_transactions_actions(!$this->ppde_actions->get_ipn_test() && $this->ppde_actions->get_donor_is_member());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
848
		}
849
850
		$this->log->add('admin', $this->user->data['user_id'], $this->user->ip, 'LOG_' . $this->lang_key_prefix . '_UPDATED', time());
851
	}
852
853
	/**
854
	 * View transaction details
855
	 */
856
	public function view(): void
857
	{
858
		// Request Identifier of the transaction
859
		$transaction_id = (int) $this->request->variable('id', 0);
860
861
		// Add additional fields to the table schema needed by entity->import()
862
		$additional_table_schema = [
863
			'item_username'    => ['name' => 'username', 'type' => 'string'],
864
			'item_user_colour' => ['name' => 'user_colour', 'type' => 'string'],
865
		];
866
867
		// Grab transaction data
868
		$data_ary = $this->ppde_entity->get_data($this->ppde_operator->build_sql_data($transaction_id), $additional_table_schema);
869
870
		array_map([$this, 'action_assign_template_vars'], $data_ary);
871
872
		$this->template->assign_vars([
873
			'U_FIND_USERNAME' => append_sid($this->phpbb_root_path . 'memberlist.' . $this->php_ext, 'mode=searchuser&amp;form=view_transactions&amp;field=username&amp;select_single=true'),
874
			'U_ACTION'        => $this->u_action,
875
			'U_BACK'          => $this->u_action,
876
			'S_VIEW'          => true,
877
		]);
878
	}
879
880
	/**
881
	 * Delete transaction(s)
882
	 */
883
	public function delete(): void
884
	{
885
		$where_sql = '';
886
887
		if ($this->args['hidden_fields']['delmarked'] && count($this->args['hidden_fields']['mark']))
888
		{
889
			$where_sql = $this->ppde_operator->build_marked_where_sql($this->args['hidden_fields']['mark']);
890
		}
891
892
		if ($where_sql || $this->args['hidden_fields']['delall'])
893
		{
894
			$this->ppde_entity->delete(0, '', $where_sql, $this->args['hidden_fields']['delall']);
895
			$this->ppde_actions->set_ipn_test_properties(true);
896
			$this->ppde_actions->update_overview_stats();
897
			$this->ppde_actions->set_ipn_test_properties(false);
898
			$this->ppde_actions->update_overview_stats();
899
			$this->log->add('admin', $this->user->data['user_id'], $this->user->ip, 'LOG_' . $this->lang_key_prefix . '_PURGED', time());
900
		}
901
	}
902
903
	/**
904
	 * Assign action template variables
905
	 *
906
	 * @param array $data Transaction data
907
	 */
908
	protected function action_assign_template_vars(array $data): void
909
	{
910
		$this->template_helper->assign_hidden_fields($data);
911
		$this->template_helper->assign_currency_data($data);
912
		$this->template_helper->assign_user_data($data);
913
		$this->template_helper->assign_transaction_details($data);
914
		$this->template_helper->assign_payment_details($data);
915
		$this->template_helper->assign_error_data($data);
916
	}
917
}
918