Passed
Branch develop-3.3.x-donors_module (d1eba4)
by Mario
03:56
created

transactions::query_sql_count()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 2
eloc 9
c 3
b 0
f 0
nc 2
nop 2
dl 0
loc 16
rs 9.9666
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)
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
		];
57
58
		// Use WHERE clause when $currency_id is different from 0
59
		if ((int) $transaction_id)
60
		{
61
			$sql_ary['WHERE'] = 'txn.transaction_id = ' . (int) $transaction_id;
62
		}
63
64
		// Return all transactions entities
65
		return $this->db->sql_build_query('SELECT', $sql_ary);
66
	}
67
68
	/**
69
	 * SQL Query to count donors, grouped by username and currency
70
	 *
71
	 * @param bool $group_by_currency
72
	 *
73
	 * @return array
74
	 * @access public
75
	 */
76
	public function sql_count_donors($group_by_currency = true)
77
	{
78
		$sql_count_donors = [
79
			'SELECT'   => 'txn.user_id, txn.mc_currency',
80
			'FROM'     => [$this->ppde_transactions_log_table => 'txn'],
81
			'WHERE'    => 'txn.user_id <> ' . ANONYMOUS . "
82
							AND txn.payment_status = 'Completed'
83
							AND txn.test_ipn = 0",
84
			'GROUP_BY' => 'txn.user_id, txn.mc_currency',
85
		];
86
87
		$sql_count_donors = $this->sql_group_by_currency($sql_count_donors, $group_by_currency);
88
89
		return $sql_count_donors;
90
	}
91
92
	/**
93
	 * SQL Query to list donors
94
	 *
95
	 * @param string $order_by
96
	 * @param bool   $group_by_currency Add a "group by" statement on column "mc_currency"
97
	 *
98
	 * @return array
99
	 * @access public
100
	 */
101
	public function sql_donors_list($order_by = '', $group_by_currency = true)
102
	{
103
		// Build sql request
104
		$sql_donorslist = [
105
			'SELECT'    => 'txn.user_id, MAX(txn.transaction_id) AS max_txn_id, SUM(txn.mc_gross) AS amount, MAX(u.username)',
106
			'FROM'      => [$this->ppde_transactions_log_table => 'txn'],
107
			'LEFT_JOIN' => [
108
				[
109
					'FROM' => [USERS_TABLE => 'u'],
110
					'ON'   => 'u.user_id = txn.user_id',
111
				]],
112
			'WHERE'     => 'txn.user_id <> ' . ANONYMOUS . "
113
							AND txn.payment_status = 'Completed'
114
							AND txn.test_ipn = 0",
115
			'GROUP_BY'  => 'txn.user_id',
116
		];
117
118
		$sql_donorslist = $this->sql_group_by_currency($sql_donorslist, $group_by_currency);
119
120
		if ($order_by)
121
		{
122
			$sql_donorslist['ORDER_BY'] = $order_by;
123
		}
124
125
		return $sql_donorslist;
126
	}
127
128
	/**
129
	 * @param array $sql
130
	 * @param bool  $grouped
131
	 *
132
	 * @return array
133
	 * @access private
134
	 */
135
	private function sql_group_by_currency(array $sql, bool $grouped)
136
	{
137
		if ($grouped)
138
		{
139
			$sql['SELECT'] .= ', txn.mc_currency';
140
			$sql['GROUP_BY'] .= ', txn.mc_currency';
141
		}
142
		else
143
		{
144
			$sql['SELECT'] .= ', MAX(txn.mc_currency) AS mc_currency';
145
		}
146
147
		return $sql;
148
	}
149
150
	/**
151
	 * SQL Query to return information of the last donation of the donor
152
	 *
153
	 * @param int $transaction_id
154
	 *
155
	 * @return array
156
	 * @access public
157
	 */
158
	public function sql_last_donation_ary($transaction_id)
