Passed
Push — develop-3.3.x ( 378726...43b836 )
by Mario
04:29
created

transactions_controller::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 44
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Many Parameters   

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 *
4
 * PayPal Donation extension for the phpBB Forum Software package.
5
 *
6
 * @copyright (c) 2015-2020 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\operators\transactions;
25
use Symfony\Component\DependencyInjection\ContainerInterface;
26
27
/**
28
 * @property array              args               Array of args for hidden fiels
29
 * @property config             config             Config object
30
 * @property ContainerInterface container          Service container interface
31
 * @property string             id_prefix_name     Prefix name for identifier in the URL
32
 * @property string             lang_key_prefix    Prefix for the messages thrown by exceptions
33
 * @property language           language           Language user object
34
 * @property log                log                The phpBB log system.
35
 * @property string             module_name        Name of the module currently used
36
 * @property request            request            Request object.
37
 * @property bool               submit             State of submit $_POST variable
38
 * @property template           template           Template object
39
 * @property string             u_action           Action URL
40
 * @property user               user               User object.
41
 * @property user_loader        user_loader        User loader object
42
 */
43
class transactions_controller extends admin_main
44
{
45
	public $ppde_operator;
46
	protected $adm_relative_path;
47
	protected $auth;
48
	protected $user_loader;
49
	protected $entry_count;
50
	protected $last_page_offset;
51
	protected $php_ext;
52
	protected $phpbb_admin_path;
53
	protected $phpbb_root_path;
54
	protected $ppde_actions;
55
	protected $ppde_actions_currency;
56
	protected $ppde_entity;
57
	protected $table_prefix;
58
	protected $table_ppde_transactions;
59
60
	/**
61
	 * Constructor
62
	 *
63
	 * @param auth                             $auth                       Authentication object
64
	 * @param config                           $config                     Config object
65
	 * @param ContainerInterface               $container                  Service container interface
66
	 * @param language                         $language                   Language user object
67
	 * @param log                              $log                        The phpBB log system
68
	 * @param core                             $ppde_actions               PPDE actions object
69
	 * @param currency                         $ppde_actions_currency      PPDE currency actions object
70
	 * @param \skouat\ppde\entity\transactions $ppde_entity_transactions   Entity object
71
	 * @param transactions                     $ppde_operator_transactions Operator object
72
	 * @param request                          $request                    Request object
73
	 * @param template                         $template                   Template object
74
	 * @param user                             $user                       User object.
75
	 * @param user_loader                      $user_loader                User loader object
76
	 * @param string                           $adm_relative_path          phpBB admin relative path
77
	 * @param string                           $phpbb_root_path            phpBB root path
78
	 * @param string                           $php_ext                    phpEx
79
	 * @param string                           $table_prefix               The table prefix
80
	 * @param string                           $table_ppde_transactions    Name of the table used to store data
81
	 *
82
	 * @access public
83
	 */
84
	public function __construct(
85
		auth $auth,
86
		config $config,
87
		ContainerInterface $container,
88
		language $language,
89
		log $log,
90
		core $ppde_actions,
91
		currency $ppde_actions_currency,
92
		\skouat\ppde\entity\transactions $ppde_entity_transactions,
93
		transactions $ppde_operator_transactions,
94
		request $request,
95
		template $template,
96
		user $user,
97
		user_loader $user_loader,
98
		string $adm_relative_path,
99
		string $phpbb_root_path,
100
		string $php_ext,
101
		string $table_prefix,
102
		string $table_ppde_transactions
103
	)
104
	{
105
		$this->auth = $auth;
106
		$this->config = $config;
107
		$this->container = $container;
108
		$this->language = $language;
109
		$this->log = $log;
110
		$this->ppde_actions = $ppde_actions;
111
		$this->ppde_actions_currency = $ppde_actions_currency;
112
		$this->ppde_entity = $ppde_entity_transactions;
113
		$this->ppde_operator = $ppde_operator_transactions;
114
		$this->request = $request;
115
		$this->template = $template;
116
		$this->user = $user;
117
		$this->user_loader = $user_loader;
118
		$this->adm_relative_path = $adm_relative_path;
119
		$this->phpbb_admin_path = $phpbb_root_path . $adm_relative_path;
120
		$this->phpbb_root_path = $phpbb_root_path;
121
		$this->php_ext = $php_ext;
122
		$this->table_prefix = $table_prefix;
123
		$this->table_ppde_transactions = $table_ppde_transactions;
124
		parent::__construct(
125
			'transactions',
126
			'PPDE_DT',
127
			'transaction'
128
		);
129
	}
130
131
	/**
132
	 * {@inheritdoc}
133
	 */
134
	public function display(): void
135
	{
136
		/** @type \phpbb\pagination $pagination */
137
		$pagination = $this->container->get('pagination');
138
139
		// Sorting
140
		$limit_days = [
141
			0   => $this->language->lang('ALL_ENTRIES'),
142
			1   => $this->language->lang('1_DAY'),
143
			7   => $this->language->lang('7_DAYS'),
144
			14  => $this->language->lang('2_WEEKS'),
145
			30  => $this->language->lang('1_MONTH'),
146
			90  => $this->language->lang('3_MONTHS'),
147
			180 => $this->language->lang('6_MONTHS'),
148
			365 => $this->language->lang('1_YEAR'),
149
		];
150
		$sort_by_text = [
151
			'txn'      => $this->language->lang('PPDE_DT_SORT_TXN_ID'),
152
			'u'        => $this->language->lang('PPDE_DT_SORT_DONORS'),
153
			'ipn'      => $this->language->lang('PPDE_DT_SORT_IPN_STATUS'),
154
			'ipn_test' => $this->language->lang('PPDE_DT_SORT_IPN_TYPE'),
155
			'ps'       => $this->language->lang('PPDE_DT_SORT_PAYMENT_STATUS'),
156
			't'        => $this->language->lang('SORT_DATE'),
157
		];
158
		$sort_by_sql = [
159
			'txn'      => 'txn.txn_id',
160
			'u'        => 'u.username_clean',
161
			'ipn'      => 'txn.confirmed',
162
			'ipn_test' => 'txn.test_ipn',
163
			'ps'       => 'txn.payment_status',
164
			't'        => 'txn.payment_date',
165
		];
166
167
		$s_limit_days = $s_sort_key = $s_sort_dir = $u_sort_param = '';
168
		gen_sort_selects($limit_days, $sort_by_text, $this->args['hidden_fields']['st'], $this->args['hidden_fields']['sk'], $this->args['hidden_fields']['sd'], $s_limit_days, $s_sort_key, $s_sort_dir, $u_sort_param);
169
170
		// Define where and sort sql for use in displaying transactions
171
		$sql_where = ($this->args['hidden_fields']['st']) ? (time() - ($this->args['hidden_fields']['st'] * self::SECONDS_IN_A_DAY)) : 0;
172
		$sql_sort = $sort_by_sql[$this->args['hidden_fields']['sk']] . ' ' . (($this->args['hidden_fields']['sd'] === 'd') ? 'DESC' : 'ASC');
173
174
		$keywords = $this->request->variable('keywords', '', true);
175
		$keywords_param = !empty($keywords) ? '&amp;keywords=' . urlencode(htmlspecialchars_decode($keywords)) : '';
176
177
		// Grab log data
178
		$log_data = [];
179
		$log_count = 0;
180
181
		$this->view_txn_log($log_data, $log_count, (int) $this->config['topics_per_page'], $this->args['hidden_fields']['start'], $sql_where, $sql_sort, $keywords);
182
183
		$base_url = $this->u_action . '&amp;' . $u_sort_param . $keywords_param;
184
		$pagination->generate_template_pagination($base_url, 'pagination', 'start', $log_count, (int) $this->config['topics_per_page'], $this->args['hidden_fields']['start']);
185
186
		$this->template->assign_vars([
187
			'S_CLEARLOGS'  => $this->auth->acl_get('a_ppde_manage'),
188
			'S_KEYWORDS'   => $keywords,
189
			'S_LIMIT_DAYS' => $s_limit_days,
190
			'S_SORT_KEY'   => $s_sort_key,
191
			'S_SORT_DIR'   => $s_sort_dir,
192
			'U_ACTION'     => $this->u_action . '&amp;' . $u_sort_param . $keywords_param . '&amp;start=' . $this->args['hidden_fields']['start'],
193
		]);
194
195
		array_map([$this, 'display_log_assign_template_vars'], $log_data);
196
	}
197
198
	/**
199
	 * View log
200
	 *
201
	 * @param array  &$log         The result array with the logs
202
	 * @param mixed  &$log_count   If $log_count is set to false, we will skip counting all entries in the
203
	 *                             database. Otherwise an integer with the number of total matching entries is returned.
204
	 * @param int     $limit       Limit the number of entries that are returned
205
	 * @param int     $offset      Offset when fetching the log entries, f.e. when paginating
206
	 * @param int     $limit_days
207
	 * @param string  $sort_by     SQL order option, e.g. 'l.log_time DESC'
208
	 * @param string  $keywords    Will only return log entries that have the keywords in log_operation or log_data
209
	 *
210
	 * @return int Returns the offset of the last valid page, if the specified offset was invalid (too high)
211
	 * @access private
212
	 */
213
	private function view_txn_log(&$log, &$log_count, $limit = 0, $offset = 0, $limit_days = 0, $sort_by = 'txn.payment_date DESC', $keywords = ''): int
214
	{
215
		$count_logs = ($log_count !== false);
216
217
		$log = $this->get_logs($count_logs, $limit, $offset, $limit_days, $sort_by, $keywords);
218
		$log_count = $this->get_log_count();
219
220
		return $this->get_valid_offset();
221
	}
222
223
	/**
224
	 * @param bool   $count_logs
225
	 * @param int    $limit
226
	 * @param int    $offset
227
	 * @param int    $log_time
228
	 * @param string $sort_by
229
	 * @param string $keywords
230
	 *
231
	 * @return array $log
232
	 * @access private
233
	 */
234
	private function get_logs($count_logs = true, $limit = 0, $offset = 0, $log_time = 0, $sort_by = 'txn.payment_date DESC', $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
		}
246
		else
247
		{
248
			$url_ary['profile_url'] = append_sid($this->phpbb_root_path . 'memberlist.' . $this->php_ext, 'mode=viewprofile');
249
			$url_ary['txn_url'] = '';
250
		}
251
252
		$get_logs_sql_ary = $this->ppde_operator->get_logs_sql_ary($keywords, $sort_by, $log_time);
253
254
		if ($count_logs)
255
		{
256
			$this->entry_count = $this->ppde_operator->query_sql_count($get_logs_sql_ary, 'txn.transaction_id');
257
258
			if ($this->entry_count === 0)
259
			{
260
				// Save the queries, because there are no logs to display
261
				$this->last_page_offset = 0;
262
263
				return [];
264
			}
265
266
			// Return the user to the last page that is valid
267
			while ($this->last_page_offset >= $this->entry_count)
268
			{
269
				$this->last_page_offset = max(0, $this->last_page_offset - $limit);
270
			}
271
		}
272
273
		return $this->ppde_operator->build_log_entries($get_logs_sql_ary, $url_ary, $limit, $this->last_page_offset);
274
	}
