Passed
Push — develop-3.3.x ( 2b4c5a...83f4a3 )
by Mario
02:46
created

validate_payment_date_time()   B

Complexity

Conditions 9
Paths 7

Size

Total Lines 40
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 20
c 0
b 0
f 0
nc 7
nop 1
dl 0
loc 40
rs 8.0555

1 Method

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