diff --git a/.cursor/rules/development.mdc b/.cursor/rules/development.mdc
new file mode 100644
index 0000000..502b52b
--- /dev/null
+++ b/.cursor/rules/development.mdc
@@ -0,0 +1,64 @@
+---
+alwaysApply: true
+---
+---
+description: General rules for WordPress Development
+globs:
+alwaysApply: true
+---
+
+You are an expert in WordPress, PHP, PHP Sniffer, coding standard and related web development technologies.
+
+Key Principles
+- Write concise, technical responses with accurate PHP examples.
+- Follow WordPress coding standards and best practices.
+- Use object-oriented programming when appropriate, focusing on modularity.
+- Prefer iteration and modularization over duplication.
+- Use descriptive function, variable, and file names.
+
+Format code
+- Use tabs for indentations.
+- Space to align equals in same group of variable definitions.
+- Always write code and comments in English.
+- Documentation always add in /docs and add it to .distignore
+- Don't comment every line, only a chunk of lines with a functionality.
+- Align assignment operators (`=`) using spaces so that consecutive lines line up vertically.
+- Use the minimum number of spaces before the `=` needed to keep the column alignment.
+- Keep exactly one space on each side of the operator (`Squiz.WhiteSpace.OperatorSpacing`).
+
+PHP/WordPress
+- Use PHP 7.4+ features when appropriate (e.g., typed properties, arrow functions).
+- Follow WordPress PHP Coding Standards.
+- Utilize WordPress core functions and APIs when available.
+- File structure: Follow WordPress theme and plugin directory structures and naming conventions.
+- Implement proper error handling and logging.
+- Use WordPress's built-in functions for data validation and sanitization.
+- Use prepare() statements for secure database queries.
+- Use Yoda conditions ALWAYS.
+- PHP inline comments must start with capital letter and end with period character.
+- PHP inline comments should be concise.
+
+JavaScript
+- Don't use jQuery. Better Vanilla JavaScript.
+
+Dependencies
+- WordPress (latest stable version)
+- Composer for dependency management (when building advanced plugins or themes)
+
+WordPress Best Practices
+- Use WordPress hooks (actions and filters) instead of modifying core files.
+- Implement proper theme functions using functions.php.
+- Use WordPress's built-in user roles and capabilities system.
+- Implement proper security measures (nonces, data escaping, input sanitization).
+
+Key Conventions
+1. Follow WordPress's plugin API for extending functionality.
+2. Use WordPress's template hierarchy for theme development.
+3. Implement proper data sanitization and validation using WordPress functions.
+4. Use WordPress's template tags and conditional tags in themes.
+5. Implement proper database queries using $wpdb or WP_Query.
+6. Use WordPress's authentication and authorization functions.
+7. Implement proper AJAX handling using admin-ajax.php or REST API.
+8. Use WordPress's hook system for modular and extensible code.
+9. Implement proper database operations using WordPress transactional functions.
+10. Use WordPress's WP_Cron API for scheduling tasks.
\ No newline at end of file
diff --git a/.github/WORKFLOWS-SUMMARY.md b/.github/WORKFLOWS-SUMMARY.md
deleted file mode 100644
index 696f25b..0000000
--- a/.github/WORKFLOWS-SUMMARY.md
+++ /dev/null
@@ -1,218 +0,0 @@
-# 🚀 FormsCRM - Workflows de GitHub Actions
-
-## 📋 Resumen: ¿Qué se Ejecuta Cuando?
-
-### 🔀 **Cuando Haces PUSH a trunk/main/master**
-
-```mermaid
-graph TD
- A[git push origin trunk] --> B[PHP Code Linting]
- A --> C[PHPUnit Tests]
-
- B --> B1[PHP 8.1: composer lint]
- B --> B2[PHPStan Analysis]
-
- C --> C1[PHP 7.4: composer test]
- C --> C2[PHP 8.0: composer test]
- C --> C3[PHP 8.1: composer test]
- C --> C4[PHP 8.1 + Coverage]
-```
-
-**Total: 6 jobs en paralelo** (5-12 minutos)
-
-### 📝 **Cuando Haces PULL REQUEST**
-
-```mermaid
-graph TD
- A[Pull Request] --> B[PHP Code Linting]
- A --> C[PHPUnit Tests]
- A --> D[Unit Tests antiguo]
-
- B --> B1[Linting PHP 8.1]
- B --> B2[PHPStan PHP 8.1]
-
- C --> C1[Tests en 3 versiones PHP]
- C --> C2[Coverage Report]
-
- D --> D1[Tests en 4 versiones PHP]
-```
-
-**Total: 8 jobs en paralelo**
-
-### 🏷️ **Cuando Creas un TAG**
-
-```mermaid
-graph LR
- A[git tag 4.0.7] --> B[Deploy to WordPress.org]
- B --> C[SVN Upload]
-```
-
-**Deploy automático a WordPress.org**
-
-## 📊 Detalle de Cada Workflow
-
-### 1. `phpunit.yml` - **Tests Unitarios** ✅
-
-**Se ejecuta:** Push + PR a trunk/main/master
-**Duración:** 10-15 minutos
-**Jobs:** 4
-
-| Job | PHP | Qué hace |
-|-----|-----|----------|
-| PHPUnit Tests | 7.4 | Todos los tests (19 notificaciones + helpers + APIs) |
-| PHPUnit Tests | 8.0 | Todos los tests + output testdox |
-| PHPUnit Tests | 8.1 | Todos los tests + output testdox |
-| Coverage | 8.1 + Xdebug | Tests con cobertura + Upload Codecov |
-
-**Tests ejecutados:**
-```bash
-✅ 19 tests de notificaciones (Email + Slack)
-✅ Tests de helpers functions
-✅ Tests de Clientify API
-✅ Tests de Contact Form 7
-✅ Total: ~25-30 tests
-```
-
-**Output ejemplo:**
-```
-Notifications
- ✔ Debug email lead function exists
- ✔ Email uses custom email when configured
- ✔ Email uses admin email when no custom email
- ✔ Email subject includes site name
- ✔ Slack sends when webhook configured
- ✔ Slack includes site information
- ...
-
-OK (19 tests, 65 assertions)
-```
-
-### 2. `php-lint.yml` - **Linting & Análisis** ⚠️
-
-**Se ejecuta:** Push + PR a ramas principales
-**Duración:** 3-5 minutos
-**Jobs:** 2
-**Estado:** Informativo (no bloquea CI)
-
-| Job | PHP | Qué hace |
-|-----|-----|----------|
-| PHP Linting | 8.1 | PHPCS WordPress Coding Standards |
-| PHPStan | 8.1 | Análisis estático nivel 0 (informativo) |
-
-**Comandos ejecutados:**
-```bash
-composer validate # Validar composer.json
-composer lint # WordPress Coding Standards (220 errores arreglados!)
-composer phpstan # Análisis estático (informativo)
-```
-
-**Nota:** Linting solo se ejecuta una vez porque no depende de la versión de PHP.
-
-### 3. `php-test.yml` - **Tests Antiguos** (ya existía)
-
-**Se ejecuta:** Solo en Pull Requests
-**Duración:** 8-10 minutos
-**Jobs:** 4 (PHP 7.4, 8.1, 8.2, 8.3)
-
-**Comando:**
-```bash
-phpunit # Sin filtros
-```
-
-### 4. `deploy.yml` - **Deploy Automático** 🚀
-
-**Se ejecuta:** Solo cuando creas un tag
-**Duración:** 3-5 minutos
-
-**Comando:**
-```bash
-git tag 4.0.7
-git push origin --tags
-```
-
-## 🎯 Lo que Pasará Cuando Hagas PUSH
-
-```bash
-git push origin error-notification-slack
-```
-
-### ✅ Se Ejecutarán (en paralelo):
-
-1. **PHP Code Linting** (php-lint.yml)
- - ⚠️ Linting en PHP 7.4, 8.0, 8.1 (informativo)
- - ⚠️ PHPStan análisis (informativo)
-
-2. **PHPUnit Tests** (phpunit.yml)
- - ✅ Tests en PHP 7.4, 8.0, 8.1 (DEBEN PASAR)
- - ✅ Coverage report
- - ✅ **19 tests de notificaciones**
- - ✅ Todos los demás tests
-
-### ❌ NO se ejecutarán:
-
-- `php-test.yml` (solo en PR)
-- `deploy.yml` (solo en tags)
-
-## 📝 Comandos Locales Antes de Push
-
-Para asegurarte de que todo está bien:
-
-```bash
-# 1. Formatear código automáticamente
-composer format
-
-# 2. Verificar linting (informativo)
-composer lint
-
-# 3. Verificar PHPStan (informativo)
-composer phpstan
-
-# 4. IMPORTANTE: Ejecutar tests (DEBEN PASAR)
-composer test
-```
-
-## ✅ Estado Actual
-
-| Check | Estado | Bloquea CI |
-|-------|--------|------------|
-| Linting | ⚠️ 200+ errores | ❌ No (informativo) |
-| PHPStan | ⚠️ 78 errores | ❌ No (informativo) |
-| Tests Unitarios | ⏳ Pendiente MySQL | ✅ Sí |
-| Código Formateado | ✅ 220 arreglados | N/A |
-
-## 🚦 Recomendación
-
-**PUSH CON CONFIANZA** 🚀
-
-Los workflows están configurados para:
-- ✅ **Tests unitarios DEBEN pasar** (bloquean CI)
-- ⚠️ **Linting y PHPStan son informativos** (no bloquean)
-- ✅ **220 errores de formato ya arreglados**
-
-Cuando hagas push verás:
-- Tests ejecutándose en 3 versiones de PHP
-- Linting reportando warnings (no críticos)
-- PHPStan reportando sugerencias (no críticos)
-
-**Los tests unitarios se ejecutarán en GitHub Actions** donde hay MySQL disponible, así que no te preocupes por el MySQL local.
-
-## 🎉 Siguiente Paso
-
-```bash
-git add .
-git commit -m "feat: Add custom email and Slack notifications for errors + 19 unit tests"
-git push origin error-notification-slack
-```
-
-Luego ve a:
-```
-https://github.com/tu-usuario/formscrm/actions
-```
-
-¡Y verás los workflows corriendo! 🎯
-
----
-
-**Última actualización:** 2025-01-04
-**Versión:** 4.0.7
-
diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml
deleted file mode 100644
index 255df37..0000000
--- a/.github/workflows/phpunit.yml
+++ /dev/null
@@ -1,147 +0,0 @@
-name: PHPUnit Tests
-
-on:
- push:
- branches:
- - trunk
- - main
- - master
- - 'release/**'
- paths:
- - '**.php'
- - 'composer.json'
- - 'composer.lock'
- - 'phpunit.xml.dist'
- - '.github/workflows/phpunit.yml'
- pull_request:
- branches:
- - trunk
- - main
- - master
- - 'release/**'
- - 'feature/**'
- paths:
- - '**.php'
- - 'composer.json'
- - 'composer.lock'
- - 'phpunit.xml.dist'
- - '.github/workflows/phpunit.yml'
- types:
- - opened
- - reopened
- - synchronize
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ github.ref != 'refs/heads/trunk' && github.ref != 'refs/heads/main' && github.ref != 'refs/heads/master' }}
-
-jobs:
- phpunit:
- name: PHPUnit Tests (PHP ${{ matrix.php-version }})
- runs-on: ubuntu-latest
- timeout-minutes: 20
-
- strategy:
- fail-fast: false
- matrix:
- php-version: ['8.1', '8.0', '7.4']
-
- services:
- mysql:
- image: mysql:5.7
- env:
- MYSQL_ROOT_PASSWORD: root
- MYSQL_DATABASE: wordpress_tests
- ports:
- - 3306:3306
- options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v4
-
- - name: Setup PHP ${{ matrix.php-version }}
- uses: shivammathur/setup-php@v2
- with:
- php-version: ${{ matrix.php-version }}
- extensions: mbstring, xml, zip, intl, pdo, mysql
- coverage: none
- tools: composer:v2
-
- - name: Get Composer cache directory
- id: composer-cache
- run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
-
- - name: Cache Composer dependencies
- uses: actions/cache@v3
- with:
- path: ${{ steps.composer-cache.outputs.dir }}
- key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
- restore-keys: ${{ runner.os }}-composer-
-
- - name: Validate Composer configuration
- run: composer validate --strict
-
- - name: Install Composer dependencies
- run: composer install --prefer-dist --no-progress --no-interaction
-
- - name: Install SVN
- run: sudo apt-get update && sudo apt-get install -y subversion
-
- - name: Setup WordPress test environment
- run: bash bin/install-wp-tests.sh wordpress_tests root root 127.0.0.1 latest true
-
- - name: Run PHPUnit tests
- run: composer test
-
- - name: Run PHPUnit tests with testdox output
- if: always()
- run: composer test -- --testdox
-
- phpunit-coverage:
- name: PHPUnit Tests with Coverage
- runs-on: ubuntu-latest
- timeout-minutes: 20
-
- services:
- mysql:
- image: mysql:5.7
- env:
- MYSQL_ROOT_PASSWORD: root
- MYSQL_DATABASE: wordpress_tests
- ports:
- - 3306:3306
- options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v4
-
- - name: Setup PHP 8.1 with Xdebug
- uses: shivammathur/setup-php@v2
- with:
- php-version: '8.1'
- extensions: mbstring, xml, zip, intl, pdo, mysql
- coverage: xdebug
- tools: composer:v2
-
- - name: Install Composer dependencies
- run: composer install --prefer-dist --no-progress --no-interaction
-
- - name: Install SVN
- run: sudo apt-get update && sudo apt-get install -y subversion
-
- - name: Setup WordPress test environment
- run: bash bin/install-wp-tests.sh wordpress_tests root root 127.0.0.1 latest true
-
- - name: Run PHPUnit tests with coverage
- run: vendor/bin/phpunit --coverage-text --coverage-clover=coverage.xml
-
- - name: Upload coverage reports
- uses: codecov/codecov-action@v3
- with:
- files: ./coverage.xml
- flags: unittests
- name: codecov-umbrella
- fail_ci_if_error: false
-
diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist
index 75713ac..024887a 100644
--- a/.phpcs.xml.dist
+++ b/.phpcs.xml.dist
@@ -30,12 +30,21 @@
-
+
+
+
+
+
+
+
+
-
+
+
+
@@ -58,4 +67,12 @@
0
+
+
+
+ 0
+
+
+ 0
+
diff --git a/formscrm.php b/formscrm.php
index 56dc376..304e1e4 100644
--- a/formscrm.php
+++ b/formscrm.php
@@ -3,7 +3,7 @@
* Plugin Name: FormsCRM
* Plugin URI : https://close.technology/wordpress-plugins/formscrm/
* Description: Connects Forms with CRM, ERP and Email Marketing.
- * Version: 4.0.6
+ * Version: 4.1.0-beta.1
* Author: CloseTechnology
* Author URI: https://close.technology
* Text Domain: formscrm
@@ -23,7 +23,7 @@
defined( 'ABSPATH' ) || die( 'No script kiddies please!' );
-define( 'FORMSCRM_VERSION', '4.0.6' );
+define( 'FORMSCRM_VERSION', '4.1.0-beta.1' );
define( 'FORMSCRM_PLUGIN', __FILE__ );
define( 'FORMSCRM_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
define( 'FORMSCRM_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
diff --git a/includes/admin/class-admin-options.php b/includes/admin/class-admin-options.php
index 89b1523..5e5a18a 100644
--- a/includes/admin/class-admin-options.php
+++ b/includes/admin/class-admin-options.php
@@ -6,8 +6,6 @@
* @author David Perez
* @copyright 2019 Closemarketing
* @version 1.0
- *
- * phpcs:disable WordPress.Files.FileName.InvalidClassFileName
*/
defined( 'ABSPATH' ) || exit;
@@ -30,8 +28,6 @@
* Handles admin settings page for FormsCRM plugin.
*/
class FORMSCRM_Admin {
- // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName -- File name follows plugin convention.
-
/**
* Construct of class
*/
@@ -166,43 +162,16 @@ public function create_admin_page() {
* @return void
*/
public function settings_page() {
- $source_shop_url = 'es' === strtok( get_locale(), '_' ) ? 'https://close.technology/' : 'https://close.technology/en/';
- $utm_source = '?utm_source=WordPress+Settings&utm_medium=plugin&utm_campaign=link';
- $slack_webhook_url = get_option( 'formscrm_slack_webhook_url', '' );
- ?>
-
-
-
-
-
-
-
+
@@ -229,88 +212,88 @@ public function settings_page() {
diff --git a/includes/crm-library/class-crmlib-clientify.php b/includes/crm-library/class-crmlib-clientify.php
index 6b27af7..aae8259 100644
--- a/includes/crm-library/class-crmlib-clientify.php
+++ b/includes/crm-library/class-crmlib-clientify.php
@@ -923,30 +923,29 @@ public function create_entry( $settings, $merge_vars ) {
// Add tags to deal.
if ( ! empty( $deal_tags ) ) {
$deal_tags_raw = explode( ',', $deal_tags );
- if ( ! empty( $deal_tags_raw ) ) {
- $deal_id = $result['data']['id'];
- foreach ( $deal_tags_raw as $deal_tag ) {
- $deal_tags_api = array(
- 'name' => sanitize_text_field( $deal_tag ),
- );
+ $deal_id = $result['data']['id'];
+
+ foreach ( $deal_tags_raw as $deal_tag ) {
+ $deal_tags_api = array(
+ 'name' => sanitize_text_field( $deal_tag ),
+ );
+
+ $result_tag = $this->request( 'deals/' . $deal_id . '/tags/', $deal_tags_api, $apikey );
- $result_tag = $this->request( 'deals/' . $deal_id . '/tags/', $deal_tags_api, $apikey );
-
- if ( 'ok' !== $result_tag['status'] ) {
- $result_deal_tag = sprintf(
- /* translators: %s: Tag name */
- __( 'Tag %s not added to deal', 'formscrm' ),
- $deal_tag,
- );
- } else {
- $result_deal_tag = sprintf(
- /* translators: %s: Tag name */
- __( 'Tag %s added to deal', 'formscrm' ),
- $deal_tag,
- );
- }
- $response_result['message'] .= ' ' . $result_deal_tag;
+ if ( 'ok' !== $result_tag['status'] ) {
+ $result_deal_tag = sprintf(
+ /* translators: %s: Tag name */
+ __( 'Tag %s not added to deal', 'formscrm' ),
+ $deal_tag,
+ );
+ } else {
+ $result_deal_tag = sprintf(
+ /* translators: %s: Tag name */
+ __( 'Tag %s added to deal', 'formscrm' ),
+ $deal_tag,
+ );
}
+ $response_result['message'] .= ' ' . $result_deal_tag;
}
}
diff --git a/includes/crm-library/class-crmlib-holded.php b/includes/crm-library/class-crmlib-holded.php
index 34b8dc4..61635d0 100644
--- a/includes/crm-library/class-crmlib-holded.php
+++ b/includes/crm-library/class-crmlib-holded.php
@@ -9,20 +9,16 @@
* @package FormsCRM
* @version 1.0.0
* @copyright 2021 Closemarketing
- *
- * phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound
- * phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound
*/
defined( 'ABSPATH' ) || exit;
-define( 'MAX_LIMIT_HOLDED_API', 500 ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- Legacy constant, changing would break compatibility.
+define( 'FORMSCRM_MAX_LIMIT_HOLDED_API', 500 );
/**
* Class for Holded connection.
*/
class CRMLIB_HOLDED {
- // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Legacy class name, changing would break compatibility.
/**
* Gets information from Holded CRM
*
@@ -133,7 +129,7 @@ public function search_by_email( $module, $email, $apikey ) {
}
}
- if ( count( $contacts['data'] ) === MAX_LIMIT_HOLDED_API ) {
+ if ( count( $contacts['data'] ) === FORMSCRM_MAX_LIMIT_HOLDED_API ) {
++$page;
} else {
$next = false;
@@ -394,7 +390,7 @@ public function create_entry( $settings, $merge_vars ) {
foreach ( $merge_vars as $element ) {
if ( false !== strpos( $element['name'], '|' ) ) {
$data_field = explode( '|', $element['name'] );
- if ( is_array( $data_field ) && ! empty( $data_field ) ) {
+ if ( is_array( $data_field ) ) {
$contact[ $data_field[0] ][ $data_field[1] ] = (string) $element['value'];
}
} elseif ( 'tags' === $element['name'] ) {
diff --git a/includes/formscrm-library/class-contactform7.php b/includes/formscrm-library/class-contactform7.php
index 0ade192..14e2468 100644
--- a/includes/formscrm-library/class-contactform7.php
+++ b/includes/formscrm-library/class-contactform7.php
@@ -6,8 +6,6 @@
* @author David Perez
* @copyright 2021 Closemarketing
* @version 3.3
- *
- * phpcs:disable WordPress.Files.FileName.InvalidClassFileName
*/
defined( 'ABSPATH' ) || exit;
@@ -21,8 +19,6 @@
* @version 1.0
*/
class FORMSCRM_CF7_Settings {
- // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName -- File name follows plugin convention.
-
/**
* CRM LIB external
*
@@ -56,32 +52,6 @@ public function show_cm_metabox( $panels ) {
return $panels;
}
- /**
- * Include library connector
- *
- * @param string $crmtype Type of CRM.
- * @return void
- */
- private function include_library( $crmtype ) {
- if ( isset( $crmtype ) ) {
- $crmname = strtolower( $crmtype );
- $crmclassname = str_replace( ' ', '', $crmname );
- $crmclassname = 'CRMLIB_' . strtoupper( $crmclassname );
- $crmname = str_replace( ' ', '_', $crmname );
-
- $array_path = formscrm_get_crmlib_path();
- if ( isset( $array_path[ $crmname ] ) ) {
- include_once $array_path[ $crmname ];
- }
-
- formscrm_debug_message( $array_path[ $crmname ] );
-
- if ( class_exists( $crmclassname ) ) {
- $this->crmlib = new $crmclassname();
- }
- }
- }
-
/**
* Adds CRM options in Contact Form 7
*
@@ -150,9 +120,11 @@ public function settings_add_crm( $args ) {
-
+
+ crmlib = formscrm_get_api_class( $cf7_crm['fc_crm_type'] );
+ ?>
- include_library( $cf7_crm['fc_crm_type'] ); ?>