275
276
	/**
277
	 * @return int
278
	 */
279
	public function get_log_count(): int
280
	{
281
		return (int) $this->entry_count ?: 0;
282
	}
283
284
	/**
285
	 * @return int
286
	 */
287
	public function get_valid_offset(): int
288
	{
289
		return (int) $this->last_page_offset ?: 0;
290
	}
291
292
	/**
293
	 * Gets vars from POST then build a array of them
294
	 *
295
	 * @param string $id     Module id
296
	 * @param string $mode   Module categorie
297
	 * @param string $action Action name
298
	 *
299
	 * @return void
300
	 * @access private
301
	 */
302
	public function set_hidden_fields($id, $mode, $action): void
303
	{
304
		$this->args['action'] = $action;
305
		$this->args['hidden_fields'] = [
306
			'start'     => $this->request->variable('start', 0),
307
			'delall'    => $this->request->variable('delall', false, false, \phpbb\request\request_interface::POST),
308
			'delmarked' => $this->request->variable('delmarked', false, false, \phpbb\request\request_interface::POST),
309
			'i'         => $id,
310
			'mark'      => $this->request->variable('mark', [0]),
311
			'mode'      => $mode,
312
			'st'        => $this->request->variable('st', 0),
313
			'sk'        => $this->request->variable('sk', 't'),
314
			'sd'        => $this->request->variable('sd', 'd'),
315
		];
316
317
		// Prepares args depending actions
318
		if (($this->args['hidden_fields']['delmarked'] || $this->args['hidden_fields']['delall']) && $this->auth->acl_get('a_ppde_manage'))
319
		{
320
			$this->args['action'] = 'delete';
321
		}
322
		else if ($this->request->is_set('approve'))
323
		{
324
			$this->args['action'] = 'approve';
325
			$this->args['hidden_fields'] = array_merge($this->args['hidden_fields'], [
326
				'approve'             => true,
327
				'id'                  => $this->request->variable('id', 0),
328
				'txn_errors_approved' => $this->request->variable('txn_errors_approved', 0),
329
			]);
330
		}
331
		else if ($this->request->is_set('add'))
332
		{
333
			$this->args['action'] = 'add';
334
		}
335
		else if ($this->request->is_set_post('change'))
336
		{
337
			$this->args['action'] = 'change';
338
		}
339
	}