159
	{
160
		// Build sql request
161
		return [
162
			'SELECT' => 'txn.payment_date, txn.mc_gross, txn.mc_currency',
163
			'FROM'   => [$this->ppde_transactions_log_table => 'txn'],
164
			'WHERE'  => 'txn.transaction_id = ' . (int) $transaction_id,
165
		];
166
	}
167
168
	/**
169
	 * Build SQL Query to return the donors list
170
	 *
171
	 * @param array $sql_donorlist_ary
172
	 *
173
	 * @return string
174
	 * @access public
175
	 */
176
	public function build_sql_donorlist_data($sql_donorlist_ary)
177
	{
178
		// Return all transactions entities
179
		return $this->db->sql_build_query('SELECT', $sql_donorlist_ary);
180
	}
181
182
	/**
183
	 * Returns total entries of selected field
184
	 *
185
	 * @param array  $count_sql_ary
186
	 * @param string $selected_field
187
	 *
188
	 * @return int
189
	 * @access public
190
	 */
191
	public function query_sql_count($count_sql_ary, $selected_field)
192
	{
193
		$count_sql_ary['SELECT'] = 'COUNT(' . $selected_field . ') AS total_entries';
194
195
		if (array_key_exists('GROUP_BY', $count_sql_ary))
196
		{
197
			$count_sql_ary['SELECT'] = 'COUNT(DISTINCT ' . $count_sql_ary['GROUP_BY'] . ') AS total_entries';
198
		}
199
		unset($count_sql_ary['ORDER_BY'], $count_sql_ary['GROUP_BY']);
200
201
		$sql = $this->db->sql_build_query('SELECT', $count_sql_ary);
202
		$result = $this->db->sql_query($sql);
203
		$field = (int) $this->db->sql_fetchfield('total_entries');
204
		$this->db->sql_freeresult($result);
205
206
		return $field;
207
	}
208
209
	/**
210
	 * Returns the SQL Query for displaying simple transactions details
211
	 *
212
	 * @param string $keywords
213
	 * @param string $sort_by
214
	 * @param int    $log_time
215
	 *
216
	 * @return array
217
	 * @access public
218
	 */
219
	public function get_logs_sql_ary($keywords, $sort_by, $log_time)
220
	{
221
		$sql_keywords = '';
222
		if (!empty($keywords))
223
		{
224
			// Get the SQL condition for our keywords
225
			$sql_keywords = $this->generate_sql_keyword($keywords);
226
		}
227
228
		$get_logs_sql_ary = [
229
			'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',
230
			'FROM'     => [
231
				$this->ppde_transactions_log_table => 'txn',
232
				USERS_TABLE                        => 'u',
233
			],
234
			'WHERE'    => 'txn.user_id = u.user_id ' . $sql_keywords,
235
			'ORDER_BY' => $sort_by,
236
		];
237
238
		if ($log_time)
239
		{
240
			$get_logs_sql_ary['WHERE'] = 'txn.payment_date >= ' . (int) $log_time . '
241
					AND ' . $get_logs_sql_ary['WHERE'];
242
		}
243
244
		return $get_logs_sql_ary;
245
	}
246
247
	/**
248
	 * Generates a sql condition for the specified keywords
249
	 *
250
	 * @param string $keywords           The keywords the user specified to search for
251
	 * @param string $statement_operator The operator used to prefix the statement ('AND' by default)
252
	 *
253
	 * @return string Returns the SQL condition searching for the keywords
254
	 * @access private
255
	 */
256
	private function generate_sql_keyword($keywords, $statement_operator = 'AND')
