Skip to content
Open
Show file tree
Hide file tree
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
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,8 @@
"editor.codeActionsOnSave": {
"source.fixAll.stylelint": "never"
}
},
"search.exclude": {
"**/pro__premium_only": false
}
Comment on lines +43 to 46
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

search.exclude value looks inverted for the described behavior.

In VS Code, "search.exclude": { "**/pro__premium_only": false } explicitly includes that folder in search. If your intention is to hide pro__premium_only from search results, this needs to be true instead:

-	"search.exclude": {
-		"**/pro__premium_only": false
-	}
+	"search.exclude": {
+		"**/pro__premium_only": true
+	}

If the goal is indeed to keep it searchable, the current setting is fine.

📝 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
},
"search.exclude": {
"**/pro__premium_only": false
}
},
"search.exclude": {
"**/pro__premium_only": true
}
🤖 Prompt for AI Agents
.vscode/settings.json lines 43-46: the search.exclude entry currently sets
"**/pro__premium_only": false which explicitly includes that folder in search;
if the intent is to hide/exclude pro__premium_only from searches change the
value to true so VS Code will exclude it; if the intent is to keep it
searchable, leave as-is.

}
6 changes: 3 additions & 3 deletions interactions.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* Author URI: http://gambit.ph
* License: GPLv2 or later
* Text Domain: interactions
* Version: 1.3.0
* Version: 1.3.2
*
* @fs_premium_only /freemius.php, /freemius/
*/
Expand All @@ -18,7 +18,7 @@
}

defined( 'INTERACT_BUILD' ) || define( 'INTERACT_BUILD', 'free' );
defined( 'INTERACT_VERSION' ) || define( 'INTERACT_VERSION', '1.3.0' );
defined( 'INTERACT_VERSION' ) || define( 'INTERACT_VERSION', '1.3.2' );
defined( 'INTERACT_FILE' ) || define( 'INTERACT_FILE', __FILE__ );