340
341
	public function get_hidden_fields(): array
342
	{
343
		return array_merge(
344
			['i'                           => $this->args['hidden_fields']['i'],
345
			 'mode'                        => $this->args['hidden_fields']['mode'],
346
			 'action'                      => $this->args['action'],
347
			 $this->id_prefix_name . '_id' => $this->args[$this->id_prefix_name . '_id']],
348
			$this->args['hidden_fields']);
349
	}
350
351
	/**
352
	 * {@inheritdoc}
353
	 */
354
	public function change(): void
355
	{
356
		$username = $this->request->variable('username', '', true);
357
		$donor_id = $this->request->variable('donor_id', 0);
358
359
		try
360
		{
361
			$user_id = $this->validate_user_id($username, $donor_id);
362
		}
363
		catch (transaction_exception $e)
364
		{
365
			trigger_error(implode('<br>', $e->get_errors()) . adm_back_link($this->u_action), E_USER_WARNING);
366
		}
367
368
		// Request Identifier of the transaction
369
		$transaction_id = $this->request->variable('id', 0);
370
371
		$this->ppde_entity->load($transaction_id);
372
373
		if (!$this->ppde_entity->data_exists($this->ppde_entity->build_sql_data_exists()))
374
		{
375
			trigger_error($this->language->lang('PPDE_DT_NO_TRANSACTION') . adm_back_link($this->u_action), E_USER_WARNING);
376
		}
377
378
		$log_action = $this->ppde_entity
379
			->set_user_id($user_id)
380
			->add_edit_data()
381
		;
382
383
		$this->log->add('admin', $this->user->data['user_id'], $this->user->ip, 'LOG_' . $this->lang_key_prefix . '_' . strtoupper($log_action));
384
		trigger_error($this->language->lang($this->lang_key_prefix . '_' . strtoupper($log_action)) . adm_back_link($this->u_action));
385
	}
