PluginProbe ʕ •ᴥ•ʔ
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress / trunk
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress vtrunk
4.16.18 4.16.17 4.16.16 trunk 1.0 1.0.1 1.0.2 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.5a 1.1.6 1.1.7 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.4 1.4.1 1.4.2 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.7 1.7.1 1.7.2 1.8 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.1.9 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.2 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 3.0 3.1 3.1.1 3.1.10 3.1.11 3.1.12 3.1.13 3.1.14 3.1.15 3.1.16 3.1.17 3.1.18 3.1.19 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.10 3.2.11 3.2.12 3.2.13 3.2.14 3.2.15 3.2.16 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 4.0.0 4.0.1 4.0.2 4.0.3 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.10.0 4.10.1 4.10.2 4.10.3 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.13.3 4.13.4 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.15.0 4.15.1 4.15.10 4.15.11 4.15.12 4.15.13 4.15.14 4.15.15 4.15.16 4.15.17 4.15.18 4.15.19 4.15.2 4.15.20 4.15.20.1 4.15.21 4.15.22 4.15.23 4.15.24 4.15.25 4.15.3 4.15.4 4.15.5 4.15.6 4.15.7 4.15.8 4.15.9 4.16.0 4.16.1 4.16.10 4.16.11 4.16.12 4.16.13 4.16.14 4.16.15 4.16.2 4.16.3 4.16.4 4.16.5 4.16.6 4.16.7 4.16.8 4.16.9 4.2.0 4.3.0 4.3.1 4.3.2 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.7.0 4.8.0 4.9.0
wp-user-avatar / src / Membership / DigitalProducts / DownloadService.php
wp-user-avatar / src / Membership / DigitalProducts Last commit date
DownloadHandler.php 5 months ago DownloadService.php 3 years ago Init.php 3 years ago UploadHandler.php 4 hours ago index.php 3 years ago
DownloadService.php
296 lines
1 <?php
2
3 namespace ProfilePress\Core\Membership\DigitalProducts;
4
5 use ProfilePress\Core\Base;
6 use ProfilePress\Core\Classes\PROFILEPRESS_sql;
7 use ProfilePress\Core\Membership\Models\Order\OrderFactory;
8
9 class DownloadService
10 {
11 public function get_download_file_url($order_key, $file_index, $expiry = 0)
12 {
13 if ($expiry == '' || intval($expiry) === 0) {
14 $ttl = 2147472000;
15 } else {
16 $ttl = time() + (intval($expiry) * DAY_IN_SECONDS);
17 }
18
19 $order = OrderFactory::fromOrderKey($order_key);
20
21 if ( ! $order->exists()) return false;
22
23 $args = array_fill_keys($this->get_url_token_parameters(), '');
24
25 $args['ppress_file'] = rawurlencode(sprintf('%d:%d:%d', $order->id, $order->plan_id, $file_index));
26 $args['ttl'] = rawurlencode($ttl);
27
28 $args['token'] = $this->get_download_token(add_query_arg(array_filter($args), untrailingslashit(site_url())));
29
30 return add_query_arg(array_filter($args), site_url('index.php'));
31 }
32
33 /**
34 * Generates a token for a given URL.
35 *
36 * @param string $url URL to generate a token for.
37 *
38 * @return string Token for the URL.
39 */
40 function get_download_token($url = '')
41 {
42 $secret = hash('sha256', wp_salt());
43
44 $url = add_query_arg(['secret' => $secret], $url);
45 $parts = wp_parse_url($url);
46
47 // In the event there isn't a path, set an empty one so we can MD5 the token
48 if ( ! isset($parts['path'])) $parts['path'] = '';
49
50 parse_str($parts['query'], $result);
51
52 $query_parts = [
53 'ppress_file' => ppress_var($result, 'ppress_file', ''),
54 'ttl' => ppress_var($result, 'ttl', '')
55 ];
56
57 return hash_hmac('sha256', $parts['path'] . '?' . implode('&', $query_parts), wp_salt('ppress_file_download_link'));
58 }
59
60 public function get_url_token_parameters()
61 {
62 return ['ppress_file', 'ttl', 'token'];
63 }
64
65 /**
66 * Generate a token for a URL and match it against the existing token to make
67 * sure the URL hasn't been tampered with.
68 *
69 * @param string $url URL to test.
70 *
71 * @return bool
72 */
73 function validate_url_token($url = '')
74 {
75 $parts = parse_url($url);
76 $query_args = [];
77
78 if (isset($parts['query'])) {
79
80 parse_str($parts['query'], $query_args);
81
82 // If the TTL is in the past, die out before we go any further.
83 if (isset($query_args['ttl']) && time() > $query_args['ttl']) {
84 wp_die(apply_filters('ppress_download_link_expired_text', esc_html__('Sorry but your download link has expired.', 'wp-user-avatar')), esc_html__('Error', 'wp-user-avatar'), ['response' => 403]);
85 }
86
87 // Collect the allowed tags in proper order, remove all tags, and re-add only the allowed ones.
88 $validated_query_args = [];
89
90 foreach ($this->get_url_token_parameters() as $key) {
91 if (true === array_key_exists($key, $query_args)) {
92 $validated_query_args[$key] = $query_args[$key];
93 }
94 }
95
96 // strtok allows a quick clearing of existing query string parameters, so we can re-add the allowed ones.
97 $url = add_query_arg($validated_query_args, strtok($url, '?'));
98
99 if (isset($query_args['token']) && hash_equals($query_args['token'], $this->get_download_token($url))) {
100 return true;
101 }
102 }
103
104 return false;
105 }
106
107 /**
108 * @param $args
109 *
110 * @return bool|int
111 */
112 public function add_download_log($args)
113 {
114 $args = wp_parse_args($args, [
115 'plan_id' => '',
116 'file_url' => '',
117 'order_id' => ''
118 ]);
119
120 return PROFILEPRESS_sql::add_meta_data(
121 sprintf('fdl_%s', intval($args['order_id'])),
122 [
123 'plan_id' => intval($args['plan_id']),
124 'file_url' => sanitize_text_field($args['file_url']),
125 'order_id' => intval($args['order_id']),
126 'ip' => ppress_get_ip_address(),
127 'date' => current_time('mysql', true)
128 ],
129 'ppress_download_logs'
130 );
131 }
132
133 public function get_download_file_name($plan_id, $file_url)
134 {
135 $downloads = ppress_get_plan($plan_id)->get_downloads();
136
137 if (
138 isset($downloads['files']) &&
139 ! empty($downloads['files']) &&
140 ! empty($downloads['files'][$file_url])
141 ) {
142 return $downloads['files'][$file_url];
143 }
144
145 return false;
146 }
147
148 public function get_download_log_count($order_id = 0)
149 {
150 global $wpdb;
151
152 $table = Base::meta_data_db_table();
153
154 $replacement = ['ppress_download_logs'];
155 $sql = "SELECT COUNT(*) FROM $table WHERE flag = %s";
156 if ($order_id > 0) {
157 $sql .= " AND meta_key = %s";
158 $replacement[] = sprintf('fdl_%d', $order_id);
159 }
160
161 return $wpdb->get_var($wpdb->prepare($sql, $replacement));
162 }
163
164 public function get_download_log($limit = 0, $current_page = 1, $order_id = 0)
165 {
166 global $wpdb;
167
168 $table = Base::meta_data_db_table();
169
170 $order_id = (int)$order_id;
171
172 $replacement = ['ppress_download_logs'];
173 $sql = "SELECT * FROM $table WHERE flag = %s";
174
175 if ($order_id > 0) {
176 $sql .= " AND meta_key = %s";
177 $replacement[] = sprintf('fdl_%d', $order_id);
178 }
179
180 $sql .= " ORDER BY id DESC";
181
182 if ($limit > 0) {
183 $sql .= " LIMIT %d";
184 $replacement[] = $limit;
185 }
186
187 if ($current_page > 1) {
188 $sql .= " OFFSET %d";
189 $replacement[] = ($current_page - 1) * $limit;
190 }
191
192 $result = $wpdb->get_results($wpdb->prepare($sql, $replacement), 'ARRAY_A');
193
194 if (empty($result)) return false;
195
196 $output = [];
197 foreach ($result as $meta) {
198 $output[$meta['id']] = ['id' => $meta['id']] + unserialize($meta['meta_value'], ['allowed_classes' => false]);
199 }
200
201 return $output;
202 }
203
204 /**
205 * @param $order_id
206 * @param $plan_id
207 * @param $file_url
208 *
209 * @return false|int
210 */
211 public function get_downloads_count($order_id, $plan_id, $file_url)
212 {
213 $logs = $this->get_download_log(0, 1, $order_id);
214
215 if (is_array($logs) && ! empty($logs)) {
216
217 $download_count = count(wp_list_filter($logs, [
218 'file_url' => $file_url,
219 'order_id' => intval($order_id),
220 'plan_id' => intval($plan_id)
221 ])
222 );
223
224 return $download_count;
225 }
226
227 return false;
228 }
229
230 /**
231 * Checks if a file is at its download limit
232 *
233 * This limit refers to the maximum number of times files connected to a plan can be downloaded.
234 *
235 * @param int $plan_id
236 * @param int $order_id Order ID.
237 * @param string $file_url
238 *
239 * @return bool
240 *
241 */
242 public function is_file_at_download_limit($plan_id, $order_id, $file_url)
243 {
244 $plan_downloads = ppress_get_plan($plan_id)->get_downloads();
245
246 $download_limit = $plan_downloads['download_limit'];
247
248 if ( ! empty($file_url) && absint($download_limit) > 0) {
249
250 $download_count = $this->get_downloads_count($order_id, $plan_id, $file_url);
251
252 if (false !== $download_count && $download_count >= $download_limit) {
253 return true;
254 }
255 }
256
257 return false;
258 }
259
260 /**
261 * @param $order_id
262 * @param $plan_id
263 * @param $file_url
264 *
265 * @return mixed|string
266 */
267 public function get_downloads_remaining($order_id, $plan_id, $file_url)
268 {
269 $plan_downloads = ppress_get_plan($plan_id)->get_downloads();
270
271 $download_limit = $plan_downloads['download_limit'];
272
273 if ( ! empty($file_url) && absint($download_limit) > 0) {
274 $download_count = $this->get_downloads_count($order_id, $plan_id, $file_url);
275
276 // max ensures lowest result is 0 and no negative integer.
277 return max($download_limit - $download_count, 0);
278 }
279
280 return '&infin;';
281 }
282
283 /**
284 * @return self
285 */
286 public static function init()
287 {
288 static $instance = null;
289
290 if (is_null($instance)) {
291 $instance = new self();
292 }
293
294 return $instance;
295 }
296 }