enqueue_wizard(); } /** * The wizard's script, styles and data. The Lead Funnels page loads them, and so does the dashboard, whose bar opens * the same wizard without leaving the page. */ public function enqueue_wizard() { static $done = false; if ( $done ) { return; } $done = true; $css = ACQUIS_PLUGIN_DIR . 'admin/funnels/acquis-funnels.css'; $js = ACQUIS_PLUGIN_DIR . 'admin/funnels/acquis-funnels.js'; wp_enqueue_style( 'acquis-funnels', ACQUIS_PLUGIN_URL . 'admin/funnels/acquis-funnels.css', array( 'dashicons' ), acquis_asset_ver( $css ) ); wp_enqueue_script( 'acquis-funnels', ACQUIS_PLUGIN_URL . 'admin/funnels/acquis-funnels.js', array( 'jquery' ), acquis_asset_ver( $js ), true ); $templates = array(); foreach ( ACQUIS_Funnel_Templates::all() as $id => $t ) { $templates[] = array( 'id' => $id, 'label' => $t['label'], 'description' => $t['description'] ?? '', 'icon' => $t['icon'] ?? 'dashicons-admin-generic', 'needs_file' => ! empty( $t['needs_file'] ), 'defaults' => $t['defaults'], 'extra' => array_values( (array) ( $t['extra'] ?? array() ) ), 'sections' => wp_parse_args( (array) ( $t['sections'] ?? array() ), array( 'after' => 1, 'email' => 1, 'notify' => 1 ) ), 'prefill_redirect' => (string) ( $t['prefill_redirect'] ?? '' ), 'notify_default' => ! empty( $t['notify_default'] ) ? 1 : 0, 'pro_defaults' => (array) ( $t['pro_defaults'] ?? array() ), 'notes' => (array) ( $t['notes'] ?? array() ), 'no_display' => ! empty( $t['needs_file'] ), // 'resource' puts the template's own questions on the file screen, under the file they depend on. 'extra_screen' => (string) ( $t['extra_screen'] ?? '' ), 'hide_details' => ! empty( $t['hide_details'] ), 'file_sub' => (string) ( $t['file_sub'] ?? '' ), // what the review says about the file, when it is not "reused and not changed" ); } $files = array(); foreach ( (array) ACQUIS_File_Manager::get_all_files() as $f ) { $f = is_object( $f ) ? (array) $f : (array) $f; $fid = (int) ( $f['id'] ?? $f['ID'] ?? 0 ); $title = (string) ( $f['title'] ?? $f['post_title'] ?? '' ); if ( $fid ) { $files[] = array( 'id' => $fid, 'title' => $title ); } } $forms = array(); foreach ( ACQUIS_P3_DB::get_forms() as $f ) { $forms[] = array( 'id' => (int) $f->id, 'title' => $f->title ); } $std = array(); foreach ( ACQUIS_Funnel_Templates::standard_fields() as $k => $def ) { $std[] = array( 'key' => $k, 'label' => $def['label'], 'required' => ! empty( $def['required'] ) && 'email' === $k ); } $config = array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( self::NONCE ), 'templates' => $templates, 'files' => $files, 'forms' => $forms, 'fields' => $std, 'adminEmail' => (string) get_option( 'admin_email' ), 'pro' => ACQUIS_P3_Admin::is_pro_active(), 'showNote' => ! ACQUIS_P3_Admin::is_pro_active() && ! get_user_meta( get_current_user_id(), self::NOTE, true ), 'addonsUrl' => admin_url( 'admin.php?page=acquis-addons' ), 'listUrl' => admin_url( 'admin.php?page=' . self::PAGE ), 'filesUrl' => admin_url( 'admin.php?page=acquisitionsuite-files' ), 'displays' => array_values( array_intersect( array( 'embedded', 'popup', 'floating', 'slidein' ), (array) apply_filters( 'acquis_form_display_styles', array( 'embedded', 'popup' ) ) ) ), 'i18n' => $this->strings(), ); /** * The data the wizard script gets. Pro adds its own (lists of tags, sequences, pipelines) here. * * @param array $config */ $config = apply_filters( 'acquis_funnel_wizard_config', $config ); wp_localize_script( 'acquis-funnels', 'ACQUIS_FUNNELS', $config ); /** * The wizard's script was enqueued (on the Lead Funnels page or the dashboard). Pro adds its own script here. */ do_action( 'acquis_funnel_wizard_enqueued' ); } private function strings() { return array( 'create' => __( 'Create funnel', 'acquisitionsuite' ), 'save' => __( 'Save changes', 'acquisitionsuite' ), 'cancel' => __( 'Cancel', 'acquisitionsuite' ), 'back' => __( 'Back', 'acquisitionsuite' ), 'continue' => __( 'Continue', 'acquisitionsuite' ), 'close' => __( 'Close', 'acquisitionsuite' ), 'useTemplate' => __( 'Use template', 'acquisitionsuite' ), 'pickTitle' => __( 'Build a lead funnel in a few minutes', 'acquisitionsuite' ), 'pickSub' => __( 'What do you want to achieve?', 'acquisitionsuite' ), 'basicsSub' => __( 'Let\'s get started.', 'acquisitionsuite' ), 'name' => __( 'Funnel name', 'acquisitionsuite' ), 'nameHelp' => __( 'Only you see this. It names the funnel and its form.', 'acquisitionsuite' ), 'headline' => __( 'Headline', 'acquisitionsuite' ), 'headlineHelp' => __( 'Shown above the form.', 'acquisitionsuite' ), 'description' => __( 'Description', 'acquisitionsuite' ), 'resourceTitle' => __( 'Resource and form', 'acquisitionsuite' ), 'resourceSub' => __( 'What do people get, and what do you ask for?', 'acquisitionsuite' ), 'resource' => __( 'Resource to deliver', 'acquisitionsuite' ), 'noFiles' => __( 'You have no uploaded files yet.', 'acquisitionsuite' ), 'uploadFile' => __( 'Upload a file', 'acquisitionsuite' ), 'chooseFile' => __( 'Choose a file…', 'acquisitionsuite' ), 'resourceHelp' => __( 'Delivery follows your delivery settings (instant, email, or both).', 'acquisitionsuite' ), 'form' => __( 'Form', 'acquisitionsuite' ), 'createForm' => __( 'Create a form', 'acquisitionsuite' ), 'createFormSub' => __( 'Built for this funnel', 'acquisitionsuite' ), 'existingForm' => __( 'Use an existing form', 'acquisitionsuite' ), 'existingSub' => __( 'Reused, never changed', 'acquisitionsuite' ), 'chooseForm' => __( 'Choose a form…', 'acquisitionsuite' ), 'fields' => __( 'Fields', 'acquisitionsuite' ), 'emailRequired' => __( 'always asked', 'acquisitionsuite' ), 'addField' => __( 'Add a custom field', 'acquisitionsuite' ), 'fieldLabel' => __( 'Field label', 'acquisitionsuite' ), 'removeField' => __( 'Remove field', 'acquisitionsuite' ), 'how' => __( 'How it appears', 'acquisitionsuite' ), 'embedded' => __( 'Embedded on a page', 'acquisitionsuite' ), 'popup' => __( 'Popup', 'acquisitionsuite' ), 'floating' => __( 'Floating button', 'acquisitionsuite' ), 'slidein' => __( 'Slide-in panel', 'acquisitionsuite' ), 'slideDelay' => __( 'Open by itself after (seconds)', 'acquisitionsuite' ), 'slideHelp' => __( 'A small tab stays on the edge of the page. Use 0 to open it only when it is clicked. Visitors who close it are not shown it again for a week.', 'acquisitionsuite' ), 'sectionsOff' => __( 'This kind of funnel is paid for at checkout, so the message, email and notification come from your payment settings.', 'acquisitionsuite' ), 'details' => __( 'Details', 'acquisitionsuite' ), 'styleNote' => __( 'The form uses the default style from your plugin settings.', 'acquisitionsuite' ), 'refreshForm' => __( 'Reload fields from Form Builder', 'acquisitionsuite' ), 'fieldType' => __( 'Type:', 'acquisitionsuite' ), 'mergeNote' => __( 'Changes made to this form in Form Builder are kept. Only what you change here is applied to it.', 'acquisitionsuite' ), /* translators: %d: number of fields */ 'othersKept' => __( '%d field(s) here are dropdowns, checkboxes or other types made in Form Builder. You can rename or remove them here; their options are edited in Form Builder.', 'acquisitionsuite' ), 'formLocked' => __( 'The form this funnel was built on cannot be changed here. To use a different form, create a new funnel.', 'acquisitionsuite' ), 'afterTitle' => __( 'After submission', 'acquisitionsuite' ), 'afterSub' => __( 'What happens when someone submits?', 'acquisitionsuite' ), 'showMessage' => __( 'Show a message', 'acquisitionsuite' ), 'redirect' => __( 'Redirect to a page', 'acquisitionsuite' ), 'message' => __( 'Message', 'acquisitionsuite' ), 'redirectUrl' => __( 'Address to send people to', 'acquisitionsuite' ), 'redirectNote' => __( 'If a file is delivered instantly, the download starts before the redirect.', 'acquisitionsuite' ), 'emailTitle' => __( 'Delivery email, for this funnel only', 'acquisitionsuite' ), 'emailCustom' => __( 'Write a custom email for this funnel', 'acquisitionsuite' ), 'emailGlobal' => __( 'Off: this funnel uses your global email template from Email Settings.', 'acquisitionsuite' ), 'emailSubject' => __( 'Subject', 'acquisitionsuite' ), 'emailBody' => __( 'Message', 'acquisitionsuite' ), 'emailTags' => __( 'You can use {full_name}, {email}, {file_name} and {site_name}. The download button is added for you when a file is delivered.', 'acquisitionsuite' ), 'notify' => __( 'Email me when someone signs up', 'acquisitionsuite' ), 'notifyTo' => __( 'Send it to', 'acquisitionsuite' ), 'proNote' => __( 'Pro can also tag leads, start email sequences and move them through your pipeline.', 'acquisitionsuite' ), 'dismiss' => __( 'Dismiss', 'acquisitionsuite' ), 'learnMore' => __( 'See what Pro adds', 'acquisitionsuite' ), 'reviewTitle' => __( 'Review', 'acquisitionsuite' ), 'reviewSub' => __( 'Everything is ready. AcquisitionSuite will:', 'acquisitionsuite' ), 'rvForm' => __( 'Create a lead capture form', 'acquisitionsuite' ), 'rvFormNew' => __( 'New. It appears under Form Builder.', 'acquisitionsuite' ), 'rvFormUse' => __( 'Use your existing form', 'acquisitionsuite' ), 'rvFormUseSub' => __( 'Reused and not changed.', 'acquisitionsuite' ), 'rvFile' => __( 'Deliver your resource', 'acquisitionsuite' ), 'rvFileSub' => __( 'Reused and not changed.', 'acquisitionsuite' ), 'rvEmail' => __( 'Send a custom delivery email', 'acquisitionsuite' ), 'rvEmailSub' => __( 'For this funnel only.', 'acquisitionsuite' ), 'rvNotify' => __( 'Notify you of new leads', 'acquisitionsuite' ), 'rvAfter' => __( 'Show a message after submission', 'acquisitionsuite' ), 'rvRedirect' => __( 'Redirect to your page after submission', 'acquisitionsuite' ), 'rvTrack' => __( 'Track leads and analytics', 'acquisitionsuite' ), 'rvTrackSub' => __( 'Filtered to this funnel.', 'acquisitionsuite' ), 'rvPages' => __( 'Nothing on your pages is changed. You will get a shortcode to place where you want the form.', 'acquisitionsuite' ), 'working' => __( 'Creating your funnel…', 'acquisitionsuite' ), 'workingEdit' => __( 'Saving your funnel…', 'acquisitionsuite' ), 'undo' => __( 'If any step fails, everything created so far is undone.', 'acquisitionsuite' ), 'stepForm' => __( 'Creating the form', 'acquisitionsuite' ), 'stepResource' => __( 'Checking your resource', 'acquisitionsuite' ), 'stepPublish' => __( 'Publishing', 'acquisitionsuite' ), 'stepSaving' => __( 'Saving changes', 'acquisitionsuite' ), 'failed' => __( 'Nothing was created. Please try again.', 'acquisitionsuite' ), 'tryAgain' => __( 'Back to review', 'acquisitionsuite' ), 'readyTitle' => __( 'Your funnel is ready', 'acquisitionsuite' ), 'savedTitle' => __( 'Your funnel is saved', 'acquisitionsuite' ), 'shortcode' => __( 'Shortcode', 'acquisitionsuite' ), 'copy' => __( 'Copy', 'acquisitionsuite' ), 'copied' => __( 'Copied', 'acquisitionsuite' ), 'addToPage' => __( 'Add it to a page', 'acquisitionsuite' ), 'how1' => __( 'Copy the shortcode.', 'acquisitionsuite' ), 'how2' => __( 'Edit any page or post.', 'acquisitionsuite' ), 'how3' => __( 'Add a Shortcode block, paste it, and update the page.', 'acquisitionsuite' ), 'howAlt' => __( 'You can also use the AcquisitionSuite form block, the Elementor widget or the Bricks element.', 'acquisitionsuite' ), 'openForm' => __( 'Open the form', 'acquisitionsuite' ), 'viewLeads' => __( 'View leads', 'acquisitionsuite' ), 'viewReport' => __( 'View analytics', 'acquisitionsuite' ), 'editFunnel' => __( 'Edit funnel', 'acquisitionsuite' ), 'allFunnels' => __( 'All funnels', 'acquisitionsuite' ), 'notChanged' => __( 'Your pages were not changed. Add the shortcode where you want the form to appear.', 'acquisitionsuite' ), 'conflictTitle' => __( 'The form was changed outside this funnel', 'acquisitionsuite' ), 'conflictText' => __( 'Someone edited this funnel\'s form in Form Builder. Do you want to replace those changes with the funnel\'s settings, or keep them and only update the funnel?', 'acquisitionsuite' ), 'overwrite' => __( 'Replace the form', 'acquisitionsuite' ), 'keep' => __( 'Keep my form changes', 'acquisitionsuite' ), 'deleteTitle' => __( 'Delete this funnel?', 'acquisitionsuite' ), 'deleteSub' => __( 'Leads and submissions are always kept. Choose what happens to the rest.', 'acquisitionsuite' ), 'deleteForm' => __( 'Delete the form this funnel created', 'acquisitionsuite' ), 'deleteRule' => __( 'Delete the automation rule this funnel created', 'acquisitionsuite' ), 'restorePrice' => __( 'Put the file back as it was (it was priced by this funnel)', 'acquisitionsuite' ), 'formUsed' => __( 'Pages that use its shortcode will stop showing it.', 'acquisitionsuite' ), 'reusedKept' => __( 'is reused, so it is never deleted', 'acquisitionsuite' ), 'deleteFunnel' => __( 'Delete funnel', 'acquisitionsuite' ), 'errName' => __( 'Enter a name for this funnel.', 'acquisitionsuite' ), 'errFill' => __( 'Fill in:', 'acquisitionsuite' ), 'errFile' => __( 'Choose the file people should receive.', 'acquisitionsuite' ), 'errForm' => __( 'Choose one of your existing forms.', 'acquisitionsuite' ), 'errUrl' => __( 'Enter the address people should be sent to, starting with https://.', 'acquisitionsuite' ), 'errEmail' => __( 'Write a subject and a message, or switch the custom email off.', 'acquisitionsuite' ), 'errNotify' => __( 'Enter a valid email address.', 'acquisitionsuite' ), 'error' => __( 'Something went wrong. Please try again.', 'acquisitionsuite' ), 'repaired' => __( 'Repaired', 'acquisitionsuite' ), ); } // ── Pages ────────────────────────────────────────────────────────────── public function render() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Insufficient permissions.', 'acquisitionsuite' ), 403 ); } ACQUIS_Funnels::maybe_install(); // phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only screen selection $view = isset( $_GET['view'] ) ? sanitize_key( wp_unslash( $_GET['view'] ) ) : ''; $id = isset( $_GET['funnel'] ) ? absint( $_GET['funnel'] ) : 0; $days = isset( $_GET['days'] ) ? absint( $_GET['days'] ) : 30; // phpcs:enable ?>
v
render_report( $id, $days ); } else { $this->render_list(); } echo '
'; } private function status_label( $status ) { $map = array( 'active' => array( __( 'Active', 'acquisitionsuite' ), 'is-ok' ), 'needs_repair' => array( __( 'Needs repair', 'acquisitionsuite' ), 'is-warn' ), 'draft' => array( __( 'Draft', 'acquisitionsuite' ), '' ), ); return isset( $map[ $status ] ) ? $map[ $status ] : array( $status, '' ); } private function render_list() { $funnels = ACQUIS_Funnels::all(); $tpls = ACQUIS_Funnel_Templates::all(); ?>

form_id, $f->file_id, 30 ); list( $label, $cls ) = $this->status_label( $f->status ); $problems = ACQUIS_Funnels::problems( $f ); $tplname = isset( $tpls[ $f->template ] ) ? $tpls[ $f->template ]['label'] : $f->template; $sum = ACQUIS_Funnel_Builder::summary( $f->id ); // What is wrong, in words. Pro adds its own codes through the filter. $labels = (array) apply_filters( 'acquis_funnel_problem_labels', array( 'form_missing' => __( 'The form was deleted.', 'acquisitionsuite' ), 'file_missing' => __( 'The resource file was deleted. Edit the funnel to choose another.', 'acquisitionsuite' ), ) ); // A file cannot be recreated; everything else can be repaired with the button. $repairable = array_diff( $problems, array( 'file_missing' ) ); ?>
name ); ?>
' . esc_html__( 'That funnel no longer exists.', 'acquisitionsuite' ) . ' ' . esc_html__( 'All funnels', 'acquisitionsuite' ) . '