386
387
	/**
388
	 * Returns the intended user ID
389
	 *
390
	 * @param string $username
391
	 * @param int    $donor_id
392
	 *
393
	 * @return int returns user_id
394
	 * @throws transaction_exception if the user_id is less than or equal to the default value for ANONYMOUS.
395
	 * @access private
396
	 */
397
	private function validate_user_id($username, $donor_id = 0): int
398
	{
399
		if ($this->should_return_anonymous($username, $donor_id))
400
		{
401
			return ANONYMOUS;
402
		}
403
404
		$user_id = ($username !== '') ? $this->user_loader->load_user_by_username($username) : $donor_id;
405
406
		if ($user_id <= ANONYMOUS)
407
		{
408
			throw (new transaction_exception())->set_errors([$this->language->lang('PPDE_MT_DONOR_NOT_FOUND')]);
409
		}
410
411
		return $user_id;
412
	}
413
414
	/**
415
	 * Determines if the given username and donor ID should result in an anonymous response.
416
	 *
417
	 * @param string $username The username to check.
418
	 * @param int    $donor_id The donor ID to check.
419
	 * @return bool Returns true if the username is empty and either the donor ID is ANONYMOUS or the 'u' parameter is
420
	 *                         set in the URL. Otherwise, returns false.
421
	 */
422
	private function should_return_anonymous(string $username, int $donor_id): bool
423
	{
424
		// if the username is empty and (donor_id is ANONYMOUS or 'u' parameter is set in URL),
425
		return $username === '' && ($donor_id === ANONYMOUS || $this->request->is_set('u'));
426
	}
427
428
	public function approve(): void
429
	{
430
		$transaction_id = (int) $this->args['hidden_fields']['id'];
431
		$txn_approved = empty($this->args['hidden_fields']['txn_errors_approved']);
432
433
		// Update DB record
434
		$this->ppde_entity->load($transaction_id);
435
		$this->ppde_entity->set_txn_errors_approved($txn_approved);
436
		$this->ppde_entity->save(false);
437
438
		// Prepare transaction settings before doing actions
439
		$transaction_data = $this->ppde_entity->get_data($this->ppde_operator->build_sql_data($transaction_id));
440
		$this->ppde_actions->set_transaction_data($transaction_data[0]);
441
		$this->ppde_actions->set_ipn_test_properties($this->ppde_entity->get_test_ipn());
442
		$this->ppde_actions->is_donor_is_member();
443
444
		if ($txn_approved)
445
		{
446
			$this->do_transactions_actions(!$this->ppde_actions->get_ipn_test() && $this->ppde_actions->get_donor_is_member());
447
		}
448
449
		$this->log->add('admin', $this->user->data['user_id'], $this->user->ip, 'LOG_' . $this->lang_key_prefix . '_UPDATED', time());
450
	}
