--- a/ninja-forms/blocks/bootstrap.php
+++ b/ninja-forms/blocks/bootstrap.php
@@ -125,14 +125,25 @@
]);
// For block editor, provide a token that allows access to all forms
- // This is safe because it's only loaded in admin context with proper capability checks
- $token = NinjaFormsBlocksAuthenticationTokenFactory::make();
- $publicKey = NinjaFormsBlocksAuthenticationKeyFactory::make();
- $allFormIds = array_map(function($form) { return absint($form['formID']); }, $forms);
-
- wp_localize_script('ninja-forms/submissions-table/block', 'ninjaFormsViews', [
- 'token' => $token->create($publicKey, $allFormIds),
- ]);
+ // SECURITY: Only users with appropriate capability can receive tokens for viewing submissions
+ // This prevents Contributors/Authors from accessing form submission data via the REST API
+ //
+ // Uses ninja_forms_admin_submissions_capabilities filter for consistency with Submissions menu
+ // Additional filter ninja_forms_views_token_capability allows specific customization for Views API
+ $views_capability = apply_filters(
+ 'ninja_forms_views_token_capability',
+ apply_filters( 'ninja_forms_admin_submissions_capabilities', 'manage_options' )
+ );
+
+ if ( current_user_can( $views_capability ) ) {
+ $token = NinjaFormsBlocksAuthenticationTokenFactory::make();
+ $publicKey = NinjaFormsBlocksAuthenticationKeyFactory::make();
+ $allFormIds = array_map(function($form) { return absint($form['formID']); }, $forms);
+
+ wp_localize_script('ninja-forms/submissions-table/block', 'ninjaFormsViews', [
+ 'token' => $token->create($publicKey, $allFormIds),
+ ]);
+ }
});
/**
@@ -164,9 +175,15 @@
$tokenHeader = $request->get_header('X-NinjaFormsViews-Auth');
$formId = $request->get_param('id');
- // If user is logged in and has manage_options capability, allow access
+ // If user is logged in and has appropriate capability, allow access
// This provides fallback for admin users
- if (is_user_logged_in() && current_user_can('manage_options')) {
+ // Uses same capability filter as token generation for consistency
+ $views_capability = apply_filters(
+ 'ninja_forms_views_token_capability',
+ apply_filters( 'ninja_forms_admin_submissions_capabilities', 'manage_options' )
+ );
+
+ if (is_user_logged_in() && current_user_can($views_capability)) {
return true;
}
@@ -190,8 +207,13 @@
$formsBuilder = (new NinjaFormsBlocksDataBuilderFormsBuilderFactory)->make();
$allForms = $formsBuilder->get();
- // If user has manage_options capability, return all forms
- if (is_user_logged_in() && current_user_can('manage_options')) {
+ // If user has appropriate capability, return all forms
+ $views_capability = apply_filters(
+ 'ninja_forms_views_token_capability',
+ apply_filters( 'ninja_forms_admin_submissions_capabilities', 'manage_options' )
+ );
+
+ if (is_user_logged_in() && current_user_can($views_capability)) {
return $allForms;
}
@@ -270,36 +292,69 @@
/**
* Token Refresh Endpoint
*
- * Generates a new token scoped to requested form IDs.
+ * Generates a new token scoped to the same form ID as the previous token.
* Used for automatic token refresh when tokens expire or after secret rotation.
*
- * FIX: Restricts token generation to single forms and validates form access
+ * SECURITY: Requires the old token to be provided. This ensures:
+ * - Only tokens that were legitimately issued can be refreshed
+ * - Tokens can only be refreshed for the same form ID
+ * - No reliance on spoofable Referer headers
*/
register_rest_route('ninja-forms-views', 'token/refresh', array(
'methods' => 'POST',
'callback' => function (WP_REST_Request $request) {
- // REFACTOR: Accept single formID instead of formIds array
+ $tokenValidator = NinjaFormsBlocksAuthenticationTokenFactory::make();
+
+ // SECURITY: Require the old token for refresh
+ // This prevents attackers from generating tokens without having a legitimate one first
+ $oldToken = $request->get_header('X-NinjaFormsViews-Auth');
+ if (!$oldToken) {
+ return new WP_Error(
+ 'missing_token',
+ __('A valid token is required for refresh. Include the current token in X-NinjaFormsViews-Auth header.', 'ninja-forms'),
+ array('status' => 401)
+ );
+ }
+
+ // Validate the old token's signature (allows expired tokens for refresh)
+ // This ensures the token was legitimately issued by this site
+ if (!$tokenValidator->validateSignatureOnly($oldToken)) {
+ return new WP_Error(
+ 'invalid_token',
+ __('The provided token is invalid or has been tampered with.', 'ninja-forms'),
+ array('status' => 403)
+ );
+ }
+
+ // Extract form IDs from the old token - these are the only forms allowed for refresh
+ $authorizedFormIds = $tokenValidator->getFormIds($oldToken);
+ if ($authorizedFormIds === false || empty($authorizedFormIds)) {
+ return new WP_Error(
+ 'invalid_token_payload',
+ __('Could not extract form authorization from token.', 'ninja-forms'),
+ array('status' => 403)
+ );
+ }
+
+ // Get the requested form ID (optional - defaults to first form in old token)
$formId = $request->get_param('formID');
-
+
// Check for legacy formIds parameter for backward compatibility
if (!$formId && $request->get_param('formIds')) {
$formIds = $request->get_param('formIds');
if (is_array($formIds) && !empty($formIds)) {
- // Only accept single form from legacy array
- if (count($formIds) > 1) {
- return new WP_Error(
- 'too_many_form_ids',
- __('Token generation is limited to one form at a time. Please use formID parameter instead.', 'ninja-forms'),
- array('status' => 400)
- );
- }
$formId = $formIds[0];
}
}
- // Sanitize and validate form ID
+ // If no form ID specified, use the first (and typically only) form from old token
+ if (!$formId) {
+ $formId = $authorizedFormIds[0];
+ }
+
+ // Sanitize form ID
$formId = absint($formId);
-
+
if (!$formId) {
return new WP_Error(
'invalid_form_id',
@@ -308,97 +363,23 @@
);
}
- // FIX: Validate that the form exists and is accessible
- $form = Ninja_Forms()->form( $formId )->get();
- if (!$form) {
- return new WP_Error(
- 'form_not_found',
- __('The requested form does not exist', 'ninja-forms'),
- array('status' => 404)
- );
- }
-
- // FIX: Validate that user has permission to access this form
- // This prevents users from generating tokens for arbitrary forms
- $referer = wp_get_referer();
- if (!$referer) {
+ // SECURITY: Verify the requested form ID was in the old token
+ // This prevents upgrading a single-form token to access other forms
+ if (!in_array($formId, array_map('intval', $authorizedFormIds), true)) {
return new WP_Error(
- 'invalid_request',
- __('Request must come from a valid page with submissions table block', 'ninja-forms'),
+ 'unauthorized_form_access',
+ __('The requested form was not authorized in your original token.', 'ninja-forms'),
array('status' => 403)
);
}
- // Parse the referring page to validate block authorization
- $post_id = url_to_postid($referer);
- if (!$post_id) {
- // Handle front page, archives, etc.
- $parsed_url = parse_url($referer);
- if ($parsed_url['path'] === '/' || $parsed_url['path'] === home_url('/')) {
- $post_id = get_option('page_on_front');
- }
- }
-
- // Check if the form is actually embedded in a submissions table block on this page
- if ($post_id) {
- $post = get_post($post_id);
- $is_public = is_post_publicly_viewable( $post );
-
- // If post is public _and_ password-protected, but user hasn't provided a valid password
- if( $is_public && post_password_required( $post ) ) {
- return new WP_Error(
- 'unauthorized_form_access',
- __('You do not have permission to access this form via this page', 'ninja-forms'),
- array('status' => 403)
- );
- }
-
- // If post is private or just generally not public, and logged-in user cannot read it
- if( ! $is_public && ! current_user_can( 'read_post', $post ) ) {
- return new WP_Error(
- 'unauthorized_form_access',
- __('You do not have permission to access this form via this page', 'ninja-forms'),
- array('status' => 403)
- );
- }
-
- if ($post && has_blocks($post->post_content)) {
- $blocks = parse_blocks($post->post_content);
- $found_authorized_form = false;
-
- // Recursively search for ninja-forms/submissions-table blocks
- $search_blocks = function($blocks) use ($formId, &$found_authorized_form, &$search_blocks) {
- foreach ($blocks as $block) {
- if ($block['blockName'] === 'ninja-forms/submissions-table') {
- if (isset($block['attrs']['formID']) &&
- intval($block['attrs']['formID']) === $formId) {
- $found_authorized_form = true;
- return;
- }
- }
- // Search inner blocks recursively
- if (!empty($block['innerBlocks'])) {
- $search_blocks($block['innerBlocks']);
- }
- }
- };
-
- $search_blocks($blocks);
-
- if (!$found_authorized_form) {
- return new WP_Error(
- 'unauthorized_form_access',
- __('You do not have permission to access this form via this page', 'ninja-forms'),
- array('status' => 403)
- );
- }
- }
- } else {
- // If we can't determine the post ID, return an error
+ // Validate that the form still exists
+ $form = Ninja_Forms()->form($formId)->get();
+ if (!$form) {
return new WP_Error(
- 'post_id_not_found',
- __('The requested data could not be related to a valid page', 'ninja-forms'),
- array('status' => 403)
+ 'form_not_found',
+ __('The requested form does not exist', 'ninja-forms'),
+ array('status' => 404)
);
}
@@ -411,7 +392,7 @@
'token' => $newToken,
'publicKey' => $publicKey,
'expiresIn' => 900, // 15 minutes in seconds
- 'formID' => $formId, // Changed from formIds to formID
+ 'formID' => $formId,
);
},
'permission_callback' => function (WP_REST_Request $request) {
@@ -426,7 +407,7 @@
return $rateLimitCheck; // Returns 429 Too Many Requests
}
- return true; // Public endpoint (rate-limited) but with form validation
+ return true; // Rate-limited, but token validation happens in callback
},
));
--- a/ninja-forms/blocks/views/includes/Authentication/Token.php
+++ b/ninja-forms/blocks/views/includes/Authentication/Token.php
@@ -1,124 +1,189 @@
-<?php
-
-namespace NinjaFormsBlocksAuthentication;
-
-/**
- * Creates an encoded public/private key hash and validates it.
- *
- * Security improvements:
- * - Tokens are bound to specific form IDs
- * - Tokens include expiration timestamps (15 minutes)
- * - Validation checks both authenticity and authorization
- */
-class Token {
-
- /** @var string */
- protected $privateKey;
-
- /** @var int Token expiration time in seconds */
- const TOKEN_EXPIRATION = 900; // 15 minutes
-
- /**
- * @param string $privateKey
- */
- public function __construct( $privateKey ) {
- $this->privateKey = $privateKey;
- }
-
- /**
- * Create a token bound to specific form IDs with expiration.
- *
- * @param string $publicKey
- * @param array $formIds Array of form IDs this token can access
- *
- * @return string
- */
- public function create( $publicKey, $formIds = array() ) {
- $expiration = time() + self::TOKEN_EXPIRATION;
- $payload = json_encode( array(
- 'formIds' => array_map( 'intval', $formIds ),
- 'exp' => $expiration
- ) );
-
- $hash = $this->hash( $publicKey, $payload );
- return base64_encode( $hash . ':' . $publicKey . ':' . $payload );
- }
-
- /**
- * Validate token authenticity and check if it grants access to a specific form.
- *
- * @param string $token
- * @param int|null $formId Form ID to check access for (null to only validate token structure)
- *
- * @return bool
- */
- public function validate( $token, $formId = null ) {
- // If the token is malformed, then list() may return an undefined index error.
- // Pad the exploded array to add missing indexes.
- // Limit explode to 3 parts to handle colons in payload JSON
- list( $hash, $publicKey, $payload ) = array_pad( explode( ':', base64_decode( $token ), 3 ), 3, false );
-
- // Validate token structure and hash
- if ( ! hash_equals( $hash, $this->hash( $publicKey, $payload ) ) ) {
- return false;
- }
-
- // Decode and validate payload
- $data = json_decode( $payload, true );
- if ( ! is_array( $data ) || ! isset( $data['formIds'] ) || ! isset( $data['exp'] ) ) {
- return false;
- }
-
- // Check expiration
- if ( time() > $data['exp'] ) {
- return false;
- }
-
- // If a specific form ID is requested, check authorization
- if ( $formId !== null ) {
- if ( ! in_array( intval( $formId ), $data['formIds'], true ) ) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Extract form IDs from a token without full validation.
- * Used for debugging/logging purposes only.
- *
- * @param string $token
- *
- * @return array|false Array of form IDs or false on failure
- */
- public function getFormIds( $token ) {
- // Limit explode to 3 parts to handle colons in payload JSON
- $parts = explode( ':', base64_decode( $token ), 3 );
-
- // Token format: hash:publicKey:payload
- // We only need the payload (3rd part)
- if ( count( $parts ) < 3 ) {
- return false;
- }
-
- $payload = $parts[2];
- $data = json_decode( $payload, true );
- return isset( $data['formIds'] ) ? $data['formIds'] : false;
- }
-
- /**
- * Generate HMAC hash for token validation using hash_hmac().
- *
- * Uses HMAC-SHA256 which is cryptographically stronger than simple
- * concatenation and prevents length extension attacks.
- *
- * @param string $publicKey
- * @param string $payload
- *
- * @return string
- */
- protected function hash( $publicKey, $payload = '' ) {
- return hash_hmac( 'sha256', $publicKey . $payload, $this->privateKey );
- }
+<?php
+
+namespace NinjaFormsBlocksAuthentication;
+
+/**
+ * Creates an encoded public/private key hash and validates it.
+ *
+ * Security improvements:
+ * - Tokens are bound to specific form IDs
+ * - Tokens include expiration timestamps (15 minutes)
+ * - Validation checks both authenticity and authorization
+ */
+class Token {
+
+ /** @var string */
+ protected $privateKey;
+
+ /** @var int Token expiration time in seconds */
+ const TOKEN_EXPIRATION = 900; // 15 minutes
+
+ /** @var int Maximum token length in bytes (security: prevent memory exhaustion DoS) */
+ const MAX_TOKEN_LENGTH = 8192; // 8KB is generous for typical tokens
+
+ /** @var int Maximum age past expiration for token refresh (security: limit refresh window) */
+ const MAX_REFRESH_AGE = 86400; // 24 hours - tokens older than this cannot be refreshed
+
+ /**
+ * @param string $privateKey
+ */
+ public function __construct( $privateKey ) {
+ $this->privateKey = $privateKey;
+ }
+
+ /**
+ * Create a token bound to specific form IDs with expiration.
+ *
+ * @param string $publicKey
+ * @param array $formIds Array of form IDs this token can access
+ *
+ * @return string
+ */
+ public function create( $publicKey, $formIds = array() ) {
+ $expiration = time() + self::TOKEN_EXPIRATION;
+ $payload = json_encode( array(
+ 'formIds' => array_map( 'intval', $formIds ),
+ 'exp' => $expiration
+ ) );
+
+ $hash = $this->hash( $publicKey, $payload );
+ return base64_encode( $hash . ':' . $publicKey . ':' . $payload );
+ }
+
+ /**
+ * Validate token authenticity and check if it grants access to a specific form.
+ *
+ * @param string $token
+ * @param int|null $formId Form ID to check access for (null to only validate token structure)
+ *
+ * @return bool
+ */
+ public function validate( $token, $formId = null ) {
+ // Security: Validate token size before decoding to prevent memory exhaustion DoS
+ if ( ! is_string( $token ) || strlen( $token ) > self::MAX_TOKEN_LENGTH ) {
+ return false;
+ }
+
+ // If the token is malformed, then list() may return an undefined index error.
+ // Pad the exploded array to add missing indexes.
+ // Limit explode to 3 parts to handle colons in payload JSON
+ list( $hash, $publicKey, $payload ) = array_pad( explode( ':', base64_decode( $token ), 3 ), 3, false );
+
+ // Validate token structure and hash
+ if ( ! hash_equals( $hash, $this->hash( $publicKey, $payload ) ) ) {
+ return false;
+ }
+
+ // Decode and validate payload
+ $data = json_decode( $payload, true );
+ if ( ! is_array( $data ) || ! isset( $data['formIds'] ) || ! isset( $data['exp'] ) ) {
+ return false;
+ }
+
+ // Check expiration
+ if ( time() > $data['exp'] ) {
+ return false;
+ }
+
+ // If a specific form ID is requested, check authorization
+ if ( $formId !== null ) {
+ if ( ! in_array( intval( $formId ), $data['formIds'], true ) ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Validate token signature and structure without checking expiration.
+ * Used for token refresh - allows refreshing expired but authentic tokens.
+ *
+ * @param string $token
+ * @param int|null $formId Form ID to check access for (null to only validate signature)
+ *
+ * @return bool True if token signature is valid (regardless of expiration)
+ */
+ public function validateSignatureOnly( $token, $formId = null ) {
+ // Security: Validate token size before decoding to prevent memory exhaustion DoS
+ if ( ! is_string( $token ) || strlen( $token ) > self::MAX_TOKEN_LENGTH ) {
+ return false;
+ }
+
+ // If the token is malformed, then list() may return an undefined index error.
+ list( $hash, $publicKey, $payload ) = array_pad( explode( ':', base64_decode( $token ), 3 ), 3, false );
+
+ // Validate token structure and hash (signature check)
+ if ( ! $hash || ! $publicKey || ! $payload ) {
+ return false;
+ }
+
+ if ( ! hash_equals( $hash, $this->hash( $publicKey, $payload ) ) ) {
+ return false;
+ }
+
+ // Decode and validate payload structure
+ $data = json_decode( $payload, true );
+ if ( ! is_array( $data ) || ! isset( $data['formIds'] ) || ! isset( $data['exp'] ) ) {
+ return false;
+ }
+
+ // Security: Limit how old a token can be for refresh
+ // This prevents indefinite refresh of tokens from logs/browser history
+ if ( time() > $data['exp'] + self::MAX_REFRESH_AGE ) {
+ return false;
+ }
+
+ // If a specific form ID is requested, check authorization
+ if ( $formId !== null ) {
+ if ( ! in_array( intval( $formId ), $data['formIds'], true ) ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Extract form IDs from a token without full validation.
+ * Used for debugging/logging purposes only.
+ *
+ * @param string $token
+ *
+ * @return array|false Array of form IDs or false on failure
+ */
+ public function getFormIds( $token ) {
+ // Security: Validate token size before decoding to prevent memory exhaustion DoS
+ if ( ! is_string( $token ) || strlen( $token ) > self::MAX_TOKEN_LENGTH ) {
+ return false;
+ }
+
+ // Limit explode to 3 parts to handle colons in payload JSON
+ $parts = explode( ':', base64_decode( $token ), 3 );
+
+ // Token format: hash:publicKey:payload
+ // We only need the payload (3rd part)
+ if ( count( $parts ) < 3 ) {
+ return false;
+ }
+
+ $payload = $parts[2];
+ $data = json_decode( $payload, true );
+ return isset( $data['formIds'] ) ? $data['formIds'] : false;
+ }
+
+ /**
+ * Generate HMAC hash for token validation using hash_hmac().
+ *
+ * Uses HMAC-SHA256 which is cryptographically stronger than simple
+ * concatenation and prevents length extension attacks.
+ *
+ * @param string $publicKey
+ * @param string $payload
+ *
+ * @return string
+ */
+ protected function hash( $publicKey, $payload = '' ) {
+ return hash_hmac( 'sha256', $publicKey . $payload, $this->privateKey );
+ }
}
No newline at end of file
--- a/ninja-forms/blocks/views/includes/DataBuilder/FieldsBuilder.php
+++ b/ninja-forms/blocks/views/includes/DataBuilder/FieldsBuilder.php
@@ -18,11 +18,11 @@
}
protected function toArray( $field ) {
- extract( $field );
+ // Security: Use explicit array access instead of extract() to prevent variable overwriting attacks
return [
- 'id' => $id,
- 'label' => $label,
- 'type' => $type
+ 'id' => $field['id'] ?? null,
+ 'label' => $field['label'] ?? '',
+ 'type' => $field['type'] ?? ''
];
}
}
No newline at end of file
--- a/ninja-forms/blocks/views/includes/DataBuilder/FormsBuilder.php
+++ b/ninja-forms/blocks/views/includes/DataBuilder/FormsBuilder.php
@@ -19,10 +19,10 @@
}
protected function toArray( $form ) {
- extract($form);
+ // Security: Use explicit array access instead of extract() to prevent variable overwriting attacks
return [
- 'formID' => $id,
- 'formTitle' => $title,
+ 'formID' => $form['id'] ?? null,
+ 'formTitle' => $form['title'] ?? '',
];
}
}
--- a/ninja-forms/blocks/views/includes/DataBuilder/SubmissionsBuilder.php
+++ b/ninja-forms/blocks/views/includes/DataBuilder/SubmissionsBuilder.php
@@ -49,14 +49,18 @@
/**
* Basic File Uploads support.
- *
+ *
* Auto-detect a file uploads value, by format, as a serialized array.
* @note using a preliminary `is_serialized()` check to determine
* if the value is from File Uploads, since we do not have
* access to the field information in this context.
+ *
+ * Security: Use allowed_classes => false to prevent PHP object instantiation
+ * which could lead to object injection attacks (CVE potential).
*/
if (is_serialized($value)) {
- $unserialized = unserialize($value);
+ // Safely unserialize with object instantiation disabled to prevent RCE
+ $unserialized = unserialize($value, ['allowed_classes' => false]);
if (is_array($unserialized)) {
// This is the default value assuming it is a file upload
--- a/ninja-forms/includes/AJAX/Controllers/Submission.php
+++ b/ninja-forms/includes/AJAX/Controllers/Submission.php
@@ -694,18 +694,19 @@
/**
* Process fields merge tags for fields inside a repeater fieldset
- *
+ *
* @param object $field The Repeater Fieldset
- *
+ *
+ * @since 3.14.1 Disabled merge tag processing on user-submitted values
+ * to prevent unauthenticated information disclosure.
+ * See: https://github.com/Saturday-Drive/ninja-forms/issues/7838
*/
protected function process_repeater_fields_merge_tags( $field ){
- //Compare the Repeater field passed calling the function with the array of fields values from the submission object
- foreach( $this->_form_data['fields'][$field->get_id()]['value'] as $id => $data ){
- //Check if field is a Repeater Field
- if( Ninja_Forms()->fieldsetRepeater->isRepeaterFieldByFieldReference($id) && !empty($data['value']) && is_string($data['value']) ) {
- //Merge tags in the Repeater Field Sub Fields values
- $this->_form_data['fields'][$field->get_id()]['value'][$id]['value'] = apply_filters( 'ninja_forms_merge_tags', $data['value'] );
- }
- }
+ // SECURITY FIX: Do not process merge tags on user-submitted data.
+ // Merge tags (e.g., {post_meta:KEY}) in user input could expose
+ // sensitive information to unauthenticated attackers.
+ // Merge tag resolution should only occur on admin-configured content
+ // (email templates, success messages, calculations), not on form submissions.
+ return;
}
}
--- a/ninja-forms/includes/Abstracts/MergeTags.php
+++ b/ninja-forms/includes/Abstracts/MergeTags.php
@@ -72,7 +72,7 @@
$subject['payment_total'] = substr_replace($subject['payment_total'], ':calc', -1, 0);
}
- if( 'email' == $subject['type'] ) {
+ if( 'email' == $subject['type'] || 'successmessage' == $subject['type'] ) {
$this->use_safe = true;
} else {
$this->use_safe = false;
--- a/ninja-forms/includes/Actions/SuccessMessage.php
+++ b/ninja-forms/includes/Actions/SuccessMessage.php
@@ -65,10 +65,10 @@
if ($ob) {
$data['debug']['console'][] = sprintf(esc_html__('Shortcodes should return and not echo, see: %s', 'ninja-forms'), 'https://codex.wordpress.org/Shortcode_API#Output');
- $data['actions']['success_message'] .= $action_settings['success_msg'];
+ $data['actions']['success_message'] .= wp_kses_post($action_settings['success_msg']);
} else {
$message = do_shortcode($action_settings['success_msg']);
- $data['actions']['success_message'] .= wpautop($message);
+ $data['actions']['success_message'] .= wp_kses_post(wpautop($message));
}
}
--- a/ninja-forms/includes/Admin/Menus/Forms.php
+++ b/ninja-forms/includes/Admin/Menus/Forms.php
@@ -329,7 +329,9 @@
wp_enqueue_style( 'jBox', Ninja_Forms::$url . 'assets/css/jBox.css' );
wp_enqueue_style( 'codemirror', Ninja_Forms::$url . 'assets/css/codemirror.css' );
wp_enqueue_style( 'codemirror-monokai', Ninja_Forms::$url . 'assets/css/monokai-theme.css' );
- wp_enqueue_style( 'summernote', Ninja_Forms::$url . 'assets/css/summernote-lite.min.css' );
+ wp_enqueue_style( 'quill-core', Ninja_Forms::$url . 'assets/css/quill.core.css' );
+ wp_enqueue_style( 'quill-snow', Ninja_Forms::$url . 'assets/css/quill.snow.css' );
+ wp_enqueue_style( 'quill-custom', Ninja_Forms::$url . 'assets/css/quill-custom.css' );
/**
* JS Libraries
@@ -352,7 +354,7 @@
wp_enqueue_script( 'codemirror', Ninja_Forms::$url . 'assets/js/lib/codemirror.min.js', array( 'jquery', 'nf-builder-deps' ) );
wp_enqueue_script( 'codemirror-xml', Ninja_Forms::$url . 'assets/js/lib/codemirror-xml.min.js', array( 'jquery', 'codemirror' ) );
wp_enqueue_script( 'codemirror-formatting', Ninja_Forms::$url . 'assets/js/lib/codemirror-formatting.min.js', array( 'jquery', 'codemirror' ) );
- wp_enqueue_script( 'summernote', Ninja_Forms::$url . 'assets/js/lib/summernote-lite.min.js', array( 'jquery', 'nf-builder-deps' ) );
+ wp_enqueue_script( 'quill', Ninja_Forms::$url . 'assets/js/lib/quill.min.js', array( 'jquery', 'nf-builder-deps' ) );
wp_enqueue_script( 'nf-builder', Ninja_Forms::$url . 'assets/js/min/builder.js', array( 'jquery', 'jquery-ui-core', 'jquery-ui-draggable', 'jquery-ui-droppable', 'jquery-ui-sortable', 'jquery-effects-bounce', 'wp-color-picker' ), $this->ver );
--- a/ninja-forms/includes/Display/Render.php
+++ b/ninja-forms/includes/Display/Render.php
@@ -874,7 +874,9 @@
wp_enqueue_media();
}
- wp_enqueue_style( 'summernote', $css_dir . 'summernote-lite.min.css' , $ver );
+ wp_enqueue_style( 'quill-core', $css_dir . 'quill.core.css' , $ver );
+ wp_enqueue_style( 'quill-snow', $css_dir . 'quill.snow.css' , $ver );
+ wp_enqueue_style( 'quill-custom', $css_dir . 'quill-custom.css' , $ver );
wp_enqueue_style( 'codemirror', $css_dir . 'codemirror.css' , $ver );
wp_enqueue_style( 'codemirror-monokai', $css_dir . 'monokai-theme.css', $ver );
wp_enqueue_script('nf-front-end--rte', $js_dir . 'front-end--rte.min.js', array( 'jquery' ), $ver );
--- a/ninja-forms/includes/Templates/admin-menu-new-form.html.php
+++ b/ninja-forms/includes/Templates/admin-menu-new-form.html.php
@@ -1023,7 +1023,7 @@
</script>
<script id="tmpl-nf-rte-link-dropdown" type="text/template">
- <div class="summernote-link">
+ <div class="rte-link">
URL
<input type="url" class="widefat code link-url"> <br />
Text
--- a/ninja-forms/ninja-forms.php
+++ b/ninja-forms/ninja-forms.php
@@ -3,7 +3,7 @@
Plugin Name: Ninja Forms
Plugin URI: http://ninjaforms.com/?utm_source=WordPress&utm_medium=readme
Description: Ninja Forms is a webform builder with unparalleled ease of use and features.
-Version: 3.14.0
+Version: 3.14.1
Author: Saturday Drive
Author URI: http://ninjaforms.com/?utm_source=Ninja+Forms+Plugin&utm_medium=Plugins+WP+Dashboard
Text Domain: ninja-forms
@@ -43,7 +43,7 @@
* @since 3.0
*/
- const VERSION = '3.14.0';
+ const VERSION = '3.14.1';
/**
* @since 3.4.0
--- a/ninja-forms/vendor/composer/installed.php
+++ b/ninja-forms/vendor/composer/installed.php
@@ -1,9 +1,9 @@
<?php return array(
'root' => array(
'name' => 'saturday-drive/ninja-forms',
- 'pretty_version' => 'dev-e236c3cd7af8a264e1b17155c7a46434d4ebbfd6',
- 'version' => 'dev-e236c3cd7af8a264e1b17155c7a46434d4ebbfd6',
- 'reference' => 'e236c3cd7af8a264e1b17155c7a46434d4ebbfd6',
+ 'pretty_version' => 'dev-c6cdcdc02d670ebeebc34bd3025b804b17e57d75',
+ 'version' => 'dev-c6cdcdc02d670ebeebc34bd3025b804b17e57d75',
+ 'reference' => 'c6cdcdc02d670ebeebc34bd3025b804b17e57d75',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -11,9 +11,9 @@
),
'versions' => array(
'saturday-drive/ninja-forms' => array(
- 'pretty_version' => 'dev-e236c3cd7af8a264e1b17155c7a46434d4ebbfd6',
- 'version' => 'dev-e236c3cd7af8a264e1b17155c7a46434d4ebbfd6',
- 'reference' => 'e236c3cd7af8a264e1b17155c7a46434d4ebbfd6',
+ 'pretty_version' => 'dev-c6cdcdc02d670ebeebc34bd3025b804b17e57d75',
+ 'version' => 'dev-c6cdcdc02d670ebeebc34bd3025b804b17e57d75',
+ 'reference' => 'c6cdcdc02d670ebeebc34bd3025b804b17e57d75',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),