/**
Expand All @@ -31,8 +31,8 @@ function interact_on_activation() {
// Run migration if version not set or outdated
if ( ! $saved_version || version_compare( $saved_version, INTERACT_VERSION, '<' ) ) {
do_action( 'interact/on_plugin_update', $saved_version, INTERACT_VERSION );
update_option( 'interact_plugin_version', INTERACT_VERSION );
}
update_option( 'interact_plugin_version', INTERACT_VERSION );
}
}
register_activation_hook( __FILE__, 'interact_on_activation' );
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "interactions",
"version": "1.3.0",
"version": "1.3.2",
"description": "Make your blocks interactive! Effortlessly set triggers that do actions",
"author": "Benjamin Intal of Gambit",
"private": true,
Expand Down
53 changes: 35 additions & 18 deletions readme.txt
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
=== Interactions ===
=== Interactions - Create Interactive Experiences in the Block Editor ===
Contributors: bfintal, gambitph
Tags: interaction, interactivity, trigger, blocks, gutenberg
Requires at least: 6.6.4
Tested up to: 6.8.3
Requires at least: 6.7.4
Tested up to: 6.9
Requires PHP: 8.0
Stable tag: 1.3.0
Stable tag: 1.3.2
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Add animations and interactivity to your blocks. Choose from ready-made effects like scroll & hover in the Interactions Library, or build your own.

== Description ==

**Interactions – WordPress Animations, Effects & Functionality for Gutenberg Blocks**
**Interactions – WordPress Animations, Interactive Experiences for Gutenberg Blocks**

Want to make your website feel alive and interactive? **Interactions** is the easiest way to add animations, effects, interactivity, and functional features to WordPress — directly inside the block editor.
[Visit our website](https://wpinteractions.com) to learn more about how Interactions work.

Want to make your website feel alive and interactive? **Interactions** is the easiest way to add animations, effects, interactivity, and functional features to WordPress — directly inside the block editor. Check our [samples page here](https://wpinteractions.com/samples/) to see a glimpse of what type of interactions you can create.

You don't need coding skills or complex tools. With Interactions, you can:

- **Pick from the Interactions Library** – A collection of pre-built animations and effects (like images that move upon scrolling down the page, buttons that glow when hovered, and more). Just click and apply.
- **Build your own custom effects** – Use a simple **Trigger → Action** system. Example: "On scroll → Fade in block", or "On click → Play video".
- **Add functional features** – Securely update post data, handle form submissions, display user info, copy text to clipboard, and more without coding.
- **Pick from the [Interactions Library](https://docs.wpinteractions.com/article/744-how-to-use-interactions-library)** – A collection of pre-built animations and effects (like images that move upon scrolling down the page, buttons that glow when hovered, and more). Just click and apply. [Learn more](https://docs.wpinteractions.com/article/744-how-to-use-interactions-library)
- **Build your own custom effects** – Use a simple **Trigger → Action** system. Example: "On scroll → Fade in block", or "On click → Play video". [Learn more](https://docs.wpinteractions.com/article/577-what-is-wp-interactions-and-how-does-it-work)
- **Add functional features** – Securely update post data, handle form submissions, display user info, copy text to clipboard, and more without coding. [Learn more](https://docs.wpinteractions.com/category/729-interactions)

Whether you want subtle hover effects, attention-grabbing story-telling animations, playful micro-interactions, or powerful functional features, Interactions makes it possible.
Whether you want subtle hover effects, attention-grabbing story-telling animations, playful micro-interactions, or powerful functional features, [Interactions](https://wpinteractions.com) makes it possible.

### 🚀 Features

Create custom interactions easily with a simple Trigger → Action builder. Features include:

Create [custom interactions](https://docs.wpinteractions.com/article/571-what-are-interactions) easily with a simple Trigger → Action builder. Features include:

**Animations & Visual Effects:**

Expand Down Expand Up @@ -70,6 +71,8 @@ Create custom interactions easily with a simple Trigger → Action builder. Feat

### 💎 What's in Premium?

[Check our pricing page](https://wpinteractions.com/pricing/) to learn more about what's in Interactions premium.

**Advanced Interactions:**

- **Scroll Strength** – Measure scroll intensity
Expand Down Expand Up @@ -98,6 +101,11 @@ Create custom interactions easily with a simple Trigger → Action builder. Feat
- **Regular Updates** – New features and improvements
- **Commercial License** – Use in client projects

**Source Code:**

The source code for this plugin is available on GitHub:
https://github.com/gambitph/Interactions

== Installation ==

1. Install “Interactions” from the WordPress Plugin Directory, or upload it to `/wp-content/plugins/interactions/`.
Expand Down Expand Up @@ -133,23 +141,32 @@ The free version includes basic animations and interactions. Premium adds advanc

== Screenshots ==

1. Interaction Library – Pre-built animations and effects.
1. Adding from the Interaction Library – Pre-built animations and effects.
2. Advanced trigger and action timeline builder – Create custom interactions with flexible logic and multiple steps.
3. Interaction Library contents – Pre-built animations and effects.

== Source ==

The source code for this plugin is available on GitHub:
https://github.com/gambitph/Interactions
== Upgrade Notice ==

== Changelog ==

= 1.3.2 =

* Fixed: Added restrictions for users without unfiltered_html capabilities
* Fixed: Added additional input sanitization

= 1.3.1 =

* Fixed: Updated readme info
* Fixed: Updated long name in the plugins page
* Fixed: License activation issue

= 1.3.0 =

* New: Interaction library
* New: Initial release in the WordPress Plugin Directory!
* New: Block name field is now searchable #70
* New: Import / export functionality #71
* New: Box shadow action #81
* New: 3D Rotate - new transform origin option
* Fixed: Hover interaction glitches when hovering too fast #9
* Fixed: On enter viewport doesn't always trigger when on mobile #23
* Fixed: Confetti action - selecting window will no longer show a display target warning message #74
Expand Down
10 changes: 10 additions & 0 deletions scripts/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,16 @@ async function packagePlugin() {
}
}

// Rename interactions.php to plugin.php for premium builds only
if ( IS_PREMIUM_BUILD ) {
const oldPath = path.join( BUILD_DIR, 'interactions.php' )
const newPath = path.join( BUILD_DIR, 'plugin.php' )
if ( fs.existsSync( oldPath ) ) {
fs.renameSync( oldPath, newPath )
console.log( '📝 Renamed interactions.php to plugin.php for premium build' )
}
}

console.log( '📁 Copying source directories...' )
// Pass isSrcRoot = true for the top-level src folder
copyDir( 'src', path.join( BUILD_DIR, 'src' ), true )
Expand Down
150 changes: 150 additions & 0 deletions src/action-types/abstract-action-type.php
Original file line number Diff line number Diff line change
Expand Up @@ -256,5 +256,155 @@ public function initilize_action( $action, $animation_data ) {

return $action;
}

/**
* Sanitizes the action's value before saving.
*
* Override this in a child class to implement specific sanitization.
*
* @param mixed $value The action value to sanitize.
* @return mixed The sanitized action value.
*/
public function sanitize_data_for_saving( $value ) {
// By default, no sanitization is applied.
return $value;
}

/**
* Remove any `expression(...)` and `javascript:` content from a CSS style string for security.
*
* @param string $string
* @return string
*/
public function sanitize_style_value( $string ) {
if ( ! is_string( $string ) ) {
return $string;
}
// Remove all expression(...) (case-insensitive).
$string = preg_replace( '/expression\s*\((?:[^\(\)]|(?R))*\)/i', '', $string );

// Remove all javascript: URIs (case-insensitive).
$string = preg_replace( '/javascript\s*:/i', '', $string );

return $string;
}

/**
* Detect if an HTML tag is considered dangerous (can execute scripts or
* otherwise modify page behavior).
*
* @param string $tag_name
* @return bool
*/
public function is_dangerous_tag( $tag_name ) {
if ( empty( $tag_name ) || ! is_string( $tag_name ) ) {
return false;
}

$tag_name = strtolower( trim( $tag_name ) );

// Tags that can execute scripts or modify page behavior
$dangerous_tags = [
'script',
'iframe',
'object',
'embed',
'applet',
'meta',
'link',
'style',
'base',
'form',
];

return in_array( $tag_name, $dangerous_tags, true );
}