257
	{
258
		// Use no preg_quote for $keywords because this would lead to sole
259
		// backslashes being added. We also use an OR connection here for
260
		// spaces and the | string. Currently, regex is not supported for
261
		// searching (but may come later).
262
		$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

262
		$keywords = preg_split('#[\s|]+#u', /** @scrutinizer ignore-call */ utf8_strtolower($keywords), 0, PREG_SPLIT_NO_EMPTY);
Loading history...
263
		$sql_keywords = '';
264
265
		if (!empty($keywords))
266
		{
267
			// Build pattern and keywords...
268
			for ($i = 0, $num_keywords = count($keywords); $i < $num_keywords; $i++)
269
			{
270
				$keywords[$i] = $this->db->sql_like_expression($this->db->get_any_char() . $keywords[$i] . $this->db->get_any_char());
271
			}
272
273
			$sql_keywords = ' ' . $statement_operator . ' (';
274
			$columns = ['txn.txn_id', 'u.username'];
275
			$sql_lowers = array();
276
277
			foreach ($columns as $column_name)
278
			{
279
				$sql_lower = $this->db->sql_lower_text($column_name);
280
				$sql_lowers[] = $sql_lower . ' ' . implode(' OR ' . $sql_lower . ' ', $keywords);
281
			}
282
			unset($columns, $column_name);
283
284
			$sql_keywords .= implode(' OR ', $sql_lowers) . ')';
285
		}
286
287
		return $sql_keywords;
288
	}
289
290
	/**
291
	 * Returns user information based on the donor ID or email
292
	 *
293
	 * @param string     $type
294
	 * @param int|string $arg
295
	 *
296
	 * @return array|bool
297
	 * @access public
298
	 */
299
	public function query_donor_user_data($type = 'user', $arg = 1)
300
	{
301
302
		switch ($type)
303
		{
304
			case 'user':
305
				$sql_where = ' WHERE user_id = ' . (int) $arg;
306
			break;
307
			case 'username':
308
				$sql_where = " WHERE username_clean = '" . $this->db->sql_escape(utf8_clean_string($arg)) . "'";
309
			break;
310
			case 'email':
311
				$sql_where = " WHERE user_email_hash = '" . $this->db->sql_escape(phpbb_email_hash($arg)) . "'";
0 ignored issues
show
Deprecated Code introduced by
The function phpbb_email_hash() has been deprecated: 3.3.0-b2 (To be removed: 4.0.0) ( Ignorable by Annotation )

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

311
				$sql_where = " WHERE user_email_hash = '" . $this->db->sql_escape(/** @scrutinizer ignore-deprecated */ phpbb_email_hash($arg)) . "'";

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
312
			break;
313
			default:
314
				$sql_where = '';
315
		}
316
317
		$sql = 'SELECT user_id, username, user_ppde_donated_amount
318
			FROM ' . USERS_TABLE .
319
			$sql_where;
320
		$result = $this->db->sql_query($sql);
321
		$row = $this->db->sql_fetchrow($result);
322
		$this->db->sql_freeresult($result);
323
324
		return $row;
325
	}
326
327
	/**
328
	 * Returns simple details of all PayPal transactions logged in the database
329
	 *
330
	 * @param array $get_logs_sql_ary
331
	 * @param array $url_ary
332
	 * @param int   $limit
333
	 * @param int   $last_page_offset
334
	 *
335
	 * @return array $log
336
	 * @access public
337
	 */
338
	public function build_log_ary($get_logs_sql_ary, $url_ary, $limit = 0, $last_page_offset = 0)
339
	{
340
		$sql = $this->db->sql_build_query('SELECT', $get_logs_sql_ary);
341
		$result = $this->db->sql_query_limit($sql, $limit, $last_page_offset);
342
343
		$log = [];
344
345
		while ($row = $this->db->sql_fetchrow($result))
346
		{
347
			$log[] = [
348
				'confirmed'      => $row['confirmed'],
349
				'payment_date'   => $row['payment_date'],
350
				'payment_status' => $row['payment_status'],
351
				'test_ipn'       => $row['test_ipn'],
352
				'transaction_id' => $row['transaction_id'],
353
				'txn_errors'     => $row['txn_errors'],
354
				'txn_id'         => $this->build_transaction_url($row['transaction_id'], $row['txn_id'], $url_ary['txn_url'], $row['confirmed']),
355
				'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

355
				'username_full'  => get_username_string('full', $row['user_id'], $row['username'], $row['user_colour'], /** @scrutinizer ignore-type */ false, $url_ary['profile_url']),
Loading history...
356
			];
357
		}
358
359
		$this->db->sql_freeresult($result);
360
361
		return $log;
362
	}
