Skip to content
Merged
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
61 changes: 61 additions & 0 deletions .cursor/rules/development.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
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.
19 changes: 19 additions & 0 deletions .distignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.distignore
.editorconfig
.git
.gitignore
.travis.yml
circle.yml
.DS_Store
.github
.cursor
composer.lock
.wordpress-org
*.sql
*.tar.gz
*.zip
phpstan.neon.dist
.phpcs.xml.dist
.phplint.yml
tests/
languages/
18 changes: 18 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Deploy to WordPress.org
on:
push:
tags:
- "*"
jobs:
tag:
name: New tag
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@master
- run: "composer install --no-dev"
- name: WordPress Plugin Deploy
uses: 10up/action-wordpress-plugin-deploy@stable
env:
SVN_PASSWORD: ${{ secrets.SVN_PASSWORD_CTECH }}
SVN_USERNAME: ${{ secrets.SVN_USERNAME_CTECH }}
65 changes: 65 additions & 0 deletions .github/workflows/php-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: PHP Code Linting

on:
push:
branches:
- main
- 'release/**'
# Only run if PHP-related files changed.
paths:
- '.github/workflows/php-lint.yml'
- '**.php'
- '.phpcs.xml.dist'
- 'phpstan.neon.dist'
- 'composer.json'
- 'composer.lock'
pull_request:
branches:
- main
- 'release/**'
- 'feature/**'
# Only run if PHP-related files changed.
paths:
- '.github/workflows/php-lint.yml'
- '**.php'
- '.phpcs.xml.dist'
- 'phpstan.neon.dist'
- 'composer.json'
- 'composer.lock'
types:
- opened
- reopened
- synchronize

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/trunk' }}
jobs:
php-lint:
name: PHP
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4

- uses: shivammathur/setup-php@v2
with:
php-version: '8.3'

- name: Validate Composer configuration
run: composer validate

- name: Install PHP dependencies
uses: ramsey/composer-install@a2636af0004d1c0499ffca16ac0b4cc94df70565
with:
composer-options: '--prefer-dist'

- name: PHP Lint
run: composer lint

- name: PHP PHPStan
run: composer phpstan




12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# WordPress
vendor/
node_modules/

# Numerous always-ignore extensions
*.log
*.zip
.DS_Store

# OS or Editor folders
._*.cache

47 changes: 47 additions & 0 deletions .phpcs.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0"?>
<ruleset name="WordPress Coding Standards based custom ruleset for kbcb plugin">
<description>Generally-applicable sniffs for WordPress plugins.</description>

<file>.</file>
<exclude-pattern>/tests/</exclude-pattern>
<exclude-pattern>/vendor/</exclude-pattern>
<exclude-pattern>/node_modules/</exclude-pattern>

<arg value="sp"/> <!-- Show sniff and progress -->
<arg name="basepath" value="./"/><!-- Strip the file paths down to the relevant bit -->
<arg name="colors"/>
<arg name="extensions" value="php"/>
<arg name="parallel" value="8"/>

<config name="testVersion" value="7.0-"/>
<rule ref="PHPCompatibilityWP"/>

<config name="minimum_supported_wp_version" value="4.6"/>
<rule ref="WordPress">
<exclude name="Universal.Arrays.DisallowShortArraySyntax"/>
<exclude name="WordPress.Files.FileName.InvalidClassFileName"/>
<exclude name="WordPress.Files.FileName.NotHyphenatedLowercase"/>
</rule>
<rule ref="WordPress.NamingConventions.PrefixAllGlobals">
<properties>
<property name="prefixes" type="array">
<element value="KBCB"/>
<element value="kbcb"/>
<element value="CLOSE"/>
</property>
</properties>
</rule>
<rule ref="WordPress.WP.I18n">
<properties>
<property name="text_domain" type="array">
<element value="knowledge-base-chatbot"/>
</property>
</properties>
</rule>
<rule ref="WordPress.WhiteSpace.ControlStructureSpacing">
<properties>
<property name="blank_line_check" value="true"/>
</properties>
</rule>
</ruleset>

4 changes: 4 additions & 0 deletions .phplint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
path: .
jobs: 10
extensions:
- php
18 changes: 18 additions & 0 deletions .wordpress-org/blueprints/blueprint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "https://playground.wordpress.net/blueprint-schema.json",
"landingPage": "/wp-admin/index.php",
"steps": [
{
"step": "installPlugin",
"pluginZipFile": {
"resource": "wordpress.org/plugins",
"slug": "knowledge-base-chatbot"
}
},
{
"step": "login",
"username": "admin",
"password": "password"
}
]
}
Binary file added .wordpress-org/screenshot-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
86 changes: 86 additions & 0 deletions assets/admin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* Multichats Admin Script
*
* @package CLOSE\KnowledgeBaseChatbot
* @author Closemarketing
* @copyright 2025 Closemarketing
*/

(function($) {
'use strict';

$(document).ready(function() {
let fileFrame;
const i18n = (typeof knowledgeBaseChatbotSettings !== 'undefined' && knowledgeBaseChatbotSettings.i18n) ? knowledgeBaseChatbotSettings.i18n : {};

// Handle icon upload button.
$(document).on('click', '.knowledge-base-chatbot-upload-icon', function(e) {
e.preventDefault();

const button = $(this);
const fieldId = button.data('field-id');
const fileInput = $('#' + fieldId);
const preview = button.closest('.knowledge-base-chatbot-icon-field').find('.knowledge-base-chatbot-icon-preview');
const removeBtn = button.closest('.knowledge-base-chatbot-icon-field').find('.knowledge-base-chatbot-remove-icon');

// If the media frame already exists, reopen it.
if (fileFrame) {
fileFrame.open();
return;
}

// Create the media frame.
fileFrame = wp.media({
title: i18n.mediaTitle || 'Select Icon (SVG or PNG)',
button: {
text: i18n.mediaButton || 'Use this icon',
},
multiple: false,
library: {
type: ['image/svg+xml', 'image/png'],
},
});

// When a file is selected, run a callback.
fileFrame.on('select', function() {
const attachment = fileFrame.state().get('selection').first().toJSON();

// Validate that it's an SVG or PNG file.
const allowedMimes = ['image/svg+xml', 'image/png'];
if (attachment.mime && !allowedMimes.includes(attachment.mime)) {
alert(i18n.svgOrPngOnly || 'Please select only SVG or PNG files.');
return;
}

// Check file extension as fallback.
const filename = attachment.filename ? attachment.filename.toLowerCase() : '';
if (filename && !filename.endsWith('.svg') && !filename.endsWith('.png')) {
alert(i18n.svgOrPngOnly || 'Please select only SVG or PNG files.');
return;
}

fileInput.val(attachment.id);
preview.html('<img src="' + attachment.url + '" alt="' + (i18n.chatIconAlt || 'Chat icon') + '" style="max-width: 100px; height: auto; display: block;" />');
removeBtn.show();
});

// Open the modal.
fileFrame.open();
});

// Handle icon remove button.
$(document).on('click', '.knowledge-base-chatbot-remove-icon', function(e) {
e.preventDefault();

const button = $(this);
const fieldId = button.data('field-id');
const fileInput = $('#' + fieldId);
const preview = button.closest('.knowledge-base-chatbot-icon-field').find('.knowledge-base-chatbot-icon-preview');

fileInput.val('');
preview.html('<p class="description">' + (i18n.noIconSelected || 'No icon selected. Default icon will be used.') + '</p>');
button.hide();
});
});
})(jQuery);

Loading