451
452
	/**
453
	 * Does actions for validated transaction
454
	 *
455
	 * @param bool $is_member
456
	 *
457
	 * @return void
458
	 * @access private
459
	 */
460
	private function do_transactions_actions($is_member): void
461
	{
462
		$this->ppde_actions->update_overview_stats();
463
		$this->ppde_actions->update_raised_amount();
464
465
		if ($is_member)
466
		{
467
			$this->ppde_actions->update_donor_stats();
468
			$this->ppde_actions->donors_group_user_add();
469
			$this->ppde_actions->notification->notify_donor_donation_received();
470
		}
471
	}
472
473
	/**
474
	 * {@inheritdoc}
475
	 */
476
	public function add(): void
477
	{
478
		$errors = [];
479
480
		$transaction_data = $this->request_transaction_vars();
481
482
		if ($this->request->is_set_post('submit'))
483
		{
484
			$errors = $this->process_transaction($transaction_data, $errors);
485
		}
486
487
		$this->ppde_actions_currency->build_currency_select_menu((int) $this->config['ppde_default_currency']);
488
489
		$this->s_error_assign_template_vars($errors);
490
491
		$this->template->assign_vars($transaction_data);
492
493
		$this->template->assign_vars([
494
			'U_ACTION'             => $this->u_action,
495
			'U_BACK'               => $this->u_action,
496
			'S_ADD'                => true,
497
			'ANONYMOUS_USER_ID'    => ANONYMOUS,
498
			'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'),
499
			'PAYMENT_TIME_FORMATS' => $this->get_payment_time_examples(),
500
		]);
501
	}
502
503
	/**
504
	 * Process a transaction with the given transaction data and handle any errors that occur.
505
	 *
506
	 * @param array $transaction_data The data for the transaction.
507
	 * @param array $errors           The array to store any errors that occur during processing.
508
	 *
509
	 * @return array The updated array of errors after processing the transaction.
510
	 */
511
	private function process_transaction(array $transaction_data, array $errors): array
512
	{
513
		try
514
		{
515
			$this->ppde_actions->log_to_db($this->build_data_ary($transaction_data));
516
517
			// Prepare transaction settings before doing actions
518
			$this->ppde_actions->set_transaction_data($transaction_data);
519
			$this->ppde_actions->is_donor_is_member();
520
521
			$this->do_transactions_actions($this->ppde_actions->get_donor_is_member() && !$transaction_data['MT_ANONYMOUS']);
522
523
			$this->log_transaction($transaction_data);
524
		}
525
		catch (transaction_exception $e)
526
		{
527
			$errors = $e->get_errors();
528
		}
529
530
		return $errors;
531
	}
532
533
	/**
534
	 * Logs an entry in the phpBB admin log.
535
	 *
536
	 * @param array $transaction_data The data of the transaction to be logged.
537
	 *
538
	 * @return void
539
	 * @access private
540
	 */
541
	private function log_transaction(array $transaction_data): void
542
	{
543
		$this->log->add('admin', $this->user->data['user_id'], $this->user->ip, 'LOG_PPDE_MT_ADDED', time(), [$transaction_data['MT_USERNAME']]);
544
		trigger_error($this->language->lang('PPDE_MT_ADDED') . adm_back_link($this->u_action));
545
	}
546
547
	/**
548
	 * Returns requested data from manual transaction form
549
	 *
550
	 * @return array
551
	 * @access private
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
	 * Prepare data array before send it to $this->entity
575
	 *
576
	 * @param array $transaction_data
577
	 *
578
	 * @return array
579
	 * @throws transaction_exception
580
	 * @access private
581
	 */
582
	private function build_data_ary($transaction_data): array