363
364
	/**
365
	 * Build transaction url for placing into templates.
366
	 *
367
	 * @param int    $id         The users transaction id
368
	 * @param string $txn_id     The txn number id
369
	 * @param string $custom_url optional parameter to specify a profile url. The transaction id get appended to this
370
	 *                           url as &amp;id={id}
371
	 * @param bool   $colour     If false the color #FF0000 will be applied on the URL.
372
	 *
373
	 * @return string A string consisting of what is wanted.
374
	 * @access private
375
	 */
376
	private function build_transaction_url($id, $txn_id, $custom_url = '', $colour = false)
377
	{
378
		static $_profile_cache;
379
380
		// We cache some common variables we need within this function
381
		if (empty($_profile_cache))
382
		{
383
			$_profile_cache['tpl_nourl'] = '{{ TRANSACTION }}';
384
			$_profile_cache['tpl_url'] = '<a href="{{ TXN_URL }}">{{ TRANSACTION }}</a>';
385
			$_profile_cache['tpl_url_colour'] = '<a href="{{ TXN_URL }}" style="{{ TXN_COLOUR }}">{{ TRANSACTION }}</a>';
386
		}
387
388
		// Build correct transaction url
389
		$txn_url = '';
390
		if ($txn_id)
391
		{
392
			$txn_url = ($custom_url !== '') ? $custom_url . '&amp;action=view&amp;id=' . $id : $txn_id;
393
		}
394
395
		// Return
396
397
		if (!$txn_url)
398
		{
399
			return str_replace('{{ TRANSACTION }}', $txn_id, $_profile_cache['tpl_nourl']);
400
		}
401
402
		return str_replace(['{{ TXN_URL }}', '{{ TXN_COLOUR }}', '{{ TRANSACTION }}'], [$txn_url, 'color: #FF0000;', $txn_id], (!$colour) ? $_profile_cache['tpl_url_colour'] : $_profile_cache['tpl_url']);
403
	}
404
405
	/**
406
	 * Returns SQL WHERE clause for all marked items
407
	 *
408
	 * @param $marked
409
	 *
410
	 * @return string
411
	 * @access public
412
	 */
413
	public function build_marked_where_sql($marked)
414
	{
415
		$sql_in = [];
416
		foreach ($marked as $mark)
417
		{
418
			$sql_in[] = $mark;
419
		}
420
421
		return ' WHERE ' . $this->db->sql_in_set('transaction_id', $sql_in);
422
	}
423
424
	/**
425
	 * Returns the count result for updating stats
426
	 *
427
	 * @param string $type
428
	 * @param bool   $test_ipn
429
	 *
430
	 * @return int
431
	 * @access public
432
	 */
433
	public function sql_query_count_result($type, $test_ipn)
