Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 237 additions & 0 deletions gp-word-count/gwpc-post-image-subfields.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
<?php
/**
* Gravity Wiz // Gravity Forms // Word Count for Post Image Subfields (Caption, Description, Alt, Title)
*
* Instruction Video: https://www.loom.com/share/005eda834a6348f9a874a5e6cdd85461
*
* Set min/max word counts for any Post Image subfield (caption, description, alt, title) per field.
* Supports multiple subfields per field.
*
* @version 1.0.0
* @author David Smith <david@gravitywiz.com>
* @license GPL-2.0+
* @link https://gravitywiz.com/
*/
class GW_Post_Image_Word_Count {

private static $_instance = null;
private $_configs = array();

public static function get_instance() {
if ( self::$_instance === null ) {
self::$_instance = new self();
}
return self::$_instance;
}

public function __construct( $config = array() ) {

$this->_configs[] = array_merge( array(
'form_id' => false,
'field_id' => false,
'limits' => array(),
), $config );

add_action( 'init', array( $this, 'init' ) );

}

public function init() {

if ( ! $this->is_applicable() ) {
return;
}

add_action( 'gform_enqueue_scripts', array( $this, 'enqueue_scripts' ), 20 );
add_action( 'gform_register_init_scripts', array( $this, 'enqueue_textarea_counter' ), 20 );
add_filter( 'gform_validation', array( $this, 'validate_word_count' ) );

}

public function is_applicable() {
return ! empty( $this->_configs );
}

// Map subfield keys to their input suffixes and labels.
public function get_subfields() {
return array(
'caption' => array(
'suffix' => '4',
'label' => __( 'Caption', 'gravityforms' ),
),
'description' => array(
'suffix' => '7',
'label' => __( 'Description', 'gravityforms' ),
),
'alt' => array(
'suffix' => '2',
'label' => __( 'Alt Text', 'gravityforms' ),
),
'title' => array(
'suffix' => '1',
'label' => __( 'Title', 'gravityforms' ),
),
);
}

public function enqueue_scripts( $form ) {
// Check if any Post Image field is configured for word count.
foreach ( $this->_configs as $config ) {
if ( ( ! $config['form_id'] || $form['id'] == $config['form_id'] ) && $config['field_id'] ) {
foreach ( $form['fields'] as $field ) {
if ( $field->id == $config['field_id'] && $field->type === 'post_image' ) {
// Force enqueue the GP Word Count JS.
wp_enqueue_script( 'gp-word-count', plugins_url( 'scripts/jquery.textareaCounter.js', 'gwwordcount/gwwordcount.php' ), array( 'jquery' ), null, true );
return;
}
}
}
}
}
Comment on lines +77 to +90
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the textareaCounter.js script file
fd -t f "textareaCounter.js" || echo "File not found with fd"

# Also check if gwwordcount directory exists
fd -t d "gwwordcount" || echo "gwwordcount directory not found"

# Search for the actual plugin structure
ls -la 2>/dev/null | head -20

Repository: gravitywiz/snippet-library

Length of output: 1311


🏁 Script executed:

#!/bin/bash
# Find all PHP files in gp-word-count directory
find . -path "*/gp-word-count/*" -name "*.php" -type f | head -20

Repository: gravitywiz/snippet-library

Length of output: 364


🏁 Script executed:

#!/bin/bash
# Check for gwwordcount references
rg -l "gwwordcount" --type php

Repository: gravitywiz/snippet-library

Length of output: 228


🏁 Script executed:

#!/bin/bash
# Read the full gwpc-post-image-subfields.php file
cat -n gp-word-count/gwpc-post-image-subfields.php

Repository: gravitywiz/snippet-library

Length of output: 9393


🏁 Script executed:

#!/bin/bash
# Check other files in gp-word-count to understand the pattern
head -50 gp-word-count/gpwc-word-count.php

Repository: gravitywiz/snippet-library

Length of output: 410


🏁 Script executed:

#!/bin/bash
# Search for how gwwordcount plugin is referenced across the snippet library
rg "gwwordcount" -A 3 -B 3

Repository: gravitywiz/snippet-library

Length of output: 2156


🏁 Script executed:

#!/bin/bash
# Check if there's any documentation or examples about how to properly enqueue GP Word Count assets
rg "gp-word-count|textareaCounter" gp-word-count/ -B 5 -A 5

Repository: gravitywiz/snippet-library

Length of output: 7783


🏁 Script executed:

#!/bin/bash
# Check if there are any define() statements or constants that reference gwwordcount paths
rg "define|constant|GWWORDCOUNT|GP_WORD_COUNT" gp-word-count/ -i

Repository: gravitywiz/snippet-library

Length of output: 692


🏁 Script executed:

#!/bin/bash
# Look for how other snippets in this library reference external plugins
rg "plugins_url" --type php -B 2 -A 2

Repository: gravitywiz/snippet-library

Length of output: 594


Line 84: Fix the incorrect plugins_url() usage for the GP Word Count script.

The second parameter to plugins_url() must be a file path (FILE or absolute path), not a plugin slug. 'gwwordcount/gwwordcount.php' will not resolve correctly and will generate an invalid script URL.

Either use the full WP_PLUGIN_DIR constant:

wp_enqueue_script( 'gp-word-count', plugins_url( 'scripts/jquery.textareaCounter.js', WP_PLUGIN_DIR . '/gwwordcount/gwwordcount.php' ), array( 'jquery' ), null, true );

Or, if GP Word Count already registers and enqueues this script, simply use the existing handle:

wp_enqueue_script( 'gp-word-count' );


public function enqueue_textarea_counter( $form ) {

foreach ( $this->_configs as $config ) {
if ( ! $this->is_config_applicable_to_form( $config, $form ) ) {
continue;
}

$field = GFAPI::get_field( $form, $config['field_id'] );
if ( ! $field || $field->type !== 'post_image' || empty( $config['limits'] ) ) {
continue;
}

$form_id = $form['id'];
$field_id = $field->id;

foreach ( $config['limits'] as $subfield => $limits ) {
$subfields = $this->get_subfields();
if ( empty( $subfields[ $subfield ] ) ) {
continue;
}

$suffix = $subfields[ $subfield ]['suffix'];
$min = isset( $limits['min'] ) ? intval( $limits['min'] ) : 0;
$max = isset( $limits['max'] ) ? intval( $limits['max'] ) : 0;

$args = array(
'formId' => $form_id,
'limit' => $max,
'min' => $min,
'truncate' => true,
'defaultLabel' => sprintf( __( 'Max: %s words', 'gp-word-count' ), '{limit}' ),

Check warning on line 122 in gp-word-count/gwpc-post-image-subfields.php

View workflow job for this annotation

GitHub Actions / PHPCS (Files Changed)

A gettext call containing placeholders was found, but was not accompanied by a &quot;translators:&quot; comment on the line above to clarify the meaning of the placeholders.
'defaultLabelSingular' => sprintf( __( 'Max: %s word', 'gp-word-count' ), '{limit}' ),

Check warning on line 123 in gp-word-count/gwpc-post-image-subfields.php

View workflow job for this annotation

GitHub Actions / PHPCS (Files Changed)

A gettext call containing placeholders was found, but was not accompanied by a &quot;translators:&quot; comment on the line above to clarify the meaning of the placeholders.
'counterLabel' => sprintf( __( '%s words left', 'gp-word-count' ), '{remaining}' ),

Check warning on line 124 in gp-word-count/gwpc-post-image-subfields.php

View workflow job for this annotation

GitHub Actions / PHPCS (Files Changed)

A gettext call containing placeholders was found, but was not accompanied by a &quot;translators:&quot; comment on the line above to clarify the meaning of the placeholders.
'counterLabelSingular' => sprintf( __( '%s word left', 'gp-word-count' ), '{remaining}' ),

Check warning on line 125 in gp-word-count/gwpc-post-image-subfields.php

View workflow job for this annotation

GitHub Actions / PHPCS (Files Changed)

A gettext call containing placeholders was found, but was not accompanied by a &quot;translators:&quot; comment on the line above to clarify the meaning of the placeholders.
'limitReachedLabel' => '<span class="gwwc-max-reached" style="font-weight:bold;">' . sprintf( __( '%s words left', 'gp-word-count' ), '{remaining}' ) . '</span>',

Check warning on line 126 in gp-word-count/gwpc-post-image-subfields.php

View workflow job for this annotation

GitHub Actions / PHPCS (Files Changed)

A gettext call containing placeholders was found, but was not accompanied by a &quot;translators:&quot; comment on the line above to clarify the meaning of the placeholders.
'limitExceededLabel' => '<span class="gwwc-max-exceeded" style="font-weight:bold;color:#c0392b;">' . sprintf( __( 'Limit exceeded!', 'gp-word-count' ), '{remaining}' ) . '</span>',
'minCounterLabel' => sprintf( __( '%s more words required', 'gp-word-count' ), '{remaining}' ),

Check warning on line 128 in gp-word-count/gwpc-post-image-subfields.php

View workflow job for this annotation

GitHub Actions / PHPCS (Files Changed)

A gettext call containing placeholders was found, but was not accompanied by a &quot;translators:&quot; comment on the line above to clarify the meaning of the placeholders.
'minCounterLabelSingular' => sprintf( __( '%s more word required', 'gp-word-count' ), '{remaining}' ),

Check warning on line 129 in gp-word-count/gwpc-post-image-subfields.php

View workflow job for this annotation

GitHub Actions / PHPCS (Files Changed)

A gettext call containing placeholders was found, but was not accompanied by a &quot;translators:&quot; comment on the line above to clarify the meaning of the placeholders.
'minReachedLabel' => '<span class="gwwc-min-reached" style="font-weight:bold;color:#27ae60">' . __( 'Minimum word count met.', 'gp-word-count' ) . '</span>',
'minDefaultLabel' => sprintf( __( 'Min: %s words', 'gp-word-count' ), '{min}' ),

Check warning on line 131 in gp-word-count/gwpc-post-image-subfields.php

View workflow job for this annotation

GitHub Actions / PHPCS (Files Changed)

A gettext call containing placeholders was found, but was not accompanied by a &quot;translators:&quot; comment on the line above to clarify the meaning of the placeholders.
'minDefaultLabelSingular' => sprintf( __( 'Min: %s word', 'gp-word-count' ), '{min}' ),

Check warning on line 132 in gp-word-count/gwpc-post-image-subfields.php

View workflow job for this annotation

GitHub Actions / PHPCS (Files Changed)

A gettext call containing placeholders was found, but was not accompanied by a &quot;translators:&quot; comment on the line above to clarify the meaning of the placeholders.
);
Comment on lines +117 to +133
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add translator comments for gettext calls with placeholders.

Lines 122-132 contain multiple gettext calls with placeholders but lack translator comments. While the {placeholder} syntax is for the JavaScript library (not PHP sprintf), translators still need context about what these placeholders represent.

Apply this diff to add translator comments:

 		$args = array(
 			'formId'                  => $form_id,
 			'limit'                   => $max,
 			'min'                     => $min,
 			'truncate'                => true,
+			// translators: {limit} will be replaced with the maximum word count.
 			'defaultLabel'            => sprintf( __( 'Max: %s words', 'gp-word-count' ), '{limit}' ),
+			// translators: {limit} will be replaced with the maximum word count.
 			'defaultLabelSingular'    => sprintf( __( 'Max: %s word', 'gp-word-count' ), '{limit}' ),
+			// translators: {remaining} will be replaced with the remaining word count.
 			'counterLabel'            => sprintf( __( '%s words left', 'gp-word-count' ), '{remaining}' ),
+			// translators: {remaining} will be replaced with the remaining word count.
 			'counterLabelSingular'    => sprintf( __( '%s word left', 'gp-word-count' ), '{remaining}' ),
+			// translators: {remaining} will be replaced with the remaining word count.
 			'limitReachedLabel'       => '<span class="gwwc-max-reached" style="font-weight:bold;">' . sprintf( __( '%s words left', 'gp-word-count' ), '{remaining}' ) . '</span>',
 			'limitExceededLabel'      => '<span class="gwwc-max-exceeded" style="font-weight:bold;color:#c0392b;">' . sprintf( __( 'Limit exceeded!', 'gp-word-count' ), '{remaining}' ) . '</span>',
+			// translators: {remaining} will be replaced with the remaining word count needed to meet minimum.
 			'minCounterLabel'         => sprintf( __( '%s more words required', 'gp-word-count' ), '{remaining}' ),
+			// translators: {remaining} will be replaced with the remaining word count needed to meet minimum.
 			'minCounterLabelSingular' => sprintf( __( '%s more word required', 'gp-word-count' ), '{remaining}' ),
 			'minReachedLabel'         => '<span class="gwwc-min-reached" style="font-weight:bold;color:#27ae60">' . __( 'Minimum word count met.', 'gp-word-count' ) . '</span>',
+			// translators: {min} will be replaced with the minimum word count.
 			'minDefaultLabel'         => sprintf( __( 'Min: %s words', 'gp-word-count' ), '{min}' ),
+			// translators: {min} will be replaced with the minimum word count.
 			'minDefaultLabelSingular' => sprintf( __( 'Min: %s word', 'gp-word-count' ), '{min}' ),
 		);
🧰 Tools
🪛 GitHub Check: PHPCS (Files Changed)

[warning] 132-132:
A gettext call containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.


[warning] 131-131:
A gettext call containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.


[warning] 129-129:
A gettext call containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.


[warning] 128-128:
A gettext call containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.


[warning] 126-126:
A gettext call containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.


[warning] 125-125:
A gettext call containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.


[warning] 124-124:
A gettext call containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.


[warning] 123-123:
A gettext call containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.


[warning] 122-122:
A gettext call containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.

🤖 Prompt for AI Agents
In gp-word-count/gwpc-post-image-subfields.php around lines 117 to 133, the
gettext calls that include JS-style placeholders like '{limit}', '{remaining}',
and '{min}' lack translator comments; add PHP translator comments (/*
translators: ... */) immediately above each __() call to explain what each
placeholder represents (e.g., '{limit} is the maximum word count', '{remaining}
is the number of words left', '{min} is the minimum word count') so translators
have proper context for the placeholders.


$args_json = json_encode( $args );
$input_id = "input_{$form_id}_{$field_id}_{$suffix}";
$script = "jQuery('#{$input_id}').textareaCounter({$args_json});";

GFFormDisplay::add_init_script( $form_id, "gwwc_post_image_{$subfield}_wc_{$field_id}", GFFormDisplay::ON_PAGE_RENDER, $script );
}
}

}

public function validate_word_count( $result ) {

$form = $result['form'];

foreach ( $this->_configs as $config ) {
if ( ! $this->is_config_applicable_to_form( $config, $form ) ) {
continue;
}

foreach ( $form['fields'] as &$field ) {
if ( $field->id != $config['field_id'] || $field->type !== 'post_image' || empty( $config['limits'] ) ) {
continue;
}

foreach ( $config['limits'] as $subfield => $limits ) {
$subfields = $this->get_subfields();
if ( empty( $subfields[ $subfield ] ) ) {
continue;
}

$suffix = $subfields[ $subfield ]['suffix'];
$label = $subfields[ $subfield ]['label'];
$input_name = "input_{$field->id}_{$suffix}";
$value = rgpost( $input_name );
$word_count = preg_match_all( '/\S+/', trim( $value ) );

Comment on lines +165 to +170
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid PHP 8.1+ deprecation/warnings: ensure trim() gets a string

If the subfield isn’t present, rgpost() may return null. trim(null) is deprecated. Cast before trimming and handle the empty case.

Apply this diff:

-					$input_name = "input_{$field->id}_{$suffix}";
-					$value      = rgpost( $input_name );
-					$word_count = preg_match_all( '/\S+/', trim( $value ) );
+					$input_name = "input_{$field->id}_{$suffix}";
+					$value      = trim( (string) rgpost( $input_name ) );
+					$word_count = $value === '' ? 0 : preg_match_all( '/\S+/', $value );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$suffix = $subfields[ $subfield ]['suffix'];
$label = $subfields[ $subfield ]['label'];
$input_name = "input_{$field->id}_{$suffix}";
$value = rgpost( $input_name );
$word_count = preg_match_all( '/\S+/', trim( $value ) );
$suffix = $subfields[ $subfield ]['suffix'];
$label = $subfields[ $subfield ]['label'];
$input_name = "input_{$field->id}_{$suffix}";
$value = trim( (string) rgpost( $input_name ) );
$word_count = $value === '' ? 0 : preg_match_all( '/\S+/', $value );
🤖 Prompt for AI Agents
In gp-word-count/gwpc-post-image-subfields.php around lines 153 to 158, rgpost()
can return null so calling trim() directly can trigger PHP 8.1+ deprecation
warnings; ensure you coerce the value to a string or default to an empty string
before trimming and then only call preg_match_all if the trimmed string is
non-empty (otherwise set word_count to 0). Replace the direct trim($value) usage
with a safe string cast or null coalescing to '' and handle the empty case to
avoid warnings and produce a correct 0 word count.

$min = isset( $limits['min'] ) ? intval( $limits['min'] ) : 0;
$max = isset( $limits['max'] ) ? intval( $limits['max'] ) : 0;

if ( $min && $word_count < $min ) {
$field->failed_validation = true;

Check warning on line 175 in gp-word-count/gwpc-post-image-subfields.php

View workflow job for this annotation

GitHub Actions / PHPCS (Files Changed)

Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space
$field->validation_message = sprintf(
_n( '%1$s must be at least %2$s word.', '%1$s must be at least %2$s words.', $min, 'gp-word-count' ),
$label, $min
);
$result['is_valid'] = false;
}
Comment on lines +174 to +181
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix variable alignment to match coding standards.

Line 175 has an alignment issue: $field->failed_validation has 1 space after the equals sign but PHPCS expects 2 spaces to align with surrounding assignments.

Apply this diff:

 			if ( $min && $word_count < $min ) {
-				$field->failed_validation = true;
+				$field->failed_validation  = true;
 				$field->validation_message = sprintf(
 					_n( '%1$s must be at least %2$s word.', '%1$s must be at least %2$s words.', $min, 'gp-word-count' ),
 					$label, $min
 				);
 				$result['is_valid'] = false;
 			}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ( $min && $word_count < $min ) {
$field->failed_validation = true;
$field->validation_message = sprintf(
_n( '%1$s must be at least %2$s word.', '%1$s must be at least %2$s words.', $min, 'gp-word-count' ),
$label, $min
);
$result['is_valid'] = false;
}
if ( $min && $word_count < $min ) {
$field->failed_validation = true;
$field->validation_message = sprintf(
_n( '%1$s must be at least %2$s word.', '%1$s must be at least %2$s words.', $min, 'gp-word-count' ),
$label, $min
);
$result['is_valid'] = false;
}
🧰 Tools
🪛 GitHub Check: PHPCS (Files Changed)

[warning] 175-175:
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

🤖 Prompt for AI Agents
In gp-word-count/gwpc-post-image-subfields.php around lines 174 to 181, the
assignment to $field->failed_validation on line 175 is misaligned (one space
after the equals) and should use two spaces to match the surrounding assignment
alignment; update that line so there are two spaces on both sides of the equals
sign (aligning with the other assignments) to satisfy PHPCS coding standards.


if ( $max && $word_count > $max ) {
$field->failed_validation = true;
$field->validation_message = sprintf(
_n( '%1$s may only be %2$s word.', '%1$s may only be %2$s words.', $max, 'gp-word-count' ),
$label, $max
);
$result['is_valid'] = false;
}
}
}
}

$result['form'] = $form;
return $result;

}

private function is_config_applicable_to_form( $config, $form ) {
return ( ! $config['form_id'] || $form['id'] == $config['form_id'] ) && $config['field_id'];
}

}

// CONFIGURATION: Set your field IDs and subfield limits here.
new GW_Post_Image_Word_Count( array(
'form_id' => 1, // Form ID (optional, can be false to apply to all forms)
'field_id' => 3, // Post Image field ID
'limits' => array(
'caption' => array(

Check failure on line 211 in gp-word-count/gwpc-post-image-subfields.php

View workflow job for this annotation

GitHub Actions / PHPCS (Files Changed)

Spaces must be used for mid-line alignment; tabs are not allowed
'min' => 3,
'max' => 10,
),
'description' => array(
'min' => 2,
'max' => 20,
),
// 'alt' => array(
// 'min' => 1,
// 'max' => 5
// ),
// 'title' => array(
// 'min' => 1,
// 'max' => 5
// ),
),
) );
Comment on lines +206 to +228
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Replace tab alignment with spaces in configuration array (line 211) to fix PHPCS failure

The 'caption' key in the config array uses a tab for mid-line alignment, which PHPCS flags as an error (“Spaces must be used for mid-line alignment; tabs are not allowed”). Switching to spaces will clear this.

Apply this diff:

-		'caption'	  => array(
+		'caption'     => array(

You may want to quickly scan other config keys for similar tab alignment, but PHPCS is currently only reporting line 211.

🧰 Tools
🪛 GitHub Check: PHPCS (Files Changed)

[failure] 211-211:
Spaces must be used for mid-line alignment; tabs are not allowed

🤖 Prompt for AI Agents
In gp-word-count/gwpc-post-image-subfields.php around lines 206 to 228, the
configuration array uses a tab character for mid-line alignment on the 'caption'
key (line ~211) which triggers a PHPCS error; replace the tab with equivalent
spaces so that alignment uses only spaces, and scan the other keys in the same
array to ensure no other tabs remain, updating any found to spaces.


// Add more configurations as needed...
// new GW_Post_Image_Word_Count( array(
// 'form_id' => 2,
// 'field_id' => 8,
// 'limits' => array(
// 'caption' => array( 'min' => 5, 'max' => 15 ),
// ),
// ) );
Loading