post_type ? __( 'Form name, for example Contact us', 'nonprofit-forms' ) : $text; } /** * List table columns. * * @param array $cols Columns. * @return array */ public static function columns( $cols ) { return array( 'cb' => $cols['cb'], 'title' => __( 'Form', 'nonprofit-forms' ), 'shortcode' => __( 'Shortcode', 'nonprofit-forms' ), 'date' => $cols['date'], ); } /** * Column output. * * @param string $col Column. * @param int $post_id Post ID. * @return void */ public static function column_content( $col, $post_id ) { if ( 'shortcode' === $col ) { echo '[nonprofit_form id="' . (int) $post_id . '"]'; } } /** * Which forms can the current user read entries for? * * @return array{all:bool,ids:int[]} */ public static function allowed_forms() { if ( current_user_can( 'manage_options' ) ) { return array( 'all' => true, 'ids' => array(), ); } $ids = array(); $user = wp_get_current_user(); $posts = get_posts( array( 'post_type' => NPFRM_Form::POST_TYPE, 'post_status' => 'publish', 'posts_per_page' => 100, 'fields' => 'ids', ) ); foreach ( $posts as $id ) { $form = NPFRM_Form::get( $id ); if ( $form && array_intersect( (array) $form['settings']['entry_roles'], (array) $user->roles ) ) { $ids[] = (int) $id; } } return array( 'all' => false, 'ids' => $ids, ); } /** * May the current user read this form's entries? * * @param int $form_id Form ID. * @return bool */ public static function can_view_form( $form_id ) { $allowed = self::allowed_forms(); return $allowed['all'] || in_array( (int) $form_id, $allowed['ids'], true ); } /** * Entries screen address for the current user. * * @return string */ public static function entries_url() { return current_user_can( 'manage_options' ) ? admin_url( 'edit.php?post_type=' . NPFRM_Form::POST_TYPE . '&page=npfrm-entries' ) : admin_url( 'admin.php?page=npfrm-entries' ); } /** * Menu entries. * * @return void */ public static function menu() { $parent = 'edit.php?post_type=' . NPFRM_Form::POST_TYPE; if ( current_user_can( 'manage_options' ) ) { $unread = NPFRM_Entries::unread_count(); $badge = $unread ? ' ' . (int) $unread . '' : ''; add_submenu_page( $parent, __( 'Entries', 'nonprofit-forms' ), __( 'Entries', 'nonprofit-forms' ) . $badge, 'manage_options', 'npfrm-entries', array( __CLASS__, 'entries_page' ) ); add_submenu_page( $parent, __( 'Analytics', 'nonprofit-forms' ), __( 'Analytics', 'nonprofit-forms' ), 'manage_options', 'npfrm-analytics', array( __CLASS__, 'analytics_page' ) ); add_submenu_page( $parent, __( 'Settings', 'nonprofit-forms' ), __( 'Settings', 'nonprofit-forms' ), 'manage_options', 'npfrm-settings', array( __CLASS__, 'settings_page' ) ); return; } $allowed = self::allowed_forms(); if ( ! empty( $allowed['ids'] ) ) { add_menu_page( __( 'Form entries', 'nonprofit-forms' ), __( 'Form entries', 'nonprofit-forms' ), 'read', 'npfrm-entries', array( __CLASS__, 'entries_page' ), 'dashicons-feedback', 58 ); } } /** * Settings API. * * @return void */ public static function register_settings() { register_setting( 'npfrm_settings', 'npfrm_retention_days', array( 'type' => 'integer', 'sanitize_callback' => 'absint', 'default' => 0, ) ); register_setting( 'npfrm_settings', 'npfrm_rate_limit', array( 'type' => 'integer', 'sanitize_callback' => array( __CLASS__, 'sanitize_rate' ), 'default' => 5, ) ); register_setting( 'npfrm_settings', 'npfrm_block_disposable', array( 'type' => 'integer', 'sanitize_callback' => array( __CLASS__, 'sanitize_flag' ), 'default' => 1, ) ); register_setting( 'npfrm_settings', 'npfrm_spam_keywords', array( 'type' => 'string', 'sanitize_callback' => 'sanitize_textarea_field', 'default' => '', ) ); } /** * Rate limit: 1 to 50 attempts. * * @param mixed $v Value. * @return int */ public static function sanitize_rate( $v ) { return min( 50, max( 1, absint( $v ) ) ); } /** * Checkbox to 0 or 1. * * @param mixed $v Value. * @return int */ public static function sanitize_flag( $v ) { return empty( $v ) ? 0 : 1; } /** * Settings page. * * @return void */ public static function settings_page() { ?>

form_id ] = $row; } echo '

