PluginProbe
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent / 3.4.4
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent v3.4.4
3.5.4 3.5.3 3.5.2 3.5.1 3.4.9 3.5.0 3.4.8 3.4.7 trunk 2.3.1 3.3.6 3.3.7 3.3.8 3.3.9 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6
supportcandy / includes / rest-api / class-wpsc-rest-individual-ticket.php

class-wpsc-rest-individual-ticket.php in SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent 3.4.4, at includes/rest-api/class-wpsc-rest-individual-ticket.php

679 lines 20.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 exit; // Exit if accessed directly!
4 }
5
6 if ( ! class_exists( 'WPSC_REST_Individual_Ticket' ) ) :
7
8 final class WPSC_REST_Individual_Ticket {
9
10 /**
11 * Array of ticket slugs which we do not want to expose to REST API
12 *
13 * @var array
14 */
15 public static $prevent_data = array();
16
17 /**
18 * Initialize this class
19 *
20 * @return void
21 */
22 public static function init() {
23
24 add_action( 'wpsc_rest_load_ticket_prevent_data', array( __CLASS__, 'load_prevent_data' ) );
25 }
26
27 /**
28 * Load prevent data
29 *
30 * @return void
31 */
32 public static function load_prevent_data() {
33
34 $current_user = WPSC_Current_User::$current_user;
35 $data = ! $current_user->is_agent ? array( 'ip_address', 'source', 'browser', 'os', 'user_type' ) : array();
36 self::$prevent_data = apply_filters( 'wpsc_rest_prevent_ticket_data', $data );
37 }
38
39 /**
40 * Create new ticket
41 *
42 * @param WP_REST_Request $request - request object.
43 * @return WP_Error|WP_REST_Response
44 */
45 public static function create( $request ) {
46
47 $current_user = WPSC_Current_User::$current_user;
48 $gs = get_option( 'wpsc-gs-general' );
49 $advanced = get_option( 'wpsc-ms-advanced-settings' );
50
51 if ( ! (
52 ( ! $current_user->user->ID && in_array( 'guest', $gs['allow-create-ticket'] ) ) ||
53 ( $current_user->is_agent && in_array( $current_user->agent->role, $gs['allow-create-ticket'] ) ) ||
54 ( ! $current_user->is_agent && $current_user->user->ID && in_array( 'registered-user', $gs['allow-create-ticket'] ) )
55 ) ) {
56 return new WP_REST_Response( new WP_Error( 'unauthorized', 'You are not authorized to create ticket!' ), 401 );
57 }
58
59 // group by custom field type.
60 $cfs = array();
61 foreach ( WPSC_Custom_Field::$custom_fields as $cf ) {
62 $cfs[ $cf->type::$slug ][] = $cf;
63 }
64
65 // collect all data to create ticket.
66 $data = apply_filters(
67 'wpsc_rest_create_ticket',
68 array( 'errors' => new WP_Error() ),
69 $request,
70 $cfs
71 );
72
73 // check whether there are any errors.
74 if ( $data['errors']->has_errors() ) {
75 return new WP_REST_Response( $data['errors'], 400 );
76 } else {
77 unset( $data['errors'] );
78 }
79
80 // Seperate description from $data.
81 $description = $data['description'];
82 unset( $data['description'] );
83
84 // Seperate description attachments from $data.
85 $description_attachments = $data['description_attachments'];
86 unset( $data['description_attachments'] );
87
88 $data['last_reply_on'] = ( new DateTime() )->format( 'Y-m-d H:i:s' );
89
90 // insert ticket data.
91 $ticket = WPSC_Ticket::insert( $data );
92
93 if ( ! $ticket ) {
94 return new WP_REST_Response( new WP_Error( 'something_wrong', 'Something went wrong!' ), 400 );
95 }
96
97 $thread_customer = $current_user->is_agent && $current_user->customer->id != $ticket->customer->id && $advanced['raised-by-user'] == 'agent' ? $current_user->customer : $ticket->customer;
98 $ticket->last_reply_by = $thread_customer->id;
99 $ticket->last_reply_source = 'rest-api';
100 $ticket->save();
101
102 // replace macros only when current user is an agent.
103 $description = $current_user->is_agent ? WPSC_Macros::replace( $description, $ticket ) : $description;
104
105 // set signature if agent.
106 $signature = $current_user->is_agent && $current_user->customer->email == $ticket->customer->email ? $current_user->agent->get_signature() : '';
107 if ( $signature ) {
108 $description .= $signature;
109 }
110
111 // Create report thread.
112 $thread = WPSC_Thread::insert(
113 array(
114 'ticket' => $ticket->id,
115 'customer' => $thread_customer->id,
116 'type' => 'report',
117 'body' => $description,
118 'attachments' => $description_attachments,
119 'ip_address' => $ticket->ip_address,
120 'source' => $ticket->source,
121 'os' => $ticket->os,
122 'browser' => $ticket->browser,
123 )
124 );
125
126 do_action( 'wpsc_create_new_ticket', $ticket );
127
128 $request = new WP_REST_Request( 'GET', '/supportcandy/v2/tickets/' . $ticket->id );
129 return rest_do_request( $request );
130 }
131
132 /**
133 * Get individual ticket
134 *
135 * @param WP_REST_Request $request - request object.
136 * @return WP_Error|WP_REST_Response
137 */
138 public static function get( $request ) {
139
140 $current_user = WPSC_Current_User::$current_user;
141
142 // triggers loading of prevent ticket data property.
143 do_action( 'wpsc_rest_load_ticket_prevent_data' );
144
145 $ticket = new WPSC_Ticket( $request->get_param( 'id' ) );
146 if ( ! $ticket->id ) {
147 return new WP_REST_Response( new WP_Error( 'unauthorized', 'You are not authorized!' ), 401 );
148 }
149
150 WPSC_Individual_Ticket::$ticket = $ticket;
151 if ( ! (
152 ( $current_user->is_agent && WPSC_Individual_Ticket::has_ticket_cap( 'view' ) ) ||
153 WPSC_Individual_Ticket::is_customer()
154 ) ) {
155 return new WP_REST_Response( new WP_Error( 'unauthorized', 'You are not authorized!' ), 401 );
156 }
157
158 $data = self::modify_response( $ticket->to_array() );
159 return new WP_REST_Response( $data, 200 );
160 }
161
162 /**
163 * Update/modify the ticket
164 *
165 * @param WP_REST_Request $request - request object.
166 * @return WP_Error|WP_REST_Response
167 */
168 public static function update( $request ) {
169
170 $current_user = WPSC_Current_User::$current_user;
171
172 $ticket = new WPSC_Ticket( $request->get_param( 'id' ) );
173 if ( ! $ticket->id || ! $ticket->is_active ) {
174 return new WP_REST_Response( new WP_Error( 'unauthorized', 'You are not authorized!' ), 401 );
175 }
176
177 // ticket object clone to compare.
178 $ticket_clone = clone $ticket;
179 WPSC_Individual_Ticket::$ticket = $ticket;
180 $agentonly_fields = array();
181 foreach ( $request->get_params() as $slug => $val ) {
182
183 if ( in_array( $slug, array( 'id', 'description', 'description_attachments' ) ) ) {
184 continue;
185 }
186
187 $cf = WPSC_Custom_Field::get_cf_by_slug( $slug );
188 if ( ! $cf || ! in_array( $cf->field, array( 'ticket' ) ) ) {
189
190 if ( $cf || in_array( $cf->field, array( 'agentonly' ) ) ) {
191 $agentonly_fields[ $slug ] = $val;
192 }
193 continue;
194 }
195
196 // subject.
197 if ( $slug == 'subject' && $current_user->is_agent && WPSC_Individual_Ticket::has_ticket_cap( 'ctf' ) ) {
198
199 $prev = $ticket->subject;
200 $new = sanitize_text_field( $val );
201 if ( ! $new || $prev == $new ) {
202 continue;
203 }
204
205 $ticket->subject = $new;
206 do_action( 'wpsc_change_ticket_subject', $ticket, $prev, $new, $current_user->customer->id );
207 }
208
209 // status.
210 if ( $slug == 'status' && $current_user->is_agent && WPSC_Individual_Ticket::has_ticket_cap( 'cs' ) ) {
211
212 $status = new WPSC_Status( intval( $val ) );
213 if ( ! $status->id || $ticket->status == $status ) {
214 continue;
215 }
216
217 WPSC_Individual_Ticket::change_status( $ticket->status->id, $status->id, $current_user->customer->id );
218 }
219
220 // priority.
221 if ( $slug == 'priority' && $current_user->is_agent && WPSC_Individual_Ticket::has_ticket_cap( 'cs' ) ) {
222
223 $priority = new WPSC_Priority( intval( $val ) );
224 if ( ! $priority->id || $ticket->priority == $priority ) {
225 continue;
226 }
227
228 WPSC_Individual_Ticket::change_priority( $ticket->priority->id, $priority->id, $current_user->customer->id );
229 }
230
231 // category.
232 if ( $slug == 'category' && $current_user->is_agent && WPSC_Individual_Ticket::has_ticket_cap( 'cs' ) ) {
233
234 $category = new WPSC_Category( intval( $val ) );
235 if ( ! $category->id || $ticket->category == $category ) {
236 continue;
237 }
238
239 WPSC_Individual_Ticket::change_category( $ticket->category->id, $category->id, $current_user->customer->id );
240 }
241
242 // customer.
243 if ( $slug == 'customer' && $current_user->is_agent && WPSC_Individual_Ticket::has_ticket_cap( 'crb' ) ) {
244
245 $customer = new WPSC_Customer( intval( $val ) );
246 if ( ! $customer->id || $ticket->customer == $customer ) {
247 continue;
248 }
249
250 WPSC_Individual_Ticket::change_raised_by( $ticket->customer, $customer, $current_user->customer->id );
251 }
252
253 // assignee.
254 if ( $slug == 'assigned_agent' && $current_user->is_agent && WPSC_Individual_Ticket::has_ticket_cap( 'aa' ) ) {
255
256 $new = array_unique(
257 array_filter(
258 array_map(
259 function ( $id ) {
260 $agent = new WPSC_Agent( intval( $id ) );
261 return $agent->id ? $agent->id : false;
262 },
263 explode( ',', sanitize_text_field( $val ) )
264 )
265 )
266 );
267
268 $prev = array_unique(
269 array_filter(
270 array_map(
271 fn( $agent ) => $agent->id,
272 $ticket->assigned_agent
273 )
274 )
275 );
276
277 if ( ! $new || ! ( array_diff( $prev, $new ) || array_diff( $new, $prev ) ) ) {
278 continue;
279 }
280
281 $new = array_map(
282 fn( $id ) => new WPSC_Agent( $id ),
283 $new
284 );
285
286 WPSC_Individual_Ticket::change_assignee( $ticket->assigned_agent, $new, $current_user->customer->id );
287 }
288
289 // additional recipients.
290 if (
291 $slug == 'add_recipients' &&
292 (
293 ( $current_user->is_agent && WPSC_Individual_Ticket::has_ticket_cap( 'ar' ) ) ||
294 ( ! $current_user->is_agent && $ticket->customer->id == $current_user->customer->id )
295 )
296 ) {
297
298 $recipients = array_unique( array_filter( array_map( 'sanitize_email', explode( ',', $val ) ) ) );
299 $prev = $ticket->add_recipients;
300 if ( ! $recipients || ! ( array_diff( $prev, $recipients ) || array_diff( $recipients, $prev ) ) ) {
301 continue;
302 }
303
304 $ticket->add_recipients = $recipients;
305 do_action( 'wpsc_change_ticket_add_recipients', $ticket, $prev, $recipients, $current_user->customer->id );
306 }
307
308 // ticket fields.
309 if (
310 class_exists( $cf->type ) &&
311 $current_user->is_agent &&
312 WPSC_Individual_Ticket::has_ticket_cap( 'ctf' ) &&
313 ! $cf->type::$is_default &&
314 ! in_array( $cf->type::$slug, WPSC_ITW_Ticket_Fields::$ignore_cft )
315 ) {
316
317 $cf->type::set_rest_edit_ticket_cf( $ticket, $cf, $val );
318 }
319 }
320 // save the changes.
321 if ( $ticket != $ticket_clone ) {
322 $ticket->date_updated = new DateTime();
323 $ticket->save();
324 do_action( 'wpsc_change_ticket_fields', $ticket_clone, $ticket );
325 }
326
327 // ticket object clone to compare. Seperate loop to avoid duplicate logs and actions.
328 $ticket_clone = clone $ticket;
329 WPSC_Individual_Ticket::$ticket = $ticket;
330 foreach ( $agentonly_fields as $slug => $val ) {
331
332 if ( in_array( $slug, array( 'id', 'description', 'description_attachments' ) ) ) {
333 continue;
334 }
335
336 $cf = WPSC_Custom_Field::get_cf_by_slug( $slug );
337 if ( ! $cf || ! in_array( $cf->field, array( 'agentonly' ) ) ) {
338 continue;
339 }
340
341 if (
342 // agentonly fields.
343 class_exists( $cf->type ) &&
344 $current_user->is_agent &&
345 WPSC_Individual_Ticket::has_ticket_cap( 'caof' ) &&
346 ! $cf->type::$is_default &&
347 ! in_array( $cf->type::$slug, WPSC_ITW_Agentonly_Fields::$ignore_cft )
348 ) {
349
350 $cf->type::set_rest_edit_ticket_cf( $ticket, $cf, $val );
351 }
352 }
353 // save the changes.
354 if ( $ticket != $ticket_clone ) {
355 $ticket->date_updated = new DateTime();
356 $ticket->save();
357 do_action( 'wpsc_change_agentonly_fields', $ticket_clone, $ticket );
358 }
359
360 $request = new WP_REST_Request( 'GET', '/supportcandy/v2/tickets/' . $ticket->id );
361 return rest_do_request( $request );
362 }
363
364 /**
365 * Delete the ticket
366 *
367 * @param WP_REST_Request $request - request object.
368 * @return WP_Error|WP_REST_Response
369 */
370 public static function delete( $request ) {
371
372 $current_user = WPSC_Current_User::$current_user;
373 if ( ! $current_user->is_agent ) {
374 return new WP_REST_Response( new WP_Error( 'unauthorized', 'You are not authorized to delete ticket!' ), 401 );
375 }
376
377 $ticket = new WPSC_Ticket( $request->get_param( 'id' ) );
378 if ( ! $ticket->id ) {
379 return new WP_REST_Response( new WP_Error( 'bad_request', 'Bad request!' ), 400 );
380 }
381
382 WPSC_Individual_Ticket::$ticket = $ticket;
383 if ( ! WPSC_Individual_Ticket::$ticket->is_active || ! WPSC_Individual_Ticket::has_ticket_cap( 'dtt' ) ) {
384 return new WP_REST_Response( new WP_Error( 'unauthorized', 'You are not authorized to delete this ticket!' ), 401 );
385 }
386
387 WPSC_Individual_Ticket::delete_archive_ticket();
388 return new WP_REST_Response( array( 'success' => true ), 200 );
389 }
390
391 /**
392 * Get ticket threads
393 *
394 * @param WP_REST_Request $request - request object.
395 * @return WP_Error|WP_REST_Response
396 */
397 public static function get_threads( $request ) {
398
399 $current_user = WPSC_Current_User::$current_user;
400
401 $ticket = new WPSC_Ticket( $request->get_param( 'id' ) );
402 if ( ! $ticket->id || ! $ticket->is_active ) {
403 return new WP_REST_Response( new WP_Error( 'unauthorized', 'You are not authorized!' ), 401 );
404 }
405
406 WPSC_Individual_Ticket::$ticket = $ticket;
407 if ( ! (
408 ( $current_user->is_agent && WPSC_Individual_Ticket::has_ticket_cap( 'view' ) ) ||
409 ( WPSC_Individual_Ticket::is_customer() )
410 ) ) {
411 return new WP_REST_Response( new WP_Error( 'unauthorized', 'You are not authorized!' ), 401 );
412 }
413
414 $filters = array(
415 'page_no' => $request->get_param( 'page' ),
416 'items_per_page' => $request->get_param( 'per_page' ),
417 'order' => $request->get_param( 'order' ),
418 'meta_query' => array(
419 'relation' => 'AND',
420 array(
421 'slug' => 'ticket',
422 'compare' => '=',
423 'val' => $ticket->id,
424 ),
425 array(
426 'slug' => 'is_active',
427 'compare' => '=',
428 'val' => 1,
429 ),
430 ),
431 );
432
433 $type = array(
434 'slug' => 'type',
435 'compare' => 'IN',
436 'val' => array( 'report', 'reply' ),
437 );
438
439 // private note.
440 if ( $current_user->is_agent && WPSC_Individual_Ticket::has_ticket_cap( 'pn' ) ) {
441 $type['val'][] = 'note';
442 }
443
444 // logs.
445 if ( $current_user->is_agent && WPSC_Individual_Ticket::has_ticket_cap( 'vl' ) ) {
446 $type['val'][] = 'log';
447 }
448
449 $filters['meta_query'][] = $type;
450 $response = WPSC_Thread::find( $filters, false );
451
452 return new WP_REST_Response( self::modify_thread_response( $response, $ticket, $current_user ), 200 );
453 }
454
455 /**
456 * Add new thread
457 *
458 * @param WP_REST_Request $request - request object.
459 * @return WP_Error|WP_REST_Response
460 */
461 public static function add_new_thread( $request ) {
462
463 $current_user = WPSC_Current_User::$current_user;
464
465 $ticket = new WPSC_Ticket( $request->get_param( 'id' ) );
466 if ( ! $ticket->id || ! $ticket->is_active ) {
467 return new WP_REST_Response( new WP_Error( 'unauthorized', 'You are not authorized!' ), 401 );
468 }
469
470 WPSC_Individual_Ticket::$ticket = $ticket;
471 $type = $request->get_param( 'type' );
472 $body = $request->get_param( 'body' );
473 $source = $request->get_param( 'source' );
474 $attachments = $request->get_param( 'attachments' );
475
476 // Set reply profile.
477 WPSC_Individual_Ticket::$reply_profile = 'agent';
478 if ( WPSC_Individual_Ticket::is_customer() ) {
479 WPSC_Individual_Ticket::$reply_profile = 'customer';
480 }
481
482 if ( ! (
483 ( $current_user->is_agent && $type == 'reply' && WPSC_Individual_Ticket::has_ticket_cap( 'reply' ) ) ||
484 ( $current_user->is_agent && $type == 'note' && WPSC_Individual_Ticket::has_ticket_cap( 'pn' ) ) ||
485 ( WPSC_Individual_Ticket::is_customer() && $type == 'reply' )
486 ) ) {
487 return new WP_REST_Response( new WP_Error( 'unauthorized', 'You are not authorized!' ), 401 );
488 }
489
490 // submit thread.
491 $thread = WPSC_Thread::insert(
492 array(
493 'ticket' => $ticket->id,
494 'customer' => $current_user->customer->id,
495 'type' => $type,
496 'body' => $body,
497 'attachments' => implode( '|', $attachments ),
498 'ip_address' => WPSC_DF_IP_Address::get_current_user_ip(),
499 'source' => $source,
500 )
501 );
502
503 // save attachments.
504 foreach ( $attachments as $id ) {
505
506 $attachment = new WPSC_Attachment( $id );
507 $attachment->is_active = 1;
508 $attachment->source = $type;
509 $attachment->source_id = $thread->id;
510 $attachment->ticket_id = $ticket->id;
511 $attachment->save();
512 }
513
514 $ticket->date_updated = new DateTime();
515 $ticket->last_reply_on = new DateTime();
516 $ticket->last_reply_by = $current_user->customer->id;
517 $ticket->last_reply_source = $source;
518 $ticket->save();
519
520 if ( $type == 'reply' ) {
521 do_action( 'wpsc_post_reply', $thread );
522 } else {
523 do_action( 'wpsc_submit_note', $thread );
524 }
525
526 $response = self::modify_thread_response( array( 'results' => array( $thread->to_array() ) ), $ticket, $current_user );
527 return new WP_REST_Response( $response['results'][0], 200 );
528 }
529
530 /**
531 * Modify ticket response data appropreate for client side
532 *
533 * @param array $ticket - ticket response array.
534 * @return array
535 */
536 public static function modify_response( $ticket ) {
537
538 $current_user = WPSC_Current_User::$current_user;
539
540 foreach ( $ticket as $slug => $value ) {
541
542 // remove prevent ticket data.
543 if ( in_array( $slug, self::$prevent_data ) ) {
544 unset( $ticket[ $slug ] );
545 continue;
546 }
547
548 $cf = WPSC_Custom_Field::get_cf_by_slug( $slug );
549
550 // ignore if no custom field type class is loaded.
551 if ( ! $cf ) {
552 unset( $ticket[ $slug ] );
553 continue;
554 }
555
556 // ignore agentonly fields for non-agent.
557 if ( ! $current_user->is_agent && $cf->field == 'agentonly' ) {
558 unset( $ticket[ $slug ] );
559 continue;
560 }
561
562 // convert has multiple values to array.
563 if ( WPSC_Ticket::$schema[ $slug ]['has_multiple_val'] ) {
564 if ( $value ) {
565 $ticket[ $slug ] = array_filter(
566 array_map(
567 fn( $val ) => is_numeric( $val ) ? intval( $val ) : $val,
568 explode( '|', $value )
569 )
570 );
571 } else {
572 $ticket[ $slug ] = array();
573 }
574 continue;
575 }
576
577 // empty date value.
578 if ( $value == '0000-00-00 00:00:00' ) {
579 $ticket[ $slug ] = '';
580 }
581
582 // cast numeric fields into integer.
583 if ( is_numeric( $value ) ) {
584 $ticket[ $slug ] = intval( $value );
585 }
586 }
587
588 return apply_filters( 'wpsc_rest_modify_ticket_response', $ticket );
589 }
590
591 /**
592 * Modify thread response
593 *
594 * @param array $response - response array.
595 * @param WPSC_Ticket $ticket - ticket object.
596 * @param WPSC_Current_User $current_user - current user object.
597 * @return array
598 */
599 public static function modify_thread_response( $response, $ticket, $current_user ) {
600
601 if ( $response['results'] ) {
602
603 $log_items = apply_filters(
604 'wpsc_rest_allowed_thread_log_items',
605 array( 'id', 'customer', 'type', 'body', 'date_created' )
606 );
607
608 $reply_items = apply_filters(
609 'wpsc_rest_allowed_thread_reply_items',
610 array( 'id', 'customer', 'type', 'body', 'attachments', 'seen', 'date_created' )
611 );
612
613 $response['results'] = array_map(
614 function ( $thread ) use ( $log_items, $reply_items ) {
615 $items = $thread['type'] == 'log' ? $log_items : $reply_items;
616 $temp = array();
617 foreach ( $items as $item ) {
618 switch ( $item ) {
619 case 'body':
620 if ( $thread['type'] == 'log' ) {
621 $body = json_decode( $thread[ $item ], true );
622 // Handle 'prev' field.
623 if ( isset( $body['prev'] ) ) {
624 if ( is_array( $body['prev'] ) ) {
625 $body['prev'] = implode( ',', $body['prev'] );
626 }
627 if ( is_string( $body['prev'] ) && strpos( $body['prev'], '|' ) !== false ) {
628 $body['prev'] = str_replace( '|', ',', $body['prev'] );
629 }
630 }
631 // Handle 'new' field.
632 if ( isset( $body['new'] ) ) {
633 if ( is_array( $body['new'] ) ) {
634 $body['new'] = implode( ',', $body['new'] );
635 }
636 if ( is_string( $body['new'] ) && strpos( $body['new'], '|' ) !== false ) {
637 $body['new'] = str_replace( '|', ',', $body['new'] );
638 }
639 }
640 $temp[ $item ] = $body;
641 } else {
642 $temp[ $item ] = $thread[ $item ];
643 }
644 break;
645 case 'attachments':
646 $temp[ $item ] = str_replace( '|', ',', $thread[ $item ] );
647 break;
648 default:
649 $temp[ $item ] = $thread[ $item ];
650 }
651 }
652 return $temp;
653 },
654 $response['results']
655 );
656 }
657
658 return $response;
659 }
660
661 /**
662 * Validate id
663 *
664 * @param string $param - parameter value.
665 * @param WP_REST_Request $request - request object.
666 * @param string $key - filter key.
667 * @return boolean
668 */
669 public static function validate_id( $param, $request, $key ) {
670
671 $error = new WP_Error( 'invalid_id', 'Invalid ticket id', array( 'status' => 400 ) );
672 $ticket = new WPSC_Ticket( $param );
673 return $ticket->id ? true : $error;
674 }
675 }
676 endif;
677
678 WPSC_REST_Individual_Ticket::init();
679