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
9 changes: 8 additions & 1 deletion .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,19 @@ jobs:

strategy:
matrix:
node: [22.x]
node: [21.x]
steps:

- name: Checkout
uses: actions/checkout@v2

- name: Install Composer dependencies
uses: php-actions/composer@v6
with:
version: 2
php_version: '8.0'
dev: no

- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@v1
with:
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/deploy-to-wpdotorg.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ jobs:
steps:
- uses: actions/checkout@master

- name: Install Composer dependencies
uses: php-actions/composer@v6
with:
version: 2
php_version: '8.0'
dev: no

- name: Setup Node
uses: actions/setup-node@v1
with:
Expand Down
5 changes: 0 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,6 @@ uploads/
error_log
debug.log

# Package managers
package-lock.json
yarn.lock
composer.lock

# Testing and documentation generated files
TESTING_CHECKLIST.md
DISTRIBUTION_READY.md
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
22
21
216 changes: 4 additions & 212 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Site Notes Toolkit for WordPress
# Site Notes: Feedback, Notes with Sitewide Visual Commenting

[![WordPress](https://img.shields.io/badge/WordPress-6.0%2B-blue.svg)](https://wordpress.org/)
[![WordPress plugin](https://img.shields.io/wordpress/plugin/dt/analogwp-site-notes.svg?style=flat)](https://wordpress.org/plugins/analogwp-site-notes/)
[![Installs](https://img.shields.io/wordpress/plugin/installs/analogwp-site-notes.svg)](https://wordpress.org/plugins/analogwp-site-notes/)
[![PHP](https://img.shields.io/badge/PHP-7.4%2B-purple.svg)](https://php.net/)
[![License](https://img.shields.io/badge/License-GPL%20v2%2B-green.svg)](https://www.gnu.org/licenses/gpl-2.0.html)
[![Version](https://img.shields.io/badge/Version-1.1.0-orange.svg)](https://github.com/analogwp/analogwp-site-notes)
[![WordPress plugin](https://img.shields.io/wordpress/plugin/v/analogwp-site-notes.svg?style=flat)](https://wordpress.org/plugins/analogwp-site-notes/)

**Transform client feedback into action.** Your clients click on any website element to leave comments. You manage everything through a beautiful admin dashboard. No more confusing emails or vague "fix the blue thing" requests.

Expand Down Expand Up @@ -125,182 +126,6 @@ Access settings at **Site Notes → Settings**:

---

## 🛠️ Technical Details

<details>
<summary><strong>Architecture & Stack</strong></summary>

### Frontend
- **React 18.2.0** - UI framework
- **WordPress Components** - @wordpress/components for consistency
- **html2canvas** - Screenshot capture
- **react-toastify** - Toast notifications
- **Sass** - Styling with CSS variables

### Backend
- **WordPress 6.0+** - Core platform
- **PHP 7.4+** - Server-side logic
- **MySQL** - Database storage
- **REST API** - AJAX endpoints

### Build System
- **@wordpress/scripts** - Build tooling
- **Webpack 5** - Module bundling
- **Babel** - JavaScript compilation
- **PostCSS** - CSS processing

</details>

<details>
<summary><strong>Database Structure</strong></summary>

### Comments Table (\`wp_agwp_sn_comments\`)
\`\`\`sql
CREATE TABLE wp_agwp_sn_comments (
id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
post_id bigint(20) UNSIGNED NOT NULL,
user_id bigint(20) UNSIGNED NOT NULL,
comment_text text NOT NULL,
element_selector varchar(500) DEFAULT NULL,
screenshot_url longtext DEFAULT NULL,
x_position int(11) DEFAULT NULL,
y_position int(11) DEFAULT NULL,
page_url varchar(500) DEFAULT NULL,
status varchar(20) DEFAULT 'open',
priority varchar(20) DEFAULT 'medium',
category varchar(100) DEFAULT NULL,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY post_id (post_id),
KEY user_id (user_id),
KEY status (status),
KEY priority (priority),
KEY category (category)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
\`\`\`

### Replies Table (\`wp_agwp_sn_comment_replies\`)
\`\`\`sql
CREATE TABLE wp_agwp_sn_comment_replies (
id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
comment_id bigint(20) UNSIGNED NOT NULL,
user_id bigint(20) UNSIGNED NOT NULL,
reply_text text NOT NULL,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY comment_id (comment_id),
KEY user_id (user_id),
FOREIGN KEY (comment_id) REFERENCES wp_agwp_sn_comments(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
\`\`\`

### Timesheet Table (\`wp_agwp_sn_timesheet\`)
\`\`\`sql
CREATE TABLE wp_agwp_sn_timesheet (
id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
comment_id bigint(20) UNSIGNED NOT NULL,
user_id bigint(20) UNSIGNED NOT NULL,
hours decimal(10,2) NOT NULL,
description text DEFAULT NULL,
logged_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY comment_id (comment_id),
KEY user_id (user_id),
FOREIGN KEY (comment_id) REFERENCES wp_agwp_sn_comments(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
\`\`\`

</details>

<details>
<summary><strong>Component Structure</strong></summary>

\`\`\`
src/
├── frontend/
│ ├── components/
│ │ ├── VisualCommentsApp.js # Main app container
│ │ ├── CommentToggle.js # Toggle button
│ │ ├── CommentOverlay.js # Instruction overlay
│ │ ├── CommentPopup.js # New comment form
│ │ ├── CommentsDisplay.js # Comments manager
│ │ └── CommentMarker.js # Individual markers
│ ├── styles/
│ │ └── frontend.scss # Frontend styles
│ └── frontend.js # Entry point
├── admin/
│ ├── components/
│ │ ├── Dashboard.js # Main dashboard
│ │ ├── TaskBoard.js # Kanban board
│ │ ├── TaskCard.js # Individual tasks
│ │ ├── TaskDetail.js # Task detail view
│ │ ├── Timesheet.js # Timesheet view
│ │ ├── Settings.js # Settings page
│ │ └── settings/
│ │ ├── AccessControlSettings.js
│ │ ├── CategorySettings.js
│ │ └── PrioritySettings.js
│ ├── styles/
│ │ └── admin.scss # Admin styles
│ └── admin.js # Entry point
└── shared/
└── utils/ # Shared utilities
\`\`\`

</details>

<details>
<summary><strong>Security Features</strong></summary>

### Implemented Protections
- ✅ **Nonce Verification** - All AJAX requests protected with WordPress nonces
- ✅ **User Capability Checks** - Role-based access control via \`user_has_access()\`
- ✅ **Data Sanitization** - All inputs cleaned with \`sanitize_text_field()\` and \`wp_kses_post()\`
- ✅ **SQL Injection Prevention** - Prepared statements via \`$wpdb->prepare()\`
- ✅ **XSS Protection** - Output escaping with \`esc_html()\`, \`esc_url()\`, \`esc_attr()\`
- ✅ **CSRF Protection** - WordPress nonces on all forms and AJAX calls

### Permission System
\`\`\`php
// Helper method in main plugin class
public static function user_has_access() {
if ( ! is_user_logged_in() ) {
return false;
}

if ( current_user_can( 'manage_options' ) ) {
return true; // Admin always has access
}

$settings = get_option( 'agwp_sn_settings', array() );
$allowed_roles = $settings['general']['allowed_roles'] ?? array();

$user = wp_get_current_user();
return ! empty( array_intersect( $allowed_roles, $user->roles ) );
}
\`\`\`

</details>

<details>
<summary><strong>Browser Compatibility</strong></summary>

| Browser | Version | Status |
|---------|---------|--------|
| Chrome | 80+ | ✅ Full Support |
| Firefox | 75+ | ✅ Full Support |
| Safari | 13+ | ✅ Full Support |
| Edge | 80+ | ✅ Full Support |
| Mobile Safari | iOS 13+ | ✅ Full Support |
| Chrome Mobile | Android 8+ | ✅ Full Support |

</details>

---

## 🔧 Development

### Prerequisites
Expand Down Expand Up @@ -481,39 +306,6 @@ Check debug log at \`wp-content/debug.log\`

---

## 📈 Roadmap

### Version 1.1.0 ✅ **CURRENT**
- ✅ Task editing functionality
- ✅ Persistent timesheet tracking
- ✅ Modern toast notifications
- ✅ Enhanced UI/UX improvements
- ✅ Access control system
- ✅ User avatars in replies

### Version 1.2 🔜 **NEXT**
- ⏳ Email notifications for new comments and status changes
- ⏳ Enhanced mobile interface with touch optimizations
- ⏳ CSV/PDF export for timesheet reports
- ⏳ Advanced filtering with saved filter presets
- ⏳ Bulk actions for task management
- ⏳ Custom fields for tasks
- 🎨 Custom branding options

### Version 1.3 📅 **PLANNED**
- ⏳ Advanced analytics dashboard with charts
- ⏳ Custom branding options
- ⏳ REST API endpoints for third-party integrations
- ⏳ Progressive Web App (PWA) features
- ⏳ Multi-language support (WPML/Polylang)

### Future Ideas 🔮
- ⏳ Cloud storage for screenshots (AWS S3, Google Cloud)
- ⏳ AI-powered resolutions
- ⏳ Native mobile app companion
- ⏳ Real-time collaboration with WebSocket
- ⏳ Project templates and workflows

---

## 🤝 Contributing
Expand Down
77 changes: 74 additions & 3 deletions analogwp-site-notes.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?php
/**
* Plugin Name: AnalogWP Site Notes
* Plugin Name: Site Notes
* Plugin URI: https://github.com/analogwp/analogwp-site-notes
* Description: A comprehensive solution for agency-client transitions with visual commenting system, maintenance scheduling, and client-friendly editing mode.
* Version: 1.0.3
* Version: 1.1.0
* Author: AnalogWP
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
Expand All @@ -19,11 +19,82 @@
defined( 'ABSPATH' ) || exit;

// Define plugin constants.
define( 'AGWP_SN_VERSION', '1.0.3' );
define( 'AGWP_SN_VERSION', '1.1.0' );
define( 'AGWP_SN_PLUGIN_FILE', __FILE__ );
define( 'AGWP_SN_PLUGIN_URL', plugin_dir_url( AGWP_SN_PLUGIN_FILE ) );
define( 'AGWP_SN_PLUGIN_PATH', plugin_dir_path( AGWP_SN_PLUGIN_FILE ) );

// Load dependencies.
$vendor_file = __DIR__ . '/vendor/autoload.php';

if ( is_readable( $vendor_file ) ) {
require_once $vendor_file;
}


if ( ! function_exists( 'agwp_sn_fs' ) ) {
/**
* Create a helper function for easy SDK access.
*
* @since 1.1.0
*
* @return object
*/
function agwp_sn_fs() {
global $agwp_sn_fs;

if ( ! isset( $agwp_sn_fs ) ) {
// Include Freemius SDK.
// SDK is auto-loaded through Composer.

$agwp_sn_fs = fs_dynamic_init(
array(
'id' => '22009',
'slug' => 'analogwp-site-notes',
'type' => 'plugin',
'public_key' => 'pk_7c9e9f440cfa3b8d5efd79736df85',
'is_premium' => false,
'has_addons' => false,
'has_paid_plans' => false,
'menu' => array(
'slug' => 'agwp-sn-dashboard',
'override_exact' => true,
'first-path' => 'admin.php?page=agwp-sn-dashboard',
'account' => false,
'support' => false,
'contact' => false,
'addons' => false,
),
)
);
}

return $agwp_sn_fs;
}

// Init Freemius.
agwp_sn_fs();
// Signal that SDK was initiated.
do_action( 'agwp_sn_fs_loaded' );

/**
* Custom settings URL.
*
* @return string
*/
function agwp_sn_fs_settings_url() {
return admin_url( 'admin.php?page=agwp-sn-dashboard' );
}

agwp_sn_fs()->add_filter( 'connect_url', 'agwp_sn_fs_settings_url' );
agwp_sn_fs()->add_filter( 'after_skip_url', 'agwp_sn_fs_settings_url' );
agwp_sn_fs()->add_filter( 'after_connect_url', 'agwp_sn_fs_settings_url' );
agwp_sn_fs()->add_filter( 'after_pending_connect_url', 'agwp_sn_fs_settings_url' );

// Disable automatic redirect on activation to prevent redirect to settings page before menu is registered.
agwp_sn_fs()->add_filter( 'redirect_on_activation', '__return_false' );
}

/**
* Load the main plugin class.
*/
Expand Down
Loading