PluginProbe ʕ •ᴥ•ʔ
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments / 4.5.0
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments v4.5.0
4.5.1 4.5.0 4.4.2 4.4.1 4.4.0 4.3.3 4.3.2 4.3.1 4.3.0 4.2.3 4.2.2 4.2.1 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.11.1 1.11.2 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.5.0 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.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.9.0 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 2.0.0 2.0.1 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.10.0 2.10.1 2.11.0 2.11.1 2.11.2 2.11.3 2.11.4 2.12.0 2.13.0 2.14.0 2.14.1 2.15.0 2.15.1 2.16.0 2.16.1 2.16.2 2.16.3 2.17.0 2.17.1 2.17.2 2.18.0 2.19.0 2.19.2 2.19.3 2.19.4 2.2.0 2.2.1 2.20.0 2.20.1 2.20.2 2.20.3 2.20.4 2.20.5 2.20.6 2.21.0 2.22.0 2.22.1 2.23.0 2.24.0 2.25.0 2.25.1 2.25.2 2.26.0 2.27.0 2.27.1 2.28.0 2.29.0 2.29.1 2.29.2 2.29.3 2.29.4 2.3.0 2.3.1 2.30.0 2.31.0 2.31.1 2.31.2 2.31.3 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.40.0 2.40.1 2.5.0 2.5.1 2.5.2 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.9.0 3.0.0 3.0.0-RC1 3.0.0-RC2 3.0.0-beta1 3.0.0-beta2 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.10.0 3.10.1 3.11.0 3.12.0 3.13.0 3.13.1 3.13.2 3.13.3 3.13.4 3.14.0 3.15.0 3.15.1 3.15.2 3.15.3 3.15.4 3.15.5 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.16.6 3.16.7 3.16.8 3.17.0 3.17.1 3.17.2 3.17.3 3.17.4 3.17.5 3.17.6 3.18.0 3.19.0 3.19.1 3.19.2 3.2.0 3.2.1 3.2.2 3.20.0 3.20.1 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 4.0.0 4.0.1 4.0.2 4.0.3 trunk 4.1.0 0.2.19.1 4.1.1 1.0.0 4.2.0 1.0.1 1.0.2
surecart / app / src / Integrations / TutorLMS / TutorLMSService.php
surecart / app / src / Integrations / TutorLMS Last commit date
TutorLMSService.php 2 months ago TutorLMSServiceProvider.php 3 years ago add-to-cart-surecart.php 1 year ago
TutorLMSService.php
420 lines
1 <?php
2
3 namespace SureCart\Integrations\TutorLMS;
4
5 use SureCart\Integrations\Contracts\IntegrationInterface;
6 use SureCart\Integrations\Contracts\PurchaseSyncInterface;
7 use SureCart\Integrations\IntegrationService;
8 use SureCart\Models\Integration;
9 use SureCart\Models\Price;
10 use SureCart\Models\Product;
11 use SureCart\Support\Currency;
12
13 /**
14 * Controls the LearnDash integration.
15 */
16 class TutorLMSService extends IntegrationService implements IntegrationInterface, PurchaseSyncInterface {
17
18 public function bootstrap() {
19 parent::bootstrap();
20
21 add_filter( 'tutor/course/single/entry-box/free', [ $this, 'purchaseButton' ], 10, 2 );
22 add_filter( 'tutor/course/single/entry-box/purchasable', [ $this, 'purchaseButton' ], 10, 2 );
23 add_filter( 'get_tutor_course_price', [ $this, 'coursePrice' ], 11, 2 );
24 add_filter( 'tutor_course_loop_price', [ $this, 'loopPurchaseButton' ], 10, 2 );
25
26 add_action( 'surecart/models/price/updated', [ $this, 'clearPriceCache' ], 10, 2 );
27 add_action( 'surecart/models/price/created', [ $this, 'clearPriceCache' ], 10, 2 );
28 }
29
30 /**
31 * Show our purchase button if we have an integration.
32 *
33 * @param string $output The button HTML.
34 * @param int $course_id The course id.
35 *
36 * @return string
37 */
38 public function loopPurchaseButton( $output, $course_id ) {
39 // check first to see if we have any integrations.
40 $integrations = Integration::where( 'integration_id', get_the_ID() )->andWhere( 'model_name', 'product' )->get();
41 if ( empty( $integrations ) ) {
42 return $output;
43 }
44
45 // Get the model ids from the integrations.
46 $product_ids = array_column( $integrations, 'model_id' );
47 if ( empty( $product_ids ) ) {
48 return $output;
49 }
50
51 // get purchasable prices from cache.
52 $prices = $this->getCachedProductsPrices( $product_ids );
53 if ( empty( $prices ) ) {
54 return $output;
55 }
56
57 // add our components.
58 \SureCart::assets()->enqueueComponents();
59
60 $is_logged_in = is_user_logged_in();
61 $enable_guest_course_cart = tutor_utils()->get_option( 'enable_guest_course_cart' );
62 $required_loggedin_class = '';
63 if ( ! $is_logged_in && ! $enable_guest_course_cart ) {
64 $required_loggedin_class = apply_filters( 'tutor_enroll_required_login_class', 'tutor-open-login-modal' );
65 }
66
67 // template.
68 ob_start(); ?>
69
70 <div class="tutor-course-list-btn">
71 <?php
72 echo apply_filters( 'tutor_course_restrict_new_entry', '<a href="' . get_the_permalink() . '" class="tutor-btn tutor-btn-outline-primary tutor-btn-md tutor-btn-block ' . $required_loggedin_class . '">' . esc_html__( 'Enroll Course', 'surecart' ) . '</a>', $course_id ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
73 ?>
74 </div>
75 <?php
76 return ob_get_clean();
77 }
78
79 /**
80 * Clear the price cache.
81 *
82 * @param \SureCart\Models\Price $price The price model.
83 *
84 * @return void
85 */
86 public function clearPriceCache( $price ) {
87 if ( empty( $price->product ) ) {
88 return;
89 }
90
91 // get the product id.
92 $product_id = is_a( $price->product, Product::class ) ? $price->product->id : $price->product;
93
94 delete_transient( 'surecart_tutor_lms_product_' . $product_id );
95 }
96
97 /**
98 * Get cached product prices.
99 *
100 * @param array $product_ids The product ids.
101 *
102 * @return array
103 */
104 public function getCachedProductsPrices( $product_ids = [] ) {
105 $prices = [];
106 foreach ( $product_ids as $product_id ) {
107 $prices = array_merge( $prices, $this->getCachedProductPrices( $product_id ) );
108 }
109 return $prices;
110 }
111
112 /**
113 * Get the cached prices.
114 *
115 * @param string $product_id The product id.
116 *
117 * @return array
118 */
119 public function getCachedProductPrices( $product_id ) {
120 // cache key.
121 $cache_key = 'surecart_tutor_lms_product_' . $product_id;
122
123 // get the transient.
124 $prices = get_transient( $cache_key );
125
126 // if we do not have a transient.
127 if ( false === $prices ) {
128 // get purchasable prices for product.
129 $prices = Price::where(
130 [
131 'product_ids' => [ $product_id ],
132 'archived' => false,
133 ]
134 )->get();
135
136 // Don't cache or return WP_Error — allow retry on next request.
137 if ( is_wp_error( $prices ) ) {
138 return [];
139 }
140
141 // store in transient.
142 set_transient( $cache_key, $prices, apply_filters( 'surecart_tutor_lms_product_cache_time', DAY_IN_SECONDS, $this ) );
143 }
144
145 return $prices;
146 }
147
148 /**
149 * The course price.
150 *
151 * @param string $price The price string.
152 * @param integer $course_id The course id.
153 *
154 * @return string
155 */
156 public function coursePrice( $price, $course_id ) {
157 $integrations = Integration::where( 'integration_id', $course_id )->andWhere( 'model_name', 'product' )->get();
158
159 // we have no integrations.
160 if ( empty( $integrations ) ) {
161 return $price;
162 }
163
164 if ( empty( $integrations[0]->model_id ) ) {
165 return $price;
166 }
167
168 // get the first product.
169 $prices = $this->getCachedProductPrices( $integrations[0]->model_id );
170
171 // there is no price.
172 if ( empty( $prices ) ) {
173 return esc_html__( 'No price', 'surecart' );
174 }
175
176 $price_array = [];
177 foreach ( $prices as $price ) {
178 if ( $price->ad_hoc ) {
179 $price_array[] = esc_html__( 'Custom amount', 'surecart' );
180 } else {
181 $price_array[] = Currency::format( $price->amount, $price->currency ?? 'usd' );
182 }
183 }
184
185 // no price.
186 return implode( ', ', $price_array );
187 }
188
189 /**
190 * Show our purchase button if we have an integration.
191 *
192 * @param string $output The button HTML.
193 * @param integer $id The course id.
194 *
195 * @return string
196 */
197 public function purchaseButton( $output, $id ) {
198 // check first to see if we have any integrations.
199 $integrations = Integration::where( 'integration_id', $id )->andWhere( 'model_name', 'product' )->get();
200 if ( empty( $integrations ) ) {
201 return $output;
202 }
203
204 // Get the model ids from the integrations.
205 $product_ids = array_column( $integrations, 'model_id' );
206 if ( empty( $product_ids ) ) {
207 return $output;
208 }
209
210 // get purchasable prices from cache.
211 $prices = $this->getCachedProductsPrices( $product_ids );
212 if ( empty( $prices ) ) {
213 return $output;
214 }
215
216 // add our components.
217 \SureCart::assets()->enqueueComponents();
218
219 // template.
220 ob_start();
221
222 include 'add-to-cart-surecart.php';
223
224 return ob_get_clean();
225 }
226
227 /**
228 * Get the slug for the integration.
229 *
230 * @return string
231 */
232 public function getName() {
233 return 'surecart/tutor-course';
234 }
235
236 /**
237 * Get the model for the integration.
238 *
239 * @return string
240 */
241 public function getModel() {
242 return 'product';
243 }
244
245 /**
246 * Get the slug for the integration.
247 *
248 * @return string
249 */
250 public function getLogo() {
251 return esc_url_raw( trailingslashit( plugin_dir_url( SURECART_PLUGIN_FILE ) ) . 'images/integrations/tutorlms.svg' );
252 }
253
254 /**
255 * Get the slug for the integration.
256 *
257 * @return string
258 */
259 public function getLabel() {
260 return __( 'TutorLMS Course', 'surecart' );
261 }
262
263 /**
264 * Get the slug for the integration.
265 *
266 * @return string
267 */
268 public function getItemLabel() {
269 return __( 'Course Access', 'surecart' );
270 }
271
272 /**
273 * Get the slug for the integration.
274 *
275 * @return string
276 */
277 public function getItemHelp() {
278 return __( 'Enable access to a TutorLMS course.', 'surecart' );
279 }
280
281 /**
282 * Is this enabled?
283 *
284 * @return boolean
285 */
286 public function enabled() {
287 return defined( 'TUTOR_VERSION' );
288 }
289
290 /**
291 * Get item listing for the integration.
292 *
293 * @param array $items The integration items.
294 * @param string $search The search term.
295 *
296 * @return array The items for the integration.
297 */
298 public function getItems( $items = [], $search = '' ) {
299 if ( ! function_exists( 'tutor' ) ) {
300 return $items;
301 }
302
303 wp_reset_query();
304 $course_query = new \WP_Query(
305 [
306 'post_type' => tutor()->course_post_type,
307 'post_status' => 'publish',
308 's' => $search,
309 'posts_per_page' => 100,
310 ]
311 );
312
313 if ( ( isset( $course_query->posts ) ) && ( ! empty( $course_query->posts ) ) ) {
314 $items = array_map(
315 function ( $post ) {
316 return (object) [
317 'id' => $post->ID,
318 'label' => $post->post_title,
319 ];
320 },
321 $course_query->posts
322 );
323 }
324
325 return $items;
326 }
327
328 /**
329 * Get the individual item.
330 *
331 * @param string $id Id for the record.
332 *
333 * @return object The item for the integration.
334 */
335 public function getItem( $id ) {
336 $course = get_post( $id );
337 if ( ! $course ) {
338 return [];
339 }
340 return (object) [
341 'id' => $id,
342 'provider_label' => __( 'TutorLMS Course', 'surecart' ),
343 'label' => $course->post_title,
344 ];
345 }
346
347 /**
348 * Enable Access to the course.
349 *
350 * @param \SureCart\Models\Integration $integration The integrations.
351 * @param \WP_User $wp_user The user.
352 *
353 * @return boolean|void Returns true if the user course access updation was successful otherwise false.
354 */
355 public function onPurchaseCreated( $integration, $wp_user ) {
356 $this->updateAccess( $integration->integration_id, $wp_user, true );
357 }
358
359 /**
360 * Enable access when purchase is invoked
361 *
362 * @param \SureCart\Models\Integration $integration The integrations.
363 * @param \WP_User $wp_user The user.
364 *
365 * @return boolean|void Returns true if the user course access updation was successful otherwise false.
366 */
367 public function onPurchaseInvoked( $integration, $wp_user ) {
368 $this->onPurchaseCreated( $integration, $wp_user );
369 }
370
371 /**
372 * Remove a user role.
373 *
374 * @param \SureCart\Models\Integration $integration The integrations.
375 * @param \WP_User $wp_user The user.
376 *
377 * @return boolean|void Returns true if the user course access updation was successful otherwise false.
378 */
379 public function onPurchaseRevoked( $integration, $wp_user ) {
380 $this->updateAccess( $integration->integration_id, $wp_user, false );
381 }
382
383 /**
384 * Update access to a course.
385 *
386 * @param integer $course_id The course id.
387 * @param \WP_User $wp_user The user.
388 * @param boolean $add True to add the user to the course, false to remove.
389 *
390 * @return boolean|void Returns true if the user course access updation was successful otherwise false.
391 */
392 public function updateAccess( $course_id, $wp_user, $add = true ) {
393 // we don't have learndash installed.
394 if ( ! function_exists( 'tutor_utils' ) ) {
395 return;
396 }
397
398 if ( ! $add ) {
399 tutor_utils()->cancel_course_enrol( $course_id, $wp_user->ID );
400 return;
401 }
402
403 $enroll_id = tutor_utils()->do_enroll( $course_id, 0, $wp_user->ID );
404
405 // TutorLMS sets enrollment to 'pending' for purchasable courses.
406 // Since SureCart has already processed the payment, we mark it as 'completed' directly.
407 if ( $enroll_id ) {
408 wp_update_post(
409 [
410 'ID' => $enroll_id,
411 'post_status' => 'completed',
412 ]
413 );
414
415 // Fire the enrollment complete action for consistency with TutorLMS hooks.
416 do_action( 'tutor_after_enrolled', $course_id, $wp_user->ID, $enroll_id );
417 }
418 }
419 }
420