583
	{
584
		$errors = [];
585
586
		try
587
		{
588
			$user_id = $this->validate_user_id($transaction_data['MT_USERNAME']);
589
		}
590
		catch (transaction_exception $e)
591
		{
592
			$errors = $e->get_errors();
593
		}
594
595
		$payment_date = implode('-', [
596
			$transaction_data['MT_PAYMENT_DATE_YEAR'],
597
			$transaction_data['MT_PAYMENT_DATE_MONTH'],
598
			$transaction_data['MT_PAYMENT_DATE_DAY'],
599
		]);
600
601
		$payment_date_timestamp_at_midnight = $this->user->get_timestamp_from_format('Y-m-d H:i:s', $payment_date . ' 00:00:00');
602
		$payment_time = $transaction_data['MT_PAYMENT_TIME'];
603
		$payment_time_timestamp = strtotime($payment_time);
604
605
		// Normalize payment time to start from today at midnight
606
		$payment_time_timestamp_from_midnight = $payment_time_timestamp - strtotime('00:00:00');
607
608
		$payment_date_time = $payment_date_timestamp_at_midnight + $payment_time_timestamp_from_midnight;
609
610
		$errors = array_merge($errors,
611
			$this->mc_gross_too_low($transaction_data),
612
			$this->mc_fee_negative($transaction_data),
613
			$this->mc_fee_too_high($transaction_data),
614
			$this->payment_date_timestamp_at_midnight($payment_date_timestamp_at_midnight, $payment_date),
615
			$this->payment_time_timestamp($payment_time_timestamp, $payment_date),
616
			$this->payment_date_time((string) $payment_date_time));
617
618
		if (count($errors))
619
		{
620
			throw (new transaction_exception())->set_errors($errors);
621
		}
622
623
		return [
624
			'business'          => $this->config['ppde_account_id'],
625
			'confirmed'         => true,
626
			'custom'            => implode('_', ['uid', $user_id, time()]),
627
			'exchange_rate'     => '',
628
			'first_name'        => $transaction_data['MT_FIRST_NAME'],
629
			'item_name'         => '',
630
			'item_number'       => implode('_', ['uid', $user_id, time()]),
631
			'last_name'         => $transaction_data['MT_LAST_NAME'],
632
			'mc_currency'       => $transaction_data['MT_MC_CURRENCY'],
633
			'mc_gross'          => $transaction_data['MT_MC_GROSS'],
634
			'mc_fee'            => $transaction_data['MT_MC_FEE'],
635
			'net_amount'        => 0.0, // This value is calculated in core_actions:log_to_db()
636
			'parent_txn_id'     => '',
637
			'payer_email'       => $transaction_data['MT_PAYER_EMAIL'],
638
			'payer_id'          => '',
639
			'payer_status'      => '',
640
			'payment_date'      => $payment_date_time,
641
			'payment_status'    => 'Completed',
642
			'payment_type'      => '',
643
			'memo'              => $transaction_data['MT_MEMO'],
644
			'receiver_id'       => '',
645
			'receiver_email'    => '',
646
			'residence_country' => strtoupper($transaction_data['MT_RESIDENCE_COUNTRY']),
647
			'settle_amount'     => 0.0,
648
			'settle_currency'   => '',
649
			'test_ipn'          => false,
650
			'txn_errors'        => '',
651
			'txn_id'            => 'PPDE' . gen_rand_string(13),
652
			'txn_type'          => 'ppde_manual_donation',
653
			'user_id'           => $user_id,
654
		];
655
	}
656
657
	/**
658
	 * Tests if mc_gross is to low
659
	 *
660
	 * @param array $data
661
	 *
662
	 * @return array
663
	 * @access private
664
	 */
665
	private function mc_gross_too_low($data): array
666
	{
667
		if ($data['MT_MC_GROSS'] <= 0)
668
		{
669
			return [$this->language->lang('PPDE_MT_MC_GROSS_TOO_LOW')];
670
		}
671
672
		return [];
673
	}
674
675
	/**
676
	 * Tests if mc_fee has a negative value
677
	 *
678
	 * @param array $data
679
	 *
680
	 * @return array
681
	 * @access private
682
	 */
683
	private function mc_fee_negative($data): array
684
	{
685
		if ($data['MT_MC_FEE'] < 0)
686
		{
687
			return [$this->language->lang('PPDE_MT_MC_FEE_NEGATIVE')];
688
		}
689
690
		return [];
691
	}
692
693
	/**
694
	 * Tests if mc_fee is too high
695
	 *
696
	 * @param array $data
697
	 *
698
	 * @return array
699
	 * @access private
700
	 */
701
	private function mc_fee_too_high($data): array
702
	{
703
		if ($data['MT_MC_FEE'] >= $data['MT_MC_GROSS'])
704
		{
705
			return [$this->language->lang('PPDE_MT_MC_FEE_TOO_HIGH')];
706
		}
707
708
		return [];
709
	}
710
711
	/**
712
	 * Tests if the date is valid
713
	 *
714
	 * @param string|false $payment_date_timestamp_at_midnight
715
	 * @param string       $payment_date
716
	 *
717
	 * @return array
718
	 * @access private
719
	 */
720
	private function payment_date_timestamp_at_midnight($payment_date_timestamp_at_midnight, $payment_date): array
721
	{
722
		if ($payment_date_timestamp_at_midnight === false)
723
		{
724
			return [$this->language->lang('PPDE_MT_PAYMENT_DATE_ERROR', $payment_date)];
725
		}
726
727
		return [];
728
	}