434
	{
435
		switch ($type)
436
		{
437
			case 'ppde_transactions_count':
438
			case 'ppde_transactions_count_ipn':
439
				$sql_ary = $this->sql_select_stats_main('txn_id');
440
				$sql_ary['WHERE'] = "confirmed = 1 AND payment_status = 'Completed' AND txn.test_ipn = " . (int) $test_ipn;
441
			break;
442
			case 'ppde_known_donors_count':
443
			case 'ppde_known_donors_count_ipn':
444
				$sql_ary = $this->sql_select_stats_main('payer_id');
445
				$sql_ary['LEFT_JOIN'] = [
446
					[
447
						'FROM' => [USERS_TABLE => 'u'],
448
						'ON'   => 'txn.user_id = u.user_id',
449
					],
450
				];
451
				$sql_ary['WHERE'] = '(u.user_type = ' . USER_NORMAL . ' OR u.user_type = ' . USER_FOUNDER . ') AND txn.test_ipn = ' . (int) $test_ipn;
452
			break;
453
			case 'ppde_anonymous_donors_count':
454
			case 'ppde_anonymous_donors_count_ipn':
455
				$sql_ary = $this->sql_select_stats_main('payer_id');
456
				$sql_ary['WHERE'] = 'txn.user_id = ' . ANONYMOUS . ' AND txn.test_ipn = ' . (int) $test_ipn;
457
			break;
458
			default:
459
				$sql_ary = $this->sql_select_stats_main('txn_id');
460
		}
461
462
		$result = $this->db->sql_query($this->db->sql_build_query('SELECT', $sql_ary));
463
		$count = (int) $this->db->sql_fetchfield('count_result');
464
		$this->db->sql_freeresult($result);
465
466
		return $count;
467
	}
468
469
	/**
470
	 * Make body of SQL query for stats calculation.
471
	 *
472
	 * @param string $field_name Name of the field
473
	 *
474
	 * @return array
475
	 * @access private
476
	 */
477
	private function sql_select_stats_main($field_name)
478
	{
479
		return [
480
			'SELECT' => 'COUNT(DISTINCT txn.' . $field_name . ') AS count_result',
481
			'FROM'   => [$this->ppde_transactions_log_table => 'txn'],
482
		];
483
	}
484
485
	/**
486
	 * Updates the user donated amount
487
	 *
488
	 * @param int    $user_id
489
	 * @param string $value
490
	 *
491
	 * @return void
492
	 * @access public
493
	 */
494
	public function sql_update_user_stats($user_id, $value)
495
	{
496
		$sql = 'UPDATE ' . USERS_TABLE . '
497
			SET user_ppde_donated_amount = ' . (float) $value . '
498
			WHERE user_id = ' . (int) $user_id;
499
		$this->db->sql_query($sql);
500
	}
501
502
	/**
503
	 * Prepare data array before send it to $entity
504
	 *
505
	 * @param array $data
506
	 *
507
	 * @return array
508
	 * @access public
509
	 */
510
	public function build_data_ary($data)
511
	{
512
		return [
513
			'business'          => $data['business'],
514
			'confirmed'         => (bool) $data['confirmed'],
515
			'exchange_rate'     => $data['exchange_rate'],
516
			'first_name'        => $data['first_name'],
517
			'item_name'         => $data['item_name'],
518
			'item_number'       => $data['item_number'],
519
			'last_name'         => $data['last_name'],
520
			'mc_currency'       => $data['mc_currency'],
521
			'mc_gross'          => floatval($data['mc_gross']),
522
			'mc_fee'            => floatval($data['mc_fee']),
523
			'net_amount'        => floatval($data['net_amount']),
524
			'parent_txn_id'     => $data['parent_txn_id'],
525
			'payer_email'       => $data['payer_email'],
526
			'payer_id'          => $data['payer_id'],
527
			'payer_status'      => $data['payer_status'],
528
			'payment_date'      => $data['payment_date'],
529
			'payment_status'    => $data['payment_status'],
530
			'payment_type'      => $data['payment_type'],
531
			'memo'              => $data['memo'],
532
			'receiver_id'       => $data['receiver_id'],
533
			'receiver_email'    => $data['receiver_email'],
534
			'residence_country' => $data['residence_country'],
535
			'settle_amount'     => floatval($data['settle_amount']),
536
			'settle_currency'   => $data['settle_currency'],
537
			'test_ipn'          => (bool) $data['test_ipn'],
538
			'txn_errors'        => $data['txn_errors'],
539
			'txn_id'            => $data['txn_id'],
540
			'txn_type'          => $data['txn_type'],
541
			'user_id'           => (int) $data['user_id'],
542
		];
543
	}
544
}
545