PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.8
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.8
4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 All 139 releases
← All changes | inc/TemplateHooks/Order/AdminOrderItemsTemplate.php +460 -460 4.3.84.4.8 View file →
@@ -1,460 +1,460 @@
1 -<?php
2 -namespace LearnPress\TemplateHooks\Order;
3 -
4 -use Exception;
5 -use LearnPress\Databases\Order\LPOrderItemsDB;
6 -use LearnPress\Filters\Order\OrderItemsFilter;
7 -use LearnPress\Helpers\Singleton;
8 -use LearnPress\Helpers\Template;
9 -use LearnPress\Models\CoursePostModel;
10 -use LearnPress\TemplateHooks\TemplateAJAX;
11 -use stdClass;
12 -use LP_Order;
13 -use Throwable;
14 -
15 -/**
16 - * class AdminOrderItemsTemplate
17 - *
18 - * @since 4.3.2
19 - * @version 1.0.0
20 - */
21 -class AdminOrderItemsTemplate {
22 - use Singleton;
23 -
24 - public function init() {
25 - add_filter( 'lp/rest/ajax/allow_callback', array( $this, 'allow_callback' ) );
26 - add_action( 'learn-press/admin/order-items/layout', array( $this, 'order_items_layout' ) );
27 - add_action( 'learn-press/admin/order-details/items/layout', array( $this, 'order_detail_items_layout' ) );
28 - }
29 -
30 - /**
31 - * Allow callback for AJAX.
32 - *
33 - *
34 - * @param array $callbacks
35 - *
36 - * @return array
37 - */
38 - public function allow_callback( array $callbacks ): array {
39 - $callbacks[] = get_class( $this ) . ':render_order_items';
40 - $callbacks[] = get_class( $this ) . ':render_order_detail_items';
41 -
42 - return $callbacks;
43 - }
44 -
45 - /**
46 - * HTML layout for order items on screen list orders.
47 - *
48 - * @throws Exception
49 - * @since 4.3.2
50 - * @version 1.0.0
51 - */
52 - public function order_items_layout( LP_Order $lp_order ) {
53 - try {
54 - $args = array(
55 - 'id_url' => 'admin-view-order-items',
56 - 'order_id' => $lp_order->get_id(),
57 - 'paged' => 1,
58 - 'enableScrollToView' => false,
59 - 'enableUpdateParamsUrl' => false,
60 - 'html_loading_after_content_loaded' => '<i></i>',
61 - );
62 - $content_obj = self::render_order_items( $args );
63 - $args['html_no_load_ajax_first'] = $content_obj->content;
64 - $call_back = array(
65 - 'class' => self::class,
66 - 'method' => 'render_order_items',
67 - );
68 - echo TemplateAJAX::load_content_via_ajax( $args, $call_back );
69 - } catch ( Throwable $e ) {
70 -
71 - }
72 - }
73 -
74 - /**
75 - * Render html content for order items.
76 - *
77 - * @param array $data
78 - *
79 - * @return stdClass
80 - * @throws Exception
81 - * @since 4.3.2
82 - * @version 1.0.2
83 - */
84 - public static function render_order_items( array $data ): stdClass {
85 - $content = new stdClass();
86 - $order_id = $data['order_id'] ?? 0;
87 - if ( ! $order_id ) {
88 - throw new Exception( __( 'Order ID is required', 'learnpress' ) );
89 - }
90 -
91 - // Check permission to view order.
92 - $can_view = apply_filters(
93 - 'learn-press/order-detail/can-view',
94 - current_user_can( 'edit_' . LP_ORDER_CPT, $order_id ),
95 - $order_id
96 - );
97 - if ( ! $can_view ) {
98 - throw new Exception( __( 'You do not have permission to view this order.', 'learnpress' ) );
99 - }
100 -
101 - $lpOrderItemsDB = LPOrderItemsDB::getInstance();
102 - $filter = new OrderItemsFilter();
103 - $filter->page = $data['paged'] ?? 1;
104 - $filter->order_id = $data['order_id'] ?? 0;
105 - $filter->limit = 5;
106 - $total_row = 0;
107 - $items = $lpOrderItemsDB->get_items( $filter, $total_row );
108 -
109 - $html_items = '';
110 - if ( empty( $items ) ) {
111 - $html_items = sprintf( '<li>%s</li>', __( '(No item)', 'learnpress' ) );
112 - } else {
113 - foreach ( $items as $i => $itemObj ) {
114 - // Get meta data
115 - $itemObjMeta = self::get_all_metadata_order_item( $itemObj->order_item_id );
116 - $itemObj->meta = $itemObjMeta;
117 - $item_type = $itemObj->item_type ?? '';
118 - // For old data, the data not migrated yet to new column.
119 - if ( ( empty( $item_type ) || empty( $itemObj->item_id ) )
120 - && ! empty( $itemObj->meta['_course_id'] ) ) {
121 - $itemObj->item_id = $itemObj->meta['_course_id'];
122 - $item_type = LP_COURSE_CPT;
123 - }
124 -
125 - $index = ( $filter->page - 1 ) * $filter->limit + $i + 1;
126 - if ( $item_type === LP_COURSE_CPT ) {
127 - $coursePostModel = CoursePostModel::find_by_id( $itemObj->item_id, true );
128 - if ( ! $coursePostModel ) {
129 - $html_items .= sprintf(
130 - '<li>%s%s (%s)</li>',
131 - $total_row > 1 ? "$index. " : '',
132 - esc_html( $itemObj->order_item_name ),
133 - __( 'The course does not exist now.', 'learnpress' )
134 - );
135 - } else {
136 - $html_items .= sprintf(
137 - '<li>%s<a href="%s" >%s</a></li>',
138 - $total_row > 1 ? "$index. " : '',
139 - esc_url_raw( $coursePostModel->get_edit_link() ),
140 - esc_html( $itemObj->order_item_name )
141 - );
142 - }
143 - } else {
144 - if ( has_filter( 'learn-press/order-item-not-course-id' ) ) {
145 - $item_old = (array) $itemObj;
146 - $item_old['id'] = $itemObj->order_item_id;
147 - $item_old = array_merge( $item_old, $itemObjMeta );
148 - $html_items = apply_filters( 'learn-press/order-item-not-course-id', esc_html__( 'The course does not exist', 'learnpress' ), $item_old );
149 - } else {
150 - $html_items = apply_filters( 'learn-press/order-items/item', '', $itemObj, $order_id, $data );
151 - }
152 - }
153 - }
154 - }
155 -
156 - $total_pages = $lpOrderItemsDB::get_total_pages( $filter->limit, $total_row );
157 - $html_pagination = Template::instance()->html_pagination(
158 - [
159 - 'paged' => $data['paged'] ?? 1,
160 - 'total_pages' => $total_pages,
161 - ]
162 - );
163 -
164 - $section = array(
165 - 'wrap-start' => '<div class="lp-order-items-wrapper">',
166 - 'total_items' => $total_row > 1 ? sprintf(
167 - '<p class="total-items">%s: %d</p>',
168 - __( 'Total items', 'learnpress' ),
169 - $total_row
170 - ) : '',
171 - 'items' => '<ul class="order-list-items">' . $html_items . '</ul>',
172 - 'pagination' => $html_pagination,
173 - 'wrap-end' => '<div>',
174 - );
175 - $content->content = Template::combine_components( $section );
176 -
177 - return $content;
178 - }
179 -
180 - public function order_detail_items_layout( LP_Order $lp_order ) {
181 - $args = array(
182 - 'id_url' => 'admin-view-order-detail-items',
183 - 'order_id' => $lp_order->get_id(),
184 - 'paged' => 1,
185 - );
186 -
187 - /**
188 - * @use self::render_order_detail_items
189 - */
190 - $call_back = array(
191 - 'class' => self::class,
192 - 'method' => 'render_order_detail_items',
193 - );
194 - echo TemplateAJAX::load_content_via_ajax( $args, $call_back );
195 - }
196 -
197 - /**
198 - * Render html content for order detail items.
199 - *
200 - * @throws Exception
201 - * @since 4.3.2
202 - * @version 1.0.2
203 - */
204 - public static function render_order_detail_items( array $data ): stdClass {
205 - $content = new stdClass();
206 - $order_id = $data['order_id'] ?? 0;
207 - $paged = $data['paged'] ?? 1;
208 - if ( ! $order_id ) {
209 - throw new Exception( __( 'Order ID is required', 'learnpress' ) );
210 - }
211 -
212 - $lp_order = learn_press_get_order( $order_id );
213 - if ( ! $lp_order ) {
214 - throw new Exception( __( 'Order not found', 'learnpress' ) );
215 - }
216 -
217 - // Check permission to view order.
218 - $can_view = apply_filters(
219 - 'learn-press/order-detail/can-view',
220 - current_user_can( 'edit_' . LP_ORDER_CPT, $order_id ),
221 - $order_id
222 - );
223 - if ( ! $can_view ) {
224 - throw new Exception( __( 'You do not have permission to view this order.', 'learnpress' ) );
225 - }
226 -
227 - $lpOrderDB = LPOrderItemsDB::getInstance();
228 - $filter = new OrderItemsFilter();
229 - $filter->order_id = $lp_order->get_id();
230 - $filter->page = $paged;
231 - $filter->limit = - 1;
232 - $total_row = 0;
233 - $items = $lpOrderDB->get_items( $filter, $total_row );
234 -
235 - $html_items = '';
236 - if ( ! empty( $items ) ) {
237 - foreach ( $items as $itemObj ) {
238 - $itemObjMeta = self::get_all_metadata_order_item( $itemObj->order_item_id );
239 - $itemObj->meta = $itemObjMeta;
240 - $item_type = $itemObj->item_type ?? '';
241 - // For old data, the data not migrated yet to new column.
242 - if ( ( empty( $item_type ) || empty( $itemObj->item_id ) )
243 - && ! empty( $itemObj->meta['_course_id'] ) ) {
244 - $itemObj->item_id = $itemObj->meta['_course_id'];
245 - $item_type = LP_COURSE_CPT;
246 - }
247 -
248 - if ( $item_type === LP_COURSE_CPT ) {
249 - $html_items .= self::order_item_detail_html( $lp_order, $itemObj );
250 - } else {
251 - if ( has_filter( 'learn-press/order-item-not-course' ) ) {
252 - $item_old = (array) $itemObj;
253 - $item_old['id'] = $itemObj->order_item_id;
254 - $item_old = array_merge( $item_old, $itemObjMeta );
255 -
256 - ob_start();
257 - do_action( 'learn-press/order-item-not-course', $item_old, $lp_order );
258 - $html_items .= ob_get_clean();
259 - } else {
260 - ob_start();
261 - do_action( 'learn-press/order-detail/item', $itemObj, $order_id, $data );
262 - $html_items .= ob_get_clean();
263 - }
264 - }
265 - }
266 - }
267 -
268 - $total_pages = $lpOrderDB::get_total_pages( $filter->limit, $total_row );
269 - $html_pagination = Template::instance()->html_pagination(
270 - [
271 - 'paged' => $paged,
272 - 'total_pages' => $total_pages,
273 - ]
274 - );
275 -
276 - if ( ! empty( $html_pagination ) ) {
277 - $html_pagination = '<tr><td class="lp-order-items-wrapper" style="padding: 0 0 0 30px;" colspan="4">' . $html_pagination . '</td></tr>';
278 - }
279 -
280 - $section = array(
281 - 'wrap-start' => '<div class="order-items lp-order-detail-items">',
282 - 'table-start' => '<table class="list-order-items">',
283 - 'table-header' => self::table_header(),
284 - 'table-body-start' => '<tbody>',
285 - 'items' => $html_items,
286 - 'pagination' => $html_pagination,
287 - 'no_item_row' => sprintf(
288 - '<tr class="no-order-items %s">
289 - <td colspan="4">%s</td>
290 - </tr>',
291 - ! empty( $items ) ? 'lp-hidden' : '',
292 - __( 'There are no order items', 'learnpress' )
293 - ),
294 - 'table-body-end' => '</tbody>',
295 - 'table-footer' => self::table_footer( $lp_order ),
296 - 'table-end' => '</table>',
297 - 'wrap-end' => '</div>',
298 - );
299 - $content->content = Template::combine_components( $section );
300 -
301 - return $content;
302 - }
303 -
304 - /**
305 - * Table header
306 - *
307 - * @return string
308 - */
309 - public static function table_header(): string {
310 - $content = sprintf(
311 - '<thead>
312 - <tr>
313 - <th class="column-name">%1$s</th>
314 - <th class="column-price">%2$s</th>
315 - <th class="column-quantity">%3$s</th>
316 - <th class="column-total align-right">%4$s</th>
317 - </tr>
318 - </thead>',
319 - esc_html__( 'Item', 'learnpress' ),
320 - esc_html__( 'Cost', 'learnpress' ),
321 - esc_html__( 'Quantity', 'learnpress' ),
322 - esc_html__( 'Total', 'learnpress' )
323 - );
324 -
325 - return $content;
326 - }
327 -
328 - /**
329 - * order item html
330 - *
331 - * @param LP_Order $order
332 - * @param object $item lp order item
333 - *
334 - * @return string html
335 - */
336 - public static function order_item_detail_html( $order, $item ): string {
337 - $coursePostModel = CoursePostModel::find( $item->item_id, true );
338 - $total = $item->meta['_total'] ?? 0;
339 - $quantity = $item->meta['_quantity'] ?? 0;
340 - $currency_symbol = learn_press_get_currency_symbol( $order->get_currency() );
341 - $item_title = $item->order_item_name;
342 - $item_link = $coursePostModel ? $coursePostModel->get_edit_link() : '';
343 - ob_start();
344 - ?>
345 - <tr class="order-item-row" data-item_id="<?php echo esc_attr( $item->order_item_id ); ?>"
346 - data-id="<?php echo esc_attr( $item->item_id ); ?>"
347 - data-remove_nonce="<?php echo wp_create_nonce( 'remove_order_item' ); ?>">
348 - <td class="column-name">
349 - <?php if ( $order->is_manual() && $order->get_status() === LP_ORDER_PENDING ) : ?>
350 - <a class="remove-order-item" href="#">
351 - <span class="dashicons dashicons-no-alt"></span>
352 - </a>
353 - <?php endif; ?>
354 - <?php
355 - if ( ! empty( $item_link ) ) {
356 - printf(
357 - '<a href="%s" >%s</a>',
358 - esc_url( $item_link ),
359 - esc_html( $item_title )
360 - );
361 - } else {
362 - echo esc_html( $item_title );
363 - }
364 - ?>
365 - </td>
366 - <td class="column-price align-right">
367 - <?php echo learn_press_format_price( $total, $currency_symbol ); ?>
368 - </td>
369 - <td class="column-quantity align-right">
370 - <small class="times">×</small>
371 - <?php echo esc_html( $quantity ); ?>
372 - </td>
373 - <td class="column-total align-right"><?php echo learn_press_format_price( $total, $currency_symbol ); ?></td>
374 - </tr>
375 - <?php
376 - return ob_get_clean();
377 - }
378 -
379 - /**
380 - * Order Details Footer
381 - *
382 - * @param LP_Order $order
383 - *
384 - * @return string table footer
385 - */
386 - public static function table_footer( $order ) {
387 - $currency_symbol = learn_press_get_currency_symbol( $order->get_currency() ?? learn_press_get_currency() );
388 - ob_start();
389 - ?>
390 - <tfoot>
391 - <tr>
392 - <td colspan="2"></td>
393 - <td colspan="2"></td>
394 - </tr>
395 - <tr class="row-subtotal">
396 - <td width="300" colspan="3" class="align-right">
397 - <?php esc_html_e( 'Subtotal:', 'learnpress' ); ?>
398 - </td>
399 - <td width="100" class="align-right">
400 - <span class="order-subtotal">
401 - <?php echo learn_press_format_price( $order->get_data( 'order_subtotal' ), $currency_symbol ); ?>
402 - </span>
403 - </td>
404 - </tr>
405 - <?php do_action( 'learn-press/admin/order/detail/before-total', $order ); ?>
406 - <tr class="row-total">
407 - <td class="align-right" colspan="3">
408 - <?php esc_html_e( 'Total:', 'learnpress' ); ?>
409 - </td>
410 - <td class="align-right total">
411 - <span class="order-total">
412 - <?php echo learn_press_format_price( $order->get_data( 'order_total' ), $currency_symbol ); ?>
413 - </span>
414 - </td>
415 - </tr>
416 - <tr>
417 - <td colspan="2"></td>
418 - <td colspan="2" style="border-bottom: 1px dashed #DDD;"></td>
419 - </tr>
420 - <?php if ( $order->is_manual() ) : ?>
421 - <tr>
422 - <td class="align-right" colspan="4" style="border-top: 1px solid #DDD;">
423 - <?php if ( 'pending' === $order->get_status() ) : ?>
424 - <button class="button" type="button" id="learn-press-add-order-item">
425 - <?php esc_html_e( 'Add item(s)', 'learnpress' ); ?>
426 - </button>
427 - <?php else : ?>
428 - <p class="description"> <?php esc_html_e( 'In order to change the order item, please change the order status to \'Pending\'.', 'learnpress' ); ?> </p>
429 - <?php endif; ?>
430 - </td>
431 - </tr>
432 - <?php endif; ?>
433 - </tfoot>
434 - <?php
435 - return ob_get_clean();
436 - }
437 -
438 - /**
439 - * Get all meta data of order item
440 - *
441 - * @param int $order_item_id
442 - *
443 - * @return array
444 - */
445 - public static function get_all_metadata_order_item( $order_item_id ): array {
446 - $meta_data = get_metadata( 'learnpress_order_item', $order_item_id, '', true );
447 - $result = [];
448 - if ( is_array( $meta_data ) ) {
449 - foreach ( $meta_data as $key => $value ) {
450 - if ( is_array( $value ) && count( $value ) === 1 ) {
451 - $result[ $key ] = maybe_unserialize( $value[0] );
452 - } else {
453 - $result[ $key ] = maybe_unserialize( $value );
454 - }
455 - }
456 - }
457 -
458 - return $result;
459 - }
460 -}
1 +<?php
2 +namespace LearnPress\TemplateHooks\Order;
3 +
4 +use Exception;
5 +use LearnPress\Databases\Order\LPOrderItemsDB;
6 +use LearnPress\Filters\Order\OrderItemsFilter;
7 +use LearnPress\Helpers\Singleton;
8 +use LearnPress\Helpers\Template;
9 +use LearnPress\Models\CoursePostModel;
10 +use LearnPress\TemplateHooks\TemplateAJAX;
11 +use stdClass;
12 +use LP_Order;
13 +use Throwable;
14 +
15 +/**
16 + * class AdminOrderItemsTemplate
17 + *
18 + * @since 4.3.2
19 + * @version 1.0.0
20 + */
21 +class AdminOrderItemsTemplate {
22 + use Singleton;
23 +
24 + public function init() {
25 + add_filter( 'lp/rest/ajax/allow_callback', array( $this, 'allow_callback' ) );
26 + add_action( 'learn-press/admin/order-items/layout', array( $this, 'order_items_layout' ) );
27 + add_action( 'learn-press/admin/order-details/items/layout', array( $this, 'order_detail_items_layout' ) );
28 + }
29 +
30 + /**
31 + * Allow callback for AJAX.
32 + *
33 + *
34 + * @param array $callbacks
35 + *
36 + * @return array
37 + */
38 + public function allow_callback( array $callbacks ): array {
39 + $callbacks[] = get_class( $this ) . ':render_order_items';
40 + $callbacks[] = get_class( $this ) . ':render_order_detail_items';
41 +
42 + return $callbacks;
43 + }
44 +
45 + /**
46 + * HTML layout for order items on screen list orders.
47 + *
48 + * @throws Exception
49 + * @since 4.3.2
50 + * @version 1.0.0
51 + */
52 + public function order_items_layout( LP_Order $lp_order ) {
53 + try {
54 + $args = array(
55 + 'id_url' => 'admin-view-order-items',
56 + 'order_id' => $lp_order->get_id(),
57 + 'paged' => 1,
58 + 'enableScrollToView' => false,
59 + 'enableUpdateParamsUrl' => false,
60 + 'html_loading_after_content_loaded' => '<i></i>',
61 + );
62 + $content_obj = self::render_order_items( $args );
63 + $args['html_no_load_ajax_first'] = $content_obj->content;
64 + $call_back = array(
65 + 'class' => self::class,
66 + 'method' => 'render_order_items',
67 + );
68 + echo TemplateAJAX::load_content_via_ajax( $args, $call_back );
69 + } catch ( Throwable $e ) {
70 +
71 + }
72 + }
73 +
74 + /**
75 + * Render html content for order items.
76 + *
77 + * @param array $data
78 + *
79 + * @return stdClass
80 + * @throws Exception
81 + * @since 4.3.2
82 + * @version 1.0.2
83 + */
84 + public static function render_order_items( array $data ): stdClass {
85 + $content = new stdClass();
86 + $order_id = $data['order_id'] ?? 0;
87 + if ( ! $order_id ) {
88 + throw new Exception( __( 'Order ID is required', 'learnpress' ) );
89 + }
90 +
91 + // Check permission to view order.
92 + $can_view = apply_filters(
93 + 'learn-press/order-detail/can-view',
94 + current_user_can( 'edit_' . LP_ORDER_CPT, $order_id ),
95 + $order_id
96 + );
97 + if ( ! $can_view ) {
98 + throw new Exception( __( 'You do not have permission to view this order.', 'learnpress' ) );
99 + }
100 +
101 + $lpOrderItemsDB = LPOrderItemsDB::getInstance();
102 + $filter = new OrderItemsFilter();
103 + $filter->page = $data['paged'] ?? 1;
104 + $filter->order_id = $data['order_id'] ?? 0;
105 + $filter->limit = 5;
106 + $total_row = 0;
107 + $items = $lpOrderItemsDB->get_items( $filter, $total_row );
108 +
109 + $html_items = '';
110 + if ( empty( $items ) ) {
111 + $html_items = sprintf( '<li>%s</li>', __( '(No item)', 'learnpress' ) );
112 + } else {
113 + foreach ( $items as $i => $itemObj ) {
114 + // Get meta data
115 + $itemObjMeta = self::get_all_metadata_order_item( $itemObj->order_item_id );
116 + $itemObj->meta = $itemObjMeta;
117 + $item_type = $itemObj->item_type ?? '';
118 + // For old data, the data not migrated yet to new column.
119 + if ( ( empty( $item_type ) || empty( $itemObj->item_id ) )
120 + && ! empty( $itemObj->meta['_course_id'] ) ) {
121 + $itemObj->item_id = $itemObj->meta['_course_id'];
122 + $item_type = LP_COURSE_CPT;
123 + }
124 +
125 + $index = ( $filter->page - 1 ) * $filter->limit + $i + 1;
126 + if ( $item_type === LP_COURSE_CPT ) {
127 + $coursePostModel = CoursePostModel::find_by_id( $itemObj->item_id, true );
128 + if ( ! $coursePostModel ) {
129 + $html_items .= sprintf(
130 + '<li>%s%s (%s)</li>',
131 + $total_row > 1 ? "$index. " : '',
132 + esc_html( $itemObj->order_item_name ),
133 + __( 'The course does not exist now.', 'learnpress' )
134 + );
135 + } else {
136 + $html_items .= sprintf(
137 + '<li>%s<a href="%s" >%s</a></li>',
138 + $total_row > 1 ? "$index. " : '',
139 + esc_url_raw( $coursePostModel->get_edit_link() ),
140 + esc_html( $itemObj->order_item_name )
141 + );
142 + }
143 + } else {
144 + if ( has_filter( 'learn-press/order-item-not-course-id' ) ) {
145 + $item_old = (array) $itemObj;
146 + $item_old['id'] = $itemObj->order_item_id;
147 + $item_old = array_merge( $item_old, $itemObjMeta );
148 + $html_items = apply_filters( 'learn-press/order-item-not-course-id', esc_html__( 'The course does not exist', 'learnpress' ), $item_old );
149 + } else {
150 + $html_items = apply_filters( 'learn-press/order-items/item', '', $itemObj, $order_id, $data );
151 + }
152 + }
153 + }
154 + }
155 +
156 + $total_pages = $lpOrderItemsDB::get_total_pages( $filter->limit, $total_row );
157 + $html_pagination = Template::instance()->html_pagination(
158 + [
159 + 'paged' => $data['paged'] ?? 1,
160 + 'total_pages' => $total_pages,
161 + ]
162 + );
163 +
164 + $section = array(
165 + 'wrap-start' => '<div class="lp-order-items-wrapper">',
166 + 'total_items' => $total_row > 1 ? sprintf(
167 + '<p class="total-items">%s: %d</p>',
168 + __( 'Total items', 'learnpress' ),
169 + $total_row
170 + ) : '',
171 + 'items' => '<ul class="order-list-items">' . $html_items . '</ul>',
172 + 'pagination' => $html_pagination,
173 + 'wrap-end' => '<div>',
174 + );
175 + $content->content = Template::combine_components( $section );
176 +
177 + return $content;
178 + }
179 +
180 + public function order_detail_items_layout( LP_Order $lp_order ) {
181 + $args = array(
182 + 'id_url' => 'admin-view-order-detail-items',
183 + 'order_id' => $lp_order->get_id(),
184 + 'paged' => 1,
185 + );
186 +
187 + /**
188 + * @use self::render_order_detail_items
189 + */
190 + $call_back = array(
191 + 'class' => self::class,
192 + 'method' => 'render_order_detail_items',
193 + );
194 + echo TemplateAJAX::load_content_via_ajax( $args, $call_back );
195 + }
196 +
197 + /**
198 + * Render html content for order detail items.
199 + *
200 + * @throws Exception
201 + * @since 4.3.2
202 + * @version 1.0.2
203 + */
204 + public static function render_order_detail_items( array $data ): stdClass {
205 + $content = new stdClass();
206 + $order_id = $data['order_id'] ?? 0;
207 + $paged = $data['paged'] ?? 1;
208 + if ( ! $order_id ) {
209 + throw new Exception( __( 'Order ID is required', 'learnpress' ) );
210 + }
211 +
212 + $lp_order = learn_press_get_order( $order_id );
213 + if ( ! $lp_order ) {
214 + throw new Exception( __( 'Order not found', 'learnpress' ) );
215 + }
216 +
217 + // Check permission to view order.
218 + $can_view = apply_filters(
219 + 'learn-press/order-detail/can-view',
220 + current_user_can( 'edit_' . LP_ORDER_CPT, $order_id ),
221 + $order_id
222 + );
223 + if ( ! $can_view ) {
224 + throw new Exception( __( 'You do not have permission to view this order.', 'learnpress' ) );
225 + }
226 +
227 + $lpOrderDB = LPOrderItemsDB::getInstance();
228 + $filter = new OrderItemsFilter();
229 + $filter->order_id = $lp_order->get_id();
230 + $filter->page = $paged;
231 + $filter->limit = - 1;
232 + $total_row = 0;
233 + $items = $lpOrderDB->get_items( $filter, $total_row );
234 +
235 + $html_items = '';
236 + if ( ! empty( $items ) ) {
237 + foreach ( $items as $itemObj ) {
238 + $itemObjMeta = self::get_all_metadata_order_item( $itemObj->order_item_id );
239 + $itemObj->meta = $itemObjMeta;
240 + $item_type = $itemObj->item_type ?? '';
241 + // For old data, the data not migrated yet to new column.
242 + if ( ( empty( $item_type ) || empty( $itemObj->item_id ) )
243 + && ! empty( $itemObj->meta['_course_id'] ) ) {
244 + $itemObj->item_id = $itemObj->meta['_course_id'];
245 + $item_type = LP_COURSE_CPT;
246 + }
247 +
248 + if ( $item_type === LP_COURSE_CPT ) {
249 + $html_items .= self::order_item_detail_html( $lp_order, $itemObj );
250 + } else {
251 + if ( has_filter( 'learn-press/order-item-not-course' ) ) {
252 + $item_old = (array) $itemObj;
253 + $item_old['id'] = $itemObj->order_item_id;
254 + $item_old = array_merge( $item_old, $itemObjMeta );
255 +
256 + ob_start();
257 + do_action( 'learn-press/order-item-not-course', $item_old, $lp_order );
258 + $html_items .= ob_get_clean();
259 + } else {
260 + ob_start();
261 + do_action( 'learn-press/order-detail/item', $itemObj, $order_id, $data );
262 + $html_items .= ob_get_clean();
263 + }
264 + }
265 + }
266 + }
267 +
268 + $total_pages = $lpOrderDB::get_total_pages( $filter->limit, $total_row );
269 + $html_pagination = Template::instance()->html_pagination(
270 + [
271 + 'paged' => $paged,
272 + 'total_pages' => $total_pages,
273 + ]
274 + );
275 +
276 + if ( ! empty( $html_pagination ) ) {
277 + $html_pagination = '<tr><td class="lp-order-items-wrapper" style="padding: 0 0 0 30px;" colspan="4">' . $html_pagination . '</td></tr>';
278 + }
279 +
280 + $section = array(
281 + 'wrap-start' => '<div class="order-items lp-order-detail-items">',
282 + 'table-start' => '<table class="list-order-items">',
283 + 'table-header' => self::table_header(),
284 + 'table-body-start' => '<tbody>',
285 + 'items' => $html_items,
286 + 'pagination' => $html_pagination,
287 + 'no_item_row' => sprintf(
288 + '<tr class="no-order-items %s">
289 + <td colspan="4">%s</td>
290 + </tr>',
291 + ! empty( $items ) ? 'lp-hidden' : '',
292 + __( 'There are no order items', 'learnpress' )
293 + ),
294 + 'table-body-end' => '</tbody>',
295 + 'table-footer' => self::table_footer( $lp_order ),
296 + 'table-end' => '</table>',
297 + 'wrap-end' => '</div>',
298 + );
299 + $content->content = Template::combine_components( $section );
300 +
301 + return $content;
302 + }
303 +
304 + /**
305 + * Table header
306 + *
307 + * @return string
308 + */
309 + public static function table_header(): string {
310 + $content = sprintf(
311 + '<thead>
312 + <tr>
313 + <th class="column-name">%1$s</th>
314 + <th class="column-price">%2$s</th>
315 + <th class="column-quantity">%3$s</th>
316 + <th class="column-total align-right">%4$s</th>
317 + </tr>
318 + </thead>',
319 + esc_html__( 'Item', 'learnpress' ),
320 + esc_html__( 'Cost', 'learnpress' ),
321 + esc_html__( 'Quantity', 'learnpress' ),
322 + esc_html__( 'Total', 'learnpress' )
323 + );
324 +
325 + return $content;
326 + }
327 +
328 + /**
329 + * order item html
330 + *
331 + * @param LP_Order $order
332 + * @param object $item lp order item
333 + *
334 + * @return string html
335 + */
336 + public static function order_item_detail_html( $order, $item ): string {
337 + $coursePostModel = CoursePostModel::find( $item->item_id, true );
338 + $total = $item->meta['_total'] ?? 0;
339 + $quantity = $item->meta['_quantity'] ?? 0;
340 + $currency_symbol = learn_press_get_currency_symbol( $order->get_currency() );
341 + $item_title = $item->order_item_name;
342 + $item_link = $coursePostModel ? $coursePostModel->get_edit_link() : '';
343 + ob_start();
344 + ?>
345 + <tr class="order-item-row" data-item_id="<?php echo esc_attr( $item->order_item_id ); ?>"
346 + data-id="<?php echo esc_attr( $item->item_id ); ?>"
347 + data-remove_nonce="<?php echo wp_create_nonce( 'remove_order_item' ); ?>">
348 + <td class="column-name">
349 + <?php if ( $order->is_manual() && $order->get_status() === LP_ORDER_PENDING ) : ?>
350 + <a class="remove-order-item" href="#">
351 + <span class="dashicons dashicons-no-alt"></span>
352 + </a>
353 + <?php endif; ?>
354 + <?php
355 + if ( ! empty( $item_link ) ) {
356 + printf(
357 + '<a href="%s" >%s</a>',
358 + esc_url( $item_link ),
359 + esc_html( $item_title )
360 + );
361 + } else {
362 + echo esc_html( $item_title );
363 + }
364 + ?>
365 + </td>
366 + <td class="column-price align-right">
367 + <?php echo learn_press_format_price( $total, $currency_symbol ); ?>
368 + </td>
369 + <td class="column-quantity align-right">
370 + <small class="times">×</small>
371 + <?php echo esc_html( $quantity ); ?>
372 + </td>
373 + <td class="column-total align-right"><?php echo learn_press_format_price( $total, $currency_symbol ); ?></td>
374 + </tr>
375 + <?php
376 + return ob_get_clean();
377 + }
378 +
379 + /**
380 + * Order Details Footer
381 + *
382 + * @param LP_Order $order
383 + *
384 + * @return string table footer
385 + */
386 + public static function table_footer( $order ) {
387 + $currency_symbol = learn_press_get_currency_symbol( $order->get_currency() ?? learn_press_get_currency() );
388 + ob_start();
389 + ?>
390 + <tfoot>
391 + <tr>
392 + <td colspan="2"></td>
393 + <td colspan="2"></td>
394 + </tr>
395 + <tr class="row-subtotal">
396 + <td width="300" colspan="3" class="align-right">
397 + <?php esc_html_e( 'Subtotal:', 'learnpress' ); ?>
398 + </td>
399 + <td width="100" class="align-right">
400 + <span class="order-subtotal">
401 + <?php echo learn_press_format_price( $order->get_data( 'order_subtotal' ), $currency_symbol ); ?>
402 + </span>
403 + </td>
404 + </tr>
405 + <?php do_action( 'learn-press/admin/order/detail/before-total', $order ); ?>
406 + <tr class="row-total">
407 + <td class="align-right" colspan="3">
408 + <?php esc_html_e( 'Total:', 'learnpress' ); ?>
409 + </td>
410 + <td class="align-right total">
411 + <span class="order-total">
412 + <?php echo learn_press_format_price( $order->get_data( 'order_total' ), $currency_symbol ); ?>
413 + </span>
414 + </td>
415 + </tr>
416 + <tr>
417 + <td colspan="2"></td>
418 + <td colspan="2" style="border-bottom: 1px dashed #DDD;"></td>
419 + </tr>
420 + <?php if ( $order->is_manual() ) : ?>
421 + <tr>
422 + <td class="align-right" colspan="4" style="border-top: 1px solid #DDD;">
423 + <?php if ( 'pending' === $order->get_status() ) : ?>
424 + <button class="button" type="button" id="learn-press-add-order-item">
425 + <?php esc_html_e( 'Add item(s)', 'learnpress' ); ?>
426 + </button>
427 + <?php else : ?>
428 + <p class="description"> <?php esc_html_e( 'In order to change the order item, please change the order status to \'Pending\'.', 'learnpress' ); ?> </p>
429 + <?php endif; ?>
430 + </td>
431 + </tr>
432 + <?php endif; ?>
433 + </tfoot>
434 + <?php
435 + return ob_get_clean();
436 + }
437 +
438 + /**
439 + * Get all meta data of order item
440 + *
441 + * @param int $order_item_id
442 + *
443 + * @return array
444 + */
445 + public static function get_all_metadata_order_item( $order_item_id ): array {
446 + $meta_data = get_metadata( 'learnpress_order_item', $order_item_id, '', true );
447 + $result = [];
448 + if ( is_array( $meta_data ) ) {
449 + foreach ( $meta_data as $key => $value ) {
450 + if ( is_array( $value ) && count( $value ) === 1 ) {
451 + $result[ $key ] = maybe_unserialize( $value[0] );
452 + } else {
453 + $result[ $key ] = maybe_unserialize( $value );
454 + }
455 + }
456 + }
457 +
458 + return $result;
459 + }
460 +}