729
730
	/**
731
	 * @param int|false $payment_time_timestamp
732
	 * @param string    $payment_date
733
	 *
734
	 * @return array
735
	 * @access private
736
	 */
737
	private function payment_time_timestamp($payment_time_timestamp, $payment_date): array
738
	{
739
		if ($payment_time_timestamp === false)
740
		{
741
			return [$this->language->lang('PPDE_MT_PAYMENT_TIME_ERROR', $payment_date)];
742
		}
743
744
		return [];
745
	}
746
747
	/**
748
	 * @param string $payment_date_time
749
	 *
750
	 * @return array
751
	 * @access private
752
	 */
753
	private function payment_date_time($payment_date_time): array
754
	{
755
		if ($payment_date_time > time())
756
		{
757
			return [$this->language->lang('PPDE_MT_PAYMENT_DATE_FUTURE', $this->user->format_date($payment_date_time))];
758
		}
759
760
		return [];
761
	}
762
763
	/**
764
	 * Returns a list of valid times that the user can provide in the manual transaction form
765
	 *
766
	 * @return array Array of strings representing the current time, each in a different format
767
	 * @access private
768
	 */
769
	private function get_payment_time_examples(): array
770
	{
771
		$formats = [
772
			'H:i:s',
773
			'G:i',
774
			'h:i:s a',
775
			'g:i A',
776
		];
777
778
		$examples = [];
779
780
		foreach ($formats as $format)
781
		{
782
			$examples[] = $this->user->format_date(time(), $format);
783
		}
784
785
		return $examples;
786
	}
787
788
	/**
789
	 * {@inheritdoc}
790
	 */
791
	public function view(): void
792
	{
793
		// Request Identifier of the transaction
794
		$transaction_id = (int) $this->request->variable('id', 0);
795
796
		// add additional fields to the table schema needed by entity->import()
797
		$additional_table_schema = [
798
			'item_username'    => ['name' => 'username', 'type' => 'string'],
799
			'item_user_colour' => ['name' => 'user_colour', 'type' => 'string'],
800
		];
801
802
		// Grab transaction data
803
		$data_ary = $this->ppde_entity->get_data($this->ppde_operator->build_sql_data($transaction_id), $additional_table_schema);
804
805
		array_map([$this, 'action_assign_template_vars'], $data_ary);
806
807
		$this->template->assign_vars([
808
			'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'),
809
			'U_ACTION'        => $this->u_action,
810
			'U_BACK'          => $this->u_action,
811
			'S_VIEW'          => true,
812
		]);
813
	}
814
815
	/**
816
	 * {@inheritdoc}
817
	 */
818
	public function delete(): void
819
	{
820
		$where_sql = '';
821
822
		if ($this->args['hidden_fields']['delmarked'] && count($this->args['hidden_fields']['mark']))
823
		{
824
			$where_sql = $this->ppde_operator->build_marked_where_sql($this->args['hidden_fields']['mark']);
825
		}
826
827
		if ($where_sql || $this->args['hidden_fields']['delall'])
828
		{
829
			$this->ppde_entity->delete(0, '', $where_sql, $this->args['hidden_fields']['delall']);
830
			$this->ppde_actions->set_ipn_test_properties(true);
831
			$this->ppde_actions->update_overview_stats();
832
			$this->ppde_actions->set_ipn_test_properties(false);
833
			$this->ppde_actions->update_overview_stats();
834
			$this->log->add('admin', $this->user->data['user_id'], $this->user->ip, 'LOG_' . $this->lang_key_prefix . '_PURGED', time());
835
		}
836
	}
837
838
	/**
839
	 * Set log output vars for display in the template
840
	 *
841
	 * @param array $row
842
	 *
843
	 * @return void
844
	 * @access protected
845
	 */
846
	protected function display_log_assign_template_vars(array $row): void
847
	{
848
		$this->template->assign_block_vars('log', [
849
			'CONFIRMED'        => ($row['confirmed']) ? $this->language->lang('PPDE_DT_VERIFIED') : $this->language->lang('PPDE_DT_UNVERIFIED'),
850
			'DATE'             => $this->user->format_date($row['payment_date']),
851
			'ID'               => $row['transaction_id'],
852
			'PAYMENT_STATUS'   => $this->language->lang(['PPDE_DT_PAYMENT_STATUS_VALUES', strtolower($row['payment_status'])]),
853
			'TXN_ID'           => $row['txn_id'],
854
			'USERNAME'         => $row['username_full'],
855
			'S_CONFIRMED'      => (bool) $row['confirmed'],
856
			'S_PAYMENT_STATUS' => strtolower($row['payment_status']) === 'completed',
857
			'S_TXN_ERRORS'     => !empty($row['txn_errors']),
858
			'S_TEST_IPN'       => (bool) $row['test_ipn'],
859
		]);
860
	}
