Changeset 3183478
- Timestamp:
- 11/07/2024 01:13:18 AM (16 months ago)
- Location:
- litesurveys
- Files:
-
- 62 added
- 2 deleted
- 7 edited
- 1 copied
-
assets/screenshot-2.png (modified) (previous)
-
tags/2.0.0 (copied) (copied from litesurveys/trunk)
-
tags/2.0.0/.wordpress-org (added)
-
tags/2.0.0/.wordpress-org/screenshot-1.png (added)
-
tags/2.0.0/.wordpress-org/screenshot-2.png (added)
-
tags/2.0.0/README.md (deleted)
-
tags/2.0.0/includes (added)
-
tags/2.0.0/includes/class-rest-api.php (added)
-
tags/2.0.0/includes/index.php (added)
-
tags/2.0.0/languages (added)
-
tags/2.0.0/languages/index.php (added)
-
tags/2.0.0/languages/litesurveys.pot (added)
-
tags/2.0.0/litesurveys-wordpress-plugin.php (modified) (3 diffs)
-
tags/2.0.0/phpunit.xml.dist (added)
-
tags/2.0.0/readme.txt (modified) (3 diffs)
-
tags/2.0.0/resources (added)
-
tags/2.0.0/resources/css (added)
-
tags/2.0.0/resources/css/admin.css (added)
-
tags/2.0.0/resources/css/admin.min.css (added)
-
tags/2.0.0/resources/css/frontend.css (added)
-
tags/2.0.0/resources/css/frontend.min.css (added)
-
tags/2.0.0/resources/css/index.php (added)
-
tags/2.0.0/resources/index.php (added)
-
tags/2.0.0/resources/js (added)
-
tags/2.0.0/resources/js/admin.js (added)
-
tags/2.0.0/resources/js/admin.min.js (added)
-
tags/2.0.0/resources/js/frontend.js (added)
-
tags/2.0.0/resources/js/frontend.min.js (added)
-
tags/2.0.0/resources/js/index.php (added)
-
tags/2.0.0/uninstall.php (modified) (1 diff)
-
tags/2.0.0/views (added)
-
tags/2.0.0/views/admin (added)
-
tags/2.0.0/views/admin/index.php (added)
-
tags/2.0.0/views/admin/survey-edit.php (added)
-
tags/2.0.0/views/admin/survey-submissions.php (added)
-
tags/2.0.0/views/admin/surveys-admin.php (added)
-
tags/2.0.0/views/index.php (added)
-
trunk/.wordpress-org (added)
-
trunk/.wordpress-org/screenshot-1.png (added)
-
trunk/.wordpress-org/screenshot-2.png (added)
-
trunk/README.md (deleted)
-
trunk/includes (added)
-
trunk/includes/class-rest-api.php (added)
-
trunk/includes/index.php (added)
-
trunk/languages (added)
-
trunk/languages/index.php (added)
-
trunk/languages/litesurveys.pot (added)
-
trunk/litesurveys-wordpress-plugin.php (modified) (3 diffs)
-
trunk/phpunit.xml.dist (added)
-
trunk/readme.txt (modified) (3 diffs)
-
trunk/resources (added)
-
trunk/resources/css (added)
-
trunk/resources/css/admin.css (added)
-
trunk/resources/css/admin.min.css (added)
-
trunk/resources/css/frontend.css (added)
-
trunk/resources/css/frontend.min.css (added)
-
trunk/resources/css/index.php (added)
-
trunk/resources/index.php (added)
-
trunk/resources/js (added)
-
trunk/resources/js/admin.js (added)
-
trunk/resources/js/admin.min.js (added)
-
trunk/resources/js/frontend.js (added)
-
trunk/resources/js/frontend.min.js (added)
-
trunk/resources/js/index.php (added)
-
trunk/uninstall.php (modified) (1 diff)
-
trunk/views (added)
-
trunk/views/admin (added)
-
trunk/views/admin/index.php (added)
-
trunk/views/admin/survey-edit.php (added)
-
trunk/views/admin/survey-submissions.php (added)
-
trunk/views/admin/surveys-admin.php (added)
-
trunk/views/index.php (added)
Legend:
- Unmodified
- Added
- Removed
-
litesurveys/tags/2.0.0/litesurveys-wordpress-plugin.php
r3130242 r3183478 2 2 /** 3 3 * Plugin Name: LiteSurveys 4 * Description: Adds your LiteSurveys to your WordPress site5 * Version: 1.0.36 * Requires at least: 6. 04 * Description: Adds simple one-question surveys to your WordPress site 5 * Version: 2.0.0 6 * Requires at least: 6.1 7 7 * Requires PHP: 8.0 8 8 * License: GPLv3 … … 10 10 * Author: LiteSurveys 11 11 * Author URI: https://litesurveys.com 12 * Text Domain: litesurveys 12 13 * 13 * @ authorLiteSurveys14 * @package LiteSurveys 14 15 */ 15 16 16 17 // Exits if accessed directly. 17 if ( ! defined( 'ABSPATH' ) ) { 18 exit; 19 } 20 21 // Define plugin version constant. 22 define( 'LSAPP_PLUGIN_VERSION', '1.0.3' ); 23 18 defined( 'ABSPATH' ) || exit; 24 19 25 20 /** 26 * The plugin's main class21 * Main LiteSurveys plugin class. 27 22 * 28 23 * @since 1.0.0 29 24 */ 30 class LSAPP_LiteSurveys_Integration { 31 32 33 34 /** 35 * Initializes our plugin 25 class LSAPP_LiteSurveys { 26 27 /** 28 * Plugin version. 29 * 30 * @var string 31 */ 32 const VERSION = '2.0.0'; 33 34 /** 35 * The single instance of the class. 36 * 37 * @var LSAPP_LiteSurveys 38 */ 39 private static $instance = null; 40 41 /** 42 * Plugin path. 43 * 44 * @var string 45 */ 46 private $plugin_path; 47 48 /** 49 * Plugin URL. 50 * 51 * @var string 52 */ 53 private $plugin_url; 54 55 /** 56 * Our REST API functionality. 57 */ 58 private $rest_api; 59 60 /** 61 * Cache time in seconds. 62 * 63 * @var int 64 */ 65 const CACHE_TIME = 300; 66 67 /** 68 * Main LSAPP_LiteSurveys Instance. 69 * 70 * Ensures only one instance of LSAPP_LiteSurveys is loaded or can be loaded. 36 71 * 37 72 * @since 1.0.0 38 */ 39 public static function init() { 40 self::load_hooks(); 41 } 42 43 /** 44 * Adds in any plugin-wide hooks 73 * @return LSAPP_LiteSurveys Main instance 74 */ 75 public static function get_instance() { 76 if ( is_null( self::$instance ) ) { 77 self::$instance = new self(); 78 } 79 return self::$instance; 80 } 81 82 /** 83 * LSAPP_LiteSurveys Constructor. 84 */ 85 private function __construct() { 86 $this->plugin_path = plugin_dir_path( __FILE__ ); 87 $this->plugin_url = plugin_dir_url( __FILE__ ); 88 89 // Include the REST API class 90 require_once $this->plugin_path . 'includes/class-rest-api.php'; 91 $this->rest_api = new LSAPP_REST_API(); 92 93 $this->init_hooks(); 94 } 95 96 /** 97 * Initialize WordPress hooks. 45 98 * 46 99 * @since 1.0.0 47 100 */ 48 public static function load_hooks() { 49 if (is_admin()) { 50 add_action( 'admin_menu', array( __CLASS__, 'setup_admin_menu' ) ); 51 add_action( 'admin_init', array( __CLASS__, 'admin_init' ) ); 101 private function init_hooks() { 102 // Setting up plugin upon activation. 103 register_activation_hook( __FILE__, array( $this, 'activate_plugin' ) ); 104 105 // Load translations. 106 add_action( 'init', array( $this, 'load_textdomain' ) ); 107 108 // Add Admin code. 109 add_action( 'admin_menu', array( $this, 'add_admin_menu' ) ); 110 add_action( 'admin_post_save_survey', array( $this, 'handle_save_survey' ) ); 111 add_action( 'admin_post_delete_survey', array( $this, 'handle_delete_survey' ) ); 112 add_action( 'admin_post_delete_submission', array( $this, 'handle_delete_submission' ) ); 113 add_action( 'admin_notices', array( $this, 'display_admin_notices' ) ); 114 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) ); 115 add_filter( 'plugin_action_links', array( $this, 'plugin_action_links' ), 10, 2 ); 116 117 // Add REST API endpoints. 118 add_action( 'rest_api_init', array( $this->rest_api, 'register_rest_routes' ) ); 119 120 // Add frontend script. 121 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_assets' ) ); 122 } 123 124 /** 125 * Activate plugin and create database tables. 126 * 127 * @since 1.0.0 128 */ 129 public function activate_plugin() { 130 global $wpdb; 131 132 if ( ! current_user_can( 'activate_plugins' ) ) { 133 return; 134 } 135 136 $charset_collate = $wpdb->get_charset_collate(); 137 138 $this->create_database_tables( $charset_collate ); 139 140 add_option( 'lsapp_litesurveys_version', self::VERSION ); 141 } 142 143 /** 144 * Create plugin database tables. 145 * 146 * @since 1.0.0 147 * @param string $charset_collate Database charset and collation. 148 */ 149 public function create_database_tables( $charset_collate ) { 150 global $wpdb; 151 152 // Create surveys table. 153 $sql_surveys = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}litesurveys_surveys ( 154 id bigint(20) NOT NULL AUTO_INCREMENT, 155 name varchar(100) NOT NULL, 156 active tinyint(1) DEFAULT 0, 157 submit_message text NOT NULL, 158 targeting_settings json DEFAULT NULL, 159 appearance_settings json DEFAULT NULL, 160 created_at timestamp DEFAULT CURRENT_TIMESTAMP, 161 updated_at timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 162 deleted_at timestamp NULL DEFAULT NULL, 163 PRIMARY KEY (id) 164 ) $charset_collate;"; 165 166 // Create questions table. 167 $sql_questions = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}litesurveys_questions ( 168 id bigint(20) NOT NULL AUTO_INCREMENT, 169 survey_id bigint(20) NOT NULL, 170 type varchar(25) NOT NULL, 171 content varchar(100) NOT NULL, 172 answers json DEFAULT NULL, 173 created_at timestamp DEFAULT CURRENT_TIMESTAMP, 174 updated_at timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 175 deleted_at timestamp NULL DEFAULT NULL, 176 PRIMARY KEY (id), 177 FOREIGN KEY (survey_id) REFERENCES {$wpdb->prefix}litesurveys_surveys(id) ON DELETE CASCADE 178 ) $charset_collate;"; 179 180 // Create submissions table. 181 $sql_submissions = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}litesurveys_submissions ( 182 id bigint(20) NOT NULL AUTO_INCREMENT, 183 survey_id bigint(20) NOT NULL, 184 page varchar(255) DEFAULT NULL, 185 created_at timestamp DEFAULT CURRENT_TIMESTAMP, 186 deleted_at timestamp NULL DEFAULT NULL, 187 PRIMARY KEY (id), 188 FOREIGN KEY (survey_id) REFERENCES {$wpdb->prefix}litesurveys_surveys(id) ON DELETE CASCADE 189 ) $charset_collate;"; 190 191 // Create responses table. 192 $sql_responses = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}litesurveys_responses ( 193 id bigint(20) NOT NULL AUTO_INCREMENT, 194 submission_id bigint(20) NOT NULL, 195 question_id bigint(20) NOT NULL, 196 content text NOT NULL, 197 created_at timestamp DEFAULT CURRENT_TIMESTAMP, 198 deleted_at timestamp NULL DEFAULT NULL, 199 PRIMARY KEY (id), 200 FOREIGN KEY (submission_id) REFERENCES {$wpdb->prefix}litesurveys_submissions(id) ON DELETE CASCADE, 201 FOREIGN KEY (question_id) REFERENCES {$wpdb->prefix}litesurveys_questions(id) ON DELETE CASCADE 202 ) $charset_collate;"; 203 204 require_once ABSPATH . 'wp-admin/includes/upgrade.php'; 205 dbDelta( $sql_surveys ); 206 dbDelta( $sql_questions ); 207 dbDelta( $sql_submissions ); 208 dbDelta( $sql_responses ); 209 } 210 211 /** 212 * Load plugin text domain for translations. 213 * 214 * @since 1.0.0 215 */ 216 public function load_textdomain() { 217 load_plugin_textdomain( 218 'litesurveys', 219 false, 220 dirname( plugin_basename( __FILE__ ) ) . '/languages' 221 ); 222 } 223 224 /** 225 * Enqueue admin scripts and styles. 226 * 227 * @since 1.0.0 228 * @param string $hook The current admin page. 229 */ 230 public function enqueue_admin_assets( $hook ) { 231 if ( false === strpos( $hook, 'litesurveys' ) ) { 232 return; 233 } 234 235 $action = isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : 'list'; 236 237 if ( 'edit' === $action || 'new' === $action ) { 238 $suffix = $this->get_asset_suffix(); 239 wp_enqueue_style( 240 'litesurveys-admin', 241 $this->plugin_url . "resources/css/admin{$suffix}.css", 242 array(), 243 self::VERSION 244 ); 245 246 wp_enqueue_script( 247 'litesurveys-admin', 248 $this->plugin_url . "resources/js/admin{$suffix}.js", 249 array( 'jquery' ), 250 self::VERSION, 251 true 252 ); 253 } 254 } 255 256 /** 257 * Add admin menu items. 258 * 259 * @since 1.0.0 260 */ 261 public function add_admin_menu() { 262 add_menu_page( 263 'LiteSurveys', 264 'LiteSurveys', 265 'manage_options', 266 'LSAPP_litesurveys', 267 array( $this, 'render_admin_page' ), 268 'dashicons-chart-bar', 269 30 270 ); 271 } 272 273 /** 274 * Render the admin page content. 275 * 276 * @since 1.0.0 277 */ 278 public function render_admin_page() { 279 $this->verify_admin_access(); 280 281 global $wpdb; 282 283 $action = isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : 'list'; 284 285 switch ( $action ) { 286 case 'view-responses': 287 $survey_id = isset( $_GET['id'] ) ? absint( $_GET['id'] ) : 0; 288 if ( ! $survey_id ) { 289 wp_die( esc_html__( 'Invalid survey ID.', 'litesurveys' ) ); 290 } 291 292 // Get survey data with question. 293 $survey = $wpdb->get_row( 294 $wpdb->prepare( 295 "SELECT s.*, q.content as question_content 296 FROM {$wpdb->prefix}litesurveys_surveys s 297 LEFT JOIN {$wpdb->prefix}litesurveys_questions q ON s.id = q.survey_id 298 WHERE s.id = %d AND s.deleted_at IS NULL", 299 $survey_id 300 ) 301 ); 302 303 if ( ! $survey ) { 304 wp_die( esc_html__( 'Survey not found.', 'litesurveys' ) ); 305 } 306 307 // Pagination settings. 308 $per_page = 20; 309 $current_page = isset( $_GET['paged'] ) ? max( 1, absint( $_GET['paged'] ) ) : 1; 310 $offset = ( $current_page - 1 ) * $per_page; 311 312 // Get total count for pagination. 313 $total_items = $wpdb->get_var( 314 $wpdb->prepare( 315 "SELECT COUNT(*) 316 FROM {$wpdb->prefix}litesurveys_submissions s 317 WHERE s.survey_id = %d AND s.deleted_at IS NULL", 318 $survey_id 319 ) 320 ); 321 322 // Get submissions with responses. 323 $submissions = $wpdb->get_results( 324 $wpdb->prepare( 325 "SELECT s.id, s.created_at, s.page, r.content as response 326 FROM {$wpdb->prefix}litesurveys_submissions s 327 LEFT JOIN {$wpdb->prefix}litesurveys_responses r ON s.id = r.submission_id 328 WHERE s.survey_id = %d AND s.deleted_at IS NULL 329 ORDER BY s.created_at DESC 330 LIMIT %d OFFSET %d", 331 $survey_id, 332 $per_page, 333 $offset 334 ) 335 ); 336 337 // Calculate pagination values. 338 $total_pages = ceil( $total_items / $per_page ); 339 340 include $this->plugin_path . 'views/admin/survey-submissions.php'; 341 break; 342 343 case 'edit': 344 case 'new': 345 // Get survey data if editing. 346 $survey_id = isset( $_GET['id'] ) ? absint( $_GET['id'] ) : 0; 347 $survey = null; 348 349 if ( $survey_id ) { 350 $survey = $wpdb->get_row( 351 $wpdb->prepare( 352 "SELECT * FROM {$wpdb->prefix}litesurveys_surveys WHERE id = %d AND deleted_at IS NULL", 353 $survey_id 354 ) 355 ); 356 357 if ( $survey ) { 358 $question = $wpdb->get_row( 359 $wpdb->prepare( 360 "SELECT * FROM {$wpdb->prefix}litesurveys_questions WHERE survey_id = %d AND deleted_at IS NULL", 361 $survey_id 362 ) 363 ); 364 365 if ( $question ) { 366 $survey->question = $question; 367 $survey->question->answers = json_decode( $question->answers ); 368 } 369 370 $survey->targeting_settings = json_decode( $survey->targeting_settings ); 371 $survey->appearance_settings = json_decode( $survey->appearance_settings ); 372 } 373 } 374 375 // Set defaults for new survey. 376 if ( ! $survey ) { 377 $survey = (object) array( 378 'name' => '', 379 'active' => false, 380 'submit_message' => 'Thanks! I appreciate you taking the time to respond.', 381 'targeting_settings' => (object) array( 382 'targets' => (object) array( 383 'show' => 'all', 384 'includes' => array(), 385 'excludes' => array(), 386 ), 387 'trigger' => array( 388 (object) array( 389 'type' => 'auto', 390 'auto_timing' => 5, 391 ), 392 ), 393 ), 394 'appearance_settings' => (object) array( 395 'horizontal_position' => 'right', 396 ), 397 'question' => (object) array( 398 'type' => 'multiple-choice', 399 'content' => '', 400 'answers' => array( '', '', '' ), 401 ), 402 ); 403 } 404 include $this->plugin_path . 'views/admin/survey-edit.php'; 405 break; 406 407 default: 408 $surveys = $wpdb->get_results( 409 "SELECT * FROM {$wpdb->prefix}litesurveys_surveys WHERE deleted_at IS NULL ORDER BY created_at DESC" 410 ); 411 include $this->plugin_path . 'views/admin/surveys-admin.php'; 412 break; 413 } 414 } 415 416 /** 417 * Handle saving survey data. 418 * 419 * @since 1.0.0 420 */ 421 public function handle_save_survey() { 422 $this->verify_admin_access(); 423 424 check_admin_referer( 'save_survey', 'survey_nonce' ); 425 426 global $wpdb; 427 428 $survey_id = isset( $_POST['survey_id'] ) ? absint( $_POST['survey_id'] ) : 0; 429 $save_type = isset( $_POST['save_type'] ) ? sanitize_text_field( wp_unslash( $_POST['save_type'] ) ) : 'draft'; 430 431 try { 432 // Validate required fields. 433 if ( empty( $_POST['survey_name'] ) ) { 434 throw new Exception( __( 'Survey name is required.', 'litesurveys' ) ); 435 } 436 437 if ( empty( $_POST['question_content'] ) ) { 438 throw new Exception( __( 'Survey question is required.', 'litesurveys' ) ); 439 } 440 441 // Prepare targeting settings. 442 $targeting_settings = array( 443 'targets' => array( 444 'show' => sanitize_text_field( wp_unslash( $_POST['targeting_show'] ) ), 445 'includes' => isset( $_POST['includes'] ) ? 446 array_map( 'sanitize_text_field', wp_unslash( $_POST['includes'] ) ) : array(), 447 'excludes' => isset( $_POST['excludes'] ) ? 448 array_map( 'sanitize_text_field', wp_unslash( $_POST['excludes'] ) ) : array(), 449 ), 450 'trigger' => array( 451 array( 452 'type' => sanitize_text_field( wp_unslash( $_POST['trigger_type'] ) ), 453 'auto_timing' => absint( $_POST['auto_timing'] ), 454 ), 455 ), 456 ); 457 458 // Prepare appearance settings. 459 $appearance_settings = array( 460 'horizontal_position' => sanitize_text_field( wp_unslash( $_POST['horizontal_position'] ) ), 461 ); 462 463 // Sanitize JSON data. 464 $targeting_json = $this->sanitize_json_data( $targeting_settings ); 465 $appearance_json = $this->sanitize_json_data( $appearance_settings ); 466 467 if ( false === $targeting_json || false === $appearance_json ) { 468 throw new Exception( __( 'Invalid settings data.', 'litesurveys' ) ); 469 } 470 471 // Prepare survey data. 472 $survey_data = array( 473 'name' => sanitize_text_field( wp_unslash( $_POST['survey_name'] ) ), 474 'submit_message' => sanitize_textarea_field( wp_unslash( $_POST['submit_message'] ) ), 475 'active' => ( 'publish' === $save_type ), 476 'targeting_settings' => $targeting_json, 477 'appearance_settings' => $appearance_json, 478 ); 479 480 // Prepare answers for multiple choice. 481 $answers = array(); 482 if ( 'multiple-choice' === $_POST['question_type'] && ! empty( $_POST['answers'] ) ) { 483 $answers = array_filter( array_map( 'sanitize_text_field', wp_unslash( $_POST['answers'] ) ) ); 484 if ( count( $answers ) < 2 ) { 485 throw new Exception( __( 'Multiple choice questions must have at least 2 answers.', 'litesurveys' ) ); 486 } 487 } 488 489 // Prepare question data. 490 $question_data = array( 491 'type' => sanitize_text_field( wp_unslash( $_POST['question_type'] ) ), 492 'content' => sanitize_textarea_field( wp_unslash( $_POST['question_content'] ) ), 493 'answers' => $this->sanitize_json_data( $answers ), 494 ); 495 496 // Start transaction. 497 $wpdb->query( 'START TRANSACTION' ); 498 499 if ( $survey_id ) { 500 // Update existing survey. 501 $result = $wpdb->update( 502 $wpdb->prefix . 'litesurveys_surveys', 503 $survey_data, 504 array( 'id' => $survey_id ) 505 ); 506 507 if ( false === $result ) { 508 throw new Exception( __( 'Failed to update survey.', 'litesurveys' ) ); 509 } 510 511 // Update or insert question. 512 $existing_question = $wpdb->get_row( 513 $wpdb->prepare( 514 "SELECT id FROM {$wpdb->prefix}litesurveys_questions WHERE survey_id = %d AND deleted_at IS NULL", 515 $survey_id 516 ) 517 ); 518 519 if ( $existing_question ) { 520 $result = $wpdb->update( 521 $wpdb->prefix . 'litesurveys_questions', 522 $question_data, 523 array( 'id' => $existing_question->id ) 524 ); 525 if ( false === $result ) { 526 throw new Exception( __( 'Failed to update survey question.', 'litesurveys' ) ); 527 } 528 } else { 529 $question_data['survey_id'] = $survey_id; 530 $result = $wpdb->insert( $wpdb->prefix . 'litesurveys_questions', $question_data ); 531 if ( ! $result ) { 532 throw new Exception( __( 'Failed to create survey question.', 'litesurveys' ) ); 533 } 534 } 535 } else { 536 // Insert new survey. 537 $result = $wpdb->insert( $wpdb->prefix . 'litesurveys_surveys', $survey_data ); 538 if ( ! $result ) { 539 throw new Exception( __( 'Failed to create survey.', 'litesurveys' ) ); 540 } 541 $survey_id = $wpdb->insert_id; 542 543 // Insert question. 544 $question_data['survey_id'] = $survey_id; 545 $result = $wpdb->insert( $wpdb->prefix . 'litesurveys_questions', $question_data ); 546 if ( ! $result ) { 547 throw new Exception( __( 'Failed to create survey question.', 'litesurveys' ) ); 548 } 549 } 550 551 // Commit transaction. 552 $wpdb->query( 'COMMIT' ); 553 554 // Clear caches after successful save. 555 $this->clear_survey_caches(); 556 557 // Build redirect arguments. 558 $redirect_args = array( 559 'page' => 'LSAPP_litesurveys', 560 'action' => 'edit', 561 'id' => $survey_id, 562 'message' => 'publish' === $save_type ? 'survey-published' : 'survey-saved', 563 ); 564 565 // Redirect with success message. 566 wp_safe_redirect( add_query_arg( $redirect_args, admin_url( 'admin.php' ) ) ); 567 exit; 568 569 } catch ( Exception $e ) { 570 // Rollback transaction. 571 $wpdb->query( 'ROLLBACK' ); 572 573 wp_safe_redirect( 574 add_query_arg( 575 array( 576 'page' => 'LSAPP_litesurveys', 577 'action' => $survey_id ? 'edit' : 'new', 578 'id' => $survey_id, 579 'message' => 'error', 580 'error' => rawurlencode( $e->getMessage() ), 581 ), 582 admin_url( 'admin.php' ) 583 ) 584 ); 585 exit; 586 } 587 } 588 589 public function handle_delete_survey() { 590 $this->verify_admin_access(); 591 592 // Verify nonce 593 $nonce = isset($_REQUEST['_wpnonce']) ? sanitize_text_field($_REQUEST['_wpnonce']) : ''; 594 $survey_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; 595 596 if (!wp_verify_nonce($nonce, 'delete-survey_' . $survey_id)) { 597 wp_die(__('Security check failed.', 'litesurveys')); 598 } 599 600 if (!$survey_id) { 601 wp_die(__('Invalid survey ID.', 'litesurveys')); 602 } 603 604 global $wpdb; 605 606 try { 607 // Start transaction 608 $wpdb->query('START TRANSACTION'); 609 610 // Soft delete the survey 611 $result = $wpdb->update( 612 $wpdb->prefix . 'litesurveys_surveys', 613 array('deleted_at' => current_time('mysql')), 614 array('id' => $survey_id), 615 array('%s'), 616 array('%d') 617 ); 618 619 if ($result === false) { 620 throw new Exception(__('Failed to delete survey.', 'litesurveys')); 621 } 622 623 // Soft delete associated questions 624 $wpdb->update( 625 $wpdb->prefix . 'litesurveys_questions', 626 array('deleted_at' => current_time('mysql')), 627 array('survey_id' => $survey_id), 628 array('%s'), 629 array('%d') 630 ); 631 632 // Commit transaction 633 $wpdb->query('COMMIT'); 634 635 // Clear caches after successful save 636 $this->clear_survey_caches(); 637 638 // Redirect with success message 639 wp_safe_redirect(add_query_arg( 640 array( 641 'page' => 'LSAPP_litesurveys', 642 'message' => 'survey-deleted' 643 ), 644 admin_url('admin.php') 645 )); 646 exit; 647 648 } catch (Exception $e) { 649 // Rollback transaction 650 $wpdb->query('ROLLBACK'); 651 652 // Redirect with error message 653 wp_safe_redirect(add_query_arg( 654 array( 655 'page' => 'LSAPP_litesurveys', 656 'message' => 'error', 657 'error' => urlencode($e->getMessage()) 658 ), 659 admin_url('admin.php') 660 )); 661 exit; 662 } 663 } 664 665 public function handle_delete_submission() { 666 $this->verify_admin_access(); 667 668 // Verify nonce 669 $nonce = isset($_REQUEST['_wpnonce']) ? sanitize_text_field($_REQUEST['_wpnonce']) : ''; 670 $submission_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; 671 $survey_id = isset($_REQUEST['survey_id']) ? intval($_REQUEST['survey_id']) : 0; 672 673 if (!wp_verify_nonce($nonce, 'delete-submission_' . $submission_id)) { 674 wp_die(__('Security check failed.', 'litesurveys')); 675 } 676 677 if (!$submission_id || !$survey_id) { 678 wp_die(__('Invalid submission ID.', 'litesurveys')); 679 } 680 681 global $wpdb; 682 683 try { 684 // Start transaction 685 $wpdb->query('START TRANSACTION'); 686 687 // Soft delete the submission 688 $result = $wpdb->update( 689 $wpdb->prefix . 'litesurveys_submissions', 690 array('deleted_at' => current_time('mysql')), 691 array('id' => $submission_id), 692 array('%s'), 693 array('%d') 694 ); 695 696 if ($result === false) { 697 throw new Exception(__('Failed to delete submission.', 'litesurveys')); 698 } 699 700 // Soft delete associated responses 701 $wpdb->update( 702 $wpdb->prefix . 'litesurveys_responses', 703 array('deleted_at' => current_time('mysql')), 704 array('submission_id' => $submission_id), 705 array('%s'), 706 array('%d') 707 ); 708 709 // Commit transaction 710 $wpdb->query('COMMIT'); 711 712 // Redirect with success message 713 wp_safe_redirect(add_query_arg( 714 array( 715 'page' => 'LSAPP_litesurveys', 716 'action' => 'view-responses', 717 'id' => $survey_id, 718 'message' => 'submission-deleted' 719 ), 720 admin_url('admin.php') 721 )); 722 exit; 723 724 } catch (Exception $e) { 725 // Rollback transaction 726 $wpdb->query('ROLLBACK'); 727 728 // Redirect with error message 729 wp_safe_redirect(add_query_arg( 730 array( 731 'page' => 'LSAPP_litesurveys', 732 'action' => 'view-responses', 733 'id' => $survey_id, 734 'message' => 'error', 735 'error' => urlencode($e->getMessage()) 736 ), 737 admin_url('admin.php') 738 )); 739 exit; 740 } 741 } 742 743 /** 744 * Display admin notices with proper escaping. 745 */ 746 public function display_admin_notices() { 747 if (!isset($_GET['page']) || 'LSAPP_litesurveys' !== $_GET['page']) { 748 return; 749 } 750 751 if (isset($_GET['message'])) { 752 $message_type = sanitize_text_field($_GET['message']); 753 $class = 'notice '; 754 $message = ''; 755 756 switch ($message_type) { 757 case 'survey-published': 758 $class .= 'notice-success'; 759 $message = __('Survey published successfully.', 'litesurveys'); 760 break; 761 case 'survey-unpublished': 762 $class .= 'notice-warning'; 763 $message = __('Survey unpublished.', 'litesurveys'); 764 break; 765 case 'survey-saved': 766 $class .= 'notice-success'; 767 $message = __('Survey saved successfully.', 'litesurveys'); 768 break; 769 case 'survey-deleted': 770 $class .= 'notice-success'; 771 $message = __('Survey deleted successfully.', 'litesurveys'); 772 break; 773 case 'submission-deleted': 774 $class .= 'notice-success'; 775 $message = __('Submission deleted successfully.', 'litesurveys'); 776 break; 777 case 'error': 778 $class .= 'notice-error'; 779 $message = isset($_GET['error']) ? 780 sanitize_text_field(urldecode($_GET['error'])) : 781 __('An error occurred while processing your request.', 'litesurveys'); 782 break; 783 default: 784 return; // Unknown message type 785 } 786 787 printf( 788 '<div class="%1$s is-dismissible"><p>%2$s</p></div>', 789 esc_attr($class), 790 esc_html($message) 791 ); 792 } 793 } 794 795 public function enqueue_frontend_assets() { 796 // Only load if there are active surveys 797 if (!$this->has_active_surveys()) { 798 return; 52 799 } 53 800 54 add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_script' ), 50 ); 55 add_filter( 'wp_script_attributes', array( __CLASS__, 'add_script_attributes' ) ); 56 add_filter( 'plugin_action_links', array( __CLASS__, 'plugin_action_links' ), 10, 2 ); 57 } 58 59 /** 60 * Code to be run during admin_init 61 * 62 * @since 1.0.0 63 */ 64 public static function admin_init() { 65 register_setting( 'LSAPP_litesurveys', 'LSAPP_litesurveys_settings' ); 66 add_settings_section( 67 'LSAPP_litesurveys_settings_section', 68 '', 69 array( __CLASS__, 'litesurveys_settings_section_callback' ), 70 'LSAPP_litesurveys' 801 $suffix = $this->get_asset_suffix(); 802 803 wp_enqueue_style( 804 'litesurveys-frontend', 805 $this->plugin_url . "resources/css/frontend{$suffix}.css", 806 array(), 807 self::VERSION 71 808 ); 72 add_settings_field( 73 'LSAPP_litesurveys_settings_site_id', 74 'Site ID', 75 array( __CLASS__, 'litesurveys_settings_site_id_callback' ), 76 'LSAPP_litesurveys', 77 'LSAPP_litesurveys_settings_section', 78 array( 79 'label_for' => 'site_id', 80 'class' => 'wporg_row', 81 ) 809 810 wp_enqueue_script( 811 'litesurveys-frontend', 812 $this->plugin_url . "resources/js/frontend{$suffix}.js", 813 array(), 814 self::VERSION, 815 true 82 816 ); 83 } 84 85 /** 86 * Sets up our page in the admin menu 87 * 88 * @since 1.0.0 89 */ 90 public static function setup_admin_menu() { 91 add_options_page( 'LiteSurveys', 'LiteSurveys', 'manage_options', 'LSAPP_litesurveys', array( __CLASS__, 'generate_admin_page' ) ); 92 } 93 94 /** 95 * Callback for our main settings section 96 * 97 * @since 1.0.0 98 */ 99 public static function litesurveys_settings_section_callback() { 100 ?> 101 <p>You will need to have an active <a href="https://litesurveys.com" target="_blank">LiteSurveys</a> account to use this plugin. Within your LiteSurveys account, go to the "Connect Website" page to get your Website ID.</p> 102 <?php 103 } 104 105 /** 106 * Callback for site ID settings field 107 * 108 * @since 1.0.0 109 */ 110 public static function litesurveys_settings_site_id_callback($args) { 111 $site_id = self::get_site_id(); 112 ?> 113 <input id="<?php echo esc_attr( $args['label_for'] ); ?>" name="LSAPP_litesurveys_settings[<?php echo esc_attr( $args['label_for'] ); ?>]" type="text" value="<?php echo esc_attr( $site_id ); ?>"> 114 <p class="description">(Leave blank to disable)</p> 115 <?php 116 } 117 118 /** 119 * Generates our admin page 120 * 121 * @since 1.0.0 122 */ 123 public static function generate_admin_page() { 124 if ( ! current_user_can( 'manage_options' ) ) { 125 return; 126 } 127 128 ?> 129 <div class="wrap"> 130 <h1>LiteSurveys Integration</h1> 131 <form action="options.php" method="post"> 132 <?php 133 settings_fields( 'LSAPP_litesurveys' ); 134 do_settings_sections( 'LSAPP_litesurveys' ); 135 submit_button( 'Save Settings' ); 136 ?> 137 </form> 138 </div> 139 <?php 140 } 141 142 /** 143 * Enqueues the LiteSurveys script 144 * 145 * @since 1.0.0 146 */ 147 public static function enqueue_script() { 148 if ( ! self::get_site_id() ) { 149 return; 150 } 151 wp_enqueue_script( 'litesurveys', 'https://embeds.litesurveys.com/litesurveys.min.js', array(), LSAPP_PLUGIN_VERSION, array( 'strategy' => 'defer' ) ); 152 } 153 154 /** 155 * Filter the script attributes to add id and data-site-id attributes. 156 * 157 * @param array $attributes The script tag attributes. 158 * @return array 159 */ 160 public static function add_script_attributes( $attributes ) { 161 if ( 'litesurveys-js' === $attributes['id'] ) { 162 $attributes['data-site-id'] = self::get_site_id(); 163 } 164 return $attributes; 165 } 166 167 /** 168 * Retrieves the saved site id setting 169 * 170 * @since 1.0.0 171 * @return string 172 */ 173 private static function get_site_id() { 174 return self::get_setting('site_id', ''); 175 } 176 177 /** 178 * Retrieves a specific plugin setting 179 * 180 * @since 1.0.0 181 * @param string $setting Which setting to retrieve. 182 * @param mixed $default The value to return if setting does not exist. 183 * @return mixed 184 */ 185 private static function get_setting($setting, $default = false) { 186 $settings = self::get_settings(); 187 if ( isset( $settings[$setting] ) ) { 188 return $settings[$setting]; 189 } 190 191 return $default; 192 } 193 194 /** 195 * Retrieves our plugin settings 196 * 197 * @since 1.0.0 198 * @return array Our settings 199 */ 200 private static function get_settings() { 201 $settings = get_option( 'LSAPP_litesurveys_settings', [] ); 202 if (! is_array( $settings ) ) { 203 $settings = []; 204 } 205 206 return $settings; 817 818 wp_localize_script('litesurveys-frontend', 'liteSurveysSettings', array( 819 'ajaxUrl' => rest_url('litesurveys/v1/') 820 )); 207 821 } 208 822 … … 219 833 'litesurveys-wordpress-plugin/litesurveys-wordpress-plugin.php' 220 834 ]; 221 if (in_array($plugin_file, $plugin_files)) { 222 $settings_url = sprintf( '<a href="%s">Settings</a>', esc_url( admin_url( 'options-general.php?page=LSAPP_litesurveys' ) ) ); 223 $actions = array_merge( ['litesurveys_settings' => $settings_url], $actions) ; 835 if ( in_array( $plugin_file, $plugin_files, true ) ) { 836 $surveys_url = esc_url( admin_url( 'admin.php?page=LSAPP_litesurveys' ) ); 837 $surveys_link = sprintf( '<a href="%s">%s</a>', $surveys_url, __( 'Surveys', 'litesurveys' ) ); 838 $actions = array_merge( array( 'surveys' => $surveys_link ), $actions ); 224 839 } 225 840 return $actions; 226 841 } 842 843 /** 844 * Get the REST API instance. 845 * 846 * @since 2.0.0 847 * @return LSAPP_REST_API The REST API instance. 848 */ 849 public function get_rest_api() { 850 return $this->rest_api; 851 } 852 853 /** 854 * Check if there are any active surveys. 855 * 856 * @return bool True if there are active surveys, false otherwise. 857 */ 858 private function has_active_surveys() { 859 $cache_key = 'litesurveys_has_active'; 860 $has_active = get_transient($cache_key); 861 862 if (false === $has_active) { 863 global $wpdb; 864 865 $has_active = (bool)$wpdb->get_var($wpdb->prepare( 866 "SELECT EXISTS( 867 SELECT 1 FROM {$wpdb->prefix}litesurveys_surveys 868 WHERE active = %d AND deleted_at IS NULL 869 )", 870 1 871 )); 872 873 set_transient($cache_key, $has_active ? '1' : '0', self::CACHE_TIME); 874 } 875 876 return $has_active === '1'; 877 } 878 879 private function verify_admin_access() { 880 if (!current_user_can('manage_options')) { 881 wp_die( 882 esc_html__('You do not have sufficient permissions to access this page.', 'litesurveys'), 883 403 884 ); 885 } 886 } 887 888 /** 889 * Sanitize JSON data before storage. 890 * 891 * @param array $data The data to be sanitized and stored as JSON. 892 * @return string|false Sanitized JSON string or false on failure. 893 */ 894 private function sanitize_json_data($data) { 895 if (!is_array($data)) { 896 return false; 897 } 898 899 array_walk_recursive($data, function(&$value) { 900 if (is_string($value)) { 901 $value = sanitize_text_field($value); 902 } elseif (is_int($value)) { 903 $value = intval($value); 904 } elseif (is_float($value)) { 905 $value = floatval($value); 906 } elseif (is_bool($value)) { 907 $value = (bool)$value; 908 } else { 909 $value = ''; 910 } 911 }); 912 913 return wp_json_encode($data); 914 } 915 916 /** 917 * Get the suffix for asset files (.min in production, empty in debug). 918 * 919 * @return string 920 */ 921 private function get_asset_suffix() { 922 return defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min'; 923 } 924 925 /** 926 * Clear survey caches when a survey is updated. 927 */ 928 private function clear_survey_caches() { 929 delete_transient('litesurveys_active_surveys'); 930 delete_transient('litesurveys_has_active'); 931 } 227 932 } 228 933 229 LSAPP_LiteSurveys_Integration::init(); 934 // Initialize plugin 935 LSAPP_LiteSurveys::get_instance(); 230 936 ?> -
litesurveys/tags/2.0.0/readme.txt
r3130242 r3183478 4 4 Requires at least: 6.0 5 5 Tested up to: 6.6.1 6 Stable tag: 1.0.36 Stable tag: 2.0.0 7 7 Requires PHP: 8.0 8 8 License: GPLv3 … … 13 13 == Description == 14 14 15 Easily hear from your users by adding pre-sale surveys, post-sale surveys, feedback surveys, and more to your website by integrating your site with [the LiteSurveys service](https://litesurveys.com/).15 Learn from your site visitors using quick surveys. 16 16 17 **Important Note**: This plugin requires a [LiteSurveys](https://litesurveys.com) plan to create surveys and collect responses. Get started for free today! 17 == Description == 18 18 19 ## Collect Feedback and Ideas From Your Site Visitors 19 LiteSurveys is a lightweight WordPress plugin that helps you gather feedback from your website visitors through simple, unobtrusive surveys. With LiteSurveys, you can easily create and manage single-question surveys that appear as slide-in popups on your website. 20 20 21 You can use LiteSurveys to: 21 ## Key Features 22 22 23 * **Conduct User Research** - Using LiteSurveys small slide-in surveys, users can quickly provide information about who they are or what they are looking for. 24 * **Identify Pain Points** - Adding in quick surveys during the sales funnel allows site visitors to provide feedback about concerns and issues. 25 * **Get Marketing Insights** - Using surveys at key points during the user experience can provide details about the user’s decisions and thoughts which can influence your copy and strategy for your marketing efforts. 26 * And much more! 23 * **Simple Survey Creation** - Create surveys directly in your WordPress admin with just a few clicks 24 * **Multiple Question Types** - Choose between multiple choice or open-ended questions 25 * **Customizable Display** - Control when and where your surveys appear 26 * **Easy Response Management** - View and manage survey responses right in your WordPress dashboard 27 * **Unobtrusive Design** - Surveys appear as subtle slide-in popups that won't disrupt the user experience 28 * **Mobile Friendly** - Surveys work great on all devices 29 30 ## Use LiteSurveys to: 31 32 * **Conduct User Research** - Using small slide-in surveys, users can quickly provide information about who they are or what they are looking for 33 * **Identify Pain Points** - Adding quick surveys during the sales funnel allows site visitors to provide feedback about concerns and issues 34 * **Get Marketing Insights** - Using surveys at key points during the user experience can provide details about the user's decisions and thoughts which can influence your copy and strategy 35 * **Gather Customer Feedback** - Collect real feedback from actual users about their experience with your site or product 27 36 28 37 ## Some Features of LiteSurveys: 29 38 30 * **Question Type Variety** - Select from a variety question types including multiple choice, open answer, and more. 31 * **Analyze Results** - Easily see how individual site visitors respond but also analyze how all site visitors are responding. 32 * **Control Timing** - LiteSurveys has many options available for controlling when the survey appears. 33 * **Target Specific Pages** - Show your survey on your whole website or only on specific pages. 34 * **Export Results** - Export your survey responses so you can use them in spreadsheets, databases, other tools, and more. 35 36 Ready to get started? [Create your account](https://litesurveys.com/) for free to create your first survey within minutes! 39 * **Question Type Variety** - Select from multiple choice or open-ended questions to best suit your needs 40 * **Timing Control** - Set when your survey should appear to visitors 41 * **Position Control** - Choose whether your survey appears in the bottom left or bottom right 42 * **Active/Inactive States** - Easily activate or deactivate surveys as needed 43 * **Submissions Per Page** - Easily see what page the website visitor was on when they submitted the survey 37 44 38 45 == Installation == … … 47 54 48 55 1. An example LiteSurveys slide-in survey. 49 2. The settings page within the WordPress plugin.56 2. Surveys admin page showing your surveys, which are active, and how many submissions each has. 50 57 51 58 == Changelog == 59 60 = 2.0.0 (November 6, 2024) = 61 62 * Major rewrite of plugin to be standalone instead of requiring LiteSurveys service 63 * Add ability to create and manage surveys directly in WordPress admin 64 * Add support for multiple choice and open-ended questions 65 * Add support for customizing survey timing and position 52 66 53 67 = 1.0.3 (August 2, 2024) = -
litesurveys/tags/2.0.0/uninstall.php
r3013451 r3183478 1 1 <?php 2 // if uninstall.php is not called by WordPress, die3 if ( ! defined( 'WP_UNINSTALL_PLUGIN' )) {4 die;2 // If uninstall not called from WordPress, exit 3 if (!defined('WP_UNINSTALL_PLUGIN')) { 4 exit; 5 5 } 6 6 7 delete_option( 'LSAPP_litesurveys_settings' ); 7 global $wpdb; 8 9 // Drop tables in reverse order of dependencies 10 $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}litesurveys_responses"); 11 $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}litesurveys_submissions"); 12 $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}litesurveys_questions"); 13 $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}litesurveys_surveys"); 14 15 // Delete options 16 delete_option('lsapp_litesurveys_version'); 17 delete_option('LSAPP_litesurveys_settings'); -
litesurveys/trunk/litesurveys-wordpress-plugin.php
r3130242 r3183478 2 2 /** 3 3 * Plugin Name: LiteSurveys 4 * Description: Adds your LiteSurveys to your WordPress site5 * Version: 1.0.36 * Requires at least: 6. 04 * Description: Adds simple one-question surveys to your WordPress site 5 * Version: 2.0.0 6 * Requires at least: 6.1 7 7 * Requires PHP: 8.0 8 8 * License: GPLv3 … … 10 10 * Author: LiteSurveys 11 11 * Author URI: https://litesurveys.com 12 * Text Domain: litesurveys 12 13 * 13 * @ authorLiteSurveys14 * @package LiteSurveys 14 15 */ 15 16 16 17 // Exits if accessed directly. 17 if ( ! defined( 'ABSPATH' ) ) { 18 exit; 19 } 20 21 // Define plugin version constant. 22 define( 'LSAPP_PLUGIN_VERSION', '1.0.3' ); 23 18 defined( 'ABSPATH' ) || exit; 24 19 25 20 /** 26 * The plugin's main class21 * Main LiteSurveys plugin class. 27 22 * 28 23 * @since 1.0.0 29 24 */ 30 class LSAPP_LiteSurveys_Integration { 31 32 33 34 /** 35 * Initializes our plugin 25 class LSAPP_LiteSurveys { 26 27 /** 28 * Plugin version. 29 * 30 * @var string 31 */ 32 const VERSION = '2.0.0'; 33 34 /** 35 * The single instance of the class. 36 * 37 * @var LSAPP_LiteSurveys 38 */ 39 private static $instance = null; 40 41 /** 42 * Plugin path. 43 * 44 * @var string 45 */ 46 private $plugin_path; 47 48 /** 49 * Plugin URL. 50 * 51 * @var string 52 */ 53 private $plugin_url; 54 55 /** 56 * Our REST API functionality. 57 */ 58 private $rest_api; 59 60 /** 61 * Cache time in seconds. 62 * 63 * @var int 64 */ 65 const CACHE_TIME = 300; 66 67 /** 68 * Main LSAPP_LiteSurveys Instance. 69 * 70 * Ensures only one instance of LSAPP_LiteSurveys is loaded or can be loaded. 36 71 * 37 72 * @since 1.0.0 38 */ 39 public static function init() { 40 self::load_hooks(); 41 } 42 43 /** 44 * Adds in any plugin-wide hooks 73 * @return LSAPP_LiteSurveys Main instance 74 */ 75 public static function get_instance() { 76 if ( is_null( self::$instance ) ) { 77 self::$instance = new self(); 78 } 79 return self::$instance; 80 } 81 82 /** 83 * LSAPP_LiteSurveys Constructor. 84 */ 85 private function __construct() { 86 $this->plugin_path = plugin_dir_path( __FILE__ ); 87 $this->plugin_url = plugin_dir_url( __FILE__ ); 88 89 // Include the REST API class 90 require_once $this->plugin_path . 'includes/class-rest-api.php'; 91 $this->rest_api = new LSAPP_REST_API(); 92 93 $this->init_hooks(); 94 } 95 96 /** 97 * Initialize WordPress hooks. 45 98 * 46 99 * @since 1.0.0 47 100 */ 48 public static function load_hooks() { 49 if (is_admin()) { 50 add_action( 'admin_menu', array( __CLASS__, 'setup_admin_menu' ) ); 51 add_action( 'admin_init', array( __CLASS__, 'admin_init' ) ); 101 private function init_hooks() { 102 // Setting up plugin upon activation. 103 register_activation_hook( __FILE__, array( $this, 'activate_plugin' ) ); 104 105 // Load translations. 106 add_action( 'init', array( $this, 'load_textdomain' ) ); 107 108 // Add Admin code. 109 add_action( 'admin_menu', array( $this, 'add_admin_menu' ) ); 110 add_action( 'admin_post_save_survey', array( $this, 'handle_save_survey' ) ); 111 add_action( 'admin_post_delete_survey', array( $this, 'handle_delete_survey' ) ); 112 add_action( 'admin_post_delete_submission', array( $this, 'handle_delete_submission' ) ); 113 add_action( 'admin_notices', array( $this, 'display_admin_notices' ) ); 114 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) ); 115 add_filter( 'plugin_action_links', array( $this, 'plugin_action_links' ), 10, 2 ); 116 117 // Add REST API endpoints. 118 add_action( 'rest_api_init', array( $this->rest_api, 'register_rest_routes' ) ); 119 120 // Add frontend script. 121 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_assets' ) ); 122 } 123 124 /** 125 * Activate plugin and create database tables. 126 * 127 * @since 1.0.0 128 */ 129 public function activate_plugin() { 130 global $wpdb; 131 132 if ( ! current_user_can( 'activate_plugins' ) ) { 133 return; 134 } 135 136 $charset_collate = $wpdb->get_charset_collate(); 137 138 $this->create_database_tables( $charset_collate ); 139 140 add_option( 'lsapp_litesurveys_version', self::VERSION ); 141 } 142 143 /** 144 * Create plugin database tables. 145 * 146 * @since 1.0.0 147 * @param string $charset_collate Database charset and collation. 148 */ 149 public function create_database_tables( $charset_collate ) { 150 global $wpdb; 151 152 // Create surveys table. 153 $sql_surveys = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}litesurveys_surveys ( 154 id bigint(20) NOT NULL AUTO_INCREMENT, 155 name varchar(100) NOT NULL, 156 active tinyint(1) DEFAULT 0, 157 submit_message text NOT NULL, 158 targeting_settings json DEFAULT NULL, 159 appearance_settings json DEFAULT NULL, 160 created_at timestamp DEFAULT CURRENT_TIMESTAMP, 161 updated_at timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 162 deleted_at timestamp NULL DEFAULT NULL, 163 PRIMARY KEY (id) 164 ) $charset_collate;"; 165 166 // Create questions table. 167 $sql_questions = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}litesurveys_questions ( 168 id bigint(20) NOT NULL AUTO_INCREMENT, 169 survey_id bigint(20) NOT NULL, 170 type varchar(25) NOT NULL, 171 content varchar(100) NOT NULL, 172 answers json DEFAULT NULL, 173 created_at timestamp DEFAULT CURRENT_TIMESTAMP, 174 updated_at timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 175 deleted_at timestamp NULL DEFAULT NULL, 176 PRIMARY KEY (id), 177 FOREIGN KEY (survey_id) REFERENCES {$wpdb->prefix}litesurveys_surveys(id) ON DELETE CASCADE 178 ) $charset_collate;"; 179 180 // Create submissions table. 181 $sql_submissions = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}litesurveys_submissions ( 182 id bigint(20) NOT NULL AUTO_INCREMENT, 183 survey_id bigint(20) NOT NULL, 184 page varchar(255) DEFAULT NULL, 185 created_at timestamp DEFAULT CURRENT_TIMESTAMP, 186 deleted_at timestamp NULL DEFAULT NULL, 187 PRIMARY KEY (id), 188 FOREIGN KEY (survey_id) REFERENCES {$wpdb->prefix}litesurveys_surveys(id) ON DELETE CASCADE 189 ) $charset_collate;"; 190 191 // Create responses table. 192 $sql_responses = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}litesurveys_responses ( 193 id bigint(20) NOT NULL AUTO_INCREMENT, 194 submission_id bigint(20) NOT NULL, 195 question_id bigint(20) NOT NULL, 196 content text NOT NULL, 197 created_at timestamp DEFAULT CURRENT_TIMESTAMP, 198 deleted_at timestamp NULL DEFAULT NULL, 199 PRIMARY KEY (id), 200 FOREIGN KEY (submission_id) REFERENCES {$wpdb->prefix}litesurveys_submissions(id) ON DELETE CASCADE, 201 FOREIGN KEY (question_id) REFERENCES {$wpdb->prefix}litesurveys_questions(id) ON DELETE CASCADE 202 ) $charset_collate;"; 203 204 require_once ABSPATH . 'wp-admin/includes/upgrade.php'; 205 dbDelta( $sql_surveys ); 206 dbDelta( $sql_questions ); 207 dbDelta( $sql_submissions ); 208 dbDelta( $sql_responses ); 209 } 210 211 /** 212 * Load plugin text domain for translations. 213 * 214 * @since 1.0.0 215 */ 216 public function load_textdomain() { 217 load_plugin_textdomain( 218 'litesurveys', 219 false, 220 dirname( plugin_basename( __FILE__ ) ) . '/languages' 221 ); 222 } 223 224 /** 225 * Enqueue admin scripts and styles. 226 * 227 * @since 1.0.0 228 * @param string $hook The current admin page. 229 */ 230 public function enqueue_admin_assets( $hook ) { 231 if ( false === strpos( $hook, 'litesurveys' ) ) { 232 return; 233 } 234 235 $action = isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : 'list'; 236 237 if ( 'edit' === $action || 'new' === $action ) { 238 $suffix = $this->get_asset_suffix(); 239 wp_enqueue_style( 240 'litesurveys-admin', 241 $this->plugin_url . "resources/css/admin{$suffix}.css", 242 array(), 243 self::VERSION 244 ); 245 246 wp_enqueue_script( 247 'litesurveys-admin', 248 $this->plugin_url . "resources/js/admin{$suffix}.js", 249 array( 'jquery' ), 250 self::VERSION, 251 true 252 ); 253 } 254 } 255 256 /** 257 * Add admin menu items. 258 * 259 * @since 1.0.0 260 */ 261 public function add_admin_menu() { 262 add_menu_page( 263 'LiteSurveys', 264 'LiteSurveys', 265 'manage_options', 266 'LSAPP_litesurveys', 267 array( $this, 'render_admin_page' ), 268 'dashicons-chart-bar', 269 30 270 ); 271 } 272 273 /** 274 * Render the admin page content. 275 * 276 * @since 1.0.0 277 */ 278 public function render_admin_page() { 279 $this->verify_admin_access(); 280 281 global $wpdb; 282 283 $action = isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : 'list'; 284 285 switch ( $action ) { 286 case 'view-responses': 287 $survey_id = isset( $_GET['id'] ) ? absint( $_GET['id'] ) : 0; 288 if ( ! $survey_id ) { 289 wp_die( esc_html__( 'Invalid survey ID.', 'litesurveys' ) ); 290 } 291 292 // Get survey data with question. 293 $survey = $wpdb->get_row( 294 $wpdb->prepare( 295 "SELECT s.*, q.content as question_content 296 FROM {$wpdb->prefix}litesurveys_surveys s 297 LEFT JOIN {$wpdb->prefix}litesurveys_questions q ON s.id = q.survey_id 298 WHERE s.id = %d AND s.deleted_at IS NULL", 299 $survey_id 300 ) 301 ); 302 303 if ( ! $survey ) { 304 wp_die( esc_html__( 'Survey not found.', 'litesurveys' ) ); 305 } 306 307 // Pagination settings. 308 $per_page = 20; 309 $current_page = isset( $_GET['paged'] ) ? max( 1, absint( $_GET['paged'] ) ) : 1; 310 $offset = ( $current_page - 1 ) * $per_page; 311 312 // Get total count for pagination. 313 $total_items = $wpdb->get_var( 314 $wpdb->prepare( 315 "SELECT COUNT(*) 316 FROM {$wpdb->prefix}litesurveys_submissions s 317 WHERE s.survey_id = %d AND s.deleted_at IS NULL", 318 $survey_id 319 ) 320 ); 321 322 // Get submissions with responses. 323 $submissions = $wpdb->get_results( 324 $wpdb->prepare( 325 "SELECT s.id, s.created_at, s.page, r.content as response 326 FROM {$wpdb->prefix}litesurveys_submissions s 327 LEFT JOIN {$wpdb->prefix}litesurveys_responses r ON s.id = r.submission_id 328 WHERE s.survey_id = %d AND s.deleted_at IS NULL 329 ORDER BY s.created_at DESC 330 LIMIT %d OFFSET %d", 331 $survey_id, 332 $per_page, 333 $offset 334 ) 335 ); 336 337 // Calculate pagination values. 338 $total_pages = ceil( $total_items / $per_page ); 339 340 include $this->plugin_path . 'views/admin/survey-submissions.php'; 341 break; 342 343 case 'edit': 344 case 'new': 345 // Get survey data if editing. 346 $survey_id = isset( $_GET['id'] ) ? absint( $_GET['id'] ) : 0; 347 $survey = null; 348 349 if ( $survey_id ) { 350 $survey = $wpdb->get_row( 351 $wpdb->prepare( 352 "SELECT * FROM {$wpdb->prefix}litesurveys_surveys WHERE id = %d AND deleted_at IS NULL", 353 $survey_id 354 ) 355 ); 356 357 if ( $survey ) { 358 $question = $wpdb->get_row( 359 $wpdb->prepare( 360 "SELECT * FROM {$wpdb->prefix}litesurveys_questions WHERE survey_id = %d AND deleted_at IS NULL", 361 $survey_id 362 ) 363 ); 364 365 if ( $question ) { 366 $survey->question = $question; 367 $survey->question->answers = json_decode( $question->answers ); 368 } 369 370 $survey->targeting_settings = json_decode( $survey->targeting_settings ); 371 $survey->appearance_settings = json_decode( $survey->appearance_settings ); 372 } 373 } 374 375 // Set defaults for new survey. 376 if ( ! $survey ) { 377 $survey = (object) array( 378 'name' => '', 379 'active' => false, 380 'submit_message' => 'Thanks! I appreciate you taking the time to respond.', 381 'targeting_settings' => (object) array( 382 'targets' => (object) array( 383 'show' => 'all', 384 'includes' => array(), 385 'excludes' => array(), 386 ), 387 'trigger' => array( 388 (object) array( 389 'type' => 'auto', 390 'auto_timing' => 5, 391 ), 392 ), 393 ), 394 'appearance_settings' => (object) array( 395 'horizontal_position' => 'right', 396 ), 397 'question' => (object) array( 398 'type' => 'multiple-choice', 399 'content' => '', 400 'answers' => array( '', '', '' ), 401 ), 402 ); 403 } 404 include $this->plugin_path . 'views/admin/survey-edit.php'; 405 break; 406 407 default: 408 $surveys = $wpdb->get_results( 409 "SELECT * FROM {$wpdb->prefix}litesurveys_surveys WHERE deleted_at IS NULL ORDER BY created_at DESC" 410 ); 411 include $this->plugin_path . 'views/admin/surveys-admin.php'; 412 break; 413 } 414 } 415 416 /** 417 * Handle saving survey data. 418 * 419 * @since 1.0.0 420 */ 421 public function handle_save_survey() { 422 $this->verify_admin_access(); 423 424 check_admin_referer( 'save_survey', 'survey_nonce' ); 425 426 global $wpdb; 427 428 $survey_id = isset( $_POST['survey_id'] ) ? absint( $_POST['survey_id'] ) : 0; 429 $save_type = isset( $_POST['save_type'] ) ? sanitize_text_field( wp_unslash( $_POST['save_type'] ) ) : 'draft'; 430 431 try { 432 // Validate required fields. 433 if ( empty( $_POST['survey_name'] ) ) { 434 throw new Exception( __( 'Survey name is required.', 'litesurveys' ) ); 435 } 436 437 if ( empty( $_POST['question_content'] ) ) { 438 throw new Exception( __( 'Survey question is required.', 'litesurveys' ) ); 439 } 440 441 // Prepare targeting settings. 442 $targeting_settings = array( 443 'targets' => array( 444 'show' => sanitize_text_field( wp_unslash( $_POST['targeting_show'] ) ), 445 'includes' => isset( $_POST['includes'] ) ? 446 array_map( 'sanitize_text_field', wp_unslash( $_POST['includes'] ) ) : array(), 447 'excludes' => isset( $_POST['excludes'] ) ? 448 array_map( 'sanitize_text_field', wp_unslash( $_POST['excludes'] ) ) : array(), 449 ), 450 'trigger' => array( 451 array( 452 'type' => sanitize_text_field( wp_unslash( $_POST['trigger_type'] ) ), 453 'auto_timing' => absint( $_POST['auto_timing'] ), 454 ), 455 ), 456 ); 457 458 // Prepare appearance settings. 459 $appearance_settings = array( 460 'horizontal_position' => sanitize_text_field( wp_unslash( $_POST['horizontal_position'] ) ), 461 ); 462 463 // Sanitize JSON data. 464 $targeting_json = $this->sanitize_json_data( $targeting_settings ); 465 $appearance_json = $this->sanitize_json_data( $appearance_settings ); 466 467 if ( false === $targeting_json || false === $appearance_json ) { 468 throw new Exception( __( 'Invalid settings data.', 'litesurveys' ) ); 469 } 470 471 // Prepare survey data. 472 $survey_data = array( 473 'name' => sanitize_text_field( wp_unslash( $_POST['survey_name'] ) ), 474 'submit_message' => sanitize_textarea_field( wp_unslash( $_POST['submit_message'] ) ), 475 'active' => ( 'publish' === $save_type ), 476 'targeting_settings' => $targeting_json, 477 'appearance_settings' => $appearance_json, 478 ); 479 480 // Prepare answers for multiple choice. 481 $answers = array(); 482 if ( 'multiple-choice' === $_POST['question_type'] && ! empty( $_POST['answers'] ) ) { 483 $answers = array_filter( array_map( 'sanitize_text_field', wp_unslash( $_POST['answers'] ) ) ); 484 if ( count( $answers ) < 2 ) { 485 throw new Exception( __( 'Multiple choice questions must have at least 2 answers.', 'litesurveys' ) ); 486 } 487 } 488 489 // Prepare question data. 490 $question_data = array( 491 'type' => sanitize_text_field( wp_unslash( $_POST['question_type'] ) ), 492 'content' => sanitize_textarea_field( wp_unslash( $_POST['question_content'] ) ), 493 'answers' => $this->sanitize_json_data( $answers ), 494 ); 495 496 // Start transaction. 497 $wpdb->query( 'START TRANSACTION' ); 498 499 if ( $survey_id ) { 500 // Update existing survey. 501 $result = $wpdb->update( 502 $wpdb->prefix . 'litesurveys_surveys', 503 $survey_data, 504 array( 'id' => $survey_id ) 505 ); 506 507 if ( false === $result ) { 508 throw new Exception( __( 'Failed to update survey.', 'litesurveys' ) ); 509 } 510 511 // Update or insert question. 512 $existing_question = $wpdb->get_row( 513 $wpdb->prepare( 514 "SELECT id FROM {$wpdb->prefix}litesurveys_questions WHERE survey_id = %d AND deleted_at IS NULL", 515 $survey_id 516 ) 517 ); 518 519 if ( $existing_question ) { 520 $result = $wpdb->update( 521 $wpdb->prefix . 'litesurveys_questions', 522 $question_data, 523 array( 'id' => $existing_question->id ) 524 ); 525 if ( false === $result ) { 526 throw new Exception( __( 'Failed to update survey question.', 'litesurveys' ) ); 527 } 528 } else { 529 $question_data['survey_id'] = $survey_id; 530 $result = $wpdb->insert( $wpdb->prefix . 'litesurveys_questions', $question_data ); 531 if ( ! $result ) { 532 throw new Exception( __( 'Failed to create survey question.', 'litesurveys' ) ); 533 } 534 } 535 } else { 536 // Insert new survey. 537 $result = $wpdb->insert( $wpdb->prefix . 'litesurveys_surveys', $survey_data ); 538 if ( ! $result ) { 539 throw new Exception( __( 'Failed to create survey.', 'litesurveys' ) ); 540 } 541 $survey_id = $wpdb->insert_id; 542 543 // Insert question. 544 $question_data['survey_id'] = $survey_id; 545 $result = $wpdb->insert( $wpdb->prefix . 'litesurveys_questions', $question_data ); 546 if ( ! $result ) { 547 throw new Exception( __( 'Failed to create survey question.', 'litesurveys' ) ); 548 } 549 } 550 551 // Commit transaction. 552 $wpdb->query( 'COMMIT' ); 553 554 // Clear caches after successful save. 555 $this->clear_survey_caches(); 556 557 // Build redirect arguments. 558 $redirect_args = array( 559 'page' => 'LSAPP_litesurveys', 560 'action' => 'edit', 561 'id' => $survey_id, 562 'message' => 'publish' === $save_type ? 'survey-published' : 'survey-saved', 563 ); 564 565 // Redirect with success message. 566 wp_safe_redirect( add_query_arg( $redirect_args, admin_url( 'admin.php' ) ) ); 567 exit; 568 569 } catch ( Exception $e ) { 570 // Rollback transaction. 571 $wpdb->query( 'ROLLBACK' ); 572 573 wp_safe_redirect( 574 add_query_arg( 575 array( 576 'page' => 'LSAPP_litesurveys', 577 'action' => $survey_id ? 'edit' : 'new', 578 'id' => $survey_id, 579 'message' => 'error', 580 'error' => rawurlencode( $e->getMessage() ), 581 ), 582 admin_url( 'admin.php' ) 583 ) 584 ); 585 exit; 586 } 587 } 588 589 public function handle_delete_survey() { 590 $this->verify_admin_access(); 591 592 // Verify nonce 593 $nonce = isset($_REQUEST['_wpnonce']) ? sanitize_text_field($_REQUEST['_wpnonce']) : ''; 594 $survey_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; 595 596 if (!wp_verify_nonce($nonce, 'delete-survey_' . $survey_id)) { 597 wp_die(__('Security check failed.', 'litesurveys')); 598 } 599 600 if (!$survey_id) { 601 wp_die(__('Invalid survey ID.', 'litesurveys')); 602 } 603 604 global $wpdb; 605 606 try { 607 // Start transaction 608 $wpdb->query('START TRANSACTION'); 609 610 // Soft delete the survey 611 $result = $wpdb->update( 612 $wpdb->prefix . 'litesurveys_surveys', 613 array('deleted_at' => current_time('mysql')), 614 array('id' => $survey_id), 615 array('%s'), 616 array('%d') 617 ); 618 619 if ($result === false) { 620 throw new Exception(__('Failed to delete survey.', 'litesurveys')); 621 } 622 623 // Soft delete associated questions 624 $wpdb->update( 625 $wpdb->prefix . 'litesurveys_questions', 626 array('deleted_at' => current_time('mysql')), 627 array('survey_id' => $survey_id), 628 array('%s'), 629 array('%d') 630 ); 631 632 // Commit transaction 633 $wpdb->query('COMMIT'); 634 635 // Clear caches after successful save 636 $this->clear_survey_caches(); 637 638 // Redirect with success message 639 wp_safe_redirect(add_query_arg( 640 array( 641 'page' => 'LSAPP_litesurveys', 642 'message' => 'survey-deleted' 643 ), 644 admin_url('admin.php') 645 )); 646 exit; 647 648 } catch (Exception $e) { 649 // Rollback transaction 650 $wpdb->query('ROLLBACK'); 651 652 // Redirect with error message 653 wp_safe_redirect(add_query_arg( 654 array( 655 'page' => 'LSAPP_litesurveys', 656 'message' => 'error', 657 'error' => urlencode($e->getMessage()) 658 ), 659 admin_url('admin.php') 660 )); 661 exit; 662 } 663 } 664 665 public function handle_delete_submission() { 666 $this->verify_admin_access(); 667 668 // Verify nonce 669 $nonce = isset($_REQUEST['_wpnonce']) ? sanitize_text_field($_REQUEST['_wpnonce']) : ''; 670 $submission_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; 671 $survey_id = isset($_REQUEST['survey_id']) ? intval($_REQUEST['survey_id']) : 0; 672 673 if (!wp_verify_nonce($nonce, 'delete-submission_' . $submission_id)) { 674 wp_die(__('Security check failed.', 'litesurveys')); 675 } 676 677 if (!$submission_id || !$survey_id) { 678 wp_die(__('Invalid submission ID.', 'litesurveys')); 679 } 680 681 global $wpdb; 682 683 try { 684 // Start transaction 685 $wpdb->query('START TRANSACTION'); 686 687 // Soft delete the submission 688 $result = $wpdb->update( 689 $wpdb->prefix . 'litesurveys_submissions', 690 array('deleted_at' => current_time('mysql')), 691 array('id' => $submission_id), 692 array('%s'), 693 array('%d') 694 ); 695 696 if ($result === false) { 697 throw new Exception(__('Failed to delete submission.', 'litesurveys')); 698 } 699 700 // Soft delete associated responses 701 $wpdb->update( 702 $wpdb->prefix . 'litesurveys_responses', 703 array('deleted_at' => current_time('mysql')), 704 array('submission_id' => $submission_id), 705 array('%s'), 706 array('%d') 707 ); 708 709 // Commit transaction 710 $wpdb->query('COMMIT'); 711 712 // Redirect with success message 713 wp_safe_redirect(add_query_arg( 714 array( 715 'page' => 'LSAPP_litesurveys', 716 'action' => 'view-responses', 717 'id' => $survey_id, 718 'message' => 'submission-deleted' 719 ), 720 admin_url('admin.php') 721 )); 722 exit; 723 724 } catch (Exception $e) { 725 // Rollback transaction 726 $wpdb->query('ROLLBACK'); 727 728 // Redirect with error message 729 wp_safe_redirect(add_query_arg( 730 array( 731 'page' => 'LSAPP_litesurveys', 732 'action' => 'view-responses', 733 'id' => $survey_id, 734 'message' => 'error', 735 'error' => urlencode($e->getMessage()) 736 ), 737 admin_url('admin.php') 738 )); 739 exit; 740 } 741 } 742 743 /** 744 * Display admin notices with proper escaping. 745 */ 746 public function display_admin_notices() { 747 if (!isset($_GET['page']) || 'LSAPP_litesurveys' !== $_GET['page']) { 748 return; 749 } 750 751 if (isset($_GET['message'])) { 752 $message_type = sanitize_text_field($_GET['message']); 753 $class = 'notice '; 754 $message = ''; 755 756 switch ($message_type) { 757 case 'survey-published': 758 $class .= 'notice-success'; 759 $message = __('Survey published successfully.', 'litesurveys'); 760 break; 761 case 'survey-unpublished': 762 $class .= 'notice-warning'; 763 $message = __('Survey unpublished.', 'litesurveys'); 764 break; 765 case 'survey-saved': 766 $class .= 'notice-success'; 767 $message = __('Survey saved successfully.', 'litesurveys'); 768 break; 769 case 'survey-deleted': 770 $class .= 'notice-success'; 771 $message = __('Survey deleted successfully.', 'litesurveys'); 772 break; 773 case 'submission-deleted': 774 $class .= 'notice-success'; 775 $message = __('Submission deleted successfully.', 'litesurveys'); 776 break; 777 case 'error': 778 $class .= 'notice-error'; 779 $message = isset($_GET['error']) ? 780 sanitize_text_field(urldecode($_GET['error'])) : 781 __('An error occurred while processing your request.', 'litesurveys'); 782 break; 783 default: 784 return; // Unknown message type 785 } 786 787 printf( 788 '<div class="%1$s is-dismissible"><p>%2$s</p></div>', 789 esc_attr($class), 790 esc_html($message) 791 ); 792 } 793 } 794 795 public function enqueue_frontend_assets() { 796 // Only load if there are active surveys 797 if (!$this->has_active_surveys()) { 798 return; 52 799 } 53 800 54 add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_script' ), 50 ); 55 add_filter( 'wp_script_attributes', array( __CLASS__, 'add_script_attributes' ) ); 56 add_filter( 'plugin_action_links', array( __CLASS__, 'plugin_action_links' ), 10, 2 ); 57 } 58 59 /** 60 * Code to be run during admin_init 61 * 62 * @since 1.0.0 63 */ 64 public static function admin_init() { 65 register_setting( 'LSAPP_litesurveys', 'LSAPP_litesurveys_settings' ); 66 add_settings_section( 67 'LSAPP_litesurveys_settings_section', 68 '', 69 array( __CLASS__, 'litesurveys_settings_section_callback' ), 70 'LSAPP_litesurveys' 801 $suffix = $this->get_asset_suffix(); 802 803 wp_enqueue_style( 804 'litesurveys-frontend', 805 $this->plugin_url . "resources/css/frontend{$suffix}.css", 806 array(), 807 self::VERSION 71 808 ); 72 add_settings_field( 73 'LSAPP_litesurveys_settings_site_id', 74 'Site ID', 75 array( __CLASS__, 'litesurveys_settings_site_id_callback' ), 76 'LSAPP_litesurveys', 77 'LSAPP_litesurveys_settings_section', 78 array( 79 'label_for' => 'site_id', 80 'class' => 'wporg_row', 81 ) 809 810 wp_enqueue_script( 811 'litesurveys-frontend', 812 $this->plugin_url . "resources/js/frontend{$suffix}.js", 813 array(), 814 self::VERSION, 815 true 82 816 ); 83 } 84 85 /** 86 * Sets up our page in the admin menu 87 * 88 * @since 1.0.0 89 */ 90 public static function setup_admin_menu() { 91 add_options_page( 'LiteSurveys', 'LiteSurveys', 'manage_options', 'LSAPP_litesurveys', array( __CLASS__, 'generate_admin_page' ) ); 92 } 93 94 /** 95 * Callback for our main settings section 96 * 97 * @since 1.0.0 98 */ 99 public static function litesurveys_settings_section_callback() { 100 ?> 101 <p>You will need to have an active <a href="https://litesurveys.com" target="_blank">LiteSurveys</a> account to use this plugin. Within your LiteSurveys account, go to the "Connect Website" page to get your Website ID.</p> 102 <?php 103 } 104 105 /** 106 * Callback for site ID settings field 107 * 108 * @since 1.0.0 109 */ 110 public static function litesurveys_settings_site_id_callback($args) { 111 $site_id = self::get_site_id(); 112 ?> 113 <input id="<?php echo esc_attr( $args['label_for'] ); ?>" name="LSAPP_litesurveys_settings[<?php echo esc_attr( $args['label_for'] ); ?>]" type="text" value="<?php echo esc_attr( $site_id ); ?>"> 114 <p class="description">(Leave blank to disable)</p> 115 <?php 116 } 117 118 /** 119 * Generates our admin page 120 * 121 * @since 1.0.0 122 */ 123 public static function generate_admin_page() { 124 if ( ! current_user_can( 'manage_options' ) ) { 125 return; 126 } 127 128 ?> 129 <div class="wrap"> 130 <h1>LiteSurveys Integration</h1> 131 <form action="options.php" method="post"> 132 <?php 133 settings_fields( 'LSAPP_litesurveys' ); 134 do_settings_sections( 'LSAPP_litesurveys' ); 135 submit_button( 'Save Settings' ); 136 ?> 137 </form> 138 </div> 139 <?php 140 } 141 142 /** 143 * Enqueues the LiteSurveys script 144 * 145 * @since 1.0.0 146 */ 147 public static function enqueue_script() { 148 if ( ! self::get_site_id() ) { 149 return; 150 } 151 wp_enqueue_script( 'litesurveys', 'https://embeds.litesurveys.com/litesurveys.min.js', array(), LSAPP_PLUGIN_VERSION, array( 'strategy' => 'defer' ) ); 152 } 153 154 /** 155 * Filter the script attributes to add id and data-site-id attributes. 156 * 157 * @param array $attributes The script tag attributes. 158 * @return array 159 */ 160 public static function add_script_attributes( $attributes ) { 161 if ( 'litesurveys-js' === $attributes['id'] ) { 162 $attributes['data-site-id'] = self::get_site_id(); 163 } 164 return $attributes; 165 } 166 167 /** 168 * Retrieves the saved site id setting 169 * 170 * @since 1.0.0 171 * @return string 172 */ 173 private static function get_site_id() { 174 return self::get_setting('site_id', ''); 175 } 176 177 /** 178 * Retrieves a specific plugin setting 179 * 180 * @since 1.0.0 181 * @param string $setting Which setting to retrieve. 182 * @param mixed $default The value to return if setting does not exist. 183 * @return mixed 184 */ 185 private static function get_setting($setting, $default = false) { 186 $settings = self::get_settings(); 187 if ( isset( $settings[$setting] ) ) { 188 return $settings[$setting]; 189 } 190 191 return $default; 192 } 193 194 /** 195 * Retrieves our plugin settings 196 * 197 * @since 1.0.0 198 * @return array Our settings 199 */ 200 private static function get_settings() { 201 $settings = get_option( 'LSAPP_litesurveys_settings', [] ); 202 if (! is_array( $settings ) ) { 203 $settings = []; 204 } 205 206 return $settings; 817 818 wp_localize_script('litesurveys-frontend', 'liteSurveysSettings', array( 819 'ajaxUrl' => rest_url('litesurveys/v1/') 820 )); 207 821 } 208 822 … … 219 833 'litesurveys-wordpress-plugin/litesurveys-wordpress-plugin.php' 220 834 ]; 221 if (in_array($plugin_file, $plugin_files)) { 222 $settings_url = sprintf( '<a href="%s">Settings</a>', esc_url( admin_url( 'options-general.php?page=LSAPP_litesurveys' ) ) ); 223 $actions = array_merge( ['litesurveys_settings' => $settings_url], $actions) ; 835 if ( in_array( $plugin_file, $plugin_files, true ) ) { 836 $surveys_url = esc_url( admin_url( 'admin.php?page=LSAPP_litesurveys' ) ); 837 $surveys_link = sprintf( '<a href="%s">%s</a>', $surveys_url, __( 'Surveys', 'litesurveys' ) ); 838 $actions = array_merge( array( 'surveys' => $surveys_link ), $actions ); 224 839 } 225 840 return $actions; 226 841 } 842 843 /** 844 * Get the REST API instance. 845 * 846 * @since 2.0.0 847 * @return LSAPP_REST_API The REST API instance. 848 */ 849 public function get_rest_api() { 850 return $this->rest_api; 851 } 852 853 /** 854 * Check if there are any active surveys. 855 * 856 * @return bool True if there are active surveys, false otherwise. 857 */ 858 private function has_active_surveys() { 859 $cache_key = 'litesurveys_has_active'; 860 $has_active = get_transient($cache_key); 861 862 if (false === $has_active) { 863 global $wpdb; 864 865 $has_active = (bool)$wpdb->get_var($wpdb->prepare( 866 "SELECT EXISTS( 867 SELECT 1 FROM {$wpdb->prefix}litesurveys_surveys 868 WHERE active = %d AND deleted_at IS NULL 869 )", 870 1 871 )); 872 873 set_transient($cache_key, $has_active ? '1' : '0', self::CACHE_TIME); 874 } 875 876 return $has_active === '1'; 877 } 878 879 private function verify_admin_access() { 880 if (!current_user_can('manage_options')) { 881 wp_die( 882 esc_html__('You do not have sufficient permissions to access this page.', 'litesurveys'), 883 403 884 ); 885 } 886 } 887 888 /** 889 * Sanitize JSON data before storage. 890 * 891 * @param array $data The data to be sanitized and stored as JSON. 892 * @return string|false Sanitized JSON string or false on failure. 893 */ 894 private function sanitize_json_data($data) { 895 if (!is_array($data)) { 896 return false; 897 } 898 899 array_walk_recursive($data, function(&$value) { 900 if (is_string($value)) { 901 $value = sanitize_text_field($value); 902 } elseif (is_int($value)) { 903 $value = intval($value); 904 } elseif (is_float($value)) { 905 $value = floatval($value); 906 } elseif (is_bool($value)) { 907 $value = (bool)$value; 908 } else { 909 $value = ''; 910 } 911 }); 912 913 return wp_json_encode($data); 914 } 915 916 /** 917 * Get the suffix for asset files (.min in production, empty in debug). 918 * 919 * @return string 920 */ 921 private function get_asset_suffix() { 922 return defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min'; 923 } 924 925 /** 926 * Clear survey caches when a survey is updated. 927 */ 928 private function clear_survey_caches() { 929 delete_transient('litesurveys_active_surveys'); 930 delete_transient('litesurveys_has_active'); 931 } 227 932 } 228 933 229 LSAPP_LiteSurveys_Integration::init(); 934 // Initialize plugin 935 LSAPP_LiteSurveys::get_instance(); 230 936 ?> -
litesurveys/trunk/readme.txt
r3130242 r3183478 4 4 Requires at least: 6.0 5 5 Tested up to: 6.6.1 6 Stable tag: 1.0.36 Stable tag: 2.0.0 7 7 Requires PHP: 8.0 8 8 License: GPLv3 … … 13 13 == Description == 14 14 15 Easily hear from your users by adding pre-sale surveys, post-sale surveys, feedback surveys, and more to your website by integrating your site with [the LiteSurveys service](https://litesurveys.com/).15 Learn from your site visitors using quick surveys. 16 16 17 **Important Note**: This plugin requires a [LiteSurveys](https://litesurveys.com) plan to create surveys and collect responses. Get started for free today! 17 == Description == 18 18 19 ## Collect Feedback and Ideas From Your Site Visitors 19 LiteSurveys is a lightweight WordPress plugin that helps you gather feedback from your website visitors through simple, unobtrusive surveys. With LiteSurveys, you can easily create and manage single-question surveys that appear as slide-in popups on your website. 20 20 21 You can use LiteSurveys to: 21 ## Key Features 22 22 23 * **Conduct User Research** - Using LiteSurveys small slide-in surveys, users can quickly provide information about who they are or what they are looking for. 24 * **Identify Pain Points** - Adding in quick surveys during the sales funnel allows site visitors to provide feedback about concerns and issues. 25 * **Get Marketing Insights** - Using surveys at key points during the user experience can provide details about the user’s decisions and thoughts which can influence your copy and strategy for your marketing efforts. 26 * And much more! 23 * **Simple Survey Creation** - Create surveys directly in your WordPress admin with just a few clicks 24 * **Multiple Question Types** - Choose between multiple choice or open-ended questions 25 * **Customizable Display** - Control when and where your surveys appear 26 * **Easy Response Management** - View and manage survey responses right in your WordPress dashboard 27 * **Unobtrusive Design** - Surveys appear as subtle slide-in popups that won't disrupt the user experience 28 * **Mobile Friendly** - Surveys work great on all devices 29 30 ## Use LiteSurveys to: 31 32 * **Conduct User Research** - Using small slide-in surveys, users can quickly provide information about who they are or what they are looking for 33 * **Identify Pain Points** - Adding quick surveys during the sales funnel allows site visitors to provide feedback about concerns and issues 34 * **Get Marketing Insights** - Using surveys at key points during the user experience can provide details about the user's decisions and thoughts which can influence your copy and strategy 35 * **Gather Customer Feedback** - Collect real feedback from actual users about their experience with your site or product 27 36 28 37 ## Some Features of LiteSurveys: 29 38 30 * **Question Type Variety** - Select from a variety question types including multiple choice, open answer, and more. 31 * **Analyze Results** - Easily see how individual site visitors respond but also analyze how all site visitors are responding. 32 * **Control Timing** - LiteSurveys has many options available for controlling when the survey appears. 33 * **Target Specific Pages** - Show your survey on your whole website or only on specific pages. 34 * **Export Results** - Export your survey responses so you can use them in spreadsheets, databases, other tools, and more. 35 36 Ready to get started? [Create your account](https://litesurveys.com/) for free to create your first survey within minutes! 39 * **Question Type Variety** - Select from multiple choice or open-ended questions to best suit your needs 40 * **Timing Control** - Set when your survey should appear to visitors 41 * **Position Control** - Choose whether your survey appears in the bottom left or bottom right 42 * **Active/Inactive States** - Easily activate or deactivate surveys as needed 43 * **Submissions Per Page** - Easily see what page the website visitor was on when they submitted the survey 37 44 38 45 == Installation == … … 47 54 48 55 1. An example LiteSurveys slide-in survey. 49 2. The settings page within the WordPress plugin.56 2. Surveys admin page showing your surveys, which are active, and how many submissions each has. 50 57 51 58 == Changelog == 59 60 = 2.0.0 (November 6, 2024) = 61 62 * Major rewrite of plugin to be standalone instead of requiring LiteSurveys service 63 * Add ability to create and manage surveys directly in WordPress admin 64 * Add support for multiple choice and open-ended questions 65 * Add support for customizing survey timing and position 52 66 53 67 = 1.0.3 (August 2, 2024) = -
litesurveys/trunk/uninstall.php
r3013451 r3183478 1 1 <?php 2 // if uninstall.php is not called by WordPress, die3 if ( ! defined( 'WP_UNINSTALL_PLUGIN' )) {4 die;2 // If uninstall not called from WordPress, exit 3 if (!defined('WP_UNINSTALL_PLUGIN')) { 4 exit; 5 5 } 6 6 7 delete_option( 'LSAPP_litesurveys_settings' ); 7 global $wpdb; 8 9 // Drop tables in reverse order of dependencies 10 $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}litesurveys_responses"); 11 $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}litesurveys_submissions"); 12 $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}litesurveys_questions"); 13 $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}litesurveys_surveys"); 14 15 // Delete options 16 delete_option('lsapp_litesurveys_version'); 17 delete_option('LSAPP_litesurveys_settings');
Note: See TracChangeset
for help on using the changeset viewer.