/**
* Detect if an HTML attribute is considered dangerous (event handlers,
* attributes that can contain JS URIs, form actions, etc.).
*
* @param string $attribute_name
* @return bool
*/
public function is_dangerous_attribute( $attribute_name ) {
if ( empty( $attribute_name ) || ! is_string( $attribute_name ) ) {
return false;
}

$attribute_name = strtolower( trim( $attribute_name ) );

// Event handler attributes (onclick, onerror, onload, etc.)
if ( preg_match( '/^on[a-z]+/', $attribute_name ) ) {
return true;
}

// Attributes that can contain JavaScript URIs or code
$dangerous_attributes = [
'href',
'src',
'action',
'formaction',
'form',
'formmethod',
'formtarget',
];

return in_array( $attribute_name, $dangerous_attributes, true );
}

/**
* Validate an HTML snippet for dangerous tags, attributes or protocols.
* Returns true when safe, or a WP_Error describing the violation.
*
* @param string $html
* @return true|WP_Error
*/
public function validate_html_for_saving( $html ) {
if ( ! is_string( $html ) ) {
return new WP_Error(
'invalid_html',
__( 'HTML must be a string.', 'interactions' )
);
}

// Detect dangerous tags
if ( preg_match_all( '/<\s*([a-z0-9\-]+)/i', $html, $matches ) ) {
foreach ( $matches[1] as $tag ) {
if ( $this->is_dangerous_tag( $tag ) ) {
return new WP_Error(
'invalid_tag',
sprintf( __( 'The HTML tag "%s" is not allowed.', 'interactions' ), esc_html( $tag ) )
);
}
}
}

// Detect dangerous attributes
if ( preg_match_all( '/<[^>]+>/i', $html, $tagMatches ) ) {
foreach ( $tagMatches[0] as $tagString ) {
if ( preg_match_all( '/([a-zA-Z0-9:\-]+)\s*=\s*(?:"[^"]*"|\'[^\']*\'|[^\s>]+)/i', $tagString, $attrMatches ) ) {
foreach ( $attrMatches[1] as $attr ) {
if ( $this->is_dangerous_attribute( $attr ) ) {
return new WP_Error(
'invalid_attribute',
sprintf( __( 'The HTML attribute "%s" is not allowed.', 'interactions' ), esc_html( $attr ) )
);
}
}
}
}
}

// Detect disallowed protocols
if ( preg_match( '/javascript:\s*/i', $html ) || preg_match( '/data:\s*text\//i', $html ) ) {
return new WP_Error(
'invalid_protocol',
__( 'The HTML contains disallowed protocols (javascript: or data:).', 'interactions' )
);
}

return true;
}
}
}
7 changes: 7 additions & 0 deletions src/action-types/class-action-type-background-color.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ public function initialize() {

// return parent::initilize_action( $action, $animation_data );
// }

public function sanitize_data_for_saving( $value ) {
if ( is_array( $value ) && isset( $value['color'] ) ) {
$value['color'] = $this->sanitize_style_value( $value['color'] );
}
return $value;
}
}

interact_add_action_type( 'backgroundColor', 'Interact_Action_Type_Background_Color' );
Expand Down
7 changes: 7 additions & 0 deletions src/action-types/class-action-type-background-image.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ public function initialize() {

$this->has_dynamic = false;
}

public function sanitize_data_for_saving( $value ) {
if ( is_array( $value ) && isset( $value['image'] ) ) {
$value['image'] = $this->sanitize_style_value( $value['image'] );
}
return $value;
}
}

interact_add_action_type( 'backgroundImage', 'Interact_Action_Type_Background_Image' );
Expand Down
7 changes: 7 additions & 0 deletions src/action-types/class-action-type-css-rule.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ public function initialize() {
],
];
}

public function sanitize_data_for_saving( $value ) {
if ( is_array( $value ) && isset( $value['value'] ) ) {
$value['value'] = $this->sanitize_style_value( $value['value'] );
}
return $value;
}
}

interact_add_action_type( 'cssRule', 'Interact_Action_Type_Css_Rule' );
Expand Down
23 changes: 23 additions & 0 deletions src/action-types/class-action-type-display.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,29 @@ public function initialize() {
$this->has_duration = false;
$this->has_easing = false;
}

public function sanitize_data_for_saving( $value ) {
if ( is_array( $value ) && isset( $value['display'] ) ) {
$allowed_values = [
'block',
'none',
'inline',
'inline-block',
'flex',
'inline-flex',
'grid',
'inline-grid',
'initial',
'inherit',
'revert',
'unset',
];
if ( ! in_array( $value['display'], $allowed_values, true ) ) {
$value['display'] = 'block';
}
}
return $value;
}
}

interact_add_action_type( 'display', 'Interact_Action_Type_Display' );
Expand Down
Loading