'; return; } $days = in_array( $days, array( 7, 30, 90, 365 ), true ) ? $days : 30; $s = ACQUIS_Funnels::stats( $f->form_id, $f->file_id, $days ); $sum = ACQUIS_Funnel_Builder::summary( $f->id ); $max = 1; foreach ( $s['daily'] as $d ) { $max = max( $max, (int) $d->n ); } $base = add_query_arg( array( 'page' => self::PAGE, 'funnel' => (int) $f->id, 'view' => 'report' ), admin_url( 'admin.php' ) ); ?>

←

name ); ?>

__( '7 days', 'acquisitionsuite' ), 30 => __( '30 days', 'acquisitionsuite' ), 90 => __( '90 days', 'acquisitionsuite' ), 365 => __( '1 year', 'acquisitionsuite' ) ) as $d => $lbl ) : ?>

__( 'Insufficient permissions.', 'acquisitionsuite' ) ), 403 ); } ACQUIS_Funnels::maybe_install(); } /** The decoded JSON "answers" the wizard posts. */ private function posted_answers() { // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- nonce checked in guard(); decoded JSON is sanitized by ACQUIS_Funnel_Templates::sanitize() $raw = isset( $_POST['answers'] ) ? json_decode( wp_unslash( (string) $_POST['answers'] ), true ) : array(); return is_array( $raw ) ? $raw : array(); } private function fail( $err ) { $data = array( 'message' => $err->get_error_message() ); $d = $err->get_error_data(); if ( is_array( $d ) && ! empty( $d['field'] ) ) { $data['field'] = sanitize_key( $d['field'] ); } wp_send_json_error( $data ); } public function ajax_options() { $this->guard(); wp_send_json_success( array() ); } public function ajax_start() { $this->guard(); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce checked in guard() $template = isset( $_POST['template'] ) ? sanitize_key( wp_unslash( $_POST['template'] ) ) : ''; $res = ACQUIS_Funnel_Builder::start( $template, $this->posted_answers() ); if ( is_wp_error( $res ) ) { $this->fail( $res ); } wp_send_json_success( $res ); } public function ajax_step() { $this->guard(); // phpcs:disable WordPress.Security.NonceVerification.Missing -- nonce checked in guard() $token = isset( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : ''; $step = isset( $_POST['step'] ) ? sanitize_key( wp_unslash( $_POST['step'] ) ) : ''; // phpcs:enable $res = ACQUIS_Funnel_Builder::run_step( $token, $step ); if ( is_wp_error( $res ) ) { $this->fail( $res ); } wp_send_json_success( $res ); } public function ajax_rollback() { $this->guard(); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce checked in guard() $token = isset( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : ''; wp_send_json_success( array( 'undone' => ACQUIS_Funnel_Builder::rollback( $token ) ) ); } public function ajax_get() { $this->guard(); $f = ACQUIS_Funnels::get( isset( $_POST['id'] ) ? absint( $_POST['id'] ) : 0 ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce checked in guard() if ( ! $f ) { wp_send_json_error( array( 'message' => __( 'That funnel no longer exists.', 'acquisitionsuite' ) ) ); } $f = ACQUIS_Funnels::sync_status( $f ); $obj = ACQUIS_Funnels::object( $f->id, 'form' ); wp_send_json_success( array( 'id' => (int) $f->id, 'template' => $f->template, // What the form looks like now, so changes made in Form Builder show here instead of being undone on save. 'config' => $obj && (int) $obj->owned ? ACQUIS_Funnel_Builder::answers_from_form( $f ) : $f->config, 'others' => $obj && (int) $obj->owned ? ACQUIS_Funnel_Builder::untouched_field_count( $f ) : 0, 'form_id' => (int) $f->form_id, 'owned' => $obj ? (int) $obj->owned : 0, 'summary' => ACQUIS_Funnel_Builder::summary( $f->id ), ) ); } public function ajax_update() { $this->guard(); // phpcs:disable WordPress.Security.NonceVerification.Missing -- nonce checked in guard() $id = isset( $_POST['id'] ) ? absint( $_POST['id'] ) : 0; $mode = isset( $_POST['mode'] ) ? sanitize_key( wp_unslash( $_POST['mode'] ) ) : ''; // phpcs:enable $res = ACQUIS_Funnel_Builder::update( $id, $this->posted_answers(), $mode ); if ( is_wp_error( $res ) ) { $this->fail( $res ); } if ( ! empty( $res['conflict'] ) ) { wp_send_json_success( array( 'conflict' => true ) ); } wp_send_json_success( array( 'conflict' => false, 'summary' => ACQUIS_Funnel_Builder::summary( $id ) ) ); } public function ajax_deletable() { $this->guard(); $id = isset( $_POST['id'] ) ? absint( $_POST['id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce checked in guard() $f = ACQUIS_Funnels::get( $id ); if ( ! $f ) { wp_send_json_error( array( 'message' => __( 'That funnel no longer exists.', 'acquisitionsuite' ) ) ); } wp_send_json_success( array( 'name' => $f->name, 'objects' => ACQUIS_Funnel_Builder::deletable( $id ) ) ); } public function ajax_delete() { $this->guard(); // phpcs:disable WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- nonce checked in guard(); each role is sanitized below $id = isset( $_POST['id'] ) ? absint( $_POST['id'] ) : 0; $roles = isset( $_POST['delete'] ) && is_array( $_POST['delete'] ) ? array_map( 'sanitize_key', wp_unslash( $_POST['delete'] ) ) : array(); // phpcs:enable if ( ! ACQUIS_Funnel_Builder::delete( $id, $roles ) ) { wp_send_json_error( array( 'message' => __( 'That funnel no longer exists.', 'acquisitionsuite' ) ) ); } wp_send_json_success( array( 'deleted' => true ) ); } public function ajax_repair() { $this->guard(); $res = ACQUIS_Funnel_Builder::repair( isset( $_POST['id'] ) ? absint( $_POST['id'] ) : 0 ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce checked in guard() if ( is_wp_error( $res ) ) { $this->fail( $res ); } wp_send_json_success( $res ); } public function ajax_dismiss_bar() { $this->guard(); update_user_meta( get_current_user_id(), self::BAR, 1 ); wp_send_json_success(); } public function ajax_dismiss_note() { $this->guard(); update_user_meta( get_current_user_id(), self::NOTE, 1 ); wp_send_json_success(); } }