861
862
	/**
863
	 * Assigns action template variables
864
	 *
865
	 * @param array $data The data array containing the necessary information for assigning template variables.
866
	 *
867
	 * @return void
868
	 * @access protected
869
	 */
870
	protected function action_assign_template_vars(array $data): void
871
	{
872
		$s_hidden_fields = build_hidden_fields([
873
			'id'                  => $data['transaction_id'],
874
			'donor_id'            => $data['user_id'],
875
			'txn_errors_approved' => $data['txn_errors_approved'],
876
		]);
877
878
		$currency_mc_data = $this->ppde_actions_currency->get_currency_data($data['mc_currency']);
879
		$currency_settle_data = $this->ppde_actions_currency->get_currency_data($data['settle_currency']);
880
881
		$this->template->assign_vars([
882
			'BOARD_USERNAME' => get_username_string('full', $data['user_id'], $data['username'], $data['user_colour'], $this->language->lang('GUEST'), append_sid($this->phpbb_admin_path . 'index.' . $this->php_ext, 'i=users&amp;mode=overview')),
883
			'EXCHANGE_RATE'  => '1 ' . $data['mc_currency'] . ' = ' . $data['exchange_rate'] . ' ' . $data['settle_currency'],
884
			'ITEM_NAME'      => $data['item_name'],
885
			'ITEM_NUMBER'    => $data['item_number'],
886
			'MC_GROSS'       => $this->ppde_actions_currency->format_currency((float) $data['mc_gross'], $currency_mc_data[0]['currency_iso_code'], $currency_mc_data[0]['currency_symbol'], (bool) $currency_mc_data[0]['currency_on_left']),
887
			'MC_FEE'         => $this->ppde_actions_currency->format_currency((float) $data['mc_fee'], $currency_mc_data[0]['currency_iso_code'], $currency_mc_data[0]['currency_symbol'], (bool) $currency_mc_data[0]['currency_on_left']),
888
			'MC_NET'         => $this->ppde_actions_currency->format_currency((float) $data['net_amount'], $currency_mc_data[0]['currency_iso_code'], $currency_mc_data[0]['currency_symbol'], (bool) $currency_mc_data[0]['currency_on_left']),
889
			'MEMO'           => $data['memo'],
890
			'NAME'           => $data['first_name'] . ' ' . $data['last_name'],
891
			'PAYER_EMAIL'    => $data['payer_email'],
892
			'PAYER_ID'       => $data['payer_id'],
893
			'PAYER_STATUS'   => $data['payer_status'] ? $this->language->lang('PPDE_DT_VERIFIED') : $this->language->lang('PPDE_DT_UNVERIFIED'),
894
			'PAYMENT_DATE'   => $this->user->format_date($data['payment_date']),
895
			'PAYMENT_STATUS' => $this->language->lang(['PPDE_DT_PAYMENT_STATUS_VALUES', strtolower($data['payment_status'])]),
896
			'RECEIVER_EMAIL' => $data['receiver_email'],
897
			'RECEIVER_ID'    => $data['receiver_id'],
898
			'SETTLE_AMOUNT'  => $this->ppde_actions_currency->format_currency((float) $data['settle_amount'], $currency_settle_data[0]['currency_iso_code'], $currency_settle_data[0]['currency_symbol'], (bool) $currency_settle_data[0]['currency_on_left']),
899
			'TXN_ID'         => $data['txn_id'],
900
901
			'L_PPDE_DT_SETTLE_AMOUNT'         => $this->language->lang('PPDE_DT_SETTLE_AMOUNT', $data['settle_currency']),
902
			'L_PPDE_DT_EXCHANGE_RATE_EXPLAIN' => $this->language->lang('PPDE_DT_EXCHANGE_RATE_EXPLAIN', $this->user->format_date($data['payment_date'])),
903
			'S_CONVERT'                       => !((int) $data['settle_amount'] === 0 && empty($data['exchange_rate'])),
904
			'S_ERROR'                         => !empty($data['txn_errors']),
905
			'S_ERROR_APPROVED'                => !empty($data['txn_errors_approved']),
906
			'S_HIDDEN_FIELDS'                 => $s_hidden_fields,
907
			'ERROR_MSG'                       => (!empty($data['txn_errors'])) ? $data['txn_errors'] : '',
908
		]);
909
	}
910
}
911