transactions_controller   F
last analyzed

Complexity

Total Complexity 62

Size/Duplication

Total Lines 719
Duplicated Lines 0 %

Importance

Changes 17
Bugs 0 Features 1
Metric Value
eloc 290
dl 0
loc 719
rs 3.44
c 17
b 0
f 1
wmc 62

28 Methods

Rating   Name   Duplication   Size   Complexity  
A display() 0 36 2
A process_transaction() 0 10 2
A prepare_add_template() 0 12 1
A get_sort_by_text_options() 0 9 1
A output_errors() 0 3 1
A get_keywords_param() 0 3 2
A change() 0 15 2
A delete() 0 17 5
A calculate_timestamp() 0 3 2
A approve() 0 22 3
A add() 0 19 3
A view_txn_log() 0 6 1
A generate_pagination() 0 5 1
A request_transaction_vars() 0 17 1
B build_data_ary() 0 67 5
A update_transaction() 0 10 2
A assign_log_entries_to_template() 0 15 3
B set_hidden_fields() 0 36 8
A action_assign_template_vars() 0 8 1
A get_hidden_fields() 0 8 1
A log_action() 0 4 1
A get_limit_day_options() 0 11 1
A get_payment_time_examples() 0 17 2
A __construct() 0 37 1
A view() 0 21 1
A get_log_count() 0 3 2
A assign_template_vars() 0 9 1
A get_logs() 0 39 6

How to fix   Complexity   

Complex Class

Complex classes like transactions_controller often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use transactions_controller, and based on these observations, apply Extract Interface, too.

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