' . esc_html__( 'Form analytics', 'nonprofit-forms' ) . '

'; echo '

' . esc_html__( 'Last 30 days. These are plain totals kept on your own site. No cookies, no IP addresses and no outside tools. If a page cache serves your page, some views may not be counted.', 'nonprofit-forms' ) . '

'; if ( empty( $forms ) ) { echo '

' . esc_html__( 'No forms yet.', 'nonprofit-forms' ) . '

'; return; } echo ''; foreach ( $forms as $id => $title ) { $views = isset( $totals[ $id ] ) ? (int) $totals[ $id ]->views : 0; $subs = isset( $totals[ $id ] ) ? (int) $totals[ $id ]->submissions : 0; $rate = $views > 0 ? min( 100, round( $subs * 100 / $views ) ) : 0; echo ''; } echo '
' . esc_html__( 'Form', 'nonprofit-forms' ) . '' . esc_html__( 'Views', 'nonprofit-forms' ) . '' . esc_html__( 'Submitted', 'nonprofit-forms' ) . '' . esc_html__( 'Out of 100 visitors', 'nonprofit-forms' ) . '
' . esc_html( $title ) . '' . (int) $views . '' . (int) $subs . ''; echo ' ' . (int) $rate . '
'; } /** * Admin assets for the form editor only. * * @param string $hook Hook suffix. * @return void */ public static function assets( $hook ) { $screen = get_current_screen(); if ( ! $screen || NPFRM_Form::POST_TYPE !== $screen->post_type || ! in_array( $hook, array( 'post.php', 'post-new.php' ), true ) ) { return; } wp_enqueue_style( 'npfrm-admin', NPFRM_URL . 'assets/admin.css', array(), NPFRM_VERSION ); wp_enqueue_script( 'npfrm-admin', NPFRM_URL . 'assets/admin.js', array( 'wp-i18n' ), NPFRM_VERSION, true ); wp_set_script_translations( 'npfrm-admin', 'nonprofit-forms' ); $types = array(); foreach ( NPFRM_Fields::types() as $slug => $type ) { $types[] = array( 'slug' => $slug, 'label' => $type['label'], 'choices' => ! empty( $type['choices'] ), ); } wp_localize_script( 'npfrm-admin', 'npfrmAdmin', array( 'types' => $types ) ); } /** * Meta boxes. * * @return void */ public static function meta_boxes() { $t = NPFRM_Form::POST_TYPE; add_meta_box( 'npfrm_fields', __( 'Fields', 'nonprofit-forms' ), array( __CLASS__, 'box_fields' ), $t, 'normal', 'high' ); add_meta_box( 'npfrm_settings', __( 'Email and messages', 'nonprofit-forms' ), array( __CLASS__, 'box_settings' ), $t, 'normal', 'default' ); add_meta_box( 'npfrm_privacy', __( 'Privacy notice (DPDP)', 'nonprofit-forms' ), array( __CLASS__, 'box_privacy' ), $t, 'normal', 'default' ); add_meta_box( 'npfrm_access', __( 'Who can use this form and read entries', 'nonprofit-forms' ), array( __CLASS__, 'box_access' ), $t, 'normal', 'default' ); add_meta_box( 'npfrm_schedule', __( 'Open, close and limits', 'nonprofit-forms' ), array( __CLASS__, 'box_schedule' ), $t, 'normal', 'default' ); add_meta_box( 'npfrm_embed', __( 'Show this form', 'nonprofit-forms' ), array( __CLASS__, 'box_embed' ), $t, 'side', 'high' ); } /** * Current settings for a post. * * @param WP_Post $post Post. * @return array */ private static function settings_for( $post ) { $s = get_post_meta( $post->ID, '_npfrm_settings', true ); return wp_parse_args( is_array( $s ) ? $s : array(), NPFRM_Form::default_settings() ); } /** * Roles a site owner can pick (administrators always have access). * * @return array slug => name */ private static function role_choices() { $roles = wp_roles()->get_names(); unset( $roles['administrator'] ); return $roles; } /** * Print role checkboxes. * * @param string $name Input name. * @param array $selected Selected role slugs. * @return void */ private static function role_boxes( $name, $selected ) { foreach ( self::role_choices() as $slug => $label ) { echo ''; } } /** * Fields box. The JS builds the rows and writes JSON to the hidden input. * * @param WP_Post $post Post. * @return void */ public static function box_fields( $post ) { wp_nonce_field( 'npfrm_save_form', 'npfrm_nonce' ); $fields = json_decode( (string) get_post_meta( $post->ID, '_npfrm_fields', true ), true ); if ( ! is_array( $fields ) || empty( $fields ) ) { $fields = NPFRM_Form::default_fields(); } ?>




