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

transactions_controller::assign_hidden_fields()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 8
rs 10
c 1
b 0
f 0
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