Passed
Push — develop-3.3.x ( 83f4a3...1bac3f )
by Mario
02:57
created

transactions::sql_last_donation_ary()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 7
rs 10
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\operators;
12
13
use phpbb\db\driver\driver_interface;
14
15
class transactions
16
{
17
	protected $container;
18
	protected $db;
19
	protected $ppde_transactions_log_table;
20
21
	/**
22
	 * Constructor
23
	 *
24
	 * @param driver_interface $db                          Database connection
25
	 * @param string           $ppde_transactions_log_table Table name
26
	 *
27
	 * @access public
28
	 */
29
	public function __construct(driver_interface $db, $ppde_transactions_log_table)
30
	{
31
		$this->db = $db;
32
		$this->ppde_transactions_log_table = $ppde_transactions_log_table;
33
	}
34
35
	/**
36
	 * SQL Query to return Transaction log data table
37
	 *
38
	 * @param $transaction_id
39
	 *
40
	 * @return string
41
	 * @access public
42
	 */
43
	public function build_sql_data($transaction_id = 0): string
44
	{
45
		// Build main sql request
46
		$sql_ary = [
47
			'SELECT'    => 'txn.*, u.username, u.user_colour',
48
			'FROM'      => [$this->ppde_transactions_log_table => 'txn'],
49
			'LEFT_JOIN' => [
50
				[
51
					'FROM' => [USERS_TABLE => 'u'],
52
					'ON'   => 'u.user_id = txn.user_id',
53
				],
54
			],
55
			'ORDER_BY'  => 'txn.transaction_id',
56
			'WHERE'     => ($transaction_id ? 'txn.transaction_id = ' . (int) $transaction_id : ''),
57
		];
58
59
		// Return all transactions entities
60
		return $this->db->sql_build_query('SELECT', $sql_ary);
61
	}
62
63
	/**
64
	 * SQL Query to count how many donated
65
	 *
66
	 * @param bool   $detailed
67
	 * @param string $order_by
68
	 *
69
	 * @return array
70
	 * @access public
71
	 */
72
	public function sql_donorlist_ary($detailed = false, $order_by = ''): array
73
	{
74
		// Build sql request
75
		$sql_donorslist_ary = [
76
			'SELECT'   => 'txn.user_id, txn.mc_currency',
77
			'FROM'     => [$this->ppde_transactions_log_table => 'txn'],
78
			'WHERE'    => 'txn.user_id <> ' . ANONYMOUS . "
79
							AND txn.payment_status = 'Completed'
80
							AND txn.test_ipn = 0",
81
			'GROUP_BY' => 'txn.user_id, txn.mc_currency',
82
			'ORDER_BY' => $order_by,
83
		];
84
85
		if ($detailed)
86
		{
87
			$sql_donorslist_ary['SELECT'] = 'txn.user_id, txn.mc_currency, MAX(txn.transaction_id) AS max_txn_id, SUM(txn.mc_gross) AS amount, MAX(u.username)';
88
			$sql_donorslist_ary['LEFT_JOIN'] = [
89
				[
90
					'FROM' => [USERS_TABLE => 'u'],
91
					'ON'   => 'u.user_id = txn.user_id',
92
				]];
93
		}
94
95
		return $sql_donorslist_ary;
96
	}
97
98
	/**
99
	 * SQL Query to return information of the last donation of the donor
100
	 *
101
	 * @param int $transaction_id
102
	 *
103
	 * @return array
104
	 * @access public
105
	 */
106
	public function sql_last_donation_ary($transaction_id): array
107
	{
108
		// Build sql request
109
		return [
110
			'SELECT' => 'txn.payment_date, txn.mc_gross, txn.mc_currency',
111
			'FROM'   => [$this->ppde_transactions_log_table => 'txn'],
112
			'WHERE'  => 'txn.transaction_id = ' . (int) $transaction_id,
113
		];
114
	}
115
116
	/**
117
	 * Build SQL Query to return the donors list
118
	 *
119
	 * @param array $sql_donorlist_ary
120
	 *
121
	 * @return string
122
	 * @access public
123
	 */
124
	public function build_sql_donorlist_data($sql_donorlist_ary): string
125
	{
126
		// Return all transactions entities
127
		return $this->db->sql_build_query('SELECT', $sql_donorlist_ary);
128
	}
129
130
	/**
131
	 * Returns total entries of selected field
132
	 *
133
	 * @param array  $count_sql_ary
134
	 * @param string $selected_field
135
	 *
136
	 * @return int
137
	 * @access public
138
	 */
139
	public function query_sql_count($count_sql_ary, $selected_field): int
140
	{
141
		$count_sql_ary['SELECT'] = 'COUNT(' . $selected_field . ') AS total_entries';
142
143
		if (array_key_exists('GROUP_BY', $count_sql_ary))
144
		{
145
			$count_sql_ary['SELECT'] = 'COUNT(DISTINCT ' . $count_sql_ary['GROUP_BY'] . ') AS total_entries';
146
		}
147
		unset($count_sql_ary['ORDER_BY'], $count_sql_ary['GROUP_BY']);
148
149
		$result = $this->db->sql_query($this->db->sql_build_query('SELECT', $count_sql_ary));
150
		$field = (int) $this->db->sql_fetchfield('total_entries');
151
		$this->db->sql_freeresult($result);
152
153
		return $field;
154
	}
155
156
	/**
157
	 * Returns the SQL Query for displaying simple transactions details
158
	 *
159
	 * @param string $keywords
160
	 * @param string $sort_by
161
	 * @param int    $log_time
162
	 *
163
	 * @return array
164
	 * @access public
165
	 */
166
	public function get_logs_sql_ary($keywords, $sort_by, $log_time): array
167
	{
168
		$sql_keywords = '';
169
		if (!empty($keywords))
170
		{
171
			// Get the SQL condition for our keywords
172
			$sql_keywords = $this->generate_sql_keyword($keywords);
173
		}
174
175
		$get_logs_sql_ary = [
176
			'SELECT'   => 'txn.transaction_id, txn.txn_id, txn.test_ipn, txn.confirmed, txn.txn_errors, txn.payment_date, txn.payment_status, txn.user_id, u.username, u.user_colour',
177
			'FROM'     => [
178
				$this->ppde_transactions_log_table => 'txn',
179
				USERS_TABLE                        => 'u',
180
			],
181
			'WHERE'    => 'txn.user_id = u.user_id ' . $sql_keywords,
182
			'ORDER_BY' => $sort_by,
183
		];
184
185
		if ($log_time)
186
		{
187
			$get_logs_sql_ary['WHERE'] = 'txn.payment_date >= ' . (int) $log_time . '
188
					AND ' . $get_logs_sql_ary['WHERE'];
189
		}
190
191
		return $get_logs_sql_ary;
192
	}
193
194
	/**
195
	 * Generates a sql condition for the specified keywords
196
	 *
197
	 * @param string $keywords           The keywords the user specified to search for
198
	 * @param string $statement_operator The operator used to prefix the statement ('AND' by default)
199
	 *
200
	 * @return string Returns the SQL condition searching for the keywords
201
	 * @access private
202
	 */
203
	private function generate_sql_keyword($keywords, $statement_operator = 'AND'): string
204
	{
205
		// Use no preg_quote for $keywords because this would lead to sole
206
		// backslashes being added. We also use an OR connection here for
207
		// spaces and the | string. Currently, regex is not supported for
208
		// searching (but may come later).
209
		$keywords = preg_split('#[\s|]+#u', utf8_strtolower($keywords), 0, PREG_SPLIT_NO_EMPTY);
0 ignored issues
show
Bug introduced by
The function utf8_strtolower was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

209
		$keywords = preg_split('#[\s|]+#u', /** @scrutinizer ignore-call */ utf8_strtolower($keywords), 0, PREG_SPLIT_NO_EMPTY);
Loading history...
210
		$sql_keywords = '';
211
212
		if (!empty($keywords))
213
		{
214
			// Build pattern and keywords...
215
			$keywords = array_map(function ($keyword) {
216
				return $this->db->sql_like_expression($this->db->get_any_char() . $keyword . $this->db->get_any_char());
217
			}, $keywords);
218
219
			$sql_keywords = ' ' . $statement_operator . ' (';
220
			$columns = ['txn.txn_id', 'u.username'];
221
			$sql_lowers = array();
222
223
			foreach ($columns as $column_name)
224
			{
225
				$sql_lower = $this->db->sql_lower_text($column_name);
226
				$sql_lowers[] = $sql_lower . ' ' . implode(' OR ' . $sql_lower . ' ', $keywords);
227
			}
228
			unset($columns);
229
230
			$sql_keywords .= implode(' OR ', $sql_lowers) . ')';
231
		}
232
233
		return $sql_keywords;
234
	}
235
236
	/**
237
	 * Returns user information based on the donor ID or email
238
	 *
239
	 * @param string     $type
240
	 * @param int|string $arg
241
	 *
242
	 * @return array|bool
243
	 * @access public
244
	 */
245
	public function query_donor_user_data($type = 'user', $arg = 1)
246
	{
247
		$sql_where = '';
248
249
		switch ($type)
250
		{
251
			case 'user':
252
				$sql_where = ' WHERE user_id = ' . (int) $arg;
253
			break;
254
			case 'username':
255
				$sql_where = " WHERE username_clean = '" . $this->db->sql_escape(utf8_clean_string($arg)) . "'";
256
			break;
257
			case 'email':
258
				$sql_where = " WHERE user_email = '" . $this->db->sql_escape(strtolower($arg)) . "'";
259
			break;
260
		}
261
262
		$sql = 'SELECT user_id, username, user_ppde_donated_amount
263
			FROM ' . USERS_TABLE .
264
			$sql_where;
265
		$result = $this->db->sql_query($sql);
266
		$row = $this->db->sql_fetchrow($result);
267
		$this->db->sql_freeresult($result);
268
269
		return $row ?: [];
270
	}
271
272
	/**
273
	 * Returns simple details of all PayPal transactions logged in the database
274
	 *
275
	 * @param array $get_logs_sql_ary
276
	 * @param array $url_ary
277
	 * @param int   $limit
278
	 * @param int   $last_page_offset
279
	 *
280
	 * @return array $log
281
	 * @access public
282
	 */
283
	public function build_log_entries($get_logs_sql_ary, $url_ary, $limit = 0, $last_page_offset = 0): array
284
	{
285
		$sql = $this->db->sql_build_query('SELECT', $get_logs_sql_ary);
286
		$result = $this->db->sql_query_limit($sql, $limit, $last_page_offset);
287
288
		$log_entries = [];
289
290
		while ($row = $this->db->sql_fetchrow($result))
291
		{
292
			$log_entries[] = [
293
				'confirmed'      => $row['confirmed'],
294
				'payment_date'   => $row['payment_date'],
295
				'payment_status' => $row['payment_status'],
296
				'test_ipn'       => $row['test_ipn'],
297
				'transaction_id' => $row['transaction_id'],
298
				'txn_errors'     => $row['txn_errors'],
299
				'txn_id'         => $this->build_transaction_url($row['transaction_id'], $row['txn_id'], $url_ary['txn_url'], $row['confirmed']),
300
				'username_full'  => get_username_string('full', $row['user_id'], $row['username'], $row['user_colour'], false, $url_ary['profile_url']),
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type string expected by parameter $guest_username of get_username_string(). ( Ignorable by Annotation )

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

300
				'username_full'  => get_username_string('full', $row['user_id'], $row['username'], $row['user_colour'], /** @scrutinizer ignore-type */ false, $url_ary['profile_url']),
Loading history...
301
			];
302
		}
303
304
		$this->db->sql_freeresult($result);
305
306
		return $log_entries;
307
	}
308
309
	/**
310
	 * Build transaction url for placing into templates.
311
	 *
312
	 * @param int    $id         The user's transaction id
313
	 * @param string $txn_id     The txn number id
314
	 * @param string $custom_url optional parameter to specify a profile url. The transaction id get appended to this
315
	 *                           url as &amp;id={id}
316
	 * @param bool   $colour     If false, the color #FF0000 will be applied on the URL.
317
	 *
318
	 * @return string A string consisting of what is wanted.
319
	 * @access private
320
	 */
321
	private function build_transaction_url($id, $txn_id, $custom_url = '', $colour = false): string
322
	{
323
		// We cache some common variables we need within this function
324
		$transaction_templates = [
325
			'tpl_nourl'      => '{{ TRANSACTION }}',
326
			'tpl_url'        => '<a href="{{ TXN_URL }}">{{ TRANSACTION }}</a>',
327
			'tpl_url_colour' => '<a href="{{ TXN_URL }}" style="{{ TXN_COLOUR }}">{{ TRANSACTION }}</a>',
328
		];
329
330
		// Returns the correct transaction url
331
332
		if (!$txn_id)
333
		{
334
			return str_replace('{{ TRANSACTION }}', $txn_id, $transaction_templates['tpl_nourl']);
335
		}
336
337
		$txn_url = ($custom_url !== '') ? $custom_url . '&amp;action=view&amp;id=' . $id : $txn_id;
338
		if ($colour)
339
		{
340
			return str_replace(
341
				['{{ TXN_URL }}', '{{ TRANSACTION }}'],
342
				[$txn_url, $txn_id],
343
				$transaction_templates['tpl_url']
344
			);
345
		}
346
		return str_replace(
347
			['{{ TXN_URL }}', '{{ TXN_COLOUR }}', '{{ TRANSACTION }}'],
348
			[$txn_url, 'color: #ff0000;', $txn_id],
349
			$transaction_templates['tpl_url_colour']
350
		);
351
	}
352
353
	/**
354
	 * Builds the SQL WHERE clause for marked transactions.
355
	 *
356
	 * @param array $marked The array of marked transaction IDs.
357
	 *
358
	 * @return string The SQL WHERE clause.
359
	 */
360
	public function build_marked_where_sql($marked): string
361
	{
362
		if (!is_array($marked) || empty($marked))
0 ignored issues
show
introduced by
The condition is_array($marked) is always true.
Loading history...
363
		{
364
			return '';
365
		}
366
367
		return ' WHERE ' . $this->db->sql_in_set('transaction_id', array_map('intval', $marked));
368
	}
369
370
	/**
371
	 * Returns the count result for updating stats
372
	 *
373
	 * @param string $type     The type of query to be executed.
374
	 * @param bool   $test_ipn The value indicating whether to use test IPN.
375
	 *
376
	 * @return int
377
	 * @access public
378
	 */
379
	public function sql_query_count_result(string $type, bool $test_ipn): int
380
	{
381
		$is_transactions_count = strpos($type, 'transactions_count') !== false;
382
		$is_known_donors_count = strpos($type, 'known_donors_count') !== false;
383
		$is_anonymous_donors_count = strpos($type, 'anonymous_donors_count') !== false;
384
385
		$field_name = $is_transactions_count ? 'txn_id' : 'payer_id';
386
		$sql_ary = $this->sql_select_stats_main($field_name);
387
		$test_ipn_str = (int) $test_ipn;
388
389
		if ($is_transactions_count)
390
		{
391
			$sql_ary['WHERE'] = "confirmed = 1 AND payment_status = 'Completed' AND txn.test_ipn = " . $test_ipn_str;
392
		}
393
		else if ($is_known_donors_count)
394
		{
395
			$sql_ary['LEFT_JOIN'] = [
396
				[
397
					'FROM' => [USERS_TABLE => 'u'],
398
					'ON'   => 'txn.user_id = u.user_id',
399
				],
400
			];
401
			$sql_ary['WHERE'] = '(u.user_type = ' . USER_NORMAL . ' OR u.user_type = ' . USER_FOUNDER . ') AND txn.test_ipn = ' . $test_ipn_str;
402
		}
403
		else if ($is_anonymous_donors_count)
404
		{
405
			$sql_ary['WHERE'] = 'txn.user_id = ' . ANONYMOUS . ' AND txn.test_ipn = ' . $test_ipn_str;
406
		}
407
408
		$result = $this->db->sql_query($this->db->sql_build_query('SELECT', $sql_ary));
409
		$count = (int) $this->db->sql_fetchfield('count_result');
410
		$this->db->sql_freeresult($result);
411
412
		return $count;
413
	}
414
415
	/**
416
	 * Make body of SQL query for stats calculation.
417
	 *
418
	 * @param string $field_name Name of the field
419
	 *
420
	 * @return array
421
	 * @access private
422
	 */
423
	private function sql_select_stats_main(string $field_name): array
424
	{
425
		return [
426
			'SELECT' => 'COUNT(DISTINCT txn.' . $field_name . ') AS count_result',
427
			'FROM'   => [$this->ppde_transactions_log_table => 'txn'],
428
		];
429
	}
430
431
	/**
432
	 * Updates the user donated amount
433
	 *
434
	 * @param int    $user_id
435
	 * @param string $value
436
	 *
437
	 * @return void
438
	 * @access public
439
	 */
440
	public function sql_update_user_stats($user_id, $value): void
441
	{
442
		$sql = 'UPDATE ' . USERS_TABLE . '
443
			SET user_ppde_donated_amount = ' . (float) $value . '
444
			WHERE user_id = ' . (int) $user_id;
445
		$this->db->sql_query($sql);
446
	}
447
448
	/**
449
	 * Prepare data array before send it to $entity
450
	 *
451
	 * @param array $data
452
	 *
453
	 * @return array
454
	 * @access public
455
	 */
456
	public function build_transaction_data_ary($data): array
457
	{
458
		return [
459
			'business'          => $data['business'],
460
			'confirmed'         => (bool) $data['confirmed'],
461
			'custom'            => $data['custom'],
462
			'exchange_rate'     => $data['exchange_rate'],
463
			'first_name'        => $data['first_name'],
464
			'item_name'         => $data['item_name'],
465
			'item_number'       => $data['item_number'],
466
			'last_name'         => $data['last_name'],
467
			'mc_currency'       => $data['mc_currency'],
468
			'mc_gross'          => (float) $data['mc_gross'],
469
			'mc_fee'            => (float) $data['mc_fee'],
470
			'net_amount'        => (float) $data['net_amount'],
471
			'parent_txn_id'     => $data['parent_txn_id'],
472
			'payer_email'       => $data['payer_email'],
473
			'payer_id'          => $data['payer_id'],
474
			'payer_status'      => $data['payer_status'],
475
			'payment_date'      => $data['payment_date'],
476
			'payment_status'    => $data['payment_status'],
477
			'payment_type'      => $data['payment_type'],
478
			'memo'              => $data['memo'],
479
			'receiver_id'       => $data['receiver_id'],
480
			'receiver_email'    => $data['receiver_email'],
481
			'residence_country' => $data['residence_country'],
482
			'settle_amount'     => (float) $data['settle_amount'],
483
			'settle_currency'   => $data['settle_currency'],
484
			'test_ipn'          => (bool) $data['test_ipn'],
485
			'txn_errors'        => $data['txn_errors'],
486
			'txn_id'            => $data['txn_id'],
487
			'txn_type'          => $data['txn_type'],
488
			'user_id'           => (int) $data['user_id'],
489
		];
490
	}
491
}
492