'npfrm_settings[privacy_page]', 'id' => 'npfrm_privacy_page', 'selected' => (int) $s['privacy_page'], 'show_option_none' => __( 'WordPress default privacy page (if set)', 'nonprofit-forms' ), 'option_none_value' => '0', ) ); ?>





post_status ) { esc_html_e( 'Publish the form to get its shortcode.', 'nonprofit-forms' ); return; } echo '

[nonprofit_form id="' . (int) $post->ID . '"]

'; echo '

' . esc_html__( 'Or add the "Nonprofit Form" block in the editor.', 'nonprofit-forms' ) . '

'; } /** * Clean a list of role slugs against real roles. * * @param mixed $roles Raw list. * @return string[] */ private static function clean_roles( $roles ) { $valid = array_keys( self::role_choices() ); return array_values( array_intersect( array_map( 'sanitize_key', (array) $roles ), $valid ) ); } /** * Clean a datetime-local value. * * @param mixed $v Raw. * @return string */ private static function clean_datetime( $v ) { $v = is_string( $v ) ? trim( $v ) : ''; return preg_match( '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/', $v ) ? $v : ''; } /** * Save the editor. * * @param int $post_id Post ID. * @return void */ public static function save( $post_id ) { if ( ! isset( $_POST['npfrm_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['npfrm_nonce'] ) ), 'npfrm_save_form' ) ) { return; } if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } if ( ! current_user_can( 'manage_options' ) ) { return; } if ( isset( $_POST['npfrm_fields_json'] ) ) { $json = sanitize_textarea_field( wp_unslash( $_POST['npfrm_fields_json'] ) ); $decoded = json_decode( $json, true ); // json_decode() transforms but does not sanitize; clean every decoded value before use. $decoded = is_array( $decoded ) ? map_deep( $decoded, 'sanitize_textarea_field' ) : array(); $fields = NPFRM_Fields::sanitize_definitions( $decoded ); update_post_meta( $post_id, '_npfrm_fields', wp_slash( wp_json_encode( $fields ) ) ); } // Sanitize on intake (textarea-safe, keeps line breaks), then validate per key below. $in = isset( $_POST['npfrm_settings'] ) && is_array( $_POST['npfrm_settings'] ) ? map_deep( wp_unslash( $_POST['npfrm_settings'] ), 'sanitize_textarea_field' ) : array(); $to = array(); foreach ( explode( ',', isset( $in['to'] ) ? (string) $in['to'] : '' ) as $addr ) { $addr = sanitize_email( trim( $addr ) ); if ( is_email( $addr ) ) { $to[] = $addr; } } $text = function ( $key ) use ( $in ) { return isset( $in[ $key ] ) ? sanitize_text_field( $in[ $key ] ) : ''; }; $access = $text( 'access' ); $contact = sanitize_email( $text( 'notice_contact' ) ); update_post_meta( $post_id, '_npfrm_settings', array( 'to' => $to ? implode( ', ', $to ) : get_option( 'admin_email' ), 'submit' => $text( 'submit' ), 'success' => $text( 'success' ), 'store' => ! empty( $in['store'] ), 'access' => in_array( $access, array( 'everyone', 'logged_in', 'roles' ), true ) ? $access : 'everyone', 'access_roles' => self::clean_roles( isset( $in['access_roles'] ) ? $in['access_roles'] : array() ), 'access_msg' => $text( 'access_msg' ), 'entry_roles' => self::clean_roles( isset( $in['entry_roles'] ) ? $in['entry_roles'] : array() ), 'open_from' => self::clean_datetime( isset( $in['open_from'] ) ? $in['open_from'] : '' ), 'close_at' => self::clean_datetime( isset( $in['close_at'] ) ? $in['close_at'] : '' ), 'max_entries' => isset( $in['max_entries'] ) ? absint( $in['max_entries'] ) : 0, 'closed_msg' => $text( 'closed_msg' ), 'notice_on' => ! empty( $in['notice_on'] ), 'notice_purpose' => $text( 'notice_purpose' ), 'notice_contact' => is_email( $contact ) ? $contact : '', 'privacy_page' => isset( $in['privacy_page'] ) ? absint( $in['privacy_page'] ) : 0, 'routing' => isset( $in['routing'] ) ? sanitize_textarea_field( $in['routing'] ) : '', 'reply_on' => ! empty( $in['reply_on'] ), 'reply_subject' => $text( 'reply_subject' ), 'reply_body' => isset( $in['reply_body'] ) ? sanitize_textarea_field( $in['reply_body'] ) : '', ) ); } /** * Entries screen. * * @return void */ public static function entries_page() { $allowed = self::allowed_forms(); if ( ! $allowed['all'] && empty( $allowed['ids'] ) ) { wp_die( esc_html__( 'You are not allowed to see entries.', 'nonprofit-forms' ) ); } // phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only filters. $entry_id = isset( $_GET['entry'] ) ? absint( $_GET['entry'] ) : 0; $form_id = isset( $_GET['form'] ) ? absint( $_GET['form'] ) : 0; $paged = isset( $_GET['paged'] ) ? max( 1, absint( $_GET['paged'] ) ) : 1; $search = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : ''; $status = isset( $_GET['status'] ) ? sanitize_key( wp_unslash( $_GET['status'] ) ) : ''; // phpcs:enable WordPress.Security.NonceVerification.Recommended $base = self::entries_url(); $forms = NPFRM_Form::choices(); if ( ! $allowed['all'] ) { $forms = array_intersect_key( $forms, array_flip( $allowed['ids'] ) ); } echo '

' . esc_html__( 'Entries', 'nonprofit-forms' ) . '

'; if ( $entry_id ) { self::entry_detail( $entry_id, $base, $forms ); echo '
'; return; } echo '
'; if ( current_user_can( 'manage_options' ) ) { echo ''; } echo ''; echo ' '; if ( $form_id && isset( $forms[ $form_id ] ) ) { $url = wp_nonce_url( admin_url( 'admin-post.php?action=npfrm_export&form=' . $form_id ), 'npfrm_export_' . $form_id ); echo ' ' . esc_html__( 'Download CSV', 'nonprofit-forms' ) . ''; } echo '
'; if ( $form_id && ! isset( $forms[ $form_id ] ) ) { $form_id = 0; } $ids = $form_id ? array( $form_id ) : ( $allowed['all'] ? array() : $allowed['ids'] ); $per = 20; $result = NPFRM_Entries::query( $ids, $per, $paged, $search, $status ); if ( empty( $result['rows'] ) ) { echo '

' . esc_html__( 'No entries found.', 'nonprofit-forms' ) . '

'; return; } $is_admin = current_user_can( 'manage_options' ); echo '
'; wp_nonce_field( 'npfrm_bulk', 'npfrm_bulk_nonce' ); echo ''; echo ''; if ( $is_admin ) { echo ''; } echo ''; $labels = self::statuses(); foreach ( $result['rows'] as $row ) { $data = NPFRM_Entries::data( $row ); $first = $data ? reset( $data ) : array( 'value' => '' ); $summary = wp_trim_words( $first['value'] . ( $row->email ? ' (' . $row->email . ')' : '' ), 12 ); $view = add_query_arg( 'entry', $row->id, $base ); echo ''; if ( $is_admin ) { echo ''; } echo ''; echo ''; echo ''; echo ''; echo ''; } echo '
' . esc_html__( 'Date', 'nonprofit-forms' ) . '' . esc_html__( 'Form', 'nonprofit-forms' ) . '' . esc_html__( 'Summary', 'nonprofit-forms' ) . '' . esc_html__( 'Status', 'nonprofit-forms' ) . '
' . ( $row->is_read ? '' : '' ) . esc_html( get_date_from_gmt( $row->created_at, get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ) ) . ( $row->is_read ? '' : '' ) . '' . esc_html( isset( $forms[ $row->form_id ] ) ? $forms[ $row->form_id ] : '#' . $row->form_id ) . '' . esc_html( $summary ) . '' . esc_html( isset( $labels[ $row->status ] ) ? $labels[ $row->status ] : $row->status ) . '' . esc_html__( 'Open', 'nonprofit-forms' ) . '
'; if ( $is_admin ) { echo '

'; } echo '
'; $links = paginate_links( array( 'base' => add_query_arg( 'paged', '%#%', remove_query_arg( 'paged' ) ), 'format' => '', 'current' => $paged, 'total' => (int) ceil( $result['total'] / $per ), 'type' => 'plain', ) ); if ( $links ) { echo wp_kses_post( $links ); } echo ''; } /** * Follow-up statuses. * * @return array */ private static function statuses() { return array( 'new' => __( 'New', 'nonprofit-forms' ), 'contacted' => __( 'Contacted', 'nonprofit-forms' ), 'done' => __( 'Done', 'nonprofit-forms' ), ); } /** * One entry, with status and a private note. * * @param int $entry_id Entry ID. * @param string $base Entries address. * @param array $forms Forms the user can see. * @return void */ private static function entry_detail( $entry_id, $base, array $forms ) { $row = NPFRM_Entries::get( $entry_id ); if ( ! $row || ! self::can_view_form( $row->form_id ) ) { echo '

' . esc_html__( 'Entry not found.', 'nonprofit-forms' ) . '

'; return; } NPFRM_Entries::mark_read( $row->id ); echo '

← ' . esc_html__( 'All entries', 'nonprofit-forms' ) . '

'; echo ''; echo ''; echo ''; foreach ( NPFRM_Entries::data( $row ) as $pair ) { echo ''; } $meta = $row->meta ? json_decode( $row->meta, true ) : array(); if ( ! empty( $meta['notice'] ) ) { echo ''; } echo '
' . esc_html__( 'Form', 'nonprofit-forms' ) . '' . esc_html( isset( $forms[ $row->form_id ] ) ? $forms[ $row->form_id ] : '#' . $row->form_id ) . '
' . esc_html__( 'Received', 'nonprofit-forms' ) . '' . esc_html( get_date_from_gmt( $row->created_at, get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ) ) . '
' . esc_html( $pair['label'] ) . '' . nl2br( esc_html( $pair['value'] ) ) . '
' . esc_html__( 'Privacy notice shown', 'nonprofit-forms' ) . '' . esc_html( $meta['notice'] ) . '
' . esc_html( $meta['shown_at'] ) . '
'; echo '

' . esc_html__( 'Follow-up', 'nonprofit-forms' ) . '

'; echo '
'; wp_nonce_field( 'npfrm_entry_' . $row->id, 'npfrm_entry_nonce' ); echo ''; echo '


'; echo '

'; if ( current_user_can( 'manage_options' ) ) { $del = wp_nonce_url( admin_url( 'admin-post.php?action=npfrm_delete_entry&entry=' . $row->id ), 'npfrm_delete_' . $row->id ); echo '

' . esc_html__( 'Delete this entry', 'nonprofit-forms' ) . ''; if ( $row->email ) { $erase = wp_nonce_url( admin_url( 'admin-post.php?action=npfrm_erase_person&entry=' . $row->id ), 'npfrm_erase_' . $row->id ); echo ' | ' . esc_html__( 'Erase everything from this person', 'nonprofit-forms' ) . ''; } echo '

'; } } /** * Save status and note. * * @return void */ public static function save_entry() { $id = isset( $_POST['entry'] ) ? absint( $_POST['entry'] ) : 0; check_admin_referer( 'npfrm_entry_' . $id, 'npfrm_entry_nonce' ); $row = NPFRM_Entries::get( $id ); if ( ! $row || ! self::can_view_form( $row->form_id ) ) { wp_die( esc_html__( 'You are not allowed to do that.', 'nonprofit-forms' ) ); } $status = isset( $_POST['status'] ) ? sanitize_key( wp_unslash( $_POST['status'] ) ) : 'new'; $note = isset( $_POST['note'] ) ? sanitize_textarea_field( wp_unslash( $_POST['note'] ) ) : ''; NPFRM_Entries::update_follow_up( $id, $status, $note ); wp_safe_redirect( add_query_arg( 'entry', $id, self::entries_url() ) ); exit; } /** * Delete the selected entries (administrators only). * * @return void */ public static function bulk_entries() { check_admin_referer( 'npfrm_bulk', 'npfrm_bulk_nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You are not allowed to do that.', 'nonprofit-forms' ) ); } $ids = isset( $_POST['ids'] ) ? array_map( 'absint', (array) wp_unslash( $_POST['ids'] ) ) : array(); foreach ( $ids as $id ) { NPFRM_Entries::delete( $id ); } wp_safe_redirect( self::entries_url() ); exit; } /** * Erase every entry from one email address (administrators only). * * @return void */ public static function erase_person() { $id = isset( $_GET['entry'] ) ? absint( $_GET['entry'] ) : 0; check_admin_referer( 'npfrm_erase_' . $id ); if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You are not allowed to do that.', 'nonprofit-forms' ) ); } $row = NPFRM_Entries::get( $id ); if ( $row && $row->email ) { NPFRM_Entries::delete_by_email( $row->email ); } wp_safe_redirect( self::entries_url() ); exit; } /** * Delete one entry. * * @return void */ public static function delete_entry() { $id = isset( $_GET['entry'] ) ? absint( $_GET['entry'] ) : 0; check_admin_referer( 'npfrm_delete_' . $id ); if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You are not allowed to do that.', 'nonprofit-forms' ) ); } NPFRM_Entries::delete( $id ); wp_safe_redirect( self::entries_url() ); exit; } /** * CSV export for one form. * * @return void */ public static function export_csv() { $form_id = isset( $_GET['form'] ) ? absint( $_GET['form'] ) : 0; check_admin_referer( 'npfrm_export_' . $form_id ); if ( ! $form_id || ! self::can_view_form( $form_id ) ) { wp_die( esc_html__( 'You are not allowed to do that.', 'nonprofit-forms' ) ); } $rows = NPFRM_Entries::all_for_form( $form_id ); $headers = array(); foreach ( $rows as $row ) { foreach ( NPFRM_Entries::data( $row ) as $pair ) { if ( ! in_array( $pair['label'], $headers, true ) ) { $headers[] = $pair['label']; } } } nocache_headers(); header( 'Content-Type: text/csv; charset=utf-8' ); header( 'Content-Disposition: attachment; filename="entries-' . $form_id . '-' . gmdate( 'Ymd' ) . '.csv"' ); $out = fopen( 'php://output', 'w' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen fwrite( $out, "\xEF\xBB\xBF" ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite -- BOM so Excel reads Indian scripts. fputcsv( $out, array_map( array( __CLASS__, 'csv_safe' ), array_merge( array( 'Date (UTC)' ), $headers ) ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions foreach ( $rows as $row ) { $map = array(); foreach ( NPFRM_Entries::data( $row ) as $pair ) { $map[ $pair['label'] ] = $pair['value']; } $line = array( $row->created_at ); foreach ( $headers as $h ) { $line[] = isset( $map[ $h ] ) ? $map[ $h ] : ''; } fputcsv( $out, array_map( array( __CLASS__, 'csv_safe' ), $line ) ); } fclose( $out ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose exit; } /** * Stop spreadsheet formula injection. * * @param string $cell Cell. * @return string */ public static function csv_safe( $cell ) { $cell = (string) $cell; return preg_match( '/^[=+\-@\t\r]/', $cell ) ? "'" . $cell : $cell; } /** * Admin notice: no published privacy policy page linked from forms. * * @return void */ public static function privacy_notice() { if ( ! current_user_can( 'manage_options' ) ) { return; } $pid = (int) get_option( 'wp_page_for_privacy_policy' ); if ( $pid > 0 && 'publish' === get_post_status( $pid ) ) { return; } echo '

Nonprofit Forms: '; if ( $pid > 0 && 'draft' === get_post_status( $pid ) ) { esc_html_e( 'Your privacy policy page is still a draft. Forms will link to it once it is published.', 'nonprofit-forms' ); echo ' ' . esc_html__( 'Review and publish it', 'nonprofit-forms' ) . ''; } else { esc_html_e( 'No published privacy policy page yet. Forms show the privacy link only when one exists, so visitors never see a dead link.', 'nonprofit-forms' ); echo ' ' . esc_html__( 'Create a starter draft page', 'nonprofit-forms' ) . ''; } echo '

'; } /** * One-click starter privacy page, saved as DRAFT (never auto-published). * * @return void */ public static function create_privacy() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Not allowed.', 'nonprofit-forms' ), 403 ); } check_admin_referer( 'npfrm_create_privacy' ); $pid = (int) get_option( 'wp_page_for_privacy_policy' ); if ( $pid <= 0 || ! get_post( $pid ) ) { $pid = wp_insert_post( array( 'post_title' => __( 'Privacy Policy', 'nonprofit-forms' ), 'post_status' => 'draft', 'post_type' => 'page', 'post_content' => NPFRM_Privacy::starter_page_text(), ) ); if ( $pid && ! is_wp_error( $pid ) ) { update_option( 'wp_page_for_privacy_policy', $pid ); } } wp_safe_redirect( $pid && ! is_wp_error( $pid ) ? get_edit_post_link( $pid, 'raw' ) : admin_url( 'admin.php?page=npfrm' ) ); exit; } }